1 /*
2 ** petopt.c - Command line argument parser
3 **
4 ** Copyright (c) 1999 Peter Eriksson <pen@lysator.liu.se>
5 **
6 ** This program is free software; you can redistribute it and/or
7 ** modify it as you wish - as long as you don't claim that you wrote
8 ** it.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 */
14
15 #include "config.h"
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #ifdef HAVE_SYSLOG_H
23 #include <syslog.h>
24 #endif
25 #include "petopt.h"
26
27
28
29 /*
30 ** Compare the user supplied long style command line option against
31 ** the program defined. Handle abbreviations, truncation and ambiguous
32 ** abbreviations.
33 */
34 static int
compare_long(const char * user_option,const char * aov_long,int len)35 compare_long(const char *user_option,
36 const char *aov_long,
37 int len)
38 {
39 unsigned char *uc, *ac;
40 int diff = 0;
41 int saved_len = len;
42
43
44 uc = (unsigned char *) user_option;
45 ac = (unsigned char *) aov_long;
46
47 while (len > 0 && diff == 0)
48 {
49 /* If we reach a word delimiter, skip to next word */
50 if (*uc == '-')
51 while (*ac && *ac != '-')
52 ++ac;
53
54 diff = (int) tolower(*uc) - (int) tolower(*ac);
55
56 ++uc;
57 ++ac;
58 --len;
59 }
60
61 if (diff)
62 {
63 /* Check abbrev */
64 int adiff = 0;
65
66
67 uc = (unsigned char *) user_option;
68 ac = (unsigned char *) aov_long;
69 len = saved_len;
70
71 while (len > 0 && adiff == 0)
72 {
73 /* Locate abbrev characters (uppercase) */
74 while (*ac && (islower(*ac) || *ac == '-'))
75 ++ac;
76
77 adiff = (int) tolower(*uc) - (int) tolower(*ac);
78
79 ++uc;
80 ++ac;
81 --len;
82 }
83
84 if (adiff == 0)
85 diff = 0;
86 }
87 return diff;
88 }
89
90
91
92 static int
petopt_parse_option(PETOPT * pop,PETOPTS ** pov,const char ** optarg)93 petopt_parse_option(PETOPT *pop,
94 PETOPTS **pov,
95 const char **optarg)
96 {
97 int i;
98
99
100 *pov = NULL;
101 *optarg = NULL;
102
103
104 Again:
105 /* Index out of bounds? */
106 if (pop->ai >= pop->argc)
107 {
108 return POE_EOF;
109 }
110
111 if (pop->ci == 0)
112 {
113 /* Non option or "-" */
114 if (pop->argv[pop->ai][0] != '-' ||
115 (pop->argv[pop->ai][0] == '-' &&
116 pop->argv[pop->ai][1] == '\0'))
117 {
118 pop->oav[pop->oac++] = strdup(pop->argv[pop->ai++]);
119 goto Again;
120 }
121
122 /* "--", stop parsing and skip */
123 if (strcmp(pop->argv[pop->ai], "--") == 0)
124 {
125 pop->ai++;
126
127 while (pop->ai < pop->argc)
128 {
129 pop->oav[pop->oac++] = strdup(pop->argv[pop->ai++]);
130 }
131
132 return POE_EOF;
133 }
134
135 if (pop->argv[pop->ai][1] == '-')
136 {
137 /* Long option */
138 const char *opt, *arg;
139 int len, ix;
140 char *boolval = NULL;
141 int withval = 0;
142
143
144 pop->saved_ai = pop->ai;
145 pop->saved_ci = pop->ci;
146
147 opt = pop->argv[pop->ai]+2;
148 if (strncasecmp(opt, "enable-", 7) == 0)
149 {
150 boolval = "true";
151 opt += 7;
152 }
153 else if (strncasecmp(opt, "disable-", 8) == 0)
154 {
155 boolval = "false";
156 opt += 8;
157 }
158 else if (strncasecmp(opt, "with-", 5) == 0)
159 {
160 withval = 1;
161 opt += 5;
162 }
163 else if (strncasecmp(opt, "without-", 8) == 0)
164 {
165 withval = -1;
166 opt += 8;
167 }
168
169
170 arg = strchr(opt, '=');
171 if (arg)
172 len = arg - opt;
173 else
174 len = strlen(opt);
175
176
177 ix = -1;
178 for (i = 0; pop->pov[i].s != -1; i++)
179 {
180 if (pop->pov[i].l != NULL &&
181 compare_long(opt, pop->pov[i].l, len) == 0)
182 {
183 /* Match found */
184 if (ix < 0)
185 ix = i;
186 else
187 return POE_MULTI;
188 }
189 }
190
191
192 if (ix >= 0)
193 {
194 /* Found a unique match */
195
196 pop->ai++;
197
198 *pov = &pop->pov[ix];
199
200 if (pop->pov[ix].f)
201 {
202 /* Check for (optional) argument */
203
204 if (boolval)
205 {
206 /* --enable-XXX or --disable-XXX */
207
208 if (arg ||
209 (pop->pov[ix].f & POF_TYPEMASK) != POF_BOOL)
210 {
211 return POE_INVALID;
212 }
213
214 *optarg = boolval;
215 return 0;
216 }
217
218 if (withval)
219 {
220 if (withval == 1)
221 {
222 if (arg)
223 *optarg = arg+1;
224 else
225 *optarg = "yes";
226 }
227 else
228 {
229 if (arg)
230 return POE_INVALID;
231 else
232 *optarg = NULL;
233 }
234 return 0;
235 }
236
237 if (arg)
238 {
239 /* Argument after "=" */
240 *optarg = arg+1;
241 return 0;
242 }
243
244 if (pop->ai < pop->argc &&
245 (pop->argv[pop->ai][0] != '-' ||
246 pop->argv[pop->ai][0] == '\0'))
247 {
248 /* Have an argument, empty string or "-" */
249 if (((pop->pov[ix].f & POF_TYPEMASK) == POF_STR) ||
250 isdigit((int) pop->argv[pop->ai][0]))
251 {
252 *optarg = pop->argv[pop->ai];
253 pop->ai++;
254 }
255 }
256
257 /* Argument missing, and not optional? */
258 if (*optarg == NULL &&
259 !((pop->pov[ix].f & POF_OPT) ||
260 ((pop->pov[ix].f & POF_TYPEMASK) == POF_BOOL)))
261 {
262 pop->ai--;
263 return POE_MISSING;
264 }
265 }
266
267 return 0;
268 }
269
270 /* Unknown long command line switch */
271 return POE_OPTION;
272 }
273 else
274 {
275 /* Short option */
276
277 pop->ci = 1;
278 }
279 }
280
281 /* Short option */
282
283 pop->saved_ai = pop->ai;
284 pop->saved_ci = pop->ci;
285
286 i = 0;
287 while (pop->pov[i].s != -1 && pop->pov[i].s != pop->argv[pop->ai][pop->ci])
288 i++;
289
290 if (pop->pov[i].s == -1)
291 {
292 /* Unknown short command line switch */
293 return POE_OPTION;
294 }
295
296 /* Found a matching short option */
297
298 *pov = &pop->pov[i];
299 pop->ci++;
300
301 if (pop->argv[pop->ai][pop->ci] == '\0')
302 {
303 pop->ai++;
304 pop->ci = 0;
305 }
306
307 /* Have an (optional) argument? */
308 if (pop->pov[i].f)
309 {
310 if (pop->ai < pop->argc &&
311 (pop->ci != 0 ||
312 ((pop->argv[pop->ai][0] != '-') ||
313 pop->argv[pop->ai][1] == '\0')))
314 {
315 if (((pop->pov[i].f & POF_TYPEMASK) == POF_STR) ||
316 (isdigit((int) pop->argv[pop->ai][pop->ci]) ||
317 pop->argv[pop->ai][pop->ci] == '-'))
318 {
319 *optarg = pop->argv[pop->ai]+pop->ci;
320
321 pop->ai++;
322 pop->ci = 0;
323 }
324 }
325 }
326
327 /* Argument missing, and not optional? */
328 if (pop->pov[i].f &&
329 !((pop->pov[i].f & POF_OPT) ||
330 (pop->pov[i].f & POF_TYPEMASK) == POF_BOOL) &&
331 *optarg == NULL)
332 {
333 if (pop->ci == 0)
334 {
335 pop->ai--;
336 pop->ci = strlen(pop->argv[pop->ai])-1;
337 }
338
339 return POE_MISSING;
340 }
341
342 return 0;
343 }
344
345
346 int
petopt_print_error(PETOPT * pop,PETOPTS * pov,int err,FILE * fp)347 petopt_print_error(PETOPT *pop,
348 PETOPTS *pov,
349 int err,
350 FILE *fp)
351 {
352 char buf[3];
353 const char *arg;
354
355
356 if (pop->saved_ci > 0)
357 {
358 buf[0] = '-';
359 buf[1] = pop->argv[pop->saved_ai][pop->saved_ci];
360 buf[2] = '\0';
361 arg = buf;
362 }
363 else
364 {
365 arg = pop->argv[pop->saved_ai];
366 if (arg == NULL)
367 arg = "";
368 }
369
370 switch (err)
371 {
372 case POE_EOF:
373 return 0;
374
375 case POE_OPTION:
376 #ifdef HAVE_SYSLOG
377 if (pop->f & POF_SYSLOG)
378 syslog(LOG_ERR, "Unrecognized option: %s", arg);
379 #endif
380 fprintf(fp, "%s: Unrecognized option: %s\n",
381 pop->argv[0], arg);
382 break;
383
384 case POE_MULTI:
385 #ifdef HAVE_SYSLOG
386 if (pop->f & POF_SYSLOG)
387 syslog(LOG_ERR, "Ambiguous option: %s", arg);
388 #endif
389 fprintf(fp, "%s: Ambiguous option: %s\n",
390 pop->argv[0], arg);
391 break;
392
393 case POE_MISSING:
394 #ifdef HAVE_SYSLOG
395 if (pop->f & POF_SYSLOG)
396 syslog(LOG_ERR, "Missing argument for option: %s", arg);
397 #endif
398
399 fprintf(fp, "%s: Missing argument for option: %s\n",
400 pop->argv[0], arg);
401 break;
402
403 case POE_INVALID:
404 #ifdef HAVE_SYSLOG
405 if (pop->f & POF_SYSLOG)
406 syslog(LOG_ERR, "Invalid argument for option: %s", arg);
407 #endif
408
409 fprintf(fp, "%s: Invalid argument for option: %s\n",
410 pop->argv[0], arg);
411 break;
412
413 case POE_INTERNAL:
414 #ifdef HAVE_SYSLOG
415 if (pop->f & POF_SYSLOG)
416 syslog(LOG_ERR, "Internal error parsing option: %s", arg);
417 #endif
418
419 fprintf(fp, "%s: Internal error parsing option: %s\n",
420 pop->argv[0], arg);
421 break;
422
423 default:
424 #ifdef HAVE_SYSLOG
425 if (pop->f & POF_SYSLOG)
426 syslog(LOG_ERR, "Internal options parsing error: #%d", err);
427 #endif
428
429 fprintf(fp, "%s: Internal options parsing error: #%d\n",
430 pop->argv[0], err);
431 }
432
433 return -1;
434 }
435
436
437 int
petopt_parse(PETOPT * pop,int * o_argc,char *** o_argv)438 petopt_parse(PETOPT *pop,
439 int *o_argc,
440 char ***o_argv)
441 {
442 int err;
443 const char *arg;
444 PETOPTS *pov;
445
446
447 while ((err = petopt_parse_option(pop, &pov, &arg)) == 0)
448 {
449 if (pop->parse &&
450 (err = pop->parse(pop, pov, arg)) < 0)
451 {
452 goto Fail;
453 }
454
455 /* Parser assigned the values */
456 if (err == 0)
457 continue;
458
459 /* Option not handled, and no storage assigned? */
460 if (pov->v == NULL)
461 {
462 err = POE_INTERNAL;
463 goto Fail;
464 }
465
466 switch (pov->f & POF_TYPEMASK)
467 {
468 case POF_NONE:
469 break;
470
471 case POF_BOOL:
472 if (arg && *arg != '\0')
473 {
474 if (strcasecmp(arg, "true") == 0 ||
475 strcasecmp(arg, "yes") == 0 ||
476 strcasecmp(arg, "on") == 0)
477 {
478 *(int *)(pov->v) = 1;
479 }
480 else if (strcasecmp(arg, "false") == 0 ||
481 strcasecmp(arg, "no") == 0 ||
482 strcasecmp(arg, "off") == 0)
483 {
484 *(int *)(pov->v) = 0;
485 }
486 else
487 {
488 err = POE_INVALID;
489 goto Fail;
490 }
491 }
492 else
493 {
494 *(int *)(pov->v) = 1;
495 }
496 break;
497
498 case POF_INT:
499 if (arg && *arg != '\0')
500 {
501 /* XXX: Check for integer number */
502 *(int *)(pov->v) = atoi(arg);
503 }
504 else
505 {
506 /* XXX: Check for POF_INC flag?? */
507 ++*(int *)(pov->v);
508 }
509 break;
510
511 case POF_STR:
512 if (pov->f & POF_DUP)
513 *(char **)(pov->v) = strdup(arg);
514 else
515 *(char **)(pov->v) = (char *) arg;
516 break;
517
518 default:
519 err = POE_OPTION;
520 goto Fail;
521 }
522 }
523
524 if (err == POE_EOF)
525 err = 0;
526
527 Fail:
528 if (err)
529 err = pop->errpr(pop, pov, err,
530 (pop->f & POF_NOERRPRINT) ? NULL : stderr);
531
532 *o_argc = pop->oac;
533 *o_argv = pop->oav;
534
535 return err;
536 }
537
538
539
540 int
petopt_print_usage(PETOPT * pop,FILE * fp)541 petopt_print_usage(PETOPT *pop,
542 FILE *fp)
543 {
544 int i, len;
545 struct petopt_option *pov;
546
547
548 pov = pop->pov;
549 for (i = 0; pov[i].s != -1; i++)
550 {
551 if (pov[i].s > ' ' && pov[i].s <='~')
552 fprintf(fp, " -%c, ", pov[i].s);
553 else
554 fprintf(fp, " ");
555
556 len = 0;
557 if (pov[i].l)
558 {
559 len = fprintf(fp, "--%s", pov[i].l);
560 if (pov[i].f)
561 {
562 if (pov[i].f & POF_OPT)
563 len += fprintf(fp, " [ARG]");
564 else
565 len += fprintf(fp, " ARG");
566 }
567 }
568
569 while (len++ < 30)
570 putchar(' ');
571
572 if (pov[i].h)
573 fputs(pov[i].h, fp);
574 if (pop->f & POF_PRDEFAULT && pov[i].v != NULL)
575 switch (pov[i].f & POF_TYPEMASK)
576 {
577 case POF_INT:
578 {
579 int *iv = pov[i].v;
580 if (*iv != 0)
581 fprintf(fp, " (Default: %d)", *iv);
582 }
583 break;
584
585 case POF_STR:
586 {
587 char **cv = pov[i].v;
588 if (*cv)
589 fprintf(fp, " (Default: \"%s\")", *cv);
590 }
591 break;
592 }
593 putc('\n', fp);
594 }
595
596 return 0;
597 }
598
599
600 int
petopt_setup(PETOPT ** popp,int f,int argc,char ** argv,PETOPTS * pov,int (* parse)(PETOPT * pop,PETOPTS * pov,const char * arg),int (* errpr)(PETOPT * pop,PETOPTS * pov,int err,FILE * fp))601 petopt_setup(PETOPT **popp,
602 int f,
603 int argc,
604 char **argv,
605 PETOPTS *pov,
606 int (*parse)(PETOPT *pop, PETOPTS *pov, const char *arg),
607 int (*errpr)(PETOPT *pop, PETOPTS *pov, int err, FILE *fp))
608 {
609 PETOPT *pop = malloc(sizeof(PETOPT));
610 if (pop == NULL)
611 return errno;
612
613 memset(pop, 0, sizeof(*pop));
614
615 pop->f = f;
616
617 pop->argc = argc;
618 pop->argv = (const char **) argv;
619 pop->pov = pov;
620 pop->parse = parse;
621
622 if (errpr)
623 pop->errpr = errpr;
624 else
625 pop->errpr = petopt_print_error;
626
627 pop->ai = 1;
628 pop->ci = 0;
629
630 pop->saved_ai = 0;
631 pop->saved_ci = 0;
632
633 pop->oac = 1;
634
635 pop->oav = calloc(argc+1, sizeof(char *));
636 pop->oav[0] = strdup(pop->argv[0]);
637 pop->oav[1] = NULL;
638
639 *popp = pop;
640 return 0;
641 }
642
643
644 int
petopt_rewind(PETOPT * pop)645 petopt_rewind(PETOPT *pop)
646 {
647 pop->ai = 1;
648 pop->ci = 0;
649
650 pop->oac = 1;
651 pop->oav[1] = NULL;
652
653 return 0;
654 }
655
656
657 int
petopt_cleanup(PETOPT * pop)658 petopt_cleanup(PETOPT *pop)
659 {
660 int i;
661
662 if (pop->oav)
663 {
664 for (i = 0; i < pop->oac; i++)
665 if (pop->oav[i])
666 free(pop->oav[i]);
667 free(pop->oav);
668 }
669
670 free(pop);
671 return 0;
672 }
673