diff options
author | Junio C Hamano <gitster@pobox.com> | 2007-10-31 23:53:51 -0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2007-10-31 23:53:51 -0700 |
commit | 452b80058235040461dae01933270c6668741b64 (patch) | |
tree | d9915c8215d2840504bf34bcb0cac91122fc334b | |
parent | 37701381b66ab66234a0a5992f2b107a6507a2fb (diff) | |
parent | e8f5d87056093f40a271f89c2c91d1a7025e2440 (diff) | |
download | git-452b80058235040461dae01933270c6668741b64.tar.gz |
Merge branch 'sp/help'
* sp/help:
shell should call the new setup_path() to setup $PATH
include $PATH in generating list of commands for "help -a"
use only the $PATH for exec'ing git commands
list_commands(): simplify code by using chdir()
"current_exec_path" is a misleading name, use "argv_exec_path"
remove unused/unneeded "pattern" argument of list_commands
"git" returns 1; "git help" and "git help -a" return 0
-rw-r--r-- | builtin.h | 1 | ||||
-rw-r--r-- | exec_cmd.c | 132 | ||||
-rw-r--r-- | exec_cmd.h | 3 | ||||
-rw-r--r-- | git.c | 52 | ||||
-rw-r--r-- | help.c | 185 | ||||
-rw-r--r-- | shell.c | 8 |
6 files changed, 203 insertions, 178 deletions
@@ -6,6 +6,7 @@ extern const char git_version_string[]; extern const char git_usage_string[]; +extern void list_common_cmds_help(void); extern void help_unknown_cmd(const char *cmd); extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix); extern void prune_packed_objects(int); diff --git a/exec_cmd.c b/exec_cmd.c index 9b74ed2f42..2d0a758512 100644 --- a/exec_cmd.c +++ b/exec_cmd.c @@ -5,11 +5,11 @@ extern char **environ; static const char *builtin_exec_path = GIT_EXEC_PATH; -static const char *current_exec_path; +static const char *argv_exec_path; -void git_set_exec_path(const char *exec_path) +void git_set_argv_exec_path(const char *exec_path) { - current_exec_path = exec_path; + argv_exec_path = exec_path; } @@ -18,8 +18,8 @@ const char *git_exec_path(void) { const char *env; - if (current_exec_path) - return current_exec_path; + if (argv_exec_path) + return argv_exec_path; env = getenv(EXEC_PATH_ENVIRONMENT); if (env && *env) { @@ -29,85 +29,69 @@ const char *git_exec_path(void) return builtin_exec_path; } +static void add_path(struct strbuf *out, const char *path) +{ + if (path && *path) { + if (is_absolute_path(path)) + strbuf_addstr(out, path); + else + strbuf_addstr(out, make_absolute_path(path)); + + strbuf_addch(out, ':'); + } +} + +void setup_path(const char *cmd_path) +{ + const char *old_path = getenv("PATH"); + struct strbuf new_path; + + strbuf_init(&new_path, 0); + + add_path(&new_path, argv_exec_path); + add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT)); + add_path(&new_path, builtin_exec_path); + add_path(&new_path, cmd_path); + + if (old_path) + strbuf_addstr(&new_path, old_path); + else + strbuf_addstr(&new_path, "/usr/local/bin:/usr/bin:/bin"); + + setenv("PATH", new_path.buf, 1); + + strbuf_release(&new_path); +} int execv_git_cmd(const char **argv) { - char git_command[PATH_MAX + 1]; - int i; - const char *paths[] = { current_exec_path, - getenv(EXEC_PATH_ENVIRONMENT), - builtin_exec_path }; - - for (i = 0; i < ARRAY_SIZE(paths); ++i) { - size_t len; - int rc; - const char *exec_dir = paths[i]; - const char *tmp; - - if (!exec_dir || !*exec_dir) continue; - - if (*exec_dir != '/') { - if (!getcwd(git_command, sizeof(git_command))) { - fprintf(stderr, "git: cannot determine " - "current directory: %s\n", - strerror(errno)); - break; - } - len = strlen(git_command); - - /* Trivial cleanup */ - while (!prefixcmp(exec_dir, "./")) { - exec_dir += 2; - while (*exec_dir == '/') - exec_dir++; - } - - rc = snprintf(git_command + len, - sizeof(git_command) - len, "/%s", - exec_dir); - if (rc < 0 || rc >= sizeof(git_command) - len) { - fprintf(stderr, "git: command name given " - "is too long.\n"); - break; - } - } else { - if (strlen(exec_dir) + 1 > sizeof(git_command)) { - fprintf(stderr, "git: command name given " - "is too long.\n"); - break; - } - strcpy(git_command, exec_dir); - } - - len = strlen(git_command); - rc = snprintf(git_command + len, sizeof(git_command) - len, - "/git-%s", argv[0]); - if (rc < 0 || rc >= sizeof(git_command) - len) { - fprintf(stderr, - "git: command name given is too long.\n"); - break; - } + struct strbuf cmd; + const char *tmp; - /* argv[0] must be the git command, but the argv array - * belongs to the caller, and my be reused in - * subsequent loop iterations. Save argv[0] and - * restore it on error. - */ + strbuf_init(&cmd, 0); + strbuf_addf(&cmd, "git-%s", argv[0]); - tmp = argv[0]; - argv[0] = git_command; + /* + * argv[0] must be the git command, but the argv array + * belongs to the caller, and may be reused in + * subsequent loop iterations. Save argv[0] and + * restore it on error. + */ + tmp = argv[0]; + argv[0] = cmd.buf; - trace_argv_printf(argv, -1, "trace: exec:"); + trace_argv_printf(argv, -1, "trace: exec:"); - /* execve() can only ever return if it fails */ - execve(git_command, (char **)argv, environ); + /* execvp() can only ever return if it fails */ + execvp(cmd.buf, (char **)argv); - trace_printf("trace: exec failed: %s\n", strerror(errno)); + trace_printf("trace: exec failed: %s\n", strerror(errno)); - argv[0] = tmp; - } - return -1; + argv[0] = tmp; + strbuf_release(&cmd); + + return -1; } diff --git a/exec_cmd.h b/exec_cmd.h index 849a8395a0..a892355c82 100644 --- a/exec_cmd.h +++ b/exec_cmd.h @@ -1,8 +1,9 @@ #ifndef GIT_EXEC_CMD_H #define GIT_EXEC_CMD_H -extern void git_set_exec_path(const char *exec_path); +extern void git_set_argv_exec_path(const char *exec_path); extern const char* git_exec_path(void); +extern void setup_path(const char *); extern int execv_git_cmd(const char **argv); /* NULL terminated */ extern int execl_git_cmd(const char *cmd, ...); @@ -6,28 +6,6 @@ const char git_usage_string[] = "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]"; -static void prepend_to_path(const char *dir, int len) -{ - const char *old_path = getenv("PATH"); - char *path; - int path_len = len; - - if (!old_path) - old_path = "/usr/local/bin:/usr/bin:/bin"; - - path_len = len + strlen(old_path) + 1; - - path = xmalloc(path_len + 1); - - memcpy(path, dir, len); - path[len] = ':'; - memcpy(path + len + 1, old_path, path_len - len); - - setenv("PATH", path, 1); - - free(path); -} - static int handle_options(const char*** argv, int* argc, int* envchanged) { int handled = 0; @@ -51,7 +29,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) if (!prefixcmp(cmd, "--exec-path")) { cmd += 11; if (*cmd == '=') - git_set_exec_path(cmd + 1); + git_set_argv_exec_path(cmd + 1); else { puts(git_exec_path()); exit(0); @@ -408,7 +386,7 @@ int main(int argc, const char **argv) { const char *cmd = argv[0] ? argv[0] : "git-help"; char *slash = strrchr(cmd, '/'); - const char *exec_path = NULL; + const char *cmd_path = NULL; int done_alias = 0; /* @@ -418,10 +396,7 @@ int main(int argc, const char **argv) */ if (slash) { *slash++ = 0; - if (*cmd == '/') - exec_path = cmd; - else - exec_path = xstrdup(make_absolute_path(cmd)); + cmd_path = cmd; cmd = slash; } @@ -450,23 +425,20 @@ int main(int argc, const char **argv) if (!prefixcmp(argv[0], "--")) argv[0] += 2; } else { - /* Default command: "help" */ - argv[0] = "help"; - argc = 1; + /* The user didn't specify a command; give them help */ + printf("usage: %s\n\n", git_usage_string); + list_common_cmds_help(); + exit(1); } cmd = argv[0]; /* - * We execute external git command via execv_git_cmd(), - * which looks at "--exec-path" option, GIT_EXEC_PATH - * environment, and $(gitexecdir) in Makefile while built, - * in this order. For scripted commands, we prepend - * the value of the exec_path variable to the PATH. + * We use PATH to find git commands, but we prepend some higher + * precidence paths: the "--exec-path" option, the GIT_EXEC_PATH + * environment, and the $(gitexecdir) from the Makefile at build + * time. */ - if (exec_path) - prepend_to_path(exec_path, strlen(exec_path)); - exec_path = git_exec_path(); - prepend_to_path(exec_path, strlen(exec_path)); + setup_path(cmd_path); while (1) { /* See if it's an internal command */ @@ -37,24 +37,25 @@ static inline void mput_char(char c, unsigned int num) putchar(c); } -static struct cmdname { - size_t len; - char name[1]; -} **cmdname; -static int cmdname_alloc, cmdname_cnt; - -static void add_cmdname(const char *name, int len) +static struct cmdnames { + int alloc; + int cnt; + struct cmdname { + size_t len; + char name[1]; + } **names; +} main_cmds, other_cmds; + +static void add_cmdname(struct cmdnames *cmds, const char *name, int len) { - struct cmdname *ent; - if (cmdname_alloc <= cmdname_cnt) { - cmdname_alloc = cmdname_alloc + 200; - cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname)); - } - ent = xmalloc(sizeof(*ent) + len); + struct cmdname *ent = xmalloc(sizeof(*ent) + len); + ent->len = len; memcpy(ent->name, name, len); ent->name[len] = 0; - cmdname[cmdname_cnt++] = ent; + + ALLOC_GROW(cmds->names, cmds->cnt + 1, cmds->alloc); + cmds->names[cmds->cnt++] = ent; } static int cmdname_compare(const void *a_, const void *b_) @@ -64,7 +65,42 @@ static int cmdname_compare(const void *a_, const void *b_) return strcmp(a->name, b->name); } -static void pretty_print_string_list(struct cmdname **cmdname, int longest) +static void uniq(struct cmdnames *cmds) +{ + int i, j; + + if (!cmds->cnt) + return; + + for (i = j = 1; i < cmds->cnt; i++) + if (strcmp(cmds->names[i]->name, cmds->names[i-1]->name)) + cmds->names[j++] = cmds->names[i]; + + cmds->cnt = j; +} + +static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes) { + int ci, cj, ei; + int cmp; + + ci = cj = ei = 0; + while (ci < cmds->cnt && ei < excludes->cnt) { + cmp = strcmp(cmds->names[ci]->name, excludes->names[ei]->name); + if (cmp < 0) + cmds->names[cj++] = cmds->names[ci++]; + else if (cmp == 0) + ci++, ei++; + else if (cmp > 0) + ei++; + } + + while (ci < cmds->cnt) + cmds->names[cj++] = cmds->names[ci++]; + + cmds->cnt = cj; +} + +static void pretty_print_string_list(struct cmdnames *cmds, int longest) { int cols = 1, rows; int space = longest + 1; /* min 1 SP between words */ @@ -73,9 +109,7 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) if (space < max_cols) cols = max_cols / space; - rows = (cmdname_cnt + cols - 1) / cols; - - qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare); + rows = (cmds->cnt + cols - 1) / cols; for (i = 0; i < rows; i++) { printf(" "); @@ -83,71 +117,112 @@ static void pretty_print_string_list(struct cmdname **cmdname, int longest) for (j = 0; j < cols; j++) { int n = j * rows + i; int size = space; - if (n >= cmdname_cnt) + if (n >= cmds->cnt) break; - if (j == cols-1 || n + rows >= cmdname_cnt) + if (j == cols-1 || n + rows >= cmds->cnt) size = 1; - printf("%-*s", size, cmdname[n]->name); + printf("%-*s", size, cmds->names[n]->name); } putchar('\n'); } } -static void list_commands(const char *exec_path, const char *pattern) +static unsigned int list_commands_in_dir(struct cmdnames *cmds, + const char *path) { unsigned int longest = 0; - char path[PATH_MAX]; - int dirlen; - DIR *dir = opendir(exec_path); + const char *prefix = "git-"; + int prefix_len = strlen(prefix); + DIR *dir = opendir(path); struct dirent *de; - if (!dir) { - fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno)); - exit(1); - } - - dirlen = strlen(exec_path); - if (PATH_MAX - 20 < dirlen) { - fprintf(stderr, "git: insanely long exec-path '%s'\n", - exec_path); - exit(1); - } - - memcpy(path, exec_path, dirlen); - path[dirlen++] = '/'; + if (!dir || chdir(path)) + return 0; while ((de = readdir(dir)) != NULL) { struct stat st; int entlen; - if (prefixcmp(de->d_name, "git-")) + if (prefixcmp(de->d_name, prefix)) continue; - strcpy(path+dirlen, de->d_name); - if (stat(path, &st) || /* stat, not lstat */ + + if (stat(de->d_name, &st) || /* stat, not lstat */ !S_ISREG(st.st_mode) || !(st.st_mode & S_IXUSR)) continue; - entlen = strlen(de->d_name); + entlen = strlen(de->d_name) - prefix_len; if (has_extension(de->d_name, ".exe")) entlen -= 4; if (longest < entlen) longest = entlen; - add_cmdname(de->d_name + 4, entlen-4); + add_cmdname(cmds, de->d_name + prefix_len, entlen); } closedir(dir); - printf("git commands available in '%s'\n", exec_path); - printf("----------------------------"); - mput_char('-', strlen(exec_path)); - putchar('\n'); - pretty_print_string_list(cmdname, longest - 4); - putchar('\n'); + return longest; } -static void list_common_cmds_help(void) +static void list_commands(void) +{ + unsigned int longest = 0; + unsigned int len; + const char *env_path = getenv("PATH"); + char *paths, *path, *colon; + const char *exec_path = git_exec_path(); + + if (exec_path) + longest = list_commands_in_dir(&main_cmds, exec_path); + + if (!env_path) { + fprintf(stderr, "PATH not set\n"); + exit(1); + } + + path = paths = xstrdup(env_path); + while (1) { + if ((colon = strchr(path, ':'))) + *colon = 0; + + len = list_commands_in_dir(&other_cmds, path); + if (len > longest) + longest = len; + + if (!colon) + break; + path = colon + 1; + } + free(paths); + + qsort(main_cmds.names, main_cmds.cnt, + sizeof(*main_cmds.names), cmdname_compare); + uniq(&main_cmds); + + qsort(other_cmds.names, other_cmds.cnt, + sizeof(*other_cmds.names), cmdname_compare); + uniq(&other_cmds); + exclude_cmds(&other_cmds, &main_cmds); + + if (main_cmds.cnt) { + printf("available git commands in '%s'\n", exec_path); + printf("----------------------------"); + mput_char('-', strlen(exec_path)); + putchar('\n'); + pretty_print_string_list(&main_cmds, longest); + putchar('\n'); + } + + if (other_cmds.cnt) { + printf("git commands available from elsewhere on your $PATH\n"); + printf("---------------------------------------------------\n"); + pretty_print_string_list(&other_cmds, longest); + putchar('\n'); + } +} + +void list_common_cmds_help(void) { int i, longest = 0; @@ -198,19 +273,17 @@ int cmd_version(int argc, const char **argv, const char *prefix) int cmd_help(int argc, const char **argv, const char *prefix) { const char *help_cmd = argc > 1 ? argv[1] : NULL; - const char *exec_path = git_exec_path(); if (!help_cmd) { printf("usage: %s\n\n", git_usage_string); list_common_cmds_help(); - exit(1); + exit(0); } else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) { printf("usage: %s\n\n", git_usage_string); - if(exec_path) - list_commands(exec_path, "git-*"); - exit(1); + list_commands(); + exit(0); } else @@ -24,17 +24,11 @@ static int do_cvs_cmd(const char *me, char *arg) const char *cvsserver_argv[3] = { "cvsserver", "server", NULL }; - const char *oldpath = getenv("PATH"); - struct strbuf newpath = STRBUF_INIT; if (!arg || strcmp(arg, "server")) die("git-cvsserver only handles server: %s", arg); - strbuf_addstr(&newpath, git_exec_path()); - strbuf_addch(&newpath, ':'); - strbuf_addstr(&newpath, oldpath); - - setenv("PATH", strbuf_detach(&newpath, NULL), 1); + setup_path(NULL); return execv_git_cmd(cvsserver_argv); } |