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