Refactor the option & verb table handling and convert a few more programs (#41335)

After having some more experience with how this works, I think some
changes are in order. So there are a handful of preparatory patches and
then conversion of a few progs that make use of the new functionality.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2026-03-31 10:17:26 +02:00
committed by GitHub
21 changed files with 330 additions and 350 deletions
+3 -3
View File
@@ -48,10 +48,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -68,7 +68,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
if (option_parser_get_n_args(&state, argc, argv) > 0)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
+3 -3
View File
@@ -71,10 +71,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -170,7 +170,7 @@ static int parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, ASK_PASSWORD_HIDE_EMOJI, !r);
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if (!strv_isempty(args)) {
arg_message = strv_join(args, " ");
+3 -3
View File
@@ -137,10 +137,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -165,7 +165,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if ((arg_unregister || arg_cat_flags != CAT_CONFIG_OFF) && !strv_isempty(args))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+4 -4
View File
@@ -50,7 +50,7 @@ static int help(void) {
if (r < 0)
return r;
(void) table_sync_column_width(options, 0, verbs, 0);
(void) table_sync_column_widths(0, options, verbs);
printf("%s [OPTIONS...] COMMAND\n"
"\n%sMark the boot process as good or bad.%s\n"
@@ -75,10 +75,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -93,7 +93,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
*ret_args = option_parser_get_args(&state, argc, argv);
*ret_args = option_parser_get_args(&state);
return 1;
}
+30 -62
View File
@@ -1,12 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "alloc-util.h"
#include "ansi-color.h"
#include "build.h"
#include "confidential-virt.h"
#include "format-table.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "pretty-print.h"
#include "string-table.h"
#include "virt.h"
@@ -23,109 +24,76 @@ static enum {
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL;
int r;
r = terminal_urlify_man("systemd-detect-virt", "1", &link);
if (r < 0)
return log_oom();
printf("%s [OPTIONS...]\n\n"
"Detect execution in a virtualized environment.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -c --container Only detect whether we are run in a container\n"
" -v --vm Only detect whether we are run in a VM\n"
" -r --chroot Detect whether we are run in a chroot() environment\n"
" --private-users Only detect whether we are running in a user namespace\n"
" --cvm Only detect whether we are run in a confidential VM\n"
" -q --quiet Don't output anything, just set return value\n"
" --list List all known and detectable types of virtualization\n"
" --list-cvm List all known and detectable types of confidential \n"
" virtualization\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
link);
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
printf("%s [OPTIONS...]\n"
"\n%sDetect execution in a virtualized environment.%s\n"
"\nOptions:\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal());
table_print(options, stdout);
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_PRIVATE_USERS,
ARG_LIST,
ARG_CVM,
ARG_LIST_CVM,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "container", no_argument, NULL, 'c' },
{ "vm", no_argument, NULL, 'v' },
{ "chroot", no_argument, NULL, 'r' },
{ "private-users", no_argument, NULL, ARG_PRIVATE_USERS },
{ "quiet", no_argument, NULL, 'q' },
{ "cvm", no_argument, NULL, ARG_CVM },
{ "list", no_argument, NULL, ARG_LIST },
{ "list-cvm", no_argument, NULL, ARG_LIST_CVM },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hqcvr", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case 'q':
OPTION('q', "quiet", NULL, "Don't output anything, just set return value"):
arg_quiet = true;
break;
case 'c':
OPTION('c', "container", NULL, "Only detect whether we are run in a container"):
arg_mode = ONLY_CONTAINER;
break;
case ARG_PRIVATE_USERS:
OPTION_LONG("private-users", NULL, "Only detect whether we are running in a user namespace"):
arg_mode = ONLY_PRIVATE_USERS;
break;
case 'v':
OPTION('v', "vm", NULL, "Only detect whether we are run in a VM"):
arg_mode = ONLY_VM;
break;
case 'r':
OPTION('r', "chroot", NULL, "Detect whether we are run in a chroot() environment"):
arg_mode = ONLY_CHROOT;
break;
case ARG_LIST:
OPTION_LONG("list", NULL, "List all known and detectable types of virtualization"):
return DUMP_STRING_TABLE(virtualization, Virtualization, _VIRTUALIZATION_MAX);
case ARG_CVM:
OPTION_LONG("cvm", NULL, "Only detect whether we are run in a confidential VM"):
arg_mode = ONLY_CVM;
return 1;
case ARG_LIST_CVM:
OPTION_LONG("list-cvm", NULL, "List all known and detectable types of confidential virtualization"):
return DUMP_STRING_TABLE(confidential_virtualization, ConfidentialVirtualization, _CONFIDENTIAL_VIRTUALIZATION_MAX);
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind < argc)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s takes no arguments.",
program_invocation_short_name);
+4 -4
View File
@@ -141,7 +141,7 @@ static int help(void) {
return r;
/* Make the 1st column same width in both tables */
(void) table_sync_column_width(options, 0, commands, 0);
(void) table_sync_column_widths(0, options, commands);
printf("%1$s [OPTIONS...] IMAGE\n"
"%1$s [OPTIONS...] --mount IMAGE PATH\n"
@@ -207,11 +207,11 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *current;
const char *arg;
FOREACH_OPTION_FULL(&state, c, argc, argv, &current, &arg, /* on_error= */ return c)
FOREACH_OPTION_FULL(&state, c, &current, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_NO_PAGER:
@@ -495,7 +495,7 @@ static int parse_argv(int argc, char *argv[]) {
arg_runtime_scope = system_scope_requested && user_scope_requested ? _RUNTIME_SCOPE_INVALID :
system_scope_requested ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
switch (arg_action) {
+39 -55
View File
@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "sd-json.h"
#include "sd-varlink.h"
@@ -12,9 +10,11 @@
#include "efivars.h"
#include "errno-util.h"
#include "factory-reset.h"
#include "format-table.h"
#include "fs-util.h"
#include "json-util.h"
#include "main-func.h"
#include "options.h"
#include "os-util.h"
#include "pretty-print.h"
#include "udev-util.h"
@@ -28,76 +28,62 @@ static bool arg_varlink = false;
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = terminal_urlify_man("systemd-factory-reset", "8", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND\n"
"\n%5$sQuery, request, cancel factory reset operation.%6$s\n"
"\n%3$sCommands:%4$s\n"
" status Report current factory reset status\n"
" request Request a factory reset on next boot\n"
" cancel Cancel a prior factory reset request for next boot\n"
" complete Mark a factory reset as complete\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
" --retrigger Retrigger block devices\n"
" -q --quiet Suppress output\n"
"\nSee the %2$s for details.\n",
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
(void) table_sync_column_widths(0, options, verbs);
printf("%s [OPTIONS...] COMMAND\n"
"\n%sQuery, request, cancel factory reset operation.%s\n"
"\nCommands:\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
table_print(verbs, stdout);
printf("\nOptions:\n");
table_print(options, stdout);
printf("\nSee the %s for details.\n", link);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_RETRIGGER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "retrigger", no_argument, NULL, ARG_RETRIGGER },
{ "quiet", no_argument, NULL, 'q' },
{}
};
int r, c;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hq", options, NULL)) >= 0)
switch (c) {
OptionParser state = { argc, argv };
const char *arg;
case 'h':
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case ARG_RETRIGGER:
OPTION_LONG("retrigger", NULL, "Retrigger block devices"):
arg_retrigger = true;
break;
case 'q':
OPTION('q', "quiet", NULL, "Suppress output"):
arg_quiet = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
@@ -106,9 +92,11 @@ static int parse_argv(int argc, char *argv[]) {
if (r > 0)
arg_varlink = true;
*ret_args = option_parser_get_args(&state);
return 1;
}
VERB(verb_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Report current factory reset status");
static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
static const int exit_status_table[_FACTORY_RESET_MODE_MAX] = {
/* Report current mode also as via exit status, but only return a subset of states */
@@ -130,6 +118,7 @@ static int verb_status(int argc, char *argv[], uintptr_t _data, void *userdata)
return exit_status_table[f];
}
VERB_NOARG(verb_request, "request", "Request a factory reset on next boot");
static int verb_request(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
@@ -197,6 +186,7 @@ static int verb_request(int argc, char *argv[], uintptr_t _data, void *userdata)
return 0;
}
VERB_NOARG(verb_cancel, "cancel", "Cancel a prior factory reset request for next boot");
static int verb_cancel(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
@@ -269,6 +259,7 @@ static int retrigger_block_devices(void) {
return 0;
}
VERB_NOARG(verb_complete, "complete", "Mark a factory reset as complete");
static int verb_complete(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
@@ -359,26 +350,19 @@ static int varlink_service(void) {
}
static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "request", VERB_ANY, 1, 0, verb_request },
{ "cancel", VERB_ANY, 1, 0, verb_cancel },
{ "complete", VERB_ANY, 1, 0, verb_complete },
{}
};
int r;
log_setup();
r = parse_argv(argc, argv);
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
if (arg_varlink)
return varlink_service();
return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL);
return dispatch_verb_with_args(args, /* userdata= */ NULL);
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
-1
View File
@@ -143,7 +143,6 @@
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
#define CONCATENATE3(x, y, z) CONCATENATE(x, CONCATENATE(y, z))
#define assert_cc(expr) _Static_assert(expr, #expr)
+60 -105
View File
@@ -1,6 +1,5 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <linux/vm_sockets.h>
#include <locale.h>
#include <string.h>
@@ -23,6 +22,7 @@
#include "hostname-util.h"
#include "log.h"
#include "main-func.h"
#include "options.h"
#include "parse-argument.h"
#include "polkit-agent.h"
#include "pretty-print.h"
@@ -549,6 +549,7 @@ static int get_hostname_based_on_flag(sd_bus *bus) {
return get_one_name(bus, attr, NULL);
}
VERB(verb_show_status, "status", NULL, VERB_ANY, 1, VERB_DEFAULT, "Show current hostname settings");
static int verb_show_status(int argc, char *argv[], uintptr_t _data, void *userdata) {
sd_bus *bus = userdata;
int r;
@@ -687,26 +688,36 @@ static int verb_set_hostname(int argc, char *argv[], uintptr_t _data, void *user
return ret;
}
VERB(verb_get_or_set_hostname, "hostname", "[NAME]", VERB_ANY, 2, 0, "Get/set system hostname");
VERB(verb_get_or_set_hostname, "set-hostname", "NAME", 2, 2, 0, NULL); /* obsolete */
static int verb_get_or_set_hostname(int argc, char *argv[], uintptr_t data, void *userdata) {
return argc == 1 ? get_hostname_based_on_flag(userdata) :
verb_set_hostname(argc, argv, data, userdata);
}
VERB(verb_get_or_set_icon_name, "icon-name", "[NAME]", VERB_ANY, 2, 0, "Get/set icon name for host");
VERB(verb_get_or_set_icon_name, "set-icon-name", "NAME", 2, 2, 0, NULL); /* obsolete */
static int verb_get_or_set_icon_name(int argc, char *argv[], uintptr_t _data, void *userdata) {
return argc == 1 ? get_one_name(userdata, "IconName", NULL) :
set_simple_string(userdata, "icon", "SetIconName", argv[1]);
}
VERB(verb_get_or_set_chassis, "chassis", "[NAME]", VERB_ANY, 2, 0, "Get/set chassis type for host");
VERB(verb_get_or_set_chassis, "set-chassis", "NAME", 2, 2, 0, NULL); /* obsolete */
static int verb_get_or_set_chassis(int argc, char *argv[], uintptr_t _data, void *userdata) {
return argc == 1 ? get_one_name(userdata, "Chassis", NULL) :
set_simple_string(userdata, "chassis", "SetChassis", argv[1]);
}
VERB(verb_get_or_set_deployment, "deployment", "[NAME]", VERB_ANY, 2, 0, "Get/set deployment environment for host");
VERB(verb_get_or_set_deployment, "set-deployment", "NAME", 2, 2, 0, NULL); /* obsolete */
static int verb_get_or_set_deployment(int argc, char *argv[], uintptr_t _data, void *userdata) {
return argc == 1 ? get_one_name(userdata, "Deployment", NULL) :
set_simple_string(userdata, "deployment", "SetDeployment", argv[1]);
}
VERB(verb_get_or_set_location, "location", "[NAME]", VERB_ANY, 2, 0, "Get/set location for host");
VERB(verb_get_or_set_location, "set-location", "NAME", 2, 2, 0, NULL); /* obsolete */
static int verb_get_or_set_location(int argc, char *argv[], uintptr_t _data, void *userdata) {
return argc == 1 ? get_one_name(userdata, "Location", NULL) :
set_simple_string(userdata, "location", "SetLocation", argv[1]);
@@ -714,164 +725,108 @@ static int verb_get_or_set_location(int argc, char *argv[], uintptr_t _data, voi
static int help(void) {
_cleanup_free_ char *link = NULL;
_cleanup_(table_unrefp) Table *options = NULL, *verbs = NULL;
int r;
r = terminal_urlify_man("hostnamectl", "1", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND ...\n\n"
"%2$sQuery or change system hostname.%3$s\n"
"\n%4$sCommands:%5$s\n"
" status Show current hostname settings\n"
" hostname [NAME] Get/set system hostname\n"
" icon-name [NAME] Get/set icon name for host\n"
" chassis [NAME] Get/set chassis type for host\n"
" deployment [NAME] Get/set deployment environment for host\n"
" location [NAME] Get/set location for host\n"
"\n%4$sOptions:%5$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-ask-password Do not prompt for password\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" --transient Only set transient hostname\n"
" --static Only set static hostname\n"
" --pretty Only set pretty hostname\n"
" --json=pretty|short|off\n"
" Generate JSON output\n"
" -j Same as --json=pretty on tty, --json=short otherwise\n"
"\nSee the %6$s for details.\n",
r = option_parser_get_help_table(&options);
if (r < 0)
return r;
r = verbs_get_help_table(&verbs);
if (r < 0)
return r;
(void) table_sync_column_widths(0, options, verbs);
printf("%s [OPTIONS...] COMMAND ...\n"
"\n%sQuery or change system hostname.%s\n"
"\nCommands:\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
ansi_underline(),
ansi_normal(),
link);
ansi_normal());
table_print(verbs, stdout);
printf("\nOptions:\n");
table_print(options, stdout);
printf("\nSee the %s for details.\n", link);
return 0;
}
static int verb_help(int argc, char *argv[], uintptr_t _data, void *userdata) {
return help();
}
VERB_COMMON_HELP_HIDDEN(help);
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_NO_ASK_PASSWORD,
ARG_TRANSIENT,
ARG_STATIC,
ARG_PRETTY,
ARG_JSON,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "transient", no_argument, NULL, ARG_TRANSIENT },
{ "static", no_argument, NULL, ARG_STATIC },
{ "pretty", no_argument, NULL, ARG_PRETTY },
{ "host", required_argument, NULL, 'H' },
{ "machine", required_argument, NULL, 'M' },
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
{ "json", required_argument, NULL, ARG_JSON },
{}
};
int c, r;
static int parse_argv(int argc, char *argv[], char ***ret_args) {
int r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hH:M:j", options, NULL)) >= 0)
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
case 'h':
OPTION_COMMON_HELP:
return help();
case ARG_VERSION:
OPTION_COMMON_VERSION:
return version();
case 'H':
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = optarg;
OPTION_COMMON_NO_ASK_PASSWORD:
arg_ask_password = false;
break;
case 'M':
r = parse_machine_argument(optarg, &arg_host, &arg_transport);
OPTION_COMMON_HOST:
arg_transport = BUS_TRANSPORT_REMOTE;
arg_host = arg;
break;
OPTION_COMMON_MACHINE:
r = parse_machine_argument(arg, &arg_host, &arg_transport);
if (r < 0)
return r;
break;
case ARG_TRANSIENT:
OPTION_LONG("transient", NULL, "Only set transient hostname"):
arg_transient = true;
break;
case ARG_PRETTY:
arg_pretty = true;
break;
case ARG_STATIC:
OPTION_LONG("static", NULL, "Only set static hostname"):
arg_static = true;
break;
case ARG_NO_ASK_PASSWORD:
arg_ask_password = false;
OPTION_LONG("pretty", NULL, "Only set pretty hostname"):
arg_pretty = true;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
OPTION_COMMON_JSON:
r = parse_json_argument(arg, &arg_json_format_flags);
if (r <= 0)
return r;
break;
case 'j':
OPTION_COMMON_LOWERCASE_J:
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
*ret_args = option_parser_get_args(&state);
return 1;
}
static int hostnamectl_main(sd_bus *bus, int argc, char *argv[]) {
static const Verb verbs[] = {
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_show_status },
{ "hostname", VERB_ANY, 2, 0, verb_get_or_set_hostname },
{ "set-hostname", 2, 2, 0, verb_get_or_set_hostname }, /* obsolete */
{ "icon-name", VERB_ANY, 2, 0, verb_get_or_set_icon_name },
{ "set-icon-name", 2, 2, 0, verb_get_or_set_icon_name }, /* obsolete */
{ "chassis", VERB_ANY, 2, 0, verb_get_or_set_chassis },
{ "set-chassis", 2, 2, 0, verb_get_or_set_chassis }, /* obsolete */
{ "deployment", VERB_ANY, 2, 0, verb_get_or_set_deployment },
{ "set-deployment", 2, 2, 0, verb_get_or_set_deployment }, /* obsolete */
{ "location", VERB_ANY, 2, 0, verb_get_or_set_location },
{ "set-location", 2, 2, 0, verb_get_or_set_location }, /* obsolete */
{ "help", VERB_ANY, VERB_ANY, 0, verb_help }, /* Not documented, but supported since it is created. */
{}
};
return dispatch_verb(argc, argv, verbs, bus);
}
static int run(int argc, char *argv[]) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
char **args = NULL;
int r;
setlocale(LC_ALL, "");
log_setup();
r = parse_argv(argc, argv);
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
@@ -879,7 +834,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return bus_log_connect_error(r, arg_transport, RUNTIME_SCOPE_SYSTEM);
return hostnamectl_main(bus, argc, argv);
return dispatch_verb_with_args(args, bus);
}
DEFINE_MAIN_FUNCTION(run);
+5 -6
View File
@@ -208,7 +208,7 @@ static int help(void) {
return r;
/* Make the 1st column same width in both tables */
(void) table_sync_column_width(options, 0, verbs, 0);
(void) table_sync_column_widths(0, options, verbs);
printf("%s [OPTIONS...] COMMAND\n\n"
"%sGenerate and print 128-bit identifiers.%s\n"
@@ -233,10 +233,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -259,8 +259,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
return r;
break;
OPTION_SHORT('j', NULL,
"Equivalent to --json=pretty (on TTY) or --json=short (otherwise)"):
OPTION_COMMON_LOWERCASE_J:
arg_json_format_flags = SD_JSON_FORMAT_PRETTY_AUTO|SD_JSON_FORMAT_COLOR_AUTO;
break;
@@ -288,7 +287,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
break;
}
*ret_args = option_parser_get_args(&state, argc, argv);
*ret_args = option_parser_get_args(&state);
return 1;
}
+3 -3
View File
@@ -158,10 +158,10 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
@@ -283,7 +283,7 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
bool have_env = arg_ready || arg_stopping || arg_reloading || arg_status || pidref_is_set(&arg_pid) || !fdset_isempty(arg_fds);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
switch (arg_action) {
+20 -11
View File
@@ -2185,26 +2185,35 @@ int table_set_column_width(Table *t, size_t column, size_t width) {
return r;
}
int table_sync_column_width(Table *a, size_t column_a, Table *b, size_t column_b) {
size_t w1, w2;
int r;
int _table_sync_column_widths(size_t column, Table *a, ...) {
size_t max = 0;
va_list ap;
int r = 0;
assert(a);
assert(b);
/* Make both tables have specified columns of same width */
/* Make the specified column have the same width in the tables. */
r = table_data_requested_width(a, column_a, &w1);
if (r < 0)
return log_error_errno(r, "Failed to query table column width: %m");
va_start(ap, a);
for (Table *t = a; t; t = va_arg(ap, Table*)) {
size_t w;
r = table_data_requested_width(b, column_b, &w2);
r = table_data_requested_width(t, column, &w);
if (r < 0)
break;
max = MAX(max, w);
}
va_end(ap);
if (r < 0)
return log_error_errno(r, "Failed to query table column width: %m");
r = 0;
RET_GATHER(r, table_set_column_width(a, column_a, MAX(w1, w2)));
RET_GATHER(r, table_set_column_width(b, column_b, MAX(w1, w2)));
va_start(ap, a);
for (Table *t = a; t; t = va_arg(ap, Table*))
RET_GATHER(r, table_set_column_width(t, column, max));
va_end(ap);
return r;
}
+2 -1
View File
@@ -144,7 +144,8 @@ int table_hide_column_from_display_internal(Table *t, ...);
int table_data_requested_width(Table *table, size_t column, size_t *ret);
int table_set_column_width(Table *t, size_t column, size_t width);
int table_sync_column_width(Table *a, size_t column_a, Table *b, size_t column_b);
int _table_sync_column_widths(size_t column, Table *a, ...);
#define table_sync_column_widths(column, a, ...) _table_sync_column_widths(column, a, __VA_ARGS__, NULL)
int table_print(Table *t, FILE *f);
int table_format(Table *t, char **ret);
+38 -36
View File
@@ -25,14 +25,6 @@ static bool option_is_metadata(const Option *opt) {
FLAGS_SET(ASSERT_PTR(opt)->flags, OPTION_HELP_ENTRY);
}
static void kill_arg(char* argv[], int argc, int index) {
assert(index < argc);
assert(!argv[argc]);
/* Eliminate argv[index] */
memmove(argv + index, argv + index + 1, (argc - index) * sizeof(char*));
}
static void shift_arg(char* argv[], int target, int source) {
assert(argv);
assert(target <= source);
@@ -77,7 +69,6 @@ int option_parse(
const Option options[],
const Option options_end[],
OptionParser *state,
int argc, char *argv[],
const Option **ret_option,
const char **ret_arg) {
@@ -85,13 +76,10 @@ int option_parse(
/* Check and initialize */
if (state->optind == 0) {
if (argc < 1 || strv_isempty(argv))
if (state->argc < 1 || strv_isempty(state->argv))
return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "argv cannot be empty");
*state = (OptionParser) {
.optind = 1,
.positional_offset = 1,
};
state->optind = state->positional_offset = 1;
}
/* Look for the next option */
@@ -104,20 +92,21 @@ int option_parse(
if (state->short_option_offset == 0) {
/* Skip over non-option parameters */
for (;;) {
if (state->optind == argc)
if (state->optind == state->argc)
return 0;
if (streq(argv[state->optind], "--")) {
/* No more options. Eliminate "--" so that the list of positional args is clean. */
kill_arg(argv, argc, state->optind);
return 0;
if (streq(state->argv[state->optind], "--")) {
/* No more options. Move "--" before positional args so that
* the list of positional args is clean. */
shift_arg(state->argv, state->positional_offset++, state->optind++);
state->parsing_stopped = true;
}
if (state->parsing_stopped)
return 0;
if (argv[state->optind][0] == '-' &&
argv[state->optind][1] != '\0')
if (state->argv[state->optind][0] == '-' &&
state->argv[state->optind][1] != '\0')
/* Looks like we found an option parameter */
break;
@@ -126,13 +115,13 @@ int option_parse(
/* Find matching option entry.
* First, figure out if we have a long option or a short option. */
assert(argv[state->optind][0] == '-');
assert(state->argv[state->optind][0] == '-');
if (argv[state->optind][1] == '-') {
if (state->argv[state->optind][1] == '-') {
/* We have a long option. */
char *eq = strchr(argv[state->optind], '=');
char *eq = strchr(state->argv[state->optind], '=');
if (eq) {
optname = _optname = strndup(argv[state->optind], eq - argv[state->optind]);
optname = _optname = strndup(state->argv[state->optind], eq - state->argv[state->optind]);
if (!_optname)
return log_oom();
@@ -140,7 +129,7 @@ int option_parse(
optval = eq + 1;
} else
/* argument (if any) is separate */
optname = argv[state->optind];
optname = state->argv[state->optind];
const Option *last_partial = NULL;
unsigned n_partial_matches = 0; /* The commandline option matches a defined prefix. */
@@ -179,7 +168,7 @@ int option_parse(
}
if (state->short_option_offset > 0) {
char optchar = argv[state->optind][state->short_option_offset];
char optchar = state->argv[state->optind][state->short_option_offset];
if (asprintf(&_optname, "-%c", optchar) < 0)
return log_oom();
@@ -194,7 +183,7 @@ int option_parse(
if (option_is_metadata(option) || optchar != option->short_code)
continue;
const char *rest = argv[state->optind] + state->short_option_offset + 1;
const char *rest = state->argv[state->optind] + state->short_option_offset + 1;
if (option_takes_arg(option) && !isempty(rest)) {
/* The rest of this parameter is the value. */
@@ -216,19 +205,19 @@ int option_parse(
"%s: option '%s' doesn't allow an argument",
program_invocation_short_name, optname);
if (!optval && option_arg_required(option)) {
if (!argv[state->optind + 1])
if (!state->argv[state->optind + 1])
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: option '%s' requires an argument",
program_invocation_short_name, optname);
optval = argv[state->optind + 1];
optval = state->argv[state->optind + 1];
separate_optval = true;
}
if (state->short_option_offset == 0) {
/* We're done with this option. Adjust the array and position. */
shift_arg(argv, state->positional_offset++, state->optind++);
shift_arg(state->argv, state->positional_offset++, state->optind++);
if (separate_optval)
shift_arg(argv, state->positional_offset++, state->optind++);
shift_arg(state->argv, state->positional_offset++, state->optind++);
}
if (FLAGS_SET(option->flags, OPTION_STOPS_PARSING))
@@ -241,12 +230,25 @@ int option_parse(
return option->id;
}
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]) {
char** option_parser_get_args(const OptionParser *state) {
/* Returns positional args as a strv.
* If "--" was found, it has been removed. */
* If "--" was found, it has been moved before state->positional_offset.
* The array is only valid, i.e. clean without any options, after parsing
* has naturally finished. */
assert(state->optind > 0);
return argv + state->positional_offset;
assert(state->optind == state->argc || state->parsing_stopped);
assert(state->positional_offset <= state->argc);
return state->argv + state->positional_offset;
}
size_t option_parser_get_n_args(const OptionParser *state) {
assert(state->optind > 0);
assert(state->optind == state->argc || state->parsing_stopped);
assert(state->positional_offset <= state->argc);
return state->argc - state->positional_offset;
}
int _option_parser_get_help_table(
@@ -264,7 +266,7 @@ int _option_parser_get_help_table(
bool in_group = group == NULL; /* Are we currently in the section on the array that forms
* group <group>? The first part is the default group, so
* the group was not specified, we are in. */
* if the group was not specified, we are in. */
for (const Option *opt = options; opt < options_end; opt++) {
bool group_marker = FLAGS_SET(opt->flags, OPTION_GROUP_MARKER);
+19 -10
View File
@@ -2,7 +2,6 @@
#pragma once
#include "shared-forward.h"
#include "strv.h"
typedef enum OptionFlags {
OPTION_OPTIONAL_ARG = 1U << 0, /* Same as optional_argument in getopt */
@@ -61,14 +60,27 @@ typedef struct Option {
OPTION_LONG("cat-config", NULL, "Show configuration files")
#define OPTION_COMMON_TLDR \
OPTION_LONG("tldr", NULL, "Show non-comment parts of configuration")
#define OPTION_COMMON_NO_ASK_PASSWORD \
OPTION_LONG("no-ask-password", NULL, "Do not prompt for password")
#define OPTION_COMMON_HOST \
OPTION('H', "host", "[USER@]HOST", "Operate on remote host")
#define OPTION_COMMON_MACHINE \
OPTION('M', "machine", "CONTAINER", "Operate on local container")
#define OPTION_COMMON_JSON \
OPTION_LONG("json", "FORMAT", "Generate JSON output (pretty, short, or off)")
#define OPTION_COMMON_LOWERCASE_J \
OPTION_SHORT('j', NULL, \
"Equivalent to --json=pretty (on TTY) or --json=short (otherwise)")
/* This is magically mapped to the beginning and end of the section */
extern const Option __start_SYSTEMD_OPTIONS[];
extern const Option __stop_SYSTEMD_OPTIONS[];
typedef struct OptionParser {
/* Those two should stay first so that it's possible to initialize the struct as { argc, argv }. */
int argc; /* The original argc. */
char **argv; /* The argv array, possibly reordered. */
int optind; /* Position of the parameter being handled.
* 0 → option parsing hasn't been started yet. */
int short_option_offset; /* Set when we're parsing an argument with one or more short options.
@@ -82,25 +94,22 @@ int option_parse(
const Option options[],
const Option options_end[],
OptionParser *state,
int argc, char *argv[],
const Option **ret_option,
const char **ret_arg);
/* Iterate over options. */
#define FOREACH_OPTION_FULL(parser, opt, argc, argv, ret_o, ret_a, on_error) \
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, argc, argv, ret_o, ret_a)) != 0; ) \
#define FOREACH_OPTION_FULL(parser, opt, ret_o, ret_a, on_error) \
for (int opt; (opt = option_parse(ALIGN_PTR(__start_SYSTEMD_OPTIONS), __stop_SYSTEMD_OPTIONS, parser, ret_o, ret_a)) != 0; ) \
if (opt < 0) { \
on_error; \
break; \
} else
#define FOREACH_OPTION(parser, opt, argc, argv, ret_a, on_error) \
FOREACH_OPTION_FULL(parser, opt, argc, argv, /* ret_o= */ NULL, ret_a, on_error)
#define FOREACH_OPTION(parser, opt, ret_a, on_error) \
FOREACH_OPTION_FULL(parser, opt, /* ret_o= */ NULL, ret_a, on_error)
char** option_parser_get_args(const OptionParser *state, int argc, char *argv[]);
static inline size_t option_parser_get_n_args(const OptionParser *state, int argc, char *argv[]) {
return strv_length(option_parser_get_args(state, argc, argv));
}
char** option_parser_get_args(const OptionParser *state);
size_t option_parser_get_n_args(const OptionParser *state);
int _option_parser_get_help_table(
const Option options[],
+33 -4
View File
@@ -58,12 +58,21 @@ bool should_bypass(const char *env_prefix) {
return true;
}
static bool verb_is_metadata(const Verb *verb) {
/* A metadata entry that is not a real verb, like the group marker */
return FLAGS_SET(ASSERT_PTR(verb)->flags, VERB_GROUP_MARKER);
}
const Verb* verbs_find_verb(const char *name, const Verb verbs[], const Verb verbs_end[]) {
assert(verbs);
for (const Verb *verb = verbs; verb < verbs_end; verb++)
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
if (verb_is_metadata(verb))
continue;
if (name ? streq(name, verb->verb) : FLAGS_SET(verb->flags, VERB_DEFAULT))
return verb;
}
/* At the end of the list? */
return NULL;
@@ -85,6 +94,9 @@ int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_e
_cleanup_strv_free_ char **verb_strv = NULL;
for (verb = verbs; verb < verbs_end; verb++) {
if (verb_is_metadata(verb))
continue;
r = strv_extend(&verb_strv, verb->verb);
if (r < 0)
return log_oom();
@@ -142,13 +154,17 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
assert(argc >= optind);
size_t n = 0;
while (verbs[n].dispatch)
while (verbs[n].verb)
n++;
return _dispatch_verb_with_args(strv_skip(argv, optind), verbs, verbs + n, userdata);
}
int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret) {
int _verbs_get_help_table(
const Verb verbs[],
const Verb verbs_end[],
const char *group,
Table **ret) {
int r;
assert(ret);
@@ -157,10 +173,23 @@ int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **re
if (!table)
return log_oom();
bool in_group = group == NULL; /* Are we currently in the section on the array that forms
* group <group>? The first part is the default group, so
* if the group was not specified, we are in. */
for (const Verb *verb = verbs; verb < verbs_end; verb++) {
assert(verb->dispatch);
assert(verb->verb);
bool group_marker = FLAGS_SET(verb->flags, VERB_GROUP_MARKER);
if (!in_group) {
in_group = group_marker && streq(group, verb->verb);
continue;
}
if (group_marker)
break; /* End of group */
if (!verb->help)
/* No help string — we do not show the verb */
continue;
/* We indent the option string by two spaces. We could set the minimum cell width and
+23 -8
View File
@@ -8,6 +8,7 @@
typedef enum VerbFlags {
VERB_DEFAULT = 1 << 0, /* The verb to run if no verb is specified */
VERB_ONLINE_ONLY = 1 << 1, /* Just do nothing when running in chroot or offline */
VERB_GROUP_MARKER = 1 << 2, /* Fake verb entry to separate groups */
} VerbFlags;
typedef struct {
@@ -20,16 +21,13 @@ typedef struct {
const char *help;
} Verb;
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
DISABLE_WARNING_REDUNDANT_DECLS \
static int d(int, char**, uintptr_t, void*); \
REENABLE_WARNING \
#define _VERB_DATA(d, v, a, amin, amax, f, dat, h) \
_section_("SYSTEMD_VERBS") \
_alignptr_ \
_used_ \
_retain_ \
_variable_no_sanitize_address_ \
static const Verb CONCATENATE3(d, _data_, __COUNTER__) = { \
static const Verb CONCATENATE(verb_data_, __COUNTER__) = { \
.verb = v, \
.min_args = amin, \
.max_args = amax, \
@@ -40,6 +38,12 @@ typedef struct {
.help = h, \
}
#define VERB_FULL(d, v, a, amin, amax, f, dat, h) \
DISABLE_WARNING_REDUNDANT_DECLS \
static int d(int, char**, uintptr_t, void*); \
REENABLE_WARNING \
_VERB_DATA(d, v, a, amin, amax, f, dat, h)
/* The same as VERB_FULL, but without the data argument */
#define VERB(d, v, a, amin, amax, f, h) \
VERB_FULL(d, v, a, amin, amax, f, /* dat= */ 0, h)
@@ -48,6 +52,11 @@ typedef struct {
#define VERB_NOARG(d, v, h) \
VERB(d, v, /* a= */ NULL, /* amin= */ VERB_ANY, /* amax= */ 1, /* f= */ 0, h)
/* Magic entry in the table (which will not be returned) that designates the start of the group <gr>. */
#define VERB_GROUP(gr) \
_VERB_DATA(/* d= */ NULL, /* v= */ gr, /* a= */ NULL, /* amin= */ 0, /* amax= */ 0, \
/* f= */ VERB_GROUP_MARKER, /* dat= */ 0, /* h= */ NULL)
/* This is magically mapped to the beginning and end of the section */
extern const Verb __start_SYSTEMD_VERBS[];
extern const Verb __stop_SYSTEMD_VERBS[];
@@ -64,9 +73,15 @@ int _dispatch_verb_with_args(char **args, const Verb verbs[], const Verb verbs_e
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
int _verbs_get_help_table(const Verb verbs[], const Verb verbs_end[], Table **ret);
#define verbs_get_help_table(ret) \
_verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, ret)
int _verbs_get_help_table(
const Verb verbs[],
const Verb verbs_end[],
const char *group,
Table **ret);
#define verbs_get_help_table_group(group, ret) \
_verbs_get_help_table(ALIGN_PTR(__start_SYSTEMD_VERBS), __stop_SYSTEMD_VERBS, group, ret)
#define verbs_get_help_table(ret) \
verbs_get_help_table_group(/* group= */ NULL, ret)
#define _VERB_COMMON_HELP_IMPL(impl) \
static int verb_help(int argc, char **argv, uintptr_t data, void *userdata) { \
+9 -9
View File
@@ -31,10 +31,10 @@ static void test_option_parse_one(
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
n_entries++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
for (int c; (c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg)) != 0; ) {
for (int c; (c = option_parse(options, options + n_options, &state, &opt, &arg)) != 0; ) {
ASSERT_OK(c);
ASSERT_NOT_NULL(opt);
@@ -54,11 +54,11 @@ static void test_option_parse_one(
ASSERT_EQ(i, n_entries);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
ASSERT_TRUE(strv_equal(args, remaining));
ASSERT_STREQ(argv[0], saved_argv0);
ASSERT_EQ(option_parser_get_n_args(&state, argc, argv), strv_length(remaining));
ASSERT_EQ(option_parser_get_n_args(&state), strv_length(remaining));
}
static void test_option_invalid_one(
@@ -77,11 +77,11 @@ static void test_option_invalid_one(
for (const Option *o = options; o->short_code != 0 || o->long_code; o++)
n_options++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
int c = option_parse(options, options + n_options, &state, argc, argv, &opt, &arg);
int c = option_parse(options, options + n_options, &state, &opt, &arg);
ASSERT_ERROR(c, EINVAL);
}
@@ -691,11 +691,11 @@ static void test_macros_parse_one(
for (const Entry *e = entries; e && (e->long_code || e->short_code != 0); e++)
n_entries++;
OptionParser state = {};
OptionParser state = { argc, argv };
const Option *opt;
const char *arg;
FOREACH_OPTION_FULL(&state, c, argc, argv, &opt, &arg, ASSERT_TRUE(false)) {
FOREACH_OPTION_FULL(&state, c, &opt, &arg, ASSERT_TRUE(false)) {
log_debug("%c %s: %s=%s",
opt->short_code != 0 ? opt->short_code : ' ',
opt->long_code ?: "",
@@ -751,7 +751,7 @@ static void test_macros_parse_one(
ASSERT_EQ(i, n_entries);
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
ASSERT_TRUE(strv_equal(args, remaining));
ASSERT_STREQ(argv[0], saved_argv0);
}
+26 -16
View File
@@ -16,14 +16,16 @@ static int noop_dispatcher(int argc, char *argv[], uintptr_t _data, void *userda
TEST(verbs) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
{ "list", VERB_ANY, 2, VERB_DEFAULT, noop_dispatcher },
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
{ "login", 2, 2, 0, noop_dispatcher },
{ "copy-to", 3, 4, 0, noop_dispatcher },
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
{ "list", VERB_ANY, 2, VERB_DEFAULT, noop_dispatcher },
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
{ "Group2", VERB_ANY, VERB_ANY, VERB_GROUP_MARKER, NULL },
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
{ "Group3", 0, 0, VERB_GROUP_MARKER, NULL },
{ "login", 2, 2, 0, noop_dispatcher },
{ "copy-to", 3, 4, 0, noop_dispatcher },
{}
};
@@ -44,6 +46,12 @@ TEST(verbs) {
/* no verb, but a default is set */
test_dispatch_one(STRV_EMPTY, verbs, 0);
/* the group entry shall not be found */
test_dispatch_one(STRV_MAKE("Group2"), verbs, -EINVAL);
/* the group entry shall not be found */
test_dispatch_one(STRV_MAKE("Group3"), verbs, -EINVAL);
}
TEST(verbs_no_default) {
@@ -60,14 +68,15 @@ TEST(verbs_no_default) {
TEST(verbs_no_default_many) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
{ "list", VERB_ANY, 2, 0, noop_dispatcher },
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
{ "login", 2, 2, 0, noop_dispatcher },
{ "copy-to", 3, 4, 0, noop_dispatcher },
{ "help", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "list-images", VERB_ANY, 1, 0, noop_dispatcher },
{ "list", VERB_ANY, 2, 0, noop_dispatcher },
{ "status", 2, VERB_ANY, 0, noop_dispatcher },
{ "Specials", VERB_ANY, VERB_ANY, VERB_GROUP_MARKER, NULL },
{ "show", VERB_ANY, VERB_ANY, 0, noop_dispatcher },
{ "terminate", 2, VERB_ANY, 0, noop_dispatcher },
{ "login", 2, 2, 0, noop_dispatcher },
{ "copy-to", 3, 4, 0, noop_dispatcher },
{}
};
@@ -75,6 +84,7 @@ TEST(verbs_no_default_many) {
test_dispatch_one(STRV_MAKE("hel"), verbs, -EINVAL);
test_dispatch_one(STRV_MAKE("helpp"), verbs, -EINVAL);
test_dispatch_one(STRV_MAKE("hgrejgoraoiosafso"), verbs, -EINVAL);
test_dispatch_one(STRV_MAKE("Specials"), verbs, -EINVAL);
}
DEFINE_TEST_MAIN(LOG_INFO);
+3 -3
View File
@@ -94,10 +94,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -112,7 +112,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
if (option_parser_get_n_args(&state, argc, argv) > 0)
if (option_parser_get_n_args(&state) > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
return 1;
+3 -3
View File
@@ -62,10 +62,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
OptionParser state = {};
OptionParser state = { argc, argv };
const char *arg;
FOREACH_OPTION(&state, c, argc, argv, &arg, /* on_error= */ return c)
FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c)
switch (c) {
OPTION_COMMON_HELP:
return help();
@@ -88,7 +88,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
char **args = option_parser_get_args(&state, argc, argv);
char **args = option_parser_get_args(&state);
if (strv_length(args) != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),