From 9ae1afa5e659dc61602e8f50fe469c21a9704319 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:54 +0200 Subject: Revert "Windows: teach getenv to do a case-sensitive search" This reverts commit df599e9612788b728ce43a03159b85f1fe624d6a. As of 5e9637c6 "i18n: add infrastructure for translating Git with gettext", eval_gettext uses MinGW envsubst.exe instead of git-sh-i18n--envsubst.exe for variable substitution. This breaks git-submodule.sh messages and tests, as envsubst.exe doesn't support case-sensitive environment lookup (the same is true for almost everything on Windows, including MSys and Cygwin tools). 30a615ac "Windows/i18n: rename $path to prevent clashes with $PATH" renames the conflicting variable in git-submodule.sh, so that it works on Windows (i.e. with case-insensitive environment, regardless of the toolset). Revert to the documented behaviour of case-insensitive environment on Windows. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index c19e3d954b..ca1b6bd6ab 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1245,31 +1245,14 @@ char **make_augmented_environ(const char *const *vars) } #undef getenv - -/* - * The system's getenv looks up the name in a case-insensitive manner. - * This version tries a case-sensitive lookup and falls back to - * case-insensitive if nothing was found. This is necessary because, - * as a prominent example, CMD sets 'Path', but not 'PATH'. - * Warning: not thread-safe. - */ -static char *getenv_cs(const char *name) -{ - size_t len = strlen(name); - int i = lookup_env(environ, name, len); - if (i >= 0) - return environ[i] + len + 1; /* skip past name and '=' */ - return getenv(name); -} - char *mingw_getenv(const char *name) { - char *result = getenv_cs(name); + char *result = getenv(name); if (!result && !strcmp(name, "TMPDIR")) { /* on Windows it is TMP and TEMP */ - result = getenv_cs("TMP"); + result = getenv("TMP"); if (!result) - result = getenv_cs("TEMP"); + result = getenv("TEMP"); } return result; } -- cgit v1.2.3 From 7eb2619c5cce7034cca1cdd07a090cbe648a3911 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:55 +0200 Subject: Win32: Unicode environment (outgoing) Convert environment from UTF-8 to UTF-16 when creating other processes. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index ca1b6bd6ab..c725a3e206 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -4,6 +4,7 @@ #include #include "../strbuf.h" #include "../run-command.h" +#include "../cache.h" static const int delay[] = { 0, 1, 10, 20, 40 }; @@ -919,9 +920,9 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, { STARTUPINFOW si; PROCESS_INFORMATION pi; - struct strbuf envblk, args; - wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs; - unsigned flags; + struct strbuf args; + wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL; + unsigned flags = CREATE_UNICODE_ENVIRONMENT; BOOL ret; /* Determine whether or not we are associated to a console */ @@ -938,7 +939,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, * instead of CREATE_NO_WINDOW to make ssh * recognize that it has no console. */ - flags = DETACHED_PROCESS; + flags |= DETACHED_PROCESS; } else { /* There is already a console. If we specified * DETACHED_PROCESS here, too, Windows would @@ -946,7 +947,6 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, * The same is true for CREATE_NO_WINDOW. * Go figure! */ - flags = 0; CloseHandle(cons); } memset(&si, 0, sizeof(si)); @@ -985,6 +985,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, if (env) { int count = 0; char **e, **sorted_env; + int size = 0, wenvsz = 0, wenvpos = 0; for (e = env; *e; e++) count++; @@ -994,20 +995,22 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1)); qsort(sorted_env, count, sizeof(*sorted_env), env_compare); - strbuf_init(&envblk, 0); + /* create environment block from temporary environment */ for (e = sorted_env; *e; e++) { - strbuf_addstr(&envblk, *e); - strbuf_addch(&envblk, '\0'); + size = 2 * strlen(*e) + 2; /* +2 for final \0 */ + ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz); + wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1; } + /* add final \0 terminator */ + wenvblk[wenvpos] = 0; free(sorted_env); } memset(&pi, 0, sizeof(pi)); ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags, - env ? envblk.buf : NULL, dir ? wdir : NULL, &si, &pi); + wenvblk, dir ? wdir : NULL, &si, &pi); - if (env) - strbuf_release(&envblk); + free(wenvblk); free(wargs); if (!ret) { -- cgit v1.2.3 From b729f98fa50b10cfba7cbf3f37a0ac255e1fbdcd Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:56 +0200 Subject: Win32: Unicode environment (incoming) Convert environment from UTF-16 to UTF-8 on startup. No changes to getenv() are necessary, as the MSVCRT version is implemented on top of char **environ. However, putenv / _wputenv from MSVCRT no longer work, for two reasons: 1. they try to keep environ, _wenviron and the Win32 process environment in sync, using the default system encoding instead of UTF-8 to convert between charsets 2. msysgit and MSVCRT use different allocators, memory allocated in git cannot be freed by the CRT and vice versa Implement mingw_putenv using the env_setenv helper function from the environment merge code. Note that in case of memory allocation failure, putenv now dies with error message (due to xrealloc) instead of failing with ENOMEM. As git assumes setenv / putenv to always succeed, this prevents it from continuing with incorrect settings. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 15 +++++++++++++++ compat/mingw.h | 2 ++ 2 files changed, 17 insertions(+) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index c725a3e206..cb0914af6c 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1260,6 +1260,12 @@ char *mingw_getenv(const char *name) return result; } +int mingw_putenv(const char *namevalue) +{ + environ = env_setenv(environ, namevalue); + return 0; +} + /* * Note, this isn't a complete replacement for getaddrinfo. It assumes * that service contains a numerical port, or that it is null. It @@ -2052,6 +2058,11 @@ void mingw_startup() maxlen = wcslen(_wpgmptr); for (i = 1; i < argc; i++) maxlen = max(maxlen, wcslen(wargv[i])); + for (i = 0; wenv[i]; i++) + maxlen = max(maxlen, wcslen(wenv[i])); + + /* nedmalloc can't free CRT memory, allocate resizable environment list */ + environ = xcalloc(i + 1, sizeof(char*)); /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */ maxlen = 3 * maxlen + 1; @@ -2064,6 +2075,10 @@ void mingw_startup() len = xwcstoutf(buffer, wargv[i], maxlen); __argv[i] = xmemdupz(buffer, len); } + for (i = 0; wenv[i]; i++) { + len = xwcstoutf(buffer, wenv[i], maxlen); + environ[i] = xmemdupz(buffer, len); + } free(buffer); /* initialize critical section for waitpid pinfo_t list */ diff --git a/compat/mingw.h b/compat/mingw.h index 405c08fcc3..ca80be1bbe 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -207,6 +207,8 @@ char *mingw_getcwd(char *pointer, int len); char *mingw_getenv(const char *name); #define getenv mingw_getenv +int mingw_putenv(const char *namevalue); +#define putenv mingw_putenv int mingw_gethostname(char *host, int namelen); #define gethostname mingw_gethostname -- cgit v1.2.3 From e96942e821dec273b884fec378cc2a97a7f5d689 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:57 +0200 Subject: Win32: fix environment memory leaks All functions that modify the environment have memory leaks. Disable gitunsetenv in the Makefile and use env_setenv (via mingw_putenv) instead (this frees removed environment entries). Move xstrdup from env_setenv to make_augmented_environ, so that mingw_putenv no longer copies the environment entries (according to POSIX [1], "the string [...] shall become part of the environment"). This also fixes the memory leak in gitsetenv, which expects a POSIX compliant putenv. [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html Note: This patch depends on taking control of char **environ and having our own mingw_putenv (both introduced in "Win32: Unicode environment (incoming)"). Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 10 ++++++---- compat/mingw.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index cb0914af6c..f6c7ad7fd8 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1220,14 +1220,14 @@ static char **env_setenv(char **env, const char *name) for (i = 0; env[i]; i++) ; env = xrealloc(env, (i+2)*sizeof(*env)); - env[i] = xstrdup(name); + env[i] = (char*) name; env[i+1] = NULL; } } else { free(env[i]); if (*eq) - env[i] = xstrdup(name); + env[i] = (char*) name; else for (; env[i]; i++) env[i] = env[i+1]; @@ -1242,8 +1242,10 @@ char **make_augmented_environ(const char *const *vars) { char **env = copy_environ(); - while (*vars) - env = env_setenv(env, *vars++); + while (*vars) { + const char *v = *vars++; + env = env_setenv(env, strchr(v, '=') ? xstrdup(v) : v); + } return env; } diff --git a/compat/mingw.h b/compat/mingw.h index ca80be1bbe..828d97760c 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -209,6 +209,7 @@ char *mingw_getenv(const char *name); #define getenv mingw_getenv int mingw_putenv(const char *namevalue); #define putenv mingw_putenv +#define unsetenv mingw_putenv int mingw_gethostname(char *host, int namelen); #define gethostname mingw_gethostname -- cgit v1.2.3 From 38d2750126f326c21b06d63e7c21b05d3a6b74f7 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:58 +0200 Subject: Win32: unify environment case-sensitivity The environment on Windows is case-insensitive. Some environment functions (such as unsetenv and make_augmented_environ) have always used case- sensitive comparisons instead, while others (getenv, putenv, sorting in spawn*) were case-insensitive. Prevent potential inconsistencies by using case-insensitive comparison in lookup_env (used by putenv, unsetenv and make_augmented_environ). Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index f6c7ad7fd8..654bea4828 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1199,8 +1199,7 @@ static int lookup_env(char **env, const char *name, size_t nmln) int i; for (i = 0; env[i]; i++) { - if (0 == strncmp(env[i], name, nmln) - && '=' == env[i][nmln]) + if (!strncasecmp(env[i], name, nmln) && '=' == env[i][nmln]) /* matches */ return i; } -- cgit v1.2.3 From 26c7b21ab107bea5d7c308fa5c286af54dfd8cec Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:37:59 +0200 Subject: Win32: unify environment function names Environment helper functions use random naming ('env' prefix or suffix or both, with or without '_'). Change to POSIX naming scheme ('env' suffix, no '_'). Env_setenv has more in common with putenv than setenv. Change to do_putenv. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 654bea4828..cec3c2069b 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -899,7 +899,7 @@ static char *path_lookup(const char *cmd, char **path, int exe_only) return prog; } -static int env_compare(const void *a, const void *b) +static int compareenv(const void *a, const void *b) { char *const *ea = a; char *const *eb = b; @@ -993,7 +993,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, /* environment must be sorted */ sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1)); memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1)); - qsort(sorted_env, count, sizeof(*sorted_env), env_compare); + qsort(sorted_env, count, sizeof(*sorted_env), compareenv); /* create environment block from temporary environment */ for (e = sorted_env; *e; e++) { @@ -1194,7 +1194,7 @@ void free_environ(char **env) free(env); } -static int lookup_env(char **env, const char *name, size_t nmln) +static int lookupenv(char **env, const char *name, size_t nmln) { int i; @@ -1209,10 +1209,10 @@ static int lookup_env(char **env, const char *name, size_t nmln) /* * If name contains '=', then sets the variable, otherwise it unsets it */ -static char **env_setenv(char **env, const char *name) +static char **do_putenv(char **env, const char *name) { char *eq = strchrnul(name, '='); - int i = lookup_env(env, name, eq-name); + int i = lookupenv(env, name, eq-name); if (i < 0) { if (*eq) { @@ -1243,7 +1243,7 @@ char **make_augmented_environ(const char *const *vars) while (*vars) { const char *v = *vars++; - env = env_setenv(env, strchr(v, '=') ? xstrdup(v) : v); + env = do_putenv(env, strchr(v, '=') ? xstrdup(v) : v); } return env; } @@ -1263,7 +1263,7 @@ char *mingw_getenv(const char *name) int mingw_putenv(const char *namevalue) { - environ = env_setenv(environ, namevalue); + environ = do_putenv(environ, namevalue); return 0; } -- cgit v1.2.3 From df0e998c31a6d91b905b1ad5b3ff89435ba29313 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:00 +0200 Subject: Win32: factor out environment block creation Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 55 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 23 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index cec3c2069b..8c5cf903ba 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -906,6 +906,36 @@ static int compareenv(const void *a, const void *b) return strcasecmp(*ea, *eb); } +/* + * Create environment block suitable for CreateProcess. + */ +static wchar_t *make_environment_block(char **env) +{ + wchar_t *wenvblk = NULL; + int count = 0; + char **e, **tmpenv; + int size = 0, wenvsz = 0, wenvpos = 0; + + for (e = env; *e; e++) + count++; + + /* environment must be sorted */ + tmpenv = xmalloc(sizeof(*tmpenv) * (count + 1)); + memcpy(tmpenv, env, sizeof(*tmpenv) * (count + 1)); + qsort(tmpenv, count, sizeof(*tmpenv), compareenv); + + /* create environment block from temporary environment */ + for (e = tmpenv; *e; e++) { + size = 2 * strlen(*e) + 2; /* +2 for final \0 */ + ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz); + wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1; + } + /* add final \0 terminator */ + wenvblk[wenvpos] = 0; + free(tmpenv); + return wenvblk; +} + struct pinfo_t { struct pinfo_t *next; pid_t pid; @@ -982,29 +1012,8 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, xutftowcs(wargs, args.buf, 2 * args.len + 1); strbuf_release(&args); - if (env) { - int count = 0; - char **e, **sorted_env; - int size = 0, wenvsz = 0, wenvpos = 0; - - for (e = env; *e; e++) - count++; - - /* environment must be sorted */ - sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1)); - memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1)); - qsort(sorted_env, count, sizeof(*sorted_env), compareenv); - - /* create environment block from temporary environment */ - for (e = sorted_env; *e; e++) { - size = 2 * strlen(*e) + 2; /* +2 for final \0 */ - ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz); - wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1; - } - /* add final \0 terminator */ - wenvblk[wenvpos] = 0; - free(sorted_env); - } + if (env) + wenvblk = make_environment_block(env); memset(&pi, 0, sizeof(pi)); ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags, -- cgit v1.2.3 From 77734da241f97607eea8f2bb55e0e19937e918d4 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:01 +0200 Subject: Win32: don't copy the environment twice when spawning child processes When spawning child processes via start_command(), the environment and all environment entries are copied twice. First by make_augmented_environ / copy_environ to merge with child_process.env. Then a second time by make_environment_block to create a sorted environment block string as required by CreateProcess. Move the merge logic to make_environment_block so that we only need to copy the environment once. This changes semantics of the env parameter: it now expects a delta (such as child_process.env) rather than a full environment. This is not a problem as the parameter is only used by start_command() (all other callers previously passed char **environ, and now pass NULL). The merge logic no longer xstrdup()s the environment strings, so do_putenv must not free them. Add a parameter to distinguish this from normal putenv. Remove the now unused make_augmented_environ / free_environ API. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 76 ++++++++++++++++++++-------------------------------------- compat/mingw.h | 8 ++----- 2 files changed, 28 insertions(+), 56 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 8c5cf903ba..789a0ec6dc 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -899,6 +899,8 @@ static char *path_lookup(const char *cmd, char **path, int exe_only) return prog; } +static char **do_putenv(char **env, const char *name, int free_old); + static int compareenv(const void *a, const void *b) { char *const *ea = a; @@ -907,21 +909,30 @@ static int compareenv(const void *a, const void *b) } /* - * Create environment block suitable for CreateProcess. + * Create environment block suitable for CreateProcess. Merges current + * process environment and the supplied environment changes. */ -static wchar_t *make_environment_block(char **env) +static wchar_t *make_environment_block(char **deltaenv) { wchar_t *wenvblk = NULL; int count = 0; char **e, **tmpenv; int size = 0, wenvsz = 0, wenvpos = 0; - for (e = env; *e; e++) + while (environ[count]) count++; - /* environment must be sorted */ + /* copy the environment */ tmpenv = xmalloc(sizeof(*tmpenv) * (count + 1)); - memcpy(tmpenv, env, sizeof(*tmpenv) * (count + 1)); + memcpy(tmpenv, environ, sizeof(*tmpenv) * (count + 1)); + + /* merge supplied environment changes into the temporary environment */ + for (e = deltaenv; e && *e; e++) + tmpenv = do_putenv(tmpenv, *e, 0); + + /* environment must be sorted */ + for (count = 0; tmpenv[count]; ) + count++; qsort(tmpenv, count, sizeof(*tmpenv), compareenv); /* create environment block from temporary environment */ @@ -944,7 +955,7 @@ struct pinfo_t { static struct pinfo_t *pinfo = NULL; CRITICAL_SECTION pinfo_cs; -static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, +static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv, const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) { @@ -1012,8 +1023,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, xutftowcs(wargs, args.buf, 2 * args.len + 1); strbuf_release(&args); - if (env) - wenvblk = make_environment_block(env); + wenvblk = make_environment_block(deltaenv); memset(&pi, 0, sizeof(pi)); ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags, @@ -1051,10 +1061,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd) { - return mingw_spawnve_fd(cmd, argv, environ, NULL, prepend_cmd, 0, 1, 2); + return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2); } -pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env, +pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv, const char *dir, int fhin, int fhout, int fherr) { @@ -1078,14 +1088,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env, pid = -1; } else { - pid = mingw_spawnve_fd(iprog, argv, env, dir, 1, + pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1, fhin, fhout, fherr); free(iprog); } argv[0] = argv0; } else - pid = mingw_spawnve_fd(prog, argv, env, dir, 0, + pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0, fhin, fhout, fherr); free(prog); } @@ -1182,27 +1192,6 @@ int mingw_kill(pid_t pid, int sig) return -1; } -static char **copy_environ(void) -{ - char **env; - int i = 0; - while (environ[i]) - i++; - env = xmalloc((i+1)*sizeof(*env)); - for (i = 0; environ[i]; i++) - env[i] = xstrdup(environ[i]); - env[i] = NULL; - return env; -} - -void free_environ(char **env) -{ - int i; - for (i = 0; env[i]; i++) - free(env[i]); - free(env); -} - static int lookupenv(char **env, const char *name, size_t nmln) { int i; @@ -1218,7 +1207,7 @@ static int lookupenv(char **env, const char *name, size_t nmln) /* * If name contains '=', then sets the variable, otherwise it unsets it */ -static char **do_putenv(char **env, const char *name) +static char **do_putenv(char **env, const char *name, int free_old) { char *eq = strchrnul(name, '='); int i = lookupenv(env, name, eq-name); @@ -1233,7 +1222,8 @@ static char **do_putenv(char **env, const char *name) } } else { - free(env[i]); + if (free_old) + free(env[i]); if (*eq) env[i] = (char*) name; else @@ -1243,20 +1233,6 @@ static char **do_putenv(char **env, const char *name) return env; } -/* - * Copies global environ and adjusts variables as specified by vars. - */ -char **make_augmented_environ(const char *const *vars) -{ - char **env = copy_environ(); - - while (*vars) { - const char *v = *vars++; - env = do_putenv(env, strchr(v, '=') ? xstrdup(v) : v); - } - return env; -} - #undef getenv char *mingw_getenv(const char *name) { @@ -1272,7 +1248,7 @@ char *mingw_getenv(const char *name) int mingw_putenv(const char *namevalue) { - environ = do_putenv(environ, namevalue); + environ = do_putenv(environ, namevalue, 1); return 0; } diff --git a/compat/mingw.h b/compat/mingw.h index 828d97760c..3851857c2d 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -357,12 +357,8 @@ int mingw_offset_1st_component(const char *path); void mingw_open_html(const char *path); #define open_html mingw_open_html -/* - * helpers - */ - -char **make_augmented_environ(const char *const *vars); -void free_environ(char **env); +void mingw_mark_as_git_dir(const char *dir); +#define mark_as_git_dir mingw_mark_as_git_dir /** * Converts UTF-8 encoded string to UTF-16LE. -- cgit v1.2.3 From f279242d5e3f17e01e1d70010c4f072798899a47 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:02 +0200 Subject: Win32: reduce environment array reallocations Move environment array reallocation from do_putenv to the respective callers. Keep track of the environment size in a global variable. Use ALLOC_GROW in mingw_putenv to reduce reallocations. Allocate a sufficiently sized environment array in make_environment_block to prevent reallocations. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 62 +++++++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 27 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 789a0ec6dc..967c1a0a58 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -899,7 +899,12 @@ static char *path_lookup(const char *cmd, char **path, int exe_only) return prog; } -static char **do_putenv(char **env, const char *name, int free_old); +static int do_putenv(char **env, const char *name, int size, int free_old); + +/* used number of elements of environ array, including terminating NULL */ +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) { @@ -915,31 +920,28 @@ static int compareenv(const void *a, const void *b) static wchar_t *make_environment_block(char **deltaenv) { wchar_t *wenvblk = NULL; - int count = 0; - char **e, **tmpenv; - int size = 0, wenvsz = 0, wenvpos = 0; + char **tmpenv; + int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0; - while (environ[count]) - count++; + while (deltaenv && deltaenv[i]) + i++; - /* copy the environment */ - tmpenv = xmalloc(sizeof(*tmpenv) * (count + 1)); - memcpy(tmpenv, environ, sizeof(*tmpenv) * (count + 1)); + /* copy the environment, leaving space for changes */ + tmpenv = xmalloc((size + i) * sizeof(char*)); + memcpy(tmpenv, environ, size * sizeof(char*)); /* merge supplied environment changes into the temporary environment */ - for (e = deltaenv; e && *e; e++) - tmpenv = do_putenv(tmpenv, *e, 0); + for (i = 0; deltaenv && deltaenv[i]; i++) + size = do_putenv(tmpenv, deltaenv[i], size, 0); /* environment must be sorted */ - for (count = 0; tmpenv[count]; ) - count++; - qsort(tmpenv, count, sizeof(*tmpenv), compareenv); + qsort(tmpenv, size - 1, sizeof(char*), compareenv); /* create environment block from temporary environment */ - for (e = tmpenv; *e; e++) { - size = 2 * strlen(*e) + 2; /* +2 for final \0 */ + for (i = 0; tmpenv[i]; i++) { + size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */ ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz); - wenvpos += xutftowcs(&wenvblk[wenvpos], *e, size) + 1; + wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1; } /* add final \0 terminator */ wenvblk[wenvpos] = 0; @@ -1206,19 +1208,19 @@ static int lookupenv(char **env, const char *name, size_t nmln) /* * If name contains '=', then sets the variable, otherwise it unsets it + * Size includes the terminating NULL. Env must have room for size + 1 entries + * (in case of insert). Returns the new size. Optionally frees removed entries. */ -static char **do_putenv(char **env, const char *name, int free_old) +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); if (i < 0) { if (*eq) { - for (i = 0; env[i]; i++) - ; - env = xrealloc(env, (i+2)*sizeof(*env)); - env[i] = (char*) name; - env[i+1] = NULL; + env[size - 1] = (char*) name; + env[size] = NULL; + size++; } } else { @@ -1226,11 +1228,13 @@ static char **do_putenv(char **env, const char *name, int free_old) free(env[i]); if (*eq) env[i] = (char*) name; - else + else { for (; env[i]; i++) env[i] = env[i+1]; + size--; + } } - return env; + return size; } #undef getenv @@ -1248,7 +1252,8 @@ char *mingw_getenv(const char *name) int mingw_putenv(const char *namevalue) { - environ = do_putenv(environ, namevalue, 1); + ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc); + environ_size = do_putenv(environ, namevalue, environ_size, 1); return 0; } @@ -2048,7 +2053,9 @@ void mingw_startup() maxlen = max(maxlen, wcslen(wenv[i])); /* nedmalloc can't free CRT memory, allocate resizable environment list */ - environ = xcalloc(i + 1, sizeof(char*)); + environ = NULL; + environ_size = i + 1; + ALLOC_GROW(environ, environ_size * sizeof(char*), environ_alloc); /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */ maxlen = 3 * maxlen + 1; @@ -2065,6 +2072,7 @@ void mingw_startup() len = xwcstoutf(buffer, wenv[i], maxlen); environ[i] = xmemdupz(buffer, len); } + environ[i] = NULL; free(buffer); /* initialize critical section for waitpid pinfo_t list */ -- cgit v1.2.3 From 6f1c189cadd7c18c03138070324af02563c0b0d1 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:03 +0200 Subject: Win32: use low-level memory allocation during initialization As of d41489a6 "Add more large blob test cases", git's high-level memory allocation functions (xmalloc, xmemdupz etc.) access the environment to simulate limited memory in tests (see 'getenv("GIT_ALLOC_LIMIT")' in memory_limit_check()). These functions should not be used before the environment is fully initialized (particularly not to initialize the environment itself). The current solution ('environ = NULL; ALLOC_GROW(environ...)') only works because MSVCRT's getenv() reinitializes environ when it is NULL (i.e. it leaves us with two sets of unusabe (non-UTF-8) and unfreeable (CRT- allocated) environments). Add our own set of malloc-or-die functions to be used in startup code. Also check the result of __wgetmainargs, which may fail if there's not enough memory for wide-char arguments and environment. This patch is in preparation of the sorted environment feature, which completely replaces MSVCRT's getenv() implementation. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 967c1a0a58..a99ebd081a 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2033,9 +2033,23 @@ static NORETURN void die_startup() exit(128); } +static void *malloc_startup(size_t size) +{ + void *result = malloc(size); + if (!result) + die_startup(); + return result; +} + +static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len) +{ + len = xwcstoutf(buffer, wcs, len) + 1; + return memcpy(malloc_startup(len), buffer, len); +} + void mingw_startup() { - int i, len, maxlen, argc; + int i, maxlen, argc; char *buffer; wchar_t **wenv, **wargv; _startupinfo si; @@ -2052,26 +2066,25 @@ void mingw_startup() for (i = 0; wenv[i]; i++) maxlen = max(maxlen, wcslen(wenv[i])); - /* nedmalloc can't free CRT memory, allocate resizable environment list */ - environ = NULL; + /* + * nedmalloc can't free CRT memory, allocate resizable environment + * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot + * use it while initializing the environment itself. + */ environ_size = i + 1; - ALLOC_GROW(environ, environ_size * sizeof(char*), environ_alloc); + environ_alloc = alloc_nr(environ_size * sizeof(char*)); + environ = malloc_startup(environ_alloc); /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */ maxlen = 3 * maxlen + 1; - buffer = xmalloc(maxlen); + buffer = malloc_startup(maxlen); /* convert command line arguments and environment to UTF-8 */ - len = xwcstoutf(buffer, _wpgmptr, maxlen); - __argv[0] = xmemdupz(buffer, len); - for (i = 1; i < argc; i++) { - len = xwcstoutf(buffer, wargv[i], maxlen); - __argv[i] = xmemdupz(buffer, len); - } - for (i = 0; wenv[i]; i++) { - len = xwcstoutf(buffer, wenv[i], maxlen); - environ[i] = xmemdupz(buffer, len); - } + __argv[0] = wcstoutfdup_startup(buffer, _wpgmptr, maxlen); + for (i = 1; i < argc; i++) + __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen); + for (i = 0; wenv[i]; i++) + environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen); environ[i] = NULL; free(buffer); -- cgit v1.2.3 From 343ff06da7d83f40892b10a3b653c7d0e6cb526c Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:04 +0200 Subject: 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 Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 104 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 65 insertions(+), 39 deletions(-) (limited to 'compat') 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); -- cgit v1.2.3 From 6dc715439b8e9ec85d6412750665431dd6a5afc6 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:05 +0200 Subject: Win32: patch Windows environment on startup Fix Windows specific environment settings on startup rather than checking for special values on every getenv call. As a side effect, this makes the patched environment (i.e. with properly initialized TMPDIR and TERM) available to child processes. Signed-off-by: Karsten Blees Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 968e8d3c3e..74c6180a20 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1251,7 +1251,7 @@ static int do_putenv(char **env, const char *name, int size, int free_old) return size; } -static char *do_getenv(const char *name) +char *mingw_getenv(const char *name) { char *value; int pos = bsearchenv(environ, name, environ_size - 1); @@ -1261,18 +1261,6 @@ static char *do_getenv(const char *name) return value ? &value[1] : NULL; } -char *mingw_getenv(const char *name) -{ - char *result = do_getenv(name); - if (!result && !strcmp(name, "TMPDIR")) { - /* on Windows it is TMP and TEMP */ - result = do_getenv("TMP"); - if (!result) - result = do_getenv("TEMP"); - } - return result; -} - int mingw_putenv(const char *namevalue) { ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc); @@ -2114,6 +2102,17 @@ void mingw_startup() /* sort environment for O(log n) getenv / putenv */ qsort(environ, i, sizeof(char*), compareenv); + /* fix Windows specific environment settings */ + + /* on Windows it is TMP and TEMP */ + if (!mingw_getenv("TMPDIR")) { + const char *tmp = mingw_getenv("TMP"); + if (!tmp) + tmp = mingw_getenv("TEMP"); + if (tmp) + setenv("TMPDIR", tmp, 1); + } + /* initialize critical section for waitpid pinfo_t list */ InitializeCriticalSection(&pinfo_cs); -- cgit v1.2.3 From baea068d677fae92d7903e984cf93bbd5195a000 Mon Sep 17 00:00:00 2001 From: Karsten Blees Date: Thu, 17 Jul 2014 17:38:06 +0200 Subject: Win32: enable color output in Windows cmd.exe Git requires the TERM environment variable to be set for all color* settings. Simulate the TERM variable if it is not set (default on Windows). Signed-off-by: Karsten Blees Signed-off-by: Johannes Schindelin Signed-off-by: Stepan Kasal Signed-off-by: Junio C Hamano --- compat/mingw.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 74c6180a20..df0fa03194 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2113,6 +2113,10 @@ void mingw_startup() setenv("TMPDIR", tmp, 1); } + /* simulate TERM to enable auto-color (see color.c) */ + if (!getenv("TERM")) + setenv("TERM", "cygwin", 1); + /* initialize critical section for waitpid pinfo_t list */ InitializeCriticalSection(&pinfo_cs); -- cgit v1.2.3