/* My very own implementation of putenv() and friends, ** because NeXTSTEP is a lame UNIX. ** ** Copyright (c) 1994 by Olin Shivers. ** You may use this software for any purpose provided I am not held ** accountable for its effects and you leave this copyright notice ** intact. */ /* This file exports three names: ** extern int putenv(const char *str); ** extern int unsetenv(const char *name); ** extern int setenv(const char *name, const char *val); */ /* This code leaks memory. It is unavoidable. */ #include /* Don't want to include stdlib.h because it declares putenv to take ** a const string -- bogus. So we'll just declare malloc directly. */ extern void *malloc(size_t size); #define Malloc(type,n) ((type *) malloc(sizeof(type)*(n))) extern char **environ; /*****************************************************************************/ /* Internal utility. ** Copy the entire env to a new block, and add the new definition. ** Drop the old block on the floor; can't free() it. ** Return 0 if win ** non-zero if the malloc fails. */ static int append_envvar(char *str, int old_envsize) { char **envp, **nenvp; char **newenv = Malloc(char*, 1+old_envsize); if( !newenv ) return 1; for( envp=environ, nenvp=newenv; *envp; envp++, nenvp++ ) *nenvp = *envp; *nenvp++ = str; *nenvp = 0; environ = newenv; return 0; } /* int putenv(char *str) *************************** ** Change or add a value to the environment. ** ** str is an env string of the form "=". ** The environ vector is scanned for a matching binding. ** If one is found, str is installed in that slot in the vector. ** Otherwise, the environ vector is copied into a new vector of one greater ** size, and str is added in the new slot. Note: in either case, str ** becomes part of the environment structure (until is later replaced by ** another putenv() call), so altering str changes the environment. ** ** Malloc is used to allocate new environ vectors. ** In neither replacement strategy are we able to free() the unused ** storage; it is simply dropped on the floor. ** Putenv returns ** 0 if it wins; ** non-zero if str doesn't contain an '=' char or if the malloc fails. */ int putenv(char *str) { char **envp; char *equalsign = strchr(str, '='); int namelen; if( ! equalsign ) return 0; /* No equals sign in str! */ namelen = 1 + equalsign - str; /* Count the terminating =. */ for(envp = environ; *envp; envp++) if( ! memcmp(*envp, str, namelen) ) { *envp = str; return 0; } /* The env var wasn't defined. Copy the entire env to a new ** block, and add the new definition. */ return append_envvar(str, envp-environ+1); } /* int unsetenv(const char *name) *********************************** ** Delete an environment var from environ. ** name is an environment variable . All strings in environ ** beginning with "=" are deleted from environ. unsetenv ** returns the number of occurrences it found and deleted; if ** it returns 0, then the variable wasn't in environ to begin with. ** If name is the null pointer, unsetenv returns -1 immediately. */ int unsetenv(const char *name) { char **envp, **target; int hits; int slen; if( !name ) return -1; slen = strlen(name); hits = 0; target = environ; for( envp=environ; *envp; envp++ ) if( !strncmp(*envp, name, slen) && (*envp)[slen] == '=' ) hits++; else *target++ = *envp; *target = 0; return hits; } /* int setenv(const char *name, const char *val) ************************************************ ** Sets an existing env var or adds a new one. ** - If val is the null pointer, the env var is deleted. ** - If name is the null pointer, setenv() returns an error and does nothing. ** ** If env var is already defined in environ, then ** the new value is copied over the var's old value if ** there is space. Otherwise a fresh string is allocated ** with malloc. If the var is not defined, then environ ** is copied to a fresh block of storage, of size one greater, ** and the new "=" binding installed in that block. ** ** Returns 0 if it wins. ** Returns non-zero if there is an error. */ int setenv(const char *name, const char *val) { char **envp; char *s; int val_len, name_len; if( !name ) return 1; if( !val ) { unsetenv(name); return 0; } name_len = strlen(name); val_len = strlen(val); for( envp=environ; *envp; envp++ ) if( !strncmp(*envp, name, name_len) && (*envp)[name_len] == '=' ) { char *equal = name_len + *envp; if( strlen(equal+1) >= val_len ) memcpy(equal+1, val, val_len+1); /* Copy in place. */ else { char *s = Malloc(char, name_len + val_len + 2); if( !s ) return 1; memcpy(s, name, name_len); s[name_len] = '='; memcpy(s+name_len+1, val, val_len+1); *envp=s; } return 0; } /* Not found. Add. */ s = Malloc(char, val_len + name_len + 2); if( !s ) return 1; memcpy(s, name, name_len); s[name_len] = '='; memcpy(s+name_len+1, val, val_len+1); return append_envvar(s, 1+envp-environ); }