find.c revision 1.9 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