/src/util-linux/include/optutils.h
Line | Count | Source |
1 | | /* |
2 | | * No copyright is claimed. This code is in the public domain; do with |
3 | | * it what you wish. |
4 | | */ |
5 | | #ifndef UTIL_LINUX_OPTUTILS_H |
6 | | #define UTIL_LINUX_OPTUTILS_H |
7 | | |
8 | | #include <assert.h> |
9 | | |
10 | | #include "c.h" |
11 | | #include "nls.h" |
12 | | #include "cctype.h" |
13 | | |
14 | | /* |
15 | | * Converts the short option @c to the corresponding long option from @opts, or |
16 | | * returns NULL. |
17 | | */ |
18 | | static inline const char *ul_get_longopt(const struct option *opts, int c) |
19 | 0 | { |
20 | 0 | const struct option *o; |
21 | 0 |
|
22 | 0 | assert(!(opts == NULL)); |
23 | 0 | for (o = opts; o->name; o++) |
24 | 0 | if (o->val == c) |
25 | 0 | return o->name; |
26 | 0 | return NULL; |
27 | 0 | } |
28 | | |
29 | | /* |
30 | | * Converts the short options @c to "%c" or "0x<hex>" if not printable. |
31 | | */ |
32 | | static inline const char *ul_get_shortopt(char *buf, size_t bufsz, int c) |
33 | 0 | { |
34 | 0 | if (c_isgraph(c)) |
35 | 0 | snprintf(buf, bufsz, "%c", c); |
36 | 0 | else |
37 | 0 | snprintf(buf, bufsz, "<0x%02x>", c); /* should not happen */ |
38 | 0 |
|
39 | 0 | return buf; |
40 | 0 | } |
41 | | |
42 | | /* |
43 | | * Check if @arg is an option whose value is expected in the next argv entry. |
44 | | * Returns 1 for options with required_argument that don't have an inline |
45 | | * value (i.e., not --name=val or bundled -Bval), 0 otherwise. |
46 | | */ |
47 | | static inline int ul_is_option_with_arg(const char *arg, |
48 | | const struct option *opts) |
49 | 0 | { |
50 | 0 | const struct option *o; |
51 | 0 |
|
52 | 0 | if (arg[0] != '-' || !arg[1]) |
53 | 0 | return 0; |
54 | 0 |
|
55 | 0 | if (arg[1] == '-') { |
56 | 0 | const char *name = arg + 2; |
57 | 0 |
|
58 | 0 | if (!*name || strchr(name, '=')) |
59 | 0 | return 0; |
60 | 0 | for (o = opts; o->name; o++) { |
61 | 0 | if (o->has_arg == required_argument |
62 | 0 | && strcmp(name, o->name) == 0) |
63 | 0 | return 1; |
64 | 0 | } |
65 | 0 | return 0; |
66 | 0 | } |
67 | 0 |
|
68 | 0 | /* short option(s): walk bundled chars; if an earlier char takes |
69 | 0 | * an argument, the rest of the string is consumed as its value */ |
70 | 0 | const char *p; |
71 | 0 |
|
72 | 0 | for (p = arg + 1; *p; p++) { |
73 | 0 | for (o = opts; o->name; o++) { |
74 | 0 | if (o->val == *p) |
75 | 0 | break; |
76 | 0 | } |
77 | 0 | if (!o->name) |
78 | 0 | return 0; |
79 | 0 | if (o->has_arg != no_argument) |
80 | 0 | return !*(p + 1) && o->has_arg == required_argument; |
81 | 0 | } |
82 | 0 |
|
83 | 0 | return 0; |
84 | 0 | } |
85 | | |
86 | | /* |
87 | | * Find the "--" separator in argv[], ignoring "--" when it appears as an |
88 | | * argument to an option that requires a value (e.g., -I -- or --log-in --). |
89 | | * |
90 | | * Note on getopt_long() behavior with "--": |
91 | | * - required_argument: "--" is consumed as the option value, NOT treated |
92 | | * as end-of-options (e.g., --log-in -- sets the value to "--") |
93 | | * - optional_argument: "--" is NOT consumed as the value, it is treated |
94 | | * as end-of-options (optarg is NULL) |
95 | | * - no_argument: "--" is treated as end-of-options |
96 | | * |
97 | | * This function scans backward from each "--" counting consecutive |
98 | | * options-with-required-arg. They pair up (each consumes the next as its |
99 | | * value), so an odd count means "--" is consumed as an option value, an |
100 | | * even count (including zero) means "--" is the real separator. |
101 | | * |
102 | | * For example: |
103 | | * --log-in -- file odd (1), "--" is value for --log-in |
104 | | * --log-in --log-in -- cmd even (2), "--" is separator |
105 | | * |
106 | | * Returns the index of "--" in argv, or -1 if not found. |
107 | | */ |
108 | | static inline int ul_find_argv_separator(int argc, char *const argv[], |
109 | | const struct option *opts) |
110 | 0 | { |
111 | 0 | int i; |
112 | 0 |
|
113 | 0 | for (i = 1; i < argc; i++) { |
114 | 0 | int count, k; |
115 | 0 |
|
116 | 0 | if (strcmp(argv[i], "--") != 0) |
117 | 0 | continue; |
118 | 0 |
|
119 | 0 | for (count = 0, k = i - 1; k >= 1; k--) { |
120 | 0 | if (!ul_is_option_with_arg(argv[k], opts)) |
121 | 0 | break; |
122 | 0 | count++; |
123 | 0 | } |
124 | 0 |
|
125 | 0 | if (count % 2 == 1) |
126 | 0 | continue; |
127 | 0 |
|
128 | 0 | return i; |
129 | 0 | } |
130 | 0 |
|
131 | 0 | return -1; |
132 | 0 | } |
133 | | |
134 | | #ifndef OPTUTILS_EXIT_CODE |
135 | | # define OPTUTILS_EXIT_CODE EXIT_FAILURE |
136 | | #endif |
137 | | |
138 | | /* |
139 | | * Check collisions between options. |
140 | | * |
141 | | * The conflicts between options are described in ul_excl_t array. The |
142 | | * array contains groups of mutually exclusive options. For example |
143 | | * |
144 | | * static const ul_excl_t excl[] = { |
145 | | * { 'Z','b','c' }, // first group |
146 | | * { 'b','x' }, // second group |
147 | | * { 0 } |
148 | | * }; |
149 | | * |
150 | | * int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; |
151 | | * |
152 | | * while ((c = getopt_long(argc, argv, "Zbcx", longopts, NULL)) != -1) { |
153 | | * |
154 | | * err_exclusive_options(c, longopts, excl, excl_st); |
155 | | * |
156 | | * switch (c) { |
157 | | * case 'Z': |
158 | | * .... |
159 | | * } |
160 | | * } |
161 | | * |
162 | | * The array excl[] defines two groups of the mutually exclusive options. The |
163 | | * option '-b' is in the both groups. |
164 | | * |
165 | | * Note that the options in the group have to be in ASCII order (ABC..abc..) and |
166 | | * groups have to be also in ASCII order. |
167 | | * |
168 | | * The maximal number of the options in the group is 15 (size of the array is |
169 | | * 16, last is zero). |
170 | | * |
171 | | * The current status of options is stored in excl_st array. The size of the array |
172 | | * must be the same as number of the groups in the ul_excl_t array. |
173 | | * |
174 | | * If you're unsure then see sys-utils/mount.c or misc-utils/findmnt.c. |
175 | | */ |
176 | | #define UL_EXCL_STATUS_INIT { 0 } |
177 | | typedef int ul_excl_t[16]; |
178 | | |
179 | | static inline void err_exclusive_options( |
180 | | int c, |
181 | | const struct option *opts, |
182 | | const ul_excl_t *excl, |
183 | | int *status) |
184 | 0 | { |
185 | 0 | int e; |
186 | 0 |
|
187 | 0 | for (e = 0; excl[e][0] && excl[e][0] <= c; e++) { |
188 | 0 | const int *op = excl[e]; |
189 | 0 |
|
190 | 0 | for (; *op && *op <= c; op++) { |
191 | 0 | if (*op != c) |
192 | 0 | continue; |
193 | 0 | if (status[e] == 0) |
194 | 0 | status[e] = c; |
195 | 0 | else if (status[e] != c) { |
196 | 0 | const char *a = ul_get_longopt(opts, status[e]); |
197 | 0 | const char *b = ul_get_longopt(opts, c); |
198 | 0 | char buf[16]; /* short option in hex */ |
199 | 0 |
|
200 | 0 | errx(OPTUTILS_EXIT_CODE, |
201 | 0 | _("options %s%s and %s%s cannot be combined"), |
202 | 0 | a ? "--" : "-", |
203 | 0 | a ? a : ul_get_shortopt(buf, sizeof(buf), status[e]), |
204 | 0 | b ? "--" : "-", |
205 | 0 | b ? b : ul_get_shortopt(buf, sizeof(buf), c)); |
206 | 0 | } |
207 | 0 | break; |
208 | 0 | } |
209 | 0 | } |
210 | 0 | } |
211 | | |
212 | | #endif |