mirror of
https://github.com/GnoConsortium/gno.git
synced 2024-12-22 14:30:29 +00:00
687 lines
18 KiB
C
687 lines
18 KiB
C
/*
|
|
* 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 <stddef.h>
|
|
#include <types.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <gsos.h>
|
|
#include <orca.h>
|
|
#include <shell.h>
|
|
|
|
|
|
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 <name> to <value>. This affects only
|
|
* the internal representation, not that of the environ variable.
|
|
*
|
|
* Pre: <name> and <value> are null-terminated strings
|
|
*
|
|
* Post: <name> is set to <value>. 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: <name> 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 <name>, using the internal
|
|
* (not environ) representation.
|
|
*
|
|
* Pre: <name> is a null-terminated string
|
|
*
|
|
* Post: returns a pointer to the value of <name>. 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: <str> 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: <name> is a null-terminated string, which may end with '='.
|
|
*
|
|
* Post: returns a pointer to the value associated with <name> in the
|
|
* environment, if any, else it returns NULL. Sets <offset> 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 <name>.
|
|
*
|
|
* 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: <name> is the name of the environment variable to be set with
|
|
* value <value>. <rewrite> is either unset (zero) or set (non-
|
|
* zero).
|
|
*
|
|
* Post: If <name> does not previously exist, then it is added to the
|
|
* environment with value <value>. If <name> has been previously
|
|
* set, then <rewrite> is tested: If <rewrite> is non-zero then
|
|
* the old value is replaced, otherwise the call has no effect.
|
|
*
|
|
* Returns zero on success or if <name> already exists and <rewrite>
|
|
* 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: <name> 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;
|
|
}
|