Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'jk/argv-array' into maint

* jk/argv-array:
  run_hook: use argv_array API
  checkout: use argv_array API
  bisect: use argv_array API
  quote: provide sq_dequote_to_argv_array
  refactor argv_array into generic code
  quote.h: fix bogus comment
  add sha1_array API docs
  • Loading branch information...
commit 60f60b49629b6693ce5b8ce9201b2f6dd3989354 2 parents 7bb07f6 + 5d40a17
Junio C Hamano authored
46 Documentation/technical/api-argv-array.txt
... ... @@ -0,0 +1,46 @@
  1 +argv-array API
  2 +==============
  3 +
  4 +The argv-array API allows one to dynamically build and store
  5 +NULL-terminated lists. An argv-array maintains the invariant that the
  6 +`argv` member always points to a non-NULL array, and that the array is
  7 +always NULL-terminated at the element pointed to by `argv[argc]`. This
  8 +makes the result suitable for passing to functions expecting to receive
  9 +argv from main(), or the link:api-run-command.html[run-command API].
  10 +
  11 +The link:api-string-list.html[string-list API] is similar, but cannot be
  12 +used for these purposes; instead of storing a straight string pointer,
  13 +it contains an item structure with a `util` field that is not compatible
  14 +with the traditional argv interface.
  15 +
  16 +Each `argv_array` manages its own memory. Any strings pushed into the
  17 +array are duplicated, and all memory is freed by argv_array_clear().
  18 +
  19 +Data Structures
  20 +---------------
  21 +
  22 +`struct argv_array`::
  23 +
  24 + A single array. This should be initialized by assignment from
  25 + `ARGV_ARRAY_INIT`, or by calling `argv_array_init`. The `argv`
  26 + member contains the actual array; the `argc` member contains the
  27 + number of elements in the array, not including the terminating
  28 + NULL.
  29 +
  30 +Functions
  31 +---------
  32 +
  33 +`argv_array_init`::
  34 + Initialize an array. This is no different than assigning from
  35 + `ARGV_ARRAY_INIT`.
  36 +
  37 +`argv_array_push`::
  38 + Push a copy of a string onto the end of the array.
  39 +
  40 +`argv_array_pushf`::
  41 + Format a string and push it onto the end of the array. This is a
  42 + convenience wrapper combining `strbuf_addf` and `argv_array_push`.
  43 +
  44 +`argv_array_clear`::
  45 + Free all memory associated with the array and return it to the
  46 + initial, empty state.
