/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <dirent.h>
-#include <getopt.h>
#include "alloc-util.h"
#include "architecture.h"
#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 {
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;
}
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,