Merge branch 'rs/receive-pack-use-labs'
[gitweb.git] / compat / mingw.c
index 967c1a0a584446e9730e1da8cb9be7f28482226a..70f3191a4f19f10a156d1f2c054943d5147ab049 100644 (file)
@@ -312,7 +312,7 @@ int mingw_open (const char *filename, int oflags, ...)
                return -1;
        fd = _wopen(wfilename, oflags, mode);
 
-       if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
+       if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
                DWORD attrs = GetFileAttributesW(wfilename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
@@ -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,41 +1229,36 @@ 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);
+
+       /* optionally free removed / replaced entry */
+       if (i >= 0 && free_old)
+               free(env[i]);
 
-       if (i < 0) {
-               if (*eq) {
-                       env[size - 1] = (char*) name;
-                       env[size] = NULL;
+       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
 char *mingw_getenv(const char *name)
 {
-       char *result = getenv(name);
-       if (!result && !strcmp(name, "TMPDIR")) {
-               /* on Windows it is TMP and TEMP */
-               result = getenv("TMP");
-               if (!result)
-                       result = getenv("TEMP");
-       }
-       return result;
+       char *value;
+       int pos = bsearchenv(environ, name, environ_size - 1);
+       if (pos < 0)
+               return NULL;
+       value = strchr(environ[pos], '=');
+       return value ? &value[1] : NULL;
 }
 
 int mingw_putenv(const char *namevalue)
@@ -1299,8 +1310,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
        else
                ai->ai_canonname = NULL;
 
-       sin = xmalloc(ai->ai_addrlen);
-       memset(sin, 0, ai->ai_addrlen);
+       sin = xcalloc(1, ai->ai_addrlen);
        sin->sin_family = AF_INET;
        /* Note: getaddrinfo is supposed to allow service to be a string,
         * which should be looked up using getservbyname. This is
@@ -2033,9 +2043,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,29 +2076,46 @@ 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);
 
+       /* 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);
+       }
+
+       /* 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);