79 Documentation/technical/api-sha1-array.txt
... ... @@ -0,0 +1,79 @@
  1 +sha1-array API
  2 +==============
  3 +
  4 +The sha1-array API provides storage and manipulation of sets of SHA1
  5 +identifiers. The emphasis is on storage and processing efficiency,
  6 +making them suitable for large lists. Note that the ordering of items is
  7 +not preserved over some operations.
  8 +
  9 +Data Structures
  10 +---------------
  11 +
  12 +`struct sha1_array`::
  13 +
  14 + A single array of SHA1 hashes. This should be initialized by
  15 + assignment from `SHA1_ARRAY_INIT`. The `sha1` member contains
  16 + the actual data. The `nr` member contains the number of items in
  17 + the set. The `alloc` and `sorted` members are used internally,
  18 + and should not be needed by API callers.
  19 +
  20 +Functions
  21 +---------
  22 +
  23 +`sha1_array_append`::
  24 + Add an item to the set. The sha1 will be placed at the end of
  25 + the array (but note that some operations below may lose this
  26 + ordering).
  27 +
  28 +`sha1_array_sort`::
  29 + Sort the elements in the array.
  30 +
  31 +`sha1_array_lookup`::
  32 + Perform a binary search of the array for a specific sha1.
  33 + If found, returns the offset (in number of elements) of the
  34 + sha1. If not found, returns a negative integer. If the array is
  35 + not sorted, this function has the side effect of sorting it.
  36 +
  37 +`sha1_array_clear`::
  38 + Free all memory associated with the array and return it to the
  39 + initial, empty state.
  40 +
  41 +`sha1_array_for_each_unique`::
  42 + Efficiently iterate over each unique element of the list,
  43 + executing the callback function for each one. If the array is
  44 + not sorted, this function has the side effect of sorting it.
  45 +
  46 +Examples
  47 +--------
  48 +
  49 +-----------------------------------------
  50 +void print_callback(const unsigned char sha1[20],
  51 + void *data)
  52 +{
  53 + printf("%s\n", sha1_to_hex(sha1));
  54 +}
  55 +
  56 +void some_func(void)
  57 +{
  58 + struct sha1_array hashes = SHA1_ARRAY_INIT;
  59 + unsigned char sha1[20];
  60 +
  61 + /* Read objects into our set */
  62 + while (read_object_from_stdin(sha1))
  63 + sha1_array_append(&hashes, sha1);
  64 +
  65 + /* Check if some objects are in our set */
  66 + while (read_object_from_stdin(sha1)) {
  67 + if (sha1_array_lookup(&hashes, sha1) >= 0)
  68 + printf("it's in there!\n");
  69 +
  70 + /*
  71 + * Print the unique set of objects. We could also have
  72 + * avoided adding duplicate objects in the first place,
  73 + * but we would end up re-sorting the array repeatedly.
  74 + * Instead, this will sort once and then skip duplicates
  75 + * in linear time.
  76 + */
  77 + sha1_array_for_each_unique(&hashes, print_callback, NULL);
  78 +}
  79 +-----------------------------------------
2  Makefile
@@ -505,6 +505,7 @@ VCSSVN_LIB=vcs-svn/lib.a
505 505
506 506 LIB_H += advice.h
507 507 LIB_H += archive.h
  508 +LIB_H += argv-array.h
508 509 LIB_H += attr.h
509 510 LIB_H += blob.h
510 511 LIB_H += builtin.h
@@ -586,6 +587,7 @@ LIB_OBJS += alloc.o
586 587 LIB_OBJS += archive.o
587 588 LIB_OBJS += archive-tar.o
588 589 LIB_OBJS += archive-zip.o
  590 +LIB_OBJS += argv-array.o
589 591 LIB_OBJS += attr.o
590 592 LIB_OBJS += base85.o
591 593 LIB_OBJS += bisect.o
51 argv-array.c
... ... @@ -0,0 +1,51 @@
  1 +#include "cache.h"
  2 +#include "argv-array.h"
  3 +#include "strbuf.h"
  4 +
  5 +static const char *empty_argv_storage = NULL;
  6 +const char **empty_argv = &empty_argv_storage;
  7 +
  8 +void argv_array_init(struct argv_array *array)
  9 +{
  10 + array->argv = empty_argv;
  11 + array->argc = 0;
  12 + array->alloc = 0;
  13 +}
  14 +
  15 +static void argv_array_push_nodup(struct argv_array *array, const char *value)
  16 +{
  17 + if (array->argv == empty_argv)
  18 + array->argv = NULL;
  19 +
  20 + ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
  21 + array->argv[array->argc++] = value;
  22 + array->argv[array->argc] = NULL;
  23 +}
  24 +
  25 +void argv_array_push(struct argv_array *array, const char *value)
  26 +{
  27 + argv_array_push_nodup(array, xstrdup(value));
  28 +}
  29 +
  30 +void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
  31 +{
  32 + va_list ap;
  33 + struct strbuf v = STRBUF_INIT;
  34 +
  35 + va_start(ap, fmt);
  36 + strbuf_vaddf(&v, fmt, ap);
  37 + va_end(ap);
  38 +
  39 + argv_array_push_nodup(array, strbuf_detach(&v, NULL));
  40 +}
  41 +
  42 +void argv_array_clear(struct argv_array *array)
  43 +{
  44 + if (array->argv != empty_argv) {
  45 + int i;
  46 + for (i = 0; i < array->argc; i++)
  47 + free((char **)array->argv[i]);
  48 + free(array->argv);
  49 + }
  50 + argv_array_init(array);
  51 +}
20 argv-array.h
... ... @@ -0,0 +1,20 @@
  1 +#ifndef ARGV_ARRAY_H
  2 +#define ARGV_ARRAY_H
  3 +
  4 +extern const char **empty_argv;
  5 +
  6 +struct argv_array {
  7 + const char **argv;
  8 + int argc;
  9 + int alloc;
  10 +};
  11 +
  12 +#define ARGV_ARRAY_INIT { empty_argv, 0, 0 }
  13 +
  14 +void argv_array_init(struct argv_array *);
  15 +void argv_array_push(struct argv_array *, const char *);
  16 +__attribute__((format (printf,2,3)))
  17 +void argv_array_pushf(struct argv_array *, const char *fmt, ...);
  18 +void argv_array_clear(struct argv_array *);
  19 +
  20 +#endif /* ARGV_ARRAY_H */
48 bisect.c
@@ -10,18 +10,13 @@
10 10 #include "log-tree.h"
11 11 #include "bisect.h"
12 12 #include "sha1-array.h"
  13 +#include "argv-array.h"
13 14
14 15 static struct sha1_array good_revs;
15 16 static struct sha1_array skipped_revs;
16 17
17 18 static const unsigned char *current_bad_sha1;
18 19
19   -struct argv_array {
20   - const char **argv;
21   - int argv_nr;
22   - int argv_alloc;
23   -};
24   -
25 20 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
26 21 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
27 22 static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
@@ -405,21 +400,6 @@ struct commit_list *find_bisection(struct commit_list *list,
405 400 return best;
406 401 }
407 402
408   -static void argv_array_push(struct argv_array *array, const char *string)
409   -{
410   - ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
411   - array->argv[array->argv_nr++] = string;
412   -}
413   -
414   -static void argv_array_push_sha1(struct argv_array *array,
415   - const unsigned char *sha1,
416   - const char *format)
417   -{
418   - struct strbuf buf = STRBUF_INIT;
419   - strbuf_addf(&buf, format, sha1_to_hex(sha1));
420   - argv_array_push(array, strbuf_detach(&buf, NULL));
421   -}
422   -
423 403 static int register_ref(const char *refname, const unsigned char *sha1,
424 404 int flags, void *cb_data)
425 405 {
@@ -449,16 +429,10 @@ static void read_bisect_paths(struct argv_array *array)
449 429 die_errno("Could not open file '%s'", filename);
450 430
451 431 while (strbuf_getline(&str, fp, '\n') != EOF) {
452   - char *quoted;
453   - int res;
454   -
455 432 strbuf_trim(&str);
456   - quoted = strbuf_detach(&str, NULL);
457   - res = sq_dequote_to_argv(quoted, &array->argv,
458   - &array->argv_nr, &array->argv_alloc);
459   - if (res)
  433 + if (sq_dequote_to_argv_array(str.buf, array))
460 434 die("Badly quoted content in file '%s': %s",
461   - filename, quoted);
  435 + filename, str.buf);
462 436 }
463 437
464 438 strbuf_release(&str);
@@ -623,7 +597,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
623 597 const char *bad_format, const char *good_format,
624 598 int read_paths)
625 599 {
626   - struct argv_array rev_argv = { NULL, 0, 0 };
  600 + struct argv_array rev_argv = ARGV_ARRAY_INIT;
627 601 int i;
628 602
629 603 init_revisions(revs, prefix);
@@ -631,17 +605,17 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
631 605 revs->commit_format = CMIT_FMT_UNSPECIFIED;
632 606
633 607 /* rev_argv.argv[0] will be ignored by setup_revisions */
634   - argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
635   - argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
  608 + argv_array_push(&rev_argv, "bisect_rev_setup");
  609 + argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
636 610 for (i = 0; i < good_revs.nr; i++)
637   - argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
638   - good_format);
639   - argv_array_push(&rev_argv, xstrdup("--"));
  611 + argv_array_pushf(&rev_argv, good_format,
  612 + sha1_to_hex(good_revs.sha1[i]));
  613 + argv_array_push(&rev_argv, "--");
