run-command: add "use shell" option
authorJeff King <peff@peff.net>
Wed, 30 Dec 2009 10:53:16 +0000 (05:53 -0500)
committerJunio C Hamano <gitster@pobox.com>
Sat, 2 Jan 2010 01:53:46 +0000 (17:53 -0800)
Many callsites run "sh -c $CMD" to run $CMD. We can make it
a little simpler for them by factoring out the munging of
argv.

For simple cases with no arguments, this doesn't help much, but:

  1. For cases with arguments, we save the caller from
     having to build the appropriate shell snippet.

  2. We can later optimize to avoid the shell when
     there are no metacharacters in the program.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
run-command.c
run-command.h

index cf2d8f7fae1356e50736cb9d599625df79738a2a..b2bdcfda3df9cc9a50453dae33926da7377a7b57 100644 (file)
@@ -15,6 +15,48 @@ static inline void dup_devnull(int to)
        close(fd);
 }
 
+static const char **prepare_shell_cmd(const char **argv)
+{
+       int argc, nargc = 0;
+       const char **nargv;
+
+       for (argc = 0; argv[argc]; argc++)
+               ; /* just counting */
+       /* +1 for NULL, +3 for "sh -c" plus extra $0 */
+       nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
+
+       if (argc < 1)
+               die("BUG: shell command is empty");
+
+       nargv[nargc++] = "sh";
+       nargv[nargc++] = "-c";
+
+       if (argc < 2)
+               nargv[nargc++] = argv[0];
+       else {
+               struct strbuf arg0 = STRBUF_INIT;
+               strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
+               nargv[nargc++] = strbuf_detach(&arg0, NULL);
+       }
+
+       for (argc = 0; argv[argc]; argc++)
+               nargv[nargc++] = argv[argc];
+       nargv[nargc] = NULL;
+
+       return nargv;
+}
+
+#ifndef WIN32
+static int execv_shell_cmd(const char **argv)
+{
+       const char **nargv = prepare_shell_cmd(argv);
+       trace_argv_printf(nargv, "trace: exec:");
+       execvp(nargv[0], (char **)nargv);
+       free(nargv);
+       return -1;
+}
+#endif
+
 int start_command(struct child_process *cmd)
 {
        int need_in, need_out, need_err;
@@ -123,6 +165,8 @@ fail_pipe:
                        cmd->preexec_cb();
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
+               } else if (cmd->use_shell) {
+                       execv_shell_cmd(cmd->argv);
                } else {
                        execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
@@ -179,6 +223,8 @@ fail_pipe:
 
        if (cmd->git_cmd) {
                cmd->argv = prepare_git_cmd(cmd->argv);
+       } else if (cmd->use_shell) {
+               cmd->argv = prepare_shell_cmd(cmd->argv);
        }
 
        cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
@@ -297,6 +343,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
        cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
        cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
+       cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
 }
 
 int run_command_v_opt(const char **argv, int opt)
index fb342090e3cac49f41689f9610bfe2d6c87c87c1..967ba8cc09786934724132b629587419f195b245 100644 (file)
@@ -33,6 +33,7 @@ struct child_process {
        unsigned git_cmd:1; /* if this is to be git sub-command */
        unsigned silent_exec_failure:1;
        unsigned stdout_to_stderr:1;
+       unsigned use_shell:1;
        void (*preexec_cb)(void);
 };
 
@@ -46,6 +47,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
 #define RUN_SILENT_EXEC_FAILURE 8
+#define RUN_USING_SHELL 16
 int run_command_v_opt(const char **argv, int opt);
 
 /*