[comp.unix.wizards] replacement for putenv

rogol@marob.MASA.COM (Fred Buck) (02/11/89)

In article <164@bds-ny.UUCP> jduro@bds-ny.UUCP (Jacques Durosier) writes:

>Setting: UniPlus SysV release 5.0 
>Problem: lack of putenv() 
>
>The function putenv() which is included in Release 5.2 and above is 
>not part of libc.a on the above system.
>
>Apparently, there is a PD replacement, which I have not been able
>to locate.

Whether this one, which I wrote for another system lacking putenv(), is
the PD replacement to which you refer, I dunno, although others besides
me use it.  (Coding style crunched a bit to excuse posting [even short]
source to a non-source newsgroup):

/* putenv( string ) -- place a string into the current process' environment
 * ================
 *   char *string;
 *
 *          Returns: (-1) on error, otherwise 0
 *
 * Putenv() places 'string' into the environment of the current
 * process.  'String' should be a null-terminated string of the
 * form "name=value".  If the environment already contains such a
 * string, 'string' is substituted for it; otherwise, 'string' is
 * added to the environment as a new variable.
 * 
 * If the existing environment lacks sufficient space to hold
 * 'string', putenv() attempts to allocate more space via
 * malloc().
 * 
 * Putenv() will fail and return a value of (-1) if 'string' is
 * not in the form "name=value" or if additional necessary space
 * cannot be allocated for the new environment.
 */

putenv(string)
char *string;
{
    extern char **environ;
    extern char *strchr(), *malloc();
    extern char **calloc();        	/* a lie for 'lint' */

    register int i;
    int namelen, numvars;
    char *cptr, **newenv;

    if (string==0) return(-1);
    namelen = (int)(strchr(string,'=') - string) + 1;
    if (namelen<2) return(-1);
    /* see if this variable is already in environment */
    i = (-1);
    while (environ[++i]) {
        if (strncmp(string,environ[i],namelen)==0) {  /* It's there */
                /* if we can just patch it, do so and return */
            if (strlen(string)<=strlen(environ[i])) {
                strcpy(environ[i],string); return(0);
            }
            else break;
        }
    }
        /* OK, allocate a spot for 'string' and copy it there */
    cptr = malloc(strlen(string)+1);
    if (cptr==0) return(-1);          /* can't malloc */
    strcpy(cptr,string);
        /* no env at all; or else var exists but's too small to hold 'string' */
    if (i==0 || environ[i]) {  
        environ[i] = cptr; return(0);     /* ok, done */
    }
        /* "name" ain't there, or no space for it; so rebuild env. array */
    numvars = i;
    newenv = calloc(numvars+2,sizeof(char *));
    if (newenv==0) return(-1);        /* can't calloc */
    for (i=0;i<numvars;++i)
        newenv[i] = environ[i];
    newenv[i] = cptr;
    newenv[++i] = 0;
    environ = newenv;
    return(0);
}

leo@philmds.UUCP (Leo de Wit) (02/12/89)

In article <549@marob.MASA.COM> rogol@marob.masa.com (Fred Buck) writes:
    []
|    /* see if this variable is already in environment */
|    i = (-1);
|    while (environ[++i]) {
|        if (strncmp(string,environ[i],namelen)==0) {  /* It's there */
|                /* if we can just patch it, do so and return */
|            if (strlen(string)<=strlen(environ[i])) {
|                strcpy(environ[i],string); return(0);
|            }
|            else break;
|        }
|    }
|        /* OK, allocate a spot for 'string' and copy it there */
|    cptr = malloc(strlen(string)+1);
|    if (cptr==0) return(-1);          /* can't malloc */
|    strcpy(cptr,string);
|        /* no env at all; or else var exists but's too small to hold 'string' */
|    if (i==0 || environ[i]) {  
|        environ[i] = cptr; return(0);     /* ok, done */
|    }

If i == 0 (no env at all), you end up overwriting the terminating null
ptr (environ[0]). So the last three lines should read:

    if (environ[i] != (char *)0) {        /* or simply: if (environ[i]) */
        environ[i] = cptr; return(0);     /* ok, done */
    }

Now for i == 0 a array for two char ptrs is allocated in the code that follows.

	 Leo.

chip@ateng.ateng.com (Chip Salzenberg) (02/14/89)

Here is a rather nice replacement for putenv().  I wrote it for the BSD port
of my deliver program.  (I know it's source, but it's short.)  Its nicest
feature is the avoidance of memory waste when it is called several times.
---8<---cut here---8<---

int
putenv(s)
char *s;
{
	static  char    **env_array;
	static  int     env_size;
	char    *e;
	int     i, j;

	if (env_array == NULL)
	{
		for (i = 0; environ[i]; ++i)
			{}
		env_size = i + 10;      /* arbitrary */
		env_array = (char **) malloc(env_size * sizeof(char *));
		if (env_array == NULL)
			return 1;
		memcpy((char *)env_array, (char *)environ,
		       (int) ((i + 1) * sizeof(char *)));
		environ = env_array;
	}
	else if (environ != env_array)
		fprintf(stderr, "putenv: warning: someone moved environ!\n");

	if ((e = strchr(s, '=')) != NULL)
		++e;
	else
		e = s + strlen(s);

	j = 0;
	for (i = 0; env_array[i]; ++i)
	{
		if (strncmp(env_array[i], s, e - s) != 0)
			env_array[j++] = env_array[i];
	}

	if ((j + 1) >= env_size)
	{
		env_size += 10;                 /* arbitrary */
		env_array = (char **) realloc((char *)env_array,
					env_size * sizeof(char **));
		if (env_array == NULL)
			return 1;
	}

	env_array[j++] = s;
	env_array[j] = NULL;

	environ = env_array;
	return 0;
}

-- 
Chip Salzenberg             <chip@ateng.com> or <uunet!ateng!chip>
A T Engineering             Me?  Speak for my company?  Surely you jest!
	  "It's no good.  They're tapping the lines."