summaryrefslogtreecommitdiff
path: root/parse-options.c
diff options
context:
space:
mode:
Diffstat (limited to 'parse-options.c')
-rw-r--r--parse-options.c365
1 files changed, 177 insertions, 188 deletions
diff --git a/parse-options.c b/parse-options.c
index c52b8ccf59..c1c66bd408 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -2,11 +2,23 @@
#include "parse-options.h"
#include "cache.h"
#include "commit.h"
+#include "color.h"
+
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int err);
#define OPT_SHORT 1
#define OPT_UNSET 2
-static int opterror(const struct option *opt, const char *reason, int flags)
+int optbug(const struct option *opt, const char *reason)
+{
+ if (opt->long_name)
+ return error("BUG: option '%s' %s", opt->long_name, reason);
+ return error("BUG: switch '%c' %s", opt->short_name, reason);
+}
+
+int opterror(const struct option *opt, const char *reason, int flags)
{
if (flags & OPT_SHORT)
return error("switch `%c' %s", opt->short_name, reason);
@@ -31,35 +43,32 @@ 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);
if (unset && (opt->flags & PARSE_OPT_NONEG))
return opterror(opt, "isn't available", flags);
-
- if (!(flags & OPT_SHORT) && p->opt) {
- switch (opt->type) {
- case OPTION_CALLBACK:
- if (!(opt->flags & PARSE_OPT_NOARG))
- break;
- /* FALLTHROUGH */
- case OPTION_BOOLEAN:
- case OPTION_BIT:
- case OPTION_NEGBIT:
- case OPTION_SET_INT:
- case OPTION_SET_PTR:
- return opterror(opt, "takes no value", flags);
- default:
- break;
- }
- }
+ if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
+ return opterror(opt, "takes no value", flags);
switch (opt->type) {
+ case OPTION_LOWLEVEL_CALLBACK:
+ return (*(parse_opt_ll_cb *)opt->callback)(p, opt, unset);
+
case OPTION_BIT:
if (unset)
*(int *)opt->value &= ~opt->defval;
@@ -74,7 +83,7 @@ static int get_value(struct parse_opt_ctx_t *p,
*(int *)opt->value &= ~opt->defval;
return 0;
- case OPTION_BOOLEAN:
+ case OPTION_COUNTUP:
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
return 0;
@@ -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;
@@ -171,13 +193,14 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
arg_end = arg + strlen(arg);
for (; options->type != OPTION_END; options++) {
- const char *rest;
- int flags = 0;
+ const char *rest, *long_name = options->long_name;
+ int flags = 0, opt_flags = 0;
- if (!options->long_name)
+ if (!long_name)
continue;
- rest = skip_prefix(arg, options->long_name);
+again:
+ rest = skip_prefix(arg, long_name);
if (options->type == OPTION_ARGUMENT) {
if (!rest)
continue;
@@ -190,7 +213,7 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
}
if (!rest) {
/* abbreviated? */
- if (!strncmp(options->long_name, arg, arg_end - arg)) {
+ if (!strncmp(long_name, arg, arg_end - arg)) {
is_abbreviated:
if (abbrev_option) {
/*
@@ -205,21 +228,30 @@ is_abbreviated:
if (!(flags & OPT_UNSET) && *arg_end)
p->opt = arg_end + 1;
abbrev_option = options;
- abbrev_flags = flags;
+ abbrev_flags = flags ^ opt_flags;
continue;
}
+ /* negation allowed? */
+ if (options->flags & PARSE_OPT_NONEG)
+ continue;
/* negated and abbreviated very much? */
if (!prefixcmp("no-", arg)) {
flags |= OPT_UNSET;
goto is_abbreviated;
}
/* negated? */
- if (strncmp(arg, "no-", 3))
+ if (prefixcmp(arg, "no-")) {
+ if (!prefixcmp(long_name, "no-")) {
+ long_name += 3;
+ opt_flags |= OPT_UNSET;
+ goto again;
+ }
continue;
+ }
flags |= OPT_UNSET;
- rest = skip_prefix(arg + 3, options->long_name);
+ rest = skip_prefix(arg + 3, long_name);
/* abbreviated and negated? */
- if (!rest && !prefixcmp(options->long_name, arg + 3))
+ if (!rest && !prefixcmp(long_name, arg + 3))
goto is_abbreviated;
if (!rest)
continue;
@@ -229,7 +261,7 @@ is_abbreviated:
continue;
p->opt = rest + 1;
}
- return get_value(p, options, flags);
+ return get_value(p, options, flags ^ opt_flags);
}
if (ambiguous_option)
@@ -251,13 +283,6 @@ static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
for (; options->type != OPTION_END; options++) {
if (!(options->flags & PARSE_OPT_NODASH))
continue;
- if ((options->flags & PARSE_OPT_OPTARG) ||
- !(options->flags & PARSE_OPT_NOARG))
- die("BUG: dashless options don't support arguments");
- if (!(options->flags & PARSE_OPT_NONEG))
- die("BUG: dashless options don't support negation");
- if (options->long_name)
- die("BUG: dashless options can't be long");
if (options->short_name == arg[0] && arg[1] == '\0')
return get_value(p, options, OPT_SHORT);
}
@@ -284,22 +309,60 @@ static void check_typos(const char *arg, const struct option *options)
}
}
+static void parse_options_check(const struct option *opts)
+{
+ int err = 0;
+
+ for (; opts->type != OPTION_END; opts++) {
+ if ((opts->flags & PARSE_OPT_LASTARG_DEFAULT) &&
+ (opts->flags & PARSE_OPT_OPTARG))
+ err |= optbug(opts, "uses incompatible flags "
+ "LASTARG_DEFAULT and OPTARG");
+ if (opts->flags & PARSE_OPT_NODASH &&
+ ((opts->flags & PARSE_OPT_OPTARG) ||
+ !(opts->flags & PARSE_OPT_NOARG) ||
+ !(opts->flags & PARSE_OPT_NONEG) ||
+ opts->long_name))
+ err |= optbug(opts, "uses feature "
+ "not supported for dashless options");
+ switch (opts->type) {
+ case OPTION_COUNTUP:
+ case OPTION_BIT:
+ case OPTION_NEGBIT:
+ case OPTION_SET_INT:
+ case OPTION_SET_PTR:
+ case OPTION_NUMBER:
+ if ((opts->flags & PARSE_OPT_OPTARG) ||
+ !(opts->flags & PARSE_OPT_NOARG))
+ err |= optbug(opts, "should not accept an argument");
+ default:
+ ; /* ok. (usually accepts an argument) */
+ }
+ }
+ if (err)
+ exit(128);
+}
+
void parse_options_start(struct parse_opt_ctx_t *ctx,
- int argc, const char **argv, int flags)
+ int argc, const char **argv, const char *prefix,
+ const struct option *options, int flags)
{
memset(ctx, 0, sizeof(*ctx));
ctx->argc = argc - 1;
ctx->argv = argv + 1;
ctx->out = argv;
+ ctx->prefix = prefix;
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
ctx->flags = flags;
if ((flags & PARSE_OPT_KEEP_UNKNOWN) &&
(flags & PARSE_OPT_STOP_AT_NON_OPTION))
die("STOP_AT_NON_OPTION and KEEP_UNKNOWN don't go together");
+ parse_options_check(options);
}
-static int usage_with_options_internal(const char * const *,
- const struct option *, int);
+static int usage_with_options_internal(struct parse_opt_ctx_t *,
+ const char * const *,
+ const struct option *, int, int);
int parse_options_step(struct parse_opt_ctx_t *ctx,
const struct option *options,
@@ -317,7 +380,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
if (parse_nodash_opt(ctx, arg, options) == 0)
continue;
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
- break;
+ return PARSE_OPT_NON_OPTION;
ctx->out[ctx->cpidx++] = ctx->argv[0];
continue;
}
@@ -325,21 +388,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
if (arg[1] != '-') {
ctx->opt = arg + 1;
if (internal_help && *ctx->opt == 'h')
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
+ if (ctx->opt)
+ check_typos(arg + 1, options);
goto unknown;
}
if (ctx->opt)
check_typos(arg + 1, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h')
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_short_opt(ctx, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
/* fake a short option thing to hide the fact that we may have
* started to parse aggregated stuff
@@ -363,12 +428,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
}
if (internal_help && !strcmp(arg + 2, "help-all"))
- return usage_with_options_internal(usagestr, options, 1);
+ return usage_with_options_internal(ctx, usagestr, options, 1, 0);
if (internal_help && !strcmp(arg + 2, "help"))
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 0);
switch (parse_long_opt(ctx, arg + 2, options)) {
case -1:
- return parse_options_usage(usagestr, options);
+ return parse_options_usage(ctx, usagestr, options, 1);
case -2:
goto unknown;
}
@@ -389,15 +454,17 @@ int parse_options_end(struct parse_opt_ctx_t *ctx)
return ctx->cpidx + ctx->argc;
}
-int parse_options(int argc, const char **argv, const struct option *options,
- const char * const usagestr[], int flags)
+int parse_options(int argc, const char **argv, const char *prefix,
+ const struct option *options, const char * const usagestr[],
+ int flags)
{
struct parse_opt_ctx_t ctx;
- parse_options_start(&ctx, argc, argv, flags);
+ parse_options_start(&ctx, argc, argv, prefix, options, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
exit(129);
+ case PARSE_OPT_NON_OPTION:
case PARSE_OPT_DONE:
break;
default: /* PARSE_OPT_UNKNOWN */
@@ -409,199 +476,121 @@ int parse_options(int argc, const char **argv, const struct option *options,
usage_with_options(usagestr, options);
}
+ precompose_argv(argc, argv);
return parse_options_end(&ctx);
}
+static int usage_argh(const struct option *opts, FILE *outfile)
+{
+ const char *s;
+ int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || !opts->argh;
+ if (opts->flags & PARSE_OPT_OPTARG)
+ if (opts->long_name)
+ s = literal ? "[=%s]" : "[=<%s>]";
+ else
+ s = literal ? "[%s]" : "[<%s>]";
+ else
+ s = literal ? " %s" : " <%s>";
+ return fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
+}
+
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-int usage_with_options_internal(const char * const *usagestr,
- const struct option *opts, int full)
+static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int full, int err)
{
+ FILE *outfile = err ? stderr : stdout;
+
if (!usagestr)
return PARSE_OPT_HELP;
- fprintf(stderr, "usage: %s\n", *usagestr++);
+ if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+ fprintf(outfile, "cat <<\\EOF\n");
+
+ fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
while (*usagestr && **usagestr)
- fprintf(stderr, " or: %s\n", *usagestr++);
+ /* TRANSLATORS: the colon here should align with the
+ one in "usage: %s" translation */
+ fprintf_ln(outfile, _(" or: %s"), _(*usagestr++));
while (*usagestr) {
- fprintf(stderr, "%s%s\n",
- **usagestr ? " " : "",
- *usagestr);
+ if (**usagestr)
+ fprintf_ln(outfile, _(" %s"), _(*usagestr));
+ else
+ putchar('\n');
usagestr++;
}
if (opts->type != OPTION_GROUP)
- fputc('\n', stderr);
+ fputc('\n', outfile);
for (; opts->type != OPTION_END; opts++) {
size_t pos;
int pad;
if (opts->type == OPTION_GROUP) {
- fputc('\n', stderr);
+ fputc('\n', outfile);
if (*opts->help)
- fprintf(stderr, "%s\n", opts->help);
+ fprintf(outfile, "%s\n", _(opts->help));
continue;
}
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
continue;
- pos = fprintf(stderr, " ");
+ pos = fprintf(outfile, " ");
if (opts->short_name) {
if (opts->flags & PARSE_OPT_NODASH)
- pos += fprintf(stderr, "%c", opts->short_name);
+ pos += fprintf(outfile, "%c", opts->short_name);
else
- pos += fprintf(stderr, "-%c", opts->short_name);
+ pos += fprintf(outfile, "-%c", opts->short_name);
}
if (opts->long_name && opts->short_name)
- pos += fprintf(stderr, ", ");
+ pos += fprintf(outfile, ", ");
if (opts->long_name)
- pos += fprintf(stderr, "--%s", opts->long_name);
+ pos += fprintf(outfile, "--%s", opts->long_name);
if (opts->type == OPTION_NUMBER)
- pos += fprintf(stderr, "-NUM");
+ pos += fprintf(outfile, "-NUM");
- switch (opts->type) {
- case OPTION_ARGUMENT:
- break;
- case OPTION_INTEGER:
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=<n>]");
- else
- pos += fprintf(stderr, "[<n>]");
- else
- pos += fprintf(stderr, " <n>");
- break;
- case OPTION_CALLBACK:
- if (opts->flags & PARSE_OPT_NOARG)
- break;
- /* FALLTHROUGH */
- case OPTION_STRING:
- if (opts->argh) {
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=<%s>]", opts->argh);
- else
- pos += fprintf(stderr, "[<%s>]", opts->argh);
- else
- pos += fprintf(stderr, " <%s>", opts->argh);
- } else {
- if (opts->flags & PARSE_OPT_OPTARG)
- if (opts->long_name)
- pos += fprintf(stderr, "[=...]");
- else
- pos += fprintf(stderr, "[...]");
- else
- pos += fprintf(stderr, " ...");
- }
- break;
- default: /* OPTION_{BIT,BOOLEAN,NUMBER,SET_INT,SET_PTR} */
- break;
- }
+ if ((opts->flags & PARSE_OPT_LITERAL_ARGHELP) ||
+ !(opts->flags & PARSE_OPT_NOARG))
+ pos += usage_argh(opts, outfile);
if (pos <= USAGE_OPTS_WIDTH)
pad = USAGE_OPTS_WIDTH - pos;
else {
- fputc('\n', stderr);
+ fputc('\n', outfile);
pad = USAGE_OPTS_WIDTH;
}
- fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
}
- fputc('\n', stderr);
+ fputc('\n', outfile);
+
+ if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
+ fputs("EOF\n", outfile);
return PARSE_OPT_HELP;
}
-void usage_with_options(const char * const *usagestr,
+void NORETURN usage_with_options(const char * const *usagestr,
const struct option *opts)
{
- usage_with_options_internal(usagestr, opts, 0);
+ usage_with_options_internal(NULL, usagestr, opts, 0, 1);
exit(129);
}
-int parse_options_usage(const char * const *usagestr,
- const struct option *opts)
-{
- return usage_with_options_internal(usagestr, opts, 0);
-}
-
-
-/*----- some often used options -----*/
-#include "cache.h"
-
-int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
+void NORETURN usage_msg_opt(const char *msg,
+ const char * const *usagestr,
+ const struct option *options)
{
- int v;
-
- if (!arg) {
- v = unset ? 0 : DEFAULT_ABBREV;
- } else {
- v = strtol(arg, (char **)&arg, 10);
- if (*arg)
- return opterror(opt, "expects a numerical value", 0);
- if (v && v < MINIMUM_ABBREV)
- v = MINIMUM_ABBREV;
- else if (v > 40)
- v = 40;
- }
- *(int *)(opt->value) = v;
- return 0;
-}
-
-int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
- int unset)
-{
- *(unsigned long *)(opt->value) = approxidate(arg);
- return 0;
-}
-
-int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
- int unset)
-{
- int *target = opt->value;
-
- if (unset)
- /* --no-quiet, --no-verbose */
- *target = 0;
- else if (opt->short_name == 'v') {
- if (*target >= 0)
- (*target)++;
- else
- *target = 1;
- } else {
- if (*target <= 0)
- (*target)--;
- else
- *target = -1;
- }
- return 0;
-}
-
-int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
-{
- unsigned char sha1[20];
- struct commit *commit;
-
- if (!arg)
- return -1;
- if (get_sha1(arg, sha1))
- return error("malformed object name %s", arg);
- commit = lookup_commit_reference(sha1);
- if (!commit)
- return error("no such commit %s", arg);
- commit_list_insert(commit, opt->value);
- return 0;
+ fprintf(stderr, "%s\n\n", msg);
+ usage_with_options(usagestr, options);
}
-/*
- * 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)
+static int parse_options_usage(struct parse_opt_ctx_t *ctx,
+ const char * const *usagestr,
+ const struct option *opts, int err)
{
- if (!file || !prefix || is_absolute_path(file) || !strcmp("-", file))
- return file;
- return prefix_filename(prefix, strlen(prefix), file);
+ return usage_with_options_internal(ctx, usagestr, opts, 0, err);
}