Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 904d58a

Browse files
kbleeskasal
authored andcommitted
Win32: keep the environment sorted
The Windows environment is sorted, keep it that way for O(log n) environment access. Change compareenv to compare only the keys, so that it can be used to find an entry irrespective of the value. Change lookupenv to binary seach for an entry. Return one's complement of the insert position if not found (libc's bsearch returns NULL). Replace MSVCRT's getenv with a minimal do_getenv based on the binary search function. Change do_putenv to insert new entries at the correct position. Simplify the function by swapping if conditions and using memmove instead of for loops. Move qsort from make_environment_block to mingw_startup. We still need to sort on startup to make sure that the environment is sorted according to our compareenv function (while Win32 / CreateProcess requires the environment block to be sorted case-insensitively, CreateProcess currently doesn't enforce this, and some applications such as bash just don't care). Note that environment functions are _not_ thread-safe and are not required to be so by POSIX, the application is responsible for synchronizing access to the environment. MSVCRT's getenv and our new getenv implementation are better than that in that they are thread-safe with respect to other getenv calls as long as the environment is not modified. Git's indiscriminate use of getenv in background threads currently requires this property. Signed-off-by: Karsten Blees <[email protected]>
1 parent d0f63e9 commit 904d58a

File tree

1 file changed

+63
-37
lines changed

1 file changed

+63
-37
lines changed

compat/mingw.c

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -713,23 +713,42 @@ char *mingw_getcwd(char *pointer, int len)
713713
return pointer;
714714
}
715715

716-
static int compareenv(const void *a, const void *b)
717-
{
718-
char *const *ea = a;
719-
char *const *eb = b;
720-
return strcasecmp(*ea, *eb);
716+
/*
717+
* Compare environment entries by key (i.e. stopping at '=' or '\0').
718+
*/
719+
static int compareenv(const void *v1, const void *v2)
720+
{
721+
const char *e1 = *(const char**)v1;
722+
const char *e2 = *(const char**)v2;
723+
724+
for (;;) {
725+
int c1 = *e1++;
726+
int c2 = *e2++;
727+
c1 = (c1 == '=') ? 0 : tolower(c1);
728+
c2 = (c2 == '=') ? 0 : tolower(c2);
729+
if (c1 > c2)
730+
return 1;
731+
if (c1 < c2)
732+
return -1;
733+
if (c1 == 0)
734+
return 0;
735+
}
721736
}
722737

723-
static int lookupenv(char **env, const char *name, size_t nmln)
738+
static int bsearchenv(char **env, const char *name, size_t size)
724739
{
725-
int i;
726-
727-
for (i = 0; env[i]; i++) {
728-
if (!strncasecmp(env[i], name, nmln) && '=' == env[i][nmln])
729-
/* matches */
730-
return i;
740+
unsigned low = 0, high = size;
741+
while (low < high) {
742+
unsigned mid = low + ((high - low) >> 1);
743+
int cmp = compareenv(&env[mid], &name);
744+
if (cmp < 0)
745+
low = mid + 1;
746+
else if (cmp > 0)
747+
high = mid;
748+
else
749+
return mid;
731750
}
732-
return -1;
751+
return ~low; /* not found, return 1's complement of insert position */
733752
}
734753

735754
/*
@@ -739,26 +758,24 @@ static int lookupenv(char **env, const char *name, size_t nmln)
739758
*/
740759
static int do_putenv(char **env, const char *name, int size, int free_old)
741760
{
742-
char *eq = strchrnul(name, '=');
743-
int i = lookupenv(env, name, eq-name);
761+
int i = bsearchenv(env, name, size - 1);
744762

745-
if (i < 0) {
746-
if (*eq) {
747-
env[size - 1] = (char*) name;
748-
env[size] = NULL;
763+
/* optionally free removed / replaced entry */
764+
if (i >= 0 && free_old)
765+
free(env[i]);
766+
767+
if (strchr(name, '=')) {
768+
/* if new value ('key=value') is specified, insert or replace entry */
769+
if (i < 0) {
770+
i = ~i;
771+
memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*));
749772
size++;
750773
}
751-
}
752-
else {
753-
if (free_old)
754-
free(env[i]);
755-
if (*eq)
756-
env[i] = (char*) name;
757-
else {
758-
for (; env[i]; i++)
759-
env[i] = env[i+1];
760-
size--;
761-
}
774+
env[i] = (char*) name;
775+
} else if (i >= 0) {
776+
/* otherwise ('key') remove existing entry */
777+
size--;
778+
memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*));
762779
}
763780
return size;
764781
}
@@ -768,15 +785,24 @@ static int environ_size = 0;
768785
/* allocated size of environ array, in bytes */
769786
static int environ_alloc = 0;
770787

771-
#undef getenv
788+
static char *do_getenv(const char *name)
789+
{
790+
char *value;
791+
int pos = bsearchenv(environ, name, environ_size - 1);
792+
if (pos < 0)
793+
return NULL;
794+
value = strchr(environ[pos], '=');
795+
return value ? &value[1] : NULL;
796+
}
797+
772798
char *mingw_getenv(const char *name)
773799
{
774-
char *result = getenv(name);
800+
char *result = do_getenv(name);
775801
if (!result && !strcmp(name, "TMPDIR")) {
776802
/* on Windows it is TMP and TEMP */
777-
result = getenv("TMP");
803+
result = do_getenv("TMP");
778804
if (!result)
779-
result = getenv("TEMP");
805+
result = do_getenv("TEMP");
780806
}
781807
else if (!result && !strcmp(name, "TERM")) {
782808
/* simulate TERM to enable auto-color (see color.c) */
@@ -995,9 +1021,6 @@ static wchar_t *make_environment_block(char **deltaenv)
9951021
for (i = 0; deltaenv && deltaenv[i]; i++)
9961022
size = do_putenv(tmpenv, deltaenv[i], size, 0);
9971023

998-
/* environment must be sorted */
999-
qsort(tmpenv, size - 1, sizeof(char*), compareenv);
1000-
10011024
/* create environment block from temporary environment */
10021025
for (i = 0; tmpenv[i]; i++) {
10031026
size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */
@@ -2062,6 +2085,9 @@ void mingw_startup()
20622085
environ[i] = NULL;
20632086
free(buffer);
20642087

2088+
/* sort environment for O(log n) getenv / putenv */
2089+
qsort(environ, i, sizeof(char*), compareenv);
2090+
20652091
/* initialize critical section for waitpid pinfo_t list */
20662092
InitializeCriticalSection(&pinfo_cs);
20672093

0 commit comments

Comments
 (0)