79a85e7ca8b3a7628fab911d4c29871701b49f06
[ghc-hetmet.git] / ghc / lib / posix / cbits / env.c
1 /*
2 %
3 % (c) The GRASP/AQUA Project, Glasgow University, 1995-1996
4 %
5 \subsection[env.lc]{Environment Handling for LibPosix}
6
7 Many useful environment functions are not necessarily provided by libc.
8 To get around this problem, we introduce our own.  The first time that
9 you modify your environment, we copy the environment wholesale into
10 malloc'ed locations, so that subsequent modifications can do proper
11 memory management.  The $environ$ variable is updated with a pointer
12 to the current environment so that the normal $getenv$ and $exec*$ functions
13 should continue to work properly.
14
15 \begin{code}
16 */
17
18 #include "rtsdefs.h"
19 #include "libposix.h"
20
21 /* Switch this on once we've moved the environment to the malloc arena */
22 int dirtyEnv = 0;
23
24 /* 
25  * For some reason, OSF turns off the prototype for this if we're
26  * _POSIX_SOURCE.  Seems to me that this ought to be an ANSI-ism
27  * rather than a POSIX-ism, but no matter.  (JSM(?))
28  */
29
30 char *
31 strDup(const char *src)
32 {
33     int len = strlen(src) + 1;
34     char *dst;
35
36     if ((dst = malloc(len)) != NULL)
37         memcpy(dst, src, len);
38     return dst;
39 }
40
41 /* Replace the entire environment */
42 int
43 setenviron(char **envp)
44 {
45     char **old = environ;
46     int dirtyOld = dirtyEnv;
47     int i;
48
49     /* A quick hack to move the strings out of the heap */
50     environ = envp;
51     if (copyenv() != 0) {
52         environ = old;
53         return -1;
54     }
55     /* Release the old space if we allocated it ourselves earlier */
56     if (dirtyOld) {
57         for (i = 0; old[i] != NULL; i++)
58             free(old[i]);
59         free(old);
60     }
61     return 0;
62 }
63
64 /* Copy initial environment into malloc arena */
65 int
66 copyenv(void)
67 {
68     char **new;
69     int i;
70
71     for (i = 0; environ[i] != NULL; i++)
72           ;
73
74     if ((new = (char **) malloc((i + 1) * sizeof(char *))) == NULL)
75          return -1;
76
77     new[i] = NULL;
78
79     while (--i >= 0) {
80         if ((new[i] = strDup(environ[i])) == NULL) {
81             while (new[++i] != NULL)
82                 free(new[i]);
83             free(new);
84             return -1;
85         }
86     }
87     environ = new;
88     dirtyEnv = 1;
89     return 0;
90 }
91
92 /* Set or replace an environment variable 
93  * simonm 14/2/96 - this is different to the standard C library 
94  * implementation and the prototypes clash, so I'm calling it _setenv.
95  */
96 int
97 _setenv(char *mapping)
98 {
99     int i, keylen;
100     char *p;
101     char **new;
102
103     /* We must have a non-empty key and an '=' */
104     if (mapping[0] == '=' || (p = strchr(mapping, '=')) == NULL) {
105         errno = EINVAL;
106         return -1;
107     }
108     /* Include through the '=' for matching */
109     keylen = p - mapping + 1;
110
111     if (!dirtyEnv && copyenv() != 0)
112         return -1;
113
114     if ((p = strDup(mapping)) == NULL)
115         return -1;
116
117     /* Look for an existing key that matches */
118     for (i = 0; environ[i] != NULL && strncmp(environ[i], p, keylen) != 0; i++);
119
120     if (environ[i] != NULL) {
121         free(environ[i]);
122         environ[i] = p;
123     } else {
124         /* We want to grow the table by *two*, one for the new entry, one for the terminator */
125         if ((new = (char **) realloc((void*)environ, (i + 2) * sizeof(char *))) == NULL) {
126             free(p);
127             return -1;
128         }
129         new[i] = p;
130         new[i + 1] = NULL;
131         environ = new;
132     }
133     return 0;
134 }
135
136 /* Delete a variable from the environment */
137 int
138 delenv(char *name)
139 {
140     int i, keylen;
141
142     if (strchr(name, '=') != NULL) {
143         errno = EINVAL;
144         return -1;
145     }
146     keylen = strlen(name);
147
148     if (!dirtyEnv && copyenv() != 0)
149         return -1;
150
151     /* Look for a matching key */
152     for (i = 0; environ[i] != NULL &&
153       (strncmp(environ[i], name, keylen) != 0 || environ[i][keylen] != '='); i++);
154
155     /* Don't complain if it wasn't there to begin with */
156     if (environ[i] == NULL) {
157         return 0;
158     }
159     free(environ[i]);
160
161     do {
162         environ[i] = environ[i + 1];
163         i++;
164     } while (environ[i] != NULL);
165
166     return 0;
167 }