Skip to content

Commit df217ed

Browse files
bebarinogitster
authored andcommitted
parse-opts: add OPT_FILENAME and transition builtins
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3778292 commit df217ed

File tree

9 files changed

+59
-33
lines changed

9 files changed

+59
-33
lines changed

Documentation/technical/api-parse-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ There are some macros to easily define options:
167167
and the result will be put into `var`.
168168
See 'Option Callbacks' below for a more elaborate description.
169169

170+
`OPT_FILENAME(short, long, &var, description)`::
171+
Introduce an option with a filename argument.
172+
The filename will be prefixed by passing the filename along with
173+
the prefix argument of `parse_options()` to `prefix_filename()`.
174+
170175
`OPT_ARGUMENT(long, description)`::
171176
Introduce a long-option argument that will be kept in `argv[]`.
172177

builtin-apply.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,7 +3278,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
32783278
"apply a patch without touching the working tree"),
32793279
OPT_BOOLEAN(0, "apply", &force_apply,
32803280
"also apply the patch (use with --stat/--summary/--check)"),
3281-
OPT_STRING(0, "build-fake-ancestor", &fake_ancestor, "file",
3281+
OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
32823282
"build a temporary index based on embedded index information"),
32833283
{ OPTION_CALLBACK, 'z', NULL, NULL, NULL,
32843284
"paths are separated with NUL character",
@@ -3315,9 +3315,6 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
33153315

33163316
argc = parse_options(argc, argv, prefix, builtin_apply_options,
33173317
apply_usage, 0);
3318-
fake_ancestor = parse_options_fix_filename(prefix, fake_ancestor);
3319-
if (fake_ancestor)
3320-
fake_ancestor = xstrdup(fake_ancestor);
33213318

33223319
if (apply_with_reject)
33233320
apply = apply_verbosely = 1;

builtin-commit.c

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ static struct option builtin_commit_options[] = {
8888
OPT__VERBOSE(&verbose),
8989
OPT_GROUP("Commit message options"),
9090

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

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

700700
argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
701701
0);
702-
logfile = parse_options_fix_filename(prefix, logfile);
703-
if (logfile)
704-
logfile = xstrdup(logfile);
705-
template_file = parse_options_fix_filename(prefix, template_file);
706-
if (template_file)
707-
template_file = xstrdup(template_file);
708702

709703
if (force_author && !strchr(force_author, '>'))
710704
force_author = find_author_by_nickname(force_author);

builtin-fmt-merge-msg.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
351351
struct option options[] = {
352352
OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"),
353353
OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
354-
OPT_STRING('F', "file", &inpath, "file", "file to read from"),
354+
OPT_FILENAME('F', "file", &inpath, "file to read from"),
355355
OPT_END()
356356
};
357357

@@ -364,7 +364,6 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
364364
0);
365365
if (argc > 0)
366366
usage_with_options(fmt_merge_msg_usage, options);
367-
inpath = parse_options_fix_filename(prefix, inpath);
368367

369368
if (inpath && strcmp(inpath, "-")) {
370369
in = fopen(inpath, "r");

builtin-tag.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
387387
"annotated tag, needs a message"),
388388
OPT_CALLBACK('m', NULL, &msg, "msg",
389389
"message for the tag", parse_msg_arg),
390-
OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
390+
OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
391391
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
392392
OPT_STRING('u', NULL, &keyid, "key-id",
393393
"use another key to sign the tag"),
@@ -406,7 +406,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
406406
git_config(git_tag_config, NULL);
407407

408408
argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
409-
msgfile = parse_options_fix_filename(prefix, msgfile);
410409

411410
if (keyid) {
412411
sign = 1;

parse-options.c

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,20 @@ static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
3131
return 0;
3232
}
3333

34+
static void fix_filename(const char *prefix, const char **file)
35+
{
36+
if (!file || !*file || !prefix || is_absolute_path(*file)
37+
|| !strcmp("-", *file))
38+
return;
39+
*file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
40+
}
41+
3442
static int get_value(struct parse_opt_ctx_t *p,
3543
const struct option *opt, int flags)
3644
{
3745
const char *s, *arg;
3846
const int unset = flags & OPT_UNSET;
47+
int err;
3948

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

107+
case OPTION_FILENAME:
108+
err = 0;
109+
if (unset)
110+
*(const char **)opt->value = NULL;
111+
else if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
112+
*(const char **)opt->value = (const char *)opt->defval;
113+
else
114+
err = get_arg(p, opt, flags, (const char **)opt->value);
115+
116+
if (!err)
117+
fix_filename(p->prefix, (const char **)opt->value);
118+
return err;
119+
98120
case OPTION_CALLBACK:
99121
if (unset)
100122
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
@@ -494,6 +516,8 @@ int usage_with_options_internal(const char * const *usagestr,
494516
if (opts->flags & PARSE_OPT_NOARG)
495517
break;
496518
/* FALLTHROUGH */
519+
case OPTION_FILENAME:
520+
/* FALLTHROUGH */
497521
case OPTION_STRING:
498522
if (opts->argh)
499523
pos += usage_argh(opts);
@@ -604,15 +628,3 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
604628
commit_list_insert(commit, opt->value);
605629
return 0;
606630
}
607-
608-
/*
609-
* This should really be OPTION_FILENAME type as a part of
610-
* parse_options that take prefix to do this while parsing.
611-
*/
612-
extern const char *parse_options_fix_filename(const char *prefix, const char *file)
613-
{
614-
if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file))
615-
return file;
616-
return prefix_filename(prefix, strlen(prefix), file);
617-
}
618-

