-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The option parser takes argc, argv, an array of struct option and a usage string. Each of the struct option elements in the array describes a valid option, its type and a pointer to the location where the value is written. The entry point is parse_options(), which scans through the given argv, and matches each option there against the list of valid options. During the scan, argv is rewritten to only contain the non-option command line arguments and the number of these is returned. Aggregation of single switches is allowed: -rC0 is the same as -r -C 0 (supposing that -C wants an arg). Every long option automatically support the option with the same name, prefixed with 'no-' to unset the switch. It assumes that initial value for strings are "NULL" and for integers is "0". Long options are supported either with '=' or without: --some-option=foo is the same as --some-option foo Acked-by: Kristian Høgsberg <krh@redhat.com> Signed-off-by: Pierre Habouzit <madcoder@debian.org> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
- Loading branch information
Pierre Habouzit
authored and
Junio C Hamano
committed
Oct 30, 2007
1 parent
09149c7
commit 4a59fd1
Showing
3 changed files
with
204 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
#include "git-compat-util.h" | ||
#include "parse-options.h" | ||
#include "strbuf.h" | ||
|
||
#define OPT_SHORT 1 | ||
#define OPT_UNSET 2 | ||
|
||
struct optparse_t { | ||
const char **argv; | ||
int argc; | ||
const char *opt; | ||
}; | ||
|
||
static inline const char *get_arg(struct optparse_t *p) | ||
{ | ||
if (p->opt) { | ||
const char *res = p->opt; | ||
p->opt = NULL; | ||
return res; | ||
} | ||
p->argc--; | ||
return *++p->argv; | ||
} | ||
|
||
static inline const char *skip_prefix(const char *str, const char *prefix) | ||
{ | ||
size_t len = strlen(prefix); | ||
return strncmp(str, prefix, len) ? NULL : str + len; | ||
} | ||
|
||
static int opterror(const struct option *opt, const char *reason, int flags) | ||
{ | ||
if (flags & OPT_SHORT) | ||
return error("switch `%c' %s", opt->short_name, reason); | ||
if (flags & OPT_UNSET) | ||
return error("option `no-%s' %s", opt->long_name, reason); | ||
return error("option `%s' %s", opt->long_name, reason); | ||
} | ||
|
||
static int get_value(struct optparse_t *p, | ||
const struct option *opt, int flags) | ||
{ | ||
const char *s; | ||
|
||
if (p->opt && (flags & OPT_UNSET)) | ||
return opterror(opt, "takes no value", flags); | ||
|
||
switch (opt->type) { | ||
case OPTION_BOOLEAN: | ||
if (!(flags & OPT_SHORT) && p->opt) | ||
return opterror(opt, "takes no value", flags); | ||
if (flags & OPT_UNSET) | ||
*(int *)opt->value = 0; | ||
else | ||
(*(int *)opt->value)++; | ||
return 0; | ||
|
||
case OPTION_STRING: | ||
if (flags & OPT_UNSET) { | ||
*(const char **)opt->value = (const char *)NULL; | ||
return 0; | ||
} | ||
if (!p->opt && p->argc <= 1) | ||
return opterror(opt, "requires a value", flags); | ||
*(const char **)opt->value = get_arg(p); | ||
return 0; | ||
|
||
case OPTION_INTEGER: | ||
if (flags & OPT_UNSET) { | ||
*(int *)opt->value = 0; | ||
return 0; | ||
} | ||
if (!p->opt && p->argc <= 1) | ||
return opterror(opt, "requires a value", flags); | ||
*(int *)opt->value = strtol(get_arg(p), (char **)&s, 10); | ||
if (*s) | ||
return opterror(opt, "expects a numerical value", flags); | ||
return 0; | ||
|
||
default: | ||
die("should not happen, someone must be hit on the forehead"); | ||
} | ||
} | ||
|
||
static int parse_short_opt(struct optparse_t *p, const struct option *options) | ||
{ | ||
for (; options->type != OPTION_END; options++) { | ||
if (options->short_name == *p->opt) { | ||
p->opt = p->opt[1] ? p->opt + 1 : NULL; | ||
return get_value(p, options, OPT_SHORT); | ||
} | ||
} | ||
return error("unknown switch `%c'", *p->opt); | ||
} | ||
|
||
static int parse_long_opt(struct optparse_t *p, const char *arg, | ||
const struct option *options) | ||
{ | ||
for (; options->type != OPTION_END; options++) { | ||
const char *rest; | ||
int flags = 0; | ||
|
||
if (!options->long_name) | ||
continue; | ||
|
||
rest = skip_prefix(arg, options->long_name); | ||
if (!rest) { | ||
if (strncmp(arg, "no-", 3)) | ||
continue; | ||
flags |= OPT_UNSET; | ||
rest = skip_prefix(arg + 3, options->long_name); | ||
if (!rest) | ||
continue; | ||
} | ||
if (*rest) { | ||
if (*rest != '=') | ||
continue; | ||
p->opt = rest + 1; | ||
} | ||
return get_value(p, options, flags); | ||
} | ||
return error("unknown option `%s'", arg); | ||
} | ||
|
||
int parse_options(int argc, const char **argv, const struct option *options, | ||
const char *usagestr, int flags) | ||
{ | ||
struct optparse_t args = { argv + 1, argc - 1, NULL }; | ||
int j = 0; | ||
|
||
for (; args.argc; args.argc--, args.argv++) { | ||
const char *arg = args.argv[0]; | ||
|
||
if (*arg != '-' || !arg[1]) { | ||
argv[j++] = args.argv[0]; | ||
continue; | ||
} | ||
|
||
if (arg[1] != '-') { | ||
args.opt = arg + 1; | ||
do { | ||
if (*args.opt == 'h') | ||
usage(usagestr); | ||
if (parse_short_opt(&args, options) < 0) | ||
usage(usagestr); | ||
} while (args.opt); | ||
continue; | ||
} | ||
|
||
if (!arg[2]) { /* "--" */ | ||
if (!(flags & PARSE_OPT_KEEP_DASHDASH)) { | ||
args.argc--; | ||
args.argv++; | ||
} | ||
break; | ||
} | ||
|
||
if (!strcmp(arg + 2, "help")) | ||
usage(usagestr); | ||
if (parse_long_opt(&args, arg + 2, options)) | ||
usage(usagestr); | ||
} | ||
|
||
memmove(argv + j, args.argv, args.argc * sizeof(*argv)); | ||
argv[j + args.argc] = NULL; | ||
return j + args.argc; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#ifndef PARSE_OPTIONS_H | ||
#define PARSE_OPTIONS_H | ||
|
||
enum parse_opt_type { | ||
OPTION_END, | ||
OPTION_BOOLEAN, | ||
OPTION_STRING, | ||
OPTION_INTEGER, | ||
}; | ||
|
||
enum parse_opt_flags { | ||
PARSE_OPT_KEEP_DASHDASH = 1, | ||
}; | ||
|
||
struct option { | ||
enum parse_opt_type type; | ||
int short_name; | ||
const char *long_name; | ||
void *value; | ||
}; | ||
|
||
#define OPT_END() { OPTION_END } | ||
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v) } | ||
#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v) } | ||
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v) } | ||
|
||
/* parse_options() will filter out the processed options and leave the | ||
* non-option argments in argv[]. | ||
* Returns the number of arguments left in argv[]. | ||
*/ | ||
extern int parse_options(int argc, const char **argv, | ||
const struct option *options, | ||
const char *usagestr, int flags); | ||
|
||
#endif |