Home | History | Annotate | Line # | Download | only in libopts
      1  1.8  christos /*	$NetBSD: find.c,v 1.9 2024/08/18 20:47:24 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /**
      4  1.1  christos  * @file check.c
      5  1.1  christos  *
      6  1.1  christos  * @brief Hunt for options in the option descriptor list
      7  1.1  christos  *
      8  1.1  christos  *  This file contains the routines that deal with processing quoted strings
      9  1.1  christos  *  into an internal format.
     10  1.1  christos  *
     11  1.1  christos  * @addtogroup autoopts
     12  1.1  christos  * @{
     13  1.1  christos  */
     14  1.1  christos /*
     15  1.1  christos  *  This file is part of AutoOpts, a companion to AutoGen.
     16  1.1  christos  *  AutoOpts is free software.
     17  1.9  christos  *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
     18  1.1  christos  *
     19  1.1  christos  *  AutoOpts is available under any one of two licenses.  The license
     20  1.1  christos  *  in use must be one of these two and the choice is under the control
     21  1.1  christos  *  of the user of the license.
     22  1.1  christos  *
     23  1.1  christos  *   The GNU Lesser General Public License, version 3 or later
     24  1.1  christos  *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
     25  1.1  christos  *
     26  1.1  christos  *   The Modified Berkeley Software Distribution License
     27  1.1  christos  *      See the file "COPYING.mbsd"
     28  1.1  christos  *
     29  1.1  christos  *  These files have the following sha256 sums:
     30  1.1  christos  *
     31  1.1  christos  *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
     32  1.1  christos  *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
     33  1.1  christos  *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
     34  1.1  christos  */
     35  1.1  christos 
     36  1.1  christos /**
     37  1.1  christos  * find the name and name length we are looking for
     38  1.1  christos  */
     39  1.1  christos static int
     40  1.1  christos parse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz)
     41  1.1  christos {
     42  1.1  christos     int  res = 0;
     43  1.1  christos     char const * p = *nm_pp;
     44  1.1  christos     *arg_pp  = NULL;
     45  1.1  christos 
     46  1.1  christos     for (;;) {
     47  1.1  christos         switch (*(p++)) {
     48  1.1  christos         case NUL: return res;
     49  1.1  christos 
     50  1.1  christos         case '=':
     51  1.1  christos             memcpy(buf, *nm_pp, (size_t)res);
     52  1.1  christos 
     53  1.1  christos             buf[res] = NUL;
     54  1.1  christos             *nm_pp   = buf;
     55  1.9  christos             *arg_pp  = __UNCONST(p);
     56  1.1  christos             return res;
     57  1.1  christos 
     58  1.1  christos         default:
     59  1.1  christos             if (++res >= (int)bufsz)
     60  1.1  christos                 return -1;
     61  1.1  christos         }
     62  1.1  christos     }
     63  1.1  christos }
     64  1.1  christos 
     65  1.1  christos /**
     66  1.1  christos  *  print out the options that match the given name.
     67  1.1  christos  *
     68  1.1  christos  * @param pOpts      option data
     69  1.1  christos  * @param opt_name   name of option to look for
     70  1.1  christos  */
     71  1.1  christos static void
     72  1.1  christos opt_ambiguities(tOptions * opts, char const * name, int nm_len)
     73  1.1  christos {
     74  1.1  christos     char const * const hyph =
     75  1.1  christos         NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER;
     76  1.1  christos 
     77  1.1  christos     tOptDesc * pOD = opts->pOptDesc;
     78  1.1  christos     int        idx = 0;
     79  1.1  christos 
     80  1.1  christos     fputs(zambig_list_msg, stderr);
     81  1.1  christos     do  {
     82  1.1  christos         if (pOD->pz_Name == NULL)
     83  1.1  christos             continue; /* doc option */
     84  1.1  christos 
     85  1.1  christos         if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0)
     86  1.1  christos             fprintf(stderr, zambig_file, hyph, pOD->pz_Name);
     87  1.1  christos 
     88  1.1  christos         else if (  (pOD->pz_DisableName != NULL)
     89  1.1  christos                 && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
     90  1.1  christos                 )
     91  1.1  christos             fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName);
     92  1.1  christos     } while (pOD++, (++idx < opts->optCt));
     93  1.1  christos }
     94  1.1  christos 
     95  1.1  christos /**
     96  1.1  christos  *  Determine the number of options that match the name
     97  1.1  christos  *
     98  1.1  christos  * @param pOpts      option data
     99  1.1  christos  * @param opt_name   name of option to look for
    100  1.1  christos  * @param nm_len     length of provided name
    101  1.1  christos  * @param index      pointer to int for option index
    102  1.1  christos  * @param disable    pointer to bool to mark disabled option
    103  1.1  christos  * @return count of options that match
    104  1.1  christos  */
    105  1.1  christos static int
    106  1.1  christos opt_match_ct(tOptions * opts, char const * name, int nm_len,
    107  1.1  christos              int * ixp, bool * disable)
    108  1.1  christos {
    109  1.1  christos     int   matchCt  = 0;
    110  1.1  christos     int   idx      = 0;
    111  1.1  christos     int   idxLim   = opts->optCt;
    112  1.1  christos     tOptDesc * pOD = opts->pOptDesc;
    113  1.1  christos 
    114  1.1  christos     do  {
    115  1.1  christos         /*
    116  1.1  christos          *  If option disabled or a doc option, skip to next
    117  1.1  christos          */
    118  1.1  christos         if (pOD->pz_Name == NULL)
    119  1.1  christos             continue;
    120  1.1  christos 
    121  1.1  christos         if (  SKIP_OPT(pOD)
    122  1.1  christos            && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT)))
    123  1.1  christos             continue;
    124  1.1  christos 
    125  1.1  christos         if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) {
    126  1.1  christos             /*
    127  1.1  christos              *  IF we have a complete match
    128  1.1  christos              *  THEN it takes priority over any already located partial
    129  1.1  christos              */
    130  1.1  christos             if (pOD->pz_Name[ nm_len ] == NUL) {
    131  1.1  christos                 *ixp = idx;
    132  1.1  christos                 return 1;
    133  1.1  christos             }
    134  1.1  christos         }
    135  1.1  christos 
    136  1.1  christos         /*
    137  1.1  christos          *  IF       there is a disable name
    138  1.1  christos          *     *AND* the option name matches the disable name
    139  1.1  christos          *  THEN ...
    140  1.1  christos          */
    141  1.1  christos         else if (  (pOD->pz_DisableName != NULL)
    142  1.1  christos                 && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
    143  1.1  christos                 )  {
    144  1.1  christos             *disable = true;
    145  1.1  christos 
    146  1.1  christos             /*
    147  1.1  christos              *  IF we have a complete match
    148  1.1  christos              *  THEN it takes priority over any already located partial
    149  1.1  christos              */
    150  1.1  christos             if (pOD->pz_DisableName[ nm_len ] == NUL) {
    151  1.1  christos                 *ixp = idx;
    152  1.1  christos                 return 1;
    153  1.1  christos             }
    154  1.1  christos         }
    155  1.1  christos 
    156  1.1  christos         else
    157  1.1  christos             continue; /* does not match any option */
    158  1.1  christos 
    159  1.1  christos         /*
    160  1.1  christos          *  We found a full or partial match, either regular or disabling.
    161  1.1  christos          *  Remember the index for later.
    162  1.1  christos          */
    163  1.1  christos         *ixp = idx;
    164  1.1  christos         ++matchCt;
    165  1.1  christos 
    166  1.1  christos     } while (pOD++, (++idx < idxLim));
    167  1.1  christos 
    168  1.1  christos     return matchCt;
    169  1.1  christos }
    170  1.1  christos 
    171  1.1  christos /**
    172  1.1  christos  *  Set the option to the indicated option number.
    173  1.1  christos  *
    174  1.1  christos  * @param opts      option data
    175  1.1  christos  * @param arg       option argument (if glued to name)
    176  1.1  christos  * @param idx       option index
    177  1.1  christos  * @param disable   mark disabled option
    178  1.1  christos  * @param st        state about current option
    179  1.1  christos  */
    180  1.1  christos static tSuccess
    181  1.1  christos opt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st)
    182  1.1  christos {
    183  1.1  christos     tOptDesc * pOD = opts->pOptDesc + idx;
    184  1.1  christos 
    185  1.1  christos     if (SKIP_OPT(pOD)) {
    186  1.1  christos         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
    187  1.1  christos             return FAILURE;
    188  1.1  christos 
    189  1.1  christos         fprintf(stderr, zDisabledErr, opts->pzProgName, pOD->pz_Name);
    190  1.1  christos         if (pOD->pzText != NULL)
    191  1.1  christos             fprintf(stderr, SET_OFF_FMT, pOD->pzText);
    192  1.1  christos         fputc(NL, stderr);
    193  1.1  christos         (*opts->pUsageProc)(opts, EXIT_FAILURE);
    194  1.1  christos         /* NOTREACHED */
    195  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    196  1.1  christos     }
    197  1.1  christos 
    198  1.1  christos     /*
    199  1.1  christos      *  IF we found a disablement name,
    200  1.1  christos      *  THEN set the bit in the callers' flag word
    201  1.1  christos      */
    202  1.1  christos     if (disable)
    203  1.1  christos         st->flags |= OPTST_DISABLED;
    204  1.1  christos 
    205  1.1  christos     st->pOD      = pOD;
    206  1.1  christos     st->pzOptArg = arg;
    207  1.1  christos     st->optType  = TOPT_LONG;
    208  1.1  christos 
    209  1.1  christos     return SUCCESS;
    210  1.1  christos }
    211  1.1  christos 
    212  1.1  christos /**
    213  1.1  christos  *  An option was not found.  Check for default option and set it
    214  1.1  christos  *  if there is one.  Otherwise, handle the error.
    215  1.1  christos  *
    216  1.1  christos  * @param opts   option data
    217  1.1  christos  * @param name   name of option to look for
    218  1.1  christos  * @param arg    option argument
    219  1.1  christos  * @param st     state about current option
    220  1.1  christos  *
    221  1.1  christos  * @return success status
    222  1.1  christos  */
    223  1.1  christos static tSuccess
    224  1.1  christos opt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st)
    225  1.1  christos {
    226  1.1  christos     /*
    227  1.1  christos      *  IF there is no equal sign
    228  1.1  christos      *     *AND* we are using named arguments
    229  1.1  christos      *     *AND* there is a default named option,
    230  1.1  christos      *  THEN return that option.
    231  1.1  christos      */
    232  1.1  christos     if (  (arg == NULL)
    233  1.1  christos        && NAMED_OPTS(opts)
    234  1.1  christos        && (opts->specOptIdx.default_opt != NO_EQUIVALENT)) {
    235  1.1  christos 
    236  1.1  christos         st->pOD      = opts->pOptDesc + opts->specOptIdx.default_opt;
    237  1.1  christos         st->pzOptArg = name;
    238  1.1  christos         st->optType  = TOPT_DEFAULT;
    239  1.1  christos         return SUCCESS;
    240  1.1  christos     }
    241  1.1  christos 
    242  1.1  christos     if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
    243  1.1  christos         fprintf(stderr, zIllOptStr, opts->pzProgPath, name);
    244  1.1  christos         (*opts->pUsageProc)(opts, EXIT_FAILURE);
    245  1.1  christos         /* NOTREACHED */
    246  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    247  1.1  christos     }
    248  1.1  christos 
    249  1.1  christos     return FAILURE;
    250  1.1  christos }
    251  1.1  christos 
    252  1.1  christos /**
    253  1.1  christos  *  Several options match the provided name.
    254  1.1  christos  *
    255  1.1  christos  * @param opts      option data
    256  1.1  christos  * @param name      name of option to look for
    257  1.1  christos  * @param match_ct  number of matching options
    258  1.1  christos  *
    259  1.1  christos  * @return success status (always FAILURE, if it returns)
    260  1.1  christos  */
    261  1.1  christos static tSuccess
    262  1.1  christos opt_ambiguous(tOptions * opts, char const * name, int match_ct)
    263  1.1  christos {
    264  1.1  christos     if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
    265  1.1  christos         fprintf(stderr, zambig_opt_fmt, opts->pzProgPath, name, match_ct);
    266  1.1  christos         if (match_ct <= 4)
    267  1.1  christos             opt_ambiguities(opts, name, (int)strlen(name));
    268  1.1  christos         (*opts->pUsageProc)(opts, EXIT_FAILURE);
    269  1.1  christos         /* NOTREACHED */
    270  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    271  1.1  christos     }
    272  1.1  christos     return FAILURE;
    273  1.1  christos }
    274  1.1  christos 
    275  1.1  christos /*=export_func  optionVendorOption
    276  1.1  christos  * private:
    277  1.1  christos  *
    278  1.1  christos  * what:  Process a vendor option
    279  1.1  christos  * arg:   + tOptions * + pOpts    + program options descriptor +
    280  1.1  christos  * arg:   + tOptDesc * + pOptDesc + the descriptor for this arg +
    281  1.1  christos  *
    282  1.1  christos  * doc:
    283  1.1  christos  *  For POSIX specified utilities, the options are constrained to the options,
    284  1.1  christos  *  @xref{config attributes, Program Configuration}.  AutoOpts clients should
    285  1.1  christos  *  never specify this directly.  It gets referenced when the option
    286  1.1  christos  *  definitions contain a "vendor-opt" attribute.
    287  1.1  christos =*/
    288  1.1  christos void
    289  1.1  christos optionVendorOption(tOptions * pOpts, tOptDesc * pOD)
    290  1.1  christos {
    291  1.1  christos     tOptState     opt_st   = OPTSTATE_INITIALIZER(PRESET);
    292  1.1  christos     char const *  vopt_str = pOD->optArg.argString;
    293  1.1  christos 
    294  1.1  christos     if (pOpts <= OPTPROC_EMIT_LIMIT)
    295  1.1  christos         return;
    296  1.1  christos 
    297  1.1  christos     if ((pOD->fOptState & OPTST_RESET) != 0)
    298  1.1  christos         return;
    299  1.1  christos 
    300  1.1  christos     if ((pOD->fOptState & OPTPROC_IMMEDIATE) == 0)
    301  1.1  christos         opt_st.flags = OPTST_DEFINED;
    302  1.1  christos 
    303  1.1  christos     if (  ((pOpts->fOptSet & OPTPROC_VENDOR_OPT) == 0)
    304  1.1  christos        || ! SUCCESSFUL(opt_find_long(pOpts, vopt_str, &opt_st))
    305  1.1  christos        || ! SUCCESSFUL(get_opt_arg(pOpts, &opt_st)) )
    306  1.1  christos     {
    307  1.1  christos         fprintf(stderr, zIllVendOptStr, pOpts->pzProgName, vopt_str);
    308  1.1  christos         (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
    309  1.1  christos         /* NOTREACHED */
    310  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    311  1.1  christos     }
    312  1.1  christos 
    313  1.1  christos     /*
    314  1.1  christos      *  See if we are in immediate handling state.
    315  1.1  christos      */
    316  1.1  christos     if (pOpts->fOptSet & OPTPROC_IMMEDIATE) {
    317  1.1  christos         /*
    318  1.1  christos          *  See if the enclosed option is okay with that state.
    319  1.1  christos          */
    320  1.1  christos         if (DO_IMMEDIATELY(opt_st.flags))
    321  1.1  christos             (void)handle_opt(pOpts, &opt_st);
    322  1.1  christos 
    323  1.1  christos     } else {
    324  1.1  christos         /*
    325  1.1  christos          *  non-immediate direction.
    326  1.1  christos          *  See if the enclosed option is okay with that state.
    327  1.1  christos          */
    328  1.1  christos         if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags))
    329  1.1  christos             (void)handle_opt(pOpts, &opt_st);
    330  1.1  christos     }
    331  1.1  christos }
    332  1.1  christos 
    333  1.1  christos /**
    334  1.1  christos  *  Find the option descriptor by full name.
    335  1.1  christos  *
    336  1.1  christos  * @param opts      option data
    337  1.1  christos  * @param opt_name  name of option to look for
    338  1.1  christos  * @param state     state about current option
    339  1.1  christos  *
    340  1.1  christos  * @return success status
    341  1.1  christos  */
    342  1.9  christos static tSuccess
    343  1.1  christos opt_find_long(tOptions * opts, char const * opt_name, tOptState * state)
    344  1.1  christos {
    345  1.1  christos     char    name_buf[128];
    346  1.1  christos     char *  opt_arg;
    347  1.1  christos     int     nm_len = parse_opt(&opt_name, &opt_arg, name_buf, sizeof(name_buf));
    348  1.1  christos 
    349  1.1  christos     int     idx = 0;
    350  1.1  christos     bool    disable  = false;
    351  1.1  christos     int     ct;
    352  1.1  christos 
    353  1.1  christos     if (nm_len <= 1) {
    354  1.1  christos         if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
    355  1.1  christos             return FAILURE;
    356  1.1  christos 
    357  1.1  christos         fprintf(stderr, zInvalOptName, opts->pzProgName, opt_name);
    358  1.1  christos         (*opts->pUsageProc)(opts, EXIT_FAILURE);
    359  1.1  christos         /* NOTREACHED */
    360  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    361  1.1  christos     }
    362  1.1  christos 
    363  1.1  christos     ct = opt_match_ct(opts, opt_name, nm_len, &idx, &disable);
    364  1.1  christos 
    365  1.1  christos     /*
    366  1.1  christos      *  See if we found one match, no matches or multiple matches.
    367  1.1  christos      */
    368  1.1  christos     switch (ct) {
    369  1.1  christos     case 1:  return opt_set(opts, opt_arg, idx, disable, state);
    370  1.1  christos     case 0:  return opt_unknown(opts, opt_name, opt_arg, state);
    371  1.1  christos     default: return opt_ambiguous(opts, opt_name, ct);
    372  1.1  christos     }
    373  1.1  christos }
    374  1.1  christos 
    375  1.1  christos 
    376  1.1  christos /**
    377  1.1  christos  *  Find the short option descriptor for the current option
    378  1.1  christos  *
    379  1.1  christos  * @param pOpts      option data
    380  1.1  christos  * @param optValue   option flag character
    381  1.1  christos  * @param pOptState  state about current option
    382  1.1  christos  */
    383  1.9  christos static tSuccess
    384  1.5  christos opt_find_short(tOptions * pOpts, uint_t optValue, tOptState * pOptState)
    385  1.1  christos {
    386  1.5  christos     tOptDesc * pRes = pOpts->pOptDesc;
    387  1.1  christos     int        ct   = pOpts->optCt;
    388  1.1  christos 
    389  1.1  christos     /*
    390  1.1  christos      *  Search the option list
    391  1.1  christos      */
    392  1.1  christos     do  {
    393  1.1  christos         if (optValue != pRes->optValue)
    394  1.1  christos             continue;
    395  1.1  christos 
    396  1.1  christos         if (SKIP_OPT(pRes)) {
    397  1.1  christos             if (  (pRes->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
    398  1.1  christos                && (pRes->pz_Name != NULL)) {
    399  1.1  christos                 if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
    400  1.1  christos                     return FAILURE;
    401  1.1  christos 
    402  1.1  christos                 fprintf(stderr, zDisabledErr, pOpts->pzProgPath, pRes->pz_Name);
    403  1.1  christos                 if (pRes->pzText != NULL)
    404  1.1  christos                     fprintf(stderr, SET_OFF_FMT, pRes->pzText);
    405  1.1  christos                 fputc(NL, stderr);
    406  1.1  christos                 (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
    407  1.1  christos                 /* NOTREACHED */
    408  1.1  christos                 _exit(EXIT_FAILURE); /* to be certain */
    409  1.1  christos             }
    410  1.1  christos             goto short_opt_error;
    411  1.1  christos         }
    412  1.1  christos 
    413  1.1  christos         pOptState->pOD     = pRes;
    414  1.1  christos         pOptState->optType = TOPT_SHORT;
    415  1.1  christos         return SUCCESS;
    416  1.1  christos 
    417  1.1  christos     } while (pRes++, --ct > 0);
    418  1.1  christos 
    419  1.1  christos     /*
    420  1.1  christos      *  IF    the character value is a digit
    421  1.1  christos      *    AND there is a special number option ("-n")
    422  1.1  christos      *  THEN the result is the "option" itself and the
    423  1.1  christos      *       option is the specially marked "number" option.
    424  1.1  christos      */
    425  1.1  christos     if (  IS_DEC_DIGIT_CHAR(optValue)
    426  1.1  christos        && (pOpts->specOptIdx.number_option != NO_EQUIVALENT) ) {
    427  1.1  christos         pOptState->pOD = \
    428  1.1  christos         pRes           = pOpts->pOptDesc + pOpts->specOptIdx.number_option;
    429  1.1  christos         (pOpts->pzCurOpt)--;
    430  1.1  christos         pOptState->optType = TOPT_SHORT;
    431  1.1  christos         return SUCCESS;
    432  1.1  christos     }
    433  1.1  christos 
    434  1.1  christos  short_opt_error:
    435  1.1  christos 
    436  1.1  christos     /*
    437  1.1  christos      *  IF we are to stop on errors (the default, actually)
    438  1.1  christos      *  THEN call the usage procedure.
    439  1.1  christos      */
    440  1.1  christos     if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) {
    441  1.1  christos         fprintf(stderr, zIllOptChr, pOpts->pzProgPath, optValue);
    442  1.1  christos         (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
    443  1.1  christos         /* NOTREACHED */
    444  1.1  christos         _exit(EXIT_FAILURE); /* to be certain */
    445  1.1  christos     }
    446  1.1  christos 
    447  1.1  christos     return FAILURE;
    448  1.1  christos }
    449  1.1  christos 
    450  1.1  christos /**
    451  1.1  christos  *  Process option with a required argument.  Long options can either have a
    452  1.1  christos  *  separate command line argument, or an argument attached by the '='
    453  1.1  christos  *  character.  Figure out which.
    454  1.1  christos  *
    455  1.1  christos  *  @param[in,out] opts  the program option descriptor
    456  1.1  christos  *  @param[in,out] o_st  the option processing state
    457  1.1  christos  *  @returns SUCCESS or FAILURE
    458  1.1  christos  */
    459  1.1  christos static tSuccess
    460  1.1  christos get_opt_arg_must(tOptions * opts, tOptState * o_st)
    461  1.1  christos {
    462  1.1  christos     switch (o_st->optType) {
    463  1.1  christos     case TOPT_SHORT:
    464  1.1  christos         /*
    465  1.1  christos          *  See if an arg string follows the flag character
    466  1.1  christos          */
    467  1.1  christos         if (*++(opts->pzCurOpt) == NUL)
    468  1.1  christos             opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx++ ];
    469  1.1  christos         o_st->pzOptArg = opts->pzCurOpt;
    470  1.1  christos         break;
    471  1.1  christos 
    472  1.1  christos     case TOPT_LONG:
    473  1.1  christos         /*
    474  1.1  christos          *  See if an arg string has already been assigned (glued on
    475  1.1  christos          *  with an `=' character)
    476  1.1  christos          */
    477  1.1  christos         if (o_st->pzOptArg == NULL)
    478  1.1  christos             o_st->pzOptArg = opts->origArgVect[ opts->curOptIdx++ ];
    479  1.1  christos         break;
    480  1.1  christos 
    481  1.1  christos     default:
    482  1.1  christos #ifdef DEBUG
    483  1.1  christos         fputs("AutoOpts lib error: option type not selected\n", stderr);
    484  1.1  christos         option_exits(EXIT_FAILURE);
    485  1.1  christos #endif
    486  1.1  christos 
    487  1.1  christos     case TOPT_DEFAULT:
    488  1.1  christos         /*
    489  1.1  christos          *  The option was selected by default.  The current token is
    490  1.1  christos          *  the option argument.
    491  1.1  christos          */
    492  1.1  christos         break;
    493  1.1  christos     }
    494  1.1  christos 
    495  1.1  christos     /*
    496  1.1  christos      *  Make sure we did not overflow the argument list.
    497  1.1  christos      */
    498  1.1  christos     if (opts->curOptIdx > opts->origArgCt) {
    499  1.1  christos         fprintf(stderr, zMisArg, opts->pzProgPath, o_st->pOD->pz_Name);
    500  1.1  christos         return FAILURE;
    501  1.1  christos     }
    502  1.1  christos 
    503  1.1  christos     opts->pzCurOpt = NULL;  /* next time advance to next arg */
    504  1.1  christos     return SUCCESS;
    505  1.1  christos }
    506  1.1  christos 
    507  1.1  christos /**
    508  1.1  christos  * Process an option with an optional argument.  For short options, it looks
    509  1.1  christos  * at the character after the option character, or it consumes the next full
    510  1.1  christos  * argument.  For long options, it looks for an '=' character attachment to
    511  1.1  christos  * the long option name before deciding to take the next command line
    512  1.1  christos  * argument.
    513  1.1  christos  *
    514  1.1  christos  * @param pOpts      the option descriptor
    515  1.1  christos  * @param o_st  a structure for managing the current processing state
    516  1.1  christos  * @returns SUCCESS or does not return
    517  1.1  christos  */
    518  1.1  christos static tSuccess
    519  1.1  christos get_opt_arg_may(tOptions * pOpts, tOptState * o_st)
    520  1.1  christos {
    521  1.1  christos     /*
    522  1.1  christos      *  An option argument is optional.
    523  1.1  christos      */
    524  1.1  christos     switch (o_st->optType) {
    525  1.1  christos     case TOPT_SHORT:
    526  1.1  christos         if (*++pOpts->pzCurOpt != NUL)
    527  1.1  christos             o_st->pzOptArg = pOpts->pzCurOpt;
    528  1.1  christos         else {
    529  1.5  christos             char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
    530  1.1  christos 
    531  1.1  christos             /*
    532  1.1  christos              *  BECAUSE it is optional, we must make sure
    533  1.1  christos              *  we did not find another flag and that there
    534  1.1  christos              *  is such an argument.
    535  1.1  christos              */
    536  1.1  christos             if ((pzLA == NULL) || (*pzLA == '-'))
    537  1.1  christos                 o_st->pzOptArg = NULL;
    538  1.1  christos             else {
    539  1.1  christos                 pOpts->curOptIdx++; /* argument found */
    540  1.1  christos                 o_st->pzOptArg = pzLA;
    541  1.1  christos             }
    542  1.1  christos         }
    543  1.1  christos         break;
    544  1.1  christos 
    545  1.1  christos     case TOPT_LONG:
    546  1.1  christos         /*
    547  1.1  christos          *  Look for an argument if we don't already have one (glued on
    548  1.1  christos          *  with a `=' character) *AND* we are not in named argument mode
    549  1.1  christos          */
    550  1.1  christos         if (  (o_st->pzOptArg == NULL)
    551  1.1  christos            && (! NAMED_OPTS(pOpts))) {
    552  1.5  christos             char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
    553  1.1  christos 
    554  1.1  christos             /*
    555  1.1  christos              *  BECAUSE it is optional, we must make sure
    556  1.1  christos              *  we did not find another flag and that there
    557  1.1  christos              *  is such an argument.
    558  1.1  christos              */
    559  1.1  christos             if ((pzLA == NULL) || (*pzLA == '-'))
    560  1.1  christos                 o_st->pzOptArg = NULL;
    561  1.1  christos             else {
    562  1.1  christos                 pOpts->curOptIdx++; /* argument found */
    563  1.1  christos                 o_st->pzOptArg = pzLA;
    564  1.1  christos             }
    565  1.1  christos         }
    566  1.1  christos         break;
    567  1.1  christos 
    568  1.1  christos     default:
    569  1.1  christos     case TOPT_DEFAULT:
    570  1.1  christos         ao_bug(zbad_default_msg);
    571  1.1  christos     }
    572  1.1  christos 
    573  1.1  christos     /*
    574  1.1  christos      *  After an option with an optional argument, we will
    575  1.1  christos      *  *always* start with the next option because if there
    576  1.1  christos      *  were any characters following the option name/flag,
    577  1.1  christos      *  they would be interpreted as the argument.
    578  1.1  christos      */
    579  1.1  christos     pOpts->pzCurOpt = NULL;
    580  1.1  christos     return SUCCESS;
    581  1.1  christos }
    582  1.1  christos 
    583  1.1  christos /**
    584  1.1  christos  *  Process option that does not have an argument.
    585  1.1  christos  *
    586  1.1  christos  *  @param[in,out] opts  the program option descriptor
    587  1.1  christos  *  @param[in,out] o_st  the option processing state
    588  1.1  christos  *  @returns SUCCESS or FAILURE
    589  1.1  christos  */
    590  1.1  christos static tSuccess
    591  1.5  christos get_opt_arg_none(tOptions * pOpts, tOptState * o_st)
    592  1.1  christos {
    593  1.1  christos     /*
    594  1.1  christos      *  No option argument.  Make sure next time around we find
    595  1.1  christos      *  the correct option flag character for short options
    596  1.1  christos      */
    597  1.1  christos     if (o_st->optType == TOPT_SHORT)
    598  1.1  christos         (pOpts->pzCurOpt)++;
    599  1.1  christos 
    600  1.1  christos     /*
    601  1.1  christos      *  It is a long option.  Make sure there was no ``=xxx'' argument
    602  1.1  christos      */
    603  1.1  christos     else if (o_st->pzOptArg != NULL) {
    604  1.1  christos         fprintf(stderr, zNoArg, pOpts->pzProgPath, o_st->pOD->pz_Name);
    605  1.1  christos         return FAILURE;
    606  1.1  christos     }
    607  1.1  christos 
    608  1.1  christos     /*
    609  1.1  christos      *  It is a long option.  Advance to next command line argument.
    610  1.1  christos      */
    611  1.1  christos     else
    612  1.1  christos         pOpts->pzCurOpt = NULL;
    613  1.9  christos 
    614  1.1  christos     return SUCCESS;
    615  1.1  christos }
    616  1.1  christos 
    617  1.1  christos /**
    618  1.1  christos  *  Process option.  Figure out whether or not to look for an option argument.
    619  1.1  christos  *
    620  1.1  christos  *  @param[in,out] opts  the program option descriptor
    621  1.1  christos  *  @param[in,out] o_st  the option processing state
    622  1.1  christos  *  @returns SUCCESS or FAILURE
    623  1.1  christos  */
    624  1.9  christos static tSuccess
    625  1.1  christos get_opt_arg(tOptions * opts, tOptState * o_st)
    626  1.1  christos {
    627  1.1  christos     o_st->flags |= (o_st->pOD->fOptState & OPTST_PERSISTENT_MASK);
    628  1.1  christos 
    629  1.1  christos     /*
    630  1.1  christos      * Disabled options and options specified to not have arguments
    631  1.1  christos      * are handled with the "none" procedure.  Otherwise, check the
    632  1.1  christos      * optional flag and call either the "may" or "must" function.
    633  1.1  christos      */
    634  1.9  christos     if ((o_st->flags & OPTST_DISABLED) != 0)
    635  1.1  christos         return get_opt_arg_none(opts, o_st);
    636  1.9  christos 
    637  1.9  christos     switch (OPTST_GET_ARGTYPE(o_st->flags)) {
    638  1.9  christos     case OPARG_TYPE_STATIC:
    639  1.9  christos     {
    640  1.9  christos         /*
    641  1.9  christos          * Propagate the static arg
    642  1.9  christos          */
    643  1.9  christos         tSuccess res = get_opt_arg_none(opts, o_st);
    644  1.9  christos         o_st->pzOptArg = o_st->pOD->optArg.argString;
    645  1.9  christos         return res;
    646  1.9  christos     }
    647  1.9  christos 
    648  1.9  christos     case OPARG_TYPE_NONE:
    649  1.9  christos         return get_opt_arg_none(opts, o_st);
    650  1.9  christos     }
    651  1.9  christos 
    652  1.1  christos     if (o_st->flags & OPTST_ARG_OPTIONAL)
    653  1.1  christos         return get_opt_arg_may( opts, o_st);
    654  1.1  christos 
    655  1.1  christos     return get_opt_arg_must(opts, o_st);
    656  1.1  christos }
    657  1.1  christos 
    658  1.1  christos /**
    659  1.1  christos  *  Find the option descriptor for the current option.
    660  1.1  christos  *
    661  1.1  christos  *  @param[in,out] opts  the program option descriptor
    662  1.1  christos  *  @param[in,out] o_st  the option processing state
    663  1.1  christos  *  @returns SUCCESS or FAILURE
    664  1.1  christos  */
    665  1.9  christos static tSuccess
    666  1.1  christos find_opt(tOptions * opts, tOptState * o_st)
    667  1.1  christos {
    668  1.1  christos     /*
    669  1.1  christos      *  IF we are continuing a short option list (e.g. -xyz...)
    670  1.1  christos      *  THEN continue a single flag option.
    671  1.1  christos      *  OTHERWISE see if there is room to advance and then do so.
    672  1.1  christos      */
    673  1.1  christos     if ((opts->pzCurOpt != NULL) && (*opts->pzCurOpt != NUL))
    674  1.1  christos         return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
    675  1.1  christos 
    676  1.1  christos     if (opts->curOptIdx >= opts->origArgCt)
    677  1.1  christos         return PROBLEM; /* NORMAL COMPLETION */
    678  1.1  christos 
    679  1.1  christos     opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx ];
    680  1.1  christos 
    681  1.1  christos     /*
    682  1.1  christos      *  IF all arguments must be named options, ...
    683  1.1  christos      */
    684  1.1  christos     if (NAMED_OPTS(opts)) {
    685  1.1  christos         char *      pz  = opts->pzCurOpt;
    686  1.1  christos         int         def;
    687  1.1  christos         tSuccess    res;
    688  1.1  christos         uint16_t *  def_opt;
    689  1.1  christos 
    690  1.1  christos         opts->curOptIdx++;
    691  1.1  christos 
    692  1.1  christos         if (*pz != '-')
    693  1.1  christos             return opt_find_long(opts, pz, o_st);
    694  1.1  christos 
    695  1.1  christos         /*
    696  1.1  christos          *  The name is prefixed with one or more hyphens.  Strip them off
    697  1.1  christos          *  and disable the "default_opt" setting.  Use heavy recasting to
    698  1.1  christos          *  strip off the "const" quality of the "default_opt" field.
    699  1.1  christos          */
    700  1.1  christos         while (*(++pz) == '-')   ;
    701  1.5  christos         def_opt  = VOIDP(&(opts->specOptIdx.default_opt));
    702  1.1  christos         def      = *def_opt;
    703  1.1  christos         *def_opt = NO_EQUIVALENT;
    704  1.1  christos         res      = opt_find_long(opts, pz, o_st);
    705  1.1  christos         *def_opt = (uint16_t)def;
    706  1.1  christos         return res;
    707  1.1  christos     }
    708  1.1  christos 
    709  1.1  christos     /*
    710  1.1  christos      *  Note the kind of flag/option marker
    711  1.1  christos      */
    712  1.1  christos     if (*((opts->pzCurOpt)++) != '-')
    713  1.1  christos         return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
    714  1.1  christos 
    715  1.1  christos     /*
    716  1.1  christos      *  Special hack for a hyphen by itself
    717  1.1  christos      */
    718  1.1  christos     if (*(opts->pzCurOpt) == NUL)
    719  1.1  christos         return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
    720  1.1  christos 
    721  1.1  christos     /*
    722  1.1  christos      *  The current argument is to be processed as an option argument
    723  1.1  christos      */
    724  1.1  christos     opts->curOptIdx++;
    725  1.1  christos 
    726  1.1  christos     /*
    727  1.1  christos      *  We have an option marker.
    728  1.1  christos      *  Test the next character for long option indication
    729  1.1  christos      */
    730  1.1  christos     if (opts->pzCurOpt[0] == '-') {
    731  1.1  christos         if (*++(opts->pzCurOpt) == NUL)
    732  1.1  christos             /*
    733  1.1  christos              *  NORMAL COMPLETION - NOT this arg, but rest are operands
    734  1.1  christos              */
    735  1.1  christos             return PROBLEM;
    736  1.1  christos 
    737  1.1  christos         /*
    738  1.1  christos          *  We do not allow the hyphen to be used as a flag value.
    739  1.1  christos          *  Therefore, if long options are not to be accepted, we punt.
    740  1.1  christos          */
    741  1.1  christos         if ((opts->fOptSet & OPTPROC_LONGOPT) == 0) {
    742  1.1  christos             fprintf(stderr, zIllOptStr, opts->pzProgPath, opts->pzCurOpt-2);
    743  1.1  christos             return FAILURE;
    744  1.1  christos         }
    745  1.1  christos 
    746  1.1  christos         return opt_find_long(opts, opts->pzCurOpt, o_st);
    747  1.1  christos     }
    748  1.1  christos 
    749  1.1  christos     /*
    750  1.1  christos      *  If short options are not allowed, then do long
    751  1.1  christos      *  option processing.  Otherwise the character must be a
    752  1.1  christos      *  short (i.e. single character) option.
    753  1.1  christos      */
    754  1.1  christos     if ((opts->fOptSet & OPTPROC_SHORTOPT) != 0)
    755  1.1  christos         return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
    756  1.1  christos 
    757  1.1  christos     return opt_find_long(opts, opts->pzCurOpt, o_st);
    758  1.1  christos }
    759  1.1  christos 
    760  1.1  christos /** @}
    761  1.1  christos  *
    762  1.1  christos  * Local Variables:
    763  1.1  christos  * mode: C
    764  1.1  christos  * c-file-style: "stroustrup"
    765  1.1  christos  * indent-tabs-mode: nil
    766  1.1  christos  * End:
    767  1.1  christos  * end of autoopts/find.c */
    768