1275970Scy/**
2275970Scy * @file check.c
3275970Scy *
4275970Scy * @brief Hunt for options in the option descriptor list
5275970Scy *
6275970Scy *  This file contains the routines that deal with processing quoted strings
7275970Scy *  into an internal format.
8275970Scy *
9275970Scy * @addtogroup autoopts
10275970Scy * @{
11275970Scy */
12275970Scy/*
13275970Scy *  This file is part of AutoOpts, a companion to AutoGen.
14275970Scy *  AutoOpts is free software.
15285169Scy *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16275970Scy *
17275970Scy *  AutoOpts is available under any one of two licenses.  The license
18275970Scy *  in use must be one of these two and the choice is under the control
19275970Scy *  of the user of the license.
20275970Scy *
21275970Scy *   The GNU Lesser General Public License, version 3 or later
22275970Scy *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
23275970Scy *
24275970Scy *   The Modified Berkeley Software Distribution License
25275970Scy *      See the file "COPYING.mbsd"
26275970Scy *
27275970Scy *  These files have the following sha256 sums:
28275970Scy *
29275970Scy *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
30275970Scy *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
31275970Scy *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
32275970Scy */
33275970Scy
34275970Scy/* = = = START-STATIC-FORWARD = = = */
35275970Scystatic int
36275970Scyparse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz);
37275970Scy
38275970Scystatic void
39275970Scyopt_ambiguities(tOptions * opts, char const * name, int nm_len);
40275970Scy
41275970Scystatic int
42275970Scyopt_match_ct(tOptions * opts, char const * name, int nm_len,
43275970Scy             int * ixp, bool * disable);
44275970Scy
45275970Scystatic tSuccess
46275970Scyopt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st);
47275970Scy
48275970Scystatic tSuccess
49275970Scyopt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st);
50275970Scy
51275970Scystatic tSuccess
52275970Scyopt_ambiguous(tOptions * opts, char const * name, int match_ct);
53275970Scy
54275970Scystatic tSuccess
55275970Scyget_opt_arg_must(tOptions * opts, tOptState * o_st);
56275970Scy
57275970Scystatic tSuccess
58275970Scyget_opt_arg_may(tOptions * pOpts, tOptState * o_st);
59275970Scy
60275970Scystatic tSuccess
61285169Scyget_opt_arg_none(tOptions * pOpts, tOptState * o_st);
62275970Scy/* = = = END-STATIC-FORWARD = = = */
63275970Scy
64275970Scy/**
65275970Scy * find the name and name length we are looking for
66275970Scy */
67275970Scystatic int
68275970Scyparse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz)
69275970Scy{
70275970Scy    int  res = 0;
71275970Scy    char const * p = *nm_pp;
72275970Scy    *arg_pp  = NULL;
73275970Scy
74275970Scy    for (;;) {
75275970Scy        switch (*(p++)) {
76275970Scy        case NUL: return res;
77275970Scy
78275970Scy        case '=':
79275970Scy            memcpy(buf, *nm_pp, (size_t)res);
80275970Scy
81275970Scy            buf[res] = NUL;
82275970Scy            *nm_pp   = buf;
83294554Sdelphij            *arg_pp  = VOIDP(p);
84275970Scy            return res;
85275970Scy
86275970Scy        default:
87275970Scy            if (++res >= (int)bufsz)
88275970Scy                return -1;
89275970Scy        }
90275970Scy    }
91275970Scy}
92275970Scy
93275970Scy/**
94275970Scy *  print out the options that match the given name.
95275970Scy *
96275970Scy * @param pOpts      option data
97275970Scy * @param opt_name   name of option to look for
98275970Scy */
99275970Scystatic void
100275970Scyopt_ambiguities(tOptions * opts, char const * name, int nm_len)
101275970Scy{
102275970Scy    char const * const hyph =
103275970Scy        NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER;
104275970Scy
105275970Scy    tOptDesc * pOD = opts->pOptDesc;
106275970Scy    int        idx = 0;
107275970Scy
108275970Scy    fputs(zambig_list_msg, stderr);
109275970Scy    do  {
110275970Scy        if (pOD->pz_Name == NULL)
111275970Scy            continue; /* doc option */
112275970Scy
113275970Scy        if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0)
114275970Scy            fprintf(stderr, zambig_file, hyph, pOD->pz_Name);
115275970Scy
116275970Scy        else if (  (pOD->pz_DisableName != NULL)
117275970Scy                && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
118275970Scy                )
119275970Scy            fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName);
120275970Scy    } while (pOD++, (++idx < opts->optCt));
121275970Scy}
122275970Scy
123275970Scy/**
124275970Scy *  Determine the number of options that match the name
125275970Scy *
126275970Scy * @param pOpts      option data
127275970Scy * @param opt_name   name of option to look for
128275970Scy * @param nm_len     length of provided name
129275970Scy * @param index      pointer to int for option index
130275970Scy * @param disable    pointer to bool to mark disabled option
131275970Scy * @return count of options that match
132275970Scy */
133275970Scystatic int
134275970Scyopt_match_ct(tOptions * opts, char const * name, int nm_len,
135275970Scy             int * ixp, bool * disable)
136275970Scy{
137275970Scy    int   matchCt  = 0;
138275970Scy    int   idx      = 0;
139275970Scy    int   idxLim   = opts->optCt;
140275970Scy    tOptDesc * pOD = opts->pOptDesc;
141275970Scy
142275970Scy    do  {
143275970Scy        /*
144275970Scy         *  If option disabled or a doc option, skip to next
145275970Scy         */
146275970Scy        if (pOD->pz_Name == NULL)
147275970Scy            continue;
148275970Scy
149275970Scy        if (  SKIP_OPT(pOD)
150275970Scy           && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT)))
151275970Scy            continue;
152275970Scy
153275970Scy        if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) {
154275970Scy            /*
155275970Scy             *  IF we have a complete match
156275970Scy             *  THEN it takes priority over any already located partial
157275970Scy             */
158275970Scy            if (pOD->pz_Name[ nm_len ] == NUL) {
159275970Scy                *ixp = idx;
160275970Scy                return 1;
161275970Scy            }
162275970Scy        }
163275970Scy
164275970Scy        /*
165275970Scy         *  IF       there is a disable name
166275970Scy         *     *AND* the option name matches the disable name
167275970Scy         *  THEN ...
168275970Scy         */
169275970Scy        else if (  (pOD->pz_DisableName != NULL)
170275970Scy                && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
171275970Scy                )  {
172275970Scy            *disable = true;
173275970Scy
174275970Scy            /*
175275970Scy             *  IF we have a complete match
176275970Scy             *  THEN it takes priority over any already located partial
177275970Scy             */
178275970Scy            if (pOD->pz_DisableName[ nm_len ] == NUL) {
179275970Scy                *ixp = idx;
180275970Scy                return 1;
181275970Scy            }
182275970Scy        }
183275970Scy
184275970Scy        else
185275970Scy            continue; /* does not match any option */
186275970Scy
187275970Scy        /*
188275970Scy         *  We found a full or partial match, either regular or disabling.
189275970Scy         *  Remember the index for later.
190275970Scy         */
191275970Scy        *ixp = idx;
192275970Scy        ++matchCt;
193275970Scy
194275970Scy    } while (pOD++, (++idx < idxLim));
195275970Scy
196275970Scy    return matchCt;
197275970Scy}
198275970Scy
199275970Scy/**
200275970Scy *  Set the option to the indicated option number.
201275970Scy *
202275970Scy * @param opts      option data
203275970Scy * @param arg       option argument (if glued to name)
204275970Scy * @param idx       option index
205275970Scy * @param disable   mark disabled option
206275970Scy * @param st        state about current option
207275970Scy */
208275970Scystatic tSuccess
209275970Scyopt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st)
210275970Scy{
211275970Scy    tOptDesc * pOD = opts->pOptDesc + idx;
212275970Scy
213275970Scy    if (SKIP_OPT(pOD)) {
214275970Scy        if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
215275970Scy            return FAILURE;
216275970Scy
217275970Scy        fprintf(stderr, zDisabledErr, opts->pzProgName, pOD->pz_Name);
218275970Scy        if (pOD->pzText != NULL)
219275970Scy            fprintf(stderr, SET_OFF_FMT, pOD->pzText);
220275970Scy        fputc(NL, stderr);
221275970Scy        (*opts->pUsageProc)(opts, EXIT_FAILURE);
222275970Scy        /* NOTREACHED */
223275970Scy        _exit(EXIT_FAILURE); /* to be certain */
224275970Scy    }
225275970Scy
226275970Scy    /*
227275970Scy     *  IF we found a disablement name,
228275970Scy     *  THEN set the bit in the callers' flag word
229275970Scy     */
230275970Scy    if (disable)
231275970Scy        st->flags |= OPTST_DISABLED;
232275970Scy
233275970Scy    st->pOD      = pOD;
234275970Scy    st->pzOptArg = arg;
235275970Scy    st->optType  = TOPT_LONG;
236275970Scy
237275970Scy    return SUCCESS;
238275970Scy}
239275970Scy
240275970Scy/**
241275970Scy *  An option was not found.  Check for default option and set it
242275970Scy *  if there is one.  Otherwise, handle the error.
243275970Scy *
244275970Scy * @param opts   option data
245275970Scy * @param name   name of option to look for
246275970Scy * @param arg    option argument
247275970Scy * @param st     state about current option
248275970Scy *
249275970Scy * @return success status
250275970Scy */
251275970Scystatic tSuccess
252275970Scyopt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st)
253275970Scy{
254275970Scy    /*
255275970Scy     *  IF there is no equal sign
256275970Scy     *     *AND* we are using named arguments
257275970Scy     *     *AND* there is a default named option,
258275970Scy     *  THEN return that option.
259275970Scy     */
260275970Scy    if (  (arg == NULL)
261275970Scy       && NAMED_OPTS(opts)
262275970Scy       && (opts->specOptIdx.default_opt != NO_EQUIVALENT)) {
263275970Scy
264275970Scy        st->pOD      = opts->pOptDesc + opts->specOptIdx.default_opt;
265275970Scy        st->pzOptArg = name;
266275970Scy        st->optType  = TOPT_DEFAULT;
267275970Scy        return SUCCESS;
268275970Scy    }
269275970Scy
270275970Scy    if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
271275970Scy        fprintf(stderr, zIllOptStr, opts->pzProgPath, name);
272275970Scy        (*opts->pUsageProc)(opts, EXIT_FAILURE);
273275970Scy        /* NOTREACHED */
274275970Scy        _exit(EXIT_FAILURE); /* to be certain */
275275970Scy    }
276275970Scy
277275970Scy    return FAILURE;
278275970Scy}
279275970Scy
280275970Scy/**
281275970Scy *  Several options match the provided name.
282275970Scy *
283275970Scy * @param opts      option data
284275970Scy * @param name      name of option to look for
285275970Scy * @param match_ct  number of matching options
286275970Scy *
287275970Scy * @return success status (always FAILURE, if it returns)
288275970Scy */
289275970Scystatic tSuccess
290275970Scyopt_ambiguous(tOptions * opts, char const * name, int match_ct)
291275970Scy{
292275970Scy    if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
293275970Scy        fprintf(stderr, zambig_opt_fmt, opts->pzProgPath, name, match_ct);
294275970Scy        if (match_ct <= 4)
295275970Scy            opt_ambiguities(opts, name, (int)strlen(name));
296275970Scy        (*opts->pUsageProc)(opts, EXIT_FAILURE);
297275970Scy        /* NOTREACHED */
298275970Scy        _exit(EXIT_FAILURE); /* to be certain */
299275970Scy    }
300275970Scy    return FAILURE;
301275970Scy}
302275970Scy
303275970Scy/*=export_func  optionVendorOption
304275970Scy * private:
305275970Scy *
306275970Scy * what:  Process a vendor option
307275970Scy * arg:   + tOptions * + pOpts    + program options descriptor +
308275970Scy * arg:   + tOptDesc * + pOptDesc + the descriptor for this arg +
309275970Scy *
310275970Scy * doc:
311275970Scy *  For POSIX specified utilities, the options are constrained to the options,
312275970Scy *  @xref{config attributes, Program Configuration}.  AutoOpts clients should
313275970Scy *  never specify this directly.  It gets referenced when the option
314275970Scy *  definitions contain a "vendor-opt" attribute.
315275970Scy=*/
316275970Scyvoid
317275970ScyoptionVendorOption(tOptions * pOpts, tOptDesc * pOD)
318275970Scy{
319275970Scy    tOptState     opt_st   = OPTSTATE_INITIALIZER(PRESET);
320275970Scy    char const *  vopt_str = pOD->optArg.argString;
321275970Scy
322275970Scy    if (pOpts <= OPTPROC_EMIT_LIMIT)
323275970Scy        return;
324275970Scy
325275970Scy    if ((pOD->fOptState & OPTST_RESET) != 0)
326275970Scy        return;
327275970Scy
328275970Scy    if ((pOD->fOptState & OPTPROC_IMMEDIATE) == 0)
329275970Scy        opt_st.flags = OPTST_DEFINED;
330275970Scy
331275970Scy    if (  ((pOpts->fOptSet & OPTPROC_VENDOR_OPT) == 0)
332275970Scy       || ! SUCCESSFUL(opt_find_long(pOpts, vopt_str, &opt_st))
333275970Scy       || ! SUCCESSFUL(get_opt_arg(pOpts, &opt_st)) )
334275970Scy    {
335275970Scy        fprintf(stderr, zIllVendOptStr, pOpts->pzProgName, vopt_str);
336275970Scy        (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
337275970Scy        /* NOTREACHED */
338275970Scy        _exit(EXIT_FAILURE); /* to be certain */
339275970Scy    }
340275970Scy
341275970Scy    /*
342275970Scy     *  See if we are in immediate handling state.
343275970Scy     */
344275970Scy    if (pOpts->fOptSet & OPTPROC_IMMEDIATE) {
345275970Scy        /*
346275970Scy         *  See if the enclosed option is okay with that state.
347275970Scy         */
348275970Scy        if (DO_IMMEDIATELY(opt_st.flags))
349275970Scy            (void)handle_opt(pOpts, &opt_st);
350275970Scy
351275970Scy    } else {
352275970Scy        /*
353275970Scy         *  non-immediate direction.
354275970Scy         *  See if the enclosed option is okay with that state.
355275970Scy         */
356275970Scy        if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags))
357275970Scy            (void)handle_opt(pOpts, &opt_st);
358275970Scy    }
359275970Scy}
360275970Scy
361275970Scy/**
362275970Scy *  Find the option descriptor by full name.
363275970Scy *
364275970Scy * @param opts      option data
365275970Scy * @param opt_name  name of option to look for
366275970Scy * @param state     state about current option
367275970Scy *
368275970Scy * @return success status
369275970Scy */
370275970ScyLOCAL tSuccess
371275970Scyopt_find_long(tOptions * opts, char const * opt_name, tOptState * state)
372275970Scy{
373275970Scy    char    name_buf[128];
374275970Scy    char *  opt_arg;
375275970Scy    int     nm_len = parse_opt(&opt_name, &opt_arg, name_buf, sizeof(name_buf));
376275970Scy
377275970Scy    int     idx = 0;
378275970Scy    bool    disable  = false;
379275970Scy    int     ct;
380275970Scy
381275970Scy    if (nm_len <= 1) {
382275970Scy        if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
383275970Scy            return FAILURE;
384275970Scy
385275970Scy        fprintf(stderr, zInvalOptName, opts->pzProgName, opt_name);
386275970Scy        (*opts->pUsageProc)(opts, EXIT_FAILURE);
387275970Scy        /* NOTREACHED */
388275970Scy        _exit(EXIT_FAILURE); /* to be certain */
389275970Scy    }
390275970Scy
391275970Scy    ct = opt_match_ct(opts, opt_name, nm_len, &idx, &disable);
392275970Scy
393275970Scy    /*
394275970Scy     *  See if we found one match, no matches or multiple matches.
395275970Scy     */
396275970Scy    switch (ct) {
397275970Scy    case 1:  return opt_set(opts, opt_arg, idx, disable, state);
398275970Scy    case 0:  return opt_unknown(opts, opt_name, opt_arg, state);
399275970Scy    default: return opt_ambiguous(opts, opt_name, ct);
400275970Scy    }
401275970Scy}
402275970Scy
403275970Scy
404275970Scy/**
405275970Scy *  Find the short option descriptor for the current option
406275970Scy *
407275970Scy * @param pOpts      option data
408275970Scy * @param optValue   option flag character
409275970Scy * @param pOptState  state about current option
410275970Scy */
411275970ScyLOCAL tSuccess
412285169Scyopt_find_short(tOptions * pOpts, uint_t optValue, tOptState * pOptState)
413275970Scy{
414285169Scy    tOptDesc * pRes = pOpts->pOptDesc;
415275970Scy    int        ct   = pOpts->optCt;
416275970Scy
417275970Scy    /*
418275970Scy     *  Search the option list
419275970Scy     */
420275970Scy    do  {
421275970Scy        if (optValue != pRes->optValue)
422275970Scy            continue;
423275970Scy
424275970Scy        if (SKIP_OPT(pRes)) {
425275970Scy            if (  (pRes->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
426275970Scy               && (pRes->pz_Name != NULL)) {
427275970Scy                if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
428275970Scy                    return FAILURE;
429275970Scy
430275970Scy                fprintf(stderr, zDisabledErr, pOpts->pzProgPath, pRes->pz_Name);
431275970Scy                if (pRes->pzText != NULL)
432275970Scy                    fprintf(stderr, SET_OFF_FMT, pRes->pzText);
433275970Scy                fputc(NL, stderr);
434275970Scy                (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
435275970Scy                /* NOTREACHED */
436275970Scy                _exit(EXIT_FAILURE); /* to be certain */
437275970Scy            }
438275970Scy            goto short_opt_error;
439275970Scy        }
440275970Scy
441275970Scy        pOptState->pOD     = pRes;
442275970Scy        pOptState->optType = TOPT_SHORT;
443275970Scy        return SUCCESS;
444275970Scy
445275970Scy    } while (pRes++, --ct > 0);
446275970Scy
447275970Scy    /*
448275970Scy     *  IF    the character value is a digit
449275970Scy     *    AND there is a special number option ("-n")
450275970Scy     *  THEN the result is the "option" itself and the
451275970Scy     *       option is the specially marked "number" option.
452275970Scy     */
453275970Scy    if (  IS_DEC_DIGIT_CHAR(optValue)
454275970Scy       && (pOpts->specOptIdx.number_option != NO_EQUIVALENT) ) {
455275970Scy        pOptState->pOD = \
456275970Scy        pRes           = pOpts->pOptDesc + pOpts->specOptIdx.number_option;
457275970Scy        (pOpts->pzCurOpt)--;
458275970Scy        pOptState->optType = TOPT_SHORT;
459275970Scy        return SUCCESS;
460275970Scy    }
461275970Scy
462275970Scy short_opt_error:
463275970Scy
464275970Scy    /*
465275970Scy     *  IF we are to stop on errors (the default, actually)
466275970Scy     *  THEN call the usage procedure.
467275970Scy     */
468275970Scy    if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) {
469275970Scy        fprintf(stderr, zIllOptChr, pOpts->pzProgPath, optValue);
470275970Scy        (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
471275970Scy        /* NOTREACHED */
472275970Scy        _exit(EXIT_FAILURE); /* to be certain */
473275970Scy    }
474275970Scy
475275970Scy    return FAILURE;
476275970Scy}
477275970Scy
478275970Scy/**
479275970Scy *  Process option with a required argument.  Long options can either have a
480275970Scy *  separate command line argument, or an argument attached by the '='
481275970Scy *  character.  Figure out which.
482275970Scy *
483275970Scy *  @param[in,out] opts  the program option descriptor
484275970Scy *  @param[in,out] o_st  the option processing state
485275970Scy *  @returns SUCCESS or FAILURE
486275970Scy */
487275970Scystatic tSuccess
488275970Scyget_opt_arg_must(tOptions * opts, tOptState * o_st)
489275970Scy{
490275970Scy    switch (o_st->optType) {
491275970Scy    case TOPT_SHORT:
492275970Scy        /*
493275970Scy         *  See if an arg string follows the flag character
494275970Scy         */
495275970Scy        if (*++(opts->pzCurOpt) == NUL)
496275970Scy            opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx++ ];
497275970Scy        o_st->pzOptArg = opts->pzCurOpt;
498275970Scy        break;
499275970Scy
500275970Scy    case TOPT_LONG:
501275970Scy        /*
502275970Scy         *  See if an arg string has already been assigned (glued on
503275970Scy         *  with an `=' character)
504275970Scy         */
505275970Scy        if (o_st->pzOptArg == NULL)
506275970Scy            o_st->pzOptArg = opts->origArgVect[ opts->curOptIdx++ ];
507275970Scy        break;
508275970Scy
509275970Scy    default:
510275970Scy#ifdef DEBUG
511275970Scy        fputs("AutoOpts lib error: option type not selected\n", stderr);
512275970Scy        option_exits(EXIT_FAILURE);
513275970Scy#endif
514275970Scy
515275970Scy    case TOPT_DEFAULT:
516275970Scy        /*
517275970Scy         *  The option was selected by default.  The current token is
518275970Scy         *  the option argument.
519275970Scy         */
520275970Scy        break;
521275970Scy    }
522275970Scy
523275970Scy    /*
524275970Scy     *  Make sure we did not overflow the argument list.
525275970Scy     */
526275970Scy    if (opts->curOptIdx > opts->origArgCt) {
527275970Scy        fprintf(stderr, zMisArg, opts->pzProgPath, o_st->pOD->pz_Name);
528275970Scy        return FAILURE;
529275970Scy    }
530275970Scy
531275970Scy    opts->pzCurOpt = NULL;  /* next time advance to next arg */
532275970Scy    return SUCCESS;
533275970Scy}
534275970Scy
535275970Scy/**
536275970Scy * Process an option with an optional argument.  For short options, it looks
537275970Scy * at the character after the option character, or it consumes the next full
538275970Scy * argument.  For long options, it looks for an '=' character attachment to
539275970Scy * the long option name before deciding to take the next command line
540275970Scy * argument.
541275970Scy *
542275970Scy * @param pOpts      the option descriptor
543275970Scy * @param o_st  a structure for managing the current processing state
544275970Scy * @returns SUCCESS or does not return
545275970Scy */
546275970Scystatic tSuccess
547275970Scyget_opt_arg_may(tOptions * pOpts, tOptState * o_st)
548275970Scy{
549275970Scy    /*
550275970Scy     *  An option argument is optional.
551275970Scy     */
552275970Scy    switch (o_st->optType) {
553275970Scy    case TOPT_SHORT:
554275970Scy        if (*++pOpts->pzCurOpt != NUL)
555275970Scy            o_st->pzOptArg = pOpts->pzCurOpt;
556275970Scy        else {
557285169Scy            char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
558275970Scy
559275970Scy            /*
560275970Scy             *  BECAUSE it is optional, we must make sure
561275970Scy             *  we did not find another flag and that there
562275970Scy             *  is such an argument.
563275970Scy             */
564275970Scy            if ((pzLA == NULL) || (*pzLA == '-'))
565275970Scy                o_st->pzOptArg = NULL;
566275970Scy            else {
567275970Scy                pOpts->curOptIdx++; /* argument found */
568275970Scy                o_st->pzOptArg = pzLA;
569275970Scy            }
570275970Scy        }
571275970Scy        break;
572275970Scy
573275970Scy    case TOPT_LONG:
574275970Scy        /*
575275970Scy         *  Look for an argument if we don't already have one (glued on
576275970Scy         *  with a `=' character) *AND* we are not in named argument mode
577275970Scy         */
578275970Scy        if (  (o_st->pzOptArg == NULL)
579275970Scy           && (! NAMED_OPTS(pOpts))) {
580285169Scy            char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
581275970Scy
582275970Scy            /*
583275970Scy             *  BECAUSE it is optional, we must make sure
584275970Scy             *  we did not find another flag and that there
585275970Scy             *  is such an argument.
586275970Scy             */
587275970Scy            if ((pzLA == NULL) || (*pzLA == '-'))
588275970Scy                o_st->pzOptArg = NULL;
589275970Scy            else {
590275970Scy                pOpts->curOptIdx++; /* argument found */
591275970Scy                o_st->pzOptArg = pzLA;
592275970Scy            }
593275970Scy        }
594275970Scy        break;
595275970Scy
596275970Scy    default:
597275970Scy    case TOPT_DEFAULT:
598275970Scy        ao_bug(zbad_default_msg);
599275970Scy    }
600275970Scy
601275970Scy    /*
602275970Scy     *  After an option with an optional argument, we will
603275970Scy     *  *always* start with the next option because if there
604275970Scy     *  were any characters following the option name/flag,
605275970Scy     *  they would be interpreted as the argument.
606275970Scy     */
607275970Scy    pOpts->pzCurOpt = NULL;
608275970Scy    return SUCCESS;
609275970Scy}
610275970Scy
611275970Scy/**
612275970Scy *  Process option that does not have an argument.
613275970Scy *
614275970Scy *  @param[in,out] opts  the program option descriptor
615275970Scy *  @param[in,out] o_st  the option processing state
616275970Scy *  @returns SUCCESS or FAILURE
617275970Scy */
618275970Scystatic tSuccess
619285169Scyget_opt_arg_none(tOptions * pOpts, tOptState * o_st)
620275970Scy{
621275970Scy    /*
622275970Scy     *  No option argument.  Make sure next time around we find
623275970Scy     *  the correct option flag character for short options
624275970Scy     */
625275970Scy    if (o_st->optType == TOPT_SHORT)
626275970Scy        (pOpts->pzCurOpt)++;
627275970Scy
628275970Scy    /*
629275970Scy     *  It is a long option.  Make sure there was no ``=xxx'' argument
630275970Scy     */
631275970Scy    else if (o_st->pzOptArg != NULL) {
632275970Scy        fprintf(stderr, zNoArg, pOpts->pzProgPath, o_st->pOD->pz_Name);
633275970Scy        return FAILURE;
634275970Scy    }
635275970Scy
636275970Scy    /*
637275970Scy     *  It is a long option.  Advance to next command line argument.
638275970Scy     */
639275970Scy    else
640275970Scy        pOpts->pzCurOpt = NULL;
641275970Scy    return SUCCESS;
642275970Scy}
643275970Scy
644275970Scy/**
645275970Scy *  Process option.  Figure out whether or not to look for an option argument.
646275970Scy *
647275970Scy *  @param[in,out] opts  the program option descriptor
648275970Scy *  @param[in,out] o_st  the option processing state
649275970Scy *  @returns SUCCESS or FAILURE
650275970Scy */
651275970ScyLOCAL tSuccess
652275970Scyget_opt_arg(tOptions * opts, tOptState * o_st)
653275970Scy{
654275970Scy    o_st->flags |= (o_st->pOD->fOptState & OPTST_PERSISTENT_MASK);
655275970Scy
656275970Scy    /*
657275970Scy     * Disabled options and options specified to not have arguments
658275970Scy     * are handled with the "none" procedure.  Otherwise, check the
659275970Scy     * optional flag and call either the "may" or "must" function.
660275970Scy     */
661275970Scy    if (  ((o_st->flags & OPTST_DISABLED) != 0)
662275970Scy       || (OPTST_GET_ARGTYPE(o_st->flags) == OPARG_TYPE_NONE))
663275970Scy        return get_opt_arg_none(opts, o_st);
664275970Scy
665275970Scy    if (o_st->flags & OPTST_ARG_OPTIONAL)
666275970Scy        return get_opt_arg_may( opts, o_st);
667275970Scy
668275970Scy    return get_opt_arg_must(opts, o_st);
669275970Scy}
670275970Scy
671275970Scy/**
672275970Scy *  Find the option descriptor for the current option.
673275970Scy *
674275970Scy *  @param[in,out] opts  the program option descriptor
675275970Scy *  @param[in,out] o_st  the option processing state
676275970Scy *  @returns SUCCESS or FAILURE
677275970Scy */
678275970ScyLOCAL tSuccess
679275970Scyfind_opt(tOptions * opts, tOptState * o_st)
680275970Scy{
681275970Scy    /*
682275970Scy     *  IF we are continuing a short option list (e.g. -xyz...)
683275970Scy     *  THEN continue a single flag option.
684275970Scy     *  OTHERWISE see if there is room to advance and then do so.
685275970Scy     */
686275970Scy    if ((opts->pzCurOpt != NULL) && (*opts->pzCurOpt != NUL))
687275970Scy        return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
688275970Scy
689275970Scy    if (opts->curOptIdx >= opts->origArgCt)
690275970Scy        return PROBLEM; /* NORMAL COMPLETION */
691275970Scy
692275970Scy    opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx ];
693275970Scy
694275970Scy    /*
695275970Scy     *  IF all arguments must be named options, ...
696275970Scy     */
697275970Scy    if (NAMED_OPTS(opts)) {
698275970Scy        char *      pz  = opts->pzCurOpt;
699275970Scy        int         def;
700275970Scy        tSuccess    res;
701275970Scy        uint16_t *  def_opt;
702275970Scy
703275970Scy        opts->curOptIdx++;
704275970Scy
705275970Scy        if (*pz != '-')
706275970Scy            return opt_find_long(opts, pz, o_st);
707275970Scy
708275970Scy        /*
709275970Scy         *  The name is prefixed with one or more hyphens.  Strip them off
710275970Scy         *  and disable the "default_opt" setting.  Use heavy recasting to
711275970Scy         *  strip off the "const" quality of the "default_opt" field.
712275970Scy         */
713275970Scy        while (*(++pz) == '-')   ;
714285169Scy        def_opt  = VOIDP(&(opts->specOptIdx.default_opt));
715275970Scy        def      = *def_opt;
716275970Scy        *def_opt = NO_EQUIVALENT;
717275970Scy        res      = opt_find_long(opts, pz, o_st);
718275970Scy        *def_opt = (uint16_t)def;
719275970Scy        return res;
720275970Scy    }
721275970Scy
722275970Scy    /*
723275970Scy     *  Note the kind of flag/option marker
724275970Scy     */
725275970Scy    if (*((opts->pzCurOpt)++) != '-')
726275970Scy        return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
727275970Scy
728275970Scy    /*
729275970Scy     *  Special hack for a hyphen by itself
730275970Scy     */
731275970Scy    if (*(opts->pzCurOpt) == NUL)
732275970Scy        return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
733275970Scy
734275970Scy    /*
735275970Scy     *  The current argument is to be processed as an option argument
736275970Scy     */
737275970Scy    opts->curOptIdx++;
738275970Scy
739275970Scy    /*
740275970Scy     *  We have an option marker.
741275970Scy     *  Test the next character for long option indication
742275970Scy     */
743275970Scy    if (opts->pzCurOpt[0] == '-') {
744275970Scy        if (*++(opts->pzCurOpt) == NUL)
745275970Scy            /*
746275970Scy             *  NORMAL COMPLETION - NOT this arg, but rest are operands
747275970Scy             */
748275970Scy            return PROBLEM;
749275970Scy
750275970Scy        /*
751275970Scy         *  We do not allow the hyphen to be used as a flag value.
752275970Scy         *  Therefore, if long options are not to be accepted, we punt.
753275970Scy         */
754275970Scy        if ((opts->fOptSet & OPTPROC_LONGOPT) == 0) {
755275970Scy            fprintf(stderr, zIllOptStr, opts->pzProgPath, opts->pzCurOpt-2);
756275970Scy            return FAILURE;
757275970Scy        }
758275970Scy
759275970Scy        return opt_find_long(opts, opts->pzCurOpt, o_st);
760275970Scy    }
761275970Scy
762275970Scy    /*
763275970Scy     *  If short options are not allowed, then do long
764275970Scy     *  option processing.  Otherwise the character must be a
765275970Scy     *  short (i.e. single character) option.
766275970Scy     */
767275970Scy    if ((opts->fOptSet & OPTPROC_SHORTOPT) != 0)
768275970Scy        return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
769275970Scy
770275970Scy    return opt_find_long(opts, opts->pzCurOpt, o_st);
771275970Scy}
772275970Scy
773275970Scy/** @}
774275970Scy *
775275970Scy * Local Variables:
776275970Scy * mode: C
777275970Scy * c-file-style: "stroustrup"
778275970Scy * indent-tabs-mode: nil
779275970Scy * End:
780275970Scy * end of autoopts/find.c */
781