From dfd188ababfd634c4cc2a18f6ed38c3829e5058c Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 14 Jan 2008 14:05:33 +0100 Subject: [PATCH] Windows: Make 'git help -a' work. 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 executabe 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 --- help.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/help.c b/help.c index 1302a61c83..15a67dec35 100644 --- a/help.c +++ b/help.c @@ -165,6 +165,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) { @@ -178,15 +204,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; @@ -210,6 +233,11 @@ static void list_commands(void) const char *env_path = getenv("PATH"); char *paths, *path, *colon; const char *exec_path = git_exec_path(); +#ifdef __MINGW32__ + char sep = ';'; +#else + char sep = ':'; +#endif if (exec_path) longest = list_commands_in_dir(&main_cmds, exec_path); @@ -221,7 +249,7 @@ static void list_commands(void) path = paths = xstrdup(env_path); while (1) { - if ((colon = strchr(path, ':'))) + if ((colon = strchr(path, sep))) *colon = 0; len = list_commands_in_dir(&other_cmds, path);