diff options
-rw-r--r-- | compat/mingw.c | 104 |
1 files changed, 65 insertions, 39 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index a99ebd081a..968e8d3c3e 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -906,13 +906,6 @@ static int environ_size = 0; /* allocated size of environ array, in bytes */ static int environ_alloc = 0; -static int compareenv(const void *a, const void *b) -{ - char *const *ea = a; - char *const *eb = b; - return strcasecmp(*ea, *eb); -} - /* * Create environment block suitable for CreateProcess. Merges current * process environment and the supplied environment changes. @@ -934,9 +927,6 @@ static wchar_t *make_environment_block(char **deltaenv) for (i = 0; deltaenv && deltaenv[i]; i++) size = do_putenv(tmpenv, deltaenv[i], size, 0); - /* environment must be sorted */ - qsort(tmpenv, size - 1, sizeof(char*), compareenv); - /* create environment block from temporary environment */ for (i = 0; tmpenv[i]; i++) { size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */ @@ -1194,16 +1184,42 @@ int mingw_kill(pid_t pid, int sig) return -1; } -static int lookupenv(char **env, const char *name, size_t nmln) -{ - int i; +/* + * Compare environment entries by key (i.e. stopping at '=' or '\0'). + */ +static int compareenv(const void *v1, const void *v2) +{ + const char *e1 = *(const char**)v1; + const char *e2 = *(const char**)v2; + + for (;;) { + int c1 = *e1++; + int c2 = *e2++; + c1 = (c1 == '=') ? 0 : tolower(c1); + c2 = (c2 == '=') ? 0 : tolower(c2); + if (c1 > c2) + return 1; + if (c1 < c2) + return -1; + if (c1 == 0) + return 0; + } +} - for (i = 0; env[i]; i++) { - if (!strncasecmp(env[i], name, nmln) && '=' == env[i][nmln]) - /* matches */ - return i; +static int bsearchenv(char **env, const char *name, size_t size) +{ + unsigned low = 0, high = size; + while (low < high) { + unsigned mid = low + ((high - low) >> 1); + int cmp = compareenv(&env[mid], &name); + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid; + else + return mid; } - return -1; + return ~low; /* not found, return 1's complement of insert position */ } /* @@ -1213,39 +1229,46 @@ static int lookupenv(char **env, const char *name, size_t nmln) */ static int do_putenv(char **env, const char *name, int size, int free_old) { - char *eq = strchrnul(name, '='); - int i = lookupenv(env, name, eq-name); + int i = bsearchenv(env, name, size - 1); - if (i < 0) { - if (*eq) { - env[size - 1] = (char*) name; - env[size] = NULL; + /* optionally free removed / replaced entry */ + if (i >= 0 && free_old) + free(env[i]); + + if (strchr(name, '=')) { + /* if new value ('key=value') is specified, insert or replace entry */ + if (i < 0) { + i = ~i; + memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*)); size++; } - } - else { - if (free_old) - free(env[i]); - if (*eq) - env[i] = (char*) name; - else { - for (; env[i]; i++) - env[i] = env[i+1]; - size--; - } + env[i] = (char*) name; + } else if (i >= 0) { + /* otherwise ('key') remove existing entry */ + size--; + memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*)); } return size; } -#undef getenv +static char *do_getenv(const char *name) +{ + char *value; + int pos = bsearchenv(environ, name, environ_size - 1); + if (pos < 0) + return NULL; + value = strchr(environ[pos], '='); + return value ? &value[1] : NULL; +} + char *mingw_getenv(const char *name) { - char *result = getenv(name); + char *result = do_getenv(name); if (!result && !strcmp(name, "TMPDIR")) { /* on Windows it is TMP and TEMP */ - result = getenv("TMP"); + result = do_getenv("TMP"); if (!result) - result = getenv("TEMP"); + result = do_getenv("TEMP"); } return result; } @@ -2088,6 +2111,9 @@ void mingw_startup() environ[i] = NULL; free(buffer); + /* sort environment for O(log n) getenv / putenv */ + qsort(environ, i, sizeof(char*), compareenv); + /* initialize critical section for waitpid pinfo_t list */ InitializeCriticalSection(&pinfo_cs); |