640 614 if (read_paths)
641 615 read_bisect_paths(&rev_argv);
642   - argv_array_push(&rev_argv, NULL);
643 616
644   - setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
  617 + setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
  618 + /* XXX leak rev_argv, as "revs" may still be pointing to it */
645 619 }
646 620
647 621 static void bisect_common(struct rev_info *revs)
27 builtin/checkout.c
@@ -19,6 +19,7 @@
19 19 #include "ll-merge.h"
20 20 #include "resolve-undo.h"
21 21 #include "submodule.h"
  22 +#include "argv-array.h"
22 23
23 24 static const char * const checkout_usage[] = {
24 25 "git checkout [options] <branch>",
@@ -592,24 +593,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
592 593 report_tracking(new);
593 594 }
594 595
595   -struct rev_list_args {
596   - int argc;
597   - int alloc;
598   - const char **argv;
599   -};
600   -
601   -static void add_one_rev_list_arg(struct rev_list_args *args, const char *s)
602   -{
603   - ALLOC_GROW(args->argv, args->argc + 1, args->alloc);
604   - args->argv[args->argc++] = s;
605   -}
606   -
607 596 static int add_one_ref_to_rev_list_arg(const char *refname,
608 597 const unsigned char *sha1,
609 598 int flags,
610 599 void *cb_data)
611 600 {
612   - add_one_rev_list_arg(cb_data, refname);
  601 + argv_array_push(cb_data, refname);
613 602 return 0;
614 603 }
615 604
@@ -689,15 +678,14 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
689 678 */
690 679 static void orphaned_commit_warning(struct commit *commit)
691 680 {
692   - struct rev_list_args args = { 0, 0, NULL };
  681 + struct argv_array args = ARGV_ARRAY_INIT;
693 682 struct rev_info revs;
694 683
695   - add_one_rev_list_arg(&args, "(internal)");
696   - add_one_rev_list_arg(&args, sha1_to_hex(commit->object.sha1));
697   - add_one_rev_list_arg(&args, "--not");
  684 + argv_array_push(&args, "(internal)");
  685 + argv_array_push(&args, sha1_to_hex(commit->object.sha1));
  686 + argv_array_push(&args, "--not");
698 687 for_each_ref(add_one_ref_to_rev_list_arg, &args);
699   - add_one_rev_list_arg(&args, "--");
700   - add_one_rev_list_arg(&args, NULL);
  688 + argv_array_push(&args, "--");
701 689
702 690 init_revisions(&revs, NULL);
703 691 if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
@@ -709,6 +697,7 @@ static void orphaned_commit_warning(struct commit *commit)
709 697 else
710 698 describe_detached_head(_("Previous HEAD position was"), commit);
711 699
  700 + argv_array_clear(&args);
712 701 clear_commit_marks(commit, -1);
713 702 for_each_ref(clear_commit_marks_from_one_ref, NULL);
714 703 }
23 quote.c
... ... @@ -1,5 +1,6 @@
1 1 #include "cache.h"
2 2 #include "quote.h"
  3 +#include "argv-array.h"
