1 /*
2  * Copyright (C) 1997-2004, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 /**
25  * @file options.c
26  * Command Line Option Parser Source File
27  *
28  * This file contains the functions which comprise the command line
29  * option parser.
30  *
31  * @author Michael Jennings <mej@eterm.org>
32  * $Revision: 1.18 $
33  * $Date: 2004/10/26 18:01:53 $
34  */
35 
36 static const char __attribute__((unused)) cvs_ident[] = "$Id: options.c,v 1.18 2004/10/26 18:01:53 mej Exp $";
37 
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
41 
42 #include <libast_internal.h>
43 
44 /*@{*/
45 /**
46  * @name Internal Parser Macros
47  * Macros to simply certain parser operations.
48  *
49  * This group of macros is used internally by the command line option
50  * parser.  They are not available for use by client programs and are
51  * documented here solely for completeness and clarity.
52  *
53  * @see @link DOXGRP_OPT Command Line Option Parser @endlink
54  * @ingroup DOXGRP_OPT
55  */
56 
57 /** Next argument.  Proceed to parsing the next argument in the argv[] list. */
58 #define NEXT_ARG()       D_OPTIONS(("NEXT_ARG()\n")); i++; opt = SPIF_CHARPTR(argv[i]); continue
59 /** Next letter.  Proceed to the next letter in a bundled option series. */
60 #define NEXT_LETTER()    D_OPTIONS(("NEXT_LETTER(%s)\n", opt)); if (*(opt + 1)) {opt++;} else {NEXT_ARG();} continue
61 /** Next loop.  Proceed to the next parsing stage (letter or word). */
62 #define NEXT_LOOP()      D_OPTIONS(("NEXT_LOOP()\n")); if (islong || val_ptr) {NEXT_ARG();} else {NEXT_LETTER();} NOP
63 /** Option parse test.  Returns true IFF the option should be parsed on this pass. */
64 #define SHOULD_PARSE(j)  ((SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE) && SPIFOPT_OPT_IS_PREPARSE(j)) \
65                            || (!SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE) && !SPIFOPT_OPT_IS_PREPARSE(j)))
66 /*@}*/
67 
68 /**
69  * Internal option parser settings.
70  *
71  * This variable holds the actual structure containing the settings
72  * for the option parser.  It should never be accessed directly, but
73  * rather through use of the Option Parser Settings Macros.
74  *
75  * @see @link DOXGRP_OPT Command Line Option Parser @endlink
76  * @ingroup DOXGRP_OPT
77  */
78 spifopt_settings_t spifopt_settings = { NULL, 0, 0, 0, 0, 0, NULL };
79 
80 /**
81  * Option-type-to-string translator.
82  *
83  * This function is used internally by spifopt_usage() to convert an
84  * option's numerical type to a short string representing that type of
85  * option.
86  *
87  * @param type The numeric option type (SPIFOPT_OPT_FLAGS()).
88  * @return     A 6-char-max string describing the option type.
89  *
90  * @see @link DOXGRP_OPT Command Line Option Parser @endlink
91  * @ingroup DOXGRP_OPT
92  */
93 static spif_charptr_t
get_option_type_string(spif_uint16_t type)94 get_option_type_string(spif_uint16_t type)
95 {
96     switch (type) {
97         case SPIFOPT_FLAG_BOOLEAN: return SPIF_CHARPTR("(bool)"); break;
98         case SPIFOPT_FLAG_INTEGER: return SPIF_CHARPTR("(int)"); break;
99         case SPIFOPT_FLAG_ARGLIST: return SPIF_CHARPTR("(strs)"); break;
100         default: return SPIF_CHARPTR("(str)");
101     }
102     ASSERT_NOTREACHED_RVAL(NULL);
103 }
104 
105 /**
106  * Built-in default function for displaying help information.
107  *
108  * This is the default "help handler" function.  It displays a list of
109  * long and short options along with the description of each.  It also
110  * prints out a brief type identifier as produced by
111  * get_option_type_string().
112  *
113  * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, SPIFOPT_HELPHANDLER_SET(), spifopt_helphandler_t
114  * @ingroup DOXGRP_MEM
115  */
116 void
spifopt_usage(void)117 spifopt_usage(void)
118 {
119     spif_uint16_t i, col, l_long = 0, l_desc = 0;
120 
121     /* Find the longest long option and the longest description. */
122     for (i = 0; i < SPIFOPT_NUMOPTS_GET(); i++) {
123         MAX_IT(l_long, strlen(SPIFOPT_OPT_LONG(i)));
124         MAX_IT(l_desc, strlen(SPIFOPT_OPT_DESC(i)));
125     }
126     l_long += 2;  /* Add 2 for the "--" */
127     l_desc += 7;  /* Add 7 for the type and a space */
128 
129     printf("%s %s\n", libast_program_name, libast_program_version);
130     printf("Usage:\n\n");
131     printf("POSIX ");
132 
133     for (col = 0; col < (l_long - 3) / 2; col++) printf(" ");
134     printf("GNU");
135     for (col = 0; col < (l_long - 3) / 2; col++) printf(" ");
136     if (!(l_long % 2)) {
137         printf(" ");
138     }
139     printf("  ");
140 
141     for (col = 0; col < (l_desc - 11) / 2; col++) printf(" ");
142     printf("Description");
143     for (col = 0; col < (l_desc - 11) / 2; col++) printf(" ");
144     if (!(l_desc % 2)) {
145         printf(" ");
146     }
147 
148     printf("\n");
149     printf("----- ");
150 
151     for (col = 0; col < l_long; col++) printf("-");
152     printf("  ");
153     for (col = 0; col < l_desc; col++) printf("-");
154     printf("\n");
155 
156     for (i = 0, l_long -= 2; i < SPIFOPT_NUMOPTS_GET(); i++) {
157         if (SPIFOPT_OPT_SHORT(i)) {
158             printf(" -%c   ", SPIFOPT_OPT_SHORT(i));
159         } else {
160             printf("      ");
161         }
162         printf("--%s", SPIFOPT_OPT_LONG(i));
163         for (col = strlen(SPIF_CAST_C(char *) SPIFOPT_OPT_LONG(i)); col < l_long; col++) {
164             printf(" ");
165         }
166         printf("  %-6s %s\n", get_option_type_string(SPIFOPT_OPT_TYPE(i)), SPIFOPT_OPT_DESC(i));
167     }
168     exit(EXIT_FAILURE);
169 }
170 
171 /**
172  * Find matching long option.
173  *
174  * This function searches the option list for a long option which
175  * matches the given string.
176  *
177  * @param opt The long option string to match against.
178  * @return    The index of the matching option, or -1 if not found.
179  *
180  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, is_valid_option()
181  * @ingroup DOXGRP_OPT
182  */
183 static spif_int32_t
find_long_option(spif_charptr_t opt)184 find_long_option(spif_charptr_t opt)
185 {
186     spif_int32_t j;
187 
188     D_OPTIONS(("opt == \"%s\"\n", NONULL(opt)));
189     /* Check to see if we have a long option that matches this. */
190     for (j = 0; j < SPIFOPT_NUMOPTS_GET(); j++) {
191         size_t l;
192 
193         l = strlen(SPIF_CHARPTR_C(SPIFOPT_OPT_LONG(j)));
194         /* Look for matches to the part before the =, if any. */
195         if (!strncasecmp(SPIF_CHARPTR_C(SPIFOPT_OPT_LONG(j)), SPIF_CHARPTR_C(opt), l)
196             && (opt[l] == '=' || !opt[l])) {
197             /* Got one. */
198             D_OPTIONS(("Match found at %d:  %s == %s\n", j, SPIFOPT_OPT_LONG(j), opt));
199             return j;
200         }
201     }
202     /* No matching long option found.  Report an error and
203        continue with the next arg. */
204     libast_print_error("Unrecognized long option --%s\n", opt);
205     CHECK_BAD();
206     return ((spif_int32_t) -1);
207 }
208 
209 /**
210  * Find matching short option.
211  *
212  * This function searches the option list for a short option which
213  * matches the given character.
214  *
215  * @param opt The short option character to match against.
216  * @return    The index of the matching option, or -1 if not found.
217  *
218  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, is_valid_option()
219  * @ingroup DOXGRP_OPT
220  */
221 static spif_int32_t
find_short_option(char opt)222 find_short_option(char opt)
223 {
224     spif_int32_t j;
225 
226     D_OPTIONS(("opt == \"%c\"\n", opt));
227     for (j = 0; j < SPIFOPT_NUMOPTS_GET(); j++) {
228         if (SPIFOPT_OPT_SHORT(j) == opt) {
229             D_OPTIONS(("Match found at %d:  %c == %c\n", j, SPIFOPT_OPT_SHORT(j), opt));
230             return j;
231         }
232     }
233     libast_print_error("unrecognized option -%c\n", opt);
234     CHECK_BAD();
235     return ((spif_int32_t) -1);
236 }
237 
238 /**
239  * Find the value for a long option.
240  *
241  * This function looks for and returns the value associated with a
242  * long option, or NULL if one is not found.
243  *
244  * @param arg      The argument containing the long option.
245  * @param next_arg The next word on the command line.
246  * @param hasequal Address of a toggle variable to return whether or
247  *                 not the value was appended to the option with an
248  *                 equals sign ('=').
249  * @return         The option's value string, or NULL if no value is
250  *                 found.
251  *
252  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse()
253  * @ingroup DOXGRP_OPT
254  */
255 static spif_charptr_t
find_value_long(spif_charptr_t arg,spif_charptr_t next_arg,spif_charptr_t hasequal)256 find_value_long(spif_charptr_t arg, spif_charptr_t next_arg, spif_charptr_t hasequal)
257 {
258     spif_charptr_t val_ptr;
259 
260     if ((val_ptr = SPIF_CHARPTR(strchr(SPIF_CHARPTR_C(arg), '='))) != NULL) {
261         val_ptr++;
262         *hasequal = 1;
263     } else {
264         if (next_arg) {
265             val_ptr = next_arg;
266         }
267         *hasequal = 0;
268     }
269     D_OPTIONS(("hasequal == %d  val_ptr == %10.8p \"%s\"\n", *hasequal, val_ptr, NONULL(val_ptr)));
270     return val_ptr;
271 }
272 
273 /**
274  * Find the value for a short option.
275  *
276  * This function looks for and returns the value associated with a
277  * short option, or NULL if one is not found.
278  *
279  * @param arg      The argument containing the short option.
280  * @param next_arg The next word on the command line.
281  * @return         The option's value string, or NULL if no value is
282  *                 found.
283  *
284  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse()
285  * @ingroup DOXGRP_OPT
286  */
287 static spif_charptr_t
find_value_short(spif_charptr_t arg,spif_charptr_t next_arg)288 find_value_short(spif_charptr_t arg, spif_charptr_t next_arg)
289 {
290     spif_charptr_t val_ptr = NULL;
291 
292     if (arg[1]) {
293         val_ptr = arg + 1;
294     } else if (next_arg != NULL) {
295         val_ptr = next_arg;
296     }
297     D_OPTIONS(("val_ptr == %10.8p \"%s\"\n", val_ptr, NONULL(val_ptr)));
298     return val_ptr;
299 }
300 
301 /**
302  * Test for a valid boolean value.
303  *
304  * This function compares the given value pointer to the possible
305  * values for a boolean option.
306  *
307  * @param val_ptr The value to be tested.
308  * @return        TRUE if boolean, FALSE if not.
309  *
310  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse()
311  * @ingroup DOXGRP_OPT
312  */
313 static spif_bool_t
is_boolean_value(spif_charptr_t val_ptr)314 is_boolean_value(spif_charptr_t val_ptr)
315 {
316     if (!(val_ptr) || !(*val_ptr)) {
317         return FALSE;
318     }
319     return ((BOOL_OPT_ISTRUE(val_ptr) || BOOL_OPT_ISFALSE(val_ptr)) ? (TRUE) : (FALSE));
320 }
321 
322 /**
323  * Check for a match to the current option.
324  *
325  * This function does some initial parsing, then calls the appropriate
326  * sub-function to look for matches.
327  *
328  * @param opt The argument string.
329  * @return    TRUE if a match is found, FALSE otherwise.
330  *
331  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(),
332  *      find_short_option()
333  * @ingroup DOXGRP_OPT
334  */
335 static spif_bool_t
is_valid_option(spif_charptr_t opt)336 is_valid_option(spif_charptr_t opt)
337 {
338     REQUIRE_RVAL(opt != NULL, FALSE);
339 
340     if (*opt != '-') {
341         return FALSE;
342     }
343     opt++;
344     if (*opt == '-') {
345         opt++;
346         if (find_long_option(opt) >= 0) {
347             return TRUE;
348         }
349     } else {
350         if (find_short_option(*opt) >= 0) {
351             return TRUE;
352         }
353     }
354     return FALSE;
355 }
356 
357 /**
358  * Handle a boolean option.
359  *
360  * This function is reponsible for taking the proper action for a
361  * boolean option.  It sets the appropriate bitfield on the
362  * appropriate variable as defined by the settings for that option.
363  *
364  * @param n       The index for the option within the option list.
365  * @param val_ptr A pointer (possibly NULL) to the value specified
366  *                with the option.
367  * @param islong  0 if the option was short, non-zero otherwise.
368  * @return    TRUE if a match is found, FALSE otherwise.
369  *
370  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(),
371  *      find_short_option()
372  * @ingroup DOXGRP_OPT
373  */
374 static spif_bool_t
handle_boolean(spif_int32_t n,spif_charptr_t val_ptr,unsigned char islong)375 handle_boolean(spif_int32_t n, spif_charptr_t val_ptr, unsigned char islong)
376 {
377     D_OPTIONS(("Boolean option detected\n"));
378     if (val_ptr && islong) {
379         /* There's a value, so let's see what it is. */
380         if (BOOL_OPT_ISTRUE(val_ptr)) {
381             if (SHOULD_PARSE(n)) {
382                 D_OPTIONS(("\"%s\" == TRUE\n", val_ptr));
383                 *((unsigned long *) SPIFOPT_OPT_VALUE(n)) |= SPIFOPT_OPT_MASK(n);
384             }
385         } else if (BOOL_OPT_ISFALSE(val_ptr)) {
386             if (SHOULD_PARSE(n)) {
387                 D_OPTIONS(("\"%s\" == FALSE\n", val_ptr));
388                 *((unsigned long *) SPIFOPT_OPT_VALUE(n)) &= ~SPIFOPT_OPT_MASK(n);
389             }
390         } else {
391             if (SHOULD_PARSE(n)) {
392                 D_OPTIONS(("Forcing option --%s to TRUE\n", SPIFOPT_OPT_LONG(n)));
393                 *((unsigned long *) SPIFOPT_OPT_VALUE(n)) |= SPIFOPT_OPT_MASK(n);
394             }
395             return FALSE;
396         }
397     } else {
398         if (SHOULD_PARSE(n)) {
399             /* No value, or it was a short option, so pretend it was true. */
400             if (islong) {
401                 D_OPTIONS(("Forcing option --%s to TRUE\n", SPIFOPT_OPT_LONG(n)));
402             } else {
403                 val_ptr = NULL;
404                 D_OPTIONS(("Forcing option -%c to TRUE\n", SPIFOPT_OPT_SHORT(n)));
405             }
406             *((unsigned long *) SPIFOPT_OPT_VALUE(n)) |= SPIFOPT_OPT_MASK(n);
407         }
408     }
409     return TRUE;
410 }
411 
412 /**
413  * Handle an integer option.
414  *
415  * This function is responsible for taking the appropriate action when
416  * an integer option is encountered.  The variable whose address was
417  * given to the option structure is assigned the value of the option.
418  *
419  * @param n       The index of the option.
420  * @param val_ptr The value passed to the option.
421  *
422  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(),
423  *      find_short_option()
424  * @ingroup DOXGRP_OPT
425  */
426 static void
handle_integer(spif_int32_t n,spif_charptr_t val_ptr)427 handle_integer(spif_int32_t n, spif_charptr_t val_ptr)
428 {
429     D_OPTIONS(("Integer option detected\n"));
430     *((int *) SPIFOPT_OPT_VALUE(n)) = strtol(SPIF_CHARPTR_C(val_ptr), (char **) NULL, 0);
431 }
432 
433 /**
434  * Handle a string option.
435  *
436  * This function is responsible for taking the appropriate action when
437  * a string option is encountered.  The variable whose address was
438  * given to the option structure is assigned the value of the option.
439  *
440  * @param n       The index of the option.
441  * @param val_ptr The value passed to the option.
442  *
443  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(),
444  *      find_short_option()
445  * @ingroup DOXGRP_OPT
446  */
447 static void
handle_string(spif_int32_t n,spif_charptr_t val_ptr)448 handle_string(spif_int32_t n, spif_charptr_t val_ptr)
449 {
450     D_OPTIONS(("String option detected\n"));
451     *((const char **) SPIFOPT_OPT_VALUE(n)) = SPIF_CAST_C(char *) STRDUP(val_ptr);
452 }
453 
454 
455 /**
456  * Handle an argument list option.
457  *
458  * This function is responsible for taking the appropriate action when
459  * an argument list option is encountered.  An array of arguments is
460  * created at the specified address.  There can be only one of these.
461  *
462  * @param n        The index of the option.
463  * @param val_ptr  The value passed to the option.
464  * @param hasequal TRUE if the long option used '=', FALSE otherwise.
465  * @param i        The index of the current option within argc[].
466  * @param argc     The argument count.
467  * @param argv     The argument list.
468  *
469  * @see @link DOXGRP_OPT Command Line Option Parser @endlink, spifopt_parse(), find_long_option(),
470  *      find_short_option()
471  * @ingroup DOXGRP_OPT
472  */
473 static void
handle_arglist(spif_int32_t n,spif_charptr_t val_ptr,unsigned char hasequal,spif_int32_t i,int argc,char * argv[])474 handle_arglist(spif_int32_t n, spif_charptr_t val_ptr, unsigned char hasequal,
475                spif_int32_t i, int argc, char *argv[])
476 {
477     spif_charptr_t *tmp;
478     register unsigned short k;
479 
480     D_OPTIONS(("Argument list option detected\n"));
481     if (hasequal) {
482         /* There's an equals sign, so just parse the rest of this option into words. */
483         tmp = SPIF_CAST_PTR(charptr) MALLOC(sizeof(spif_charptr_t) * (spiftool_num_words(val_ptr) + 1));
484 
485         for (k = 0; val_ptr; k++) {
486             tmp[k] = spiftool_get_word(1, val_ptr);
487             val_ptr = spiftool_get_pword(2, val_ptr);
488             D_OPTIONS(("tmp[%d] == %s\n", k, tmp[k]));
489         }
490         tmp[k] = SPIF_NULL_TYPE(charptr);
491         *(SPIF_CAST_C(spif_charptr_t **) SPIFOPT_OPT_VALUE(n)) = tmp;
492     } else {
493         unsigned short len = argc - i;
494 
495         /* No equals sign, so use the rest of the command line and break. */
496         tmp = SPIF_CAST_PTR(charptr) MALLOC(sizeof(spif_charptr_t ) * (argc - i + 1));
497 
498         for (k = 0; k < len; k++) {
499             tmp[k] = SPIF_CAST(charptr) STRDUP(argv[k + i]);
500             D_OPTIONS(("tmp[%d] == %s\n", k, tmp[k]));
501             argv[k + i] = NULL;
502         }
503         tmp[k] = SPIF_NULL_TYPE(charptr);
504         *(SPIF_CAST_C(spif_charptr_t **) SPIFOPT_OPT_VALUE(n)) = tmp;
505     }
506 }
507 
508 /**
509  * Parse the command line arguments for options.
510  *
511  * This function iterates through the command line arguments looking
512  * for options which have been defined.  Each option encountered is
513  * handled according to its type.
514  *
515  * @param argc The number of arguments.
516  * @param argv The array of argument strings.
517  *
518  * @see @link DOXGRP_OPT Command Line Option Parser @endlink
519  * @ingroup DOXGRP_OPT
520  */
521 void
spifopt_parse(int argc,char * argv[])522 spifopt_parse(int argc, char *argv[])
523 {
524     spif_int32_t i, j;
525     spif_charptr_t opt;
526 
527     REQUIRE(argc > 1);
528     REQUIRE(argv != NULL);
529 
530     /* Process each command line arg one-by-one. */
531     for (i = 1, opt = SPIF_CHARPTR(argv[1]); i < argc; ) {
532         spif_charptr_t val_ptr = NULL;
533         spif_char_t islong = 0, hasequal = 0;
534 
535         D_OPTIONS(("argv[%d] == \"%s\", opt == \"%s\"\n", i, argv[i], opt));
536 
537         if (SPIF_PTR_ISNULL(opt)) {
538             /* NEXT_ARG(); */
539             break;
540         } else if (opt == SPIF_CHARPTR(argv[i])) {
541             /* If it's not an option, skip it. */
542             if (*opt != '-') {
543                 NEXT_ARG();
544             } else {
545                 opt++;
546             }
547         }
548 
549         /* If the second character is also a hyphen, it's a long option. */
550         if (*opt == '-') {
551             islong = 1;
552             /* Skip the leading "--" */
553             opt++;
554             D_OPTIONS(("Long option detected\n"));
555             if ((j = find_long_option(opt)) == -1) {
556                 NEXT_ARG();
557             }
558         } else {
559             if ((j = find_short_option(*opt)) == -1) {
560                 NEXT_LETTER();
561             }
562         }
563         if (!SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE)) {
564             argv[i] = NULL;
565         }
566 
567         /* If a value was passed to this option, set val_ptr to point to it. */
568         if (islong) {
569             val_ptr = find_value_long(SPIF_CHARPTR(opt), SPIF_CHARPTR(argv[i + 1]), &hasequal);
570         } else {
571             val_ptr = find_value_short(opt, SPIF_CHARPTR(argv[i + 1]));
572         }
573 
574         /* Boolean options may or may not have a value... */
575         if (val_ptr) {
576             if (SPIFOPT_OPT_IS_BOOLEAN(j) && !is_boolean_value(val_ptr)) {
577                 val_ptr = NULL;
578             } else if (SPIFOPT_OPT_IS_ABSTRACT(j) && is_valid_option(val_ptr)) {
579                 val_ptr = NULL;
580             }
581         }
582         if (val_ptr) {
583             if (val_ptr == SPIF_CHARPTR(argv[i + 1])) {
584                 i++;
585                 opt += strlen(SPIF_CHARPTR_C(opt));
586             }
587         }
588 
589         /* If this option is deprecated, print a warning before continuing. */
590         if (SPIFOPT_OPT_IS_DEPRECATED(j)) {
591             spif_str_t warn;
592 
593             warn = spif_str_new_from_buff(SPIF_CHARPTR("The "), 128);
594             if (SPIFOPT_OPT_SHORT(j)) {
595                 spif_str_append_char(warn, '-');
596                 spif_str_append_char(warn, SPIFOPT_OPT_SHORT(j));
597                 spif_str_append_from_ptr(warn, SPIF_CHARPTR(" / --"));
598             } else {
599                 spif_str_append_from_ptr(warn, SPIF_CHARPTR("--"));
600             }
601             spif_str_append_from_ptr(warn, SPIFOPT_OPT_LONG(j));
602             spif_str_append_from_ptr(warn, SPIF_CHARPTR(" option is deprecated and should not be used.\n"));
603             libast_print_warning(SPIF_CHARPTR_C(SPIF_STR_STR(warn)));
604             spif_str_del(warn);
605         }
606 
607         /* Make sure that options which require a parameter have them. */
608         if (SPIFOPT_OPT_NEEDS_VALUE(j)) {
609             if (val_ptr == NULL) {
610                 if (islong) {
611                     libast_print_error("long option --%s requires a%s value\n", SPIFOPT_OPT_LONG(j),
612                                 (SPIFOPT_OPT_IS_INTEGER(j)
613                                  ? ("n integer")
614                                  : (SPIFOPT_OPT_IS_STRING(j)
615                                     ? " string"
616                                     : (SPIFOPT_OPT_IS_ARGLIST(j)
617                                        ? "n argument list"
618                                        : ""))));
619                 } else {
620                     libast_print_error("option -%c requires a%s value\n", SPIFOPT_OPT_SHORT(j),
621                                 (SPIFOPT_OPT_IS_INTEGER(j)
622                                  ? ("n integer")
623                                  : (SPIFOPT_OPT_IS_STRING(j)
624                                     ? " string"
625                                     : (SPIFOPT_OPT_IS_ARGLIST(j)
626                                        ? "n argument list"
627                                        : ""))));
628                 }
629                 CHECK_BAD();
630                 continue;
631             }
632             /* Also make sure we know what to do with the value. */
633             if (SPIFOPT_OPT_VALUE(j) == NULL) {
634                 NEXT_LOOP();
635             }
636         } else if (SPIFOPT_OPT_IS_ABSTRACT(j) && SPIFOPT_OPT_VALUE(j) == NULL) {
637             /* Also make sure that abstract options have a function pointer. */
638             NEXT_LOOP();
639         }
640 
641         if (SPIFOPT_OPT_IS_BOOLEAN(j)) {
642             if (!handle_boolean(j, val_ptr, islong)) {
643                 i--;
644             }
645         } else if (SPIFOPT_OPT_IS_STRING(j)) {
646             if (SHOULD_PARSE(j)) {
647                 handle_string(j, val_ptr);
648             }
649         } else if (SPIFOPT_OPT_IS_INTEGER(j)) {
650             if (SHOULD_PARSE(j)) {
651                 handle_integer(j, val_ptr);
652             }
653         } else if (SPIFOPT_OPT_IS_ARGLIST(j)) {
654             if (SHOULD_PARSE(j)) {
655                 handle_arglist(j, val_ptr, hasequal, i, argc, argv);
656             }
657             if (!hasequal) {
658                 break;
659             }
660         } else if (SPIFOPT_OPT_IS_ABSTRACT(j)) {
661             if (SHOULD_PARSE(j)) {
662                 D_OPTIONS(("Abstract option detected\n"));
663                 ((spifopt_abstract_handler_t) SPIFOPT_OPT_VALUE(j))(val_ptr);
664             }
665         }
666         if (!SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE)) {
667             argv[i] = NULL;
668         }
669         NEXT_LOOP();
670     }
671 
672     if (SPIFOPT_FLAGS_IS_SET(SPIFOPT_SETTING_PREPARSE)) {
673         SPIFOPT_FLAGS_CLEAR(SPIFOPT_SETTING_PREPARSE);
674     } else {
675         for (i = 1, j = 1; i < argc; i++) {
676             if (argv[i]) {
677                 argv[j] = argv[i];
678                 j++;
679             }
680         }
681         if (j > 1) {
682             argv[j] = NULL;
683         }
684     }
685 }
686 
687 /**
688  * @defgroup DOXGRP_OPT Command Line Option Parser
689  *
690  * This group of functions/defines/macros comprises the command line
691  * option parser.
692  *
693  *
694  * A small sample program demonstrating some of these routines can be
695  * found @link opt_example.c here @endlink.
696  */
697 
698 /**
699  * @example opt_example.c
700  * Example code for using the options parser.
701  *
702  */
703