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