]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vpick: use the new option parser
authorZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Tue, 24 Mar 2026 09:35:30 +0000 (10:35 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@amutable.com>
Fri, 3 Apr 2026 11:07:03 +0000 (13:07 +0200)
-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 <claude@anthropic.com>
src/vpick/vpick-tool.c

index 57df857c6a42dcdb6d86928cc3e4de795db73d75..595ecae68869de93df835d8b8efa7174784a31f0 100644 (file)
@@ -1,7 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
 #include <dirent.h>
-#include <getopt.h>
 
 #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,