summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compat/mingw.c104
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);