3 4
4 5 int quote_path_fully = 1;
5 6
@@ -120,7 +121,9 @@ char *sq_dequote(char *arg)
120 121 return sq_dequote_step(arg, NULL);
121 122 }
122 123
123   -int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
  124 +static int sq_dequote_to_argv_internal(char *arg,
  125 + const char ***argv, int *nr, int *alloc,
  126 + struct argv_array *array)
124 127 {
125 128 char *next = arg;
126 129
@@ -130,13 +133,27 @@ int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
130 133 char *dequoted = sq_dequote_step(next, &next);
131 134 if (!dequoted)
132 135 return -1;
133   - ALLOC_GROW(*argv, *nr + 1, *alloc);
134   - (*argv)[(*nr)++] = dequoted;
  136 + if (argv) {
  137 + ALLOC_GROW(*argv, *nr + 1, *alloc);
  138 + (*argv)[(*nr)++] = dequoted;
  139 + }
  140 + if (array)
  141 + argv_array_push(array, dequoted);
135 142 } while (next);
136 143
137 144 return 0;
138 145 }
139 146
  147 +int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc)
  148 +{
  149 + return sq_dequote_to_argv_internal(arg, argv, nr, alloc, NULL);
  150 +}
  151 +
  152 +int sq_dequote_to_argv_array(char *arg, struct argv_array *array)
  153 +{
  154 + return sq_dequote_to_argv_internal(arg, NULL, NULL, NULL, array);
  155 +}
  156 +
140 157 /* 1 means: quote as octal
141 158 * 0 means: quote as octal if (quote_path_fully)
142 159 * -1 means: never quote
13 quote.h
@@ -40,12 +40,19 @@ extern char *sq_dequote(char *);
40 40
41 41 /*
42 42 * Same as the above, but can be used to unwrap many arguments in the
43   - * same string separated by space. "next" is changed to point to the
44   - * next argument that should be passed as first parameter. When there
45   - * is no more argument to be dequoted, "next" is updated to point to NULL.
  43 + * same string separated by space. Like sq_quote, it works in place,
  44 + * modifying arg and appending pointers into it to argv.
46 45 */
47 46 extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
48 47
  48 +/*
  49 + * Same as above, but store the unquoted strings in an argv_array. We will
  50 + * still modify arg in place, but unlike sq_dequote_to_argv, the argv_array
  51 + * will duplicate and take ownership of the strings.
  52 + */
  53 +struct argv_array;
  54 +extern int sq_dequote_to_argv_array(char *arg, struct argv_array *);
  55 +
49 56 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
50 57 extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
51 58 extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
18 run-command.c
... ... @@ -1,6 +1,7 @@
1 1 #include "cache.h"
2 2 #include "run-command.h"
3 3 #include "exec_cmd.h"
  4 +#include "argv-array.h"
