/src/ntp-dev/sntp/libopts/enum.c
Line | Count | Source |
1 | | |
2 | | /** |
3 | | * \file enumeration.c |
4 | | * |
5 | | * Handle options with enumeration names and bit mask bit names |
6 | | * for their arguments. |
7 | | * |
8 | | * @addtogroup autoopts |
9 | | * @{ |
10 | | */ |
11 | | /* |
12 | | * This routine will run run-on options through a pager so the |
13 | | * user may examine, print or edit them at their leisure. |
14 | | * |
15 | | * This file is part of AutoOpts, a companion to AutoGen. |
16 | | * AutoOpts is free software. |
17 | | * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved |
18 | | * |
19 | | * AutoOpts is available under any one of two licenses. The license |
20 | | * in use must be one of these two and the choice is under the control |
21 | | * of the user of the license. |
22 | | * |
23 | | * The GNU Lesser General Public License, version 3 or later |
24 | | * See the files "COPYING.lgplv3" and "COPYING.gplv3" |
25 | | * |
26 | | * The Modified Berkeley Software Distribution License |
27 | | * See the file "COPYING.mbsd" |
28 | | * |
29 | | * These files have the following sha256 sums: |
30 | | * |
31 | | * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 |
32 | | * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 |
33 | | * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd |
34 | | */ |
35 | | |
36 | | static void |
37 | | enum_err(tOptions * pOpts, tOptDesc * pOD, |
38 | | char const * const * paz_names, int name_ct) |
39 | 0 | { |
40 | 0 | size_t max_len = 0; |
41 | 0 | size_t ttl_len = 0; |
42 | 0 | int ct_down = name_ct; |
43 | 0 | int hidden = 0; |
44 | | |
45 | | /* |
46 | | * A real "pOpts" pointer means someone messed up. Give a real error. |
47 | | */ |
48 | 0 | if (pOpts > OPTPROC_EMIT_LIMIT) |
49 | 0 | fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName, |
50 | 0 | pOD->optArg.argString, pOD->pz_Name); |
51 | |
|
52 | 0 | fprintf(option_usage_fp, zValidKeys, pOD->pz_Name); |
53 | | |
54 | | /* |
55 | | * If the first name starts with this funny character, then we have |
56 | | * a first value with an unspellable name. You cannot specify it. |
57 | | * So, we don't list it either. |
58 | | */ |
59 | 0 | if (**paz_names == 0x7F) { |
60 | 0 | paz_names++; |
61 | 0 | hidden = 1; |
62 | 0 | ct_down = --name_ct; |
63 | 0 | } |
64 | | |
65 | | /* |
66 | | * Figure out the maximum length of any name, plus the total length |
67 | | * of all the names. |
68 | | */ |
69 | 0 | { |
70 | 0 | char const * const * paz = paz_names; |
71 | |
|
72 | 0 | do { |
73 | 0 | size_t len = strlen(*(paz++)) + 1; |
74 | 0 | if (len > max_len) |
75 | 0 | max_len = len; |
76 | 0 | ttl_len += len; |
77 | 0 | } while (--ct_down > 0); |
78 | |
|
79 | 0 | ct_down = name_ct; |
80 | 0 | } |
81 | | |
82 | | /* |
83 | | * IF any one entry is about 1/2 line or longer, print one per line |
84 | | */ |
85 | 0 | if (max_len > 35) { |
86 | 0 | do { |
87 | 0 | fprintf(option_usage_fp, ENUM_ERR_LINE, *(paz_names++)); |
88 | 0 | } while (--ct_down > 0); |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * ELSE IF they all fit on one line, then do so. |
93 | | */ |
94 | 0 | else if (ttl_len < 76) { |
95 | 0 | fputc(' ', option_usage_fp); |
96 | 0 | do { |
97 | 0 | fputc(' ', option_usage_fp); |
98 | 0 | fputs(*(paz_names++), option_usage_fp); |
99 | 0 | } while (--ct_down > 0); |
100 | 0 | fputc(NL, option_usage_fp); |
101 | 0 | } |
102 | | |
103 | | /* |
104 | | * Otherwise, columnize the output |
105 | | */ |
106 | 0 | else { |
107 | 0 | unsigned int ent_no = 0; |
108 | 0 | char fmt[16]; /* format for all-but-last entries on a line */ |
109 | |
|
110 | 0 | if (snprintf(fmt, 16, ENUM_ERR_WIDTH, (int)max_len) >= 16) |
111 | 0 | option_exits(EXIT_FAILURE); |
112 | 0 | max_len = 78 / max_len; /* max_len is now max entries on a line */ |
113 | 0 | fputs(TWO_SPACES_STR, option_usage_fp); |
114 | | |
115 | | /* |
116 | | * Loop through all but the last entry |
117 | | */ |
118 | 0 | ct_down = name_ct; |
119 | 0 | while (--ct_down > 0) { |
120 | 0 | if (++ent_no == max_len) { |
121 | | /* |
122 | | * Last entry on a line. Start next line, too. |
123 | | */ |
124 | 0 | fprintf(option_usage_fp, NLSTR_SPACE_FMT, *(paz_names++)); |
125 | 0 | ent_no = 0; |
126 | 0 | } |
127 | | |
128 | 0 | else |
129 | 0 | fprintf(option_usage_fp, fmt, *(paz_names++) ); |
130 | 0 | } |
131 | 0 | fprintf(option_usage_fp, NLSTR_FMT, *paz_names); |
132 | 0 | } |
133 | |
|
134 | 0 | if (pOpts > OPTPROC_EMIT_LIMIT) { |
135 | 0 | fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); |
136 | |
|
137 | 0 | (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE); |
138 | | /* NOTREACHED */ |
139 | 0 | } |
140 | |
|
141 | 0 | if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { |
142 | 0 | fprintf(option_usage_fp, zLowerBits, name_ct); |
143 | 0 | fputs(zSetMemberSettings, option_usage_fp); |
144 | 0 | } else { |
145 | 0 | fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | /** |
150 | | * Convert a name or number into a binary number. |
151 | | * "~0" and "-1" will be converted to the largest value in the enumeration. |
152 | | * |
153 | | * @param name the keyword name (number) to convert |
154 | | * @param pOpts the program's option descriptor |
155 | | * @param pOD the option descriptor for this option |
156 | | * @param paz_names the list of keywords for this option |
157 | | * @param name_ct the count of keywords |
158 | | */ |
159 | | static uintptr_t |
160 | | find_name(char const * name, tOptions * pOpts, tOptDesc * pOD, |
161 | | char const * const * paz_names, unsigned int name_ct) |
162 | 0 | { |
163 | | /* |
164 | | * Return the matching index as a pointer sized integer. |
165 | | * The result gets stashed in a char * pointer. |
166 | | */ |
167 | 0 | uintptr_t res = name_ct; |
168 | 0 | size_t len = strlen((char *)name); |
169 | 0 | uintptr_t idx; |
170 | |
|
171 | 0 | if (IS_DEC_DIGIT_CHAR(*name)) { |
172 | 0 | char * pz = VOIDP(name); |
173 | 0 | unsigned long val = strtoul(pz, &pz, 0); |
174 | 0 | if ((*pz == NUL) && (val < name_ct)) |
175 | 0 | return (uintptr_t)val; |
176 | 0 | pz_enum_err_fmt = znum_too_large; |
177 | 0 | option_usage_fp = stderr; |
178 | 0 | enum_err(pOpts, pOD, paz_names, (int)name_ct); |
179 | 0 | return name_ct; |
180 | 0 | } |
181 | | |
182 | 0 | if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) { |
183 | 0 | if ( ((name[0] == '~') && (name[1] == '0')) |
184 | 0 | || ((name[0] == '-') && (name[1] == '1'))) |
185 | 0 | return (uintptr_t)(name_ct - 1); |
186 | 0 | goto oops; |
187 | 0 | } |
188 | | |
189 | | /* |
190 | | * Look for an exact match, but remember any partial matches. |
191 | | * Multiple partial matches means we have an ambiguous match. |
192 | | */ |
193 | 0 | for (idx = 0; idx < name_ct; idx++) { |
194 | 0 | if (strncmp((char *)paz_names[idx], (char *)name, len) == 0) { |
195 | 0 | if (paz_names[idx][len] == NUL) |
196 | 0 | return idx; /* full match */ |
197 | | |
198 | 0 | if (res == name_ct) |
199 | 0 | res = idx; /* save partial match */ |
200 | 0 | else |
201 | 0 | res = (uintptr_t)~0; /* may yet find full match */ |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | 0 | if (res < name_ct) |
206 | 0 | return res; /* partial match */ |
207 | | |
208 | 0 | oops: |
209 | |
|
210 | 0 | pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key; |
211 | 0 | option_usage_fp = stderr; |
212 | 0 | enum_err(pOpts, pOD, paz_names, (int)name_ct); |
213 | 0 | return name_ct; |
214 | 0 | } |
215 | | |
216 | | |
217 | | /*=export_func optionKeywordName |
218 | | * what: Convert between enumeration values and strings |
219 | | * private: |
220 | | * |
221 | | * arg: tOptDesc *, pOD, enumeration option description |
222 | | * arg: unsigned int, enum_val, the enumeration value to map |
223 | | * |
224 | | * ret_type: char const * |
225 | | * ret_desc: the enumeration name from const memory |
226 | | * |
227 | | * doc: This converts an enumeration value into the matching string. |
228 | | =*/ |
229 | | char const * |
230 | | optionKeywordName(tOptDesc * pOD, unsigned int enum_val) |
231 | 0 | { |
232 | 0 | tOptDesc od = { 0 }; |
233 | 0 | od.optArg.argEnum = enum_val; |
234 | |
|
235 | 0 | (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od ); |
236 | 0 | return od.optArg.argString; |
237 | 0 | } |
238 | | |
239 | | |
240 | | /*=export_func optionEnumerationVal |
241 | | * what: Convert from a string to an enumeration value |
242 | | * private: |
243 | | * |
244 | | * arg: tOptions *, pOpts, the program options descriptor |
245 | | * arg: tOptDesc *, pOD, enumeration option description |
246 | | * arg: char const * const *, paz_names, list of enumeration names |
247 | | * arg: unsigned int, name_ct, number of names in list |
248 | | * |
249 | | * ret_type: uintptr_t |
250 | | * ret_desc: the enumeration value |
251 | | * |
252 | | * doc: This converts the optArg.argString string from the option description |
253 | | * into the index corresponding to an entry in the name list. |
254 | | * This will match the generated enumeration value. |
255 | | * Full matches are always accepted. Partial matches are accepted |
256 | | * if there is only one partial match. |
257 | | =*/ |
258 | | uintptr_t |
259 | | optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD, |
260 | | char const * const * paz_names, unsigned int name_ct) |
261 | 0 | { |
262 | 0 | uintptr_t res = 0UL; |
263 | | |
264 | | /* |
265 | | * IF the program option descriptor pointer is invalid, |
266 | | * then it is some sort of special request. |
267 | | */ |
268 | 0 | switch ((uintptr_t)pOpts) { |
269 | 0 | case (uintptr_t)OPTPROC_EMIT_USAGE: |
270 | | /* |
271 | | * print the list of enumeration names. |
272 | | */ |
273 | 0 | enum_err(pOpts, pOD, paz_names, (int)name_ct); |
274 | 0 | break; |
275 | | |
276 | 0 | case (uintptr_t)OPTPROC_EMIT_SHELL: |
277 | 0 | { |
278 | 0 | unsigned int ix = (unsigned int)pOD->optArg.argEnum; |
279 | | /* |
280 | | * print the name string. |
281 | | */ |
282 | 0 | if (ix >= name_ct) |
283 | 0 | printf(INVALID_FMT, ix); |
284 | 0 | else |
285 | 0 | fputs(paz_names[ ix ], stdout); |
286 | |
|
287 | 0 | break; |
288 | 0 | } |
289 | | |
290 | 0 | case (uintptr_t)OPTPROC_RETURN_VALNAME: |
291 | 0 | { |
292 | 0 | unsigned int ix = (unsigned int)pOD->optArg.argEnum; |
293 | | /* |
294 | | * Replace the enumeration value with the name string. |
295 | | */ |
296 | 0 | if (ix >= name_ct) |
297 | 0 | return (uintptr_t)INVALID_STR; |
298 | | |
299 | 0 | pOD->optArg.argString = paz_names[ix]; |
300 | 0 | break; |
301 | 0 | } |
302 | | |
303 | 0 | default: |
304 | 0 | if ((pOD->fOptState & OPTST_RESET) != 0) |
305 | 0 | break; |
306 | | |
307 | 0 | res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct); |
308 | |
|
309 | 0 | if (pOD->fOptState & OPTST_ALLOC_ARG) { |
310 | 0 | AGFREE(pOD->optArg.argString); |
311 | 0 | pOD->fOptState &= ~OPTST_ALLOC_ARG; |
312 | 0 | pOD->optArg.argString = NULL; |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | 0 | return res; |
317 | 0 | } |
318 | | |
319 | | static void |
320 | | set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names, |
321 | | unsigned int name_ct) |
322 | 0 | { |
323 | | /* |
324 | | * print the name string. |
325 | | */ |
326 | 0 | unsigned int ix = 0; |
327 | 0 | uintptr_t bits = (uintptr_t)pOD->optCookie; |
328 | 0 | size_t len = 0; |
329 | |
|
330 | 0 | (void)pOpts; |
331 | 0 | bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; |
332 | |
|
333 | 0 | while (bits != 0) { |
334 | 0 | if (bits & 1) { |
335 | 0 | if (len++ > 0) fputs(OR_STR, stdout); |
336 | 0 | fputs(paz_names[ix], stdout); |
337 | 0 | } |
338 | 0 | if (++ix >= name_ct) break; |
339 | 0 | bits >>= 1; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | | static void |
344 | | set_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list, |
345 | | unsigned int nm_ct) |
346 | 0 | { |
347 | 0 | char * pz; |
348 | 0 | uintptr_t mask = (1UL << (uintptr_t)nm_ct) - 1UL; |
349 | 0 | uintptr_t bits = (uintptr_t)od->optCookie & mask; |
350 | 0 | unsigned int ix = 0; |
351 | 0 | size_t len = 1; |
352 | | |
353 | | /* |
354 | | * Replace the enumeration value with the name string. |
355 | | * First, determine the needed length, then allocate and fill in. |
356 | | */ |
357 | 0 | while (bits != 0) { |
358 | 0 | if (bits & 1) |
359 | 0 | len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1; |
360 | 0 | if (++ix >= nm_ct) break; |
361 | 0 | bits >>= 1; |
362 | 0 | } |
363 | |
|
364 | 0 | od->optArg.argString = pz = AGALOC(len, "enum"); |
365 | 0 | bits = (uintptr_t)od->optCookie & mask; |
366 | 0 | if (bits == 0) { |
367 | 0 | *pz = NUL; |
368 | 0 | return; |
369 | 0 | } |
370 | | |
371 | 0 | for (ix = 0; ; ix++) { |
372 | 0 | size_t nln; |
373 | 0 | int doit = bits & 1; |
374 | |
|
375 | 0 | bits >>= 1; |
376 | 0 | if (doit == 0) |
377 | 0 | continue; |
378 | | |
379 | 0 | nln = strlen(nm_list[ix]); |
380 | 0 | memcpy(pz, nm_list[ix], nln); |
381 | 0 | pz += nln; |
382 | 0 | if (bits == 0) |
383 | 0 | break; |
384 | 0 | memcpy(pz, PLUS_STR, PLUS_STR_LEN); |
385 | 0 | pz += PLUS_STR_LEN; |
386 | 0 | } |
387 | 0 | *pz = NUL; |
388 | 0 | (void)opts; |
389 | 0 | } |
390 | | |
391 | | /** |
392 | | * Check membership start conditions. An equal character (@samp{=}) says to |
393 | | * clear the result and not carry over any residual value. A carat |
394 | | * (@samp{^}), which may follow the equal character, says to invert the |
395 | | * result. The scanning pointer is advanced past these characters and any |
396 | | * leading white space. Invalid sequences are indicated by setting the |
397 | | * scanning pointer to NULL. |
398 | | * |
399 | | * @param od the set membership option description |
400 | | * @param argp a pointer to the string scanning pointer |
401 | | * @param invert a pointer to the boolean inversion indicator |
402 | | * |
403 | | * @returns either zero or the original value for the optCookie. |
404 | | */ |
405 | | static uintptr_t |
406 | | check_membership_start(tOptDesc * od, char const ** argp, bool * invert) |
407 | 0 | { |
408 | 0 | uintptr_t res = (uintptr_t)od->optCookie; |
409 | 0 | char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString); |
410 | 0 | if ((arg == NULL) || (*arg == NUL)) |
411 | 0 | goto member_start_fail; |
412 | | |
413 | 0 | *invert = false; |
414 | |
|
415 | 0 | switch (*arg) { |
416 | 0 | case '=': |
417 | 0 | res = 0UL; |
418 | 0 | arg = SPN_WHITESPACE_CHARS(arg + 1); |
419 | 0 | switch (*arg) { |
420 | 0 | case '=': case ',': |
421 | 0 | goto member_start_fail; |
422 | 0 | case '^': |
423 | 0 | goto inversion; |
424 | 0 | default: |
425 | 0 | break; |
426 | 0 | } |
427 | 0 | break; |
428 | | |
429 | 0 | case '^': |
430 | 0 | inversion: |
431 | 0 | *invert = true; |
432 | 0 | arg = SPN_WHITESPACE_CHARS(arg + 1); |
433 | 0 | if (*arg != ',') |
434 | 0 | break; |
435 | | /* FALLTHROUGH */ |
436 | | |
437 | 0 | case ',': |
438 | 0 | goto member_start_fail; |
439 | | |
440 | 0 | default: |
441 | 0 | break; |
442 | 0 | } |
443 | | |
444 | 0 | *argp = arg; |
445 | 0 | return res; |
446 | | |
447 | 0 | member_start_fail: |
448 | 0 | *argp = NULL; |
449 | 0 | return 0UL; |
450 | 0 | } |
451 | | |
452 | | /** |
453 | | * convert a name to a bit. Look up a name string to get a bit number |
454 | | * and shift the value "1" left that number of bits. |
455 | | * |
456 | | * @param opts program options descriptor |
457 | | * @param od the set membership option description |
458 | | * @param pz address of the start of the bit name |
459 | | * @param nm_list the list of names for this option |
460 | | * @param nm_ct the number of entries in this list |
461 | | * |
462 | | * @returns 0UL on error, other an unsigned long with the correct bit set. |
463 | | */ |
464 | | static uintptr_t |
465 | | find_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len, |
466 | | char const * const * nm_list, unsigned int nm_ct) |
467 | 0 | { |
468 | 0 | char nm_buf[ AO_NAME_SIZE ]; |
469 | |
|
470 | 0 | memcpy(nm_buf, pz, len); |
471 | 0 | nm_buf[len] = NUL; |
472 | |
|
473 | 0 | { |
474 | 0 | unsigned int shift_ct = (unsigned int) |
475 | 0 | find_name(nm_buf, opts, od, nm_list, nm_ct); |
476 | 0 | if (shift_ct >= nm_ct) |
477 | 0 | return 0UL; |
478 | | |
479 | 0 | return 1UL << shift_ct; |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | | /*=export_func optionMemberList |
484 | | * what: Get the list of members of a bit mask set |
485 | | * |
486 | | * arg: tOptDesc *, od, the set membership option description |
487 | | * |
488 | | * ret_type: char * |
489 | | * ret_desc: the names of the set bits |
490 | | * |
491 | | * doc: This converts the OPT_VALUE_name mask value to a allocated string. |
492 | | * It is the caller's responsibility to free the string. |
493 | | =*/ |
494 | | char * |
495 | | optionMemberList(tOptDesc * od) |
496 | 0 | { |
497 | 0 | uintptr_t sv = od->optArg.argIntptr; |
498 | 0 | char * res; |
499 | 0 | (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od); |
500 | 0 | res = VOIDP(od->optArg.argString); |
501 | 0 | od->optArg.argIntptr = sv; |
502 | 0 | return res; |
503 | 0 | } |
504 | | |
505 | | /*=export_func optionSetMembers |
506 | | * what: Convert between bit flag values and strings |
507 | | * private: |
508 | | * |
509 | | * arg: tOptions *, opts, the program options descriptor |
510 | | * arg: tOptDesc *, od, the set membership option description |
511 | | * arg: char const * const *, |
512 | | * nm_list, list of enumeration names |
513 | | * arg: unsigned int, nm_ct, number of names in list |
514 | | * |
515 | | * doc: This converts the optArg.argString string from the option description |
516 | | * into the index corresponding to an entry in the name list. |
517 | | * This will match the generated enumeration value. |
518 | | * Full matches are always accepted. Partial matches are accepted |
519 | | * if there is only one partial match. |
520 | | =*/ |
521 | | void |
522 | | optionSetMembers(tOptions * opts, tOptDesc * od, |
523 | | char const * const * nm_list, unsigned int nm_ct) |
524 | 0 | { |
525 | | /* |
526 | | * IF the program option descriptor pointer is invalid, |
527 | | * then it is some sort of special request. |
528 | | */ |
529 | 0 | switch ((uintptr_t)opts) { |
530 | 0 | case (uintptr_t)OPTPROC_EMIT_USAGE: |
531 | 0 | enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct); |
532 | 0 | return; |
533 | | |
534 | 0 | case (uintptr_t)OPTPROC_EMIT_SHELL: |
535 | 0 | set_memb_shell(opts, od, nm_list, nm_ct); |
536 | 0 | return; |
537 | | |
538 | 0 | case (uintptr_t)OPTPROC_RETURN_VALNAME: |
539 | 0 | set_memb_names(opts, od, nm_list, nm_ct); |
540 | 0 | return; |
541 | | |
542 | 0 | default: |
543 | 0 | break; |
544 | 0 | } |
545 | | |
546 | 0 | if ((od->fOptState & OPTST_RESET) != 0) |
547 | 0 | return; |
548 | | |
549 | 0 | { |
550 | 0 | char const * arg; |
551 | 0 | bool invert; |
552 | 0 | uintptr_t res = check_membership_start(od, &arg, &invert); |
553 | 0 | if (arg == NULL) |
554 | 0 | goto fail_return; |
555 | | |
556 | 0 | while (*arg != NUL) { |
557 | 0 | bool inv_val = false; |
558 | 0 | int len; |
559 | |
|
560 | 0 | switch (*arg) { |
561 | 0 | case ',': |
562 | 0 | arg = SPN_WHITESPACE_CHARS(arg+1); |
563 | 0 | if ((*arg == ',') || (*arg == '|')) |
564 | 0 | goto fail_return; |
565 | 0 | continue; |
566 | | |
567 | 0 | case '-': |
568 | 0 | case '!': |
569 | 0 | inv_val = true; |
570 | | /* FALLTHROUGH */ |
571 | |
|
572 | 0 | case '+': |
573 | 0 | case '|': |
574 | 0 | arg = SPN_WHITESPACE_CHARS(arg+1); |
575 | 0 | } |
576 | | |
577 | 0 | len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg); |
578 | 0 | if (len == 0) |
579 | 0 | break; |
580 | | |
581 | 0 | if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) { |
582 | 0 | if (inv_val) |
583 | 0 | res = 0; |
584 | 0 | else res = ~0UL; |
585 | 0 | } |
586 | 0 | else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) { |
587 | 0 | if (! inv_val) |
588 | 0 | res = 0; |
589 | 0 | } |
590 | 0 | else do { |
591 | 0 | char * pz; |
592 | 0 | uintptr_t bit = strtoul(arg, &pz, 0); |
593 | |
|
594 | 0 | if (pz != arg + len) { |
595 | 0 | bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct); |
596 | 0 | if (bit == 0UL) |
597 | 0 | goto fail_return; |
598 | 0 | } |
599 | 0 | if (inv_val) |
600 | 0 | res &= ~bit; |
601 | 0 | else res |= bit; |
602 | 0 | } while (false); |
603 | | |
604 | 0 | arg = SPN_WHITESPACE_CHARS(arg + len); |
605 | 0 | } |
606 | | |
607 | 0 | if (invert) |
608 | 0 | res ^= ~0UL; |
609 | |
|
610 | 0 | if (nm_ct < (8 * sizeof(uintptr_t))) |
611 | 0 | res &= (1UL << nm_ct) - 1UL; |
612 | |
|
613 | 0 | od->optCookie = VOIDP(res); |
614 | 0 | } |
615 | 0 | return; |
616 | | |
617 | 0 | fail_return: |
618 | 0 | od->optCookie = VOIDP(0); |
619 | 0 | } |
620 | | |
621 | | /** @} |
622 | | * |
623 | | * Local Variables: |
624 | | * mode: C |
625 | | * c-file-style: "stroustrup" |
626 | | * indent-tabs-mode: nil |
627 | | * End: |
628 | | * end of autoopts/enum.c */ |