Users who do EDITOR="/usr/bin/emacs -nw" or similar were left unable to edit commit messages once commit became a builtin, because the editor launch code assumed that $EDITOR was a single pathname. This patch makes split_cmdline() a public function as suggested by Johannes Schindelin, and renames an internal function in git.c to avoid a name collision. Signed-off-by: Steven Grimm <koreth@xxxxxxxxxxxxx> --- builtin-tag.c | 14 +++++++++++- git.c | 60 +++------------------------------------------------- run-command.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ run-command.h | 2 + 4 files changed, 84 insertions(+), 57 deletions(-) diff --git a/builtin-tag.c b/builtin-tag.c index 274901a..0a38724 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -47,10 +47,22 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e editor = "vi"; if (strcmp(editor, ":")) { - const char *args[] = { editor, path, NULL }; + char *editor_copy = xstrdup(editor); + char **args; + int args_pos; + + args_pos = split_cmdline(editor_copy, &args, 2); + if (args_pos < 0) + die("Couldn't parse the editor command %s.", editor); + + args[args_pos++] = path; + args[args_pos++] = NULL; if (run_command_v_opt_cd_env(args, 0, NULL, env)) die("There was a problem with the editor %s.", editor); + + free(args); + free(editor_copy); } if (!buffer) diff --git a/git.c b/git.c index 15fec89..3d095ee 100644 --- a/git.c +++ b/git.c @@ -2,6 +2,7 @@ #include "exec_cmd.h" #include "cache.h" #include "quote.h" +#include "run-command.h" 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]"; @@ -98,59 +99,6 @@ static int git_alias_config(const char *var, const char *value) return 0; } -static int split_cmdline(char *cmdline, const char ***argv) -{ - int src, dst, count = 0, size = 16; - char quoted = 0; - - *argv = xmalloc(sizeof(char*) * size); - - /* split alias_string */ - (*argv)[count++] = cmdline; - for (src = dst = 0; cmdline[src];) { - char c = cmdline[src]; - if (!quoted && isspace(c)) { - cmdline[dst++] = 0; - while (cmdline[++src] - && isspace(cmdline[src])) - ; /* skip */ - if (count >= size) { - size += 16; - *argv = xrealloc(*argv, sizeof(char*) * size); - } - (*argv)[count++] = cmdline + dst; - } else if(!quoted && (c == '\'' || c == '"')) { - quoted = c; - src++; - } else if (c == quoted) { - quoted = 0; - src++; - } else { - if (c == '\\' && quoted != '\'') { - src++; - c = cmdline[src]; - if (!c) { - free(*argv); - *argv = NULL; - return error("cmdline ends with \\"); - } - } - cmdline[dst++] = c; - src++; - } - } - - cmdline[dst] = 0; - - if (quoted) { - free(*argv); - *argv = NULL; - return error("unclosed quote"); - } - - return count; -} - static int handle_alias(int *argcp, const char ***argv) { int nongit = 0, envchanged = 0, ret = 0, saved_errno = errno; @@ -182,7 +130,7 @@ static int handle_alias(int *argcp, const char ***argv) die("Failed to run '%s' when expanding alias '%s'\n", alias_string + 1, alias_command); } - count = split_cmdline(alias_string, &new_argv); + count = split_cmdline(alias_string, &new_argv, 0); option_count = handle_options(&new_argv, &count, &envchanged); if (envchanged) die("alias '%s' changes environment variables\n" @@ -238,7 +186,7 @@ struct cmd_struct { int option; }; -static int run_command(struct cmd_struct *p, int argc, const char **argv) +static int run_git_command(struct cmd_struct *p, int argc, const char **argv) { int status; struct stat st; @@ -380,7 +328,7 @@ static void handle_internal_command(int argc, const char **argv) struct cmd_struct *p = commands+i; if (strcmp(p->cmd, cmd)) continue; - exit(run_command(p, argc, argv)); + exit(run_git_command(p, argc, argv)); } } diff --git a/run-command.c b/run-command.c index 476d00c..3ae55ec 100644 --- a/run-command.c +++ b/run-command.c @@ -237,3 +237,68 @@ int finish_async(struct async *async) ret = error("waitpid (async) failed"); return ret; } + +/* + * Parses a command line into an array of char* representing the tokens + * on the command line. Pass in a count to reserve some number of additional + * slots in the allocated array, e.g., so the caller can add a filename + * argument without having to reallocate the array. + * + * Returns the number of items in the array or -1 if an error occurred. + * + * Note that the command line will be altered (nulls will be inserted + * where the original had argument-delimiting whitespace.) + */ +int split_cmdline(char *cmdline, const char ***argv, int extra_slots) +{ + int src, dst, count = 0, size = extra_slots + 16; + char quoted = 0; + + *argv = xmalloc(sizeof(char*) * size); + + /* split alias_string */ + (*argv)[count++] = cmdline; + for (src = dst = 0; cmdline[src];) { + char c = cmdline[src]; + if (!quoted && isspace(c)) { + cmdline[dst++] = 0; + while (cmdline[++src] + && isspace(cmdline[src])) + ; /* skip */ + if (count >= size) { + size += 16; + *argv = xrealloc(*argv, sizeof(char*) * size); + } + (*argv)[count++] = cmdline + dst; + } else if(!quoted && (c == '\'' || c == '"')) { + quoted = c; + src++; + } else if (c == quoted) { + quoted = 0; + src++; + } else { + if (c == '\\' && quoted != '\'') { + src++; + c = cmdline[src]; + if (!c) { + free(*argv); + *argv = NULL; + return error("cmdline ends with \\"); + } + } + cmdline[dst++] = c; + src++; + } + } + + cmdline[dst] = 0; + + if (quoted) { + free(*argv); + *argv = NULL; + return error("unclosed quote"); + } + + return count; +} + diff --git a/run-command.h b/run-command.h index 1fc781d..e2b5dea 100644 --- a/run-command.h +++ b/run-command.h @@ -66,4 +66,6 @@ struct async { int start_async(struct async *async); int finish_async(struct async *async); +int split_cmdline(char *cmdline, const char ***argv, int extra_slots); + #endif -- 1.5.4.rc0.37.g176bc - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html