parse-options.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ enum parse_opt_type {
1717
OPTION_STRING,
1818
OPTION_INTEGER,
1919
OPTION_CALLBACK,
20+
OPTION_FILENAME
2021
};
2122

2223
enum parse_opt_flags {
@@ -117,6 +118,8 @@ struct option {
117118
#define OPT_NUMBER_CALLBACK(v, h, f) \
118119
{ OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
119120
PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
121+
#define OPT_FILENAME(s, l, v, h) { OPTION_FILENAME, (s), (l), (v), \
122+
"FILE", (h) }
120123

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

187-
extern const char *parse_options_fix_filename(const char *prefix, const char *file);
188-
189190
#endif

t/t0040-parse-options.sh

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ usage: test-parse-options <options>
1919
--set23 set integer to 23
2020
-t <time> get timestamp of <time>
2121
-L, --length <str> get length of <str>
22+
-F, --file <FILE> set file to <FILE>
2223
2324
String options
2425
-s, --string <string>
@@ -56,10 +57,12 @@ abbrev: 7
5657
verbose: 2
5758
quiet: no
5859
dry run: yes
60+
file: prefix/my.file
5961
EOF
6062

6163
test_expect_success 'short options' '
62-
test-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err &&
64+
test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
65+
> output 2> output.err &&
6366
test_cmp expect output &&
6467
test ! -s output.err
6568
'
@@ -73,11 +76,12 @@ abbrev: 10
7376
verbose: 2
7477
quiet: no
7578
dry run: no
79+
file: prefix/fi.le
7680
EOF
7781

7882
test_expect_success 'long options' '
7983
test-parse-options --boolean --integer 1729 --boolean --string2=321 \
80-
--verbose --verbose --no-dry-run --abbrev=10 \
84+
--verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
8185
> output 2> output.err &&
8286
test ! -s output.err &&
8387
test_cmp expect output
@@ -87,6 +91,8 @@ test_expect_success 'missing required value' '
8791
test-parse-options -s;
8892
test $? = 129 &&
8993
test-parse-options --string;
94+
test $? = 129 &&
95+
test-parse-options --file;
9096
test $? = 129
9197
'
9298

@@ -99,6 +105,7 @@ abbrev: 7
99105
verbose: 0
100106
quiet: no
101107
dry run: no
108+
file: (not set)
102109
arg 00: a1
103110
arg 01: b1
104111
arg 02: --boolean
@@ -120,6 +127,7 @@ abbrev: 7
120127
verbose: 0
121128
quiet: no
122129
dry run: no
130+
file: (not set)
123131
EOF
124132

125133
test_expect_success 'unambiguously abbreviated option' '
@@ -148,6 +156,7 @@ abbrev: 7
148156
verbose: 0
149157
quiet: no
150158
dry run: no
159+
file: (not set)
151160
EOF
152161

153162
test_expect_success 'non ambiguous option (after two options it abbreviates)' '
@@ -175,6 +184,7 @@ abbrev: 7
175184
verbose: 0
176185
quiet: no
177186
dry run: no
187+
file: (not set)
178188
arg 00: --quux
179189
EOF
180190

@@ -193,6 +203,7 @@ abbrev: 7
193203
verbose: 0
194204
quiet: yes
195205
dry run: no
206+
file: (not set)
196207
arg 00: foo
197208
EOF
198209

@@ -213,6 +224,7 @@ abbrev: 7
213224
verbose: 0
214225
quiet: no
215226
dry run: no
227+
file: (not set)
216228
EOF
217229

218230
test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
@@ -240,6 +252,7 @@ abbrev: 7
240252
verbose: 0
241253
quiet: no
242254
dry run: no
255+
file: (not set)
243256
EOF
244257

245258
test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
@@ -263,6 +276,7 @@ abbrev: 7
263276
verbose: 0
264277
quiet: no
265278
dry run: no
279+
file: (not set)
266280
EOF
267281

268282
test_expect_success 'OPT_BIT() works' '
@@ -292,6 +306,7 @@ abbrev: 7
292306
verbose: 0
293307
quiet: no
294308
dry run: no
309+
file: (not set)
295310
EOF
296311

297312
test_expect_success 'OPT_NUMBER_CALLBACK() works' '

test-parse-options.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ static unsigned long timestamp;
77
static int abbrev = 7;
88
static int verbose = 0, dry_run = 0, quiet = 0;
99
static char *string = NULL;
10+
static char *file = NULL;
1011

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

2829
int main(int argc, const char **argv)
2930
{
31+
const char *prefix = "prefix/";
3032
const char *usage[] = {
3133
"test-parse-options <options>",
3234
NULL
@@ -43,6 +45,7 @@ int main(int argc, const char **argv)
4345
OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
4446
OPT_CALLBACK('L', "length", &integer, "str",
4547
"get length of <str>", length_callback),
48+
OPT_FILENAME('F', "file", &file, "set file to <FILE>"),
4649
OPT_GROUP("String options"),
4750
OPT_STRING('s', "string", &string, "string", "get a string"),
4851
OPT_STRING(0, "string2", &string, "str", "get another string"),
@@ -65,7 +68,7 @@ int main(int argc, const char **argv)
6568
};
6669
int i;
6770

68-
argc = parse_options(argc, argv, NULL, options, usage, 0);
71+
argc = parse_options(argc, argv, prefix, options, usage, 0);
6972

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

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

0 commit comments

Comments
 (0)