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(char *name)
41{
42    char   *equal;
43
44    equal = strchr(name, '=');
45    return ((equal == 0) ? strlen(name) : (equal - name));
46}
47
48/* findenv - given name, locate name=value */
49
50static char **findenv(char *name, int len)
51{
52    char  **envp;
53
54    for (envp = environ; envp && *envp; envp++)
55	if (strncmp(name, *envp, len) == 0 && (*envp)[len] == '=')
56	    return (envp);
57    return (0);
58}
59
60/* getenv - given name, locate value */
61
62char   *getenv(char *name)
63{
64    int     len = namelength(name);
65    char  **envp = findenv(name, len);
66
67    return (envp ? *envp + len + 1 : 0);
68}
69
70/* putenv - update or append environment (name,value) pair */
71
72int     putenv(char *nameval)
73{
74    char   *equal = strchr(nameval, '=');
75    char   *value = (equal ? equal : "");
76
77    return (setenv(nameval, value, DO_CLOBBER));
78}
79
80/* unsetenv - remove variable from environment */
81
82void    unsetenv(char *name)
83{
84    char  **envp;
85
86    if ((envp = findenv(name, namelength(name))) != 0)
87	while (envp[0] = envp[1])
88	    envp++;
89}
90
91/* setenv - update or append environment (name,value) pair */
92
93int     setenv(char *name, char *value, int clobber)
94{
95    char   *destination;
96    char  **envp;
97    int     l_name;			/* length of name part */
98    int     l_nameval;			/* length of name=value */
99
100    /* Permit name= and =value. */
101
102    l_name = namelength(name);
103    envp = findenv(name, l_name);
104    if (envp != 0 && clobber == 0)
105	return (0);
106    if (*value == '=')
107	value++;
108    l_nameval = l_name + strlen(value) + 1;
109
110    /*
111     * Use available memory if the old value is long enough. Never free an
112     * old name=value entry because it may not be allocated.
113     */
114
115    destination = (envp != 0 && strlen(*envp) >= l_nameval) ?
116	*envp : malloc(l_nameval + 1);
117    if (destination == 0)
118	return (-1);
119    strncpy(destination, name, l_name);
120    destination[l_name] = '=';
121    strcpy(destination + l_name + 1, value);
122    return ((envp == 0) ? addenv(destination) : (*envp = destination, 0));
123}
124
125/* cmalloc - malloc and copy block of memory */
126
127static char *cmalloc(int new_len, char *old, int old_len)
128{
129    char   *new = malloc(new_len);
130
131    if (new != 0)
132	memcpy(new, old, old_len);
133    return (new);
134}
135
136/* addenv - append environment entry */
137
138static int addenv(char *nameval)
139{
140    char  **envp;
141    int     n_used;			/* number of environment entries */
142    int     l_used;			/* bytes used excl. terminator */
143    int     l_need;			/* bytes needed incl. terminator */
144
145    for (envp = environ; envp && *envp; envp++)
146	 /* void */ ;
147    n_used = envp - environ;
148    l_used = n_used * sizeof(*envp);
149    l_need = l_used + 2 * sizeof(*envp);
150
151    envp = allocated ?
152	(char **) realloc((char *) environ, l_need) :
153	(char **) cmalloc(l_need, (char *) environ, l_used);
154    if (envp == 0) {
155	return (-1);
156    } else {
157	allocated = 1;
158	environ = envp;
159	environ[n_used++] = nameval;		/* add new entry */
160	environ[n_used] = 0;			/* terminate list */
161	return (0);
162    }
163}
164
165#ifdef TEST
166
167 /*
168  * Stand-alone program for test purposes.
169  */
170
171/* printenv - display environment */
172
173static void printenv()
174{
175    char  **envp;
176
177    for (envp = environ; envp && *envp; envp++)
178	printf("%s\n", *envp);
179}
180
181int     main(int argc, char **argv)
182{
183    char   *cp;
184    int     changed = 0;
185
186    if (argc < 2) {
187	printf("usage: %s name[=value]...\n", argv[0]);
188	return (1);
189    }
190    while (--argc && *++argv) {
191	if (argv[0][0] == '-') {		/* unsetenv() test */
192	    unsetenv(argv[0] + 1);
193	    changed = 1;
194	} else if (strchr(argv[0], '=') == 0) {	/* getenv() test */
195	    cp = getenv(argv[0]);
196	    printf("%s: %s\n", argv[0], cp ? cp : "not found");
197	} else {				/* putenv() test */
198	    if (putenv(argv[0])) {
199		perror("putenv");
200		return (1);
201	    }
202	    changed = 1;
203	}
204    }
205    if (changed)
206	printenv();
207    return (0);
208}
209
210#endif /* TEST */
211