4 5
5 6 static inline void close_pair(int fd[2])
6 7 {
@@ -605,26 +606,23 @@ int finish_async(struct async *async)
605 606 int run_hook(const char *index_file, const char *name, ...)
606 607 {
607 608 struct child_process hook;
608   - const char **argv = NULL, *env[2];
  609 + struct argv_array argv = ARGV_ARRAY_INIT;
  610 + const char *p, *env[2];
609 611 char index[PATH_MAX];
610 612 va_list args;
611 613 int ret;
612   - size_t i = 0, alloc = 0;
613 614
614 615 if (access(git_path("hooks/%s", name), X_OK) < 0)
615 616 return 0;
616 617
617 618 va_start(args, name);
618   - ALLOC_GROW(argv, i + 1, alloc);
619   - argv[i++] = git_path("hooks/%s", name);
620   - while (argv[i-1]) {
621   - ALLOC_GROW(argv, i + 1, alloc);
622   - argv[i++] = va_arg(args, const char *);
623   - }
  619 + argv_array_push(&argv, git_path("hooks/%s", name));
  620 + while ((p = va_arg(args, const char *)))
  621 + argv_array_push(&argv, p);
624 622 va_end(args);
625 623
626 624 memset(&hook, 0, sizeof(hook));
627   - hook.argv = argv;
  625 + hook.argv = argv.argv;
628 626 hook.no_stdin = 1;
629 627 hook.stdout_to_stderr = 1;
630 628 if (index_file) {
@@ -635,6 +633,6 @@ int run_hook(const char *index_file, const char *name, ...)
635 633 }
636 634
637 635 ret = run_command(&hook);
638   - free(argv);
  636 + argv_array_clear(&argv);
639 637 return ret;
640 638 }
41 submodule.c
@@ -9,6 +9,7 @@
9 9 #include "refs.h"
10 10 #include "string-list.h"
11 11 #include "sha1-array.h"
  12 +#include "argv-array.h"
12 13
13 14 static struct string_list config_name_for_path;
14 15 static struct string_list config_fetch_recurse_submodules_for_name;
@@ -496,56 +497,26 @@ void check_for_new_submodule_commits(unsigned char new_sha1[20])
496 497 sha1_array_append(&ref_tips_after_fetch, new_sha1);
497 498 }
498 499
499   -struct argv_array {
500   - const char **argv;
501   - unsigned int argc;
502   - unsigned int alloc;
503   -};
504   -
505   -static void init_argv(struct argv_array *array)
506   -{
507   - array->argv = NULL;
508   - array->argc = 0;
509   - array->alloc = 0;
510   -}
511   -
512   -static void push_argv(struct argv_array *array, const char *value)
513   -{
514   - ALLOC_GROW(array->argv, array->argc + 2, array->alloc);
515   - array->argv[array->argc++] = xstrdup(value);
516   - array->argv[array->argc] = NULL;
517   -}
518   -
519   -static void clear_argv(struct argv_array *array)
520   -{
521   - int i;
522   - for (i = 0; i < array->argc; i++)
523   - free((char **)array->argv[i]);
524   - free(array->argv);
525   - init_argv(array);
526   -}
527   -
528 500 static void add_sha1_to_argv(const unsigned char sha1[20], void *data)
529 501 {
530   - push_argv(data, sha1_to_hex(sha1));
  502 + argv_array_push(data, sha1_to_hex(sha1));
531 503 }
532 504
533 505 static void calculate_changed_submodule_paths(void)
534 506 {
535 507 struct rev_info rev;
536 508 struct commit *commit;
537   - struct argv_array argv;
  509 + struct argv_array argv = ARGV_ARRAY_INIT;
538 510
539 511 /* No need to check if there are no submodules configured */
540 512 if (!config_name_for_path.nr)
541 513 return;
542 514
543 515 init_revisions(&rev, NULL);
544   - init_argv(&argv);
545   - push_argv(&argv, "--"); /* argv[0] program name */
  516 + argv_array_push(&argv, "--"); /* argv[0] program name */
546 517 sha1_array_for_each_unique(&ref_tips_after_fetch,
547 518 add_sha1_to_argv, &argv);
548   - push_argv(&argv, "--not");
  519 + argv_array_push(&argv, "--not");
549 520 sha1_array_for_each_unique(&ref_tips_before_fetch,
550 521 add_sha1_to_argv, &argv);
551 522 setup_revisions(argv.argc, argv.argv, &rev, NULL);
@@ -573,7 +544,7 @@ static void calculate_changed_submodule_paths(void)
573 544 }
574 545 }
575 546
576   - clear_argv(&argv);
  547 + argv_array_clear(&argv);
577 548 sha1_array_clear(&ref_tips_before_fetch);
578 549 sha1_array_clear(&ref_tips_after_fetch);
579 550 initialized_fetch_ref_tips = 0;

0 comments on commit 60f60b4

Please sign in to comment.
Something went wrong with that request. Please try again.