// SPDX-License-Identifier: GPL-2.0
#include <linux/compiler.h>
#include <linux/string.h>
#include <linux/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include "subcmd-util.h"
#include "parse-options.h"
#include "subcmd-config.h"
#include "pager.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
char *error_buf;
static int opterror(const struct option *opt, const char *reason, int flags)
{
if (flags & OPT_SHORT)
fprintf(stderr, " Error: switch `%c' %s" , opt->short_name, reason);
else if (flags & OPT_UNSET)
fprintf(stderr, " Error: option `no-%s' %s" , opt->long_name, reason);
else
fprintf(stderr, " Error: option `%s' %s" , opt->long_name, reason);
return -1 ;
}
static const char *skip_prefix(const char *str, const char *prefix)
{
size_t len = strlen(prefix);
return strncmp(str, prefix, len) ? NULL : str + len;
}
static void optwarning(const struct option *opt, const char *reason, int flags)
{
if (flags & OPT_SHORT)
fprintf(stderr, " Warning: switch `%c' %s" , opt->short_name, reason);
else if (flags & OPT_UNSET)
fprintf(stderr, " Warning: option `no-%s' %s" , opt->long_name, reason);
else
fprintf(stderr, " Warning: option `%s' %s" , opt->long_name, reason);
}
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
int flags, const char **arg)
{
const char *res;
if (p->opt) {
res = p->opt;
p->opt = NULL;
} else if ((opt->flags & PARSE_OPT_LASTARG_DEFAULT) && (p->argc == 1 ||
**(p->argv + 1 ) == '-' )) {
res = (const char *)opt->defval;
} else if (p->argc > 1 ) {
p->argc--;
res = *++p->argv;
} else
return opterror(opt, "requires a value" , flags);
if (arg)
*arg = res;
return 0 ;
}
static int get_value(struct parse_opt_ctx_t *p,
const struct option *opt, int flags)
{
const char *s, *arg = NULL;
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 (opt->flags & PARSE_OPT_DISABLED)
return opterror(opt, "is not usable" , flags);
if (opt->flags & PARSE_OPT_EXCLUSIVE) {
if (p->excl_opt && p->excl_opt != opt) {
char msg[128 ];
if (((flags & OPT_SHORT) && p->excl_opt->short_name) ||
p->excl_opt->long_name == NULL) {
snprintf(msg, sizeof (msg), "cannot be used with switch `%c'" ,
p->excl_opt->short_name);
} else {
snprintf(msg, sizeof (msg), "cannot be used with %s" ,
p->excl_opt->long_name);
}
opterror(opt, msg, flags);
return -3 ;
}
p->excl_opt = opt;
}
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_INCR:
case OPTION_BIT:
case OPTION_SET_UINT:
case OPTION_SET_PTR:
return opterror(opt, "takes no value" , flags);
case OPTION_END:
case OPTION_ARGUMENT:
case OPTION_GROUP:
case OPTION_STRING:
case OPTION_INTEGER:
case OPTION_UINTEGER:
case OPTION_LONG:
case OPTION_ULONG:
case OPTION_U64:
default :
break ;
}
}
if (opt->flags & PARSE_OPT_NOBUILD) {
char reason[128 ];
bool noarg = false ;
err = snprintf(reason, sizeof (reason),
opt->flags & PARSE_OPT_CANSKIP ?
"is being ignored because %s " :
"is not available because %s" ,
opt->build_opt);
reason[sizeof (reason) - 1 ] = '\0' ;
if (err < 0 )
strncpy(reason, opt->flags & PARSE_OPT_CANSKIP ?
"is being ignored" :
"is not available" ,
sizeof (reason));
if (!(opt->flags & PARSE_OPT_CANSKIP))
return opterror(opt, reason, flags);
err = 0 ;
if (unset)
noarg = true ;
if (opt->flags & PARSE_OPT_NOARG)
noarg = true ;
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
noarg = true ;
switch (opt->type) {
case OPTION_BOOLEAN:
case OPTION_INCR:
case OPTION_BIT:
case OPTION_SET_UINT:
case OPTION_SET_PTR:
case OPTION_END:
case OPTION_ARGUMENT:
case OPTION_GROUP:
noarg = true ;
break ;
case OPTION_CALLBACK:
case OPTION_STRING:
case OPTION_INTEGER:
case OPTION_UINTEGER:
case OPTION_LONG:
case OPTION_ULONG:
case OPTION_U64:
default :
break ;
}
if (!noarg)
err = get_arg(p, opt, flags, NULL);
if (err)
return err;
optwarning(opt, reason, flags);
return 0 ;
}
switch (opt->type) {
case OPTION_BIT:
if (unset)
*(int *)opt->value &= ~opt->defval;
else
*(int *)opt->value |= opt->defval;
return 0 ;
case OPTION_BOOLEAN:
*(bool *)opt->value = unset ? false : true ;
if (opt->set)
*(bool *)opt->set = true ;
return 0 ;
case OPTION_INCR:
*(int *)opt->value = unset ? 0 : *(int *)opt->value + 1 ;
return 0 ;
case OPTION_SET_UINT:
*(unsigned int *)opt->value = unset ? 0 : opt->defval;
return 0 ;
case OPTION_SET_PTR:
*(void **)opt->value = unset ? NULL : (void *)opt->defval;
return 0 ;
case OPTION_STRING:
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 (opt->set)
*(bool *)opt->set = true ;
/* PARSE_OPT_NOEMPTY: Allow NULL but disallow empty string. */
if (opt->flags & PARSE_OPT_NOEMPTY) {
const char *val = *(const char **)opt->value;
if (!val)
return err;
/* Similar to unset if we are given an empty string. */
if (val[0 ] == '\0' ) {
*(const char **)opt->value = NULL;
return 0 ;
}
}
return err;
case OPTION_CALLBACK:
if (opt->set)
*(bool *)opt->set = true ;
if (unset)
return (*opt->callback)(opt, NULL, 1 ) ? (-1 ) : 0 ;
if (opt->flags & PARSE_OPT_NOARG)
return (*opt->callback)(opt, NULL, 0 ) ? (-1 ) : 0 ;
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
return (*opt->callback)(opt, NULL, 0 ) ? (-1 ) : 0 ;
if (get_arg(p, opt, flags, &arg))
return -1 ;
return (*opt->callback)(opt, arg, 0 ) ? (-1 ) : 0 ;
case OPTION_INTEGER:
if (unset) {
*(int *)opt->value = 0 ;
return 0 ;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(int *)opt->value = opt->defval;
return 0 ;
}
if (get_arg(p, opt, flags, &arg))
return -1 ;
*(int *)opt->value = strtol(arg, (char **)&s, 10 );
if (*s)
return opterror(opt, "expects a numerical value" , flags);
return 0 ;
case OPTION_UINTEGER:
if (unset) {
*(unsigned int *)opt->value = 0 ;
return 0 ;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(unsigned int *)opt->value = opt->defval;
return 0 ;
}
if (get_arg(p, opt, flags, &arg))
return -1 ;
if (arg[0 ] == '-' )
return opterror(opt, "expects an unsigned numerical value" , flags);
*(unsigned int *)opt->value = strtol(arg, (char **)&s, 10 );
if (*s)
return opterror(opt, "expects a numerical value" , flags);
return 0 ;
case OPTION_LONG:
if (unset) {
*(long *)opt->value = 0 ;
return 0 ;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(long *)opt->value = opt->defval;
return 0 ;
}
if (get_arg(p, opt, flags, &arg))
return -1 ;
*(long *)opt->value = strtol(arg, (char **)&s, 10 );
if (*s)
return opterror(opt, "expects a numerical value" , flags);
return 0 ;
case OPTION_ULONG:
if (unset) {
*(unsigned long *)opt->value = 0 ;
return 0 ;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(unsigned long *)opt->value = opt->defval;
return 0 ;
}
if (get_arg(p, opt, flags, &arg))
return -1 ;
*(unsigned long *)opt->value = strtoul(arg, (char **)&s, 10 );
if (*s)
return opterror(opt, "expects a numerical value" , flags);
return 0 ;
case OPTION_U64:
if (unset) {
*(u64 *)opt->value = 0 ;
return 0 ;
}
if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
*(u64 *)opt->value = opt->defval;
return 0 ;
}
if (get_arg(p, opt, flags, &arg))
return -1 ;
if (arg[0 ] == '-' )
return opterror(opt, "expects an unsigned numerical value" , flags);
*(u64 *)opt->value = strtoull(arg, (char **)&s, 10 );
if (*s)
return opterror(opt, "expects a numerical value" , flags);
return 0 ;
case OPTION_END:
case OPTION_ARGUMENT:
case OPTION_GROUP:
default :
die("should not happen, someone must be hit on the forehead" );
}
}
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
{
retry:
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);
}
}
if (options->parent) {
options = options->parent;
goto retry;
}
return -2 ;
}
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
const struct option *options)
{
const char *arg_end = strchr(arg, '=' );
const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
int abbrev_flags = 0 , ambiguous_flags = 0 ;
if (!arg_end)
arg_end = arg + strlen(arg);
retry:
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 (options->type == OPTION_ARGUMENT) {
if (!rest)
continue ;
if (*rest == '=' )
return opterror(options, "takes no value" , flags);
if (*rest)
continue ;
p->out[p->cpidx++] = arg - 2 ;
return 0 ;
}
if (!rest) {
if (strstarts(options->long_name, "no-" )) {
/*
* The long name itself starts with " no - " , so
* accept the option without " no - " so that users
* do not have to enter " no - no - " to get the
* negation .
*/
rest = skip_prefix(arg, options->long_name + 3 );
if (rest) {
flags |= OPT_UNSET;
goto match;
}
/* Abbreviated case */
if (strstarts(options->long_name + 3 , arg)) {
flags |= OPT_UNSET;
goto is_abbreviated;
}
}
/* abbreviated? */
if (!strncmp(options->long_name, arg, arg_end - arg)) {
is_abbreviated:
if (abbrev_option) {
/*
* If this is abbreviated , it is
* ambiguous . So when there is no
* exact match later , we need to
* error out .
*/
ambiguous_option = abbrev_option;
ambiguous_flags = abbrev_flags;
}
if (!(flags & OPT_UNSET) && *arg_end)
p->opt = arg_end + 1 ;
abbrev_option = options;
abbrev_flags = flags;
continue ;
}
/* negated and abbreviated very much? */
if (strstarts("no-" , arg)) {
flags |= OPT_UNSET;
goto is_abbreviated;
}
/* negated? */
if (strncmp(arg, "no-" , 3 ))
continue ;
flags |= OPT_UNSET;
rest = skip_prefix(arg + 3 , options->long_name);
/* abbreviated and negated? */
if (!rest && strstarts(options->long_name, arg + 3 ))
goto is_abbreviated;
if (!rest)
continue ;
}
match:
if (*rest) {
if (*rest != '=' )
continue ;
p->opt = rest + 1 ;
}
return get_value(p, options, flags);
}
if (ambiguous_option) {
fprintf(stderr,
" Error: Ambiguous option: %s (could be --%s%s or --%s%s)\n" ,
arg,
(ambiguous_flags & OPT_UNSET) ? "no-" : "" ,
ambiguous_option->long_name,
(abbrev_flags & OPT_UNSET) ? "no-" : "" ,
abbrev_option->long_name);
return -1 ;
}
if (abbrev_option)
return get_value(p, abbrev_option, abbrev_flags);
if (options->parent) {
options = options->parent;
goto retry;
}
return -2 ;
}
static void check_typos(const char *arg, const struct option *options)
{
if (strlen(arg) < 3 )
return ;
if (strstarts(arg, "no-" )) {
fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n" , arg);
exit (129 );
}
for (; options->type != OPTION_END; options++) {
if (!options->long_name)
continue ;
if (strstarts(options->long_name, arg)) {
fprintf(stderr, " Error: did you mean `--%s` (with two dashes ?)\n" , arg);
exit (129 );
}
}
}
static void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, int flags)
{
memset(ctx, 0 , sizeof (*ctx));
ctx->argc = argc - 1 ;
ctx->argv = argv + 1 ;
ctx->out = argv;
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" );
}
static int usage_with_options_internal(const char * const *,
const struct option *, int ,
struct parse_opt_ctx_t *);
static int parse_options_step(struct parse_opt_ctx_t *ctx,
const struct option *options,
const char * const usagestr[])
{
int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
int excl_short_opt = 1 ;
const char *arg;
/* we must reset ->opt, unknown short option leave it dangling */
ctx->opt = NULL;
for (; ctx->argc; ctx->argc--, ctx->argv++) {
arg = ctx->argv[0 ];
if (*arg != '-' || !arg[1 ]) {
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
break ;
ctx->out[ctx->cpidx++] = ctx->argv[0 ];
continue ;
}
if (arg[1 ] != '-' ) {
ctx->opt = ++arg;
if (internal_help && *ctx->opt == 'h' ) {
return usage_with_options_internal(usagestr, options, 0 , ctx);
}
switch (parse_short_opt(ctx, options)) {
case -1 :
return parse_options_usage(usagestr, options, arg, 1 );
case -2 :
goto unknown;
case -3 :
goto exclusive;
default :
break ;
}
if (ctx->opt)
check_typos(arg, options);
while (ctx->opt) {
if (internal_help && *ctx->opt == 'h' )
return usage_with_options_internal(usagestr, options, 0 , ctx);
arg = ctx->opt;
switch (parse_short_opt(ctx, options)) {
case -1 :
return parse_options_usage(usagestr, options, arg, 1 );
case -2 :
/* fake a short option thing to hide the fact that we may have
* started to parse aggregated stuff
*
* This is leaky , too bad .
*/
ctx->argv[0 ] = strdup(ctx->opt - 1 );
*(char *)ctx->argv[0 ] = '-' ;
goto unknown;
case -3 :
goto exclusive;
default :
break ;
}
}
continue ;
}
if (!arg[2 ]) { /* "--" */
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
ctx->argc--;
ctx->argv++;
}
break ;
}
arg += 2 ;
if (internal_help && !strcmp(arg, "help-all" ))
return usage_with_options_internal(usagestr, options, 1 , ctx);
if (internal_help && !strcmp(arg, "help" ))
return usage_with_options_internal(usagestr, options, 0 , ctx);
if (!strcmp(arg, "list-opts" ))
return PARSE_OPT_LIST_OPTS;
if (!strcmp(arg, "list-cmds" ))
return PARSE_OPT_LIST_SUBCMDS;
switch (parse_long_opt(ctx, arg, options)) {
case -1 :
return parse_options_usage(usagestr, options, arg, 0 );
case -2 :
goto unknown;
case -3 :
excl_short_opt = 0 ;
goto exclusive;
default :
break ;
}
continue ;
unknown:
if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN))
return PARSE_OPT_UNKNOWN;
ctx->out[ctx->cpidx++] = ctx->argv[0 ];
ctx->opt = NULL;
}
return PARSE_OPT_DONE;
exclusive:
parse_options_usage(usagestr, options, arg, excl_short_opt);
if ((excl_short_opt && ctx->excl_opt->short_name) ||
ctx->excl_opt->long_name == NULL) {
char opt = ctx->excl_opt->short_name;
parse_options_usage(NULL, options, &opt, 1 );
} else {
parse_options_usage(NULL, options, ctx->excl_opt->long_name, 0 );
}
return PARSE_OPT_HELP;
}
static int parse_options_end(struct parse_opt_ctx_t *ctx)
{
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof (*ctx->out));
ctx->out[ctx->cpidx + ctx->argc] = NULL;
return ctx->cpidx + ctx->argc;
}
int parse_options_subcommand(int argc, const char **argv, const struct option *options,
const char *const subcommands[], const char *usagestr[], int flags)
{
struct parse_opt_ctx_t ctx;
/* build usage string if it's not provided */
if (subcommands && !usagestr[0 ]) {
char *buf = NULL;
astrcatf(&buf, "%s %s [<options>] {" , subcmd_config.exec_name, argv[0 ]);
for (int i = 0 ; subcommands[i]; i++) {
if (i)
astrcat(&buf, "|" );
astrcat(&buf, subcommands[i]);
}
astrcat(&buf, "}" );
usagestr[0 ] = buf;
}
parse_options_start(&ctx, argc, argv, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
exit (129 );
case PARSE_OPT_DONE:
break ;
case PARSE_OPT_LIST_OPTS:
while (options->type != OPTION_END) {
if (options->long_name)
printf("--%s " , options->long_name);
options++;
}
putchar('\n' );
exit (130 );
case PARSE_OPT_LIST_SUBCMDS:
if (subcommands) {
for (int i = 0 ; subcommands[i]; i++)
printf("%s " , subcommands[i]);
}
putchar('\n' );
exit (130 );
default : /* PARSE_OPT_UNKNOWN */
if (ctx.argv[0 ][1 ] == '-' )
astrcatf(&error_buf, "unknown option `%s'" ,
ctx.argv[0 ] + 2 );
else
astrcatf(&error_buf, "unknown switch `%c'" , *ctx.opt);
usage_with_options(usagestr, options);
}
return parse_options_end(&ctx);
}
int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags)
{
return parse_options_subcommand(argc, argv, options, NULL,
(const char **) usagestr, flags);
}
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
static void print_option_help(const struct option *opts, int full)
{
size_t pos;
int pad;
if (opts->type == OPTION_GROUP) {
fputc('\n' , stderr);
if (*opts->help)
fprintf(stderr, "%s\n" , opts->help);
return ;
}
if (!full && (opts->flags & PARSE_OPT_HIDDEN))
return ;
if (opts->flags & PARSE_OPT_DISABLED)
return ;
pos = fprintf(stderr, " " );
if (opts->short_name)
pos += fprintf(stderr, "-%c" , opts->short_name);
else
pos += fprintf(stderr, " " );
if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", " );
if (opts->long_name)
pos += fprintf(stderr, "--%s" , opts->long_name);
switch (opts->type) {
case OPTION_ARGUMENT:
break ;
case OPTION_LONG:
case OPTION_ULONG:
case OPTION_U64:
case OPTION_INTEGER:
case OPTION_UINTEGER:
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,SET_UINT,SET_PTR} */
case OPTION_END:
case OPTION_GROUP:
case OPTION_BIT:
case OPTION_BOOLEAN:
case OPTION_INCR:
case OPTION_SET_UINT:
case OPTION_SET_PTR:
break ;
}
if (pos <= USAGE_OPTS_WIDTH)
pad = USAGE_OPTS_WIDTH - pos;
else {
fputc('\n' , stderr);
pad = USAGE_OPTS_WIDTH;
}
fprintf(stderr, "%*s%s\n" , pad + USAGE_GAP, "" , opts->help);
if (opts->flags & PARSE_OPT_NOBUILD)
fprintf(stderr, "%*s(not built-in because %s)\n" ,
USAGE_OPTS_WIDTH + USAGE_GAP, "" ,
opts->build_opt);
}
static int option__cmp(const void *va, const void *vb)
{
const struct option *a = va, *b = vb;
int sa = tolower(a->short_name), sb = tolower(b->short_name), ret;
if (sa == 0 )
sa = 'z' + 1 ;
if (sb == 0 )
sb = 'z' + 1 ;
ret = sa - sb;
if (ret == 0 ) {
const char *la = a->long_name ?: "" ,
*lb = b->long_name ?: "" ;
ret = strcmp(la, lb);
}
return ret;
}
static struct option *options__order(const struct option *opts)
{
int nr_opts = 0 , nr_group = 0 , nr_parent = 0 , len;
const struct option *o = NULL, *p = opts;
struct option *opt, *ordered = NULL, *group;
/* flatten the options that have parents */
for (p = opts; p != NULL; p = o->parent) {
for (o = p; o->type != OPTION_END; o++)
++nr_opts;
/*
* the length is given by the number of options plus a null
* terminator for the last loop iteration .
*/
len = sizeof (*o) * (nr_opts + !o->parent);
group = realloc(ordered, len);
if (!group)
goto out;
ordered = group;
memcpy(&ordered[nr_parent], p, sizeof (*o) * (nr_opts - nr_parent));
nr_parent = nr_opts;
}
/* copy the last OPTION_END */
memcpy(&ordered[nr_opts], o, sizeof (*o));
/* sort each option group individually */
for (opt = group = ordered; opt->type != OPTION_END; opt++) {
if (opt->type == OPTION_GROUP) {
qsort(group, nr_group, sizeof (*opt), option__cmp);
group = opt + 1 ;
nr_group = 0 ;
continue ;
}
nr_group++;
}
qsort(group, nr_group, sizeof (*opt), option__cmp);
out:
return ordered;
}
static bool option__in_argv(const struct option *opt, const struct parse_opt_ctx_t *ctx)
{
int i;
for (i = 1 ; i < ctx->argc; ++i) {
const char *arg = ctx->argv[i];
if (arg[0 ] != '-' ) {
if (arg[1 ] == '\0' ) {
if (arg[0 ] == opt->short_name)
return true ;
continue ;
}
if (opt->long_name && strcmp(opt->long_name, arg) == 0 )
return true ;
if (opt->help && strcasestr(opt->help, arg) != NULL)
return true ;
continue ;
}
if (arg[1 ] == opt->short_name ||
(arg[1 ] == '-' && opt->long_name && strcmp(opt->long_name, arg + 2 ) == 0 ))
return true ;
}
return false ;
}
static int usage_with_options_internal(const char * const *usagestr,
const struct option *opts, int full,
struct parse_opt_ctx_t *ctx)
{
struct option *ordered;
if (!usagestr)
return PARSE_OPT_HELP;
setup_pager();
if (error_buf) {
fprintf(stderr, " Error: %s\n" , error_buf);
zfree(&error_buf);
}
fprintf(stderr, "\n Usage: %s\n" , *usagestr++);
while (*usagestr && **usagestr)
fprintf(stderr, " or: %s\n" , *usagestr++);
while (*usagestr) {
fprintf(stderr, "%s%s\n" ,
**usagestr ? " " : "" ,
*usagestr);
usagestr++;
}
if (opts->type != OPTION_GROUP)
fputc('\n' , stderr);
ordered = options__order(opts);
if (ordered)
opts = ordered;
for ( ; opts->type != OPTION_END; opts++) {
if (ctx && ctx->argc > 1 && !option__in_argv(opts, ctx))
continue ;
print_option_help(opts, full);
}
fputc('\n' , stderr);
free(ordered);
return PARSE_OPT_HELP;
}
void usage_with_options(const char * const *usagestr,
const struct option *opts)
{
usage_with_options_internal(usagestr, opts, 0 , NULL);
exit (129 );
}
void usage_with_options_msg(const char * const *usagestr,
const struct option *opts, const char *fmt, ...)
{
va_list ap;
char *tmp = error_buf;
va_start(ap, fmt);
if (vasprintf(&error_buf, fmt, ap) == -1 )
die("vasprintf failed" );
va_end(ap);
free(tmp);
usage_with_options_internal(usagestr, opts, 0 , NULL);
exit (129 );
}
int parse_options_usage(const char * const *usagestr,
const struct option *opts,
const char *optstr, bool short_opt)
{
if (!usagestr)
goto opt;
fprintf(stderr, "\n Usage: %s\n" , *usagestr++);
while (*usagestr && **usagestr)
fprintf(stderr, " or: %s\n" , *usagestr++);
while (*usagestr) {
fprintf(stderr, "%s%s\n" ,
**usagestr ? " " : "" ,
*usagestr);
usagestr++;
}
fputc('\n' , stderr);
opt:
for ( ; opts->type != OPTION_END; opts++) {
if (short_opt) {
if (opts->short_name == *optstr) {
print_option_help(opts, 0 );
break ;
}
continue ;
}
if (opts->long_name == NULL)
continue ;
if (strstarts(opts->long_name, optstr))
print_option_help(opts, 0 );
if (strstarts("no-" , optstr) &&
strstarts(opts->long_name, optstr + 3 ))
print_option_help(opts, 0 );
}
return PARSE_OPT_HELP;
}
int parse_opt_verbosity_cb(const struct option *opt,
const char *arg __maybe_unused,
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 ;
}
static struct option *
find_option(struct option *opts, int shortopt, const char *longopt)
{
for (; opts->type != OPTION_END; opts++) {
if ((shortopt && opts->short_name == shortopt) ||
(opts->long_name && longopt &&
!strcmp(opts->long_name, longopt)))
return opts;
}
return NULL;
}
void set_option_flag(struct option *opts, int shortopt, const char *longopt,
int flag)
{
struct option *opt = find_option(opts, shortopt, longopt);
if (opt)
opt->flags |= flag;
return ;
}
void set_option_nobuild(struct option *opts, int shortopt,
const char *longopt,
const char *build_opt,
bool can_skip)
{
struct option *opt = find_option(opts, shortopt, longopt);
if (!opt)
return ;
opt->flags |= PARSE_OPT_NOBUILD;
opt->flags |= can_skip ? PARSE_OPT_CANSKIP : 0 ;
opt->build_opt = build_opt;
}
Messung V0.5 in Prozent C=98 H=92 G=94
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland