1 /*------------------------------------------------------------------------
2  |  FILE            shhopt.c
3  |
4  |  DESCRIPTION     Functions for parsing command line arguments. Values
5  |                  of miscellaneous types may be stored in variables,
6  |                  or passed to functions as specified.
7  |
8  |  REQUIREMENTS    Some systems lack the ANSI C -function strtoul. If your
9  |                  system is one of those, you'll need to write one yourself,
10  |                  or get the GNU liberty-library (from prep.ai.mit.edu).
11  |
12  |  WRITTEN BY      Sverre H. Huseby <sverrehu@online.no>
13  +----------------------------------------------------------------------*/
14 
15 /*************************************************************************
16   This is based on work by Sverre H. Huseby <sverrehu@online.no>.
17   These functions are backward compatible with the 'shhopt'
18   distributed by Huseby.
19 
20   See the file README.shhopt for copy licensing information.
21 *************************************************************************/
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <limits.h>
29 #include <errno.h>
30 #include <assert.h>
31 
32 #include "mallocvar.h"
33 #include "nstring.h"
34 #include "token.h"
35 #include "shhopt.h"
36 
37 /*-----------------------------------------------------------------------+
38 |  PRIVATE DATA                                                          |
39 +-----------------------------------------------------------------------*/
40 
41 static void optFatalFunc(const char *, ...);
42 static void (*optFatal)(const char *format, ...) = optFatalFunc;
43 
44 /*-----------------------------------------------------------------------+
45 |  PRIVATE FUNCTIONS                                                     |
46 +-----------------------------------------------------------------------*/
47 
48 /*------------------------------------------------------------------------
49  |  NAME          optFatalFunc
50  |
51  |  FUNCTION      Show given message and abort the program.
52  |
53  |  INPUT         format, ...
54  |                        Arguments used as with printf().
55  |
56  |  RETURNS       Never returns. The program is aborted.
57  */
58 static void
optFatalFunc(const char * format,...)59 optFatalFunc(const char *format, ...)
60 {
61     va_list ap;
62 
63     fflush(stdout);
64     va_start(ap, format);
65     vfprintf(stderr, format, ap);
66     va_end(ap);
67     fprintf(stderr, "\n");
68     exit(99);
69 }
70 
71 /*------------------------------------------------------------------------
72  |  NAME          optStructCount
73  |
74  |  FUNCTION      Get number of options in a optStruct.
75  |
76  |  INPUT         opt     array of possible options.
77  |
78  |  RETURNS       Number of options in the given array.
79  |
80  |  DESCRIPTION   Count elements in an optStruct-array. The structure must
81  |                be ended using an element of type OPT_END.
82  */
83 static int
optStructCount(const optEntry opt[])84 optStructCount(const optEntry opt[])
85 {
86     int ret = 0;
87 
88     while (opt[ret].type != OPT_END && ret < 500)
89         ++ret;
90     return ret;
91 }
92 
93 
94 
95 static int
optMatch(optEntry const opt[],const char * const s,int const lng)96 optMatch(optEntry     const opt[],
97          const char * const s,
98          int          const lng) {
99 /*------------------------------------------------------------------------
100  |  FUNCTION      Find a matching option.
101  |
102  |  INPUT         opt     array of possible options.
103  |                s       string to match, without `-' or `--'.
104  |                lng     match long option, otherwise short.
105  |
106  |  RETURNS       Index to the option if found, -1 if not found.
107  |
108  |  DESCRIPTION   Short options are matched from the first character in
109  |                the given string.
110  */
111 
112     unsigned int const nopt = optStructCount(opt);
113 
114     unsigned int q;
115     unsigned int matchlen;
116     const char * p;
117 
118     matchlen = 0;  /* initial value */
119 
120     if (lng) {
121         if ((p = strchr(s, '=')) != NULL)
122             matchlen = p - s;
123         else
124             matchlen = strlen(s);
125     }
126     for (q = 0; q < nopt; ++q) {
127         if (lng) {
128             if (opt[q].longName) {
129                 if (strncmp(s, opt[q].longName, matchlen) == 0)
130                     return q;
131             }
132         } else {
133             if (opt[q].shortName) {
134                 if (s[0] == opt[q].shortName)
135                     return q;
136             }
137         }
138     }
139     return -1;
140 }
141 
142 
143 
144 /*------------------------------------------------------------------------
145  |  NAME          optString
146  |
147  |  FUNCTION      Return a (static) string with the option name.
148  |
149  |  INPUT         opt     the option to stringify.
150  |                lng     is it a long option?
151  |
152  |  RETURNS       Pointer to static string.
153  */
154 static char *
optString(const optEntry opte,int lng)155 optString(const optEntry opte, int lng)
156 {
157     static char ret[31];
158 
159     if (lng) {
160         strcpy(ret, "--");
161         strncpy(ret + 2, opte.longName, 28);
162     } else {
163         ret[0] = '-';
164         ret[1] = opte.shortName;
165         ret[2] = '\0';
166     }
167     return ret;
168 }
169 
170 
171 
172 static optEntry
optStructToEntry(const optStruct opt)173 optStructToEntry(const optStruct opt) {
174 /*----------------------------------------------------------------------------
175    Return the information in 'opt' (an optStruct type) as an optEntry type.
176    optEntry is newer and has an additional field.
177 -----------------------------------------------------------------------------*/
178     optEntry opte;
179 
180     opte.shortName = opt.shortName;
181     opte.longName  = opt.longName;
182     opte.type      = opt.type;
183     opte.arg       = opt.arg;
184     opte.specified = NULL;
185     opte.flags     = opt.flags;
186 
187     return(opte);
188 }
189 
190 
191 
192 static optEntry *
optStructTblToEntryTbl(const optStruct optStructTable[])193 optStructTblToEntryTbl(const optStruct optStructTable[]) {
194 /*----------------------------------------------------------------------------
195    Return a table of optEntry types containing the information in the
196    input table of optStruct types.
197 
198    Return it in newly malloc'ed storage.
199 -----------------------------------------------------------------------------*/
200     int count;
201         /* Number of entries in input table, including OPT_END marker */
202     int i;
203 
204     optEntry *optEntryTable;  /* malloc'ed array */
205 
206     /* Count the entries in optStructTable[] */
207     for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++);
208     count = i+1;
209 
210     optEntryTable = (optEntry *) malloc(count * sizeof(optEntry));
211     if (optEntryTable) {
212         int i;
213         for (i = 0; i < count; i++)
214             optEntryTable[i] = optStructToEntry(optStructTable[i]);
215     }
216     return(optEntryTable);
217 }
218 
219 
220 
221 
222 /*------------------------------------------------------------------------
223  |  NAME          optNeedsArgument
224  |
225  |  FUNCTION      Check if an option requires an argument.
226  |
227  |  INPUT         opt     the option to check.
228  |
229  |  RETURNS       Boolean value.
230  */
231 static int
optNeedsArgument(const optEntry opt)232 optNeedsArgument(const optEntry opt)
233 {
234     return opt.type == OPT_STRING
235 	|| opt.type == OPT_INT
236 	|| opt.type == OPT_UINT
237 	|| opt.type == OPT_LONG
238 	|| opt.type == OPT_ULONG
239     || opt.type == OPT_FLOAT
240     || opt.type == OPT_NAMELIST
241     || opt.type == OPT_STRINGLIST
242         ;
243 }
244 
245 /*------------------------------------------------------------------------
246  |  NAME          argvRemove
247  |
248  |  FUNCTION      Remove an entry from an argv-array.
249  |
250  |  INPUT         argc    pointer to number of options.
251  |                argv    array of option-/argument-strings.
252  |                i       index of option to remove.
253  |
254  |  OUTPUT        argc    new argument count.
255  |                argv    array with given argument removed.
256  */
257 static void
argvRemove(int * argc,char * argv[],int i)258 argvRemove(int *argc, char *argv[], int i)
259 {
260     if (i >= *argc)
261         return;
262     while (i++ < *argc)
263         argv[i - 1] = argv[i];
264     --*argc;
265 }
266 
267 
268 
269 static void
getToken(const char * const tokenStart,char const delimiter,const char ** const tokenP,const char ** const nextP)270 getToken(const char *  const tokenStart,
271          char          const delimiter,
272          const char ** const tokenP,
273          const char ** const nextP) {
274 /*----------------------------------------------------------------------------
275    Find the token starting at 'tokenStart' up to but not including
276    the first 'delimiter' character or end of string.  Return it in newly
277    malloced memory as *tokenP, NUL-terminated.
278 
279    Make *nextP point just past the token, i.e. to the delimiter or
280    end of string NUL character.
281 
282    Note that if the string is empty, or starts with the delimiter,
283    we return an empty string and *nextP == tokenStart, i.e. *nextP
284    doesn't necessarily advance.
285 -----------------------------------------------------------------------------*/
286     const char * error;
287 
288     pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
289 
290     if (error)
291         optFatal("error parsing a token: %s", error);
292 }
293 
294 
295 
296 static void
parseNameList(const char * const listText,struct optNameValue ** const listP)297 parseNameList(const char *           const listText,
298               struct optNameValue ** const listP) {
299 
300     unsigned int const maxOptionCount = 100;
301 
302     const char * cursor;
303     unsigned int optionCount;
304     struct optNameValue * list;
305 
306     MALLOCARRAY_NOFAIL(list, maxOptionCount+1);
307 
308     cursor = &listText[0];  /* initial value */
309 
310     optionCount = 0;  /* initial value */
311 
312     while (optionCount < maxOptionCount && *cursor != '\0') {
313         const char * next;
314         struct optNameValue pair;
315 
316         getToken(cursor, '=', &pair.name, &next);
317 
318         cursor = next;
319 
320         if (*cursor == '\0')
321             optFatal("name=value option value ends prematurely.  An equal "
322                      "sign was expected following name '%s'", pair.name);
323 
324         assert(*cursor == '=');
325         ++cursor;
326 
327         getToken(cursor, ',', &pair.value, &next);
328 
329         cursor = next;
330 
331         list[optionCount++] = pair;
332 
333         if (*cursor != '\0') {
334             assert(*cursor == ',');
335             ++cursor;
336         }
337     }
338     list[optionCount].name  = NULL;
339     list[optionCount].value = NULL;
340 
341     *listP = list;
342 }
343 
344 
345 
346 static void
parseStringList(const char * const listText,const char *** const listP)347 parseStringList(const char *   const listText,
348                 const char *** const listP) {
349 
350     unsigned int const maxStringCount = 100;
351 
352     const char * cursor;
353     unsigned int stringCount;
354     const char ** list;
355 
356     MALLOCARRAY_NOFAIL(list, maxStringCount+1);
357 
358     cursor = &listText[0];  /* initial value */
359 
360     stringCount = 0;  /* initial value */
361 
362     while (stringCount < maxStringCount && *cursor != '\0') {
363         const char * next;
364 
365         getToken(cursor, ',', &list[stringCount++], &next);
366 
367         cursor = next;
368 
369         if (*cursor != '\0') {
370             assert(*cursor == ',');
371             ++cursor;
372         }
373     }
374     list[stringCount] = NULL;
375 
376     *listP = list;
377 }
378 
379 
380 
381 /*------------------------------------------------------------------------
382  |  NAME          optExecute
383  |
384  |  FUNCTION      Perform the action of an option.
385  |
386  |  INPUT         opt     element in array of defined options that
387  |                        applies to this option
388  |                arg     argument to option, if it applies.
389  |                lng     was the option given as a long option?
390  |
391  |  RETURNS       Nothing. Aborts in case of error.
392  */
393 static void
optExecute(optEntry const opt,char * arg,int lng)394 optExecute(optEntry  const opt, char *arg, int lng)
395 {
396     if (opt.specified)
397         *opt.specified = 1;
398 
399     switch (opt.type) {
400     case OPT_FLAG:
401         if (opt.arg)
402             *((int *) opt.arg) = 1;
403         break;
404 
405     case OPT_STRING:
406         if (opt.arg)
407             *((char **) opt.arg) = arg;
408         break;
409 
410     case OPT_INT:
411     case OPT_LONG: {
412         long tmp;
413         char *e;
414 
415         if (arg == NULL)
416             optFatal("internal error: optExecute() called with NULL argument "
417                      "'%s'", optString(opt, lng));
418         tmp = strtol(arg, &e, 10);
419         if (*e)
420             optFatal("invalid number `%s'", arg);
421         if (errno == ERANGE
422             || (opt.type == OPT_INT && (tmp > INT_MAX || tmp < INT_MIN)))
423             optFatal("number `%s' to `%s' out of range",
424                      arg, optString(opt, lng));
425         if (opt.type == OPT_INT) {
426             *((int *) opt.arg) = (int) tmp;
427         } else /* OPT_LONG */ {
428             if (opt.arg)
429                 *((long *) opt.arg) = tmp;
430         }
431     } break;
432 
433     case OPT_UINT:
434     case OPT_ULONG: {
435         unsigned long tmp;
436         char * tailPtr;
437 
438         if (arg == NULL)
439             optFatal("internal error: optExecute() called with NULL argument "
440                      "'%s'", optString(opt, lng));
441 
442         if (arg[0] == '-' || arg[1] == '+')
443             optFatal("unsigned number '%s' has a sign ('%c')",
444                      arg, arg[0]);
445         tmp = strtoul(arg, &tailPtr, 10);
446         if (*tailPtr)
447             optFatal("invalid number `%s'", arg);
448         if (errno == ERANGE
449             || (opt.type == OPT_UINT && tmp > UINT_MAX))
450             optFatal("number `%s' to `%s' out of range",
451                      arg, optString(opt, lng));
452         if (opt.type == OPT_UINT) {
453            if (opt.arg)
454                *((unsigned *) opt.arg) = (unsigned) tmp;
455         } else /* OPT_ULONG */ {
456             if (opt.arg)
457                 *((unsigned long *) opt.arg) = tmp;
458         }
459     } break;
460     case OPT_FLOAT: {
461         float tmp;
462         char *e;
463 
464         if (arg == NULL)
465             optFatal("internal error: optExecute() called with NULL argument "
466                      "'%s'", optString(opt, lng));
467         tmp = strtod(arg, &e);
468         if (*e)
469             optFatal("invalid floating point number `%s'", arg);
470         if (errno == ERANGE)
471             optFatal("floating point number `%s' to `%s' out of range",
472                      arg, optString(opt, lng));
473         if (opt.arg)
474             *((float *) opt.arg) = tmp;
475     } break;
476     case OPT_NAMELIST: {
477         if (arg == NULL)
478             optFatal("internal error: optExecute() called with NULL argument "
479                      "'%s'", optString(opt, lng));
480 
481         if (opt.arg)
482             parseNameList(arg, (struct optNameValue **)opt.arg);
483 
484     } break;
485     case OPT_STRINGLIST: {
486         if (arg == NULL)
487             optFatal("internal error: optExecute() called with NULL argument "
488                      "'%s'", optString(opt, lng));
489 
490         if (opt.arg)
491             parseStringList(arg, (const char ***)opt.arg);
492 
493     } break;
494     default:
495         break;
496     }
497 }
498 
499 
500 
501 /*-----------------------------------------------------------------------+
502 |  PUBLIC FUNCTIONS                                                      |
503 +-----------------------------------------------------------------------*/
504 
505 /*------------------------------------------------------------------------
506  |  NAME          optSetFatalFunc
507  |
508  |  FUNCTION      Set function used to display error message and exit.
509  |
510  |  SYNOPSIS      #include "shhopt.h"
511  |                void optSetFatalFunc(void (*f)(const char *, ...));
512  |
513  |  INPUT         f       function accepting printf()'like parameters,
514  |                        that _must_ abort the program.
515  */
516 void
pm_optSetFatalFunc(void (* f)(const char *,...))517 pm_optSetFatalFunc(void (*f)(const char *, ...)) {
518 
519     optFatal = f;
520 }
521 
522 
523 
524 /*------------------------------------------------------------------------
525  |  NAME          pm_optParseOptions
526  |
527  |  FUNCTION      Parse commandline options.
528  |
529  |  SYNOPSIS      #include "shhopt.h"
530  |                void pm_optParseOptions(int *argc, char *argv[],
531  |                                     optStruct opt[], int allowNegNum);
532  |
533  |  INPUT         argc    Pointer to number of options.
534  |                argv    Array of option-/argument-strings.
535  |                opt     Array of possible options.
536  |                allowNegNum
537  |                        a negative number is not to be taken as
538  |                        an option.
539  |
540  |  OUTPUT        argc    new argument count.
541  |                argv    array with arguments removed.
542  |
543  |  RETURNS       Nothing. Aborts in case of error.
544  |
545  |  DESCRIPTION   This function checks each option in the argv-array
546  |                against strings in the opt-array, and `executes' any
547  |                matching action. Any arguments to the options are
548  |                extracted and stored in the variables or passed to
549  |                functions pointed to by entries in opt.
550  |
551  |                Options and arguments used are removed from the argv-
552  |                array, and argc is decreased accordingly.
553  |
554  |                Any error leads to program abortion.
555  */
556 void
pm_optParseOptions(int * argc,char * argv[],optStruct opt[],int allowNegNum)557 pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
558 {
559     int  ai,        /* argv index. */
560          optarg,    /* argv index of option argument, or -1 if none. */
561          mi,        /* Match index in opt. */
562          done;
563     char *arg,      /* Pointer to argument to an option. */
564          *o,        /* pointer to an option character */
565          *p;
566 
567     optEntry *opt_table;  /* malloc'ed array */
568 
569     opt_table = optStructTblToEntryTbl(opt);
570     if (opt_table == NULL)
571         optFatal("Memory allocation failed (trying to allocate space for "
572                  "new-format option table)");
573 
574     /*
575      *  Loop through all arguments.
576      */
577     for (ai = 0; ai < *argc; ) {
578         /*
579          *  "--" indicates that the rest of the argv-array does not
580          *  contain options.
581          */
582         if (strcmp(argv[ai], "--") == 0) {
583             argvRemove(argc, argv, ai);
584             break;
585         }
586 
587         if (allowNegNum && argv[ai][0] == '-' && ISDIGIT(argv[ai][1])) {
588             ++ai;
589             continue;
590         } else if (strncmp(argv[ai], "--", 2) == 0) {
591             /* long option */
592             /* find matching option */
593             if ((mi = optMatch(opt_table, argv[ai] + 2, 1)) < 0)
594                 optFatal("unrecognized option `%s'", argv[ai]);
595 
596             /* possibly locate the argument to this option. */
597             arg = NULL;
598             if ((p = strchr(argv[ai], '=')) != NULL)
599                 arg = p + 1;
600 
601             /* does this option take an argument? */
602             optarg = -1;
603             if (optNeedsArgument(opt_table[mi])) {
604                 /* option needs an argument. find it. */
605                 if (!arg) {
606                     if ((optarg = ai + 1) == *argc)
607                         optFatal("option `%s' requires an argument",
608                                  optString(opt_table[mi], 1));
609                     arg = argv[optarg];
610                 }
611             } else {
612                 if (arg)
613                     optFatal("option `%s' doesn't allow an argument",
614                              optString(opt_table[mi], 1));
615             }
616             optExecute(opt_table[mi], arg, 1);
617             /* remove option and any argument from the argv-array. */
618             if (optarg >= 0)
619                 argvRemove(argc, argv, ai);
620             argvRemove(argc, argv, ai);
621         } else if (*argv[ai] == '-') {
622             /* A dash by itself is not considered an option. */
623             if (argv[ai][1] == '\0') {
624                 ++ai;
625                 continue;
626             }
627             /* Short option(s) following */
628             o = argv[ai] + 1;
629             done = 0;
630             optarg = -1;
631             while (*o && !done) {
632                 /* find matching option */
633                 if ((mi = optMatch(opt_table, o, 0)) < 0)
634                     optFatal("unrecognized option `-%c'", *o);
635 
636                 /* does this option take an argument? */
637                 optarg = -1;
638                 arg = NULL;
639                 if (optNeedsArgument(opt_table[mi])) {
640                     /* option needs an argument. find it. */
641                     arg = o + 1;
642                     if (!*arg) {
643                         if ((optarg = ai + 1) == *argc)
644                             optFatal("option `%s' requires an argument",
645                                      optString(opt_table[mi], 0));
646                         arg = argv[optarg];
647                     }
648                     done = 1;
649                 }
650                 /* perform the action of this option. */
651                 optExecute(opt_table[mi], arg, 0);
652                 ++o;
653             }
654             /* remove option and any argument from the argv-array. */
655             if (optarg >= 0)
656                 argvRemove(argc, argv, ai);
657             argvRemove(argc, argv, ai);
658         } else {
659             /* a non-option argument */
660             ++ai;
661         }
662     }
663     free(opt_table);
664 }
665 
666 
667 static void
parse_short_option_token(char * argv[],const int argc,const int ai,const optEntry opt_table[],int * const tokens_consumed_p)668 parse_short_option_token(char *argv[], const int argc, const int ai,
669                          const optEntry opt_table[],
670                          int * const tokens_consumed_p) {
671 /*----------------------------------------------------------------------------
672    Parse a cluster of short options, e.g. -walne .
673 
674    The last option in the cluster might take an argument, and we parse
675    that as well.  e.g. -cf myfile or -cfmyfile .
676 
677    argv[] and argc describe the whole program argument set.  'ai' is the
678    index of the argument that is the short option cluster.
679 -----------------------------------------------------------------------------*/
680     char *o;  /* A short option character */
681     char *arg;
682     int mi;   /* index into option table */
683     unsigned char processed_arg;  /* boolean */
684         /* We processed an argument to one of the one-character options.
685            This necessarily means there are no more options in this token
686            to process.
687            */
688 
689     *tokens_consumed_p = 1;  /* initial assumption */
690 
691     o = argv[ai] + 1;
692     processed_arg = 0;  /* initial value */
693     while (*o && !processed_arg) {
694 		/* find matching option */
695 		if ((mi = optMatch(opt_table, o, 0)) < 0)
696 		    optFatal("unrecognized option `-%c'", *o);
697 
698 		/* does this option take an argument? */
699 		if (optNeedsArgument(opt_table[mi])) {
700 		    /* option needs an argument. find it. */
701 		    arg = o + 1;
702 		    if (!*arg) {
703                 if (ai + 1 >= argc)
704 			    optFatal("option `%s' requires an argument",
705 				     optString(opt_table[mi], 0));
706 			arg = argv[ai+1];
707             (*tokens_consumed_p)++;
708 		    }
709 		    processed_arg = 1;
710 		} else
711             arg = NULL;
712 		/* perform the action of this option. */
713 		optExecute(opt_table[mi], arg, 0);
714 		++o;
715     }
716 }
717 
718 
719 
720 static void
fatalUnrecognizedLongOption(const char * const optionName,optEntry const optTable[])721 fatalUnrecognizedLongOption(const char * const optionName,
722                             optEntry     const optTable[]) {
723 
724     unsigned int const nopt = optStructCount(optTable);
725 
726     unsigned int q;
727 
728     char optList[1024];
729 
730     optList[0] = '\0';  /* initial value */
731 
732     for (q = 0;
733          q < nopt && strlen(optList) + 1 <= sizeof(optList);
734          ++q) {
735 
736         const optEntry * const optEntryP = &optTable[q];
737         const char * entry;
738 
739         if (optEntryP->longName)
740             pm_asprintf(&entry, "-%s ", optEntryP->longName);
741         else
742             pm_asprintf(&entry, "-%c ", optEntryP->shortName);
743 
744         strncat(optList, entry, sizeof(optList) - strlen(optList) - 1);
745 
746         pm_strfree(entry);
747 
748         if (strlen(optList) + 1 == sizeof(optList)) {
749             /* Buffer is full.  Overwrite end of list with ellipsis */
750             strcpy(&optList[sizeof(optList) - 4], "...");
751         }
752     }
753     optFatal("unrecognized option '%s'.  Recognized options are: %s",
754              optionName, optList);
755 }
756 
757 
758 
759 static void
parse_long_option(char * const argv[],int const argc,int const ai,int const namepos,optEntry const opt_table[],int * const tokens_consumed_p)760 parse_long_option(char *   const argv[],
761                   int      const argc,
762                   int      const ai,
763                   int      const namepos,
764                   optEntry const opt_table[],
765                   int *    const tokens_consumed_p) {
766 /*----------------------------------------------------------------------------
767    Parse a long option, e.g. -verbose or --verbose.
768 
769    The option might take an argument, and we parse
770    that as well.  e.g. -file=myfile or -file myfile .
771 
772    argv[] and argc describe the whole program argument set.  'ai' is the
773    index of the argument that is the long option.
774 -----------------------------------------------------------------------------*/
775     char *equals_arg;
776       /* The argument of an option, included in the same token, after a
777          "=".  NULL if no "=" in the token.
778          */
779     char *arg;     /* The argument of an option; NULL if none */
780     int mi;    /* index into option table */
781 
782     /* The current token is an option, and its name starts at
783        Index 'namepos' in the argument.
784     */
785     *tokens_consumed_p = 1;  /* initial assumption */
786     /* find matching option */
787     if ((mi = optMatch(opt_table, &argv[ai][namepos], 1)) < 0)
788         fatalUnrecognizedLongOption(argv[ai], opt_table);
789 
790     /* possibly locate the argument to this option. */
791     {
792         char *p;
793         if ((p = strchr(argv[ai], '=')) != NULL)
794             equals_arg = p + 1;
795         else
796             equals_arg = NULL;
797     }
798     /* does this option take an argument? */
799     if (optNeedsArgument(opt_table[mi])) {
800         /* option needs an argument. find it. */
801         if (equals_arg)
802             arg = equals_arg;
803         else {
804             if (ai + 1 == argc)
805                 optFatal("option `%s' requires an argument",
806                          optString(opt_table[mi], 1));
807             arg = argv[ai+1];
808             (*tokens_consumed_p)++;
809         }
810     } else {
811         if (equals_arg)
812             optFatal("option `%s' doesn't allow an argument, but you "
813                      "have specified it in the form name=value",
814                      optString(opt_table[mi], 1));
815         else
816             arg = NULL;
817     }
818     /* perform the action of this option. */
819     optExecute(opt_table[mi], arg, 1);
820 }
821 
822 
823 
824 /*------------------------------------------------------------------------
825  |  NAME          pm_optParseOptions2
826  |
827  |  FUNCTION      Parse commandline options.
828  |
829  |  SYNOPSIS      #include "shhopt.h"
830  |                void pm_optParseOptions2(int *argc, char *argv[],
831  |                                      optStruct2 opt, unsigned long flags);
832  |
833  |  INPUT         argc    Pointer to number of options.
834  |                argv    Array of option-/argument-strings.
835  |                opt     Structure describing option syntax.
836  |                flags   Result is undefined if not zero.
837  |                        For future expansion.
838  |
839  |  OUTPUT        argc    new argument count.
840  |                argv    array with arguments removed.
841  |
842  |  RETURNS       Nothing. Aborts in case of error.
843  |
844  |  DESCRIPTION   This function checks each option in the argv-array
845  |                against strings in the opt-array, and `executes' any
846  |                matching action. Any arguments to the options are
847  |                extracted and stored in the variables or passed to
848  |                functions pointed to by entries in opt.
849  |
850  |                This differs from pm_optParseOptions in that it accepts
851  |                long options with just one hyphen and doesn't accept
852  |                any short options.  It also has accommodations for
853  |                future expansion.
854  |
855  |                Options and arguments used are removed from the argv-
856  |                array, and argc is decreased accordingly.
857  |
858  |                Any error leads to program abortion.
859  */
860 void
pm_optParseOptions2(int * const argc_p,char * argv[],const optStruct2 opt,const unsigned long flags)861 pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt,
862                  const unsigned long flags)
863 /*----------------------------------------------------------------------------
864    This does the same thing as pm_optParseOptions3(), except that there is no
865    "specified" return value.
866 
867    This function exists for backward compatibility.
868 -----------------------------------------------------------------------------*/
869 
870 {
871     optStruct3 opt3;
872 
873     opt3.short_allowed = opt.short_allowed;
874     opt3.allowNegNum   = opt.allowNegNum;
875     opt3.opt_table     = optStructTblToEntryTbl(opt.opt_table);
876 
877     if (opt3.opt_table == NULL)
878         optFatal("Memory allocation failed (trying to allocate space for "
879                  "new-format option table)");
880 
881     pm_optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
882 
883     free(opt3.opt_table);
884 }
885 
886 
887 
888 
889 static void
zero_specified(optEntry opt_table[])890 zero_specified(optEntry opt_table[]) {
891 /*----------------------------------------------------------------------------
892    Set all the "number of times specified" return values identified in the
893    option table opt_table[] to zero.
894 -----------------------------------------------------------------------------*/
895     unsigned int i;
896 
897     for (i = 0; opt_table[i].type != OPT_END; i++) {
898         if (opt_table[i].specified)
899             *(opt_table[i].specified) = 0;
900     }
901 }
902 
903 
904 
905 /*------------------------------------------------------------------------
906  |  NAME          pm_optParseOptions3
907  |
908  |  FUNCTION      Parse commandline options.
909  |
910  |  INPUT         argc    Pointer to number of options.
911  |                argv    Array of option-/argument-strings.
912  |                opt     Structure describing option syntax.
913  |                optStructSize
914  |                        Size of "opt" (since the caller may be older
915  |                        than this function, it may be using a structure
916  |                        with fewer fields than exist today.  We use this
917  |                        parameter to handle those older callers).
918  |                flags   Result is undefined if not zero.
919  |                        For future expansion.
920  |
921  |  OUTPUT        argc    new argument count.
922  |                argv    array with arguments removed.
923  |
924  |                Areas pointed to by pointers in 'opt' get updated with
925  |                option values and counts.
926  |
927  |  RETURNS       Nothing. Aborts in case of error.
928  |
929  |  DESCRIPTION   This function checks each option in the argv-array
930  |                against strings in the opt-array, and `executes' any
931  |                matching action. Any arguments to the options are
932  |                extracted and stored in the variables or passed to
933  |                functions pointed to by entries in opt.
934  |
935  |                This differs from pm_optParseOptions in that it accepts
936  |                long options with just one hyphen and doesn't accept
937  |                any short options.  It also has accommodations for
938  |                future expansion.
939  |
940  |                Options and arguments used are removed from the argv-
941  |                array, and argc is decreased accordingly.
942  |
943  |                Any error leads to program abortion.
944  */
945 void
pm_optParseOptions3(int * const argc_p,char * argv[],const optStruct3 opt,const unsigned int optStructSize,const unsigned long flags)946 pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
947                  const unsigned int optStructSize, const unsigned long flags)
948 {
949     int  ai;        /* argv index. */
950     int tokens_consumed;
951     unsigned char no_more_options;  /* boolean */
952         /* We've encountered the "no more options" token */
953 
954     zero_specified(opt.opt_table);
955 
956     /*
957      *  Loop through all arguments.
958      */
959     no_more_options = 0;  /* initial value */
960     for (ai = 0; ai < *argc_p; ) {
961         if (no_more_options)
962             /* Can't be an option -- there aren't any more */
963             ai++;
964         else if (argv[ai][0] != '-')
965             /* Can't be an option -- doesn't start with a dash */
966             ai++;
967         else {
968             /* It starts with a dash -- could be an option */
969             if (argv[ai][1] == '\0') {
970                 /* A dash by itself is not considered an option. */
971                 ++ai;
972                 tokens_consumed = 0;
973             } else if (opt.allowNegNum && ISDIGIT(argv[ai][1])) {
974                 /* It's a negative number parameter, not an option */
975                 ++ai;
976                 tokens_consumed = 0;
977             } else if (argv[ai][1] == '-') {
978                 /* It starts with -- */
979                 if (argv[ai][2] == '\0') {
980                     /* The entire thing is "--".  That means no more options */
981                     tokens_consumed = 1;
982                     no_more_options = 1;
983                 } else
984                     /* It's an option that starts with "--" */
985                     parse_long_option(argv, *argc_p, ai, 2,
986                                       opt.opt_table, &tokens_consumed);
987             } else {
988                 if (opt.short_allowed) {
989                     /* It's a cluster of (one or more) short options */
990                     parse_short_option_token(argv, *argc_p, ai,
991                                              opt.opt_table, &tokens_consumed);
992                 } else {
993                     /* It's a long option that starts with "-" */
994                     parse_long_option(argv, *argc_p, ai, 1,
995                                       opt.opt_table, &tokens_consumed);
996                 }
997 
998             }
999             /* remove option and any argument from the argv-array. */
1000             {
1001                 int i;
1002                 for (i = 0; i < tokens_consumed; i++)
1003                     argvRemove(argc_p, argv, ai);
1004             }
1005         }
1006     }
1007 }
1008 
1009 
1010 
1011 void
pm_optDestroyNameValueList(struct optNameValue * const list)1012 pm_optDestroyNameValueList(struct optNameValue * const list) {
1013 
1014     unsigned int i;
1015 
1016     for (i = 0; list[i].name; ++i) {
1017         pm_strfree(list[i].name);
1018         pm_strfree(list[i].value);
1019     }
1020 
1021     free(list);
1022 }
1023