1290001Sglebius/**
2290001Sglebius * @file check.c
3290001Sglebius *
4290001Sglebius * @brief Hunt for options in the option descriptor list
5290001Sglebius *
6290001Sglebius *  This file contains the routines that deal with processing quoted strings
7290001Sglebius *  into an internal format.
8290001Sglebius *
9290001Sglebius * @addtogroup autoopts
10290001Sglebius * @{
11290001Sglebius */
12290001Sglebius/*
13290001Sglebius *  This file is part of AutoOpts, a companion to AutoGen.
14290001Sglebius *  AutoOpts is free software.
15290001Sglebius *  AutoOpts is Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
16290001Sglebius *
17290001Sglebius *  AutoOpts is available under any one of two licenses.  The license
18290001Sglebius *  in use must be one of these two and the choice is under the control
19290001Sglebius *  of the user of the license.
20290001Sglebius *
21290001Sglebius *   The GNU Lesser General Public License, version 3 or later
22290001Sglebius *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
23290001Sglebius *
24290001Sglebius *   The Modified Berkeley Software Distribution License
25290001Sglebius *      See the file "COPYING.mbsd"
26290001Sglebius *
27290001Sglebius *  These files have the following sha256 sums:
28290001Sglebius *
29290001Sglebius *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
30290001Sglebius *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
31290001Sglebius *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
32290001Sglebius */
33290001Sglebius
34290001Sglebius/* = = = START-STATIC-FORWARD = = = */
35290001Sglebiusstatic int
36290001Sglebiusparse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz);
37290001Sglebius
38290001Sglebiusstatic void
39290001Sglebiusopt_ambiguities(tOptions * opts, char const * name, int nm_len);
40290001Sglebius
41290001Sglebiusstatic int
42290001Sglebiusopt_match_ct(tOptions * opts, char const * name, int nm_len,
43290001Sglebius             int * ixp, bool * disable);
44290001Sglebius
45290001Sglebiusstatic tSuccess
46290001Sglebiusopt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st);
47290001Sglebius
48290001Sglebiusstatic tSuccess
49290001Sglebiusopt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st);
50290001Sglebius
51290001Sglebiusstatic tSuccess
52290001Sglebiusopt_ambiguous(tOptions * opts, char const * name, int match_ct);
53290001Sglebius
54290001Sglebiusstatic tSuccess
55290001Sglebiusget_opt_arg_must(tOptions * opts, tOptState * o_st);
56290001Sglebius
57290001Sglebiusstatic tSuccess
58290001Sglebiusget_opt_arg_may(tOptions * pOpts, tOptState * o_st);
59290001Sglebius
60290001Sglebiusstatic tSuccess
61290001Sglebiusget_opt_arg_none(tOptions * pOpts, tOptState * o_st);
62290001Sglebius/* = = = END-STATIC-FORWARD = = = */
63290001Sglebius
64290001Sglebius/**
65290001Sglebius * find the name and name length we are looking for
66290001Sglebius */
67290001Sglebiusstatic int
68290001Sglebiusparse_opt(char const ** nm_pp, char ** arg_pp, char * buf, size_t bufsz)
69290001Sglebius{
70290001Sglebius    int  res = 0;
71290001Sglebius    char const * p = *nm_pp;
72290001Sglebius    *arg_pp  = NULL;
73290001Sglebius
74290001Sglebius    for (;;) {
75290001Sglebius        switch (*(p++)) {
76290001Sglebius        case NUL: return res;
77290001Sglebius
78290001Sglebius        case '=':
79290001Sglebius            memcpy(buf, *nm_pp, (size_t)res);
80290001Sglebius
81290001Sglebius            buf[res] = NUL;
82290001Sglebius            *nm_pp   = buf;
83294905Sdelphij            *arg_pp  = VOIDP(p);
84290001Sglebius            return res;
85290001Sglebius
86290001Sglebius        default:
87290001Sglebius            if (++res >= (int)bufsz)
88290001Sglebius                return -1;
89290001Sglebius        }
90290001Sglebius    }
91290001Sglebius}
92290001Sglebius
93290001Sglebius/**
94290001Sglebius *  print out the options that match the given name.
95290001Sglebius *
96290001Sglebius * @param pOpts      option data
97290001Sglebius * @param opt_name   name of option to look for
98290001Sglebius */
99290001Sglebiusstatic void
100290001Sglebiusopt_ambiguities(tOptions * opts, char const * name, int nm_len)
101290001Sglebius{
102290001Sglebius    char const * const hyph =
103290001Sglebius        NAMED_OPTS(opts) ? "" : LONG_OPT_MARKER;
104290001Sglebius
105290001Sglebius    tOptDesc * pOD = opts->pOptDesc;
106290001Sglebius    int        idx = 0;
107290001Sglebius
108290001Sglebius    fputs(zambig_list_msg, stderr);
109290001Sglebius    do  {
110290001Sglebius        if (pOD->pz_Name == NULL)
111290001Sglebius            continue; /* doc option */
112290001Sglebius
113290001Sglebius        if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0)
114290001Sglebius            fprintf(stderr, zambig_file, hyph, pOD->pz_Name);
115290001Sglebius
116290001Sglebius        else if (  (pOD->pz_DisableName != NULL)
117290001Sglebius                && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
118290001Sglebius                )
119290001Sglebius            fprintf(stderr, zambig_file, hyph, pOD->pz_DisableName);
120290001Sglebius    } while (pOD++, (++idx < opts->optCt));
121290001Sglebius}
122290001Sglebius
123290001Sglebius/**
124290001Sglebius *  Determine the number of options that match the name
125290001Sglebius *
126290001Sglebius * @param pOpts      option data
127290001Sglebius * @param opt_name   name of option to look for
128290001Sglebius * @param nm_len     length of provided name
129290001Sglebius * @param index      pointer to int for option index
130290001Sglebius * @param disable    pointer to bool to mark disabled option
131290001Sglebius * @return count of options that match
132290001Sglebius */
133290001Sglebiusstatic int
134290001Sglebiusopt_match_ct(tOptions * opts, char const * name, int nm_len,
135290001Sglebius             int * ixp, bool * disable)
136290001Sglebius{
137290001Sglebius    int   matchCt  = 0;
138290001Sglebius    int   idx      = 0;
139290001Sglebius    int   idxLim   = opts->optCt;
140290001Sglebius    tOptDesc * pOD = opts->pOptDesc;
141290001Sglebius
142290001Sglebius    do  {
143290001Sglebius        /*
144290001Sglebius         *  If option disabled or a doc option, skip to next
145290001Sglebius         */
146290001Sglebius        if (pOD->pz_Name == NULL)
147290001Sglebius            continue;
148290001Sglebius
149290001Sglebius        if (  SKIP_OPT(pOD)
150290001Sglebius           && (pOD->fOptState != (OPTST_OMITTED | OPTST_NO_INIT)))
151290001Sglebius            continue;
152290001Sglebius
153290001Sglebius        if (strneqvcmp(name, pOD->pz_Name, nm_len) == 0) {
154290001Sglebius            /*
155290001Sglebius             *  IF we have a complete match
156290001Sglebius             *  THEN it takes priority over any already located partial
157290001Sglebius             */
158290001Sglebius            if (pOD->pz_Name[ nm_len ] == NUL) {
159290001Sglebius                *ixp = idx;
160290001Sglebius                return 1;
161290001Sglebius            }
162290001Sglebius        }
163290001Sglebius
164290001Sglebius        /*
165290001Sglebius         *  IF       there is a disable name
166290001Sglebius         *     *AND* the option name matches the disable name
167290001Sglebius         *  THEN ...
168290001Sglebius         */
169290001Sglebius        else if (  (pOD->pz_DisableName != NULL)
170290001Sglebius                && (strneqvcmp(name, pOD->pz_DisableName, nm_len) == 0)
171290001Sglebius                )  {
172290001Sglebius            *disable = true;
173290001Sglebius
174290001Sglebius            /*
175290001Sglebius             *  IF we have a complete match
176290001Sglebius             *  THEN it takes priority over any already located partial
177290001Sglebius             */
178290001Sglebius            if (pOD->pz_DisableName[ nm_len ] == NUL) {
179290001Sglebius                *ixp = idx;
180290001Sglebius                return 1;
181290001Sglebius            }
182290001Sglebius        }
183290001Sglebius
184290001Sglebius        else
185290001Sglebius            continue; /* does not match any option */
186290001Sglebius
187290001Sglebius        /*
188290001Sglebius         *  We found a full or partial match, either regular or disabling.
189290001Sglebius         *  Remember the index for later.
190290001Sglebius         */
191290001Sglebius        *ixp = idx;
192290001Sglebius        ++matchCt;
193290001Sglebius
194290001Sglebius    } while (pOD++, (++idx < idxLim));
195290001Sglebius
196290001Sglebius    return matchCt;
197290001Sglebius}
198290001Sglebius
199290001Sglebius/**
200290001Sglebius *  Set the option to the indicated option number.
201290001Sglebius *
202290001Sglebius * @param opts      option data
203290001Sglebius * @param arg       option argument (if glued to name)
204290001Sglebius * @param idx       option index
205290001Sglebius * @param disable   mark disabled option
206290001Sglebius * @param st        state about current option
207290001Sglebius */
208290001Sglebiusstatic tSuccess
209290001Sglebiusopt_set(tOptions * opts, char * arg, int idx, bool disable, tOptState * st)
210290001Sglebius{
211290001Sglebius    tOptDesc * pOD = opts->pOptDesc + idx;
212290001Sglebius
213290001Sglebius    if (SKIP_OPT(pOD)) {
214290001Sglebius        if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
215290001Sglebius            return FAILURE;
216290001Sglebius
217290001Sglebius        fprintf(stderr, zDisabledErr, opts->pzProgName, pOD->pz_Name);
218290001Sglebius        if (pOD->pzText != NULL)
219290001Sglebius            fprintf(stderr, SET_OFF_FMT, pOD->pzText);
220290001Sglebius        fputc(NL, stderr);
221290001Sglebius        (*opts->pUsageProc)(opts, EXIT_FAILURE);
222290001Sglebius        /* NOTREACHED */
223290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
224290001Sglebius    }
225290001Sglebius
226290001Sglebius    /*
227290001Sglebius     *  IF we found a disablement name,
228290001Sglebius     *  THEN set the bit in the callers' flag word
229290001Sglebius     */
230290001Sglebius    if (disable)
231290001Sglebius        st->flags |= OPTST_DISABLED;
232290001Sglebius
233290001Sglebius    st->pOD      = pOD;
234290001Sglebius    st->pzOptArg = arg;
235290001Sglebius    st->optType  = TOPT_LONG;
236290001Sglebius
237290001Sglebius    return SUCCESS;
238290001Sglebius}
239290001Sglebius
240290001Sglebius/**
241290001Sglebius *  An option was not found.  Check for default option and set it
242290001Sglebius *  if there is one.  Otherwise, handle the error.
243290001Sglebius *
244290001Sglebius * @param opts   option data
245290001Sglebius * @param name   name of option to look for
246290001Sglebius * @param arg    option argument
247290001Sglebius * @param st     state about current option
248290001Sglebius *
249290001Sglebius * @return success status
250290001Sglebius */
251290001Sglebiusstatic tSuccess
252290001Sglebiusopt_unknown(tOptions * opts, char const * name, char * arg, tOptState * st)
253290001Sglebius{
254290001Sglebius    /*
255290001Sglebius     *  IF there is no equal sign
256290001Sglebius     *     *AND* we are using named arguments
257290001Sglebius     *     *AND* there is a default named option,
258290001Sglebius     *  THEN return that option.
259290001Sglebius     */
260290001Sglebius    if (  (arg == NULL)
261290001Sglebius       && NAMED_OPTS(opts)
262290001Sglebius       && (opts->specOptIdx.default_opt != NO_EQUIVALENT)) {
263290001Sglebius
264290001Sglebius        st->pOD      = opts->pOptDesc + opts->specOptIdx.default_opt;
265290001Sglebius        st->pzOptArg = name;
266290001Sglebius        st->optType  = TOPT_DEFAULT;
267290001Sglebius        return SUCCESS;
268290001Sglebius    }
269290001Sglebius
270290001Sglebius    if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
271290001Sglebius        fprintf(stderr, zIllOptStr, opts->pzProgPath, name);
272290001Sglebius        (*opts->pUsageProc)(opts, EXIT_FAILURE);
273290001Sglebius        /* NOTREACHED */
274290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
275290001Sglebius    }
276290001Sglebius
277290001Sglebius    return FAILURE;
278290001Sglebius}
279290001Sglebius
280290001Sglebius/**
281290001Sglebius *  Several options match the provided name.
282290001Sglebius *
283290001Sglebius * @param opts      option data
284290001Sglebius * @param name      name of option to look for
285290001Sglebius * @param match_ct  number of matching options
286290001Sglebius *
287290001Sglebius * @return success status (always FAILURE, if it returns)
288290001Sglebius */
289290001Sglebiusstatic tSuccess
290290001Sglebiusopt_ambiguous(tOptions * opts, char const * name, int match_ct)
291290001Sglebius{
292290001Sglebius    if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0) {
293290001Sglebius        fprintf(stderr, zambig_opt_fmt, opts->pzProgPath, name, match_ct);
294290001Sglebius        if (match_ct <= 4)
295290001Sglebius            opt_ambiguities(opts, name, (int)strlen(name));
296290001Sglebius        (*opts->pUsageProc)(opts, EXIT_FAILURE);
297290001Sglebius        /* NOTREACHED */
298290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
299290001Sglebius    }
300290001Sglebius    return FAILURE;
301290001Sglebius}
302290001Sglebius
303290001Sglebius/*=export_func  optionVendorOption
304290001Sglebius * private:
305290001Sglebius *
306290001Sglebius * what:  Process a vendor option
307290001Sglebius * arg:   + tOptions * + pOpts    + program options descriptor +
308290001Sglebius * arg:   + tOptDesc * + pOptDesc + the descriptor for this arg +
309290001Sglebius *
310290001Sglebius * doc:
311290001Sglebius *  For POSIX specified utilities, the options are constrained to the options,
312290001Sglebius *  @xref{config attributes, Program Configuration}.  AutoOpts clients should
313290001Sglebius *  never specify this directly.  It gets referenced when the option
314290001Sglebius *  definitions contain a "vendor-opt" attribute.
315290001Sglebius=*/
316290001Sglebiusvoid
317290001SglebiusoptionVendorOption(tOptions * pOpts, tOptDesc * pOD)
318290001Sglebius{
319290001Sglebius    tOptState     opt_st   = OPTSTATE_INITIALIZER(PRESET);
320290001Sglebius    char const *  vopt_str = pOD->optArg.argString;
321290001Sglebius
322290001Sglebius    if (pOpts <= OPTPROC_EMIT_LIMIT)
323290001Sglebius        return;
324290001Sglebius
325290001Sglebius    if ((pOD->fOptState & OPTST_RESET) != 0)
326290001Sglebius        return;
327290001Sglebius
328290001Sglebius    if ((pOD->fOptState & OPTPROC_IMMEDIATE) == 0)
329290001Sglebius        opt_st.flags = OPTST_DEFINED;
330290001Sglebius
331290001Sglebius    if (  ((pOpts->fOptSet & OPTPROC_VENDOR_OPT) == 0)
332290001Sglebius       || ! SUCCESSFUL(opt_find_long(pOpts, vopt_str, &opt_st))
333290001Sglebius       || ! SUCCESSFUL(get_opt_arg(pOpts, &opt_st)) )
334290001Sglebius    {
335290001Sglebius        fprintf(stderr, zIllVendOptStr, pOpts->pzProgName, vopt_str);
336290001Sglebius        (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
337290001Sglebius        /* NOTREACHED */
338290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
339290001Sglebius    }
340290001Sglebius
341290001Sglebius    /*
342290001Sglebius     *  See if we are in immediate handling state.
343290001Sglebius     */
344290001Sglebius    if (pOpts->fOptSet & OPTPROC_IMMEDIATE) {
345290001Sglebius        /*
346290001Sglebius         *  See if the enclosed option is okay with that state.
347290001Sglebius         */
348290001Sglebius        if (DO_IMMEDIATELY(opt_st.flags))
349290001Sglebius            (void)handle_opt(pOpts, &opt_st);
350290001Sglebius
351290001Sglebius    } else {
352290001Sglebius        /*
353290001Sglebius         *  non-immediate direction.
354290001Sglebius         *  See if the enclosed option is okay with that state.
355290001Sglebius         */
356290001Sglebius        if (DO_NORMALLY(opt_st.flags) || DO_SECOND_TIME(opt_st.flags))
357290001Sglebius            (void)handle_opt(pOpts, &opt_st);
358290001Sglebius    }
359290001Sglebius}
360290001Sglebius
361290001Sglebius/**
362290001Sglebius *  Find the option descriptor by full name.
363290001Sglebius *
364290001Sglebius * @param opts      option data
365290001Sglebius * @param opt_name  name of option to look for
366290001Sglebius * @param state     state about current option
367290001Sglebius *
368290001Sglebius * @return success status
369290001Sglebius */
370290001SglebiusLOCAL tSuccess
371290001Sglebiusopt_find_long(tOptions * opts, char const * opt_name, tOptState * state)
372290001Sglebius{
373290001Sglebius    char    name_buf[128];
374290001Sglebius    char *  opt_arg;
375290001Sglebius    int     nm_len = parse_opt(&opt_name, &opt_arg, name_buf, sizeof(name_buf));
376290001Sglebius
377290001Sglebius    int     idx = 0;
378290001Sglebius    bool    disable  = false;
379290001Sglebius    int     ct;
380290001Sglebius
381290001Sglebius    if (nm_len <= 1) {
382290001Sglebius        if ((opts->fOptSet & OPTPROC_ERRSTOP) == 0)
383290001Sglebius            return FAILURE;
384290001Sglebius
385290001Sglebius        fprintf(stderr, zInvalOptName, opts->pzProgName, opt_name);
386290001Sglebius        (*opts->pUsageProc)(opts, EXIT_FAILURE);
387290001Sglebius        /* NOTREACHED */
388290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
389290001Sglebius    }
390290001Sglebius
391290001Sglebius    ct = opt_match_ct(opts, opt_name, nm_len, &idx, &disable);
392290001Sglebius
393290001Sglebius    /*
394290001Sglebius     *  See if we found one match, no matches or multiple matches.
395290001Sglebius     */
396290001Sglebius    switch (ct) {
397290001Sglebius    case 1:  return opt_set(opts, opt_arg, idx, disable, state);
398290001Sglebius    case 0:  return opt_unknown(opts, opt_name, opt_arg, state);
399290001Sglebius    default: return opt_ambiguous(opts, opt_name, ct);
400290001Sglebius    }
401290001Sglebius}
402290001Sglebius
403290001Sglebius
404290001Sglebius/**
405290001Sglebius *  Find the short option descriptor for the current option
406290001Sglebius *
407290001Sglebius * @param pOpts      option data
408290001Sglebius * @param optValue   option flag character
409290001Sglebius * @param pOptState  state about current option
410290001Sglebius */
411290001SglebiusLOCAL tSuccess
412290001Sglebiusopt_find_short(tOptions * pOpts, uint_t optValue, tOptState * pOptState)
413290001Sglebius{
414290001Sglebius    tOptDesc * pRes = pOpts->pOptDesc;
415290001Sglebius    int        ct   = pOpts->optCt;
416290001Sglebius
417290001Sglebius    /*
418290001Sglebius     *  Search the option list
419290001Sglebius     */
420290001Sglebius    do  {
421290001Sglebius        if (optValue != pRes->optValue)
422290001Sglebius            continue;
423290001Sglebius
424290001Sglebius        if (SKIP_OPT(pRes)) {
425290001Sglebius            if (  (pRes->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
426290001Sglebius               && (pRes->pz_Name != NULL)) {
427290001Sglebius                if ((pOpts->fOptSet & OPTPROC_ERRSTOP) == 0)
428290001Sglebius                    return FAILURE;
429290001Sglebius
430290001Sglebius                fprintf(stderr, zDisabledErr, pOpts->pzProgPath, pRes->pz_Name);
431290001Sglebius                if (pRes->pzText != NULL)
432290001Sglebius                    fprintf(stderr, SET_OFF_FMT, pRes->pzText);
433290001Sglebius                fputc(NL, stderr);
434290001Sglebius                (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
435290001Sglebius                /* NOTREACHED */
436290001Sglebius                _exit(EXIT_FAILURE); /* to be certain */
437290001Sglebius            }
438290001Sglebius            goto short_opt_error;
439290001Sglebius        }
440290001Sglebius
441290001Sglebius        pOptState->pOD     = pRes;
442290001Sglebius        pOptState->optType = TOPT_SHORT;
443290001Sglebius        return SUCCESS;
444290001Sglebius
445290001Sglebius    } while (pRes++, --ct > 0);
446290001Sglebius
447290001Sglebius    /*
448290001Sglebius     *  IF    the character value is a digit
449290001Sglebius     *    AND there is a special number option ("-n")
450290001Sglebius     *  THEN the result is the "option" itself and the
451290001Sglebius     *       option is the specially marked "number" option.
452290001Sglebius     */
453290001Sglebius    if (  IS_DEC_DIGIT_CHAR(optValue)
454290001Sglebius       && (pOpts->specOptIdx.number_option != NO_EQUIVALENT) ) {
455290001Sglebius        pOptState->pOD = \
456290001Sglebius        pRes           = pOpts->pOptDesc + pOpts->specOptIdx.number_option;
457290001Sglebius        (pOpts->pzCurOpt)--;
458290001Sglebius        pOptState->optType = TOPT_SHORT;
459290001Sglebius        return SUCCESS;
460290001Sglebius    }
461290001Sglebius
462290001Sglebius short_opt_error:
463290001Sglebius
464290001Sglebius    /*
465290001Sglebius     *  IF we are to stop on errors (the default, actually)
466290001Sglebius     *  THEN call the usage procedure.
467290001Sglebius     */
468290001Sglebius    if ((pOpts->fOptSet & OPTPROC_ERRSTOP) != 0) {
469290001Sglebius        fprintf(stderr, zIllOptChr, pOpts->pzProgPath, optValue);
470290001Sglebius        (*pOpts->pUsageProc)(pOpts, EXIT_FAILURE);
471290001Sglebius        /* NOTREACHED */
472290001Sglebius        _exit(EXIT_FAILURE); /* to be certain */
473290001Sglebius    }
474290001Sglebius
475290001Sglebius    return FAILURE;
476290001Sglebius}
477290001Sglebius
478290001Sglebius/**
479290001Sglebius *  Process option with a required argument.  Long options can either have a
480290001Sglebius *  separate command line argument, or an argument attached by the '='
481290001Sglebius *  character.  Figure out which.
482290001Sglebius *
483290001Sglebius *  @param[in,out] opts  the program option descriptor
484290001Sglebius *  @param[in,out] o_st  the option processing state
485290001Sglebius *  @returns SUCCESS or FAILURE
486290001Sglebius */
487290001Sglebiusstatic tSuccess
488290001Sglebiusget_opt_arg_must(tOptions * opts, tOptState * o_st)
489290001Sglebius{
490290001Sglebius    switch (o_st->optType) {
491290001Sglebius    case TOPT_SHORT:
492290001Sglebius        /*
493290001Sglebius         *  See if an arg string follows the flag character
494290001Sglebius         */
495290001Sglebius        if (*++(opts->pzCurOpt) == NUL)
496290001Sglebius            opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx++ ];
497290001Sglebius        o_st->pzOptArg = opts->pzCurOpt;
498290001Sglebius        break;
499290001Sglebius
500290001Sglebius    case TOPT_LONG:
501290001Sglebius        /*
502290001Sglebius         *  See if an arg string has already been assigned (glued on
503290001Sglebius         *  with an `=' character)
504290001Sglebius         */
505290001Sglebius        if (o_st->pzOptArg == NULL)
506290001Sglebius            o_st->pzOptArg = opts->origArgVect[ opts->curOptIdx++ ];
507290001Sglebius        break;
508290001Sglebius
509290001Sglebius    default:
510290001Sglebius#ifdef DEBUG
511290001Sglebius        fputs("AutoOpts lib error: option type not selected\n", stderr);
512290001Sglebius        option_exits(EXIT_FAILURE);
513290001Sglebius#endif
514290001Sglebius
515290001Sglebius    case TOPT_DEFAULT:
516290001Sglebius        /*
517290001Sglebius         *  The option was selected by default.  The current token is
518290001Sglebius         *  the option argument.
519290001Sglebius         */
520290001Sglebius        break;
521290001Sglebius    }
522290001Sglebius
523290001Sglebius    /*
524290001Sglebius     *  Make sure we did not overflow the argument list.
525290001Sglebius     */
526290001Sglebius    if (opts->curOptIdx > opts->origArgCt) {
527290001Sglebius        fprintf(stderr, zMisArg, opts->pzProgPath, o_st->pOD->pz_Name);
528290001Sglebius        return FAILURE;
529290001Sglebius    }
530290001Sglebius
531290001Sglebius    opts->pzCurOpt = NULL;  /* next time advance to next arg */
532290001Sglebius    return SUCCESS;
533290001Sglebius}
534290001Sglebius
535290001Sglebius/**
536290001Sglebius * Process an option with an optional argument.  For short options, it looks
537290001Sglebius * at the character after the option character, or it consumes the next full
538290001Sglebius * argument.  For long options, it looks for an '=' character attachment to
539290001Sglebius * the long option name before deciding to take the next command line
540290001Sglebius * argument.
541290001Sglebius *
542290001Sglebius * @param pOpts      the option descriptor
543290001Sglebius * @param o_st  a structure for managing the current processing state
544290001Sglebius * @returns SUCCESS or does not return
545290001Sglebius */
546290001Sglebiusstatic tSuccess
547290001Sglebiusget_opt_arg_may(tOptions * pOpts, tOptState * o_st)
548290001Sglebius{
549290001Sglebius    /*
550290001Sglebius     *  An option argument is optional.
551290001Sglebius     */
552290001Sglebius    switch (o_st->optType) {
553290001Sglebius    case TOPT_SHORT:
554290001Sglebius        if (*++pOpts->pzCurOpt != NUL)
555290001Sglebius            o_st->pzOptArg = pOpts->pzCurOpt;
556290001Sglebius        else {
557290001Sglebius            char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
558290001Sglebius
559290001Sglebius            /*
560290001Sglebius             *  BECAUSE it is optional, we must make sure
561290001Sglebius             *  we did not find another flag and that there
562290001Sglebius             *  is such an argument.
563290001Sglebius             */
564290001Sglebius            if ((pzLA == NULL) || (*pzLA == '-'))
565290001Sglebius                o_st->pzOptArg = NULL;
566290001Sglebius            else {
567290001Sglebius                pOpts->curOptIdx++; /* argument found */
568290001Sglebius                o_st->pzOptArg = pzLA;
569290001Sglebius            }
570290001Sglebius        }
571290001Sglebius        break;
572290001Sglebius
573290001Sglebius    case TOPT_LONG:
574290001Sglebius        /*
575290001Sglebius         *  Look for an argument if we don't already have one (glued on
576290001Sglebius         *  with a `=' character) *AND* we are not in named argument mode
577290001Sglebius         */
578290001Sglebius        if (  (o_st->pzOptArg == NULL)
579290001Sglebius           && (! NAMED_OPTS(pOpts))) {
580290001Sglebius            char * pzLA = pOpts->origArgVect[ pOpts->curOptIdx ];
581290001Sglebius
582290001Sglebius            /*
583290001Sglebius             *  BECAUSE it is optional, we must make sure
584290001Sglebius             *  we did not find another flag and that there
585290001Sglebius             *  is such an argument.
586290001Sglebius             */
587290001Sglebius            if ((pzLA == NULL) || (*pzLA == '-'))
588290001Sglebius                o_st->pzOptArg = NULL;
589290001Sglebius            else {
590290001Sglebius                pOpts->curOptIdx++; /* argument found */
591290001Sglebius                o_st->pzOptArg = pzLA;
592290001Sglebius            }
593290001Sglebius        }
594290001Sglebius        break;
595290001Sglebius
596290001Sglebius    default:
597290001Sglebius    case TOPT_DEFAULT:
598290001Sglebius        ao_bug(zbad_default_msg);
599290001Sglebius    }
600290001Sglebius
601290001Sglebius    /*
602290001Sglebius     *  After an option with an optional argument, we will
603290001Sglebius     *  *always* start with the next option because if there
604290001Sglebius     *  were any characters following the option name/flag,
605290001Sglebius     *  they would be interpreted as the argument.
606290001Sglebius     */
607290001Sglebius    pOpts->pzCurOpt = NULL;
608290001Sglebius    return SUCCESS;
609290001Sglebius}
610290001Sglebius
611290001Sglebius/**
612290001Sglebius *  Process option that does not have an argument.
613290001Sglebius *
614290001Sglebius *  @param[in,out] opts  the program option descriptor
615290001Sglebius *  @param[in,out] o_st  the option processing state
616290001Sglebius *  @returns SUCCESS or FAILURE
617290001Sglebius */
618290001Sglebiusstatic tSuccess
619290001Sglebiusget_opt_arg_none(tOptions * pOpts, tOptState * o_st)
620290001Sglebius{
621290001Sglebius    /*
622290001Sglebius     *  No option argument.  Make sure next time around we find
623290001Sglebius     *  the correct option flag character for short options
624290001Sglebius     */
625290001Sglebius    if (o_st->optType == TOPT_SHORT)
626290001Sglebius        (pOpts->pzCurOpt)++;
627290001Sglebius
628290001Sglebius    /*
629290001Sglebius     *  It is a long option.  Make sure there was no ``=xxx'' argument
630290001Sglebius     */
631290001Sglebius    else if (o_st->pzOptArg != NULL) {
632290001Sglebius        fprintf(stderr, zNoArg, pOpts->pzProgPath, o_st->pOD->pz_Name);
633290001Sglebius        return FAILURE;
634290001Sglebius    }
635290001Sglebius
636290001Sglebius    /*
637290001Sglebius     *  It is a long option.  Advance to next command line argument.
638290001Sglebius     */
639290001Sglebius    else
640290001Sglebius        pOpts->pzCurOpt = NULL;
641290001Sglebius    return SUCCESS;
642290001Sglebius}
643290001Sglebius
644290001Sglebius/**
645290001Sglebius *  Process option.  Figure out whether or not to look for an option argument.
646290001Sglebius *
647290001Sglebius *  @param[in,out] opts  the program option descriptor
648290001Sglebius *  @param[in,out] o_st  the option processing state
649290001Sglebius *  @returns SUCCESS or FAILURE
650290001Sglebius */
651290001SglebiusLOCAL tSuccess
652290001Sglebiusget_opt_arg(tOptions * opts, tOptState * o_st)
653290001Sglebius{
654290001Sglebius    o_st->flags |= (o_st->pOD->fOptState & OPTST_PERSISTENT_MASK);
655290001Sglebius
656290001Sglebius    /*
657290001Sglebius     * Disabled options and options specified to not have arguments
658290001Sglebius     * are handled with the "none" procedure.  Otherwise, check the
659290001Sglebius     * optional flag and call either the "may" or "must" function.
660290001Sglebius     */
661290001Sglebius    if (  ((o_st->flags & OPTST_DISABLED) != 0)
662290001Sglebius       || (OPTST_GET_ARGTYPE(o_st->flags) == OPARG_TYPE_NONE))
663290001Sglebius        return get_opt_arg_none(opts, o_st);
664290001Sglebius
665290001Sglebius    if (o_st->flags & OPTST_ARG_OPTIONAL)
666290001Sglebius        return get_opt_arg_may( opts, o_st);
667290001Sglebius
668290001Sglebius    return get_opt_arg_must(opts, o_st);
669290001Sglebius}
670290001Sglebius
671290001Sglebius/**
672290001Sglebius *  Find the option descriptor for the current option.
673290001Sglebius *
674290001Sglebius *  @param[in,out] opts  the program option descriptor
675290001Sglebius *  @param[in,out] o_st  the option processing state
676290001Sglebius *  @returns SUCCESS or FAILURE
677290001Sglebius */
678290001SglebiusLOCAL tSuccess
679290001Sglebiusfind_opt(tOptions * opts, tOptState * o_st)
680290001Sglebius{
681290001Sglebius    /*
682290001Sglebius     *  IF we are continuing a short option list (e.g. -xyz...)
683290001Sglebius     *  THEN continue a single flag option.
684290001Sglebius     *  OTHERWISE see if there is room to advance and then do so.
685290001Sglebius     */
686290001Sglebius    if ((opts->pzCurOpt != NULL) && (*opts->pzCurOpt != NUL))
687290001Sglebius        return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
688290001Sglebius
689290001Sglebius    if (opts->curOptIdx >= opts->origArgCt)
690290001Sglebius        return PROBLEM; /* NORMAL COMPLETION */
691290001Sglebius
692290001Sglebius    opts->pzCurOpt = opts->origArgVect[ opts->curOptIdx ];
693290001Sglebius
694290001Sglebius    /*
695290001Sglebius     *  IF all arguments must be named options, ...
696290001Sglebius     */
697290001Sglebius    if (NAMED_OPTS(opts)) {
698290001Sglebius        char *      pz  = opts->pzCurOpt;
699290001Sglebius        int         def;
700290001Sglebius        tSuccess    res;
701290001Sglebius        uint16_t *  def_opt;
702290001Sglebius
703290001Sglebius        opts->curOptIdx++;
704290001Sglebius
705290001Sglebius        if (*pz != '-')
706290001Sglebius            return opt_find_long(opts, pz, o_st);
707290001Sglebius
708290001Sglebius        /*
709290001Sglebius         *  The name is prefixed with one or more hyphens.  Strip them off
710290001Sglebius         *  and disable the "default_opt" setting.  Use heavy recasting to
711290001Sglebius         *  strip off the "const" quality of the "default_opt" field.
712290001Sglebius         */
713290001Sglebius        while (*(++pz) == '-')   ;
714290001Sglebius        def_opt  = VOIDP(&(opts->specOptIdx.default_opt));
715290001Sglebius        def      = *def_opt;
716290001Sglebius        *def_opt = NO_EQUIVALENT;
717290001Sglebius        res      = opt_find_long(opts, pz, o_st);
718290001Sglebius        *def_opt = (uint16_t)def;
719290001Sglebius        return res;
720290001Sglebius    }
721290001Sglebius
722290001Sglebius    /*
723290001Sglebius     *  Note the kind of flag/option marker
724290001Sglebius     */
725290001Sglebius    if (*((opts->pzCurOpt)++) != '-')
726290001Sglebius        return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
727290001Sglebius
728290001Sglebius    /*
729290001Sglebius     *  Special hack for a hyphen by itself
730290001Sglebius     */
731290001Sglebius    if (*(opts->pzCurOpt) == NUL)
732290001Sglebius        return PROBLEM; /* NORMAL COMPLETION - this + rest are operands */
733290001Sglebius
734290001Sglebius    /*
735290001Sglebius     *  The current argument is to be processed as an option argument
736290001Sglebius     */
737290001Sglebius    opts->curOptIdx++;
738290001Sglebius
739290001Sglebius    /*
740290001Sglebius     *  We have an option marker.
741290001Sglebius     *  Test the next character for long option indication
742290001Sglebius     */
743290001Sglebius    if (opts->pzCurOpt[0] == '-') {
744290001Sglebius        if (*++(opts->pzCurOpt) == NUL)
745290001Sglebius            /*
746290001Sglebius             *  NORMAL COMPLETION - NOT this arg, but rest are operands
747290001Sglebius             */
748290001Sglebius            return PROBLEM;
749290001Sglebius
750290001Sglebius        /*
751290001Sglebius         *  We do not allow the hyphen to be used as a flag value.
752290001Sglebius         *  Therefore, if long options are not to be accepted, we punt.
753290001Sglebius         */
754290001Sglebius        if ((opts->fOptSet & OPTPROC_LONGOPT) == 0) {
755290001Sglebius            fprintf(stderr, zIllOptStr, opts->pzProgPath, opts->pzCurOpt-2);
756290001Sglebius            return FAILURE;
757290001Sglebius        }
758290001Sglebius
759290001Sglebius        return opt_find_long(opts, opts->pzCurOpt, o_st);
760290001Sglebius    }
761290001Sglebius
762290001Sglebius    /*
763290001Sglebius     *  If short options are not allowed, then do long
764290001Sglebius     *  option processing.  Otherwise the character must be a
765290001Sglebius     *  short (i.e. single character) option.
766290001Sglebius     */
767290001Sglebius    if ((opts->fOptSet & OPTPROC_SHORTOPT) != 0)
768290001Sglebius        return opt_find_short(opts, (uint8_t)*(opts->pzCurOpt), o_st);
769290001Sglebius
770290001Sglebius    return opt_find_long(opts, opts->pzCurOpt, o_st);
771290001Sglebius}
772290001Sglebius
773290001Sglebius/** @}
774290001Sglebius *
775290001Sglebius * Local Variables:
776290001Sglebius * mode: C
777290001Sglebius * c-file-style: "stroustrup"
778290001Sglebius * indent-tabs-mode: nil
779290001Sglebius * End:
780290001Sglebius * end of autoopts/find.c */
781