Skip to content

Commit ffc5c04

Browse files
pks-tgitster
authored andcommitted
git: refactor alias handling to use a struct strvec
In `handle_alias()` we use both `argcp` and `argv` as in-out parameters. Callers mostly pass through the static array from `main()`, but once we handle an alias we replace it with an allocated array that may contain some allocated strings. Callers do not handle this scenario at all and thus leak memory. We could in theory handle the lifetime of `argv` in a hacky fashion by letting callers free it in case they see that an alias was handled. But while that would likely work, we still wouldn't be able to easily handle the lifetime of strings referenced by `argv`. Refactor the code to instead use a `struct strvec`, which effectively removes the need for us to manually track lifetimes. Signed-off-by: Patrick Steinhardt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3f5fade commit ffc5c04

File tree

2 files changed

+33
-26
lines changed

2 files changed

+33
-26
lines changed

git.c

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -362,18 +362,18 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
362362
return (*argv) - orig_argv;
363363
}
364364

365-
static int handle_alias(int *argcp, const char ***argv)
365+
static int handle_alias(struct strvec *args)
366366
{
367367
int envchanged = 0, ret = 0, saved_errno = errno;
368368
int count, option_count;
369369
const char **new_argv;
370370
const char *alias_command;
371371
char *alias_string;
372372

373-
alias_command = (*argv)[0];
373+
alias_command = args->v[0];
374374
alias_string = alias_lookup(alias_command);
375375
if (alias_string) {
376-
if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
376+
if (args->nr > 1 && !strcmp(args->v[1], "-h"))
377377
fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
378378
alias_command, alias_string);
379379
if (alias_string[0] == '!') {
@@ -390,7 +390,7 @@ static int handle_alias(int *argcp, const char ***argv)
390390
child.wait_after_clean = 1;
391391
child.trace2_child_class = "shell_alias";
392392
strvec_push(&child.args, alias_string + 1);
393-
strvec_pushv(&child.args, (*argv) + 1);
393+
strvec_pushv(&child.args, args->v + 1);
394394

395395
trace2_cmd_alias(alias_command, child.args.v);
396396
trace2_cmd_name("_run_shell_alias_");
@@ -423,15 +423,13 @@ static int handle_alias(int *argcp, const char ***argv)
423423
trace_argv_printf(new_argv,
424424
"trace: alias expansion: %s =>",
425425
alias_command);
426-
427-
REALLOC_ARRAY(new_argv, count + *argcp);
428-
/* insert after command name */
429-
COPY_ARRAY(new_argv + count, *argv + 1, *argcp);
430-
431426
trace2_cmd_alias(alias_command, new_argv);
432427

433-
*argv = new_argv;
434-
*argcp += count - 1;
428+
/* Replace the alias with the new arguments. */
429+
strvec_splice(args, 0, 1, new_argv, count);
430+
431+
free(alias_string);
432+
free(new_argv);
435433

436434
ret = 1;
437435
}
@@ -800,10 +798,10 @@ static void execv_dashed_external(const char **argv)
800798
exit(128);
801799
}
802800

803-
static int run_argv(int *argcp, const char ***argv)
801+
static int run_argv(struct strvec *args)
804802
{
805803
int done_alias = 0;
806-
struct string_list cmd_list = STRING_LIST_INIT_NODUP;
804+
struct string_list cmd_list = STRING_LIST_INIT_DUP;
807805
struct string_list_item *seen;
808806

809807
while (1) {
@@ -817,8 +815,8 @@ static int run_argv(int *argcp, const char ***argv)
817815
* process.
818816
*/
819817
if (!done_alias)
820-
handle_builtin(*argcp, *argv);
821-
else if (get_builtin(**argv)) {
818+
handle_builtin(args->nr, args->v);
819+
else if (get_builtin(args->v[0])) {
822820
struct child_process cmd = CHILD_PROCESS_INIT;
823821
int i;
824822

@@ -834,8 +832,8 @@ static int run_argv(int *argcp, const char ***argv)
834832
commit_pager_choice();
835833

836834
strvec_push(&cmd.args, "git");
837-
for (i = 0; i < *argcp; i++)
838-
strvec_push(&cmd.args, (*argv)[i]);
835+
for (i = 0; i < args->nr; i++)
836+
strvec_push(&cmd.args, args->v[i]);
839837

840838
trace_argv_printf(cmd.args.v, "trace: exec:");
841839

@@ -850,13 +848,13 @@ static int run_argv(int *argcp, const char ***argv)
850848
i = run_command(&cmd);
851849
if (i >= 0 || errno != ENOENT)
852850
exit(i);
853-
die("could not execute builtin %s", **argv);
851+
die("could not execute builtin %s", args->v[0]);
854852
}
855853

856854
/* .. then try the external ones */
857-
execv_dashed_external(*argv);
855+
execv_dashed_external(args->v);
858856

859-
seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
857+
seen = unsorted_string_list_lookup(&cmd_list, args->v[0]);
860858
if (seen) {
861859
int i;
862860
struct strbuf sb = STRBUF_INIT;
@@ -873,14 +871,14 @@ static int run_argv(int *argcp, const char ***argv)
873871
" not terminate:%s"), cmd_list.items[0].string, sb.buf);
874872
}
875873

876-
string_list_append(&cmd_list, *argv[0]);
874+
string_list_append(&cmd_list, args->v[0]);
877875

878876
/*
879877
* It could be an alias -- this works around the insanity
880878
* of overriding "git log" with "git show" by having
881879
* alias.log = show
882880
*/
883-
if (!handle_alias(argcp, argv))
881+
if (!handle_alias(args))
884882
break;
885883
done_alias = 1;
886884
}
@@ -892,6 +890,7 @@ static int run_argv(int *argcp, const char ***argv)
892890

893891
int cmd_main(int argc, const char **argv)
894892
{
893+
struct strvec args = STRVEC_INIT;
895894
const char *cmd;
896895
int done_help = 0;
897896

@@ -951,25 +950,32 @@ int cmd_main(int argc, const char **argv)
951950
*/
952951
setup_path();
953952

953+
for (size_t i = 0; i < argc; i++)
954+
strvec_push(&args, argv[i]);
955+
954956
while (1) {
955-
int was_alias = run_argv(&argc, &argv);
957+
int was_alias = run_argv(&args);
956958
if (errno != ENOENT)
957959
break;
958960
if (was_alias) {
959961
fprintf(stderr, _("expansion of alias '%s' failed; "
960962
"'%s' is not a git command\n"),
961-
cmd, argv[0]);
963+
cmd, args.v[0]);
964+
strvec_clear(&args);
962965
exit(1);
963966
}
964967
if (!done_help) {
965-
cmd = argv[0] = help_unknown_cmd(cmd);
968+
strvec_replace(&args, 0, help_unknown_cmd(cmd));
969+
cmd = args.v[0];
966970
done_help = 1;
967-
} else
971+
} else {
968972
break;
973+
}
969974
}
970975

971976
fprintf(stderr, _("failed to run command '%s': %s\n"),
972977
cmd, strerror(errno));
978+
strvec_clear(&args);
973979

974980
return 1;
975981
}

t/t0014-alias.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
test_description='git command aliasing'
44

5+
TEST_PASSES_SANITIZE_LEAK=true
56
. ./test-lib.sh
67

78
test_expect_success 'nested aliases - internal execution' '

0 commit comments

Comments
 (0)