Skip to content

Commit

Permalink
parse-opts: add OPT_FILENAME and transition builtins
Browse files Browse the repository at this point in the history
Commit dbd0f5c (Files given on the command line are relative to $cwd,
2008-08-06) introduced parse_options_fix_filename() as a minimal fix.
OPT_FILENAME is intended to be a more robust fix for the same issue.
OPT_FILENAME and its associated enum OPTION_FILENAME are used to
represent filename options within the parse options API.

This option is similar to OPTION_STRING. If --no is prefixed to the
option the filename is unset. If no argument is given and the default
value is set, the filename is set to the default value. The difference
is that the filename is prefixed with the prefix passed to
parse_options() (or parse_options_start()).

Update git-apply, git-commit, git-fmt-merge-msg, and git-tag to use
OPT_FILENAME with their filename options. Also, rename
parse_options_fix_filename() to fix_filename() as it is no longer
extern.

Signed-off-by: Stephen Boyd <bebarino@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Stephen Boyd authored and Junio C Hamano committed May 25, 2009
1 parent 3778292 commit df217ed
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 33 deletions.
5 changes: 5 additions & 0 deletions Documentation/technical/api-parse-options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ There are some macros to easily define options:
and the result will be put into `var`.
See 'Option Callbacks' below for a more elaborate description.

`OPT_FILENAME(short, long, &var, description)`::
Introduce an option with a filename argument.
The filename will be prefixed by passing the filename along with
the prefix argument of `parse_options()` to `prefix_filename()`.

`OPT_ARGUMENT(long, description)`::
Introduce a long-option argument that will be kept in `argv[]`.

Expand Down
5 changes: 1 addition & 4 deletions builtin-apply.c
Original file line number Diff line number Diff line change
Expand Up @@ -3278,7 +3278,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
"apply a patch without touching the working tree"),
OPT_BOOLEAN(0, "apply", &force_apply,
"also apply the patch (use with --stat/--summary/--check)"),
OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
"build a temporary index based on embedded index information"),
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
"paths are separated with NUL character",
Expand Down Expand Up @@ -3315,9 +3315,6 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)

argc = parse_options(argc, argv, prefix, builtin_apply_options,
apply_usage, 0);
fake_ancestor = parse_options_fix_filename(prefix, fake_ancestor);
if (fake_ancestor)
fake_ancestor = xstrdup(fake_ancestor);

if (apply_with_reject)
apply = apply_verbosely = 1;
Expand Down
10 changes: 2 additions & 8 deletions builtin-commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ static struct option builtin_commit_options[] = {
OPT__VERBOSE(&verbose),
OPT_GROUP("Commit message options"),

OPT_STRING('F', "file", &logfile, "FILE", "read log from file"),
OPT_FILENAME('F', "file", &logfile, "read log from file"),
OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),

OPT_GROUP("Commit contents options"),
Expand Down Expand Up @@ -699,12 +699,6 @@ static int parse_and_validate_options(int argc, const char *argv[],

argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
0);
logfile = parse_options_fix_filename(prefix, logfile);
if (logfile)
logfile = xstrdup(logfile);
template_file = parse_options_fix_filename(prefix, template_file);
if (template_file)
template_file = xstrdup(template_file);

if (force_author && !strchr(force_author, '>'))
force_author = find_author_by_nickname(force_author);
Expand Down
3 changes: 1 addition & 2 deletions builtin-fmt-merge-msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"),
OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
OPT_STRING('F', "file", &inpath, "file", "file to read from"),
OPT_FILENAME('F', "file", &inpath, "file to read from"),
OPT_END()
};

Expand All @@ -364,7 +364,6 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
0);
if (argc > 0)
usage_with_options(fmt_merge_msg_usage, options);
inpath = parse_options_fix_filename(prefix, inpath);

if (inpath && strcmp(inpath, "-")) {
in = fopen(inpath, "r");
Expand Down
3 changes: 1 addition & 2 deletions builtin-tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
"annotated tag, needs a message"),
OPT_CALLBACK('m', NULL, &msg, "msg",
"message for the tag", parse_msg_arg),
OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
OPT_STRING('u', NULL, &keyid, "key-id",
"use another key to sign the tag"),
Expand All @@ -406,7 +406,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
git_config(git_tag_config, NULL);

argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
msgfile = parse_options_fix_filename(prefix, msgfile);

if (keyid) {
sign = 1;
Expand Down
36 changes: 24 additions & 12 deletions parse-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,20 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
return 0;
}

static void fix_filename(const char *prefix, const char **file)
{
if (!file || !*file || !prefix || is_absolute_path(*file)
|| !strcmp("-", *file))
return;
*file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
}

static int get_value(struct parse_opt_ctx_t *p,
const struct option *opt, int flags)
{
const char *s, *arg;
const int unset = flags & OPT_UNSET;
int err;

if (unset && p->opt)
return opterror(opt, "takes no value", flags);
Expand Down Expand Up @@ -95,6 +104,19 @@ static int get_value(struct parse_opt_ctx_t *p,
return get_arg(p, opt, flags, (const char **)opt->value);
return 0;

case OPTION_FILENAME:
err = 0;
if (unset)
*(const char **)opt->value = NULL;
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
*(const char **)opt->value = (const char *)opt->defval;
else
err = get_arg(p, opt, flags, (const char **)opt->value);

if (!err)
fix_filename(p->prefix, (const char **)opt->value);
return err;

case OPTION_CALLBACK:
if (unset)
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
Expand Down Expand Up @@ -494,6 +516,8 @@ int usage_with_options_internal(const char * const *usagestr,
if (opts->flags & PARSE_OPT_NOARG)
break;
/* FALLTHROUGH */
case OPTION_FILENAME:
/* FALLTHROUGH */
case OPTION_STRING:
if (opts->argh)
pos += usage_argh(opts);
Expand Down Expand Up @@ -604,15 +628,3 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
commit_list_insert(commit, opt->value);
return 0;
}

/*
* This should really be OPTION_FILENAME type as a part of
* parse_options that take prefix to do this while parsing.
*/
extern const char *parse_options_fix_filename(const char *prefix, const char *file)
{
if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file))
return file;
return prefix_filename(prefix, strlen(prefix), file);
}

