Windows: Make 'git help -a' work.
authorJohannes Sixt <johannes.sixt@telecom.at>
Mon, 14 Jan 2008 13:05:33 +0000 (14:05 +0100)
committerJohannes Sixt <johannes.sixt@telecom.at>
Thu, 26 Jun 2008 06:47:16 +0000 (08:47 +0200)
git help -a scans the PATH for git commands. On Windows it failed for two
reasons:

- The PATH separator is ';', not ':' on Windows.

- stat() does not set the executable bit.

We now open the file and guess whether it is executable.

The result of the guess is good enough for the list of git commands, but
it is of no use for a general stat() implementation because (1) it is a
guess, (2) the user has no way to influence the outcome (via chmod or
similar), and (3) it would reduce stat() performance by an unacceptable
amount. Therefore, this strategy is a special-case local to help.c.

Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
help.c

diff --git a/help.c b/help.c
index 8aff94c64a1204f8a359e522a554f29c8f0fdc20..6c16fb4aa1406dd955c7ae1b0efd74288d72c239 100644 (file)
--- a/help.c
+++ b/help.c
@@ -391,6 +391,32 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
        }
 }
 
+static int is_executable(const char *name)
+{
+       struct stat st;
+
+       if (stat(name, &st) || /* stat, not lstat */
+           !S_ISREG(st.st_mode))
+               return 0;
+
+#ifdef __MINGW32__
+       /* cannot trust the executable bit, peek into the file instead */
+       char buf[3] = { 0 };
+       int n;
+       int fd = open(name, O_RDONLY);
+       st.st_mode &= ~S_IXUSR;
+       if (fd >= 0) {
+               n = read(fd, buf, 2);
+               if (n == 2)
+                       /* DOS executables start with "MZ" */
+                       if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
+                               st.st_mode |= S_IXUSR;
+               close(fd);
+       }
+#endif
+       return st.st_mode & S_IXUSR;
+}
+
 static unsigned int list_commands_in_dir(struct cmdnames *cmds,
                                         const char *path)
 {
@@ -404,15 +430,12 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
                return 0;
 
        while ((de = readdir(dir)) != NULL) {
-               struct stat st;
                int entlen;
 
                if (prefixcmp(de->d_name, prefix))
                        continue;
 
-               if (stat(de->d_name, &st) || /* stat, not lstat */
-                   !S_ISREG(st.st_mode) ||
-                   !(st.st_mode & S_IXUSR))
+               if (!is_executable(de->d_name))
                        continue;
 
                entlen = strlen(de->d_name) - prefix_len;
@@ -447,7 +470,7 @@ static unsigned int load_command_list(void)
 
        path = paths = xstrdup(env_path);
        while (1) {
-               if ((colon = strchr(path, ':')))
+               if ((colon = strchr(path, PATH_SEP)))
                        *colon = 0;
 
                len = list_commands_in_dir(&other_cmds, path);