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);