/* 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);
*(int *)opt->value |= opt->defval;
return 0;
+ case OPTION_NEGBIT:
+ if (unset)
+ *(int *)opt->value |= opt->defval;
+ else
+ *(int *)opt->value &= ~opt->defval;
+ return 0;
+
case OPTION_BOOLEAN:
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1;
return 0;
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
{
+ const struct option *numopt = NULL;
+
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);
}
+
+ /*
+ * Handle the numerical option later, explicit one-digit
+ * options take precedence over it.
+ */
+ if (options->type == OPTION_NUMBER)
+ numopt = options;
+ }
+ if (numopt && isdigit(*p->opt)) {
+ size_t len = 1;
+ char *arg;
+ int rc;
+
+ while (isdigit(p->opt[len]))
+ len++;
+ arg = xmemdupz(p->opt, len);
+ p->opt = p->opt[len] ? p->opt + len : NULL;
+ rc = (*numopt->callback)(numopt, arg, 0) ? (-1) : 0;
+ free(arg);
+ return rc;
}
return -2;
}
return -2;
}
+static int parse_nodash_opt(struct parse_opt_ctx_t *p, const char *arg,
+ const struct option *options)
+{
+ 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);
+ }
+ return -2;
+}
+
static void check_typos(const char *arg, const struct option *options)
{
if (strlen(arg) < 3)
const char *arg = ctx->argv[0];
if (*arg != '-' || !arg[1]) {
+ if (parse_nodash_opt(ctx, arg, options) == 0)
+ continue;
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break;
ctx->out[ctx->cpidx++] = ctx->argv[0];
return parse_options_end(&ctx);
}
+ static int usage_argh(const struct option *opts)
+ {
+ const char *s;
+ int literal = opts->flags & PARSE_OPT_LITERAL_ARGHELP;
+ 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(stderr, s, opts->argh);
+ }
+
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
continue;
pos = fprintf(stderr, " ");
- if (opts->short_name)
- pos += fprintf(stderr, "-%c", opts->short_name);
+ if (opts->short_name) {
+ if (opts->flags & PARSE_OPT_NODASH)
+ pos += fprintf(stderr, "%c", opts->short_name);
+ else
+ pos += fprintf(stderr, "-%c", opts->short_name);
+ }
if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", ");
if (opts->long_name)
pos += fprintf(stderr, "--%s", opts->long_name);
+ if (opts->type == OPTION_NUMBER)
+ pos += fprintf(stderr, "-NUM");
switch (opts->type) {
case OPTION_ARGUMENT:
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->argh)
+ pos += usage_argh(opts);
+ else {
if (opts->flags & PARSE_OPT_OPTARG)
if (opts->long_name)
pos += fprintf(stderr, "[=...]");
pos += fprintf(stderr, " ...");
}
break;
- default: /* OPTION_{BIT,BOOLEAN,SET_INT,SET_PTR} */
+ default: /* OPTION_{BIT,BOOLEAN,NUMBER,SET_INT,SET_PTR} */
break;
}
OPTION_END,
OPTION_ARGUMENT,
OPTION_GROUP,
+ OPTION_NUMBER,
/* options with no arguments */
OPTION_BIT,
+ OPTION_NEGBIT,
OPTION_BOOLEAN, /* _INCR would have been a better name */
OPTION_SET_INT,
OPTION_SET_PTR,
PARSE_OPT_NONEG = 4,
PARSE_OPT_HIDDEN = 8,
PARSE_OPT_LASTARG_DEFAULT = 16,
+ PARSE_OPT_NODASH = 32,
+ PARSE_OPT_LITERAL_ARGHELP = 64,
};
struct option;
* PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
* PARSE_OPT_NONEG: says that this option cannot be negated
- * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
- * the long one.
+ * PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
+ * shown only in the full usage.
+ * PARSE_OPT_LASTARG_DEFAULT: if no argument is given, the default value
+ * is used.
+ * PARSE_OPT_NODASH: this option doesn't start with a dash.
+ * PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
+ * (i.e. '<argh>') in the help message.
+ * Useful for options with multiple parameters.
*
* `callback`::
* pointer to the callback to use for OPTION_CALLBACK.
#define OPT_ARGUMENT(l, h) { OPTION_ARGUMENT, 0, (l), NULL, NULL, (h) }
#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) }
#define OPT_BIT(s, l, v, h, b) { OPTION_BIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
+#define OPT_NEGBIT(s, l, v, h, b) { OPTION_NEGBIT, (s), (l), (v), NULL, (h), 0, NULL, (b) }
#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) }
#define OPT_SET_INT(s, l, v, h, i) { OPTION_SET_INT, (s), (l), (v), NULL, (h), 0, NULL, (i) }
#define OPT_SET_PTR(s, l, v, h, p) { OPTION_SET_PTR, (s), (l), (v), NULL, (h), 0, NULL, (p) }
parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \
{ OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
+#define OPT_NUMBER_CALLBACK(v, h, f) \
+ { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
/* parse_options() will filter out the processed options and leave the
* non-option arguments in argv[].