1 /* opt.c -- general-purpose command line option parser
2 Copyright (C) 2016-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 3, or (at
7 your option) any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <mailutils/alloc.h>
24 #include <mailutils/opt.h>
25 #include <mailutils/nls.h>
26 #include <mailutils/errno.h>
27 #include <mailutils/stdstream.h>
28
29 #define EXIT_SUCCESS 0
30 #define EXIT_ERROR 1
31
32 /* Option and its aliases form a contiguous array. Option slot is used
33 to preserve contiguity during sorting. */
34 struct opt_slot
35 {
36 struct mu_option **opt; /* Points to the option */
37 int count; /* Number of options in opt */
38 };
39
40 /* Compare pointers to two option structs */
41 static int
optcmp(const void * a,const void * b)42 optcmp (const void *a, const void *b)
43 {
44 struct mu_option const *ap = *(struct mu_option const **)a;
45 struct mu_option const *bp = *(struct mu_option const **)b;
46
47 if (!MU_OPTION_IS_VALID_SHORT_OPTION (ap)
48 && MU_OPTION_IS_VALID_LONG_OPTION (ap)
49 && !MU_OPTION_IS_VALID_SHORT_OPTION (bp)
50 && MU_OPTION_IS_VALID_LONG_OPTION (bp))
51 return strcasecmp (ap->opt_long, bp->opt_long);
52 else
53 {
54 char afirst, bfirst;
55 int res;
56
57 afirst = ap->opt_short ? ap->opt_short : ap->opt_long ? *ap->opt_long : 0;
58 bfirst = bp->opt_short ? bp->opt_short : bp->opt_long ? *bp->opt_long : 0;
59
60 res = mu_tolower (afirst) - mu_tolower (bfirst);
61
62 return res ? res : afirst - bfirst;
63 }
64 }
65
66 /* Compare pointers to two option slots */
67 static int
slotcmp(const void * a,const void * b)68 slotcmp (const void *a, const void *b)
69 {
70 struct opt_slot const *ap = (struct opt_slot const *)a;
71 struct opt_slot const *bp = (struct opt_slot const *)b;
72 return optcmp (ap->opt, bp->opt);
73 }
74 /* Sort a group of options in OPTBUF, starting at index START (first
75 option slot after a group header (if any). The group spans up to
76 next group header or end of options */
77 static size_t
sort_group(struct mu_option ** optbuf,size_t start)78 sort_group (struct mu_option **optbuf, size_t start)
79 {
80 size_t i, count = 0;
81
82 /* Make sure the first option in group is not an alias. */
83 optbuf[start]->opt_flags &= ~MU_OPTION_ALIAS;
84 for (i = start; optbuf[i] && !MU_OPTION_IS_GROUP_HEADER (optbuf[i]); i++)
85 {
86 if (!(optbuf[i]->opt_flags & MU_OPTION_ALIAS))
87 count++;
88 }
89 if (count == i - start)
90 /* Inplace sort */
91 qsort (&optbuf[start], count, sizeof (optbuf[0]), optcmp);
92 else
93 {
94 /* Option group contains aliases. Split it into option slots. */
95 struct mu_option **tmp;
96 struct opt_slot *slots;
97 size_t j, k, l;
98
99 slots = mu_calloc (count, sizeof (slots[0]));
100 j = 0;
101 slots[0].opt = optbuf + start;
102 slots[0].count = 1;
103 for (k = start + 1; k < i; k++)
104 {
105 if (optbuf[k]->opt_flags & MU_OPTION_ALIAS)
106 slots[j].count++;
107 else
108 {
109 j++;
110 slots[j].opt = optbuf + k;
111 slots[j].count = 1;
112 }
113 }
114 /* Sort the slots */
115 qsort (slots, count, sizeof (slots[0]), slotcmp);
116 /* Create ordered array of option pointers */
117 tmp = mu_calloc (i - start, sizeof (tmp[0]));
118 for (k = l = 0; k < count; k++)
119 for (j = 0; j < slots[k].count; j++)
120 tmp[l++] = slots[k].opt[j];
121 /* Copy ordered pointers back and free temporary memory */
122 memcpy (optbuf + start, tmp, (i - start) * sizeof tmp[0]);
123 free (tmp);
124 free (slots);
125 }
126
127 return i;
128 }
129
130 /* Print help summary and exit. */
131 static void
fn_help(struct mu_parseopt * po,struct mu_option * opt,char const * unused)132 fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
133 {
134 mu_program_help (po, mu_strout);
135 exit (EXIT_SUCCESS);
136 }
137
138 /* Print usage summary and exit. */
139 static void
fn_usage(struct mu_parseopt * po,struct mu_option * opt,char const * unused)140 fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
141 {
142 mu_program_usage (po, 1, mu_strout);
143 exit (EXIT_SUCCESS);
144 }
145
146 static void
fn_version(struct mu_parseopt * po,struct mu_option * opt,char const * unused)147 fn_version (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
148 {
149 mu_program_version (po, mu_strout);
150 exit (EXIT_SUCCESS);
151 }
152
153 /* Default options */
154 struct mu_option mu_default_options[] = {
155 MU_OPTION_GROUP(""),
156 { "help", '?', NULL, MU_OPTION_IMMEDIATE, N_("give this help list"),
157 mu_c_string, NULL, fn_help },
158 { "usage", 0, NULL, MU_OPTION_IMMEDIATE, N_("give a short usage message"),
159 mu_c_string, NULL, fn_usage
160 },
161 MU_OPTION_END
162 };
163
164 struct mu_option mu_version_options[] = {
165 { "version", 'V', NULL, MU_OPTION_IMMEDIATE, N_("print program version"),
166 mu_c_string, NULL, fn_version },
167 MU_OPTION_END
168 };
169
170 /* Output error message */
171 void
mu_parseopt_error(struct mu_parseopt * po,char const * fmt,...)172 mu_parseopt_error (struct mu_parseopt *po, char const *fmt, ...)
173 {
174 va_list ap;
175
176 if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS)
177 return;
178
179 if (po->po_prog_name)
180 fprintf (stderr, "%s: ", po->po_prog_name);
181 va_start (ap, fmt);
182 vfprintf (stderr, fmt, ap);
183 va_end (ap);
184 fputc ('\n', stderr);
185 }
186
187 static void
mu_option_cache_destroy(void * ptr)188 mu_option_cache_destroy (void *ptr)
189 {
190 struct mu_option_cache *cache = ptr;
191 free (cache);
192 }
193
194 static int parseopt_apply (void *item, void *data);
195
196 /* If OPT is an immediate option, evaluate it right away. Otherwise,
197 add option OPT with argument ARG to the cache in PO. */
198 void
add_option_cache(struct mu_parseopt * po,struct mu_option * opt,char const * arg)199 add_option_cache (struct mu_parseopt *po, struct mu_option *opt,
200 char const *arg)
201 {
202 struct mu_option_cache *cache = mu_alloc (sizeof (*cache));
203
204 cache->cache_opt = opt;
205 if (arg == NULL && opt->opt_default)
206 arg = opt->opt_default;
207 cache->cache_arg = arg;
208
209 if ((po->po_flags & MU_PARSEOPT_IMMEDIATE)
210 || (opt->opt_flags & MU_OPTION_IMMEDIATE))
211 {
212 parseopt_apply (cache, po);
213 mu_option_cache_destroy (cache);
214 }
215 else
216 {
217 mu_list_append (po->po_optlist, cache);
218 }
219 }
220
221 /* Find first option for which I is an alias */
222 struct mu_option *
option_unalias(struct mu_parseopt * po,int i)223 option_unalias (struct mu_parseopt *po, int i)
224 {
225 while (i > 0 && po->po_optv[i]->opt_flags & MU_OPTION_ALIAS)
226 --i;
227 return po->po_optv[i];
228 }
229
230 /* Find a descriptor of short option CHR */
231 struct mu_option *
find_short_option(struct mu_parseopt * po,int chr)232 find_short_option (struct mu_parseopt *po, int chr)
233 {
234 size_t i;
235
236 for (i = 0; i < po->po_optc; i++)
237 {
238 if (MU_OPTION_IS_VALID_SHORT_OPTION (po->po_optv[i])
239 && po->po_optv[i]->opt_short == chr)
240 return option_unalias (po, i);
241 }
242 mu_parseopt_error (po, _("unrecognized option '-%c'"), chr);
243 return NULL;
244 }
245
246 enum neg_match
247 {
248 neg_nomatch,
249 neg_match_inexact,
250 neg_match_exact
251 };
252
253 static enum neg_match
negmatch(struct mu_parseopt * po,size_t i,char const * optstr,size_t optlen)254 negmatch (struct mu_parseopt *po, size_t i, char const *optstr, size_t optlen)
255 {
256 if (mu_option_possible_negation (po, po->po_optv[i]))
257 {
258 size_t neglen = strlen (po->po_negation);
259 size_t len = strlen (po->po_optv[i]->opt_long);
260 if (optlen <= neglen + len
261 && memcmp (optstr, po->po_negation, neglen) == 0
262 && memcmp (optstr + neglen, po->po_optv[i]->opt_long,
263 optlen - neglen) == 0)
264 {
265 return (optlen == neglen + len) ? neg_match_exact : neg_match_inexact;
266 }
267 }
268 return neg_nomatch;
269 }
270
271 /* Find a descriptor of long option OPTSTR. If it has argument, return
272 it in *ARGPTR. */
273 struct mu_option *
find_long_option(struct mu_parseopt * po,char const * optstr,struct mu_option ** used_opt_ptr,char ** used_value,char ** value)274 find_long_option (struct mu_parseopt *po, char const *optstr,
275 struct mu_option **used_opt_ptr,
276 char **used_value,
277 char **value)
278 {
279 size_t i;
280 size_t optlen; /* Length of the option in optstr */
281 int found = 0; /* 1 if the match was found, 2 if option is ambiguous */
282 int negated; /* 1 if a boolean option is negated */
283 struct mu_option *ret_opt = NULL;
284 struct mu_option *used_opt;
285
286 optlen = strcspn (optstr, "=");
287
288 for (i = 0; i < po->po_longcnt; i++)
289 {
290 size_t j = po->po_longidx[i];
291 size_t len = strlen (po->po_optv[j]->opt_long);
292 struct mu_option *opt = option_unalias (po, j);
293 enum neg_match neg = neg_nomatch;
294 if ((optlen <= len
295 && memcmp (po->po_optv[j]->opt_long, optstr, optlen) == 0)
296 || (neg = negmatch (po, j, optstr, optlen)))
297 {
298 switch (found)
299 {
300 case 0:
301 used_opt = po->po_optv[j];
302 ret_opt = opt;
303 found++;
304 negated = neg != neg_nomatch;
305 if (optlen == len || neg == neg_match_exact)
306 i = po->po_longcnt - 1; /* exact match: break the loop */
307 break;
308
309 case 1:
310 if (opt == ret_opt)
311 continue;
312 if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS)
313 return NULL;
314 mu_parseopt_error (po,
315 _("option '%s%*.*s' is ambiguous; possibilities:"),
316 po->po_long_opt_start,
317 optlen, optlen, optstr);
318 fprintf (stderr, "%s%s%s\n",
319 po->po_long_opt_start,
320 neg ? po->po_negation : "",
321 used_opt->opt_long);
322 if (neg == neg_nomatch && negmatch (po, j, optstr, optlen))
323 fprintf (stderr, "%s%s%s\n",
324 po->po_long_opt_start,
325 po->po_negation,
326 po->po_optv[j]->opt_long);
327 found++;
328
329 case 2:
330 fprintf (stderr, "%s%s%s\n",
331 po->po_long_opt_start,
332 neg ? po->po_negation : "",
333 po->po_optv[j]->opt_long);
334 if (neg == neg_nomatch && negmatch (po, j, optstr, optlen))
335 fprintf (stderr, "%s%s%s\n",
336 po->po_long_opt_start,
337 po->po_negation,
338 po->po_optv[j]->opt_long);
339 }
340 }
341 }
342
343
344 switch (found)
345 {
346 case 0:
347 mu_parseopt_error (po, _("unrecognized option '%s%s'"),
348 po->po_long_opt_start, optstr);
349 break;
350
351 case 1:
352 *used_opt_ptr = used_opt;
353 if (optstr[optlen])
354 ++optlen;
355 *used_value = (char *)(optstr + optlen);
356 if (ret_opt->opt_type == mu_c_bool)
357 *value = negated ? "0" : "1";
358 else
359 *value = NULL;
360 return ret_opt;
361
362 case 2:
363 break;
364 }
365
366 return NULL;
367 }
368
369 static void
permute(struct mu_parseopt * po)370 permute (struct mu_parseopt *po)
371 {
372 if (!(po->po_flags & MU_PARSEOPT_IN_ORDER) && po->po_arg_count)
373 {
374 /* Array to save arguments in */
375 char *save[2];
376 /* Number of arguments processed (at most two) */
377 int n = po->po_ind - (po->po_arg_start + po->po_arg_count);
378
379 if (n > 2)
380 abort ();
381
382 /* Store the processed elements away */
383 save[0] = po->po_argv[po->po_arg_start + po->po_arg_count];
384 if (n == 2)
385 save[1] = po->po_argv[po->po_arg_start + po->po_arg_count + 1];
386
387 /* Shift the array */
388 memmove (po->po_argv + po->po_arg_start + n,
389 po->po_argv + po->po_arg_start,
390 po->po_arg_count * sizeof (po->po_argv[0]));
391
392 /* Place stored elements in the vacating slots */
393 po->po_argv[po->po_arg_start] = save[0];
394 if (n == 2)
395 po->po_argv[po->po_arg_start + 1] = save[1];
396
397 /* Fix up start index */
398 po->po_arg_start += n;
399 po->po_permuted = 1;
400 }
401 }
402
403 /* Consume next option from PO. On success, update PO members as
404 described below and return 0. On end of options, return 1.
405
406 If the consumed option is a short option, then
407
408 po_chr keeps its option character, and
409 po_cur points to the next option character to be processed
410
411 Otherwise, if the consumed option is a long one, then
412
413 po_chr is 0
414 po_cur points to the first character after --
415 */
416 static int
next_opt(struct mu_parseopt * po)417 next_opt (struct mu_parseopt *po)
418 {
419 if (!*po->po_cur)
420 {
421 permute (po);
422
423 while (1)
424 {
425 po->po_cur = po->po_argv[po->po_ind++];
426 if (!po->po_cur)
427 return 1;
428 if (po->po_cur[0] == '-' && po->po_cur[1])
429 {
430 if (*++po->po_cur == '-')
431 {
432 if (*++po->po_cur == 0)
433 {
434 /* End of options */
435 permute (po);
436 ++po->po_ind;
437 return 1;
438 }
439
440 if (po->po_flags & MU_PARSEOPT_SINGLE_DASH)
441 /* a non-optional argument */;
442 else
443 {
444 /* It's a long option */
445 po->po_chr = 0;
446 return 0;
447 }
448 }
449 else if (po->po_flags & MU_PARSEOPT_SINGLE_DASH)
450 {
451 /* Assume single-dash long option */
452 po->po_chr = 0;
453 return 0;
454 }
455 else
456 break;
457 }
458
459 if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
460 {
461 if (!po->po_permuted && po->po_arg_count == 0)
462 po->po_arg_start = po->po_ind - 1;
463 po->po_arg_count++;
464 continue;
465 }
466 else
467 return 1;
468 }
469 }
470
471 po->po_chr = *po->po_cur++;
472
473 return 0;
474 }
475
476 /* Parse options */
477 static int
parse(struct mu_parseopt * po)478 parse (struct mu_parseopt *po)
479 {
480 int rc;
481
482 rc = mu_list_create (&po->po_optlist);
483 if (rc)
484 return rc;
485 mu_list_set_destroy_item (po->po_optlist, mu_option_cache_destroy);
486
487 po->po_ind = 0;
488 if (!(po->po_flags & MU_PARSEOPT_ARGV0))
489 {
490 po->po_ind++;
491 if (!(po->po_flags & MU_PARSEOPT_PROG_NAME))
492 {
493 char *p = strrchr (po->po_argv[0], '/');
494 if (p)
495 p++;
496 else
497 p = (char*) po->po_argv[0];
498 if (strlen (p) > 3 && memcmp (p, "lt-", 3) == 0)
499 p += 3;
500 po->po_prog_name = p;
501 }
502 }
503 else if (!(po->po_flags & MU_PARSEOPT_PROG_NAME))
504 po->po_prog_name = NULL;
505
506 po->po_arg_start = po->po_ind;
507 po->po_arg_count = 0;
508 po->po_permuted = 0;
509
510 po->po_cur = "";
511
512 po->po_opterr = -1;
513
514 while (next_opt (po) == 0)
515 {
516 struct mu_option *opt, *uopt;
517 char *value;
518
519 if (po->po_chr)
520 {
521 opt = find_short_option (po, po->po_chr);
522 uopt = NULL;
523 value = NULL;
524 }
525 else
526 {
527 opt = find_long_option (po, po->po_cur, &uopt, &po->po_cur, &value);
528 }
529
530 if (opt)
531 {
532 char *arg = NULL;
533
534 if (opt->opt_arg)
535 {
536 if (po->po_cur[0])
537 {
538 arg = po->po_cur;
539 po->po_cur = "";
540 }
541 else if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL)
542 /* ignore it */;
543 else if (po->po_ind < po->po_argc)
544 arg = po->po_argv[po->po_ind++];
545 else
546 {
547 if (uopt)
548 mu_parseopt_error (po,
549 _("option '%s%s' requires an argument"),
550 po->po_long_opt_start, uopt->opt_long);
551 else
552 mu_parseopt_error (po,
553 _("option '-%c' requires an argument"),
554 po->po_chr);
555 po->po_opterr = po->po_ind;
556 if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
557 {
558 if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
559 po->po_arg_count++;
560 continue;
561 }
562 exit (po->po_exit_error);
563 }
564 }
565 else
566 {
567 if (uopt
568 && po->po_cur[0]
569 && !(po->po_flags & MU_OPTION_ARG_OPTIONAL))
570 {
571 mu_parseopt_error (po,
572 _("option '%s%s' doesn't allow an argument"),
573 po->po_long_opt_start,
574 uopt->opt_long);
575 po->po_opterr = po->po_ind;
576 if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
577 {
578 if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
579 po->po_arg_count++;
580 continue;
581 }
582 exit (po->po_exit_error);
583 }
584 arg = NULL;
585 }
586
587 if (!arg && value)
588 arg = value;
589
590 add_option_cache (po, opt, arg);
591 }
592 else
593 {
594 po->po_opterr = po->po_ind;
595 if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
596 {
597 if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
598 po->po_arg_count++;
599 continue;
600 }
601 exit (po->po_exit_error);
602 }
603 }
604
605 if (!po->po_permuted)
606 po->po_arg_start = po->po_ind - 1 - po->po_arg_count;
607 return 0;
608 }
609
610 #define LONGOPT(po, i) po->po_optv[po->po_longidx[i]]->opt_long
611
612 static void
sort_longidx(struct mu_parseopt * po)613 sort_longidx (struct mu_parseopt *po)
614 {
615 /* Sort the po_longidx array so that its elements produce lexicographically
616 ascending list of long options.
617 Given relatively small number of command line options, simple insertion
618 sort is used.
619 */
620 size_t i, j;
621
622 for (i = 1; i < po->po_longcnt; i++)
623 {
624 for (j = i; j > 0 && strcmp (LONGOPT (po, j-1), LONGOPT (po, j)) > 0; j--)
625 {
626 size_t tmp = po->po_longidx[j];
627 po->po_longidx[j] = po->po_longidx[j-1];
628 po->po_longidx[j-1] = tmp;
629 }
630 }
631 }
632
633 /* Initialize structure mu_parseopt with given options and flags. */
634 static int
parseopt_init(struct mu_parseopt * po,struct mu_option ** options,int flags)635 parseopt_init (struct mu_parseopt *po, struct mu_option **options,
636 int flags)
637 {
638 struct mu_option *opt;
639 size_t i, j;
640
641 po->po_argc = 0;
642 po->po_argv = NULL;
643 po->po_optc = 0;
644 po->po_flags = flags;
645
646 /* Fix up flags */
647 if (flags & MU_PARSEOPT_IGNORE_ERRORS)
648 flags |= MU_PARSEOPT_NO_ERREXIT;
649
650 if (!(flags & MU_PARSEOPT_PROG_DOC))
651 po->po_prog_doc = NULL;
652 if (!(flags & MU_PARSEOPT_PROG_ARGS))
653 po->po_prog_args = NULL;
654 if (!(flags & MU_PARSEOPT_SPECIAL_ARGS))
655 po->po_special_args = NULL;
656 if (!(flags & MU_PARSEOPT_BUG_ADDRESS))
657 po->po_bug_address = NULL;
658 if (!(flags & MU_PARSEOPT_PACKAGE_NAME))
659 po->po_package_name = NULL;
660 if (!(flags & MU_PARSEOPT_PACKAGE_URL))
661 po->po_package_url = NULL;
662 if (!(flags & MU_PARSEOPT_DATA))
663 po->po_data = NULL;
664 if (!(flags & MU_PARSEOPT_EXTRA_INFO))
665 po->po_extra_info = NULL;
666 if (!(flags & MU_PARSEOPT_HELP_HOOK))
667 po->po_help_hook = NULL;
668 if (!(flags & MU_PARSEOPT_EXIT_ERROR))
669 po->po_exit_error = EXIT_ERROR;
670 if (!(flags & MU_PARSEOPT_VERSION_HOOK))
671 po->po_version_hook = NULL;
672 if (!(flags & MU_PARSEOPT_PROG_DOC_HOOK))
673 po->po_prog_doc_hook = NULL;
674 if (!(flags & MU_PARSEOPT_NEGATION))
675 po->po_negation = NULL;
676
677 if (flags & MU_PARSEOPT_SINGLE_DASH)
678 po->po_long_opt_start = "-";
679 else
680 po->po_long_opt_start = "--";
681
682 /* Count the options */
683 po->po_optc = 0;
684 for (i = 0; options[i]; i++)
685 for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++)
686 ++po->po_optc;
687
688 if (!(flags & MU_PARSEOPT_NO_STDOPT))
689 for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++)
690 ++po->po_optc;
691
692 if (flags & MU_PARSEOPT_VERSION_HOOK)
693 for (i = 0; !MU_OPTION_IS_END (&mu_version_options[i]); i++)
694 ++po->po_optc;
695
696 /* Allocate the working buffer of option pointers */
697 po->po_optv = mu_calloc (po->po_optc + 1, sizeof (*po->po_optv));
698 if (!po->po_optv)
699 return -1;
700
701 /* Fill in the array */
702 j = 0;
703 for (i = 0; options[i]; i++)
704 for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++, j++)
705 {
706 if (!opt->opt_set)
707 opt->opt_set = mu_option_set_value;
708 po->po_optv[j] = opt;
709 }
710
711 if (!(flags & MU_PARSEOPT_NO_STDOPT))
712 for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++, j++)
713 po->po_optv[j] = &mu_default_options[i];
714
715 if (flags & MU_PARSEOPT_VERSION_HOOK)
716 for (i = 0; !MU_OPTION_IS_END (&mu_version_options[i]); i++, j++)
717 po->po_optv[j] = &mu_version_options[i];
718
719 po->po_optv[j] = NULL;
720
721 /* Ensure sane start of options. */
722 po->po_optv[0]->opt_flags &= ~MU_OPTION_ALIAS;
723 if (!(flags & MU_PARSEOPT_NO_SORT))
724 {
725 /* Sort the options */
726 size_t start;
727
728 for (start = 0; start < po->po_optc; )
729 {
730 if (MU_OPTION_IS_GROUP_HEADER (po->po_optv[start]))
731 start = sort_group (po->po_optv, start + 1);
732 else
733 start = sort_group (po->po_optv, start);
734 }
735 }
736
737 j = 0;
738 for (i = 0; i < po->po_optc; i++)
739 if (MU_OPTION_IS_VALID_LONG_OPTION (po->po_optv[i]))
740 j++;
741 po->po_longcnt = j;
742
743 po->po_longidx = mu_calloc (j + 1, sizeof (po->po_longidx[0]));
744 j = 0;
745 for (i = 0; i < po->po_optc; i++)
746 if (MU_OPTION_IS_VALID_LONG_OPTION (po->po_optv[i]))
747 po->po_longidx[j++] = i;
748
749 sort_longidx (po);
750
751 po->po_ind = 0;
752 po->po_opterr = 0;
753 po->po_optlist = NULL;
754 po->po_cur = NULL;
755 po->po_chr = 0;
756 po->po_arg_start = 0;
757 po->po_arg_count = 0;
758 po->po_permuted = 0;
759
760 return 0;
761 }
762
763 /* Parse command line from ARGC/ARGV. Valid options are given in
764 OPTIONS. FLAGS control the parsing. */
765 int
mu_parseopt(struct mu_parseopt * po,int argc,char ** argv,struct mu_option ** options,int flags)766 mu_parseopt (struct mu_parseopt *po,
767 int argc, char **argv, struct mu_option **options,
768 int flags)
769 {
770 int rc;
771
772 if (flags & MU_PARSEOPT_REUSE)
773 {
774 mu_list_clear (po->po_optlist);
775 po->po_flags = (po->po_flags & MU_PARSEOPT_IMMUTABLE_MASK)
776 | (flags & ~MU_PARSEOPT_IMMUTABLE_MASK);
777 }
778 else
779 {
780 rc = parseopt_init (po, options, flags);
781 if (rc)
782 return rc;
783 }
784 po->po_argc = argc;
785 po->po_argv = argv;
786
787 rc = parse (po);
788
789 if (rc == 0)
790 {
791 if (po->po_opterr >= 0)
792 rc = -1;
793 else
794 {
795 if (po->po_flags & MU_PARSEOPT_IMMEDIATE)
796 rc = mu_parseopt_apply (po);
797 }
798 }
799
800 return rc;
801 }
802
803 void
mu_parseopt_free(struct mu_parseopt * popt)804 mu_parseopt_free (struct mu_parseopt *popt)
805 {
806 free (popt->po_optv);
807 free (popt->po_longidx);
808 mu_list_destroy (&popt->po_optlist);
809 }
810
811 static int
parseopt_apply(void * item,void * data)812 parseopt_apply (void *item, void *data)
813 {
814 struct mu_option_cache *cp = item;
815 struct mu_parseopt *popt = data;
816 cp->cache_opt->opt_set (popt, cp->cache_opt, cp->cache_arg);
817 return 0;
818 }
819
820 int
mu_parseopt_apply(struct mu_parseopt * popt)821 mu_parseopt_apply (struct mu_parseopt *popt)
822 {
823 return mu_list_foreach (popt->po_optlist, parseopt_apply, popt);
824 }
825
826 void
mu_option_set_value(struct mu_parseopt * po,struct mu_option * opt,char const * arg)827 mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt,
828 char const *arg)
829 {
830 if (opt->opt_ptr)
831 {
832 char *errmsg;
833 int rc;
834
835 if (arg == NULL)
836 {
837 if (opt->opt_default)
838 /* The default value has been already assigned in add_option_cache.
839 This conditional is here in case mu_option_set_value is called
840 from the user code. */
841 arg = opt->opt_default;
842 else if (opt->opt_arg == NULL)
843 arg = "1";
844 else
845 {
846 *(void**)opt->opt_ptr = NULL;
847 return;
848 }
849 }
850 rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg);
851 if (rc)
852 {
853 char const *errtext;
854 if (errmsg)
855 errtext = errmsg;
856 else
857 errtext = mu_strerror (rc);
858
859 if (opt->opt_long)
860 mu_parseopt_error (po, "%s%s: %s", po->po_long_opt_start,
861 opt->opt_long, errtext);
862 else
863 mu_parseopt_error (po, "-%c: %s", opt->opt_short, errtext);
864 free (errmsg);
865
866 if (!(po->po_flags & MU_PARSEOPT_NO_ERREXIT))
867 exit (po->po_exit_error);
868 }
869 }
870 }
871
872 int
mu_option_possible_negation(struct mu_parseopt * po,struct mu_option * opt)873 mu_option_possible_negation (struct mu_parseopt *po, struct mu_option *opt)
874 {
875 return po->po_negation && opt->opt_type == mu_c_bool && !opt->opt_arg;
876 }
877