putenv should return success on deletion of nonexistent variable.
[reactos.git] / reactos / lib / crt / misc / environ.c
1 /* $Id$
2 *
3 * dllmain.c
4 *
5 * ReactOS MSVCRT.DLL Compatibility Library
6 */
7
8 #include <precomp.h>
9 #include <internal/tls.h>
10 #include <stdlib.h>
11 #include <string.h>
12
13 #define NDEBUG
14 #include <internal/debug.h>
15
16
17 unsigned int _osver = 0;
18 unsigned int _winminor = 0;
19 unsigned int _winmajor = 0;
20 unsigned int _winver = 0;
21
22
23 char *_acmdln = NULL; /* pointer to ascii command line */
24 wchar_t *_wcmdln = NULL; /* pointer to wide character command line */
25 #undef _environ
26 #undef _wenviron
27 char **_environ = NULL; /* pointer to environment block */
28 wchar_t **_wenviron = NULL; /* pointer to environment block */
29 char **__initenv = NULL; /* pointer to initial environment block */
30 wchar_t **__winitenv = NULL; /* pointer to initial environment block */
31 #undef _pgmptr
32 char *_pgmptr = NULL; /* pointer to program name */
33 #undef _wpgmptr
34 wchar_t *_wpgmptr = NULL; /* pointer to program name */
35 int __app_type = 0; //_UNKNOWN_APP; /* application type */
36 int __mb_cur_max = 1;
37
38 int _commode = _IOCOMMIT;
39
40
41 int BlockEnvToEnvironA(void)
42 {
43 char *ptr, *environment_strings;
44 char **envptr;
45 int count = 1, len;
46
47 DPRINT("BlockEnvToEnvironA()\n");
48
49 environment_strings = GetEnvironmentStringsA();
50 if (environment_strings == NULL) {
51 return -1;
52 }
53
54 for (ptr = environment_strings; *ptr; ptr += len)
55 {
56 len = strlen(ptr) + 1;
57 /* Skip drive letter settings. */
58 if (*ptr != '=')
59 count++;
60 }
61
62 __initenv = _environ = malloc(count * sizeof(char*));
63 if (_environ)
64 {
65 for (ptr = environment_strings, envptr = _environ; count > 1; ptr += len)
66 {
67 len = strlen(ptr) + 1;
68 /* Skip drive letter settings. */
69 if (*ptr != '=')
70 {
71 if ((*envptr = malloc(len)) == NULL)
72 {
73 for (envptr--; envptr >= _environ; envptr--);
74 free(*envptr);
75 FreeEnvironmentStringsA(environment_strings);
76 free(_environ);
77 __initenv = _environ = NULL;
78 return -1;
79 }
80 memcpy(*envptr++, ptr, len);
81 count--;
82 }
83 }
84 /* Add terminating NULL entry. */
85 *envptr = NULL;
86 }
87
88 FreeEnvironmentStringsA(environment_strings);
89 return _environ ? 0 : -1;
90 }
91
92 int BlockEnvToEnvironW(void)
93 {
94 wchar_t *ptr, *environment_strings;
95 wchar_t **envptr;
96 int count = 1, len;
97
98 DPRINT("BlockEnvToEnvironW()\n");
99
100 environment_strings = GetEnvironmentStringsW();
101 if (environment_strings == NULL) {
102 return -1;
103 }
104
105 for (ptr = environment_strings; *ptr; ptr += len)
106 {
107 len = wcslen(ptr) + 1;
108 /* Skip drive letter settings. */
109 if (*ptr != '=')
110 count++;
111 }
112
113 __winitenv = _wenviron = malloc(count * sizeof(wchar_t*));
114 if (_wenviron)
115 {
116 for (ptr = environment_strings, envptr = _wenviron; count > 1; ptr += len)
117 {
118 len = wcslen(ptr) + 1;
119 /* Skip drive letter settings. */
120 if (*ptr != '=')
121 {
122 if ((*envptr = malloc(len * sizeof(wchar_t))) == NULL)
123 {
124 for (envptr--; envptr >= _wenviron; envptr--);
125 free(*envptr);
126 FreeEnvironmentStringsW(environment_strings);
127 free(_wenviron);
128 __winitenv = _wenviron = NULL;
129 return -1;
130 }
131 memcpy(*envptr++, ptr, len * sizeof(wchar_t));
132 count--;
133 }
134 }
135 /* Add terminating NULL entry. */
136 *envptr = NULL;
137 }
138
139 FreeEnvironmentStringsW(environment_strings);
140 return _wenviron ? 0 : -1;
141 }
142
143 /**
144 * Internal function to duplicate environment block. Although it's
145 * parameter are defined as char**, it's able to work also with
146 * wide character environment block which are of type wchar_t**.
147 *
148 * @param original_environment
149 * Environment to duplicate.
150 * @param wide
151 * Set to zero for multibyte environments, non-zero otherwise.
152 *
153 * @return Original environment in case of failure, otherwise
154 * pointer to new environment block.
155 */
156 char **DuplicateEnvironment(char **original_environment, int wide)
157 {
158 int count = 1;
159 char **envptr, **newenvptr, **newenv;
160
161 for (envptr = original_environment; *envptr != NULL; envptr++, count++)
162 ;
163
164 newenvptr = newenv = malloc(count * sizeof(char*));
165 if (newenv == NULL)
166 return original_environment;
167
168 for (envptr = original_environment; count > 1; newenvptr++, count--)
169 {
170 if (wide)
171 *newenvptr = (char*)_wcsdup((wchar_t*)*envptr++);
172 else
173 *newenvptr = _strdup(*envptr++);
174 if (*newenvptr == NULL)
175 {
176 for (newenvptr--; newenvptr >= newenv; newenvptr--);
177 free(*newenvptr);
178 free(newenv);
179 return original_environment;
180 }
181 }
182 *newenvptr = NULL;
183
184 return newenv;
185 }
186
187 /**
188 * Internal function to deallocate environment block. Although it's
189 * parameter are defined as char**, it's able to work also with
190 * wide character environment block which are of type wchar_t**.
191 *
192 * @param environment
193 * Environment to free.
194 */
195 void FreeEnvironment(char **environment)
196 {
197 char **envptr;
198 for (envptr = environment; *envptr != NULL; envptr++)
199 free(*envptr);
200 free(environment);
201 }
202
203 /**
204 * Internal version of _wputenv and _putenv. It works duplicates the
205 * original envirnments created during initilization if needed to prevent
206 * having spurious pointers floating around. Then it updates the internal
207 * environment tables (_environ and _wenviron) and at last updates the
208 * OS environemnt.
209 *
210 * Note that there can happen situation when the internal [_w]environ
211 * arrays will be updated, but the OS environment update will fail. In
212 * this case we don't undo the changes to the [_w]environ tables to
213 * comply with the Microsoft behaviour (and it's also much easier :-).
214 */
215 int SetEnv(const wchar_t *option)
216 {
217 wchar_t *epos, *name;
218 wchar_t **wenvptr;
219 wchar_t *woption;
220 char *mboption;
221 int remove, index, count, size, result = 0, found = 0;
222
223 if (option == NULL || (epos = wcschr(option, L'=')) == NULL)
224 return -1;
225 remove = (epos[1] == 0);
226
227 /* Duplicate environment if needed. */
228 if (_environ == __initenv)
229 {
230 if ((_environ = DuplicateEnvironment(_environ, 0)) == __initenv)
231 return -1;
232 }
233 if (_wenviron == __winitenv)
234 {
235 if ((_wenviron = (wchar_t**)DuplicateEnvironment((char**)_wenviron, 1)) ==
236 __winitenv)
237 return -1;
238 }
239
240 /* Create a copy of the option name. */
241 name = malloc((epos - option + 1) * sizeof(wchar_t));
242 if (name == NULL)
243 return -1;
244 memcpy(name, option, (epos - option) * sizeof(wchar_t));
245 name[epos - option] = 0;
246
247 /* Find the option we're trying to modify. */
248 for (index = 0, wenvptr = _wenviron; *wenvptr != NULL; wenvptr++, index++)
249 {
250 if (!_wcsnicmp(*wenvptr, option, epos - option))
251 {
252 found = 1;
253 break;
254 }
255 }
256
257 if (remove)
258 {
259 if (!found)
260 {
261 free(name);
262 return 0;
263 }
264
265 /* Remove the option from wide character environment. */
266 free(*wenvptr);
267 for (count = index; *wenvptr != NULL; wenvptr++, count++)
268 *wenvptr = *(wenvptr + 1);
269 _wenviron = realloc(_wenviron, count * sizeof(wchar_t*));
270
271 /* Remove the option from multibyte environment. We assume
272 * the environments are in sync and the option is at the
273 * same position. */
274 free(_environ[index]);
275 memmove(&_environ[index], &_environ[index+1], (count - index) * sizeof(char*));
276 _environ = realloc(_environ, count * sizeof(char*));
277
278 result = SetEnvironmentVariableW(name, NULL) ? 0 : -1;
279 }
280 else
281 {
282 /* Make a copy of the option that we will store in the environment block. */
283 woption = _wcsdup((wchar_t*)option);
284 if (woption == NULL)
285 {
286 free(name);
287 return -1;
288 }
289
290 /* Create a multibyte copy of the option. */
291 size = WideCharToMultiByte(CP_ACP, 0, option, -1, NULL, 0, NULL, NULL);
292 mboption = malloc(size);
293 if (mboption == NULL)
294 {
295 free(name);
296 free(woption);
297 return -1;
298 }
299 WideCharToMultiByte(CP_ACP, 0, option, -1, mboption, size, NULL, NULL);
300
301 if (found)
302 {
303 /* Replace the current entry. */
304 free(*wenvptr);
305 *wenvptr = woption;
306 free(_environ[index]);
307 _environ[index] = mboption;
308 }
309 else
310 {
311 wchar_t **wnewenv;
312 char **mbnewenv;
313
314 /* Get the size of the original environment. */
315 for (count = index; *wenvptr != NULL; wenvptr++, count++)
316 ;
317
318 /* Create a new entry. */
319 if ((wnewenv = realloc(_wenviron, (count + 2) * sizeof(wchar_t*))) == NULL)
320 {
321 free(name);
322 free(mboption);
323 free(woption);
324 return -1;
325 }
326 _wenviron = wnewenv;
327 if ((mbnewenv = realloc(_environ, (count + 2) * sizeof(char*))) == NULL)
328 {
329 free(name);
330 free(mboption);
331 free(woption);
332 return -1;
333 }
334 _environ = mbnewenv;
335
336 /* Set the last entry to our option. */
337 _wenviron[count] = woption;
338 _environ[count] = mboption;
339 _wenviron[count + 1] = NULL;
340 _environ[count + 1] = NULL;
341 }
342
343 /* And finally update the OS environment. */
344 result = SetEnvironmentVariableW(name, epos + 1) ? 0 : -1;
345 }
346 free(name);
347
348 return result;
349 }
350
351 /*
352 * @implemented
353 */
354 int *__p__commode(void) // not exported by NTDLL
355 {
356 return &_commode;
357 }
358
359 /*
360 * @implemented
361 */
362 void __set_app_type(int app_type)
363 {
364 __app_type = app_type;
365 }
366
367 /*
368 * @implemented
369 */
370 char **__p__acmdln(void)
371 {
372 return &_acmdln;
373 }
374
375 /*
376 * @implemented
377 */
378 wchar_t **__p__wcmdln(void)
379 {
380 return &_wcmdln;
381 }
382
383 /*
384 * @implemented
385 */
386 char ***__p__environ(void)
387 {
388 return &_environ;
389 }
390
391 /*
392 * @implemented
393 */
394 wchar_t ***__p__wenviron(void)
395 {
396 return &_wenviron;
397 }
398
399 /*
400 * @implemented
401 */
402 char ***__p___initenv(void)
403 {
404 return &__initenv;
405 }
406
407 /*
408 * @implemented
409 */
410 wchar_t ***__p___winitenv(void)
411 {
412 return &__winitenv;
413 }
414
415 /*
416 * @implemented
417 */
418 int *__p___mb_cur_max(void)
419 {
420 return &__mb_cur_max;
421 }
422
423 /*
424 * @implemented
425 */
426 unsigned int *__p__osver(void)
427 {
428 return &_osver;
429 }
430
431 /*
432 * @implemented
433 */
434 char **__p__pgmptr(void)
435 {
436 return &_pgmptr;
437 }
438
439 /*
440 * @implemented
441 */
442 wchar_t **__p__wpgmptr(void)
443 {
444 return &_wpgmptr;
445 }
446
447 /*
448 * @implemented
449 */
450 unsigned int *__p__winmajor(void)
451 {
452 return &_winmajor;
453 }
454
455 /*
456 * @implemented
457 */
458 unsigned int *__p__winminor(void)
459 {
460 return &_winminor;
461 }
462
463 /*
464 * @implemented
465 */
466 unsigned int *__p__winver(void)
467 {
468 return &_winver;
469 }
470
471 /* EOF */