|
17 | 17 | #include "strv.h" |
18 | 18 | #include "virt.h" |
19 | 19 |
|
20 | | -int proc_cmdline_filter_pid1_args( |
21 | | - char **argv, /* input, may be reordered by this function. */ |
22 | | - char ***ret) { |
23 | | - |
| 20 | +int proc_cmdline_filter_pid1_args(char **argv, char ***ret) { |
24 | 21 | enum { |
25 | 22 | COMMON_GETOPT_ARGS, |
26 | 23 | SYSTEMD_GETOPT_ARGS, |
27 | 24 | SHUTDOWN_GETOPT_ARGS, |
28 | 25 | }; |
29 | | - |
30 | 26 | static const struct option options[] = { |
31 | 27 | COMMON_GETOPT_OPTIONS, |
32 | 28 | SYSTEMD_GETOPT_OPTIONS, |
33 | 29 | SHUTDOWN_GETOPT_OPTIONS, |
34 | | - {} |
35 | 30 | }; |
| 31 | + static const char *short_options = SYSTEMD_GETOPT_SHORT_OPTIONS; |
36 | 32 |
|
37 | | - int saved_optind, saved_opterr, saved_optopt, argc; |
38 | | - char *saved_optarg; |
39 | | - char **filtered; |
40 | | - size_t idx; |
| 33 | + _cleanup_strv_free_ char **filtered = NULL; |
| 34 | + int state, r; |
41 | 35 |
|
42 | 36 | assert(argv); |
43 | 37 | assert(ret); |
44 | 38 |
|
45 | | - /* Backup global variables. */ |
46 | | - saved_optind = optind; |
47 | | - saved_opterr = opterr; |
48 | | - saved_optopt = optopt; |
49 | | - saved_optarg = optarg; |
| 39 | + /* Currently, we do not support '-', '+', and ':' at the beginning. */ |
| 40 | + assert(!IN_SET(short_options[0], '-', '+', ':')); |
50 | 41 |
|
51 | | - /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long() |
52 | | - * that checks for GNU extensions in optstring ('-' or '+' at the beginning). Here, we do not use |
53 | | - * the GNU extensions, but might be used previously. Hence, we need to always reset it. */ |
54 | | - optind = 0; |
| 42 | + /* Filter out all known options. */ |
| 43 | + state = no_argument; |
| 44 | + STRV_FOREACH(p, strv_skip(argv, 1)) { |
| 45 | + int prev_state = state; |
| 46 | + const char *a = *p; |
55 | 47 |
|
56 | | - /* Do not print an error message. */ |
57 | | - opterr = 0; |
| 48 | + /* Reset the state for the next step. */ |
| 49 | + state = no_argument; |
58 | 50 |
|
59 | | - /* Filter out all known options. */ |
60 | | - argc = strv_length(argv); |
61 | | - while (getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL) >= 0) |
62 | | - ; |
| 51 | + if (prev_state == required_argument || |
| 52 | + (prev_state == optional_argument && a[0] != '-')) |
| 53 | + /* Handled as an argument of the previous option, filtering out the string. */ |
| 54 | + continue; |
63 | 55 |
|
64 | | - idx = optind; |
| 56 | + if (a[0] != '-') { |
| 57 | + /* Not an option, accepting the string. */ |
| 58 | + r = strv_extend(&filtered, a); |
| 59 | + if (r < 0) |
| 60 | + return r; |
| 61 | + continue; |
| 62 | + } |
65 | 63 |
|
66 | | - /* Restore global variables. */ |
67 | | - optind = saved_optind; |
68 | | - opterr = saved_opterr; |
69 | | - optopt = saved_optopt; |
70 | | - optarg = saved_optarg; |
| 64 | + if (a[1] == '-') { |
| 65 | + if (a[2] == '\0') { |
| 66 | + /* "--" is specified, accepting remaining strings. */ |
| 67 | + r = strv_extend_strv(&filtered, strv_skip(p, 1), /* filter_duplicates = */ false); |
| 68 | + if (r < 0) |
| 69 | + return r; |
| 70 | + break; |
| 71 | + } |
71 | 72 |
|
72 | | - filtered = strv_copy(strv_skip(argv, idx)); |
73 | | - if (!filtered) |
74 | | - return -ENOMEM; |
| 73 | + /* long option, e.g. --foo */ |
| 74 | + for (size_t i = 0; i < ELEMENTSOF(options); i++) { |
| 75 | + const char *q = startswith(a + 2, options[i].name); |
| 76 | + if (!q || !IN_SET(q[0], '=', '\0')) |
| 77 | + continue; |
| 78 | + |
| 79 | + /* Found matching option, updating the state if necessary. */ |
| 80 | + if (q[0] == '\0' && options[i].has_arg == required_argument) |
| 81 | + state = required_argument; |
| 82 | + |
| 83 | + break; |
| 84 | + } |
| 85 | + continue; |
| 86 | + } |
| 87 | + |
| 88 | + /* short option(s), e.g. -x or -xyz */ |
| 89 | + while (a && *++a != '\0') |
| 90 | + for (const char *q = short_options; *q != '\0'; q++) { |
| 91 | + if (*q != *a) |
| 92 | + continue; |
| 93 | + |
| 94 | + /* Found matching short option. */ |
| 95 | + |
| 96 | + if (q[1] == ':') { |
| 97 | + /* An argument is required or optional, and remaining part |
| 98 | + * is handled as argument if exists. */ |
| 99 | + state = a[1] != '\0' ? no_argument : |
| 100 | + q[2] == ':' ? optional_argument : required_argument; |
| 101 | + |
| 102 | + a = NULL; /* Not necessary to parse remaining part. */ |
| 103 | + } |
| 104 | + break; |
| 105 | + } |
| 106 | + } |
75 | 107 |
|
76 | | - *ret = filtered; |
| 108 | + *ret = TAKE_PTR(filtered); |
77 | 109 | return 0; |
78 | 110 | } |
79 | 111 |
|
|
0 commit comments