#include <stddef.h>
#include <string.h>
#include <errno.h>
#ifndef NDEBUG
#   include <stdio.h>	/* needed by some non-conforming <assert.h>'s */
#endif
#include <assert.h>


extern char	**environ;


/*
 * Set an environment variable.
 *
 * Returns:
 *	 0	Success: `environ' modified
 *	-1	Failure: `environ' undisturbed, errno set
 */
    int
setenv(name, value)
    char	*name;
    char	*value;
{
    int		status;

    if (NULL == name || 0 == name[0] || NULL == value) {
	status	= -1;
	errno	= EINVAL;
    } else {
	char		*string;
	size_t		namelen		= strlen(name);

	/*
	 * Allocate storage for the new name=value string.
	 */
	string	= (char*)malloc(namelen + 1 + strlen(value) + 1);

	if (string == NULL) {
	    status	= -1;
	    errno	= ENOMEM;
	} else {
	    size_t	nslots;			/* old env. vector size */
	    char	**newenviron;
	    char	**slot;			/* slot in old env.  */
	    char	**oldslot	= NULL;	/* old name=value if exists */
	    size_t	nbytes;			/* new env. vector size */
	    static int	realloc_environ;	/* done this before? */

	    /*
	     * Set the new name=value string to name=.
	     */
	    (void) strcpy(string, name);
	    (void) strcpy(string+namelen, "=");

	    /*
	     * Count the number of slots to copy in the environment vector
	     * and find the old name= entry if it exists.
	     */
	    for (nslots = 0, slot = environ; NULL != *slot; slot++) {
		assert(0 != **slot);
		if (NULL != strstr(*slot, string)) {
		    oldslot	= slot;		/* old assignment */
		} else {
		    nslots++;
		}
	    }

	    /*
	     * Complete the value portion of the name=value string.
	     */
	    (void) strcpy(string+namelen+1, value);

	    /*
	     * Create a new environment vector that duplicates the old one but
	     * has room for the additional string pointer at the end.
	     */
	    nbytes	= (nslots+2) * sizeof(char*);
	    if (realloc_environ) {
		newenviron	= (char**)realloc(environ, nbytes);
	    } else {
		newenviron	= (char**)malloc(nbytes);
		if (newenviron != NULL) {
		    size_t	i;
		    /*
		     * Copy the name=value string pointers from the old 
		     * environment.
		     */
		    for (i = 0, slot = environ; NULL != *slot; slot++)
			if (slot != oldslot)
			    newenviron[i++]	= *slot;
		}
	    }

	    if (newenviron == NULL) {
		status	= -1;
		errno	= ENOMEM;
	    } else {
		realloc_environ	= 1;

		/*
		 * Add the new name=value string and switch the environment.
		 */
		newenviron[nslots]	= string;
		newenviron[nslots+1]	= NULL;
		environ			= newenviron;

		status		= 0;
	    }

	    if (status != 0)
		(void) free((void*)string);
	}				/* new string allocated */
    }					/* valid arguments */

    return status;
}
