From: Zbigniew Jędrzejewski-Szmek Date: Tue, 24 Mar 2026 09:35:30 +0000 (+0100) Subject: vpick: use the new option parser X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=190385b55dea5505bfd7fcd3dff3f48b38ee34ab;p=thirdparty%2Fsystemd.git vpick: use the new option parser -h/--help and --version are moved from a standalone section into the Output group, because they look misplaced without a header and having a new "Options" group with the verb-like entries also wasn't appealing. Output is changed a bit to avoid repeating "rather than path": - -p --print=filename Print selected filename rather than path - -p --print=version Print selected version rather than path - -p --print=type Print selected inode type rather than path - -p --print=arch Print selected architecture rather than path - -p --print=tries Print selected tries left/tries done rather than path - -p --print=all Print all of the above - --resolve=yes Canonicalize the result path + -h --help Show this help + --version Show package version + -p --print=WHAT Print selected WHAT rather than path + --print=filename ... print selected filename + --print=version ... print selected version + --print=type ... print selected inode + --print=arch ... print selected architecture + --print=tries ... print selected tries left/tries done + --print=all ... print all of the above + --resolve=BOOL Canonicalize the result path Co-developed-by: Claude --- diff --git a/src/vpick/vpick-tool.c b/src/vpick/vpick-tool.c index 57df857c6a4..595ecae6886 100644 --- a/src/vpick/vpick-tool.c +++ b/src/vpick/vpick-tool.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include #include "alloc-util.h" #include "architecture.h" @@ -9,12 +8,14 @@ #include "format-table.h" #include "log.h" #include "main-func.h" +#include "options.h" #include "parse-util.h" #include "path-util.h" #include "pretty-print.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "vpick.h" typedef enum { @@ -55,167 +56,165 @@ DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(print, Print); static int help(void) { _cleanup_free_ char *link = NULL; + _cleanup_(table_unrefp) Table *lookup_keys = NULL, *output = NULL; int r; r = terminal_urlify_man("systemd-vpick", "1", &link); if (r < 0) return log_oom(); - printf("%1$s [OPTIONS...] PATH...\n" - "\n%5$sPick entry from versioned directory.%6$s\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - "\n%3$sLookup Keys:%4$s\n" - " -B --basename=BASENAME\n" - " Look for specified basename\n" - " -V VERSION Look for specified version\n" - " -A ARCH Look for specified architecture\n" - " -S --suffix=SUFFIX Look for specified suffix\n" - " -t --type=TYPE Look for specified inode type\n" - "\n%3$sOutput:%4$s\n" - " -p --print=filename Print selected filename rather than path\n" - " -p --print=version Print selected version rather than path\n" - " -p --print=type Print selected inode type rather than path\n" - " -p --print=arch Print selected architecture rather than path\n" - " -p --print=tries Print selected tries left/tries done rather than path\n" - " -p --print=all Print all of the above\n" - " --resolve=yes Canonicalize the result path\n" - "\nSee the %2$s for details.\n", - program_invocation_short_name, - link, - ansi_underline(), ansi_normal(), - ansi_highlight(), ansi_normal()); + r = option_parser_get_help_table(&lookup_keys); + if (r < 0) + return r; - return 0; -} + r = option_parser_get_help_table_group("Output", &output); + if (r < 0) + return r; + + (void) table_sync_column_widths(0, lookup_keys, output); -static int parse_argv(int argc, char *argv[]) { + printf("%s [OPTIONS...] PATH...\n" + "\n%sPick entry from versioned directory.%s\n", + program_invocation_short_name, + ansi_highlight(), + ansi_normal()); - enum { - ARG_VERSION = 0x100, - ARG_RESOLVE, - }; + printf("\n%sLookup Keys:%s\n", ansi_underline(), ansi_normal()); + r = table_print_or_warn(lookup_keys); + if (r < 0) + return r; - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "basename", required_argument, NULL, 'B' }, - { "suffix", required_argument, NULL, 'S' }, - { "type", required_argument, NULL, 't' }, - { "print", required_argument, NULL, 'p' }, - { "resolve", required_argument, NULL, ARG_RESOLVE }, - {} - }; + printf("\n%sOutput:%s\n", ansi_underline(), ansi_normal()); + r = table_print_or_warn(output); + if (r < 0) + return r; - int c, r; + printf("\nSee the %s for details.\n", link); + return 0; +} + +static int parse_argv(int argc, char *argv[], char ***ret_args) { + int r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hB:V:A:S:t:p:", options, NULL)) >= 0) { + OptionParser state = { argc, argv }; + const char *arg; + FOREACH_OPTION(&state, c, &arg, /* on_error= */ return c) switch (c) { - case 'h': - return help(); - - case ARG_VERSION: - return version(); - - case 'B': - if (!filename_part_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", optarg); + OPTION('B', "basename", "BASENAME", "Look for specified basename"): + if (!filename_part_is_valid(arg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid basename string: %s", arg); - r = free_and_strdup_warn(&arg_filter_basename, optarg); + r = free_and_strdup_warn(&arg_filter_basename, arg); if (r < 0) return r; break; - case 'V': - if (!version_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", optarg); + OPTION_SHORT('V', "VERSION", "Look for specified version"): + if (!version_is_valid(arg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid version string: %s", arg); - r = free_and_strdup_warn(&arg_filter_version, optarg); + r = free_and_strdup_warn(&arg_filter_version, arg); if (r < 0) return r; break; - case 'A': - if (streq(optarg, "native")) + OPTION_SHORT('A', "ARCH", "Look for specified architecture"): + if (streq(arg, "native")) arg_filter_architecture = native_architecture(); - else if (streq(optarg, "secondary")) { + else if (streq(arg, "secondary")) { #ifdef ARCHITECTURE_SECONDARY arg_filter_architecture = ARCHITECTURE_SECONDARY; #else return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Local architecture has no secondary architecture."); #endif - } else if (streq(optarg, "uname")) + } else if (streq(arg, "uname")) arg_filter_architecture = uname_architecture(); - else if (streq(optarg, "auto")) + else if (streq(arg, "auto")) arg_filter_architecture = _ARCHITECTURE_INVALID; else { - arg_filter_architecture = architecture_from_string(optarg); + arg_filter_architecture = architecture_from_string(arg); if (arg_filter_architecture < 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", optarg); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown architecture: %s", arg); } break; - case 'S': - if (!filename_part_is_valid(optarg)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", optarg); + OPTION('S', "suffix", "SUFFIX", "Look for specified suffix"): + if (!filename_part_is_valid(arg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid suffix string: %s", arg); - r = free_and_strdup_warn(&arg_filter_suffix, optarg); + r = free_and_strdup_warn(&arg_filter_suffix, arg); if (r < 0) return r; break; - case 't': - if (isempty(optarg)) + OPTION('t', "type", "TYPE", "Look for specified inode type"): + if (isempty(arg)) arg_filter_type_mask = 0; else { mode_t m; - m = inode_type_from_string(optarg); + m = inode_type_from_string(arg); if (m == MODE_INVALID) - return log_error_errno(m, "Unknown inode type: %s", optarg); + return log_error_errno(m, "Unknown inode type: %s", arg); arg_filter_type_mask |= UINT32_C(1) << IFTODT(m); } break; - case 'p': - if (streq(optarg, "arch")) /* accept abbreviation too */ + OPTION_GROUP("Output"): {} + + OPTION_COMMON_HELP: + return help(); + + OPTION_COMMON_VERSION: + return version(); + + OPTION('p', "print", "WHAT", + "Print selected WHAT rather than path"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "filename", + "... print selected filename"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "version", + "... print selected version"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "type", + "... print selected inode type"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "arch", + "... print selected architecture"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "tries", + "... print selected tries left/tries done"): {} + OPTION_LONG_FLAGS(OPTION_HELP_ENTRY, "print", "all", + "... print all of the above"): + + if (streq(arg, "arch")) /* accept abbreviation too */ arg_print = PRINT_ARCHITECTURE; else - arg_print = print_from_string(optarg); + arg_print = print_from_string(arg); if (arg_print < 0) - return log_error_errno(arg_print, "Unknown --print= argument: %s", optarg); + return log_error_errno(arg_print, "Unknown --print= argument: %s", arg); break; - case ARG_RESOLVE: - r = parse_boolean(optarg); + OPTION_LONG("resolve", "BOOL", "Canonicalize the result path"): + r = parse_boolean(arg); if (r < 0) return log_error_errno(r, "Failed to parse --resolve= value: %m"); SET_FLAG(arg_flags, PICK_RESOLVE, r); break; - - case '?': - return -EINVAL; - - default: - assert_not_reached(); } - } if (arg_print < 0) arg_print = PRINT_PATH; + *ret_args = option_parser_get_args(&state); return 1; } @@ -224,18 +223,19 @@ static int run(int argc, char *argv[]) { log_setup(); - r = parse_argv(argc, argv); + char **args = NULL; + r = parse_argv(argc, argv, &args); if (r <= 0) return r; - if (optind >= argc) + if (strv_isempty(args)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path to resolve must be specified."); - for (int i = optind; i < argc; i++) { + STRV_FOREACH(i, args) { _cleanup_free_ char *p = NULL; - r = path_make_absolute_cwd(argv[i], &p); + r = path_make_absolute_cwd(*i, &p); if (r < 0) - return log_error_errno(r, "Failed to make path '%s' absolute: %m", argv[i]); + return log_error_errno(r, "Failed to make path '%s' absolute: %m", *i); _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL; r = path_pick(/* toplevel_path= */ NULL,