xref: /freebsd/contrib/ntp/sntp/libopts/enum.c (revision a466cc55373fc3cf86837f09da729535b57e69a1)
12b15cb3dSCy Schubert 
22b15cb3dSCy Schubert /**
32b15cb3dSCy Schubert  * \file enumeration.c
42b15cb3dSCy Schubert  *
52b15cb3dSCy Schubert  *  Handle options with enumeration names and bit mask bit names
62b15cb3dSCy Schubert  *  for their arguments.
72b15cb3dSCy Schubert  *
82b15cb3dSCy Schubert  * @addtogroup autoopts
92b15cb3dSCy Schubert  * @{
102b15cb3dSCy Schubert  */
112b15cb3dSCy Schubert /*
122b15cb3dSCy Schubert  *  This routine will run run-on options through a pager so the
132b15cb3dSCy Schubert  *  user may examine, print or edit them at their leisure.
142b15cb3dSCy Schubert  *
152b15cb3dSCy Schubert  *  This file is part of AutoOpts, a companion to AutoGen.
162b15cb3dSCy Schubert  *  AutoOpts is free software.
17*a466cc55SCy Schubert  *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
182b15cb3dSCy Schubert  *
192b15cb3dSCy Schubert  *  AutoOpts is available under any one of two licenses.  The license
202b15cb3dSCy Schubert  *  in use must be one of these two and the choice is under the control
212b15cb3dSCy Schubert  *  of the user of the license.
222b15cb3dSCy Schubert  *
232b15cb3dSCy Schubert  *   The GNU Lesser General Public License, version 3 or later
242b15cb3dSCy Schubert  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
252b15cb3dSCy Schubert  *
262b15cb3dSCy Schubert  *   The Modified Berkeley Software Distribution License
272b15cb3dSCy Schubert  *      See the file "COPYING.mbsd"
282b15cb3dSCy Schubert  *
292b15cb3dSCy Schubert  *  These files have the following sha256 sums:
302b15cb3dSCy Schubert  *
312b15cb3dSCy Schubert  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
322b15cb3dSCy Schubert  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
332b15cb3dSCy Schubert  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
342b15cb3dSCy Schubert  */
352b15cb3dSCy Schubert 
362b15cb3dSCy Schubert static void
enum_err(tOptions * pOpts,tOptDesc * pOD,char const * const * paz_names,int name_ct)372b15cb3dSCy Schubert enum_err(tOptions * pOpts, tOptDesc * pOD,
382b15cb3dSCy Schubert          char const * const * paz_names, int name_ct)
392b15cb3dSCy Schubert {
402b15cb3dSCy Schubert     size_t max_len = 0;
412b15cb3dSCy Schubert     size_t ttl_len = 0;
422b15cb3dSCy Schubert     int    ct_down = name_ct;
432b15cb3dSCy Schubert     int    hidden  = 0;
442b15cb3dSCy Schubert 
452b15cb3dSCy Schubert     /*
462b15cb3dSCy Schubert      *  A real "pOpts" pointer means someone messed up.  Give a real error.
472b15cb3dSCy Schubert      */
482b15cb3dSCy Schubert     if (pOpts > OPTPROC_EMIT_LIMIT)
492b15cb3dSCy Schubert         fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
502b15cb3dSCy Schubert                 pOD->optArg.argString, pOD->pz_Name);
512b15cb3dSCy Schubert 
522b15cb3dSCy Schubert     fprintf(option_usage_fp, zValidKeys, pOD->pz_Name);
532b15cb3dSCy Schubert 
542b15cb3dSCy Schubert     /*
552b15cb3dSCy Schubert      *  If the first name starts with this funny character, then we have
562b15cb3dSCy Schubert      *  a first value with an unspellable name.  You cannot specify it.
572b15cb3dSCy Schubert      *  So, we don't list it either.
582b15cb3dSCy Schubert      */
592b15cb3dSCy Schubert     if (**paz_names == 0x7F) {
602b15cb3dSCy Schubert         paz_names++;
612b15cb3dSCy Schubert         hidden  = 1;
622b15cb3dSCy Schubert         ct_down = --name_ct;
632b15cb3dSCy Schubert     }
642b15cb3dSCy Schubert 
652b15cb3dSCy Schubert     /*
662b15cb3dSCy Schubert      *  Figure out the maximum length of any name, plus the total length
672b15cb3dSCy Schubert      *  of all the names.
682b15cb3dSCy Schubert      */
692b15cb3dSCy Schubert     {
702b15cb3dSCy Schubert         char const * const * paz = paz_names;
712b15cb3dSCy Schubert 
722b15cb3dSCy Schubert         do  {
732b15cb3dSCy Schubert             size_t len = strlen(*(paz++)) + 1;
742b15cb3dSCy Schubert             if (len > max_len)
752b15cb3dSCy Schubert                 max_len = len;
762b15cb3dSCy Schubert             ttl_len += len;
772b15cb3dSCy Schubert         } while (--ct_down > 0);
782b15cb3dSCy Schubert 
792b15cb3dSCy Schubert         ct_down = name_ct;
802b15cb3dSCy Schubert     }
812b15cb3dSCy Schubert 
822b15cb3dSCy Schubert     /*
832b15cb3dSCy Schubert      *  IF any one entry is about 1/2 line or longer, print one per line
842b15cb3dSCy Schubert      */
852b15cb3dSCy Schubert     if (max_len > 35) {
862b15cb3dSCy Schubert         do  {
872b15cb3dSCy Schubert             fprintf(option_usage_fp, ENUM_ERR_LINE, *(paz_names++));
882b15cb3dSCy Schubert         } while (--ct_down > 0);
892b15cb3dSCy Schubert     }
902b15cb3dSCy Schubert 
912b15cb3dSCy Schubert     /*
922b15cb3dSCy Schubert      *  ELSE IF they all fit on one line, then do so.
932b15cb3dSCy Schubert      */
942b15cb3dSCy Schubert     else if (ttl_len < 76) {
952b15cb3dSCy Schubert         fputc(' ', option_usage_fp);
962b15cb3dSCy Schubert         do  {
972b15cb3dSCy Schubert             fputc(' ', option_usage_fp);
982b15cb3dSCy Schubert             fputs(*(paz_names++), option_usage_fp);
992b15cb3dSCy Schubert         } while (--ct_down > 0);
1002b15cb3dSCy Schubert         fputc(NL, option_usage_fp);
1012b15cb3dSCy Schubert     }
1022b15cb3dSCy Schubert 
1032b15cb3dSCy Schubert     /*
1042b15cb3dSCy Schubert      *  Otherwise, columnize the output
1052b15cb3dSCy Schubert      */
1062b15cb3dSCy Schubert     else {
1072b15cb3dSCy Schubert         unsigned int ent_no = 0;
108*a466cc55SCy Schubert         char fmt[16];  /* format for all-but-last entries on a line */
1092b15cb3dSCy Schubert 
110*a466cc55SCy Schubert         if (snprintf(fmt, 16, ENUM_ERR_WIDTH, (int)max_len) >= 16)
111*a466cc55SCy Schubert             option_exits(EXIT_FAILURE);
1122b15cb3dSCy Schubert         max_len = 78 / max_len; /* max_len is now max entries on a line */
1132b15cb3dSCy Schubert         fputs(TWO_SPACES_STR, option_usage_fp);
1142b15cb3dSCy Schubert 
1152b15cb3dSCy Schubert         /*
1162b15cb3dSCy Schubert          *  Loop through all but the last entry
1172b15cb3dSCy Schubert          */
1182b15cb3dSCy Schubert         ct_down = name_ct;
1192b15cb3dSCy Schubert         while (--ct_down > 0) {
1202b15cb3dSCy Schubert             if (++ent_no == max_len) {
1212b15cb3dSCy Schubert                 /*
1222b15cb3dSCy Schubert                  *  Last entry on a line.  Start next line, too.
1232b15cb3dSCy Schubert                  */
1242b15cb3dSCy Schubert                 fprintf(option_usage_fp, NLSTR_SPACE_FMT, *(paz_names++));
1252b15cb3dSCy Schubert                 ent_no = 0;
1262b15cb3dSCy Schubert             }
1272b15cb3dSCy Schubert 
1282b15cb3dSCy Schubert             else
129*a466cc55SCy Schubert                 fprintf(option_usage_fp, fmt, *(paz_names++) );
1302b15cb3dSCy Schubert         }
1312b15cb3dSCy Schubert         fprintf(option_usage_fp, NLSTR_FMT, *paz_names);
1322b15cb3dSCy Schubert     }
1332b15cb3dSCy Schubert 
1342b15cb3dSCy Schubert     if (pOpts > OPTPROC_EMIT_LIMIT) {
1352b15cb3dSCy Schubert         fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
1362b15cb3dSCy Schubert 
1372b15cb3dSCy Schubert         (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
1382b15cb3dSCy Schubert         /* NOTREACHED */
1392b15cb3dSCy Schubert     }
1402b15cb3dSCy Schubert 
1412b15cb3dSCy Schubert     if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
1422b15cb3dSCy Schubert         fprintf(option_usage_fp, zLowerBits, name_ct);
1432b15cb3dSCy Schubert         fputs(zSetMemberSettings, option_usage_fp);
1442b15cb3dSCy Schubert     } else {
1452b15cb3dSCy Schubert         fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
1462b15cb3dSCy Schubert     }
1472b15cb3dSCy Schubert }
1482b15cb3dSCy Schubert 
1492b15cb3dSCy Schubert /**
1502b15cb3dSCy Schubert  * Convert a name or number into a binary number.
1512b15cb3dSCy Schubert  * "~0" and "-1" will be converted to the largest value in the enumeration.
1522b15cb3dSCy Schubert  *
1532b15cb3dSCy Schubert  * @param name       the keyword name (number) to convert
1542b15cb3dSCy Schubert  * @param pOpts      the program's option descriptor
1552b15cb3dSCy Schubert  * @param pOD        the option descriptor for this option
1562b15cb3dSCy Schubert  * @param paz_names  the list of keywords for this option
1572b15cb3dSCy Schubert  * @param name_ct    the count of keywords
1582b15cb3dSCy Schubert  */
1592b15cb3dSCy Schubert static uintptr_t
find_name(char const * name,tOptions * pOpts,tOptDesc * pOD,char const * const * paz_names,unsigned int name_ct)1602b15cb3dSCy Schubert find_name(char const * name, tOptions * pOpts, tOptDesc * pOD,
1612b15cb3dSCy Schubert           char const * const *  paz_names, unsigned int name_ct)
1622b15cb3dSCy Schubert {
1632b15cb3dSCy Schubert     /*
1642b15cb3dSCy Schubert      *  Return the matching index as a pointer sized integer.
1652b15cb3dSCy Schubert      *  The result gets stashed in a char * pointer.
1662b15cb3dSCy Schubert      */
1672b15cb3dSCy Schubert     uintptr_t   res = name_ct;
168*a466cc55SCy Schubert     size_t      len = strlen((char *)name);
1692b15cb3dSCy Schubert     uintptr_t   idx;
1702b15cb3dSCy Schubert 
1712b15cb3dSCy Schubert     if (IS_DEC_DIGIT_CHAR(*name)) {
172*a466cc55SCy Schubert         char * pz = VOIDP(name);
173*a466cc55SCy Schubert         unsigned long val = strtoul(pz, &pz, 0);
1742b15cb3dSCy Schubert         if ((*pz == NUL) && (val < name_ct))
1752b15cb3dSCy Schubert             return (uintptr_t)val;
1762b15cb3dSCy Schubert         pz_enum_err_fmt = znum_too_large;
1772b15cb3dSCy Schubert         option_usage_fp = stderr;
1782b15cb3dSCy Schubert         enum_err(pOpts, pOD, paz_names, (int)name_ct);
1792b15cb3dSCy Schubert         return name_ct;
1802b15cb3dSCy Schubert     }
1812b15cb3dSCy Schubert 
1822b15cb3dSCy Schubert     if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) {
1832b15cb3dSCy Schubert         if (  ((name[0] == '~') && (name[1] == '0'))
1842b15cb3dSCy Schubert            || ((name[0] == '-') && (name[1] == '1')))
1852b15cb3dSCy Schubert         return (uintptr_t)(name_ct - 1);
1862b15cb3dSCy Schubert         goto oops;
1872b15cb3dSCy Schubert     }
1882b15cb3dSCy Schubert 
1892b15cb3dSCy Schubert     /*
1902b15cb3dSCy Schubert      *  Look for an exact match, but remember any partial matches.
1912b15cb3dSCy Schubert      *  Multiple partial matches means we have an ambiguous match.
1922b15cb3dSCy Schubert      */
1932b15cb3dSCy Schubert     for (idx = 0; idx < name_ct; idx++) {
194*a466cc55SCy Schubert         if (strncmp((char *)paz_names[idx], (char *)name, len) == 0) {
1952b15cb3dSCy Schubert             if (paz_names[idx][len] == NUL)
1962b15cb3dSCy Schubert                 return idx;  /* full match */
1972b15cb3dSCy Schubert 
1982b15cb3dSCy Schubert             if (res == name_ct)
1992b15cb3dSCy Schubert                 res = idx; /* save partial match */
2002b15cb3dSCy Schubert             else
2012b15cb3dSCy Schubert                 res = (uintptr_t)~0;  /* may yet find full match */
2022b15cb3dSCy Schubert         }
2032b15cb3dSCy Schubert     }
2042b15cb3dSCy Schubert 
2052b15cb3dSCy Schubert     if (res < name_ct)
2062b15cb3dSCy Schubert         return res; /* partial match */
2072b15cb3dSCy Schubert 
2082b15cb3dSCy Schubert  oops:
2092b15cb3dSCy Schubert 
2102b15cb3dSCy Schubert     pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key;
2112b15cb3dSCy Schubert     option_usage_fp = stderr;
2122b15cb3dSCy Schubert     enum_err(pOpts, pOD, paz_names, (int)name_ct);
2132b15cb3dSCy Schubert     return name_ct;
2142b15cb3dSCy Schubert }
2152b15cb3dSCy Schubert 
2162b15cb3dSCy Schubert 
2172b15cb3dSCy Schubert /*=export_func  optionKeywordName
2182b15cb3dSCy Schubert  * what:  Convert between enumeration values and strings
2192b15cb3dSCy Schubert  * private:
2202b15cb3dSCy Schubert  *
2212b15cb3dSCy Schubert  * arg:   tOptDesc *,    pOD,       enumeration option description
2222b15cb3dSCy Schubert  * arg:   unsigned int,  enum_val,  the enumeration value to map
2232b15cb3dSCy Schubert  *
2242b15cb3dSCy Schubert  * ret_type:  char const *
2252b15cb3dSCy Schubert  * ret_desc:  the enumeration name from const memory
2262b15cb3dSCy Schubert  *
2272b15cb3dSCy Schubert  * doc:   This converts an enumeration value into the matching string.
2282b15cb3dSCy Schubert =*/
2292b15cb3dSCy Schubert char const *
optionKeywordName(tOptDesc * pOD,unsigned int enum_val)2302b15cb3dSCy Schubert optionKeywordName(tOptDesc * pOD, unsigned int enum_val)
2312b15cb3dSCy Schubert {
2322b15cb3dSCy Schubert     tOptDesc od = { 0 };
2332b15cb3dSCy Schubert     od.optArg.argEnum = enum_val;
2342b15cb3dSCy Schubert 
2352b15cb3dSCy Schubert     (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
2362b15cb3dSCy Schubert     return od.optArg.argString;
2372b15cb3dSCy Schubert }
2382b15cb3dSCy Schubert 
2392b15cb3dSCy Schubert 
2402b15cb3dSCy Schubert /*=export_func  optionEnumerationVal
2412b15cb3dSCy Schubert  * what:  Convert from a string to an enumeration value
2422b15cb3dSCy Schubert  * private:
2432b15cb3dSCy Schubert  *
2442b15cb3dSCy Schubert  * arg:   tOptions *,    pOpts,     the program options descriptor
2452b15cb3dSCy Schubert  * arg:   tOptDesc *,    pOD,       enumeration option description
2462b15cb3dSCy Schubert  * arg:   char const * const *,  paz_names, list of enumeration names
2472b15cb3dSCy Schubert  * arg:   unsigned int,  name_ct,   number of names in list
2482b15cb3dSCy Schubert  *
2492b15cb3dSCy Schubert  * ret_type:  uintptr_t
2502b15cb3dSCy Schubert  * ret_desc:  the enumeration value
2512b15cb3dSCy Schubert  *
2522b15cb3dSCy Schubert  * doc:   This converts the optArg.argString string from the option description
2532b15cb3dSCy Schubert  *        into the index corresponding to an entry in the name list.
2542b15cb3dSCy Schubert  *        This will match the generated enumeration value.
2552b15cb3dSCy Schubert  *        Full matches are always accepted.  Partial matches are accepted
2562b15cb3dSCy Schubert  *        if there is only one partial match.
2572b15cb3dSCy Schubert =*/
2582b15cb3dSCy Schubert uintptr_t
optionEnumerationVal(tOptions * pOpts,tOptDesc * pOD,char const * const * paz_names,unsigned int name_ct)2592b15cb3dSCy Schubert optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
2602b15cb3dSCy Schubert                      char const * const * paz_names, unsigned int name_ct)
2612b15cb3dSCy Schubert {
2622b15cb3dSCy Schubert     uintptr_t res = 0UL;
2632b15cb3dSCy Schubert 
2642b15cb3dSCy Schubert     /*
2652b15cb3dSCy Schubert      *  IF the program option descriptor pointer is invalid,
2662b15cb3dSCy Schubert      *  then it is some sort of special request.
2672b15cb3dSCy Schubert      */
2682b15cb3dSCy Schubert     switch ((uintptr_t)pOpts) {
2692b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_EMIT_USAGE:
2702b15cb3dSCy Schubert         /*
2712b15cb3dSCy Schubert          *  print the list of enumeration names.
2722b15cb3dSCy Schubert          */
2732b15cb3dSCy Schubert         enum_err(pOpts, pOD, paz_names, (int)name_ct);
2742b15cb3dSCy Schubert         break;
2752b15cb3dSCy Schubert 
2762b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_EMIT_SHELL:
2772b15cb3dSCy Schubert     {
2782b15cb3dSCy Schubert         unsigned int ix = (unsigned int)pOD->optArg.argEnum;
2792b15cb3dSCy Schubert         /*
2802b15cb3dSCy Schubert          *  print the name string.
2812b15cb3dSCy Schubert          */
2822b15cb3dSCy Schubert         if (ix >= name_ct)
2832b15cb3dSCy Schubert             printf(INVALID_FMT, ix);
2842b15cb3dSCy Schubert         else
2852b15cb3dSCy Schubert             fputs(paz_names[ ix ], stdout);
2862b15cb3dSCy Schubert 
2872b15cb3dSCy Schubert         break;
2882b15cb3dSCy Schubert     }
2892b15cb3dSCy Schubert 
2902b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_RETURN_VALNAME:
2912b15cb3dSCy Schubert     {
2922b15cb3dSCy Schubert         unsigned int ix = (unsigned int)pOD->optArg.argEnum;
2932b15cb3dSCy Schubert         /*
2942b15cb3dSCy Schubert          *  Replace the enumeration value with the name string.
2952b15cb3dSCy Schubert          */
2962b15cb3dSCy Schubert         if (ix >= name_ct)
2972b15cb3dSCy Schubert             return (uintptr_t)INVALID_STR;
2982b15cb3dSCy Schubert 
2992b15cb3dSCy Schubert         pOD->optArg.argString = paz_names[ix];
3002b15cb3dSCy Schubert         break;
3012b15cb3dSCy Schubert     }
3022b15cb3dSCy Schubert 
3032b15cb3dSCy Schubert     default:
3042b15cb3dSCy Schubert         if ((pOD->fOptState & OPTST_RESET) != 0)
3052b15cb3dSCy Schubert             break;
3062b15cb3dSCy Schubert 
3072b15cb3dSCy Schubert         res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
3082b15cb3dSCy Schubert 
3092b15cb3dSCy Schubert         if (pOD->fOptState & OPTST_ALLOC_ARG) {
3102b15cb3dSCy Schubert             AGFREE(pOD->optArg.argString);
3112b15cb3dSCy Schubert             pOD->fOptState &= ~OPTST_ALLOC_ARG;
3122b15cb3dSCy Schubert             pOD->optArg.argString = NULL;
3132b15cb3dSCy Schubert         }
3142b15cb3dSCy Schubert     }
3152b15cb3dSCy Schubert 
3162b15cb3dSCy Schubert     return res;
3172b15cb3dSCy Schubert }
3182b15cb3dSCy Schubert 
3192b15cb3dSCy Schubert static void
set_memb_shell(tOptions * pOpts,tOptDesc * pOD,char const * const * paz_names,unsigned int name_ct)3202b15cb3dSCy Schubert set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
3212b15cb3dSCy Schubert                unsigned int name_ct)
3222b15cb3dSCy Schubert {
3232b15cb3dSCy Schubert     /*
3242b15cb3dSCy Schubert      *  print the name string.
3252b15cb3dSCy Schubert      */
3262b15cb3dSCy Schubert     unsigned int ix =  0;
3272b15cb3dSCy Schubert     uintptr_t  bits = (uintptr_t)pOD->optCookie;
3282b15cb3dSCy Schubert     size_t     len  = 0;
3292b15cb3dSCy Schubert 
3302b15cb3dSCy Schubert     (void)pOpts;
3312b15cb3dSCy Schubert     bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
3322b15cb3dSCy Schubert 
3332b15cb3dSCy Schubert     while (bits != 0) {
3342b15cb3dSCy Schubert         if (bits & 1) {
3352b15cb3dSCy Schubert             if (len++ > 0) fputs(OR_STR, stdout);
3362b15cb3dSCy Schubert             fputs(paz_names[ix], stdout);
3372b15cb3dSCy Schubert         }
3382b15cb3dSCy Schubert         if (++ix >= name_ct) break;
3392b15cb3dSCy Schubert         bits >>= 1;
3402b15cb3dSCy Schubert     }
3412b15cb3dSCy Schubert }
3422b15cb3dSCy Schubert 
3432b15cb3dSCy Schubert static void
set_memb_names(tOptions * opts,tOptDesc * od,char const * const * nm_list,unsigned int nm_ct)3442b15cb3dSCy Schubert set_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
3452b15cb3dSCy Schubert                unsigned int nm_ct)
3462b15cb3dSCy Schubert {
3472b15cb3dSCy Schubert     char *     pz;
3482b15cb3dSCy Schubert     uintptr_t  mask = (1UL << (uintptr_t)nm_ct) - 1UL;
3492b15cb3dSCy Schubert     uintptr_t  bits = (uintptr_t)od->optCookie & mask;
3502b15cb3dSCy Schubert     unsigned int ix = 0;
3512b15cb3dSCy Schubert     size_t     len  = 1;
3522b15cb3dSCy Schubert 
3532b15cb3dSCy Schubert     /*
3542b15cb3dSCy Schubert      *  Replace the enumeration value with the name string.
3552b15cb3dSCy Schubert      *  First, determine the needed length, then allocate and fill in.
3562b15cb3dSCy Schubert      */
3572b15cb3dSCy Schubert     while (bits != 0) {
3582b15cb3dSCy Schubert         if (bits & 1)
3592b15cb3dSCy Schubert             len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1;
3602b15cb3dSCy Schubert         if (++ix >= nm_ct) break;
3612b15cb3dSCy Schubert         bits >>= 1;
3622b15cb3dSCy Schubert     }
3632b15cb3dSCy Schubert 
3642b15cb3dSCy Schubert     od->optArg.argString = pz = AGALOC(len, "enum");
3652b15cb3dSCy Schubert     bits = (uintptr_t)od->optCookie & mask;
3662b15cb3dSCy Schubert     if (bits == 0) {
3672b15cb3dSCy Schubert         *pz = NUL;
3682b15cb3dSCy Schubert         return;
3692b15cb3dSCy Schubert     }
3702b15cb3dSCy Schubert 
3712b15cb3dSCy Schubert     for (ix = 0; ; ix++) {
3722b15cb3dSCy Schubert         size_t nln;
3732b15cb3dSCy Schubert         int    doit = bits & 1;
3742b15cb3dSCy Schubert 
3752b15cb3dSCy Schubert         bits >>= 1;
3762b15cb3dSCy Schubert         if (doit == 0)
3772b15cb3dSCy Schubert             continue;
3782b15cb3dSCy Schubert 
3792b15cb3dSCy Schubert         nln = strlen(nm_list[ix]);
3802b15cb3dSCy Schubert         memcpy(pz, nm_list[ix], nln);
3812b15cb3dSCy Schubert         pz += nln;
3822b15cb3dSCy Schubert         if (bits == 0)
3832b15cb3dSCy Schubert             break;
3842b15cb3dSCy Schubert         memcpy(pz, PLUS_STR, PLUS_STR_LEN);
3852b15cb3dSCy Schubert         pz += PLUS_STR_LEN;
3862b15cb3dSCy Schubert     }
3872b15cb3dSCy Schubert     *pz = NUL;
3882b15cb3dSCy Schubert     (void)opts;
3892b15cb3dSCy Schubert }
3902b15cb3dSCy Schubert 
3912b15cb3dSCy Schubert /**
3922b15cb3dSCy Schubert  * Check membership start conditions.  An equal character (@samp{=}) says to
3932b15cb3dSCy Schubert  * clear the result and not carry over any residual value.  A carat
3942b15cb3dSCy Schubert  * (@samp{^}), which may follow the equal character, says to invert the
3952b15cb3dSCy Schubert  * result.  The scanning pointer is advanced past these characters and any
3962b15cb3dSCy Schubert  * leading white space.  Invalid sequences are indicated by setting the
3972b15cb3dSCy Schubert  * scanning pointer to NULL.
3982b15cb3dSCy Schubert  *
3992b15cb3dSCy Schubert  * @param od      the set membership option description
4002b15cb3dSCy Schubert  * @param argp    a pointer to the string scanning pointer
4012b15cb3dSCy Schubert  * @param invert  a pointer to the boolean inversion indicator
4022b15cb3dSCy Schubert  *
4032b15cb3dSCy Schubert  * @returns either zero or the original value for the optCookie.
4042b15cb3dSCy Schubert  */
4052b15cb3dSCy Schubert static uintptr_t
check_membership_start(tOptDesc * od,char const ** argp,bool * invert)4062b15cb3dSCy Schubert check_membership_start(tOptDesc * od, char const ** argp, bool * invert)
4072b15cb3dSCy Schubert {
4082b15cb3dSCy Schubert     uintptr_t    res = (uintptr_t)od->optCookie;
4092b15cb3dSCy Schubert     char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
4102b15cb3dSCy Schubert     if ((arg == NULL) || (*arg == NUL))
4112b15cb3dSCy Schubert         goto member_start_fail;
4122b15cb3dSCy Schubert 
4132b15cb3dSCy Schubert     *invert = false;
4142b15cb3dSCy Schubert 
4152b15cb3dSCy Schubert     switch (*arg) {
4162b15cb3dSCy Schubert     case '=':
4172b15cb3dSCy Schubert         res = 0UL;
4182b15cb3dSCy Schubert         arg = SPN_WHITESPACE_CHARS(arg + 1);
4192b15cb3dSCy Schubert         switch (*arg) {
4202b15cb3dSCy Schubert         case '=': case ',':
4212b15cb3dSCy Schubert             goto member_start_fail;
4222b15cb3dSCy Schubert         case '^':
4232b15cb3dSCy Schubert             goto inversion;
4242b15cb3dSCy Schubert         default:
4252b15cb3dSCy Schubert             break;
4262b15cb3dSCy Schubert         }
4272b15cb3dSCy Schubert         break;
4282b15cb3dSCy Schubert 
4292b15cb3dSCy Schubert     case '^':
4302b15cb3dSCy Schubert     inversion:
4312b15cb3dSCy Schubert         *invert = true;
4322b15cb3dSCy Schubert         arg = SPN_WHITESPACE_CHARS(arg + 1);
4332b15cb3dSCy Schubert         if (*arg != ',')
4342b15cb3dSCy Schubert             break;
4352b15cb3dSCy Schubert         /* FALLTHROUGH */
4362b15cb3dSCy Schubert 
4372b15cb3dSCy Schubert     case ',':
4382b15cb3dSCy Schubert         goto member_start_fail;
4392b15cb3dSCy Schubert 
4402b15cb3dSCy Schubert     default:
4412b15cb3dSCy Schubert         break;
4422b15cb3dSCy Schubert     }
4432b15cb3dSCy Schubert 
4442b15cb3dSCy Schubert     *argp = arg;
4452b15cb3dSCy Schubert     return res;
4462b15cb3dSCy Schubert 
4472b15cb3dSCy Schubert member_start_fail:
4482b15cb3dSCy Schubert     *argp = NULL;
4492b15cb3dSCy Schubert     return 0UL;
4502b15cb3dSCy Schubert }
4512b15cb3dSCy Schubert 
4522b15cb3dSCy Schubert /**
4532b15cb3dSCy Schubert  * convert a name to a bit.  Look up a name string to get a bit number
4542b15cb3dSCy Schubert  * and shift the value "1" left that number of bits.
4552b15cb3dSCy Schubert  *
4562b15cb3dSCy Schubert  * @param opts      program options descriptor
4572b15cb3dSCy Schubert  * @param od        the set membership option description
4582b15cb3dSCy Schubert  * @param pz        address of the start of the bit name
4592b15cb3dSCy Schubert  * @param nm_list   the list of names for this option
4602b15cb3dSCy Schubert  * @param nm_ct     the number of entries in this list
4612b15cb3dSCy Schubert  *
4622b15cb3dSCy Schubert  * @returns 0UL on error, other an unsigned long with the correct bit set.
4632b15cb3dSCy Schubert  */
4642b15cb3dSCy Schubert static uintptr_t
find_member_bit(tOptions * opts,tOptDesc * od,char const * pz,int len,char const * const * nm_list,unsigned int nm_ct)4652b15cb3dSCy Schubert find_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
4662b15cb3dSCy Schubert                 char const * const * nm_list, unsigned int nm_ct)
4672b15cb3dSCy Schubert {
4682b15cb3dSCy Schubert     char nm_buf[ AO_NAME_SIZE ];
4692b15cb3dSCy Schubert 
4702b15cb3dSCy Schubert     memcpy(nm_buf, pz, len);
4712b15cb3dSCy Schubert     nm_buf[len] = NUL;
4722b15cb3dSCy Schubert 
4732b15cb3dSCy Schubert     {
4742b15cb3dSCy Schubert         unsigned int shift_ct = (unsigned int)
4752b15cb3dSCy Schubert             find_name(nm_buf, opts, od, nm_list, nm_ct);
4762b15cb3dSCy Schubert         if (shift_ct >= nm_ct)
4772b15cb3dSCy Schubert             return 0UL;
4782b15cb3dSCy Schubert 
479*a466cc55SCy Schubert         return 1UL << shift_ct;
4802b15cb3dSCy Schubert     }
4812b15cb3dSCy Schubert }
4822b15cb3dSCy Schubert 
4832b15cb3dSCy Schubert /*=export_func  optionMemberList
4842b15cb3dSCy Schubert  * what:  Get the list of members of a bit mask set
4852b15cb3dSCy Schubert  *
4862b15cb3dSCy Schubert  * arg:   tOptDesc *,  od,   the set membership option description
4872b15cb3dSCy Schubert  *
4882b15cb3dSCy Schubert  * ret_type: char *
4892b15cb3dSCy Schubert  * ret_desc: the names of the set bits
4902b15cb3dSCy Schubert  *
4912b15cb3dSCy Schubert  * doc:   This converts the OPT_VALUE_name mask value to a allocated string.
4922b15cb3dSCy Schubert  *        It is the caller's responsibility to free the string.
4932b15cb3dSCy Schubert =*/
4942b15cb3dSCy Schubert char *
optionMemberList(tOptDesc * od)4952b15cb3dSCy Schubert optionMemberList(tOptDesc * od)
4962b15cb3dSCy Schubert {
4972b15cb3dSCy Schubert     uintptr_t    sv = od->optArg.argIntptr;
4982b15cb3dSCy Schubert     char * res;
4992b15cb3dSCy Schubert     (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
500276da39aSCy Schubert     res = VOIDP(od->optArg.argString);
5012b15cb3dSCy Schubert     od->optArg.argIntptr = sv;
5022b15cb3dSCy Schubert     return res;
5032b15cb3dSCy Schubert }
5042b15cb3dSCy Schubert 
5052b15cb3dSCy Schubert /*=export_func  optionSetMembers
5062b15cb3dSCy Schubert  * what:  Convert between bit flag values and strings
5072b15cb3dSCy Schubert  * private:
5082b15cb3dSCy Schubert  *
5092b15cb3dSCy Schubert  * arg:   tOptions *,     opts,     the program options descriptor
5102b15cb3dSCy Schubert  * arg:   tOptDesc *,     od,       the set membership option description
5112b15cb3dSCy Schubert  * arg:   char const * const *,
5122b15cb3dSCy Schubert  *                       nm_list,  list of enumeration names
5132b15cb3dSCy Schubert  * arg:   unsigned int,  nm_ct,    number of names in list
5142b15cb3dSCy Schubert  *
5152b15cb3dSCy Schubert  * doc:   This converts the optArg.argString string from the option description
5162b15cb3dSCy Schubert  *        into the index corresponding to an entry in the name list.
5172b15cb3dSCy Schubert  *        This will match the generated enumeration value.
5182b15cb3dSCy Schubert  *        Full matches are always accepted.  Partial matches are accepted
5192b15cb3dSCy Schubert  *        if there is only one partial match.
5202b15cb3dSCy Schubert =*/
5212b15cb3dSCy Schubert void
optionSetMembers(tOptions * opts,tOptDesc * od,char const * const * nm_list,unsigned int nm_ct)5222b15cb3dSCy Schubert optionSetMembers(tOptions * opts, tOptDesc * od,
5232b15cb3dSCy Schubert                  char const * const * nm_list, unsigned int nm_ct)
5242b15cb3dSCy Schubert {
5252b15cb3dSCy Schubert     /*
5262b15cb3dSCy Schubert      *  IF the program option descriptor pointer is invalid,
5272b15cb3dSCy Schubert      *  then it is some sort of special request.
5282b15cb3dSCy Schubert      */
5292b15cb3dSCy Schubert     switch ((uintptr_t)opts) {
5302b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_EMIT_USAGE:
5312b15cb3dSCy Schubert         enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
5322b15cb3dSCy Schubert         return;
5332b15cb3dSCy Schubert 
5342b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_EMIT_SHELL:
5352b15cb3dSCy Schubert         set_memb_shell(opts, od, nm_list, nm_ct);
5362b15cb3dSCy Schubert         return;
5372b15cb3dSCy Schubert 
5382b15cb3dSCy Schubert     case (uintptr_t)OPTPROC_RETURN_VALNAME:
5392b15cb3dSCy Schubert         set_memb_names(opts, od, nm_list, nm_ct);
5402b15cb3dSCy Schubert         return;
5412b15cb3dSCy Schubert 
5422b15cb3dSCy Schubert     default:
5432b15cb3dSCy Schubert         break;
5442b15cb3dSCy Schubert     }
5452b15cb3dSCy Schubert 
5462b15cb3dSCy Schubert     if ((od->fOptState & OPTST_RESET) != 0)
5472b15cb3dSCy Schubert         return;
5482b15cb3dSCy Schubert 
5492b15cb3dSCy Schubert     {
5502b15cb3dSCy Schubert         char const * arg;
5512b15cb3dSCy Schubert         bool         invert;
5522b15cb3dSCy Schubert         uintptr_t    res = check_membership_start(od, &arg, &invert);
5532b15cb3dSCy Schubert         if (arg == NULL)
5542b15cb3dSCy Schubert             goto fail_return;
5552b15cb3dSCy Schubert 
5562b15cb3dSCy Schubert         while (*arg != NUL) {
5572b15cb3dSCy Schubert             bool inv_val = false;
5582b15cb3dSCy Schubert             int  len;
5592b15cb3dSCy Schubert 
5602b15cb3dSCy Schubert             switch (*arg) {
5612b15cb3dSCy Schubert             case ',':
5622b15cb3dSCy Schubert                 arg = SPN_WHITESPACE_CHARS(arg+1);
5632b15cb3dSCy Schubert                 if ((*arg == ',') || (*arg == '|'))
5642b15cb3dSCy Schubert                     goto fail_return;
5652b15cb3dSCy Schubert                 continue;
5662b15cb3dSCy Schubert 
5672b15cb3dSCy Schubert             case '-':
5682b15cb3dSCy Schubert             case '!':
5692b15cb3dSCy Schubert                 inv_val = true;
5702b15cb3dSCy Schubert                 /* FALLTHROUGH */
5712b15cb3dSCy Schubert 
5722b15cb3dSCy Schubert             case '+':
5732b15cb3dSCy Schubert             case '|':
5742b15cb3dSCy Schubert                 arg = SPN_WHITESPACE_CHARS(arg+1);
5752b15cb3dSCy Schubert             }
5762b15cb3dSCy Schubert 
5772b15cb3dSCy Schubert             len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
5782b15cb3dSCy Schubert             if (len == 0)
5792b15cb3dSCy Schubert                 break;
5802b15cb3dSCy Schubert 
5812b15cb3dSCy Schubert             if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
5822b15cb3dSCy Schubert                 if (inv_val)
5832b15cb3dSCy Schubert                      res = 0;
5842b15cb3dSCy Schubert                 else res = ~0UL;
5852b15cb3dSCy Schubert             }
5862b15cb3dSCy Schubert             else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
5872b15cb3dSCy Schubert                 if (! inv_val)
5882b15cb3dSCy Schubert                     res = 0;
5892b15cb3dSCy Schubert             }
5902b15cb3dSCy Schubert             else do {
5912b15cb3dSCy Schubert                 char *    pz;
5922b15cb3dSCy Schubert                 uintptr_t bit = strtoul(arg, &pz, 0);
5932b15cb3dSCy Schubert 
5942b15cb3dSCy Schubert                 if (pz != arg + len) {
5952b15cb3dSCy Schubert                     bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
5962b15cb3dSCy Schubert                     if (bit == 0UL)
5972b15cb3dSCy Schubert                         goto fail_return;
5982b15cb3dSCy Schubert                 }
5992b15cb3dSCy Schubert                 if (inv_val)
6002b15cb3dSCy Schubert                      res &= ~bit;
6012b15cb3dSCy Schubert                 else res |= bit;
6022b15cb3dSCy Schubert             } while (false);
6032b15cb3dSCy Schubert 
6042b15cb3dSCy Schubert             arg = SPN_WHITESPACE_CHARS(arg + len);
6052b15cb3dSCy Schubert         }
6062b15cb3dSCy Schubert 
6072b15cb3dSCy Schubert         if (invert)
6082b15cb3dSCy Schubert             res ^= ~0UL;
6092b15cb3dSCy Schubert 
6102b15cb3dSCy Schubert         if (nm_ct < (8 * sizeof(uintptr_t)))
6112b15cb3dSCy Schubert             res &= (1UL << nm_ct) - 1UL;
6122b15cb3dSCy Schubert 
613276da39aSCy Schubert         od->optCookie = VOIDP(res);
6142b15cb3dSCy Schubert     }
6152b15cb3dSCy Schubert     return;
6162b15cb3dSCy Schubert 
6172b15cb3dSCy Schubert fail_return:
618276da39aSCy Schubert     od->optCookie = VOIDP(0);
6192b15cb3dSCy Schubert }
6202b15cb3dSCy Schubert 
6212b15cb3dSCy Schubert /** @}
6222b15cb3dSCy Schubert  *
6232b15cb3dSCy Schubert  * Local Variables:
6242b15cb3dSCy Schubert  * mode: C
6252b15cb3dSCy Schubert  * c-file-style: "stroustrup"
6262b15cb3dSCy Schubert  * indent-tabs-mode: nil
6272b15cb3dSCy Schubert  * End:
6282b15cb3dSCy Schubert  * end of autoopts/enum.c */
629