1/*
2 * Many systems have putenv() but no setenv(). Other systems have setenv()
3 * but no putenv() (MIPS). Still other systems have neither (NeXT). This is a
4 * re-implementation that hopefully ends all problems.
5 *
6 * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
7 */
8
9#ifndef lint
10static char sccsid[] = "@(#) environ.c 1.2 94/03/23 16:09:46";
11#endif
12
13/* System libraries. */
14
15extern char **environ;
16extern char *strchr();
17extern char *strcpy();
18extern char *strncpy();
19extern char *malloc();
20extern char *realloc();
21extern int strncmp();
22extern void free();
23
24#ifdef no_memcpy
25#define memcpy(d,s,l) bcopy(s,d,l)
26#else
27extern char *memcpy();
28#endif
29
30/* Local stuff. */
31
32static int addenv();			/* append entry to environment */
33
34static int allocated = 0;		/* environ is, or is not, allocated */
35
36#define DO_CLOBBER	1
37
38/* namelength - determine length of name in "name=whatever" */
39
40static int namelength(name)
41char   *name;
42{
43    char   *equal;
44
45    equal = strchr(name, '=');
46    return ((equal == 0) ? strlen(name) : (equal - name));
47}
48
49/* findenv - given name, locate name=value */
50
51static char **findenv(name, len)
52char   *name;
53int     len;
54{
55    char  **envp;
56
57    for (envp = environ; envp && *envp; envp++)
58	if (strncmp(name, *envp, len) == 0 && (*envp)[len] == '=')
59	    return (envp);
60    return (0);
61}
62
63/* getenv - given name, locate value */
64
65char   *getenv(name)
66char   *name;
67{
68    int     len = namelength(name);
69    char  **envp = findenv(name, len);
70
71    return (envp ? *envp + len + 1 : 0);
72}
73
74/* putenv - update or append environment (name,value) pair */
75
76int     putenv(nameval)
77char   *nameval;
78{
79    char   *equal = strchr(nameval, '=');
80    char   *value = (equal ? equal : "");
81
82    return (setenv(nameval, value, DO_CLOBBER));
83}
84
85/* unsetenv - remove variable from environment */
86
87void    unsetenv(name)
88char   *name;
89{
90    char  **envp;
91
92    if ((envp = findenv(name, namelength(name))) != 0)
93	while (envp[0] = envp[1])
94	    envp++;
95}
96
97/* setenv - update or append environment (name,value) pair */
98
99int     setenv(name, value, clobber)
100char   *name;
101char   *value;
102int     clobber;
103{
104    char   *destination;
105    char  **envp;
106    int     l_name;			/* length of name part */
107    int     l_nameval;			/* length of name=value */
108
109    /* Permit name= and =value. */
110
111    l_name = namelength(name);
112    envp = findenv(name, l_name);
113    if (envp != 0 && clobber == 0)
114	return (0);
115    if (*value == '=')
116	value++;
117    l_nameval = l_name + strlen(value) + 1;
118
119    /*
120     * Use available memory if the old value is long enough. Never free an
121     * old name=value entry because it may not be allocated.
122     */
123
124    destination = (envp != 0 && strlen(*envp) >= l_nameval) ?
125	*envp : malloc(l_nameval + 1);
126    if (destination == 0)
127	return (-1);
128    strncpy(destination, name, l_name);
129    destination[l_name] = '=';
130    strcpy(destination + l_name + 1, value);
131    return ((envp == 0) ? addenv(destination) : (*envp = destination, 0));
132}
133
134/* cmalloc - malloc and copy block of memory */
135
136static char *cmalloc(new_len, old, old_len)
137char   *old;
138int     old_len;
139{
140    char   *new = malloc(new_len);
141
142    if (new != 0)
143	memcpy(new, old, old_len);
144    return (new);
145}
146
147/* addenv - append environment entry */
148
149static int addenv(nameval)
150char   *nameval;
151{
152    char  **envp;
153    int     n_used;			/* number of environment entries */
154    int     l_used;			/* bytes used excl. terminator */
155    int     l_need;			/* bytes needed incl. terminator */
156
157    for (envp = environ; envp && *envp; envp++)
158	 /* void */ ;
159    n_used = envp - environ;
160    l_used = n_used * sizeof(*envp);
161    l_need = l_used + 2 * sizeof(*envp);
162
163    envp = allocated ?
164	(char **) realloc((char *) environ, l_need) :
165	(char **) cmalloc(l_need, (char *) environ, l_used);
166    if (envp == 0) {
167	return (-1);
168    } else {
169	allocated = 1;
170	environ = envp;
171	environ[n_used++] = nameval;		/* add new entry */
172	environ[n_used] = 0;			/* terminate list */
173	return (0);
174    }
175}
176
177#ifdef TEST
178
179 /*
180  * Stand-alone program for test purposes.
181  */
182
183/* printenv - display environment */
184
185static void printenv()
186{
187    char  **envp;
188
189    for (envp = environ; envp && *envp; envp++)
190	printf("%s\n", *envp);
191}
192
193int     main(argc, argv)
194int     argc;
195char  **argv;
196{
197    char   *cp;
198    int     changed = 0;
199
200    if (argc < 2) {
201	printf("usage: %s name[=value]...\n", argv[0]);
202	return (1);
203    }
204    while (--argc && *++argv) {
205	if (argv[0][0] == '-') {		/* unsetenv() test */
206	    unsetenv(argv[0] + 1);
207	    changed = 1;
208	} else if (strchr(argv[0], '=') == 0) {	/* getenv() test */
209	    cp = getenv(argv[0]);
210	    printf("%s: %s\n", argv[0], cp ? cp : "not found");
211	} else {				/* putenv() test */
212	    if (putenv(argv[0])) {
213		perror("putenv");
214		return (1);
215	    }
216	    changed = 1;
217	}
218    }
219    if (changed)
220	printenv();
221    return (0);
222}
223
224#endif /* TEST */
225