/* * These routines were written by Devin Reade for GNO 2.0.1. * * $Id: environ.c,v 1.2 1997/09/21 06:21:07 gdr Exp $ * * This file is formatted with tab stops every 3 columns. */ #ifdef __ORCAC__ segment "libc_stdlb"; #endif #pragma databank 1 #include #include #include #include #include #include #include #include #include #include typedef struct stackelm { struct stackelm *next; char **env; } stack_elm; char **environ = NULL; static short __environ_initialized = 0; static short __use_environ = 0; static short __in_environInit = 0; static stack_elm *env_stack = NULL; static char *__findenv(const char *name, int *offset); static char *getvar(const char *name); static int setvar(char *name, const char *value); static void unsetvar (const char *name); /* * int environPush(void); * * Pre: none * * Post: The current state of the shell variable list is saved. This * affects both the internal and the list pointed to by environ. * * Returns 0 on success, -1 otherwise. */ int environPush(void) { stack_elm *p; PushVariablesGSPB parmBlock; parmBlock.pCount = 0; PushVariablesGS(&parmBlock); /* if we're not using environ, then we're finished */ if(!__use_environ) return 0; /* push environ onto the environment stack */ if ((p = (stack_elm *) malloc (sizeof(stack_elm))) == NULL) return -1; p->next = env_stack; env_stack = p; env_stack->env = environ; /* zero the new environment and initialize */ environ = NULL; __environ_initialized = 0; if (environInit() != 0) { /* environInit failed; restore old environ */ __environ_initialized = 1; environ = env_stack->env; p = env_stack; env_stack = env_stack->next; free(p); return -1; } return 0; } /* * void environPop(void); * * Pre: none * * Post: The shell variable list is restored to the state that it was in * when the most recent environPush() call was made. This affects both * the internal version, and the list pointed to by environ. */ void environPop(void) { stack_elm *s; char **p, **q; PushVariablesGSPB parmBlock; parmBlock.pCount = 0; PopVariablesGS(&parmBlock); /* if we're not using environ, then we're finished */ if(!__use_environ) return; if(!env_stack) return; /* empty stack */ /* restore environ to its previous value */ p = environ; environ = (env_stack) ? env_stack->env : NULL; s = env_stack; env_stack = (env_stack) ? env_stack->next : NULL; /* free up each element in the discarded environment */ q = p; while (q && *q) { free(*q); q++; } /* free the discarded environment */ if (p) free(p); /* free the discarded environment stack element */ if (s) free(s); return; } /* * static int setvar (char *name, const char *value); * * Purpose: to set shell variable to . This affects only * the internal representation, not that of the environ variable. * * Pre: and are null-terminated strings * * Post: is set to . Returns 0 on success, -1 on failure * * Acknowledgements: This routine is modified from code written by * Dave Tribby [ GEnie: D.TRIBBY Internet: tribby@cup.hp.com ] * for the "evaluate" shell utility for the Orca shell. Used * with permission. */ static int setvar(char *name, const char *value) { int error; GSString255Ptr var_value; /* Holds variable sized string */ SetGSPB set_var_pb; static GSString255 var_name; /* Shell call requires three parameters */ set_var_pb.pCount = 3; /* Create GSOS string that holds the name of the variable */ /* Truncate value if > size of GSString255 */ var_name.length = strlen(name); if (var_name.length > sizeof(var_name.text)) { var_name.length = sizeof(var_name.text); strncpy(var_name.text, name, sizeof(var_name.text)); } else { strcpy(var_name.text, name); } set_var_pb.name = &var_name; /* Allocate a GS string large enough to hold the value */ var_value = (GSString255Ptr) malloc(strlen(value)+sizeof(Word)); if (var_value == NULL) return (-1); var_value->length = strlen(value); strcpy(var_value->text, value); set_var_pb.value = var_value; set_var_pb.export = 1; /* Make the shell call to set the variable */ SetGS(&set_var_pb); error = toolerror(); free (var_value); if (error) { return -1; } else { return 0; } } /* setvar */ /* * static void unsetvar (const char *name); * * Pre: points to the name of the shell variable to be deleted. It * may have a trailing '='. * * Post: The variable is deleted from the shell's internal environment. * Any further references to it will return a NULL pointer. */ static void unsetvar (const char *name) { UnsetVariableGSPB parmblock; GSString255 parmname; /* ** delete the internal version */ /* set up the parameters */ parmblock.pCount = 1; parmblock.name = &parmname; parmname.length = strlen(name); if (parmname.length > 254) parmname.length = 254; strncpy(parmname.text,name,parmname.length); if (parmname.text[parmname.length -1] == '=') { parmname.text[parmname.length -1] = (char) NULL; } else { parmname.text[parmname.length] = (char) NULL; } UnsetVariableGS(&parmblock); return; } /* * static char *getvar (const char *name); * * Purpose: to get the value of shell variable , using the internal * (not environ) representation. * * Pre: is a null-terminated string * * Post: returns a pointer to the value of . If the variable has * not been set or if the program is executing from an environment * where shell variables do not exist, a NULL value is returned. * * Acknowledgements: This routine is modified from code written by * Dave Tribby [ GEnie: D.TRIBBY Internet: tribby@cup.hp.com ] * for the "evaluate" shell utility for the Orca shell. Used * with permission. */ static char *getvar(const char *name) { ReadVariableGSPB get_var_pb; static ResultBuf255 var_value; static GSString255 var_name; char *result; int length; /* Shell call requires three parameters */ get_var_pb.pCount = 3; /* Create GSOS string that holds the name of the variable */ /* Truncate value if > size of GSString255 */ var_name.length = strlen(name); if (var_name.length > sizeof(var_name.text)) { var_name.length = sizeof(var_name.text); strncpy(var_name.text, name, sizeof(var_name.text)); } else { strcpy(var_name.text, name); } get_var_pb.name = &var_name; /* initialize the result buffer */ var_value.bufSize = sizeof (GSString255); get_var_pb.value = &var_value; /* Make the shell call to get the variable */ ReadVariableGS(&get_var_pb); /* failed if tool error or not for export */ if (toolerror() || (get_var_pb.export == 0)) return (NULL); /* get length of variable value */ length = ((ResultBuf255Ptr) get_var_pb.value)->bufString.length; /* failed if variable not defined (length zero) */ if (length == 0) return (NULL); /* set the NULL terminator */ result = (((ResultBuf255Ptr) get_var_pb.value)->bufString.text); result[length] = (char) NULL; return (result); } /* getvar */ /* int environInit (void) * * Purpose: to initialize environ. This need not be done if the calling * program does not need to access environ directly [that is, if it * restricts itself to using getenv(), setenv(), putenv(), and * unsetenv() ] * * Pre: none * * Post: the environment environ is initialized, and contains entries for * all defined internal shell variables. Returns 0 on success, * non-zero otherwise. */ int environInit (void) { static ReadIndexedGSPB parmBuffer; static ResultBuf255 nameBuffer, valueBuffer; unsigned int nameLength, valueLength; char *name; char *value; /* make sure we only do this once */ if (__environ_initialized) return 0; __environ_initialized = 1; __use_environ = 1; __in_environInit = 1; /* ** initialize the parameter block */ parmBuffer.pCount = 4; parmBuffer.index = 1; nameBuffer.bufSize = sizeof (GSString255); valueBuffer.bufSize = sizeof (GSString255); parmBuffer.name = &nameBuffer; parmBuffer.value = &valueBuffer; /* get space for our name and value buffers */ name = (char *) malloc (255 * sizeof(char)); if (!name) { __in_environInit = 0; return 1; } value = (char *) malloc (255 * sizeof(char)); if (!value) { free(name); __in_environInit = 0; return 1; } /* ** add each variable into environ as they appear in the shell ** environment */ ReadIndexedGS (&parmBuffer); nameLength = nameBuffer.bufString.length; while (nameLength != 0) { valueLength = valueBuffer.bufString.length; /* copy the name and value */ strncpy (name, nameBuffer.bufString.text, nameLength); name[nameLength] = (char) NULL; strncpy (value, valueBuffer.bufString.text, valueLength); value[valueLength] = (char) NULL; /* try to place it in environ */ if (setenv(name, value, 1) != 0) { free(name); free(value); __in_environInit = 0; return 1; } /* get the next shell variable and continue ... */ parmBuffer.index++; ReadIndexedGS (&parmBuffer); nameLength = nameBuffer.bufString.length; } free(name); free(value); __in_environInit = 0; return 0; } /* environInit() */ /* * int putenv (const char *str) * * Purpose: Take a string of the form NAME=value and stick it into the * environment. * * Pre: is a null-terminated string of the form NAME=value * * Post: returns zero if successful. A non-zero value indicates that * space to expand the environment pointer table could not be * acquired; in this case, the string has not been added * * Warning: * Certain naming restrictions may apply if the environment variable * is referenced by shell programs */ int putenv (const char *str) { char *name, *value; size_t l_str; int result; /* get space for our buffer */ l_str = strlen(str); name = (char *) malloc (l_str + 1); if (!name) return -1; strcpy(name,str); /* replace the '=' with a null and set value */ for (value=name; (*value) && (*value != '='); value++); if (*value == '=') { /* found the end of name */ *value = (char) NULL; value++; result = (*value) ? setenv(name,value,1) : -1; } else { result = -1; } free(name); return result; } /* * char *getenv (const char *NAME) * * Purpose: search the environment for a string of the format NAME=VALUE * * Pre: NAME is the name of the variable for which the value is to be * retrieved. It may end with an extra '=' which is not part of the * name. * * Post: returns a pointer to the value of NAME. If NAME is not defined, it * returns NULL. getenv() is case sensitive to NAME. */ char *getenv(const char *name) { char *result; size_t length; length = strlen(name); if (!length) return NULL; if(name[length-1] == '=') { char *tmp_name; if ((tmp_name = malloc(length+1)) == NULL) return NULL; strcpy(tmp_name,name); tmp_name[length-1] = (char) NULL; result = getvar(tmp_name); free(tmp_name); } else { result = getvar(name); } return result; } /* * static char *__findenv(const char *name, int *offset); * * Pre: is a null-terminated string, which may end with '='. * * Post: returns a pointer to the value associated with in the * environment, if any, else it returns NULL. Sets to * be the offset of the name/value combination in the environmental * array (environ), for use by setenv(3) and unsetenv(3). It * explicitly removes '=' in argument . * * Acknowledgements: This is based on UCB code; see the above legalese. */ static char *__findenv(const char *name, int *offset) { unsigned int len; char **P, *C; if (environ==NULL) { return NULL; } for (C = name, len = 0; *C && (*C != '='); C++, len++); for (P = environ; *P; P++) { if (!strncmp(*P, name, len)) { C = *P + len; if (*C == '=') { *offset = P - environ; C++; return(C); } } } return(NULL); } /* * int setenv (const char *name, const char *value, int rewrite); * * Pre: is the name of the environment variable to be set with * value . is either unset (zero) or set (non- * zero). * * Post: If does not previously exist, then it is added to the * environment with value . If has been previously * set, then is tested: If is non-zero then * the old value is replaced, otherwise the call has no effect. * * Returns zero on success or if already exists and * is not set. Returns -1 and sets errno on failure. */ int setenv (const char *name, const char *value, int rewrite) { static int alloced; /* if allocated space before */ char *C; size_t l_value, l_name; int offset; char *tmp_name; char *tmp_str; if (*value == '=') value++; /* ignore any prepended '=' in value */ l_name = strlen(name); /* get the string lengths */ l_value = strlen(value); if(name[l_name-1] == '=') l_name--; /* ignore any appended '=' in name */ if ((l_name == 0) || (l_value == 0)) { /* bad args! */ errno = EINVAL; return -1; } /* ** make a copy of the name */ tmp_name = (char *) malloc (l_name+1); /* allocate necessary memory */ if (tmp_name == NULL) return -1; strncpy(tmp_name, name, l_name); /* do the copy */ tmp_name[l_name] = '\0'; /* ** make a string of the form name=value, if necessary */ if (__use_environ) { /* are we using the environ structure? */ tmp_str = (char *) malloc (l_name + l_value + 2); if (!tmp_str) { free(tmp_name); errno = ENOMEM; return -1; } strcpy (tmp_str,tmp_name); strcat (tmp_str,"="); strcat (tmp_str,value); } /* ** Change the internal version */ if ((!__in_environInit) && ((rewrite) || (getenv(tmp_name) == NULL))) { if (setvar(tmp_name, value)) { int tmp_err = errno; free(tmp_name); if (__use_environ) free(tmp_str); errno = tmp_err; return -1; } } if (__use_environ==0) { free(tmp_name); return 0; } /* ** Change the external (environ) version */ C = __findenv(tmp_name, &offset); /* find if already exists */ if (C!=NULL) { if (!rewrite) { free(tmp_name); free(tmp_str); return 0; } if (strlen(C) >= l_value) { /* old larger; copy over */ while (*value) { *C = *value; C++; value++; } free(tmp_name); free(tmp_str); return 0; } } else { /* not found; create new slot */ int cnt; char **P; cnt = 0; if (environ) for (P = environ; *P; P++) cnt++; if (alloced) { /* done before; just increase size */ P = (char **) realloc ((char *)environ, (size_t)(sizeof(char *) * (cnt + 2))); if (!P) { /* realloc failed */ unsetvar(tmp_name); free(tmp_name); free(tmp_str); errno = ENOMEM; return -1; } else { environ = P; } } else { /* first time; get new space */ alloced = 1; /* copy old entries into it */ P = (char **) malloc ((size_t) (sizeof(char *) * (cnt + 2))); if (!P) { unsetvar(tmp_name); free(tmp_name); free(tmp_str); errno = ENOMEM; return -1; } /* * original was: * bcopy(environ, P, cnt * sizeof(char *)); * changed so that we could use the standard Orca libraries * for non-gno implementations. */ if (environ) memcpy(P, environ, cnt * sizeof(char *)); environ = P; } environ[cnt + 1] = NULL; offset = cnt; } /* we've got the new slot, now add it in */ environ[offset] = tmp_str; free(tmp_name); return 0; } /* * void unsetenv (const char *name); * * Pre: points to the name of the shell variable to be deleted. It * may have a trailing '='. * * Post: The variable is deleted. Any further references to it will return * a NULL pointer. This routine unsets both the internal and, if it's * initialized, the environ representations. * * Acknowledgements: Contains BSD code. See the above legalese. */ void unsetenv (const char *name) { char **P; int offset; /* ** delete the internal version */ unsetvar(name); if(!__use_environ) return; /* ** delete the environ version, if necessary. */ while (__findenv(name, &offset)!=NULL) { /* if set multiple times */ free(environ[offset]); for (P = &environ[offset];; P++) if (!(*P = *(P + 1))) break; } return; }