Windows: avoid the "dup dance" when spawning a child process
[gitweb.git] / setup.c
diff --git a/setup.c b/setup.c
index 6c2deda18492acb5a8597563d6843f9d0dd232c0..3a07aa4df710f5d9584d4ddf412e3debc0e64db4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -18,9 +18,12 @@ const char *prefix_path(const char *prefix, int len, const char *path)
        if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
+               size_t len, total;
                const char *work_tree = get_git_work_tree();
-               size_t len = strlen(work_tree);
-               size_t total = strlen(sanitized) + 1;
+               if (!work_tree)
+                       goto error_out;
+               len = strlen(work_tree);
+               total = strlen(sanitized) + 1;
                if (strncmp(sanitized, work_tree, len) ||
                    (sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
@@ -41,7 +44,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
-#ifndef __MINGW32__
+#ifndef WIN32
        if (!pfx || !*pfx || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
@@ -61,6 +64,31 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
        return path;
 }
 
+int check_filename(const char *prefix, const char *arg)
+{
+       const char *name;
+       struct stat st;
+
+       name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+       if (!lstat(name, &st))
+               return 1; /* file exists */
+       if (errno == ENOENT || errno == ENOTDIR)
+               return 0; /* file does not exist */
+       die_errno("failed to stat '%s'", arg);
+}
+
+static void NORETURN die_verify_filename(const char *prefix, const char *arg)
+{
+       unsigned char sha1[20];
+       unsigned mode;
+       /* try a detailed diagnostic ... */
+       get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
+       /* ... or fall back the most general message. */
+       die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+           "Use '--' to separate paths from revisions", arg);
+
+}
+
 /*
  * Verify a filename that we got as an argument for a pathspec
  * entry. Note that a filename that begins with "-" never verifies
@@ -70,18 +98,11 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
  */
 void verify_filename(const char *prefix, const char *arg)
 {
-       const char *name;
-       struct stat st;
-
        if (*arg == '-')
                die("bad flag '%s' used after filename", arg);
-       name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
-       if (!lstat(name, &st))
+       if (check_filename(prefix, arg))
                return;
-       if (errno == ENOENT)
-               die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-                   "Use '--' to separate paths from revisions", arg);
-       die("'%s': %s", arg, strerror(errno));
+       die_verify_filename(prefix, arg);
 }
 
 /*
@@ -91,19 +112,14 @@ void verify_filename(const char *prefix, const char *arg)
  */
 void verify_non_filename(const char *prefix, const char *arg)
 {
-       const char *name;
-       struct stat st;
-
        if (!is_inside_work_tree() || is_inside_git_dir())
                return;
        if (*arg == '-')
                return; /* flag */
-       name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
-       if (!lstat(name, &st))
-               die("ambiguous argument '%s': both revision and filename\n"
-                   "Use '--' to separate filenames from revisions", arg);
-       if (errno != ENOENT && errno != ENOTDIR)
-               die("'%s': %s", arg, strerror(errno));
+       if (!check_filename(prefix, arg))
+               return;
+       die("ambiguous argument '%s': both revision and filename\n"
+           "Use '--' to separate filenames from revisions", arg);
 }
 
 const char **get_pathspec(const char *prefix, const char **pathspec)
@@ -257,7 +273,7 @@ const char *read_gitfile_gently(const char *path)
                return NULL;
        fd = open(path, O_RDONLY);
        if (fd < 0)
-               die("Error opening %s: %s", path, strerror(errno));
+               die_errno("Error opening '%s'", path);
        buf = xmalloc(st.st_size + 1);
        len = read_in_full(fd, buf, st.st_size);
        close(fd);
@@ -327,7 +343,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                                return NULL;
                        set_git_dir(make_absolute_path(gitdirenv));
                        if (chdir(work_tree_env) < 0)
-                               die ("Could not chdir to %s", work_tree_env);
+                               die_errno ("Could not chdir to '%s'", work_tree_env);
                        strcat(buffer, "/");
                        return retval;
                }
@@ -339,7 +355,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        }
 
        if (!getcwd(cwd, sizeof(cwd)-1))
-               die("Unable to read current working directory");
+               die_errno("Unable to read current working directory");
 
        ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
        if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -382,14 +398,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
                if (offset <= ceil_offset) {
                        if (nongit_ok) {
                                if (chdir(cwd))
-                                       die("Cannot come back to cwd");
+                                       die_errno("Cannot come back to cwd");
                                *nongit_ok = 1;
                                return NULL;
                        }
                        die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
                }
                if (chdir(".."))
-                       die("Cannot change to %s/..: %s", cwd, strerror(errno));
+                       die_errno("Cannot change to '%s/..'", cwd);
        }
 
        inside_git_dir = 0;
@@ -434,7 +450,7 @@ int git_config_perm(const char *var, const char *value)
 
        /*
         * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
-        * a chmod value.
+        * a chmod value to restrict to.
         */
        switch (i) {
        case PERM_UMASK:               /* 0 */
@@ -456,7 +472,7 @@ int git_config_perm(const char *var, const char *value)
         * Mask filemode value. Others can not get write permission.
         * x flags for directories are handled separately.
         */
-       return i & 0666;
+       return -(i & 0666);
 }
 
 int check_repository_format_version(const char *var, const char *value, void *cb)
@@ -493,10 +509,10 @@ const char *setup_git_directory(void)
                static char buffer[PATH_MAX + 1];
                char *rel;
                if (retval && chdir(retval))
-                       die ("Could not jump back into original cwd");
+                       die_errno ("Could not jump back into original cwd");
                rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
                if (rel && *rel && chdir(get_git_work_tree()))
-                       die ("Could not jump to working directory");
+                       die_errno ("Could not jump to working directory");
                return rel && *rel ? strcat(rel, "/") : NULL;
        }