{"thread":{"id":"11090","subject":"[PATCH 1/3] Prepare execv_git_cmd() for removal of builtins from the filesystem","startedAt":"2007-12-02T07:03:23Z","lastAt":"2007-12-02T12:24:20Z","messageCount":5,"participants":["Junio C Hamano","Christian Couder","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"61625","messageId":"1196579005-5662-1-git-send-email-gitster@pobox.com","threadId":"11090","inReplyTo":null,"subject":"[PATCH 1/3] Prepare execv_git_cmd() for removal of builtins from the filesystem","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-02T07:03:23Z","receivedAt":"2007-12-02T07:03:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Currently, execv_git_cmd() always try running the dashed form, which\nmeans we cannot easily remove the git-foo hardlinks for built-in\ncommands.  This updates the function to always exec \"git foo\" form, and\nmakes sure \"git\" potty does not infinitely recurse to itself.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n exec_cmd.c |   31 ++++++++++++-------------------\n git.c      |   32 +++++++++++++++++++++++++++++++-\n 2 files changed, 43 insertions(+), 20 deletions(-)\n\ndiff --git a/exec_cmd.c b/exec_cmd.c\nindex 2d0a758..10b2908 100644\n--- a/exec_cmd.c\n+++ b/exec_cmd.c\n@@ -65,32 +65,25 @@ void setup_path(const char *cmd_path)\n \n int execv_git_cmd(const char **argv)\n {\n-\tstruct strbuf cmd;\n-\tconst char *tmp;\n-\n-\tstrbuf_init(&cmd, 0);\n-\tstrbuf_addf(&cmd, \"git-%s\", argv[0]);\n+\tint argc;\n+\tconst char **nargv;\n \n-\t/*\n-\t * argv[0] must be the git command, but the argv array\n-\t * belongs to the caller, and may be reused in\n-\t * subsequent loop iterations. Save argv[0] and\n-\t * restore it on error.\n-\t */\n-\ttmp = argv[0];\n-\targv[0] = cmd.buf;\n+\tfor (argc = 0; argv[argc]; argc++)\n+\t\t; /* just counting */\n+\tnargv = xmalloc(sizeof(*nargv) * (argc + 2));\n \n-\ttrace_argv_printf(argv, -1, \"trace: exec:\");\n+\tnargv[0] = \"git\";\n+\tfor (argc = 0; argv[argc]; argc++)\n+\t\tnargv[argc + 1] = argv[argc];\n+\tnargv[argc + 1] = NULL;\n+\ttrace_argv_printf(nargv, -1, \"trace: exec:\");\n \n \t/* execvp() can only ever return if it fails */\n-\texecvp(cmd.buf, (char **)argv);\n+\texecvp(\"git\", (char **)nargv);\n \n \ttrace_printf(\"trace: exec failed: %s\\n\", strerror(errno));\n \n-\targv[0] = tmp;\n-\n-\tstrbuf_release(&cmd);\n-\n+\tfree(nargv);\n \treturn -1;\n }\n \ndiff --git a/git.c b/git.c\nindex 01bbbc7..d690426 100644\n--- a/git.c\n+++ b/git.c\n@@ -382,6 +382,36 @@ static void handle_internal_command(int argc, const char **argv)\n \t}\n }\n \n+static void execv_dashed_external(const char **argv)\n+{\n+\tstruct strbuf cmd;\n+\tconst char *tmp;\n+\n+\tstrbuf_init(&cmd, 0);\n+\tstrbuf_addf(&cmd, \"git-%s\", argv[0]);\n+\n+\t/*\n+\t * argv[0] must be the git command, but the argv array\n+\t * belongs to the caller, and may be reused in\n+\t * subsequent loop iterations. Save argv[0] and\n+\t * restore it on error.\n+\t */\n+\ttmp = argv[0];\n+\targv[0] = cmd.buf;\n+\n+\ttrace_argv_printf(argv, -1, \"trace: exec:\");\n+\n+\t/* execvp() can only ever return if it fails */\n+\texecvp(cmd.buf, (char **)argv);\n+\n+\ttrace_printf(\"trace: exec failed: %s\\n\", strerror(errno));\n+\n+\targv[0] = tmp;\n+\n+\tstrbuf_release(&cmd);\n+}\n+\n+\n int main(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0] ? argv[0] : \"git-help\";\n@@ -445,7 +475,7 @@ int main(int argc, const char **argv)\n \t\thandle_internal_command(argc, argv);\n \n \t\t/* .. then try the external ones */\n-\t\texecv_git_cmd(argv);\n+\t\texecv_dashed_external(argv);\n \n \t\t/* It could be an alias -- this works around the insanity\n \t\t * of overriding \"git log\" with \"git show\" by having\n-- \n1.5.3.6.2090.g4ece0\n"},{"id":"61626","messageId":"1196579005-5662-2-git-send-email-gitster@pobox.com","threadId":"11090","inReplyTo":"1196579005-5662-1-git-send-email-gitster@pobox.com","subject":"[PATCH 2/3] Avoid double exec() in execv_git_cmd()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-02T07:03:24Z","receivedAt":"2007-12-02T07:03:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This avoids double exec() when execv_git_cmd() is used to spawn an\nexternal git command.\n\nFor this, execv_git_cmd() needs to be able to tell what's built-in.\nThis is done by introducing a new function git_builtin_command() that\nchecks the list of built-in commands and returns a pointer to the\ncmd_struct if found.  The same function is used from the git potty.\n\nThis unfortunately pulls all built-in functions to libgit.a, so any\nexternal command that happens to link with exec_cmd.o will need to pull\nthem in.  I am not sure if that is worth it.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n Makefile   |    7 +-\n exec_cmd.c |  208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----\n exec_cmd.h |    8 ++\n git.c      |  155 +-------------------------------------------\n 4 files changed, 210 insertions(+), 168 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex a7e44a3..6907954 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -893,7 +893,8 @@ git.o git.spec \\\n \t$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<\n \n exec_cmd.o: exec_cmd.c GIT-CFLAGS\n-\t$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH=\"$(gitexecdir_SQ)\"' $<\n+\t$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \\\n+\t\t-DGIT_VERSION='\"$(GIT_VERSION)\"' '-DGIT_EXEC_PATH=\"$(gitexecdir_SQ)\"' $<\n builtin-init-db.o: builtin-init-db.c GIT-CFLAGS\n \t$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='\"$(template_dir_SQ)\"' $<\n \n@@ -923,8 +924,8 @@ $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)\n $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)\n builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h\n \n-$(LIB_FILE): $(LIB_OBJS)\n-\t$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)\n+$(LIB_FILE): $(LIB_OBJS) $(BUILTIN_OBJS)\n+\t$(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) $(BUILTIN_OBJS)\n \n XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \\\n \txdiff/xmerge.o\ndiff --git a/exec_cmd.c b/exec_cmd.c\nindex 10b2908..db61540 100644\n--- a/exec_cmd.c\n+++ b/exec_cmd.c\n@@ -1,3 +1,4 @@\n+#include \"builtin.h\"\n #include \"cache.h\"\n #include \"exec_cmd.h\"\n #include \"quote.h\"\n@@ -63,27 +64,206 @@ void setup_path(const char *cmd_path)\n \tstrbuf_release(&new_path);\n }\n \n+#define RUN_SETUP\t(1<<0)\n+#define USE_PAGER\t(1<<1)\n+/*\n+ * require working tree to be present -- anything uses this needs\n+ * RUN_SETUP for reading from the configuration file.\n+ */\n+#define NEED_WORK_TREE\t(1<<2)\n+\n+const char git_version_string[] = GIT_VERSION;\n+const char git_usage_string[] =\n+\t\"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]\";\n+\n+int run_builtin_command(struct cmd_struct *p, int argc, const char **argv)\n+{\n+\tint status;\n+\tstruct stat st;\n+\tconst char *prefix;\n+\n+\tprefix = NULL;\n+\tif (p->option & RUN_SETUP)\n+\t\tprefix = setup_git_directory();\n+\tif (p->option & USE_PAGER)\n+\t\tsetup_pager();\n+\tif (p->option & NEED_WORK_TREE)\n+\t\tsetup_work_tree();\n+\n+\ttrace_argv_printf(argv, argc, \"trace: built-in: git\");\n+\n+\tstatus = p->fn(argc, argv, prefix);\n+\tif (status)\n+\t\treturn status & 0xff;\n+\n+\t/* Somebody closed stdout? */\n+\tif (fstat(fileno(stdout), &st))\n+\t\treturn 0;\n+\t/* Ignore write errors for pipes and sockets.. */\n+\tif (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))\n+\t\treturn 0;\n+\n+\t/* Check for ENOSPC and EIO errors.. */\n+\tif (fflush(stdout))\n+\t\tdie(\"write failure on standard output: %s\", strerror(errno));\n+\tif (ferror(stdout))\n+\t\tdie(\"unknown write failure on standard output\");\n+\tif (fclose(stdout))\n+\t\tdie(\"close failed on standard output: %s\", strerror(errno));\n+\treturn 0;\n+}\n+\n+static struct cmd_struct builtin_command[] = {\n+\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n+\t{ \"apply\", cmd_apply },\n+\t{ \"archive\", cmd_archive },\n+\t{ \"blame\", cmd_blame, RUN_SETUP },\n+\t{ \"branch\", cmd_branch, RUN_SETUP },\n+\t{ \"bundle\", cmd_bundle },\n+\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n+\t{ \"check-attr\", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"check-ref-format\", cmd_check_ref_format },\n+\t{ \"checkout-index\", cmd_checkout_index, RUN_SETUP | NEED_WORK_TREE},\n+\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n+\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n+\t{ \"config\", cmd_config },\n+\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n+\t{ \"describe\", cmd_describe, RUN_SETUP },\n+\t{ \"diff\", cmd_diff },\n+\t{ \"diff-files\", cmd_diff_files },\n+\t{ \"diff-index\", cmd_diff_index, RUN_SETUP },\n+\t{ \"diff-tree\", cmd_diff_tree, RUN_SETUP },\n+\t{ \"fetch\", cmd_fetch, RUN_SETUP },\n+\t{ \"fetch--tool\", cmd_fetch__tool, RUN_SETUP },\n+\t{ \"fetch-pack\", cmd_fetch_pack, RUN_SETUP },\n+\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\n+\t{ \"for-each-ref\", cmd_for_each_ref, RUN_SETUP },\n+\t{ \"format-patch\", cmd_format_patch, RUN_SETUP },\n+\t{ \"fsck\", cmd_fsck, RUN_SETUP },\n+\t{ \"fsck-objects\", cmd_fsck, RUN_SETUP },\n+\t{ \"gc\", cmd_gc, RUN_SETUP },\n+\t{ \"get-tar-commit-id\", cmd_get_tar_commit_id },\n+\t{ \"grep\", cmd_grep, RUN_SETUP | USE_PAGER },\n+\t{ \"help\", cmd_help },\n+#ifndef NO_CURL\n+\t{ \"http-fetch\", cmd_http_fetch, RUN_SETUP },\n+#endif\n+\t{ \"init\", cmd_init_db },\n+\t{ \"init-db\", cmd_init_db },\n+\t{ \"log\", cmd_log, RUN_SETUP | USE_PAGER },\n+\t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n+\t{ \"ls-remote\", cmd_ls_remote },\n+\t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n+\t{ \"mailinfo\", cmd_mailinfo },\n+\t{ \"mailsplit\", cmd_mailsplit },\n+\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n+\t{ \"merge-file\", cmd_merge_file },\n+\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\n+\t{ \"mv\", cmd_mv, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"name-rev\", cmd_name_rev, RUN_SETUP },\n+\t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n+\t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n+\t{ \"peek-remote\", cmd_ls_remote },\n+\t{ \"pickaxe\", cmd_blame, RUN_SETUP },\n+\t{ \"prune\", cmd_prune, RUN_SETUP },\n+\t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n+\t{ \"push\", cmd_push, RUN_SETUP },\n+\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n+\t{ \"reflog\", cmd_reflog, RUN_SETUP },\n+\t{ \"repo-config\", cmd_config },\n+\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n+\t{ \"reset\", cmd_reset, RUN_SETUP },\n+\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\n+\t{ \"rev-parse\", cmd_rev_parse },\n+\t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"rm\", cmd_rm, RUN_SETUP },\n+\t{ \"runstatus\", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },\n+\t{ \"send-pack\", cmd_send_pack, RUN_SETUP },\n+\t{ \"shortlog\", cmd_shortlog, RUN_SETUP | USE_PAGER },\n+\t{ \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n+\t{ \"show-branch\", cmd_show_branch, RUN_SETUP },\n+\t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n+\t{ \"stripspace\", cmd_stripspace },\n+\t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n+\t{ \"tag\", cmd_tag, RUN_SETUP },\n+\t{ \"tar-tree\", cmd_tar_tree },\n+\t{ \"unpack-objects\", cmd_unpack_objects, RUN_SETUP },\n+\t{ \"update-index\", cmd_update_index, RUN_SETUP },\n+\t{ \"update-ref\", cmd_update_ref, RUN_SETUP },\n+\t{ \"upload-archive\", cmd_upload_archive },\n+\t{ \"verify-pack\", cmd_verify_pack },\n+\t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n+\t{ \"version\", cmd_version },\n+\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | USE_PAGER },\n+\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n+};\n+\n+struct cmd_struct *git_builtin_command(const char *name)\n+{\n+\tunsigned int hi, lo;\n+\tlo = 0;\n+\thi = ARRAY_SIZE(builtin_command);\n+\tdo {\n+\t\tunsigned mi = (lo + hi) / 2;\n+\t\tint cmp = strcmp(builtin_command[mi].cmd, name);\n+\t\tif (!cmp)\n+\t\t\treturn &builtin_command[mi];\n+\t\tif (cmp > 0)\n+\t\t\thi = mi;\n+\t\telse\n+\t\t\tlo = mi + 1;\n+\t} while (lo < hi);\n+\treturn NULL;\n+}\n+\n int execv_git_cmd(const char **argv)\n {\n-\tint argc;\n-\tconst char **nargv;\n+\t/*\n+\t * We exec \"git foo\" for builtin commands, but exec \"git-foo\"\n+\t * if \"foo\" is not a built-in, to avoid double exec overhead.\n+\t */\n+\tif (!git_builtin_command(argv[0])) {\n+\t\tstruct strbuf cmd;\n+\t\tconst char *save;\n+\n+\t\tstrbuf_init(&cmd, 0);\n+\t\tstrbuf_addf(&cmd, \"git-%s\", argv[0]);\n \n-\tfor (argc = 0; argv[argc]; argc++)\n-\t\t; /* just counting */\n-\tnargv = xmalloc(sizeof(*nargv) * (argc + 2));\n+\t\tsave = argv[0];\n+\t\targv[0] = cmd.buf;\n+\t\ttrace_argv_printf(argv, -1, \"trace: exec:\");\n \n-\tnargv[0] = \"git\";\n-\tfor (argc = 0; argv[argc]; argc++)\n-\t\tnargv[argc + 1] = argv[argc];\n-\tnargv[argc + 1] = NULL;\n-\ttrace_argv_printf(nargv, -1, \"trace: exec:\");\n+\t\texecvp(argv[0], (char **)argv);\n \n-\t/* execvp() can only ever return if it fails */\n-\texecvp(\"git\", (char **)nargv);\n+\t\ttrace_printf(\"trace: exec failed: %s\\n\", strerror(errno));\n+\t\tstrbuf_release(&cmd);\n+\t\targv[0] = save;\n+\t}\n+\telse {\n+\t\tint argc;\n+\t\tconst char **nargv;\n \n-\ttrace_printf(\"trace: exec failed: %s\\n\", strerror(errno));\n+\t\tfor (argc = 0; argv[argc]; argc++)\n+\t\t\t; /* just counting */\n+\t\tnargv = xmalloc(sizeof(*nargv) * (argc + 2));\n \n-\tfree(nargv);\n+\t\tnargv[0] = \"git\";\n+\t\tfor (argc = 0; argv[argc]; argc++)\n+\t\t\tnargv[argc + 1] = argv[argc];\n+\t\tnargv[argc + 1] = NULL;\n+\n+\t\ttrace_argv_printf(nargv, -1, \"trace: exec:\");\n+\n+\t\t/* execvp() can only ever return if it fails */\n+\t\texecvp(\"git\", (char **)nargv);\n+\n+\t\ttrace_printf(\"trace: exec failed: %s\\n\", strerror(errno));\n+\n+\t\tfree(nargv);\n+\t}\n \treturn -1;\n }\n \ndiff --git a/exec_cmd.h b/exec_cmd.h\nindex a892355..184bc1d 100644\n--- a/exec_cmd.h\n+++ b/exec_cmd.h\n@@ -7,5 +7,13 @@ extern void setup_path(const char *);\n extern int execv_git_cmd(const char **argv); /* NULL terminated */\n extern int execl_git_cmd(const char *cmd, ...);\n \n+struct cmd_struct {\n+\tconst char *cmd;\n+\tint (*fn)(int, const char **, const char *);\n+\tint option;\n+};\n+\n+extern struct cmd_struct *git_builtin_command(const char *);\n+extern int run_builtin_command(struct cmd_struct *p, int argc, const char **argv);\n \n #endif /* GIT_EXEC_CMD_H */\ndiff --git a/git.c b/git.c\nindex d690426..9d86f5c 100644\n--- a/git.c\n+++ b/git.c\n@@ -3,9 +3,6 @@\n #include \"cache.h\"\n #include \"quote.h\"\n \n-const char git_usage_string[] =\n-\t\"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]\";\n-\n static int handle_options(const char*** argv, int* argc, int* envchanged)\n {\n \tint handled = 0;\n@@ -222,151 +219,10 @@ static int handle_alias(int *argcp, const char ***argv)\n \treturn ret;\n }\n \n-const char git_version_string[] = GIT_VERSION;\n-\n-#define RUN_SETUP\t(1<<0)\n-#define USE_PAGER\t(1<<1)\n-/*\n- * require working tree to be present -- anything uses this needs\n- * RUN_SETUP for reading from the configuration file.\n- */\n-#define NEED_WORK_TREE\t(1<<2)\n-\n-struct cmd_struct {\n-\tconst char *cmd;\n-\tint (*fn)(int, const char **, const char *);\n-\tint option;\n-};\n-\n-static int run_command(struct cmd_struct *p, int argc, const char **argv)\n-{\n-\tint status;\n-\tstruct stat st;\n-\tconst char *prefix;\n-\n-\tprefix = NULL;\n-\tif (p->option & RUN_SETUP)\n-\t\tprefix = setup_git_directory();\n-\tif (p->option & USE_PAGER)\n-\t\tsetup_pager();\n-\tif (p->option & NEED_WORK_TREE)\n-\t\tsetup_work_tree();\n-\n-\ttrace_argv_printf(argv, argc, \"trace: built-in: git\");\n-\n-\tstatus = p->fn(argc, argv, prefix);\n-\tif (status)\n-\t\treturn status & 0xff;\n-\n-\t/* Somebody closed stdout? */\n-\tif (fstat(fileno(stdout), &st))\n-\t\treturn 0;\n-\t/* Ignore write errors for pipes and sockets.. */\n-\tif (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))\n-\t\treturn 0;\n-\n-\t/* Check for ENOSPC and EIO errors.. */\n-\tif (fflush(stdout))\n-\t\tdie(\"write failure on standard output: %s\", strerror(errno));\n-\tif (ferror(stdout))\n-\t\tdie(\"unknown write failure on standard output\");\n-\tif (fclose(stdout))\n-\t\tdie(\"close failed on standard output: %s\", strerror(errno));\n-\treturn 0;\n-}\n-\n static void handle_internal_command(int argc, const char **argv)\n {\n \tconst char *cmd = argv[0];\n-\tstatic struct cmd_struct commands[] = {\n-\t\t{ \"add\", cmd_add, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"annotate\", cmd_annotate, RUN_SETUP },\n-\t\t{ \"apply\", cmd_apply },\n-\t\t{ \"archive\", cmd_archive },\n-\t\t{ \"blame\", cmd_blame, RUN_SETUP },\n-\t\t{ \"branch\", cmd_branch, RUN_SETUP },\n-\t\t{ \"bundle\", cmd_bundle },\n-\t\t{ \"cat-file\", cmd_cat_file, RUN_SETUP },\n-\t\t{ \"checkout-index\", cmd_checkout_index,\n-\t\t\tRUN_SETUP | NEED_WORK_TREE},\n-\t\t{ \"check-ref-format\", cmd_check_ref_format },\n-\t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n-\t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\n-\t\t{ \"config\", cmd_config },\n-\t\t{ \"count-objects\", cmd_count_objects, RUN_SETUP },\n-\t\t{ \"describe\", cmd_describe, RUN_SETUP },\n-\t\t{ \"diff\", cmd_diff },\n-\t\t{ \"diff-files\", cmd_diff_files },\n-\t\t{ \"diff-index\", cmd_diff_index, RUN_SETUP },\n-\t\t{ \"diff-tree\", cmd_diff_tree, RUN_SETUP },\n-\t\t{ \"fetch\", cmd_fetch, RUN_SETUP },\n-\t\t{ \"fetch-pack\", cmd_fetch_pack, RUN_SETUP },\n-\t\t{ \"fetch--tool\", cmd_fetch__tool, RUN_SETUP },\n-\t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg, RUN_SETUP },\n-\t\t{ \"for-each-ref\", cmd_for_each_ref, RUN_SETUP },\n-\t\t{ \"format-patch\", cmd_format_patch, RUN_SETUP },\n-\t\t{ \"fsck\", cmd_fsck, RUN_SETUP },\n-\t\t{ \"fsck-objects\", cmd_fsck, RUN_SETUP },\n-\t\t{ \"gc\", cmd_gc, RUN_SETUP },\n-\t\t{ \"get-tar-commit-id\", cmd_get_tar_commit_id },\n-\t\t{ \"grep\", cmd_grep, RUN_SETUP | USE_PAGER },\n-\t\t{ \"help\", cmd_help },\n-#ifndef NO_CURL\n-\t\t{ \"http-fetch\", cmd_http_fetch, RUN_SETUP },\n-#endif\n-\t\t{ \"init\", cmd_init_db },\n-\t\t{ \"init-db\", cmd_init_db },\n-\t\t{ \"log\", cmd_log, RUN_SETUP | USE_PAGER },\n-\t\t{ \"ls-files\", cmd_ls_files, RUN_SETUP },\n-\t\t{ \"ls-tree\", cmd_ls_tree, RUN_SETUP },\n-\t\t{ \"ls-remote\", cmd_ls_remote },\n-\t\t{ \"mailinfo\", cmd_mailinfo },\n-\t\t{ \"mailsplit\", cmd_mailsplit },\n-\t\t{ \"merge-base\", cmd_merge_base, RUN_SETUP },\n-\t\t{ \"merge-file\", cmd_merge_file },\n-\t\t{ \"merge-ours\", cmd_merge_ours, RUN_SETUP },\n-\t\t{ \"mv\", cmd_mv, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"name-rev\", cmd_name_rev, RUN_SETUP },\n-\t\t{ \"pack-objects\", cmd_pack_objects, RUN_SETUP },\n-\t\t{ \"peek-remote\", cmd_ls_remote },\n-\t\t{ \"pickaxe\", cmd_blame, RUN_SETUP },\n-\t\t{ \"prune\", cmd_prune, RUN_SETUP },\n-\t\t{ \"prune-packed\", cmd_prune_packed, RUN_SETUP },\n-\t\t{ \"push\", cmd_push, RUN_SETUP },\n-\t\t{ \"read-tree\", cmd_read_tree, RUN_SETUP },\n-\t\t{ \"reflog\", cmd_reflog, RUN_SETUP },\n-\t\t{ \"repo-config\", cmd_config },\n-\t\t{ \"rerere\", cmd_rerere, RUN_SETUP },\n-\t\t{ \"reset\", cmd_reset, RUN_SETUP },\n-\t\t{ \"rev-list\", cmd_rev_list, RUN_SETUP },\n-\t\t{ \"rev-parse\", cmd_rev_parse },\n-\t\t{ \"revert\", cmd_revert, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"rm\", cmd_rm, RUN_SETUP },\n-\t\t{ \"runstatus\", cmd_runstatus, RUN_SETUP | NEED_WORK_TREE },\n-\t\t{ \"send-pack\", cmd_send_pack, RUN_SETUP },\n-\t\t{ \"shortlog\", cmd_shortlog, RUN_SETUP | USE_PAGER },\n-\t\t{ \"show-branch\", cmd_show_branch, RUN_SETUP },\n-\t\t{ \"show\", cmd_show, RUN_SETUP | USE_PAGER },\n-\t\t{ \"stripspace\", cmd_stripspace },\n-\t\t{ \"symbolic-ref\", cmd_symbolic_ref, RUN_SETUP },\n-\t\t{ \"tag\", cmd_tag, RUN_SETUP },\n-\t\t{ \"tar-tree\", cmd_tar_tree },\n-\t\t{ \"unpack-objects\", cmd_unpack_objects, RUN_SETUP },\n-\t\t{ \"update-index\", cmd_update_index, RUN_SETUP },\n-\t\t{ \"update-ref\", cmd_update_ref, RUN_SETUP },\n-\t\t{ \"upload-archive\", cmd_upload_archive },\n-\t\t{ \"verify-tag\", cmd_verify_tag, RUN_SETUP },\n-\t\t{ \"version\", cmd_version },\n-\t\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | USE_PAGER },\n-\t\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n-\t\t{ \"verify-pack\", cmd_verify_pack },\n-\t\t{ \"show-ref\", cmd_show_ref, RUN_SETUP },\n-\t\t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n-\t};\n-\tint i;\n+\tstruct cmd_struct *builtin;\n \n \t/* Turn \"git cmd --help\" into \"git help cmd\" */\n \tif (argc > 1 && !strcmp(argv[1], \"--help\")) {\n@@ -374,12 +230,9 @@ static void handle_internal_command(int argc, const char **argv)\n \t\targv[0] = cmd = \"help\";\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(commands); i++) {\n-\t\tstruct cmd_struct *p = commands+i;\n-\t\tif (strcmp(p->cmd, cmd))\n-\t\t\tcontinue;\n-\t\texit(run_command(p, argc, argv));\n-\t}\n+\tbuiltin = git_builtin_command(cmd);\n+\tif (builtin)\n+\t\texit(run_builtin_command(builtin, argc, argv));\n }\n \n static void execv_dashed_external(const char **argv)\n-- \n1.5.3.6.2090.g4ece0\n"},{"id":"61627","messageId":"1196579005-5662-3-git-send-email-gitster@pobox.com","threadId":"11090","inReplyTo":"1196579005-5662-2-git-send-email-gitster@pobox.com","subject":"[PATCH 3/3] git-shell: accept \"git foo\" form","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-02T07:03:25Z","receivedAt":"2007-12-02T07:03:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Signed-off-by: Junio C Hamano <gitster@pobox.com>\n---\n shell.c |   26 +++++++++++++++-----------\n 1 files changed, 15 insertions(+), 11 deletions(-)\n\ndiff --git a/shell.c b/shell.c\nindex 9826109..729797c 100644\n--- a/shell.c\n+++ b/shell.c\n@@ -19,17 +19,13 @@ static int do_generic_cmd(const char *me, char *arg)\n \treturn execv_git_cmd(my_argv);\n }\n \n-static int do_cvs_cmd(const char *me, char *arg)\n+static int do_cvs_cmd(void)\n {\n \tconst char *cvsserver_argv[3] = {\n \t\t\"cvsserver\", \"server\", NULL\n \t};\n \n-\tif (!arg || strcmp(arg, \"server\"))\n-\t\tdie(\"git-cvsserver only handles server: %s\", arg);\n-\n \tsetup_path(NULL);\n-\n \treturn execv_git_cmd(cvsserver_argv);\n }\n \n@@ -40,7 +36,6 @@ static struct commands {\n } cmd_list[] = {\n \t{ \"git-receive-pack\", do_generic_cmd },\n \t{ \"git-upload-pack\", do_generic_cmd },\n-\t{ \"cvs\", do_cvs_cmd },\n \t{ NULL },\n };\n \n@@ -49,15 +44,24 @@ int main(int argc, char **argv)\n \tchar *prog;\n \tstruct commands *cmd;\n \n+\t/*\n+\t * Special hack to pretend to be a CVS server\n+\t */\n \tif (argc == 2 && !strcmp(argv[1], \"cvs server\"))\n-\t\targv--;\n-\t/* We want to see \"-c cmd args\", and nothing else */\n-\telse if (argc != 3 || strcmp(argv[1], \"-c\"))\n+\t\texit(do_cvs_cmd());\n+\n+\t/*\n+\t * We do not accept anything but \"-c\" followed by \"cmd arg\",\n+\t * where \"cmd\" is a very limited subset of git commands.\n+\t */\n+\tif (argc != 3 || strcmp(argv[1], \"-c\"))\n \t\tdie(\"What do you think I am? A shell?\");\n \n \tprog = argv[2];\n-\targv += 2;\n-\targc -= 2;\n+\tif (!strncmp(prog, \"git\", 3) && isspace(prog[3]))\n+\t\t/* Accept \"git foo\" as if the caller said \"git-foo\". */\n+\t\tprog[3] = '-';\n+\n \tfor (cmd = cmd_list ; cmd->name ; cmd++) {\n \t\tint len = strlen(cmd->name);\n \t\tchar *arg;\n-- \n1.5.3.6.2090.g4ece0\n"},{"id":"61631","messageId":"200712020948.47084.chriscool@tuxfamily.org","threadId":"11090","inReplyTo":"1196579005-5662-1-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 1/3] Prepare execv_git_cmd() for removal of builtins from the filesystem","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2007-12-02T08:48:46Z","receivedAt":"2007-12-02T08:48:46Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Le dimanche 2 décembre 2007, Junio C Hamano a écrit :\n> +\tfor (argc = 0; argv[argc]; argc++)\n> +\t\t; /* just counting */\n> +\tnargv = xmalloc(sizeof(*nargv) * (argc + 2));\n>\n> -\ttrace_argv_printf(argv, -1, \"trace: exec:\");\n> +\tnargv[0] = \"git\";\n> +\tfor (argc = 0; argv[argc]; argc++)\n> +\t\tnargv[argc + 1] = argv[argc];\n> +\tnargv[argc + 1] = NULL;\n> +\ttrace_argv_printf(nargv, -1, \"trace: exec:\");\n\nMinor nit: now that the number of arguments is known, we could perhaps use \nthe argument count instead of -1 in trace_argv_printf, so that it is not \ncomputed again in quote.c:sq_quote_argv, like this:\n\ntrace_argv_printf(nargv, argc + 1, \"trace: exec:\");\n\nChristian.\n"},{"id":"61636","messageId":"Pine.LNX.4.64.0712021221410.27959@racer.site","threadId":"11090","inReplyTo":"1196579005-5662-2-git-send-email-gitster@pobox.com","subject":"Re: [PATCH 2/3] Avoid double exec() in execv_git_cmd()","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-12-02T12:24:20Z","receivedAt":"2007-12-02T12:24:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sat, 1 Dec 2007, Junio C Hamano wrote:\n\n> This avoids double exec() when execv_git_cmd() is used to spawn an\n> external git command.\n> \n> For this, execv_git_cmd() needs to be able to tell what's built-in.\n> This is done by introducing a new function git_builtin_command() that\n> checks the list of built-in commands and returns a pointer to the\n> cmd_struct if found.  The same function is used from the git potty.\n\nIMNSHO this does not buy us anything, compared to my patch series.  Your \npatch is larger, distributes the responsibility for the builtins to two \nfiles instead of one, and since we control what is builtin and what not, \nis not really less fragile with respect to what needs to be initialised.\n\nBesides, we lose a great opportunity to go in the direction of \nlibification.\n\nCiao,\nDscho\n"}]}