5 changes: 3 additions & 2 deletions parse-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ enum parse_opt_type {
OPTION_STRING,
OPTION_INTEGER,
OPTION_CALLBACK,
OPTION_FILENAME
};

enum parse_opt_flags {
Expand Down Expand Up @@ -117,6 +118,8 @@ struct option {
#define OPT_NUMBER_CALLBACK(v, h, f) \
{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
#define OPT_FILENAME(s, l, v, h) { OPTION_FILENAME, (s), (l), (v), \
"FILE", (h) }

/* parse_options() will filter out the processed options and leave the
* non-option arguments in argv[].
Expand Down Expand Up @@ -184,6 +187,4 @@ extern int parse_opt_with_commit(const struct option *, const char *, int);
"use <n> digits to display SHA-1s", \
PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }

extern const char *parse_options_fix_filename(const char *prefix, const char *file);

#endif
19 changes: 17 additions & 2 deletions t/t0040-parse-options.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ usage: test-parse-options <options>
--set23 set integer to 23
-t <time> get timestamp of <time>
-L, --length <str> get length of <str>
-F, --file <FILE> set file to <FILE>
String options
-s, --string <string>
Expand Down Expand Up @@ -56,10 +57,12 @@ abbrev: 7
verbose: 2
quiet: no
dry run: yes
file: prefix/my.file
EOF

test_expect_success 'short options' '
test-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err &&
test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
> output 2> output.err &&
test_cmp expect output &&
test ! -s output.err
'
Expand All @@ -73,11 +76,12 @@ abbrev: 10
verbose: 2
quiet: no
dry run: no
file: prefix/fi.le
EOF

test_expect_success 'long options' '
test-parse-options --boolean --integer 1729 --boolean --string2=321 \
--verbose --verbose --no-dry-run --abbrev=10 \
--verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
> output 2> output.err &&
test ! -s output.err &&
test_cmp expect output
Expand All @@ -87,6 +91,8 @@ test_expect_success 'missing required value' '
test-parse-options -s;
test $? = 129 &&
test-parse-options --string;
test $? = 129 &&
test-parse-options --file;
test $? = 129
'

Expand All @@ -99,6 +105,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
arg 00: a1
arg 01: b1
arg 02: --boolean
Expand All @@ -120,6 +127,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'unambiguously abbreviated option' '
Expand Down Expand Up @@ -148,6 +156,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'non ambiguous option (after two options it abbreviates)' '
Expand Down Expand Up @@ -175,6 +184,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
arg 00: --quux
EOF

Expand All @@ -193,6 +203,7 @@ abbrev: 7
verbose: 0
quiet: yes
dry run: no
file: (not set)
arg 00: foo
EOF

Expand All @@ -213,6 +224,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
Expand Down Expand Up @@ -240,6 +252,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
Expand All @@ -263,6 +276,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'OPT_BIT() works' '
Expand Down Expand Up @@ -292,6 +306,7 @@ abbrev: 7
verbose: 0
quiet: no
dry run: no
file: (not set)
EOF

test_expect_success 'OPT_NUMBER_CALLBACK() works' '
Expand Down
6 changes: 5 additions & 1 deletion test-parse-options.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ static unsigned long timestamp;
static int abbrev = 7;
static int verbose = 0, dry_run = 0, quiet = 0;
static char *string = NULL;
static char *file = NULL;

int length_callback(const struct option *opt, const char *arg, int unset)
{
Expand All @@ -27,6 +28,7 @@ int number_callback(const struct option *opt, const char *arg, int unset)

int main(int argc, const char **argv)
{
const char *prefix = "prefix/";
const char *usage[] = {
"test-parse-options <options>",
NULL
Expand All @@ -43,6 +45,7 @@ int main(int argc, const char **argv)
OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
OPT_CALLBACK('L', "length", &integer, "str",
"get length of <str>", length_callback),
OPT_FILENAME('F', "file", &file, "set file to <FILE>"),
OPT_GROUP("String options"),
OPT_STRING('s', "string", &string, "string", "get a string"),
OPT_STRING(0, "string2", &string, "str", "get another string"),
Expand All @@ -65,7 +68,7 @@ int main(int argc, const char **argv)
};
int i;

argc = parse_options(argc, argv, NULL, options, usage, 0);
argc = parse_options(argc, argv, prefix, options, usage, 0);

printf("boolean: %d\n", boolean);
printf("integer: %u\n", integer);
Expand All @@ -75,6 +78,7 @@ int main(int argc, const char **argv)
printf("verbose: %d\n", verbose);
printf("quiet: %s\n", quiet ? "yes" : "no");
printf("dry run: %s\n", dry_run ? "yes" : "no");
printf("file: %s\n", file ? file : "(not set)");

for (i = 0; i < argc; i++)
printf("arg %02d: %s\n", i, argv[i]);
Expand Down

0 comments on commit df217ed

Please sign in to comment.