Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / environ.cc
blobd4cedcbdfe749921f787b56107882b3a0acf0d49
1 /* environ.cc: Cygwin-adopted functions from newlib to manipulate
2 process's environment.
4 This software is a copyrighted work licensed under the terms of the
5 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
6 details. */
8 #include "winsup.h"
9 #include <userenv.h>
10 #include <stdlib.h>
11 #include <wchar.h>
12 #include <wctype.h>
13 #include <ctype.h>
14 #include <locale.h>
15 #include <assert.h>
16 #include <sys/param.h>
17 #include <cygwin/version.h>
18 #include "pinfo.h"
19 #include "perprocess.h"
20 #include "path.h"
21 #include "cygerrno.h"
22 #include "fhandler.h"
23 #include "dtable.h"
24 #include "cygheap.h"
25 #include "cygtls.h"
26 #include "tls_pbuf.h"
27 #include "registry.h"
28 #include "environ.h"
29 #include "child_info.h"
30 #include "shared_info.h"
31 #include "ntdll.h"
33 /* If this is not NULL, it points to memory allocated by us. */
34 static char **lastenviron;
36 /* Parse CYGWIN options */
38 static NO_COPY bool export_settings = false;
40 enum settings
42 isfunc,
43 setdword,
44 setbool,
45 setbit
48 /* When BUF is:
49 null or empty: disables globbing
50 "ignorecase": enables case-insensitive globbing
51 anything else: enables case-sensitive globbing */
52 static void
53 glob_init (const char *buf)
55 if (!buf || !*buf)
57 allow_glob = false;
58 ignore_case_with_glob = false;
60 else if (ascii_strncasematch (buf, "ignorecase", 10))
62 allow_glob = true;
63 ignore_case_with_glob = true;
65 else
67 allow_glob = true;
68 ignore_case_with_glob = false;
72 static void
73 set_proc_retry (const char *buf)
75 child_info::retry_count = strtoul (buf, NULL, 0);
78 static void
79 set_winsymlinks (const char *buf)
81 if (!buf || !*buf)
82 allow_winsymlinks = WSYM_lnk;
83 else if (ascii_strncasematch (buf, "lnk", 3))
84 allow_winsymlinks = WSYM_lnk;
85 else if (ascii_strncasematch (buf, "sys", 3))
86 allow_winsymlinks = WSYM_sysfile;
87 /* Make sure to try native symlinks only on systems supporting them. */
88 else if (ascii_strncasematch (buf, "native", 6))
89 allow_winsymlinks = ascii_strcasematch (buf + 6, "strict")
90 ? WSYM_nativestrict : WSYM_native;
93 /* The structure below is used to set up an array which is used to
94 parse the CYGWIN environment variable or, if enabled, options from
95 the registry. */
96 static struct parse_thing
98 const char *name;
99 union parse_setting
101 bool *b;
102 DWORD *x;
103 int *i;
104 void (*func)(const char *);
105 } setting;
107 enum settings disposition;
108 char *remember;
109 union parse_values
111 DWORD i;
112 const char *s;
113 } values[2];
114 } known[] NO_COPY =
116 {"disable_pcon", {&disable_pcon}, setbool, NULL, {{false}, {true}}},
117 {"error_start", {func: error_start_init}, isfunc, NULL, {{0}, {0}}},
118 {"export", {&export_settings}, setbool, NULL, {{false}, {true}}},
119 {"glob", {func: glob_init}, isfunc, NULL, {{0}, {s: "normal"}}},
120 {"pipe_byte", {&pipe_byte}, setbool, NULL, {{false}, {true}}},
121 {"proc_retry", {func: set_proc_retry}, isfunc, NULL, {{0}, {5}}},
122 {"reset_com", {&reset_com}, setbool, NULL, {{false}, {true}}},
123 {"wincmdln", {&wincmdln}, setbool, NULL, {{false}, {true}}},
124 {"winjitdebug", {&winjitdebug}, setbool, NULL, {{false}, {true}}},
125 {"winsymlinks", {func: set_winsymlinks}, isfunc, NULL, {{0}, {0}}},
126 {NULL, {0}, setdword, 0, {{0}, {0}}}
129 /* Return a possibly-quoted token.
130 Returns NULL when no more tokens available. */
131 static char *
132 strbrk(char *&buf)
134 buf += strspn(buf, " \t");
135 if (!*buf)
136 return NULL;
137 char *tok = buf;
138 char *sep = buf + strcspn(buf, " \t");
139 char *quotestart = strchr(buf, '"');
140 if (!quotestart || quotestart > sep)
142 buf = sep + !!*sep; /* Don't point beyond EOS */
143 quotestart = NULL;
145 else
147 char *quote = quotestart;
148 sep = NULL;
149 while (!sep)
151 char *clquote = strchr (quote + 1, '"');
152 if (!clquote)
153 sep = strchr (quote, '\0');
154 else if (clquote[-1] != '\\')
155 sep = clquote;
156 else
158 memmove (clquote - 1, clquote, 1 + strchr (clquote, '\0') - clquote);
159 quote = clquote - 1;
162 buf = sep + 1;
163 memmove (quotestart, quotestart + 1, sep - quotestart);
164 sep--;
166 *sep = '\0';
167 return tok;
171 /* Parse a string of the form "something=stuff somethingelse=more-stuff",
172 silently ignoring unknown "somethings". */
173 static void
174 parse_options (const char *inbuf)
176 int istrue;
177 parse_thing *k;
179 if (inbuf == NULL)
181 tmp_pathbuf tp;
182 char *newbuf = tp.c_get ();
183 newbuf[0] = '\0';
184 for (k = known; k->name != NULL; k++)
185 if (k->remember)
187 strcat (strcat (newbuf, " "), k->remember);
188 free (k->remember);
189 k->remember = NULL;
192 if (export_settings)
194 debug_printf ("%s", newbuf + 1);
195 setenv ("CYGWIN", newbuf + 1, 1);
197 return;
200 char *buf = strcpy ((char *) alloca (strlen (inbuf) + 1), inbuf);
202 while (char *p = strbrk (buf))
204 char *keyword_here = p;
205 if (!(istrue = !ascii_strncasematch (p, "no", 2)))
206 p += 2;
207 else if (!(istrue = *p != '-'))
208 p++;
210 char ch, *eq;
211 if ((eq = strchr (p, '=')) != NULL || (eq = strchr (p, ':')) != NULL)
212 ch = *eq, *eq++ = '\0';
213 else
214 ch = 0;
216 for (parse_thing *k = known; k->name != NULL; k++)
217 if (ascii_strcasematch (p, k->name))
219 switch (k->disposition)
221 case isfunc:
222 k->setting.func ((!eq || !istrue) ? k->values[istrue].s : eq);
223 debug_printf ("%s (called func)", k->name);
224 break;
225 case setdword:
226 if (!istrue || !eq)
227 *k->setting.x = k->values[istrue].i;
228 else
229 *k->setting.x = strtol (eq, NULL, 0);
230 debug_printf ("%s %u", k->name, *k->setting.x);
231 break;
232 case setbool:
233 if (!istrue || !eq)
234 *k->setting.b = k->values[istrue].i;
235 else
236 *k->setting.b = !!strtol (eq, NULL, 0);
237 debug_printf ("%s%s", *k->setting.b ? "" : "no", k->name);
238 break;
239 case setbit:
240 *k->setting.x &= ~k->values[istrue].i;
241 if (istrue || (eq && strtol (eq, NULL, 0)))
242 *k->setting.x |= k->values[istrue].i;
243 debug_printf ("%s %x", k->name, *k->setting.x);
244 break;
247 int n = 0;
248 if (eq)
250 *--eq = ch;
251 n = eq - p;
253 p = strdup (keyword_here);
254 if (n > 0)
255 p[n] = ':';
256 k->remember = p;
257 break;
260 debug_printf ("returning");
263 /* Helper functions for the below environment variables which have to
264 be converted Win32<->POSIX. */
265 extern "C" ssize_t env_PATH_to_posix (const void *, void *, size_t);
267 ssize_t
268 env_plist_to_posix (const void *win32, void *posix, size_t size)
270 return cygwin_conv_path_list (CCP_WIN_A_TO_POSIX | CCP_RELATIVE, win32,
271 posix, size);
274 ssize_t
275 env_plist_to_win32 (const void *posix, void *win32, size_t size)
277 return cygwin_conv_path_list (CCP_POSIX_TO_WIN_A | CCP_RELATIVE, posix,
278 win32, size);
281 ssize_t
282 env_path_to_posix (const void *win32, void *posix, size_t size)
284 return cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, win32,
285 posix, size);
288 ssize_t
289 env_path_to_win32 (const void *posix, void *win32, size_t size)
291 return cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, posix,
292 win32, size);
295 #define NL(x) x, (sizeof (x) - 1)
296 /* List of names which are converted from dos to unix
297 on the way in and back again on the way out.
299 PATH needs to be here because CreateProcess uses it and gdb uses
300 CreateProcess. HOME is here because most shells use it and would be
301 confused by Windows style path names. */
302 static win_env conv_envvars[] =
304 {NL ("PATH="), NULL, NULL, env_PATH_to_posix, env_plist_to_win32, true},
305 {NL ("HOME="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
306 {NL ("LD_LIBRARY_PATH="), NULL, NULL,
307 env_plist_to_posix, env_plist_to_win32, true},
308 {NL ("TMPDIR="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
309 {NL ("TMP="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
310 {NL ("TEMP="), NULL, NULL, env_path_to_posix, env_path_to_win32, false},
311 {NULL, 0, NULL, NULL, 0, 0}
314 #define WC ((unsigned char) 1)
315 /* Note: You *must* fill in this array setting the ordinal value of the first
316 character of the above environment variable names to 1.
317 This table is intended to speed up lookup of these variables. */
319 static const unsigned char conv_start_chars[256] =
321 0, 0, 0, 0, 0, 0, 0, 0,
322 0, 0, 0, 0, 0, 0, 0, 0,
323 0, 0, 0, 0, 0, 0, 0, 0,
324 0, 0, 0, 0, 0, 0, 0, 0,
325 0, 0, 0, 0, 0, 0, 0, 0,
326 0, 0, 0, 0, 0, 0, 0, 0,
327 0, 0, 0, 0, 0, 0, 0, 0,
328 0, 0, 0, 0, 0, 0, 0, 0,
329 /* A B C D E F G */
330 0, 0, 0, 0, 0, 0, 0, 0,
331 /* 72 */
332 /* H I J K L M N O */
333 WC, 0, 0, 0, WC, 0, 0, 0,
334 /* 80 */
335 /* P Q R S T U V W */
336 WC, 0, 0, 0, WC, 0, 0, 0,
337 /* 88 */
338 /* x Y Z */
339 0, 0, 0, 0, 0, 0, 0, 0,
340 /* 96 */
341 /* a b c d e f g */
342 0, 0, 0, 0, 0, 0, 0, 0,
343 /* 104 */
344 /* h i j k l m n o */
345 WC, 0, 0, 0, WC, 0, 0, 0,
346 /* 112 */
347 /* p q r s t u v w */
348 WC, 0, 0, 0, WC, 0, 0, 0,
351 static inline char
352 match_first_char (const char *s, unsigned char m)
354 return conv_start_chars[*(unsigned char *)s] & m;
357 struct win_env&
358 win_env::operator = (struct win_env& x)
360 name = x.name;
361 namelen = x.namelen;
362 toposix = x.toposix;
363 towin32 = x.towin32;
364 immediate = false;
365 return *this;
368 win_env::~win_env ()
370 if (posix)
371 free (posix);
372 if (native)
373 free (native);
376 void
377 win_env::add_cache (const char *in_posix, const char *in_native)
379 posix = (char *) realloc (posix, strlen (in_posix) + 1);
380 strcpy (posix, in_posix);
381 if (in_native)
383 native = (char *) realloc (native, namelen + 1 + strlen (in_native));
384 stpcpy (stpcpy (native, name), in_native);
386 else
388 tmp_pathbuf tp;
389 char *buf = tp.c_get ();
390 towin32 (in_posix, buf, NT_MAX_PATH);
391 native = (char *) realloc (native, namelen + 1 + strlen (buf));
392 stpcpy (stpcpy (native, name), buf);
394 if (immediate && cygwin_finished_initializing)
396 wchar_t s[sys_mbstowcs (NULL, 0, native) + 1];
397 sys_mbstowcs (s, sizeof s, native);
398 /* Hack. Relies on affected variables only having ASCII names. */
399 s[namelen - 1] = L'\0';
400 SetEnvironmentVariableW (s, s + namelen);
402 debug_printf ("posix %s", posix);
403 debug_printf ("native %s", native);
407 /* Check for a "special" environment variable name. *env is the pointer
408 to the beginning of the environment variable name. *in_posix is any
409 known posix value for the environment variable. Returns a pointer to
410 the appropriate conversion structure. */
411 win_env *
412 getwinenv (const char *env, const char *in_posix, win_env *temp)
414 if (!match_first_char (env, WC))
415 return NULL;
417 for (int i = 0; conv_envvars[i].name != NULL; i++)
418 if (strncmp (env, conv_envvars[i].name, conv_envvars[i].namelen) == 0)
420 win_env *we = conv_envvars + i;
421 const char *val;
422 if (!environ || !(val = in_posix ?: getenv (we->name)))
423 debug_printf ("can't set native for %s since no environ yet",
424 we->name);
425 else if (!we->posix || strcmp (val, we->posix) != 0)
427 if (temp)
429 *temp = *we;
430 we = temp;
432 we->add_cache (val);
434 return we;
436 return NULL;
439 /* Convert windows path specs to POSIX, if appropriate.
441 inline static void
442 posify_maybe (char **here, const char *value, char *outenv)
444 char *src = *here;
445 win_env *conv;
447 if (!(conv = getwinenv (src)))
448 return;
450 int len = strcspn (src, "=") + 1;
452 /* Turn all the items from c:<foo>;<bar> into their
453 mounted equivalents - if there is one. */
455 memcpy (outenv, src, len);
456 char *newvalue = outenv + len;
457 if (!conv->toposix (value, newvalue, NT_MAX_PATH - len) || errno != EIDRM)
458 conv->add_cache (newvalue, *value != '/' ? value : NULL);
459 else
461 /* The conversion routine removed elements from a path list so we have
462 to recalculate the windows path to remove elements there, too. */
463 tmp_pathbuf tp;
464 char *cleanvalue = tp.c_get ();
465 conv->towin32 (newvalue, cleanvalue, NT_MAX_PATH);
466 conv->add_cache (newvalue, cleanvalue);
469 debug_printf ("env var converted to %s", outenv);
470 *here = strdup (outenv);
471 free (src);
474 /* Returns pointer to value associated with name, if any, else NULL.
475 Sets offset to be the offset of the name/value combination in the
476 environment array, for use by setenv(3) and unsetenv(3).
477 Explicitly removes '=' in argument name. */
479 static char *
480 my_findenv (const char *name, int *offset)
482 int len;
483 char **p;
484 const char *c;
486 if (!environ)
487 return NULL;
489 c = name;
490 len = 0;
491 while (*c && *c != '=')
493 c++;
494 len++;
497 for (p = environ; *p; ++p)
498 if (!strncmp (*p, name, len))
499 if (*(c = *p + len) == '=')
501 *offset = p - environ;
502 return (char *) (++c);
504 return NULL;
507 /* Primitive getenv before the environment is built. */
509 static char *
510 getearly (const char * name, int *)
512 char *ret;
513 char **ptr;
514 int len;
516 if (spawn_info && (ptr = spawn_info->moreinfo->envp))
518 len = strlen (name);
519 for (; *ptr; ptr++)
520 if (strncasematch (name, *ptr, len) && (*ptr)[len] == '=')
521 return *ptr + len + 1;
523 else if ((len = GetEnvironmentVariableA (name, NULL, 0))
524 && (ret = (char *) cmalloc_abort (HEAP_2_STR, len))
525 && GetEnvironmentVariableA (name, ret, len))
526 return ret;
528 return NULL;
531 static char * (*findenv_func)(const char *, int *) = getearly;
533 /* Returns ptr to value associated with name, if any, else NULL. */
535 extern "C" char *
536 getenv (const char *name)
538 int offset;
539 return findenv_func (name, &offset);
542 /* This function is required so that newlib uses the same environment
543 as Cygwin. */
544 extern "C" char *
545 _getenv_r (struct _reent *, const char *name)
547 int offset;
548 return findenv_func (name, &offset);
551 /* Like getenv, but returns NULL if effective and real UID/GIDs do not match */
552 extern "C" char *
553 secure_getenv (const char *name)
555 int offset;
556 if (cygheap->user.issetuid ())
557 return NULL;
558 return findenv_func (name, &offset);
561 /* Return number of environment entries, including terminating NULL. */
562 static int
563 envsize (const char * const *in_envp)
565 const char * const *envp;
567 if (in_envp == NULL)
568 return 0;
570 for (envp = in_envp; *envp; envp++)
571 continue;
572 return 1 + envp - in_envp;
575 /* Takes similar arguments to setenv except that overwrite is
576 either -1, 0, or 1. 0 or 1 signify that the function should
577 perform similarly to setenv. Otherwise putenv is assumed. */
578 static int
579 _addenv (const char *name, const char *value, int overwrite)
581 int issetenv = overwrite >= 0;
582 int offset;
583 char *p;
585 unsigned int valuelen = strlen (value);
586 if ((p = my_findenv (name, &offset)))
587 { /* Already exists. */
588 if (!overwrite) /* Ok to overwrite? */
589 return 0; /* No. Wanted to add new value. FIXME: Right return value? */
591 /* We've found the offset into environ. If this is a setenv call and
592 there is room in the current environment entry then just overwrite it.
593 Otherwise handle this case below. */
594 if (issetenv && strlen (p) >= valuelen)
596 strcpy (p, value);
597 return 0;
600 else
601 { /* Create new slot. */
602 int sz = envsize (environ);
604 /* If sz == 0, we need two new slots, one for the terminating NULL. */
605 int newsz = sz == 0 ? 2 : sz + 1;
606 int allocsz = newsz * sizeof (char *);
608 offset = newsz - 2;
610 /* Allocate space for additional element. */
611 if (environ == lastenviron)
612 lastenviron = environ = (char **) realloc (lastenviron,
613 allocsz);
614 else if ((lastenviron = (char **) realloc (lastenviron, allocsz)) != NULL)
615 environ = (char **) memcpy (lastenviron, environ,
616 sz * sizeof (char *));
617 if (!lastenviron)
619 #ifdef DEBUGGING
620 try_to_debug ();
621 #endif
622 return -1; /* Oops. No more memory. */
625 environ[offset + 1] = NULL; /* NULL terminate. */
628 char *envhere;
629 if (!issetenv)
630 /* Not setenv. Just overwrite existing. */
631 envhere = environ[offset] = (char *) name;
632 else
633 { /* setenv */
634 /* Look for an '=' in the name and ignore anything after that if found. */
635 for (p = (char *) name; *p && *p != '='; p++)
636 continue;
638 int namelen = p - name; /* Length of name. */
639 /* Allocate enough space for name + '=' + value + '\0' */
640 envhere = environ[offset] = (char *) malloc (namelen + valuelen + 2);
641 if (!envhere)
642 return -1; /* Oops. No more memory. */
644 /* Put name '=' value into current slot. */
645 memcpy (envhere, name, namelen);
646 envhere[namelen] = '=';
647 strcpy (envhere + namelen + 1, value);
650 /* Update cygwin's cache, if appropriate */
651 win_env *spenv;
652 if ((spenv = getwinenv (envhere)))
653 spenv->add_cache (value);
654 if (strcmp (name, "CYGWIN") == 0)
655 parse_options (value);
657 return 0;
660 /* Set an environment variable */
661 extern "C" int
662 putenv (char *str)
664 __try
666 if (*str)
668 char *eq = strchr (str, '=');
669 if (eq)
670 return _addenv (str, eq + 1, -1);
672 /* Remove str from the environment. */
673 unsetenv (str);
675 return 0;
677 __except (EFAULT) {}
678 __endtry
679 return -1;
682 /* Set the value of the environment variable "name" to be
683 "value". If overwrite is set, replace any current value. */
684 extern "C" int
685 setenv (const char *name, const char *value, int overwrite)
687 __try
689 if (!name || !*name || strchr (name, '='))
691 set_errno (EINVAL);
692 __leave;
694 return _addenv (name, value, !!overwrite);
696 __except (EFAULT) {}
697 __endtry
698 return -1;
701 /* Delete environment variable "name". */
702 extern "C" int
703 unsetenv (const char *name)
705 char **e;
706 int offset;
708 __try
710 if (!name || *name == '\0' || strchr (name, '='))
712 set_errno (EINVAL);
713 __leave;
716 while (my_findenv (name, &offset)) /* if set multiple times */
717 /* Move up the rest of the array */
718 for (e = environ + offset; ; e++)
719 if (!(*e = *(e + 1)))
720 break;
722 return 0;
724 __except (EFAULT) {}
725 __endtry
726 return -1;
729 /* Clear the environment. */
730 extern "C" int
731 clearenv (void)
733 __try
735 if (environ == lastenviron)
737 free (lastenviron);
738 lastenviron = NULL;
740 environ = NULL;
741 return 0;
743 __except (EFAULT) {}
744 __endtry
745 return -1;
748 /* Minimal list of Windows vars which must be converted to uppercase.
749 Either for POSIX compatibility of for backward compatibility with
750 existing applications. */
751 static struct renv {
752 const char *name;
753 const size_t namelen;
754 } renv_arr[] = {
755 { NL("COMMONPROGRAMFILES=") }, // 0
756 { NL("COMSPEC=") },
757 { NL("PATH=") }, // 2
758 { NL("PROGRAMFILES=") },
759 { NL("SYSTEMDRIVE=") }, // 4
760 { NL("SYSTEMROOT=") },
761 { NL("TEMP=") }, // 6
762 { NL("TMP=") },
763 { NL("WINDIR=") } // 8
765 #define RENV_SIZE (sizeof (renv_arr) / sizeof (renv_arr[0]))
767 /* Set of first characters of the above list of variables. */
768 static const char idx_arr[] = "CPSTW";
769 /* Index into renv_arr at which the variables with this specific character
770 starts. */
771 static const int start_at[] = { 0, 2, 4, 6, 8 };
773 /* Turn environment variable part of a=b string into uppercase - for some
774 environment variables only. */
775 static __inline__ void
776 ucenv (char *p, const char *eq)
778 /* Hopefully as quickly as possible - only upper case specific set of important
779 Windows variables. */
780 char first = cyg_toupper (*p);
781 const char *idx = strchr (idx_arr, first);
782 if (idx)
783 for (size_t i = start_at[idx - idx_arr];
784 i < RENV_SIZE && renv_arr[i].name[0] == first;
785 ++i)
786 if (strncasematch (p, renv_arr[i].name, renv_arr[i].namelen))
788 strncpy (p, renv_arr[i].name, renv_arr[i].namelen);
789 break;
793 /* Initialize the environ array. Look for the CYGWIN environment variable and
794 set appropriate options from it. */
795 void
796 environ_init (char **envp, int envc)
798 PWCHAR rawenv;
799 char *p;
800 bool envp_passed_in;
802 __try
804 if (!envp)
805 envp_passed_in = 0;
806 else
808 envc++;
809 envc *= sizeof (char *);
810 char **newenv = (char **) malloc (envc);
811 memcpy (newenv, envp, envc);
812 cfree (envp);
814 /* Older applications relied on the fact that cygwin malloced elements of the
815 environment list. */
816 envp = newenv;
817 envp_passed_in = 1;
818 goto out;
821 rawenv = GetEnvironmentStringsW ();
822 if (!rawenv)
824 system_printf ("GetEnvironmentStrings returned NULL, %E");
825 return;
827 debug_printf ("GetEnvironmentStrings returned %p", rawenv);
829 lastenviron = envp = win32env_to_cygenv (rawenv, true);
831 FreeEnvironmentStringsW (rawenv);
833 out:
834 findenv_func = (char * (*)(const char*, int*)) my_findenv;
835 environ = envp;
836 dumper_init ();
837 if (envp_passed_in)
839 p = getenv ("CYGWIN");
840 if (p)
841 parse_options (p);
844 __except (NO_ERROR)
846 api_fatal ("internal error reading the windows environment "
847 "- too many environment variables?");
849 __endtry
852 int sawTERM = 0;
854 char **
855 win32env_to_cygenv (PWCHAR rawenv, bool posify)
857 tmp_pathbuf tp;
858 char **envp;
859 int envc;
860 char *newp;
861 int i;
862 const char cygterm[] = "TERM=cygwin";
863 const char xterm[] = "TERM=xterm-256color";
864 char *tmpbuf = tp.t_get ();
865 PWCHAR w;
867 /* Allocate space for environment + trailing NULL + CYGWIN env. */
868 envp = (char **) malloc ((4 + (envc = 100)) * sizeof (char *));
870 /* Current directory information is recorded as variables of the
871 form "=X:=X:\foo\bar; these must be changed into something legal
872 (we could just ignore them but maybe an application will
873 eventually want to use them). */
874 for (i = 0, w = rawenv; *w != L'\0'; w = wcschr (w, L'\0') + 1, i++)
876 sys_wcstombs_alloc_no_path (&newp, HEAP_NOTHEAP, w);
877 if (i >= envc)
878 envp = (char **) realloc (envp, (4 + (envc += 100)) * sizeof (char *));
879 envp[i] = newp;
880 if (*newp == '=')
881 *newp = '!';
882 char *eq = strchrnul (newp, '=');
883 ucenv (newp, eq); /* uppercase env vars which need it */
884 if (*newp == 'T' && strncmp (newp, "TERM=", 5) == 0)
885 sawTERM = 1;
886 else if (*newp == 'C' && strncmp (newp, "CYGWIN=", 7) == 0)
887 parse_options (newp + 7);
888 if (*eq && posify)
889 posify_maybe (envp + i, *++eq ? eq : --eq, tmpbuf);
890 debug_printf ("%p: %s", envp[i], envp[i]);
893 /* If console has 24 bit color capability, TERM=xterm-256color,
894 otherwise, TERM=cygwin */
895 if (!sawTERM)
896 envp[i++] = strdup (wincap.has_con_24bit_colors () ? xterm : cygterm);
898 envp[i] = NULL;
899 return envp;
902 /* Function called by qsort to sort environment strings. */
903 static int
904 env_sort (const void *a, const void *b)
906 const char **p = (const char **) a;
907 const char **q = (const char **) b;
909 return strcmp (*p, *q);
912 char *
913 getwinenveq (const char *name, size_t namelen, int x)
915 WCHAR name0[namelen - 1];
916 WCHAR valbuf[32768]; /* Max size of an env.var including trailing '\0'. */
918 name0[sys_mbstowcs (name0, sizeof name0, name, namelen - 1)] = L'\0';
919 int totlen = GetEnvironmentVariableW (name0, valbuf, 32768);
920 if (totlen > 0)
922 totlen = sys_wcstombs_no_path (NULL, 0, valbuf) + 1;
923 if (x == HEAP_1_STR)
924 totlen += namelen;
925 else
926 namelen = 0;
927 char *p = (char *) cmalloc_abort ((cygheap_types) x, totlen);
928 if (namelen)
929 strcpy (p, name);
930 sys_wcstombs_no_path (p + namelen, totlen, valbuf);
931 debug_printf ("using value from GetEnvironmentVariable for '%W'", name0);
932 return p;
935 debug_printf ("warning: %s not present in environment", name);
936 return NULL;
939 struct spenv
941 const char *name;
942 size_t namelen;
943 bool force_into_environment; /* If true, always add to env if missing */
944 bool add_if_exists; /* if true, retrieve value from cache */
945 const char * (cygheap_user::*from_cygheap) (const char *, size_t);
947 char *retrieve (bool, const char * const = NULL);
950 #define env_dontadd almost_null
952 /* Keep this list in upper case and sorted */
953 static NO_COPY spenv spenvs[] =
955 #ifdef DEBUGGING
956 {NL ("CYGWIN_DEBUG="), false, true, NULL},
957 #endif
958 {NL ("HOMEDRIVE="), false, false, &cygheap_user::env_homedrive},
959 {NL ("HOMEPATH="), false, false, &cygheap_user::env_homepath},
960 {NL ("LOGONSERVER="), false, false, &cygheap_user::env_logsrv},
961 {NL ("PATH="), false, true, NULL},
962 {NL ("SYSTEMDRIVE="), false, true, NULL},
963 {NL ("SYSTEMROOT="), true, true, &cygheap_user::env_systemroot},
964 {NL ("USERDOMAIN="), false, false, &cygheap_user::env_domain},
965 {NL ("USERNAME="), false, false, &cygheap_user::env_name},
966 {NL ("USERPROFILE="), false, false, &cygheap_user::env_userprofile},
967 {NL ("WINDIR="), true, true, &cygheap_user::env_systemroot}
970 char *
971 spenv::retrieve (bool no_envblock, const char *const env)
973 if (env && !ascii_strncasematch (env, name, namelen))
974 return NULL;
976 debug_printf ("no_envblock %d", no_envblock);
978 if (from_cygheap)
980 const char *p;
981 if (env && !cygheap->user.issetuid ())
983 debug_printf ("duping existing value for '%s'", name);
984 /* Don't really care what it's set to if we're calling a cygwin program */
985 return cstrdup1 (env);
988 /* Calculate (potentially) value for given environment variable. */
989 p = (cygheap->user.*from_cygheap) (name, namelen);
990 if (!p || (no_envblock && !env) || (p == env_dontadd))
991 return env_dontadd;
992 char *s = (char *) cmalloc_abort (HEAP_1_STR, namelen + strlen (p) + 1);
993 strcpy (s, name);
994 strcpy (s + namelen, p);
995 debug_printf ("using computed value for '%s'", name);
996 return s;
999 if (env)
1000 return cstrdup1 (env);
1002 return getwinenveq (name, namelen, HEAP_1_STR);
1005 static inline int
1006 raise_envblock (int new_tl, PWCHAR &envblock, PWCHAR &s)
1008 int tl = new_tl + 100;
1009 PWCHAR new_envblock =
1010 (PWCHAR) realloc (envblock, (2 + tl) * sizeof (WCHAR));
1011 /* If realloc moves the block, move `s' with it. */
1012 if (new_envblock != envblock)
1014 s += new_envblock - envblock;
1015 envblock = new_envblock;
1017 return tl;
1020 #define SPENVS_SIZE (sizeof (spenvs) / sizeof (spenvs[0]))
1023 env_compare (const void *key, const void *memb)
1025 const char *k = *(const char **) key;
1026 const char *m = *(const char **) memb;
1028 char *ke = strchr (k, '=');
1029 char *me = strchr (m, '=');
1030 if (ke == NULL || me == NULL)
1031 return strcasecmp (k, m);
1032 int ret = strncasecmp (k, m, MIN (ke - k, me - m));
1033 if (!ret)
1034 ret = (ke - k) - (me - m);
1035 return ret;
1038 /* Create a Windows-style environment block, i.e. a typical character buffer
1039 filled with null terminated strings, terminated by double null characters.
1040 Converts environment variables noted in conv_envvars into win32 form
1041 prior to placing them in the string.
1043 If new_token is set, we're going to switch the user account in
1044 child_info_spawn::worker. If so, we're also fetching the Windows default
1045 environment for the new user, and merge it into the environment we propage
1046 to the child. */
1047 char **
1048 build_env (const char * const *envp, PWCHAR &envblock, int &envc,
1049 bool no_envblock, HANDLE new_token)
1051 PWCHAR cwinenv = NULL;
1052 size_t winnum = 0;
1053 char **winenv = NULL;
1055 int len, n;
1056 const char * const *srcp;
1057 char **dstp;
1058 bool saw_spenv[SPENVS_SIZE] = {0};
1060 static char *const empty_env[] = { NULL };
1062 debug_printf ("envp %p", envp);
1064 if (!envp)
1065 envp = empty_env;
1067 /* How many elements? */
1068 for (n = 0; envp[n]; n++)
1069 continue;
1071 /* Fetch windows env and convert to POSIX-style env. */
1072 if (new_token
1073 && CreateEnvironmentBlock ((LPVOID *) &cwinenv, new_token, FALSE))
1075 PWCHAR var = cwinenv;
1076 while (*var)
1078 ++winnum;
1079 var = wcschr (var, L'\0') + 1;
1081 winenv = (char **) calloc (winnum + 1, sizeof (char *));
1082 if (winenv)
1084 for (winnum = 0, var = cwinenv;
1085 *var;
1086 ++winnum, var = wcschr (var, L'\0') + 1)
1087 sys_wcstombs_alloc_no_path (&winenv[winnum], HEAP_NOTHEAP, var);
1089 DestroyEnvironmentBlock (cwinenv);
1090 /* Eliminate variables which are already available in envp, as well as
1091 the small set of crucial variables needing POSIX conversion and
1092 potentially collide. The windows env is sorted, so we can use
1093 bsearch. We're doing this first step, so the following code doesn't
1094 allocate too much memory. */
1095 if (winenv)
1097 for (srcp = envp; *srcp; srcp++)
1099 char **elem = (char **) bsearch (srcp, winenv, winnum,
1100 sizeof *winenv, env_compare);
1101 if (elem)
1103 free (*elem);
1104 /* Use memmove to keep array sorted.
1105 winnum - (elem - winenv) copies all elements following
1106 elem, including the trailing NULL pointer. */
1107 memmove (elem, elem + 1,
1108 (winnum - (elem - winenv)) * sizeof *elem);
1109 --winnum;
1112 for (char **elem = winenv; *elem; elem++)
1114 if (match_first_char (*elem, WC))
1115 for (int i = 0; conv_envvars[i].name != NULL; i++)
1116 if (strncmp (*elem, conv_envvars[i].name,
1117 conv_envvars[i].namelen) == 0)
1119 free (*elem);
1120 memmove (elem, elem + 1,
1121 (winnum - (elem - winenv)) * sizeof *elem);
1122 --winnum;
1123 --elem;
1129 /* Allocate a new "argv-style" environ list with room for extra stuff. */
1130 char **newenv = (char **) cmalloc_abort (HEAP_1_ARGV, sizeof (char *) *
1131 (n + winnum + SPENVS_SIZE + 1));
1133 int tl = 0;
1134 char **pass_dstp;
1135 char **pass_env = (char **) alloca (sizeof (char *)
1136 * (n + winnum + SPENVS_SIZE + 1));
1137 /* Iterate over input list, generating a new environment list and refreshing
1138 "special" entries, if necessary. */
1139 for (srcp = envp, dstp = newenv, pass_dstp = pass_env; *srcp; srcp++)
1141 bool calc_tl = !no_envblock;
1142 /* Look for entries that require special attention */
1143 for (unsigned i = 0; i < SPENVS_SIZE; i++)
1144 if (!saw_spenv[i] && (*dstp = spenvs[i].retrieve (no_envblock, *srcp)))
1146 saw_spenv[i] = 1;
1147 if (*dstp == env_dontadd)
1148 goto next1;
1149 if (spenvs[i].add_if_exists)
1150 calc_tl = true;
1151 goto next0;
1154 /* Add entry to new environment */
1155 *dstp = cstrdup1 (*srcp);
1157 next0:
1158 if (calc_tl)
1160 *pass_dstp++ = *dstp;
1161 tl += strlen (*dstp) + 1;
1163 dstp++;
1164 next1:
1165 continue;
1168 assert ((srcp - envp) == n);
1169 /* Fill in any required-but-missing environment variables. */
1170 for (unsigned i = 0; i < SPENVS_SIZE; i++)
1171 if (!saw_spenv[i] && (spenvs[i].force_into_environment
1172 || cygheap->user.issetuid ()))
1174 *dstp = spenvs[i].retrieve (false);
1175 if (*dstp && *dstp != env_dontadd)
1177 *pass_dstp++ = *dstp;
1178 tl += strlen (*dstp) + 1;
1179 /* Eliminate from winenv. */
1180 if (winenv)
1182 char **elem = (char **) bsearch (dstp, winenv, winnum,
1183 sizeof *winenv, env_compare);
1184 if (elem)
1186 free (*elem);
1187 memmove (elem, elem + 1,
1188 (winnum - (elem - winenv)) * sizeof *elem);
1189 --winnum;
1192 dstp++;
1196 /* Fill in any Windows environment vars still missing. */
1197 if (winenv)
1199 char **elem;
1200 for (elem = winenv; *elem; ++elem)
1202 *dstp = cstrdup1 (*elem);
1203 free (*elem);
1204 *pass_dstp++ = *dstp;
1205 tl += strlen (*dstp) + 1;
1206 ++dstp;
1208 free (winenv);
1211 envc = dstp - newenv; /* Number of entries in newenv */
1212 assert ((size_t) envc <= (n + winnum + SPENVS_SIZE));
1213 *dstp = NULL; /* Terminate */
1215 size_t pass_envc = pass_dstp - pass_env;
1216 if (!pass_envc)
1217 envblock = NULL;
1218 else
1220 *pass_dstp = NULL;
1221 debug_printf ("env count %ld, bytes %d", pass_envc, tl);
1222 win_env temp;
1223 temp.reset ();
1225 /* Windows programs expect the environment block to be sorted. */
1226 qsort (pass_env, pass_envc, sizeof (char *), env_sort);
1228 /* Create an environment block suitable for passing to CreateProcess. */
1229 PWCHAR s;
1230 envblock = (PWCHAR) malloc ((2 + tl) * sizeof (WCHAR));
1231 int new_tl = 0;
1232 bool saw_PATH = false;
1233 for (srcp = pass_env, s = envblock; *srcp; srcp++)
1235 const char *p;
1236 win_env *conv;
1237 len = strcspn (*srcp, "=") + 1;
1238 const char *rest = *srcp + len;
1240 /* Check for a bad entry. This is necessary to get rid of empty
1241 strings, induced by putenv and changing the string afterwards.
1242 Note that this doesn't stop invalid strings without '=' in it
1243 etc., but we're opting for speed here for now. Adding complete
1244 checking would be pretty expensive. */
1245 if (len == 1 || !*rest)
1246 continue;
1248 /* See if this entry requires posix->win32 conversion. */
1249 conv = getwinenv (*srcp, rest, &temp);
1250 if (conv)
1252 p = conv->native; /* Use win32 path */
1253 /* Does PATH exist in the environment? */
1254 if (**srcp == 'P')
1256 /* And is it non-empty? */
1257 if (!conv->native || !conv->native[0])
1258 continue;
1259 saw_PATH = true;
1262 else
1263 p = *srcp; /* Don't worry about it */
1265 len = sys_mbstowcs (NULL, 0, p);
1266 new_tl += len; /* Keep running total of block length so far */
1268 /* See if we need to increase the size of the block. */
1269 if (new_tl > tl)
1270 tl = raise_envblock (new_tl, envblock, s);
1272 len = sys_mbstowcs (s, len, p);
1274 /* See if environment variable is "special" in a Windows sense.
1275 Under NT, the current directories for visited drives are stored
1276 as =C:=\bar. Cygwin converts the '=' to '!' for hopefully obvious
1277 reasons. We need to convert it back when building the envblock */
1278 if (s[0] == L'!' && (iswdrive (s + 1) || (s[1] == L':' && s[2] == L':'))
1279 && s[3] == L'=')
1280 *s = L'=';
1281 s += len + 1;
1283 /* If PATH doesn't exist in the environment, add a PATH with just
1284 Cygwin's bin dir to the Windows env to allow loading system DLLs
1285 during execve. */
1286 if (!saw_PATH)
1288 new_tl += cygheap->installation_dir.Length / sizeof (WCHAR) + 5 + 1;
1289 if (new_tl > tl)
1290 tl = raise_envblock (new_tl, envblock, s);
1291 s = wcpcpy (wcpcpy (s, L"PATH="),
1292 cygheap->installation_dir.Buffer) + 1;
1294 *s = L'\0'; /* Two null bytes at the end */
1295 assert ((s - envblock) <= tl); /* Detect if we somehow ran over end
1296 of buffer */
1299 debug_printf ("envp %p, envc %d", newenv, envc);
1300 return newenv;