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