1290001Sglebius
2290001Sglebius/**
3290001Sglebius * \file enumeration.c
4290001Sglebius *
5290001Sglebius *  Handle options with enumeration names and bit mask bit names
6290001Sglebius *  for their arguments.
7290001Sglebius *
8290001Sglebius * @addtogroup autoopts
9290001Sglebius * @{
10290001Sglebius */
11290001Sglebius/*
12290001Sglebius *  This routine will run run-on options through a pager so the
13290001Sglebius *  user may examine, print or edit them at their leisure.
14290001Sglebius *
15290001Sglebius *  This file is part of AutoOpts, a companion to AutoGen.
16290001Sglebius *  AutoOpts is free software.
17290001Sglebius *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
18290001Sglebius *
19290001Sglebius *  AutoOpts is available under any one of two licenses.  The license
20290001Sglebius *  in use must be one of these two and the choice is under the control
21290001Sglebius *  of the user of the license.
22290001Sglebius *
23290001Sglebius *   The GNU Lesser General Public License, version 3 or later
24290001Sglebius *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
25290001Sglebius *
26290001Sglebius *   The Modified Berkeley Software Distribution License
27290001Sglebius *      See the file "COPYING.mbsd"
28290001Sglebius *
29290001Sglebius *  These files have the following sha256 sums:
30290001Sglebius *
31290001Sglebius *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
32290001Sglebius *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
33290001Sglebius *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
34290001Sglebius */
35290001Sglebius
36290001Sglebius/* = = = START-STATIC-FORWARD = = = */
37290001Sglebiusstatic void
38290001Sglebiusenum_err(tOptions * pOpts, tOptDesc * pOD,
39290001Sglebius         char const * const * paz_names, int name_ct);
40290001Sglebius
41290001Sglebiusstatic uintptr_t
42290001Sglebiusfind_name(char const * name, tOptions * pOpts, tOptDesc * pOD,
43290001Sglebius          char const * const *  paz_names, unsigned int name_ct);
44290001Sglebius
45290001Sglebiusstatic void
46290001Sglebiusset_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
47290001Sglebius               unsigned int name_ct);
48290001Sglebius
49290001Sglebiusstatic void
50290001Sglebiusset_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
51290001Sglebius               unsigned int nm_ct);
52290001Sglebius
53290001Sglebiusstatic uintptr_t
54290001Sglebiuscheck_membership_start(tOptDesc * od, char const ** argp, bool * invert);
55290001Sglebius
56290001Sglebiusstatic uintptr_t
57290001Sglebiusfind_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
58290001Sglebius                char const * const * nm_list, unsigned int nm_ct);
59290001Sglebius/* = = = END-STATIC-FORWARD = = = */
60290001Sglebius
61290001Sglebiusstatic void
62290001Sglebiusenum_err(tOptions * pOpts, tOptDesc * pOD,
63290001Sglebius         char const * const * paz_names, int name_ct)
64290001Sglebius{
65290001Sglebius    size_t max_len = 0;
66290001Sglebius    size_t ttl_len = 0;
67290001Sglebius    int    ct_down = name_ct;
68290001Sglebius    int    hidden  = 0;
69290001Sglebius
70290001Sglebius    /*
71290001Sglebius     *  A real "pOpts" pointer means someone messed up.  Give a real error.
72290001Sglebius     */
73290001Sglebius    if (pOpts > OPTPROC_EMIT_LIMIT)
74290001Sglebius        fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
75290001Sglebius                pOD->optArg.argString, pOD->pz_Name);
76290001Sglebius
77290001Sglebius    fprintf(option_usage_fp, zValidKeys, pOD->pz_Name);
78290001Sglebius
79290001Sglebius    /*
80290001Sglebius     *  If the first name starts with this funny character, then we have
81290001Sglebius     *  a first value with an unspellable name.  You cannot specify it.
82290001Sglebius     *  So, we don't list it either.
83290001Sglebius     */
84290001Sglebius    if (**paz_names == 0x7F) {
85290001Sglebius        paz_names++;
86290001Sglebius        hidden  = 1;
87290001Sglebius        ct_down = --name_ct;
88290001Sglebius    }
89290001Sglebius
90290001Sglebius    /*
91290001Sglebius     *  Figure out the maximum length of any name, plus the total length
92290001Sglebius     *  of all the names.
93290001Sglebius     */
94290001Sglebius    {
95290001Sglebius        char const * const * paz = paz_names;
96290001Sglebius
97290001Sglebius        do  {
98290001Sglebius            size_t len = strlen(*(paz++)) + 1;
99290001Sglebius            if (len > max_len)
100290001Sglebius                max_len = len;
101290001Sglebius            ttl_len += len;
102290001Sglebius        } while (--ct_down > 0);
103290001Sglebius
104290001Sglebius        ct_down = name_ct;
105290001Sglebius    }
106290001Sglebius
107290001Sglebius    /*
108290001Sglebius     *  IF any one entry is about 1/2 line or longer, print one per line
109290001Sglebius     */
110290001Sglebius    if (max_len > 35) {
111290001Sglebius        do  {
112290001Sglebius            fprintf(option_usage_fp, ENUM_ERR_LINE, *(paz_names++));
113290001Sglebius        } while (--ct_down > 0);
114290001Sglebius    }
115290001Sglebius
116290001Sglebius    /*
117290001Sglebius     *  ELSE IF they all fit on one line, then do so.
118290001Sglebius     */
119290001Sglebius    else if (ttl_len < 76) {
120290001Sglebius        fputc(' ', option_usage_fp);
121290001Sglebius        do  {
122290001Sglebius            fputc(' ', option_usage_fp);
123290001Sglebius            fputs(*(paz_names++), option_usage_fp);
124290001Sglebius        } while (--ct_down > 0);
125290001Sglebius        fputc(NL, option_usage_fp);
126290001Sglebius    }
127290001Sglebius
128290001Sglebius    /*
129290001Sglebius     *  Otherwise, columnize the output
130290001Sglebius     */
131290001Sglebius    else {
132290001Sglebius        unsigned int ent_no = 0;
133290001Sglebius        char  zFmt[16];  /* format for all-but-last entries on a line */
134290001Sglebius
135290001Sglebius        sprintf(zFmt, ENUM_ERR_WIDTH, (int)max_len);
136290001Sglebius        max_len = 78 / max_len; /* max_len is now max entries on a line */
137290001Sglebius        fputs(TWO_SPACES_STR, option_usage_fp);
138290001Sglebius
139290001Sglebius        /*
140290001Sglebius         *  Loop through all but the last entry
141290001Sglebius         */
142290001Sglebius        ct_down = name_ct;
143290001Sglebius        while (--ct_down > 0) {
144290001Sglebius            if (++ent_no == max_len) {
145290001Sglebius                /*
146290001Sglebius                 *  Last entry on a line.  Start next line, too.
147290001Sglebius                 */
148290001Sglebius                fprintf(option_usage_fp, NLSTR_SPACE_FMT, *(paz_names++));
149290001Sglebius                ent_no = 0;
150290001Sglebius            }
151290001Sglebius
152290001Sglebius            else
153290001Sglebius                fprintf(option_usage_fp, zFmt, *(paz_names++) );
154290001Sglebius        }
155290001Sglebius        fprintf(option_usage_fp, NLSTR_FMT, *paz_names);
156290001Sglebius    }
157290001Sglebius
158290001Sglebius    if (pOpts > OPTPROC_EMIT_LIMIT) {
159290001Sglebius        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
160290001Sglebius
161290001Sglebius        (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
162290001Sglebius        /* NOTREACHED */
163290001Sglebius    }
164290001Sglebius
165290001Sglebius    if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
166290001Sglebius        fprintf(option_usage_fp, zLowerBits, name_ct);
167290001Sglebius        fputs(zSetMemberSettings, option_usage_fp);
168290001Sglebius    } else {
169290001Sglebius        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
170290001Sglebius    }
171290001Sglebius}
172290001Sglebius
173290001Sglebius/**
174290001Sglebius * Convert a name or number into a binary number.
175290001Sglebius * "~0" and "-1" will be converted to the largest value in the enumeration.
176290001Sglebius *
177290001Sglebius * @param name       the keyword name (number) to convert
178290001Sglebius * @param pOpts      the program's option descriptor
179290001Sglebius * @param pOD        the option descriptor for this option
180290001Sglebius * @param paz_names  the list of keywords for this option
181290001Sglebius * @param name_ct    the count of keywords
182290001Sglebius */
183290001Sglebiusstatic uintptr_t
184290001Sglebiusfind_name(char const * name, tOptions * pOpts, tOptDesc * pOD,
185290001Sglebius          char const * const *  paz_names, unsigned int name_ct)
186290001Sglebius{
187290001Sglebius    /*
188290001Sglebius     *  Return the matching index as a pointer sized integer.
189290001Sglebius     *  The result gets stashed in a char * pointer.
190290001Sglebius     */
191290001Sglebius    uintptr_t   res = name_ct;
192294905Sdelphij    size_t      len = strlen(name);
193290001Sglebius    uintptr_t   idx;
194290001Sglebius
195290001Sglebius    if (IS_DEC_DIGIT_CHAR(*name)) {
196294905Sdelphij        char * pz;
197294905Sdelphij        unsigned long val = strtoul(name, &pz, 0);
198290001Sglebius        if ((*pz == NUL) && (val < name_ct))
199290001Sglebius            return (uintptr_t)val;
200290001Sglebius        pz_enum_err_fmt = znum_too_large;
201290001Sglebius        option_usage_fp = stderr;
202290001Sglebius        enum_err(pOpts, pOD, paz_names, (int)name_ct);
203290001Sglebius        return name_ct;
204290001Sglebius    }
205290001Sglebius
206290001Sglebius    if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) {
207290001Sglebius        if (  ((name[0] == '~') && (name[1] == '0'))
208290001Sglebius           || ((name[0] == '-') && (name[1] == '1')))
209290001Sglebius        return (uintptr_t)(name_ct - 1);
210290001Sglebius        goto oops;
211290001Sglebius    }
212290001Sglebius
213290001Sglebius    /*
214290001Sglebius     *  Look for an exact match, but remember any partial matches.
215290001Sglebius     *  Multiple partial matches means we have an ambiguous match.
216290001Sglebius     */
217290001Sglebius    for (idx = 0; idx < name_ct; idx++) {
218294905Sdelphij        if (strncmp(paz_names[idx], name, len) == 0) {
219290001Sglebius            if (paz_names[idx][len] == NUL)
220290001Sglebius                return idx;  /* full match */
221290001Sglebius
222290001Sglebius            if (res == name_ct)
223290001Sglebius                res = idx; /* save partial match */
224290001Sglebius            else
225290001Sglebius                res = (uintptr_t)~0;  /* may yet find full match */
226290001Sglebius        }
227290001Sglebius    }
228290001Sglebius
229290001Sglebius    if (res < name_ct)
230290001Sglebius        return res; /* partial match */
231290001Sglebius
232290001Sglebius oops:
233290001Sglebius
234290001Sglebius    pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key;
235290001Sglebius    option_usage_fp = stderr;
236290001Sglebius    enum_err(pOpts, pOD, paz_names, (int)name_ct);
237290001Sglebius    return name_ct;
238290001Sglebius}
239290001Sglebius
240290001Sglebius
241290001Sglebius/*=export_func  optionKeywordName
242290001Sglebius * what:  Convert between enumeration values and strings
243290001Sglebius * private:
244290001Sglebius *
245290001Sglebius * arg:   tOptDesc *,    pOD,       enumeration option description
246290001Sglebius * arg:   unsigned int,  enum_val,  the enumeration value to map
247290001Sglebius *
248290001Sglebius * ret_type:  char const *
249290001Sglebius * ret_desc:  the enumeration name from const memory
250290001Sglebius *
251290001Sglebius * doc:   This converts an enumeration value into the matching string.
252290001Sglebius=*/
253290001Sglebiuschar const *
254290001SglebiusoptionKeywordName(tOptDesc * pOD, unsigned int enum_val)
255290001Sglebius{
256290001Sglebius    tOptDesc od = { 0 };
257290001Sglebius    od.optArg.argEnum = enum_val;
258290001Sglebius
259290001Sglebius    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
260290001Sglebius    return od.optArg.argString;
261290001Sglebius}
262290001Sglebius
263290001Sglebius
264290001Sglebius/*=export_func  optionEnumerationVal
265290001Sglebius * what:  Convert from a string to an enumeration value
266290001Sglebius * private:
267290001Sglebius *
268290001Sglebius * arg:   tOptions *,    pOpts,     the program options descriptor
269290001Sglebius * arg:   tOptDesc *,    pOD,       enumeration option description
270290001Sglebius * arg:   char const * const *,  paz_names, list of enumeration names
271290001Sglebius * arg:   unsigned int,  name_ct,   number of names in list
272290001Sglebius *
273290001Sglebius * ret_type:  uintptr_t
274290001Sglebius * ret_desc:  the enumeration value
275290001Sglebius *
276290001Sglebius * doc:   This converts the optArg.argString string from the option description
277290001Sglebius *        into the index corresponding to an entry in the name list.
278290001Sglebius *        This will match the generated enumeration value.
279290001Sglebius *        Full matches are always accepted.  Partial matches are accepted
280290001Sglebius *        if there is only one partial match.
281290001Sglebius=*/
282290001Sglebiusuintptr_t
283290001SglebiusoptionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
284290001Sglebius                     char const * const * paz_names, unsigned int name_ct)
285290001Sglebius{
286290001Sglebius    uintptr_t res = 0UL;
287290001Sglebius
288290001Sglebius    /*
289290001Sglebius     *  IF the program option descriptor pointer is invalid,
290290001Sglebius     *  then it is some sort of special request.
291290001Sglebius     */
292290001Sglebius    switch ((uintptr_t)pOpts) {
293290001Sglebius    case (uintptr_t)OPTPROC_EMIT_USAGE:
294290001Sglebius        /*
295290001Sglebius         *  print the list of enumeration names.
296290001Sglebius         */
297290001Sglebius        enum_err(pOpts, pOD, paz_names, (int)name_ct);
298290001Sglebius        break;
299290001Sglebius
300290001Sglebius    case (uintptr_t)OPTPROC_EMIT_SHELL:
301290001Sglebius    {
302290001Sglebius        unsigned int ix = (unsigned int)pOD->optArg.argEnum;
303290001Sglebius        /*
304290001Sglebius         *  print the name string.
305290001Sglebius         */
306290001Sglebius        if (ix >= name_ct)
307290001Sglebius            printf(INVALID_FMT, ix);
308290001Sglebius        else
309290001Sglebius            fputs(paz_names[ ix ], stdout);
310290001Sglebius
311290001Sglebius        break;
312290001Sglebius    }
313290001Sglebius
314290001Sglebius    case (uintptr_t)OPTPROC_RETURN_VALNAME:
315290001Sglebius    {
316290001Sglebius        unsigned int ix = (unsigned int)pOD->optArg.argEnum;
317290001Sglebius        /*
318290001Sglebius         *  Replace the enumeration value with the name string.
319290001Sglebius         */
320290001Sglebius        if (ix >= name_ct)
321290001Sglebius            return (uintptr_t)INVALID_STR;
322290001Sglebius
323290001Sglebius        pOD->optArg.argString = paz_names[ix];
324290001Sglebius        break;
325290001Sglebius    }
326290001Sglebius
327290001Sglebius    default:
328290001Sglebius        if ((pOD->fOptState & OPTST_RESET) != 0)
329290001Sglebius            break;
330290001Sglebius
331290001Sglebius        res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
332290001Sglebius
333290001Sglebius        if (pOD->fOptState & OPTST_ALLOC_ARG) {
334290001Sglebius            AGFREE(pOD->optArg.argString);
335290001Sglebius            pOD->fOptState &= ~OPTST_ALLOC_ARG;
336290001Sglebius            pOD->optArg.argString = NULL;
337290001Sglebius        }
338290001Sglebius    }
339290001Sglebius
340290001Sglebius    return res;
341290001Sglebius}
342290001Sglebius
343290001Sglebiusstatic void
344290001Sglebiusset_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
345290001Sglebius               unsigned int name_ct)
346290001Sglebius{
347290001Sglebius    /*
348290001Sglebius     *  print the name string.
349290001Sglebius     */
350290001Sglebius    unsigned int ix =  0;
351290001Sglebius    uintptr_t  bits = (uintptr_t)pOD->optCookie;
352290001Sglebius    size_t     len  = 0;
353290001Sglebius
354290001Sglebius    (void)pOpts;
355290001Sglebius    bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
356290001Sglebius
357290001Sglebius    while (bits != 0) {
358290001Sglebius        if (bits & 1) {
359290001Sglebius            if (len++ > 0) fputs(OR_STR, stdout);
360290001Sglebius            fputs(paz_names[ix], stdout);
361290001Sglebius        }
362290001Sglebius        if (++ix >= name_ct) break;
363290001Sglebius        bits >>= 1;
364290001Sglebius    }
365290001Sglebius}
366290001Sglebius
367290001Sglebiusstatic void
368290001Sglebiusset_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
369290001Sglebius               unsigned int nm_ct)
370290001Sglebius{
371290001Sglebius    char *     pz;
372290001Sglebius    uintptr_t  mask = (1UL << (uintptr_t)nm_ct) - 1UL;
373290001Sglebius    uintptr_t  bits = (uintptr_t)od->optCookie & mask;
374290001Sglebius    unsigned int ix = 0;
375290001Sglebius    size_t     len  = 1;
376290001Sglebius
377290001Sglebius    /*
378290001Sglebius     *  Replace the enumeration value with the name string.
379290001Sglebius     *  First, determine the needed length, then allocate and fill in.
380290001Sglebius     */
381290001Sglebius    while (bits != 0) {
382290001Sglebius        if (bits & 1)
383290001Sglebius            len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1;
384290001Sglebius        if (++ix >= nm_ct) break;
385290001Sglebius        bits >>= 1;
386290001Sglebius    }
387290001Sglebius
388290001Sglebius    od->optArg.argString = pz = AGALOC(len, "enum");
389290001Sglebius    bits = (uintptr_t)od->optCookie & mask;
390290001Sglebius    if (bits == 0) {
391290001Sglebius        *pz = NUL;
392290001Sglebius        return;
393290001Sglebius    }
394290001Sglebius
395290001Sglebius    for (ix = 0; ; ix++) {
396290001Sglebius        size_t nln;
397290001Sglebius        int    doit = bits & 1;
398290001Sglebius
399290001Sglebius        bits >>= 1;
400290001Sglebius        if (doit == 0)
401290001Sglebius            continue;
402290001Sglebius
403290001Sglebius        nln = strlen(nm_list[ix]);
404290001Sglebius        memcpy(pz, nm_list[ix], nln);
405290001Sglebius        pz += nln;
406290001Sglebius        if (bits == 0)
407290001Sglebius            break;
408290001Sglebius        memcpy(pz, PLUS_STR, PLUS_STR_LEN);
409290001Sglebius        pz += PLUS_STR_LEN;
410290001Sglebius    }
411290001Sglebius    *pz = NUL;
412290001Sglebius    (void)opts;
413290001Sglebius}
414290001Sglebius
415290001Sglebius/**
416290001Sglebius * Check membership start conditions.  An equal character (@samp{=}) says to
417290001Sglebius * clear the result and not carry over any residual value.  A carat
418290001Sglebius * (@samp{^}), which may follow the equal character, says to invert the
419290001Sglebius * result.  The scanning pointer is advanced past these characters and any
420290001Sglebius * leading white space.  Invalid sequences are indicated by setting the
421290001Sglebius * scanning pointer to NULL.
422290001Sglebius *
423290001Sglebius * @param od      the set membership option description
424290001Sglebius * @param argp    a pointer to the string scanning pointer
425290001Sglebius * @param invert  a pointer to the boolean inversion indicator
426290001Sglebius *
427290001Sglebius * @returns either zero or the original value for the optCookie.
428290001Sglebius */
429290001Sglebiusstatic uintptr_t
430290001Sglebiuscheck_membership_start(tOptDesc * od, char const ** argp, bool * invert)
431290001Sglebius{
432290001Sglebius    uintptr_t    res = (uintptr_t)od->optCookie;
433290001Sglebius    char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
434290001Sglebius    if ((arg == NULL) || (*arg == NUL))
435290001Sglebius        goto member_start_fail;
436290001Sglebius
437290001Sglebius    *invert = false;
438290001Sglebius
439290001Sglebius    switch (*arg) {
440290001Sglebius    case '=':
441290001Sglebius        res = 0UL;
442290001Sglebius        arg = SPN_WHITESPACE_CHARS(arg + 1);
443290001Sglebius        switch (*arg) {
444290001Sglebius        case '=': case ',':
445290001Sglebius            goto member_start_fail;
446290001Sglebius        case '^':
447290001Sglebius            goto inversion;
448290001Sglebius        default:
449290001Sglebius            break;
450290001Sglebius        }
451290001Sglebius        break;
452290001Sglebius
453290001Sglebius    case '^':
454290001Sglebius    inversion:
455290001Sglebius        *invert = true;
456290001Sglebius        arg = SPN_WHITESPACE_CHARS(arg + 1);
457290001Sglebius        if (*arg != ',')
458290001Sglebius            break;
459290001Sglebius        /* FALLTHROUGH */
460290001Sglebius
461290001Sglebius    case ',':
462290001Sglebius        goto member_start_fail;
463290001Sglebius
464290001Sglebius    default:
465290001Sglebius        break;
466290001Sglebius    }
467290001Sglebius
468290001Sglebius    *argp = arg;
469290001Sglebius    return res;
470290001Sglebius
471290001Sglebiusmember_start_fail:
472290001Sglebius    *argp = NULL;
473290001Sglebius    return 0UL;
474290001Sglebius}
475290001Sglebius
476290001Sglebius/**
477290001Sglebius * convert a name to a bit.  Look up a name string to get a bit number
478290001Sglebius * and shift the value "1" left that number of bits.
479290001Sglebius *
480290001Sglebius * @param opts      program options descriptor
481290001Sglebius * @param od        the set membership option description
482290001Sglebius * @param pz        address of the start of the bit name
483290001Sglebius * @param nm_list   the list of names for this option
484290001Sglebius * @param nm_ct     the number of entries in this list
485290001Sglebius *
486290001Sglebius * @returns 0UL on error, other an unsigned long with the correct bit set.
487290001Sglebius */
488290001Sglebiusstatic uintptr_t
489290001Sglebiusfind_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
490290001Sglebius                char const * const * nm_list, unsigned int nm_ct)
491290001Sglebius{
492290001Sglebius    char nm_buf[ AO_NAME_SIZE ];
493290001Sglebius
494290001Sglebius    memcpy(nm_buf, pz, len);
495290001Sglebius    nm_buf[len] = NUL;
496290001Sglebius
497290001Sglebius    {
498290001Sglebius        unsigned int shift_ct = (unsigned int)
499290001Sglebius            find_name(nm_buf, opts, od, nm_list, nm_ct);
500290001Sglebius        if (shift_ct >= nm_ct)
501290001Sglebius            return 0UL;
502290001Sglebius
503294905Sdelphij        return (uintptr_t)1U << shift_ct;
504290001Sglebius    }
505290001Sglebius}
506290001Sglebius
507290001Sglebius/*=export_func  optionMemberList
508290001Sglebius * what:  Get the list of members of a bit mask set
509290001Sglebius *
510290001Sglebius * arg:   tOptDesc *,  od,   the set membership option description
511290001Sglebius *
512290001Sglebius * ret_type: char *
513290001Sglebius * ret_desc: the names of the set bits
514290001Sglebius *
515290001Sglebius * doc:   This converts the OPT_VALUE_name mask value to a allocated string.
516290001Sglebius *        It is the caller's responsibility to free the string.
517290001Sglebius=*/
518290001Sglebiuschar *
519290001SglebiusoptionMemberList(tOptDesc * od)
520290001Sglebius{
521290001Sglebius    uintptr_t    sv = od->optArg.argIntptr;
522290001Sglebius    char * res;
523290001Sglebius    (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
524290001Sglebius    res = VOIDP(od->optArg.argString);
525290001Sglebius    od->optArg.argIntptr = sv;
526290001Sglebius    return res;
527290001Sglebius}
528290001Sglebius
529290001Sglebius/*=export_func  optionSetMembers
530290001Sglebius * what:  Convert between bit flag values and strings
531290001Sglebius * private:
532290001Sglebius *
533290001Sglebius * arg:   tOptions *,     opts,     the program options descriptor
534290001Sglebius * arg:   tOptDesc *,     od,       the set membership option description
535290001Sglebius * arg:   char const * const *,
536290001Sglebius *                       nm_list,  list of enumeration names
537290001Sglebius * arg:   unsigned int,  nm_ct,    number of names in list
538290001Sglebius *
539290001Sglebius * doc:   This converts the optArg.argString string from the option description
540290001Sglebius *        into the index corresponding to an entry in the name list.
541290001Sglebius *        This will match the generated enumeration value.
542290001Sglebius *        Full matches are always accepted.  Partial matches are accepted
543290001Sglebius *        if there is only one partial match.
544290001Sglebius=*/
545290001Sglebiusvoid
546290001SglebiusoptionSetMembers(tOptions * opts, tOptDesc * od,
547290001Sglebius                 char const * const * nm_list, unsigned int nm_ct)
548290001Sglebius{
549290001Sglebius    /*
550290001Sglebius     *  IF the program option descriptor pointer is invalid,
551290001Sglebius     *  then it is some sort of special request.
552290001Sglebius     */
553290001Sglebius    switch ((uintptr_t)opts) {
554290001Sglebius    case (uintptr_t)OPTPROC_EMIT_USAGE:
555290001Sglebius        enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
556290001Sglebius        return;
557290001Sglebius
558290001Sglebius    case (uintptr_t)OPTPROC_EMIT_SHELL:
559290001Sglebius        set_memb_shell(opts, od, nm_list, nm_ct);
560290001Sglebius        return;
561290001Sglebius
562290001Sglebius    case (uintptr_t)OPTPROC_RETURN_VALNAME:
563290001Sglebius        set_memb_names(opts, od, nm_list, nm_ct);
564290001Sglebius        return;
565290001Sglebius
566290001Sglebius    default:
567290001Sglebius        break;
568290001Sglebius    }
569290001Sglebius
570290001Sglebius    if ((od->fOptState & OPTST_RESET) != 0)
571290001Sglebius        return;
572290001Sglebius
573290001Sglebius    {
574290001Sglebius        char const * arg;
575290001Sglebius        bool         invert;
576290001Sglebius        uintptr_t    res = check_membership_start(od, &arg, &invert);
577290001Sglebius        if (arg == NULL)
578290001Sglebius            goto fail_return;
579290001Sglebius
580290001Sglebius        while (*arg != NUL) {
581290001Sglebius            bool inv_val = false;
582290001Sglebius            int  len;
583290001Sglebius
584290001Sglebius            switch (*arg) {
585290001Sglebius            case ',':
586290001Sglebius                arg = SPN_WHITESPACE_CHARS(arg+1);
587290001Sglebius                if ((*arg == ',') || (*arg == '|'))
588290001Sglebius                    goto fail_return;
589290001Sglebius                continue;
590290001Sglebius
591290001Sglebius            case '-':
592290001Sglebius            case '!':
593290001Sglebius                inv_val = true;
594290001Sglebius                /* FALLTHROUGH */
595290001Sglebius
596290001Sglebius            case '+':
597290001Sglebius            case '|':
598290001Sglebius                arg = SPN_WHITESPACE_CHARS(arg+1);
599290001Sglebius            }
600290001Sglebius
601290001Sglebius            len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
602290001Sglebius            if (len == 0)
603290001Sglebius                break;
604290001Sglebius
605290001Sglebius            if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
606290001Sglebius                if (inv_val)
607290001Sglebius                     res = 0;
608290001Sglebius                else res = ~0UL;
609290001Sglebius            }
610290001Sglebius            else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
611290001Sglebius                if (! inv_val)
612290001Sglebius                    res = 0;
613290001Sglebius            }
614290001Sglebius            else do {
615290001Sglebius                char *    pz;
616290001Sglebius                uintptr_t bit = strtoul(arg, &pz, 0);
617290001Sglebius
618290001Sglebius                if (pz != arg + len) {
619290001Sglebius                    bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
620290001Sglebius                    if (bit == 0UL)
621290001Sglebius                        goto fail_return;
622290001Sglebius                }
623290001Sglebius                if (inv_val)
624290001Sglebius                     res &= ~bit;
625290001Sglebius                else res |= bit;
626290001Sglebius            } while (false);
627290001Sglebius
628290001Sglebius            arg = SPN_WHITESPACE_CHARS(arg + len);
629290001Sglebius        }
630290001Sglebius
631290001Sglebius        if (invert)
632290001Sglebius            res ^= ~0UL;
633290001Sglebius
634290001Sglebius        if (nm_ct < (8 * sizeof(uintptr_t)))
635290001Sglebius            res &= (1UL << nm_ct) - 1UL;
636290001Sglebius
637290001Sglebius        od->optCookie = VOIDP(res);
638290001Sglebius    }
639290001Sglebius    return;
640290001Sglebius
641290001Sglebiusfail_return:
642290001Sglebius    od->optCookie = VOIDP(0);
643290001Sglebius}
644290001Sglebius
645290001Sglebius/** @}
646290001Sglebius *
647290001Sglebius * Local Variables:
648290001Sglebius * mode: C
649290001Sglebius * c-file-style: "stroustrup"
650290001Sglebius * indent-tabs-mode: nil
651290001Sglebius * End:
652290001Sglebius * end of autoopts/enum.c */
653