git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 6/7] walk $PATH to generate list of commands for "help -a"

From
Scott R Parish <srp@srparish.net>
Date
Oct 27, 2007, 08:36 UTC
Message-ID
<1193474215-6728-6-git-send-email-srp@srparish.net>
In-Reply-To
<1193474215-6728-5-git-send-email-srp@srparish.net>

Git had previously been using the $PATH for scripts--a previous patch moved exec'ed commands to also use the $PATH. For consistancy "help -a" should also use the $PATH.

We walk all the paths in $PATH collecting the names of "git-*" commands. To help distinguish between the main git commands and commands picked up elsewhere (probably extensions) we print them seperately. The main commands are the ones that are found in the first directory in $PATH that contains the "git" binary.

Signed-off-by: Scott R Parish <srp@srparish.net>
---
 help.c |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 125 insertions(+), 33 deletions(-)
diff --git a/help.c b/help.c
index ce3d795..ee4fce0 100644
--- a/help.c
+++ b/help.c
@@ -37,24 +37,29 @@ 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;
+	char *dir;
+	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));
+	if (cmds->alloc <= cmds->cnt) {
+		cmds->alloc = cmds->alloc + 200;
+		cmds->names = xrealloc(cmds->names,
+				       cmds->alloc * sizeof(*cmds->names));
 	}
 	ent = xmalloc(sizeof(*ent) + len);
 	ent->len = len;
 	memcpy(ent->name, name, len);
 	ent->name[len] = 0;
-	cmdname[cmdname_cnt++] = ent;
+	cmds->names[cmds->cnt++] = ent;
 }
 
 static int cmdname_compare(const void *a_, const void *b_)
@@ -64,7 +69,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 subtract_cmds(struct cmdnames *a, struct cmdnames *b) {
+	int ai, aj, bi;
+
+	ai = aj = bi = 0;
+	while (ai < a->cnt && bi < b->cnt) {
+		if (0 > strcmp(a->names[ai]->name, b->names[bi]->name))
+			a->names[aj++] = a->names[ai++];
+		else if (0 > strcmp(a->names[ai]->name, b->names[bi]->name))
+			bi++;
+		else
+			ai++, bi++;
+	}
+
+	while (ai < a->cnt)
+		a->names[aj++] = a->names[ai++];
+
+	a->cnt = aj;
+}
+
+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 +113,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,31 +121,39 @@ 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)
+static unsigned int list_commands_in_dir(const char *dir)
 {
 	unsigned int longest = 0;
 	const char *prefix = "git-";
 	int prefix_len = strlen(prefix);
-	DIR *dir = opendir(exec_path);
+	DIR *dirp = opendir(dir);
+	struct cmdnames *cmds;
 	struct dirent *de;
+	struct stat st;
 
-	if (!dir || chdir(exec_path)) {
-		fprintf(stderr, "git: '%s': %s\n", exec_path, strerror(errno));
-		exit(1);
+	if (!dirp || chdir(dir))
+		return 0;
+
+	if (!main_cmds.cnt &&
+	    !stat("git", &st) &&
+	    S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR)) {
+		cmds = &main_cmds;
+		cmds->dir = xstrdup(dir);
 	}
+	else
+		cmds = &other_cmds;
 
-	while ((de = readdir(dir)) != NULL) {
-		struct stat st;
+	while ((de = readdir(dirp)) != NULL) {
 		int entlen;
 
 		if (prefixcmp(de->d_name, prefix))
@@ -122,18 +168,66 @@ static void list_commands(const char *exec_path)
 		if (has_extension(de->d_name, ".exe"))
 			entlen -= 4;
 
+		if (has_extension(de->d_name, ".perl") ||
+		    has_extension(de->d_name, ".sh"))
+			continue;
+
 		if (longest < entlen)
 			longest = entlen;
 
-		add_cmdname(de->d_name + prefix_len, entlen);
+		add_cmdname(cmds, de->d_name + prefix_len, entlen);
+	}
+	closedir(dirp);
+
+	return longest;
+}
+
+static void list_commands()
+{
+	unsigned int longest = 0;
+	unsigned int len;
+	const char *env_path = getenv("PATH");
+	char *paths, *path, *colon;
+
+	if (!env_path) {
+		fprintf(stderr, "PATH not set\n");
+		exit(1);
 	}
-	closedir(dir);
 
-	printf("git commands available in '%s'\n", exec_path);
+	path = paths = xstrdup(env_path);
+	while ((char *)1 != path) {
+		if ((colon = strchr(path, ':')))
+			*colon = 0;
+
+		len = list_commands_in_dir(path);
+		longest = MAX(longest, len);
+
+		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);
+	subtract_cmds(&other_cmds, &main_cmds);
+
+	printf("available git commands in '%s'\n", main_cmds.dir);
 	printf("----------------------------");
-	mput_char('-', strlen(exec_path));
+	mput_char('-', strlen(main_cmds.dir));
+	putchar('\n');
+	pretty_print_string_list(&main_cmds, longest);
 	putchar('\n');
-	pretty_print_string_list(cmdname, longest);
+
+	if (!other_cmds.cnt)
+		return;
+
+	printf("git commands available from elsewhere on your $PATH\n");
+	printf("---------------------------------------------------\n");
+	pretty_print_string_list(&other_cmds, longest);
 	putchar('\n');
 }
 
@@ -189,7 +283,6 @@ 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);
@@ -199,8 +292,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
 
 	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);
+		list_commands();
 		exit(0);
 	}
 
-- 
gitgui.0.8.4.11178.g9a1bf-dirty
Previous: Scott R ParishNext: Scott R Parish
Message 6 of 25 in “"git" returns 1; "git help" and "git help -a" return 0”
  1. 1/7 "git" returns 1; "git help" and "git help -a" return 0Scott R Parish, Oct 27, 2007
  2. 2/7 remove unused/unneeded "pattern" argument of list_commandsScott R Parish, Oct 27, 2007
  3. 3/7 "current_exec_path" is a misleading name, use "argv_exec_path"Scott R Parish, Oct 27, 2007
  4. 4/7 list_commands(): simplify code by using chdir()Scott R Parish, Oct 27, 2007
  5. 5/7 use only the $PATH for exec'ing git commandsScott R Parish, Oct 27, 2007
  6. 6/7 walk $PATH to generate list of commands for "help -a"Scott R Parish, Oct 27, 2007
  7. 7/7 shell should call the new setup_path() to setup $PATHScott R Parish, Oct 27, 2007
  8. Junio C HamanoOct 28, 2007
  9. Scott ParishOct 28, 2007
  10. Junio C HamanoOct 28, 2007
  11. Scott ParishOct 28, 2007
  12. 6/7 include $PATH in generating list of commands for "help -a"Scott R Parish, Oct 28, 2007
  13. Junio C HamanoOct 28, 2007
  14. Scott ParishOct 28, 2007
  15. 6/7 include $PATH in generating list of commands for "help -a"Scott R Parish, Oct 28, 2007
  16. Johannes SchindelinOct 28, 2007
  17. Scott ParishOct 29, 2007
  18. Johannes SchindelinOct 29, 2007
  19. David SymondsOct 29, 2007
  20. 6/7 include $PATH in generating list of commands for "help -a"Scott R Parish, Oct 29, 2007
  21. Junio C HamanoOct 29, 2007
  22. Scott ParishOct 30, 2007
  23. Junio C HamanoOct 28, 2007
  24. Adam RobenOct 28, 2007
  25. 5/7 use only the $PATH for exec'ing git commandsScott R Parish, Oct 28, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.