{"thread":{"id":"2544","subject":"[PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","startedAt":"2005-11-16T00:23:18Z","lastAt":"2005-11-16T23:17:42Z","messageCount":9,"participants":["Andreas Ericsson","H. Peter Anvin","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"11969","messageId":"20051116002318.A46C55BF97@nox.op5.se","threadId":"2544","inReplyTo":null,"subject":"[PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"Andreas Ericsson","fromEmail":"exon@op5.se","sentAt":"2005-11-16T00:23:18Z","receivedAt":"2005-11-16T00:23:18Z","isPatch":true,"sender":{"key":"exon@op5.se","avatar":"https://gravatar.com/avatar/b948c4f759e868f8e721e545e37afe2cf89cfa8e2ee8b70a432f0d76aee39891?d=mp&s=160"},"body":"\nIt's by design a bit stupid (matching ^git rather than ^git-), so as\nto work with 'gitk' and 'git' as well.\n\nSigned-off-by: Andreas Ericsson <ae@op5.se>\n\n---\n\n Documentation/git.txt |    2 ++\n git.c                 |   26 ++++++++++++++++++++++++--\n 2 files changed, 26 insertions(+), 2 deletions(-)\n\napplies-to: 8a47ae8a825ab0e68ac46392bccd1ec16df39456\n53e2024f89514d31a45936e3596e3d285dfd1bfe\ndiff --git a/Documentation/git.txt b/Documentation/git.txt\nindex 91e9f9f..7cbfaf8 100644\n--- a/Documentation/git.txt\n+++ b/Documentation/git.txt\n@@ -24,6 +24,8 @@ OPTIONS\n \n --help::\n \tprints the synopsis and a list of available commands.\n+\tIf a git command is named this option will bring up the\n+\tman-page for that command.\n \n --exec-path::\n \tpath to wherever your core git programs are installed.\ndiff --git a/git.c b/git.c\nindex d189801..4b7cbf6 100644\n--- a/git.c\n+++ b/git.c\n@@ -160,6 +160,24 @@ static void prepend_to_path(const char *\n \tsetenv(\"PATH\", path, 1);\n }\n \n+static void show_man_page(char *git_cmd)\n+{\n+\tchar *page;\n+\n+\tif (!strncmp(git_cmd, \"git\", 3))\n+\t\tpage = git_cmd;\n+\telse {\n+\t\tint page_len = strlen(git_cmd) + 4;\n+\n+\t\tpage = malloc(page_len + 1);\n+\t\tstrcpy(page, \"git-\");\n+\t\tstrcpy(page + 4, git_cmd);\n+\t\tpage[page_len] = 0;\n+\t}\n+\n+\texeclp(\"man\", \"man\", page, NULL);\n+}\n+\n int main(int argc, char **argv, char **envp)\n {\n \tchar git_command[PATH_MAX + 1];\n@@ -199,8 +217,12 @@ int main(int argc, char **argv, char **e\n \t\t\tusage(NULL, NULL);\n \t}\n \n-\tif (i >= argc || show_help)\n-\t\tusage(exec_path, NULL);\n+\tif (i >= argc || show_help) {\n+\t\tif (i >= argc)\n+\t\t\tusage(exec_path, NULL);\n+\n+\t\tshow_man_page(argv[i]);\n+\t}\n \n \t/* allow relative paths, but run with exact */\n \tif (chdir(exec_path)) {\n---\n0.99.9.GIT\n"},{"id":"11992","messageId":"437AAB48.7090904@zytor.com","threadId":"2544","inReplyTo":"20051116002318.A46C55BF97@nox.op5.se","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"H. Peter Anvin","fromEmail":"hpa@zytor.com","sentAt":"2005-11-16T03:45:12Z","receivedAt":"2005-11-16T03:45:12Z","isPatch":true,"sender":{"key":"hpa@zytor.com","avatar":null},"body":"Andreas Ericsson wrote:\n>  \n> +static void show_man_page(char *git_cmd)\n> +{\n> +\tchar *page;\n> +\n> +\tif (!strncmp(git_cmd, \"git\", 3))\n> +\t\tpage = git_cmd;\n> +\telse {\n> +\t\tint page_len = strlen(git_cmd) + 4;\n> +\n> +\t\tpage = malloc(page_len + 1);\n> +\t\tstrcpy(page, \"git-\");\n> +\t\tstrcpy(page + 4, git_cmd);\n> +\t\tpage[page_len] = 0;\n> +\t}\n> +\n> +\texeclp(\"man\", \"man\", page, NULL);\n> +}\n> +\n\nThe way this made it into the actual tree was to call /usr/bin/man, but \nstill using execlp().  This is clearly bogus.  There *ARE* good reasons \nto use PATH resolutions for this, since man is one of the interactive \ncommands the user may want to wrapper.\n\nSo please drop PATH_TO_MAN that made it into the repository and revert \nto the original patch.\n\n\t-hpa\n"},{"id":"11996","messageId":"437AD82B.1050404@op5.se","threadId":"2544","inReplyTo":"437AAB48.7090904@zytor.com","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2005-11-16T06:56:43Z","receivedAt":"2005-11-16T06:56:43Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"H. Peter Anvin wrote:\n> Andreas Ericsson wrote:\n>> +\n>> +    execlp(\"man\", \"man\", page, NULL);\n>> +}\n>> +\n> \n> \n> The way this made it into the actual tree was to call /usr/bin/man, but \n> still using execlp().  This is clearly bogus.  There *ARE* good reasons \n> to use PATH resolutions for this, since man is one of the interactive \n> commands the user may want to wrapper.\n> \n\nEveryone agrees. I just brained the original implementation.\n\n> So please drop PATH_TO_MAN that made it into the repository and revert \n> to the original patch.\n> \n\nPATH_TO_MAN *was* the original. This is the updated version. I think \nJunio imported the wrong one by mistake, cause I sent this one \nspecifically to fix the first one.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"11998","messageId":"7vfypxawok.fsf_-_@assigned-by-dhcp.cox.net","threadId":"2544","inReplyTo":"437AD82B.1050404@op5.se","subject":"[PATCH] git wrapper: basic fixes.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-11-16T07:17:31Z","receivedAt":"2005-11-16T07:17:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Andreas Ericsson <ae@op5.se> writes:\n\n> PATH_TO_MAN *was* the original. This is the updated version. I think \n> Junio imported the wrong one by mistake, cause I sent this one \n> specifically to fix the first one.\n\nActually, I already had the original three in my tree before I\nsent out the message about problems like PATH_TO_MAN and\ngetcwd/chdir pair, because they were not such a big deal to fix\nlater in-tree, and I wanted to have the rest.\n\nHow does this one look, on top of what we have on the \"master\"\nbranch?\n\n-- >8 --\nUpdates to fix the nits found during the list discussion.\n\n - Lose PATH_TO_MAN; just rely on execlp() to find whereever the\n   \"man\" command is installed.\n\n - Do not randomly chdir(), but concatenate to the current\n   working directory only if the given path is not absolute.\n\n - Lose use of glob(); read from exec_path and do sorting\n   ourselves -- it is not that much more work.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n git.c |  152 +++++++++++++++++++++++++++++++++++++++++------------------------\n 1 files changed, 97 insertions(+), 55 deletions(-)\n\napplies-to: 1a3192c27df352fb11cb1f430ad174ecd90a3734\n7dbc2c0402d728a206d4f1bc59729bf3a5cc4455\ndiff --git a/git.c b/git.c\nindex 583923d..b9b8c62 100644\n--- a/git.c\n+++ b/git.c\n@@ -1,11 +1,13 @@\n #include <stdio.h>\n+#include <sys/types.h>\n+#include <sys/stat.h>\n+#include <dirent.h>\n #include <unistd.h>\n #include <stdlib.h>\n #include <string.h>\n #include <errno.h>\n #include <limits.h>\n #include <stdarg.h>\n-#include <glob.h>\n \n #ifndef PATH_MAX\n # define PATH_MAX 4096\n@@ -14,12 +16,6 @@\n static const char git_usage[] =\n \t\"Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]\";\n \n-struct string_list {\n-\tsize_t len;\n-\tchar *str;\n-\tstruct string_list *next;\n-};\n-\n /* most gui terms set COLUMNS (although some don't export it) */\n static int term_columns(void)\n {\n@@ -32,30 +28,69 @@ static int term_columns(void)\n \treturn 80;\n }\n \n+static void oom(void)\n+{\n+\tfprintf(stderr, \"git: out of memory\\n\");\n+\texit(1);\n+}\n+\n static inline void mput_char(char c, unsigned int num)\n {\n \twhile(num--)\n \t\tputchar(c);\n }\n \n-static void pretty_print_string_list(struct string_list *list, int longest)\n+static struct cmdname {\n+\tsize_t len;\n+\tchar name[1];\n+} **cmdname;\n+static int cmdname_alloc, cmdname_cnt;\n+\n+static void add_cmdname(const char *name, int len)\n+{\n+\tstruct cmdname *ent;\n+\tif (cmdname_alloc <= cmdname_cnt) {\n+\t\tcmdname_alloc = cmdname_alloc + 200;\n+\t\tcmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));\n+\t\tif (!cmdname)\n+\t\t\toom();\n+\t}\n+\tent = malloc(sizeof(*ent) + len);\n+\tif (!ent)\n+\t\toom();\n+\tent->len = len;\n+\tmemcpy(ent->name, name, len+1);\n+\tcmdname[cmdname_cnt++] = ent;\n+}\n+\n+static int cmdname_compare(const void *a_, const void *b_)\n+{\n+\tstruct cmdname *a = *(struct cmdname **)a_;\n+\tstruct cmdname *b = *(struct cmdname **)b_;\n+\treturn strcmp(a->name, b->name);\n+}\n+\n+static void pretty_print_string_list(struct cmdname **cmdname, int longest)\n {\n \tint cols = 1;\n \tint space = longest + 1; /* min 1 SP between words */\n \tint max_cols = term_columns() - 1; /* don't print *on* the edge */\n+\tint i;\n \n \tif (space < max_cols)\n \t\tcols = max_cols / space;\n \n-\twhile (list) {\n+\tqsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);\n+\n+\tfor (i = 0; i < cmdname_cnt; ) {\n \t\tint c;\n \t\tprintf(\"  \");\n \n-\t\tfor (c = cols; c && list; list = list->next) {\n-\t\t\tprintf(\"%s\", list->str);\n+\t\tfor (c = cols; c && i < cmdname_cnt; i++) {\n+\t\t\tprintf(\"%s\", cmdname[i]->name);\n \n \t\t\tif (--c)\n-\t\t\t\tmput_char(' ', space - list->len);\n+\t\t\t\tmput_char(' ', space - cmdname[i]->len);\n \t\t}\n \t\tputchar('\\n');\n \t}\n@@ -63,54 +98,53 @@ static void pretty_print_string_list(str\n \n static void list_commands(const char *exec_path, const char *pattern)\n {\n-\tstruct string_list *list = NULL, *tail = NULL;\n-\tunsigned int longest = 0, i;\n-\tglob_t gl;\n+\tunsigned int longest = 0;\n+\tchar path[PATH_MAX];\n+\tint dirlen;\n+\tDIR *dir = opendir(exec_path);\n+\tstruct dirent *de;\n \n-\tif (chdir(exec_path) < 0) {\n-\t\tprintf(\"git: '%s': %s\\n\", exec_path, strerror(errno));\n+\tif (!dir) {\n+\t\tfprintf(stderr, \"git: '%s': %s\\n\", exec_path, strerror(errno));\n \t\texit(1);\n \t}\n \n-\ti = glob(pattern, 0, NULL, &gl);\n-\tswitch(i) {\n-\tcase GLOB_NOSPACE:\n-\t\tputs(\"Out of memory when running glob()\");\n-\t\texit(2);\n-\tcase GLOB_ABORTED:\n-\t\tprintf(\"'%s': Read error: %s\\n\", exec_path, strerror(errno));\n-\t\texit(2);\n-\tcase GLOB_NOMATCH:\n-\t\tprintf(\"No git commands available in '%s'.\\n\", exec_path);\n-\t\tprintf(\"Do you need to specify --exec-path or set GIT_EXEC_PATH?\\n\");\n+\tdirlen = strlen(exec_path);\n+\tif (PATH_MAX - 20 < dirlen) {\n+\t\tfprintf(stderr, \"git: insanely long exec-path '%s'\\n\",\n+\t\t\texec_path);\n \t\texit(1);\n \t}\n \n-\tfor (i = 0; i < gl.gl_pathc; i++) {\n-\t\tint len = strlen(gl.gl_pathv[i] + 4);\n+\tmemcpy(path, exec_path, dirlen);\n+\tpath[dirlen++] = '/';\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tint entlen;\n \n-\t\tif (access(gl.gl_pathv[i], X_OK))\n+\t\tif (strncmp(de->d_name, \"git-\", 4))\n+\t\t\tcontinue;\n+\t\tstrcpy(path+dirlen, de->d_name);\n+\t\tif (stat(path, &st) || /* stat, not lstat */\n+\t\t    !S_ISREG(st.st_mode) ||\n+\t\t    !(st.st_mode & S_IXUSR))\n \t\t\tcontinue;\n \n-\t\tif (longest < len)\n-\t\t\tlongest = len;\n+\t\tentlen = strlen(de->d_name);\n \n-\t\tif (!tail)\n-\t\t\ttail = list = malloc(sizeof(struct string_list));\n-\t\telse {\n-\t\t\ttail->next = malloc(sizeof(struct string_list));\n-\t\t\ttail = tail->next;\n-\t\t}\n-\t\ttail->len = len;\n-\t\ttail->str = gl.gl_pathv[i] + 4;\n-\t\ttail->next = NULL;\n+\t\tif (longest < entlen)\n+\t\t\tlongest = entlen;\n+\n+\t\tadd_cmdname(de->d_name + 4, entlen-4);\n \t}\n+\tclosedir(dir);\n \n \tprintf(\"git commands available in '%s'\\n\", exec_path);\n \tprintf(\"----------------------------\");\n \tmput_char('-', strlen(exec_path));\n \tputchar('\\n');\n-\tpretty_print_string_list(list, longest);\n+\tpretty_print_string_list(cmdname, longest - 4);\n \tputchar('\\n');\n }\n \n@@ -146,7 +180,7 @@ static void prepend_to_path(const char *\n \tint path_len = len;\n \n \tif (!old_path)\n-\t\told_path = \"/bin:/usr/bin:.\";\n+\t\told_path = \"/usr/local/bin:/usr/bin:/bin\";\n \n \tpath_len = len + strlen(old_path) + 1;\n \n@@ -160,8 +194,6 @@ static void prepend_to_path(const char *\n \tsetenv(\"PATH\", path, 1);\n }\n \n-/* has anyone seen 'man' installed anywhere else than in /usr/bin? */\n-#define PATH_TO_MAN \"/usr/bin/man\"\n static void show_man_page(char *git_cmd)\n {\n \tchar *page;\n@@ -177,7 +209,7 @@ static void show_man_page(char *git_cmd)\n \t\tpage[page_len] = 0;\n \t}\n \n-\texeclp(PATH_TO_MAN, \"man\", page, NULL);\n+\texeclp(\"man\", \"man\", page, NULL);\n }\n \n int main(int argc, char **argv, char **envp)\n@@ -226,15 +258,25 @@ int main(int argc, char **argv, char **e\n \t\tshow_man_page(argv[i]);\n \t}\n \n-\t/* allow relative paths, but run with exact */\n-\tif (chdir(exec_path)) {\n-\t\tprintf(\"git: '%s': %s\\n\", exec_path, strerror(errno));\n-\t\texit (1);\n-\t}\n-\n-\tgetcwd(git_command, sizeof(git_command));\n-\tchdir(wd);\n+\tif (*exec_path != '/') {\n+\t\tif (!getcwd(git_command, sizeof(git_command))) {\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"git: cannot determine current directory\");\n+\t\t\texit(1);\n+\t\t}\n+\t\tlen = strlen(git_command);\n \n+\t\t/* Trivial cleanup */\n+\t\twhile (!strncmp(exec_path, \"./\", 2)) {\n+\t\t\texec_path += 2;\n+\t\t\twhile (*exec_path == '/')\n+\t\t\t\t*exec_path++;\n+\t\t}\n+\t\tsnprintf(git_command + len, sizeof(git_command) - len,\n+\t\t\t \"/%s\", exec_path);\n+\t}\n+\telse\n+\t\tstrcpy(git_command, exec_path);\n \tlen = strlen(git_command);\n \tprepend_to_path(git_command, len);\n \n---\n0.99.9.GIT\n"},{"id":"11999","messageId":"7vbr0law3z.fsf@assigned-by-dhcp.cox.net","threadId":"2544","inReplyTo":"437AAB48.7090904@zytor.com","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2005-11-16T07:29:52Z","receivedAt":"2005-11-16T07:29:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"H. Peter Anvin\" <hpa@zytor.com> writes:\n\n> The way this made it into the actual tree was to call /usr/bin/man, but \n> still using execlp().  This is clearly bogus.  There *ARE* good reasons \n> to use PATH resolutions for this, since man is one of the interactive \n> commands the user may want to wrapper.\n\nOh, that was my call, so please do not blame Andreas.\nI just sent out a proposed patch to address all the points\ndiscussed on the list for the last several hours.\n"},{"id":"12002","messageId":"Pine.LNX.4.63.0511160921520.16172@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"2544","inReplyTo":"7vfypxawok.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git wrapper: basic fixes.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2005-11-16T08:22:36Z","receivedAt":"2005-11-16T08:22:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 15 Nov 2005, Junio C Hamano wrote:\n\n>  - Lose use of glob(); read from exec_path and do sorting\n>    ourselves -- it is not that much more work.\n\nClearly a better fix than my patch.\n\nCiao,\nDscho\n"},{"id":"12051","messageId":"437BAA5F.5060608@zytor.com","threadId":"2544","inReplyTo":"7vbr0law3z.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"H. Peter Anvin","fromEmail":"hpa@zytor.com","sentAt":"2005-11-16T21:53:35Z","receivedAt":"2005-11-16T21:53:35Z","isPatch":true,"sender":{"key":"hpa@zytor.com","avatar":null},"body":"Junio C Hamano wrote:\n> \"H. Peter Anvin\" <hpa@zytor.com> writes:\n> \n>>The way this made it into the actual tree was to call /usr/bin/man, but \n>>still using execlp().  This is clearly bogus.  There *ARE* good reasons \n>>to use PATH resolutions for this, since man is one of the interactive \n>>commands the user may want to wrapper.\n> \n> Oh, that was my call, so please do not blame Andreas.\n> I just sent out a proposed patch to address all the points\n> discussed on the list for the last several hours.\n> \n\nFWIW, I rarely blame people for bad code; *everyone* does something \nstupid every now and then, and for most of us, far more often than that. \n  \"This piece of code is stupid\" != \"the author of this piece of code is \nstupid\", a distinction which unfortunately often gets lost.\n\nI say \"rarely\", because there are of course a small number of people who \n*consistently* produce crap.  However, even they occationally produce \nsomething useful -- it's just not that often, and one has to carefully \nreview it first.  With sufficient thrust pigs fly just fine[*], and \nheck, even RBJ even occationally says something correct.\n\n\t-hpa\n\n[*] It is, however, not necessarily a good idea.  It can be dangerous \nfor the people on the ground, and annoys the pig.\n"},{"id":"12056","messageId":"437BBDD6.40809@op5.se","threadId":"2544","inReplyTo":"437BAA5F.5060608@zytor.com","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2005-11-16T23:16:38Z","receivedAt":"2005-11-16T23:16:38Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"H. Peter Anvin wrote:\n> Junio C Hamano wrote:\n> \n>> \"H. Peter Anvin\" <hpa@zytor.com> writes:\n>>\n>>> The way this made it into the actual tree was to call /usr/bin/man, \n>>> but still using execlp().  This is clearly bogus.  There *ARE* good \n>>> reasons to use PATH resolutions for this, since man is one of the \n>>> interactive commands the user may want to wrapper.\n>>\n>>\n>> Oh, that was my call, so please do not blame Andreas.\n>> I just sent out a proposed patch to address all the points\n>> discussed on the list for the last several hours.\n>>\n> \n> FWIW, I rarely blame people for bad code; *everyone* does something \n> stupid every now and then, and for most of us, far more often than that. \n>  \"This piece of code is stupid\" != \"the author of this piece of code is \n> stupid\", a distinction which unfortunately often gets lost.\n> \n\nTrue, but it's possible to have a 180 iq and still know nothing about \nprogramming. Anyhow, I sent the first patch too, so please blame me if \nanyone. If I was hoping to be admired I'd go play video-games with my 6 \nyear old cousin.\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\nTel: +46 8-230225                  Fax: +46 8-230231\n"},{"id":"12055","messageId":"437BBE16.3090109@zytor.com","threadId":"2544","inReplyTo":"437BBDD6.40809@op5.se","subject":"Re: [PATCH 3/3] git --help COMMAND brings up the git-COMMAND man-page., take two","fromName":"H. Peter Anvin","fromEmail":"hpa@zytor.com","sentAt":"2005-11-16T23:17:42Z","receivedAt":"2005-11-16T23:17:42Z","isPatch":true,"sender":{"key":"hpa@zytor.com","avatar":null},"body":"Andreas Ericsson wrote:\n> \n> True, but it's possible to have a 180 iq and still know nothing about \n> programming. Anyhow, I sent the first patch too, so please blame me if \n> anyone. If I was hoping to be admired I'd go play video-games with my 6 \n> year old cousin.\n> \n\nThe point was: mistakes happen.  No point in arguing about whose fault \nit was.\n\n\t-hpa\n"}]}