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 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>
Johannes Sixt committed
Jan 14, 2008 at 14:05 UTC
cc3b7a9732f940cb0249a12cb3c02e3d83723eb0
1 file changed
+28
-5
help.c
+28
-5
@@ -391,6 +391,32 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
391
}
392
}
393
394
+static int is_executable(const char *name)
395
+{
396
+ struct stat st;
397
+
398
+ if (stat(name, &st) || /* stat, not lstat */
399
+ !S_ISREG(st.st_mode))
400
+ return 0;
401
+
402
+#ifdef __MINGW32__
403
+ /* cannot trust the executable bit, peek into the file instead */
404
+ char buf[3] = { 0 };
405
+ int n;
406
+ int fd = open(name, O_RDONLY);
407
+ st.st_mode &= ~S_IXUSR;
408
+ if (fd >= 0) {
409
+ n = read(fd, buf, 2);
410
+ if (n == 2)
411
+ /* DOS executables start with "MZ" */
412
+ if (!strcmp(buf, "#!") || !strcmp(buf, "MZ"))
413
+ st.st_mode |= S_IXUSR;
414
+ close(fd);
415
+ }
416
+#endif
417
+ return st.st_mode & S_IXUSR;
418
+}
419
+
420
static unsigned int list_commands_in_dir(struct cmdnames *cmds,
421
const char *path)
422
{
@@ -404,15 +430,12 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
430
return 0;
431
432
while ((de = readdir(dir)) != NULL) {
407
- struct stat st;
433
int entlen;
434
435
if (prefixcmp(de->d_name, prefix))
436
continue;
437
413
- if (stat(de->d_name, &st) || /* stat, not lstat */
414
- !S_ISREG(st.st_mode) ||
415
- !(st.st_mode & S_IXUSR))
438
+ if (!is_executable(de->d_name))
439
continue;
440
441
entlen = strlen(de->d_name) - prefix_len;
@@ -447,7 +470,7 @@ static unsigned int load_command_list(void)
470
471
path = paths = xstrdup(env_path);
472
while (1) {
450
- if ((colon = strchr(path, ':')))
473
+ if ((colon = strchr(path, PATH_SEP)))
474
*colon = 0;
475
476
len = list_commands_in_dir(&other_cmds, path);