g10: Fix card-edit/fetch to use keyserver_fetch.
[gnupg.git] / jnlib / argparse.c
1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2  * Copyright (C) 1998, 1999, 2000, 2001, 2006
3  *               2007, 2008, 2012  Free Software Foundation, Inc.
4  * Copyright (C) 1997, 2013 Werner Koch
5  *
6  * This file is part of JNLIB.
7  *
8  * JNLIB is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as
10  * published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * JNLIB is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <limits.h>
32 #include <errno.h>
33
34 #include "libjnlib-config.h"
35 #include "mischelp.h"
36 #include "stringhelp.h"
37 #include "logging.h"
38 #ifdef JNLIB_NEED_UTF8CONV
39 #include "utf8conv.h"
40 #endif
41 #include "argparse.h"
42
43
44
45 /*********************************
46  * @Summary arg_parse
47  *  #include <wk/lib.h>
48  *
49  *  typedef struct {
50  *      char *argc;               pointer to argc (value subject to change)
51  *      char ***argv;             pointer to argv (value subject to change)
52  *      unsigned flags;           Global flags (DO NOT CHANGE)
53  *      int err;                  print error about last option
54  *                                1 = warning, 2 = abort
55  *      int r_opt;                return option
56  *      int r_type;               type of return value (0 = no argument found)
57  *      union {
58  *          int   ret_int;
59  *          long  ret_long
60  *          ulong ret_ulong;
61  *          char *ret_str;
62  *      } r;                      Return values
63  *      struct {
64  *          int idx;
65  *          const char *last;
66  *          void *aliases;
67  *      } internal;               DO NOT CHANGE
68  *  } ARGPARSE_ARGS;
69  *
70  *  typedef struct {
71  *      int         short_opt;
72  *      const char *long_opt;
73  *      unsigned flags;
74  *  } ARGPARSE_OPTS;
75  *
76  *  int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
77  *
78  * @Description
79  *  This is my replacement for getopt(). See the example for a typical usage.
80  *  Global flags are:
81  *     Bit 0 : Do not remove options form argv
82  *     Bit 1 : Do not stop at last option but return other args
83  *             with r_opt set to -1.
84  *     Bit 2 : Assume options and real args are mixed.
85  *     Bit 3 : Do not use -- to stop option processing.
86  *     Bit 4 : Do not skip the first arg.
87  *     Bit 5 : allow usage of long option with only one dash
88  *     Bit 6 : ignore --version
89  *     all other bits must be set to zero, this value is modified by the
90  *     function, so assume this is write only.
91  *  Local flags (for each option):
92  *     Bit 2-0 : 0 = does not take an argument
93  *               1 = takes int argument
94  *               2 = takes string argument
95  *               3 = takes long argument
96  *               4 = takes ulong argument
97  *     Bit 3 : argument is optional (r_type will the be set to 0)
98  *     Bit 4 : allow 0x etc. prefixed values.
99  *     Bit 6 : Ignore this option
100  *     Bit 7 : This is a command and not an option
101  *  You stop the option processing by setting opts to NULL, the function will
102  *  then return 0.
103  * @Return Value
104  *   Returns the args.r_opt or 0 if ready
105  *   r_opt may be -2/-7 to indicate an unknown option/command.
106  * @See Also
107  *   ArgExpand
108  * @Notes
109  *  You do not need to process the options 'h', '--help' or '--version'
110  *  because this function includes standard help processing; but if you
111  *  specify '-h', '--help' or '--version' you have to do it yourself.
112  *  The option '--' stops argument processing; if bit 1 is set the function
113  *  continues to return normal arguments.
114  *  To process float args or unsigned args you must use a string args and do
115  *  the conversion yourself.
116  * @Example
117  *
118  *     ARGPARSE_OPTS opts[] = {
119  *     { 'v', "verbose",   0 },
120  *     { 'd', "debug",     0 },
121  *     { 'o', "output",    2 },
122  *     { 'c', "cross-ref", 2|8 },
123  *     { 'm', "my-option", 1|8 },
124  *     { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
125  *     { 500, "have-no-short-option-for-this-long-option", 0 },
126  *     {0} };
127  *     ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
128  *
129  *     while( ArgParse( &pargs, &opts) ) {
130  *         switch( pargs.r_opt ) {
131  *           case 'v': opt.verbose++; break;
132  *           case 'd': opt.debug++; break;
133  *           case 'o': opt.outfile = pargs.r.ret_str; break;
134  *           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
135  *           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
136  *           case 500: opt.a_long_one++;  break
137  *           default : pargs.err = 1; break; -- force warning output --
138  *         }
139  *     }
140  *     if( argc > 1 )
141  *         log_fatal( "Too many args");
142  *
143  */
144
145 typedef struct alias_def_s *ALIAS_DEF;
146 struct alias_def_s {
147     ALIAS_DEF next;
148     char *name;   /* malloced buffer with name, \0, value */
149     const char *value; /* ptr into name */
150 };
151
152
153 /* Object to store the names for the --ignore-invalid-option option.
154    This is a simple linked list.  */
155 typedef struct iio_item_def_s *IIO_ITEM_DEF;
156 struct iio_item_def_s
157 {
158   IIO_ITEM_DEF next;
159   char name[1];      /* String with the long option name.  */
160 };
161
162 static const char *(*strusage_handler)( int ) = NULL;
163
164 static int  set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
165 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
166 static void show_version(void);
167
168
169 static void
170 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
171 {
172   if( !(arg->flags & (1<<15)) )
173     {
174       /* Initialize this instance. */
175       arg->internal.idx = 0;
176       arg->internal.last = NULL;
177       arg->internal.inarg = 0;
178       arg->internal.stopped = 0;
179       arg->internal.aliases = NULL;
180       arg->internal.cur_alias = NULL;
181       arg->internal.iio_list = NULL;
182       arg->err = 0;
183       arg->flags |= 1<<15; /* Mark as initialized.  */
184       if ( *arg->argc < 0 )
185         jnlib_log_bug ("invalid argument for arg_parsee\n");
186     }
187
188
189   if (arg->err)
190     {
191       /* Last option was erroneous.  */
192       const char *s;
193
194       if (filename)
195         {
196           if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
197             s = _("argument not expected");
198           else if ( arg->r_opt == ARGPARSE_READ_ERROR )
199             s = _("read error");
200           else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
201             s = _("keyword too long");
202           else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
203             s = _("missing argument");
204           else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
205             s = _("invalid argument");
206           else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
207             s = _("invalid command");
208           else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
209             s = _("invalid alias definition");
210           else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
211             s = _("out of core");
212           else
213             s = _("invalid option");
214           jnlib_log_error ("%s:%u: %s\n", filename, *lineno, s);
215         }
216       else
217         {
218           s = arg->internal.last? arg->internal.last:"[??]";
219
220           if ( arg->r_opt == ARGPARSE_MISSING_ARG )
221             jnlib_log_error (_("missing argument for option \"%.50s\"\n"), s);
222           else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
223             jnlib_log_error (_("invalid argument for option \"%.50s\"\n"), s);
224           else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
225             jnlib_log_error (_("option \"%.50s\" does not expect an "
226                                "argument\n"), s );
227           else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
228             jnlib_log_error (_("invalid command \"%.50s\"\n"), s);
229           else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
230             jnlib_log_error (_("option \"%.50s\" is ambiguous\n"), s);
231           else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
232             jnlib_log_error (_("command \"%.50s\" is ambiguous\n"),s );
233           else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
234             jnlib_log_error ("%s\n", _("out of core\n"));
235           else
236             jnlib_log_error (_("invalid option \"%.50s\"\n"), s);
237         }
238       if ( arg->err != 1 )
239         exit (2);
240       arg->err = 0;
241     }
242
243   /* Zero out the return value union.  */
244   arg->r.ret_str = NULL;
245   arg->r.ret_long = 0;
246 }
247
248
249 static void
250 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
251 {
252     /* TODO: replace this dummy function with a rea one
253      * and fix the probelms IRIX has with (ALIAS_DEV)arg..
254      * used as lvalue
255      */
256   (void)arg;
257   (void)name;
258   (void)value;
259 #if 0
260     ALIAS_DEF a = jnlib_xmalloc( sizeof *a );
261     a->name = name;
262     a->value = value;
263     a->next = (ALIAS_DEF)arg->internal.aliases;
264     (ALIAS_DEF)arg->internal.aliases = a;
265 #endif
266 }
267
268
269 /* Return true if KEYWORD is in the ignore-invalid-option list.  */
270 static int
271 ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
272 {
273   IIO_ITEM_DEF item = arg->internal.iio_list;
274
275   for (; item; item = item->next)
276     if (!strcmp (item->name, keyword))
277       return 1;
278   return 0;
279 }
280
281
282 /* Add the keywords up to the next LF to the list of to be ignored
283    options.  After returning FP will either be at EOF or the next
284    character read wll be the first of a new line.  The function
285    returns 0 on success or true on malloc failure.  */
286 static int
287 ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
288 {
289   IIO_ITEM_DEF item;
290   int c;
291   char name[100];
292   int namelen = 0;
293   int ready = 0;
294   enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
295
296   while (!ready)
297     {
298       c = getc (fp);
299       if (c == '\n')
300         ready = 1;
301       else if (c == EOF)
302         {
303           c = '\n';
304           ready = 1;
305         }
306     again:
307       switch (state)
308         {
309         case skipWS:
310           if (!isascii (c) || !isspace(c))
311             {
312               namelen = 0;
313               state = collectNAME;
314               goto again;
315             }
316           break;
317
318         case collectNAME:
319           if (isspace (c))
320             {
321               state = addNAME;
322               goto again;
323             }
324           else if (namelen < DIM(name)-1)
325             name[namelen++] = c;
326           else /* Too long.  */
327             state = skipNAME;
328           break;
329
330         case skipNAME:
331           if (isspace (c))
332             {
333               state = skipWS;
334               goto again;
335             }
336           break;
337
338         case addNAME:
339           name[namelen] = 0;
340           if (!ignore_invalid_option_p (arg, name))
341             {
342               item = jnlib_malloc (sizeof *item + namelen);
343               if (!item)
344                 return 1;
345               strcpy (item->name, name);
346               item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
347               arg->internal.iio_list = item;
348             }
349           state = skipWS;
350           goto again;
351         }
352     }
353   return 0;
354 }
355
356
357 /* Clear the entire ignore-invalid-option list.  */
358 static void
359 ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
360 {
361   IIO_ITEM_DEF item, tmpitem;
362
363   for (item = arg->internal.iio_list; item; item = tmpitem)
364     {
365       tmpitem = item->next;
366       jnlib_free (item);
367     }
368   arg->internal.iio_list = NULL;
369 }
370
371
372
373 /****************
374  * Get options from a file.
375  * Lines starting with '#' are comment lines.
376  * Syntax is simply a keyword and the argument.
377  * Valid keywords are all keywords from the long_opt list without
378  * the leading dashes. The special keywords "help", "warranty" and "version"
379  * are not valid here.
380  * The special keyword "alias" may be used to store alias definitions,
381  * which are later expanded like long options.
382  * The option
383  *   ignore-invalid-option OPTIONNAMEs
384  * is recognized and updates a list of option which should be ignored if they
385  * are not defined.
386  * Caller must free returned strings.
387  * If called with FP set to NULL command line args are parse instead.
388  *
389  * Q: Should we allow the syntax
390  *     keyword = value
391  *    and accept for boolean options a value of 1/0, yes/no or true/false?
392  * Note: Abbreviation of options is here not allowed.
393  */
394 int
395 optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
396                ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
397 {
398   int state, i, c;
399   int idx=0;
400   char keyword[100];
401   char *buffer = NULL;
402   size_t buflen = 0;
403   int in_alias=0;
404
405   if (!fp) /* Divert to to arg_parse() in this case.  */
406     return arg_parse (arg, opts);
407
408   initialize (arg, filename, lineno);
409
410   /* Find the next keyword.  */
411   state = i = 0;
412   for (;;)
413     {
414       c = getc (fp);
415       if (c == '\n' || c== EOF )
416         {
417           if ( c != EOF )
418             ++*lineno;
419           if (state == -1)
420             break;
421           else if (state == 2)
422             {
423               keyword[i] = 0;
424               for (i=0; opts[i].short_opt; i++ )
425                 {
426                   if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
427                     break;
428                 }
429               idx = i;
430               arg->r_opt = opts[idx].short_opt;
431               if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
432                 {
433                   state = i = 0;
434                   continue;
435                 }
436               else if (!opts[idx].short_opt )
437                 {
438                   if (!strcmp (keyword, "ignore-invalid-option"))
439                     {
440                       /* No argument - ignore this meta option.  */
441                       state = i = 0;
442                       continue;
443                     }
444                   else if (ignore_invalid_option_p (arg, keyword))
445                     {
446                       /* This invalid option is in the iio list.  */
447                       state = i = 0;
448                       continue;
449                     }
450                   arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
451                                 ? ARGPARSE_INVALID_COMMAND
452                                 : ARGPARSE_INVALID_OPTION);
453                 }
454               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
455                 arg->r_type = 0; /* Does not take an arg. */
456               else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
457                 arg->r_type = 0; /* Arg is optional.  */
458               else
459                 arg->r_opt = ARGPARSE_MISSING_ARG;
460
461               break;
462             }
463           else if (state == 3)
464             {
465               /* No argument found.  */
466               if (in_alias)
467                 arg->r_opt = ARGPARSE_MISSING_ARG;
468               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
469                 arg->r_type = 0; /* Does not take an arg. */
470               else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
471                 arg->r_type = 0; /* No optional argument. */
472               else
473                 arg->r_opt = ARGPARSE_MISSING_ARG;
474
475               break;
476             }
477           else if (state == 4)
478             {
479               /* Has an argument. */
480               if (in_alias)
481                 {
482                   if (!buffer)
483                     arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
484                   else
485                     {
486                       char *p;
487
488                       buffer[i] = 0;
489                       p = strpbrk (buffer, " \t");
490                       if (p)
491                         {
492                           *p++ = 0;
493                           trim_spaces (p);
494                         }
495                       if (!p || !*p)
496                         {
497                           jnlib_free (buffer);
498                           arg->r_opt = ARGPARSE_INVALID_ALIAS;
499                         }
500                       else
501                         {
502                           store_alias (arg, buffer, p);
503                         }
504                     }
505                 }
506               else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
507                 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
508               else
509                 {
510                   char *p;
511
512                   if (!buffer)
513                     {
514                       keyword[i] = 0;
515                       buffer = jnlib_strdup (keyword);
516                       if (!buffer)
517                         arg->r_opt = ARGPARSE_OUT_OF_CORE;
518                     }
519                   else
520                     buffer[i] = 0;
521
522                   if (buffer)
523                     {
524                       trim_spaces (buffer);
525                       p = buffer;
526                       if (*p == '"')
527                         {
528                           /* Remove quotes. */
529                           p++;
530                           if (*p && p[strlen(p)-1] == '\"' )
531                             p[strlen(p)-1] = 0;
532                         }
533                       if (!set_opt_arg (arg, opts[idx].flags, p))
534                         jnlib_free(buffer);
535                     }
536                 }
537               break;
538             }
539           else if (c == EOF)
540             {
541               ignore_invalid_option_clear (arg);
542               if (ferror (fp))
543                 arg->r_opt = ARGPARSE_READ_ERROR;
544               else
545                 arg->r_opt = 0; /* EOF. */
546               break;
547             }
548           state = 0;
549           i = 0;
550         }
551       else if (state == -1)
552         ; /* Skip. */
553       else if (state == 0 && isascii (c) && isspace(c))
554         ; /* Skip leading white space.  */
555       else if (state == 0 && c == '#' )
556         state = 1;      /* Start of a comment.  */
557       else if (state == 1)
558         ; /* Skip comments. */
559       else if (state == 2 && isascii (c) && isspace(c))
560         {
561           /* Check keyword.  */
562           keyword[i] = 0;
563           for (i=0; opts[i].short_opt; i++ )
564             if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
565               break;
566           idx = i;
567           arg->r_opt = opts[idx].short_opt;
568           if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
569             {
570               state = 1; /* Process like a comment.  */
571             }
572           else if (!opts[idx].short_opt)
573             {
574               if (!strcmp (keyword, "alias"))
575                 {
576                   in_alias = 1;
577                   state = 3;
578                 }
579               else if (!strcmp (keyword, "ignore-invalid-option"))
580                 {
581                   if (ignore_invalid_option_add (arg, fp))
582                     {
583                       arg->r_opt = ARGPARSE_OUT_OF_CORE;
584                       break;
585                     }
586                   state = i = 0;
587                   ++*lineno;
588                 }
589               else if (ignore_invalid_option_p (arg, keyword))
590                 state = 1; /* Process like a comment.  */
591               else
592                 {
593                   arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
594                                 ? ARGPARSE_INVALID_COMMAND
595                                 : ARGPARSE_INVALID_OPTION);
596                   state = -1; /* Skip rest of line and leave.  */
597                 }
598             }
599           else
600             state = 3;
601         }
602       else if (state == 3)
603         {
604           /* Skip leading spaces of the argument.  */
605           if (!isascii (c) || !isspace(c))
606             {
607               i = 0;
608               keyword[i++] = c;
609               state = 4;
610             }
611         }
612       else if (state == 4)
613         {
614           /* Collect the argument. */
615           if (buffer)
616             {
617               if (i < buflen-1)
618                 buffer[i++] = c;
619               else
620                 {
621                   char *tmp;
622                   size_t tmplen = buflen + 50;
623
624                   tmp = jnlib_realloc (buffer, tmplen);
625                   if (tmp)
626                     {
627                       buflen = tmplen;
628                       buffer = tmp;
629                       buffer[i++] = c;
630                     }
631                   else
632                     {
633                       jnlib_free (buffer);
634                       arg->r_opt = ARGPARSE_OUT_OF_CORE;
635                       break;
636                     }
637                 }
638             }
639           else if (i < DIM(keyword)-1)
640             keyword[i++] = c;
641           else
642             {
643               size_t tmplen = DIM(keyword) + 50;
644               buffer = jnlib_malloc (tmplen);
645               if (buffer)
646                 {
647                   buflen = tmplen;
648                   memcpy(buffer, keyword, i);
649                   buffer[i++] = c;
650                 }
651               else
652                 {
653                   arg->r_opt = ARGPARSE_OUT_OF_CORE;
654                   break;
655                 }
656             }
657         }
658       else if (i >= DIM(keyword)-1)
659         {
660           arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
661           state = -1; /* Skip rest of line and leave.  */
662         }
663       else
664         {
665           keyword[i++] = c;
666           state = 2;
667         }
668     }
669
670   return arg->r_opt;
671 }
672
673
674
675 static int
676 find_long_option( ARGPARSE_ARGS *arg,
677                   ARGPARSE_OPTS *opts, const char *keyword )
678 {
679     int i;
680     size_t n;
681
682     (void)arg;
683
684     /* Would be better if we can do a binary search, but it is not
685        possible to reorder our option table because we would mess
686        up our help strings - What we can do is: Build a nice option
687        lookup table wehn this function is first invoked */
688     if( !*keyword )
689         return -1;
690     for(i=0; opts[i].short_opt; i++ )
691         if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
692             return i;
693 #if 0
694     {
695         ALIAS_DEF a;
696         /* see whether it is an alias */
697         for( a = args->internal.aliases; a; a = a->next ) {
698             if( !strcmp( a->name, keyword) ) {
699                 /* todo: must parse the alias here */
700                 args->internal.cur_alias = a;
701                 return -3; /* alias available */
702             }
703         }
704     }
705 #endif
706     /* not found, see whether it is an abbreviation */
707     /* aliases may not be abbreviated */
708     n = strlen( keyword );
709     for(i=0; opts[i].short_opt; i++ ) {
710         if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
711             int j;
712             for(j=i+1; opts[j].short_opt; j++ ) {
713                 if( opts[j].long_opt
714                     && !strncmp( opts[j].long_opt, keyword, n ) )
715                     return -2;  /* abbreviation is ambiguous */
716             }
717             return i;
718         }
719     }
720     return -1;  /* Not found.  */
721 }
722
723 int
724 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
725 {
726   int idx;
727   int argc;
728   char **argv;
729   char *s, *s2;
730   int i;
731
732   initialize( arg, NULL, NULL );
733   argc = *arg->argc;
734   argv = *arg->argv;
735   idx = arg->internal.idx;
736
737   if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
738     {
739       /* Skip the first argument.  */
740       argc--; argv++; idx++;
741     }
742
743  next_one:
744   if (!argc)
745     {
746       /* No more args.  */
747       arg->r_opt = 0;
748       goto leave; /* Ready. */
749     }
750
751   s = *argv;
752   arg->internal.last = s;
753
754   if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
755     {
756       arg->r_opt = ARGPARSE_IS_ARG;  /* Not an option but an argument.  */
757       arg->r_type = 2;
758       arg->r.ret_str = s;
759       argc--; argv++; idx++; /* set to next one */
760     }
761   else if( arg->internal.stopped )
762     {
763       arg->r_opt = 0;
764       goto leave; /* Ready.  */
765     }
766   else if ( *s == '-' && s[1] == '-' )
767     {
768       /* Long option.  */
769       char *argpos;
770
771       arg->internal.inarg = 0;
772       if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
773         {
774           /* Stop option processing.  */
775           arg->internal.stopped = 1;
776           argc--; argv++; idx++;
777           goto next_one;
778         }
779
780       argpos = strchr( s+2, '=' );
781       if ( argpos )
782         *argpos = 0;
783       i = find_long_option ( arg, opts, s+2 );
784       if ( argpos )
785         *argpos = '=';
786
787       if ( i < 0 && !strcmp ( "help", s+2) )
788         show_help (opts, arg->flags);
789       else if ( i < 0 && !strcmp ( "version", s+2) )
790         {
791           if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
792             {
793               show_version ();
794               exit(0);
795             }
796         }
797       else if ( i < 0 && !strcmp( "warranty", s+2))
798         {
799           puts ( strusage (16) );
800           exit (0);
801         }
802       else if ( i < 0 && !strcmp( "dump-options", s+2) )
803         {
804           for (i=0; opts[i].short_opt; i++ )
805             {
806               if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
807                 printf ("--%s\n", opts[i].long_opt);
808             }
809           fputs ("--dump-options\n--help\n--version\n--warranty\n", stdout);
810           exit (0);
811         }
812
813       if ( i == -2 )
814         arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
815       else if ( i == -1 )
816         {
817           arg->r_opt = ARGPARSE_INVALID_OPTION;
818           arg->r.ret_str = s+2;
819         }
820       else
821         arg->r_opt = opts[i].short_opt;
822       if ( i < 0 )
823         ;
824       else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
825         {
826           if ( argpos )
827             {
828               s2 = argpos+1;
829               if ( !*s2 )
830                 s2 = NULL;
831             }
832           else
833             s2 = argv[1];
834           if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
835             {
836               arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional.  */
837             }
838           else if ( !s2 )
839             {
840               arg->r_opt = ARGPARSE_MISSING_ARG;
841             }
842           else if ( !argpos && *s2 == '-'
843                     && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
844             {
845               /* The argument is optional and the next seems to be an
846                  option.  We do not check this possible option but
847                  assume no argument */
848               arg->r_type = ARGPARSE_TYPE_NONE;
849             }
850           else
851             {
852               set_opt_arg (arg, opts[i].flags, s2);
853               if ( !argpos )
854                 {
855                   argc--; argv++; idx++; /* Skip one.  */
856                 }
857             }
858         }
859       else
860         {
861           /* Does not take an argument. */
862           if ( argpos )
863             arg->r_type = ARGPARSE_UNEXPECTED_ARG;
864           else
865             arg->r_type = 0;
866         }
867       argc--; argv++; idx++; /* Set to next one.  */
868     }
869     else if ( (*s == '-' && s[1]) || arg->internal.inarg )
870       {
871         /* Short option.  */
872         int dash_kludge = 0;
873
874         i = 0;
875         if ( !arg->internal.inarg )
876           {
877             arg->internal.inarg++;
878             if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
879               {
880                 for (i=0; opts[i].short_opt; i++ )
881                   if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
882                     {
883                       dash_kludge = 1;
884                       break;
885                     }
886               }
887           }
888         s += arg->internal.inarg;
889
890         if (!dash_kludge )
891           {
892             for (i=0; opts[i].short_opt; i++ )
893               if ( opts[i].short_opt == *s )
894                 break;
895           }
896
897         if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
898           show_help (opts, arg->flags);
899
900         arg->r_opt = opts[i].short_opt;
901         if (!opts[i].short_opt )
902           {
903             arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
904               ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
905             arg->internal.inarg++; /* Point to the next arg.  */
906             arg->r.ret_str = s;
907           }
908         else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
909           {
910             if ( s[1] && !dash_kludge )
911               {
912                 s2 = s+1;
913                 set_opt_arg (arg, opts[i].flags, s2);
914               }
915             else
916               {
917                 s2 = argv[1];
918                 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
919                   {
920                     arg->r_type = ARGPARSE_TYPE_NONE;
921                   }
922                 else if ( !s2 )
923                   {
924                     arg->r_opt = ARGPARSE_MISSING_ARG;
925                   }
926                 else if ( *s2 == '-' && s2[1]
927                           && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
928                   {
929                     /* The argument is optional and the next seems to
930                        be an option.  We do not check this possible
931                        option but assume no argument.  */
932                     arg->r_type = ARGPARSE_TYPE_NONE;
933                   }
934                 else
935                   {
936                     set_opt_arg (arg, opts[i].flags, s2);
937                     argc--; argv++; idx++; /* Skip one.  */
938                   }
939               }
940             s = "x"; /* This is so that !s[1] yields false.  */
941           }
942         else
943           {
944             /* Does not take an argument.  */
945             arg->r_type = ARGPARSE_TYPE_NONE;
946             arg->internal.inarg++; /* Point to the next arg.  */
947           }
948         if ( !s[1] || dash_kludge )
949           {
950             /* No more concatenated short options.  */
951             arg->internal.inarg = 0;
952             argc--; argv++; idx++;
953           }
954       }
955   else if ( arg->flags & ARGPARSE_FLAG_MIXED )
956     {
957       arg->r_opt = ARGPARSE_IS_ARG;
958       arg->r_type = 2;
959       arg->r.ret_str = s;
960       argc--; argv++; idx++; /* Set to next one.  */
961     }
962   else
963     {
964       arg->internal.stopped = 1; /* Stop option processing.  */
965       goto next_one;
966     }
967
968  leave:
969   *arg->argc = argc;
970   *arg->argv = argv;
971   arg->internal.idx = idx;
972   return arg->r_opt;
973 }
974
975
976 /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
977    type argument.  */
978 static int
979 set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
980 {
981   int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
982   long l;
983
984   switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
985     {
986     case ARGPARSE_TYPE_LONG:
987     case ARGPARSE_TYPE_INT:
988       errno = 0;
989       l = strtol (s, NULL, base);
990       if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
991         {
992           arg->r_opt = ARGPARSE_INVALID_ARG;
993           return -1;
994         }
995       if (arg->r_type == ARGPARSE_TYPE_LONG)
996         arg->r.ret_long = l;
997       else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
998         {
999           arg->r_opt = ARGPARSE_INVALID_ARG;
1000           return -1;
1001         }
1002       else
1003         arg->r.ret_int = (int)l;
1004       return 0;
1005
1006     case ARGPARSE_TYPE_ULONG:
1007       while (isascii (*s) && isspace(*s))
1008         s++;
1009       if (*s == '-')
1010         {
1011           arg->r.ret_ulong = 0;
1012           arg->r_opt = ARGPARSE_INVALID_ARG;
1013           return -1;
1014         }
1015       errno = 0;
1016       arg->r.ret_ulong = strtoul (s, NULL, base);
1017       if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
1018         {
1019           arg->r_opt = ARGPARSE_INVALID_ARG;
1020           return -1;
1021         }
1022       return 0;
1023
1024     case ARGPARSE_TYPE_STRING:
1025     default:
1026       arg->r.ret_str = s;
1027       return 1;
1028     }
1029 }
1030
1031
1032 static size_t
1033 long_opt_strlen( ARGPARSE_OPTS *o )
1034 {
1035   size_t n = strlen (o->long_opt);
1036
1037   if ( o->description && *o->description == '|' )
1038     {
1039       const char *s;
1040 #ifdef JNLIB_NEED_UTF8CONV
1041       int is_utf8 = is_native_utf8 ();
1042 #endif
1043
1044       s=o->description+1;
1045       if ( *s != '=' )
1046         n++;
1047       /* For a (mostly) correct length calculation we exclude
1048          continuation bytes (10xxxxxx) if we are on a native utf8
1049          terminal. */
1050       for (; *s && *s != '|'; s++ )
1051 #ifdef JNLIB_NEED_UTF8CONV
1052         if ( is_utf8 && (*s&0xc0) != 0x80 )
1053 #endif
1054           n++;
1055     }
1056   return n;
1057 }
1058
1059
1060 /****************
1061  * Print formatted help. The description string has some special
1062  * meanings:
1063  *  - A description string which is "@" suppresses help output for
1064  *    this option
1065  *  - a description,ine which starts with a '@' and is followed by
1066  *    any other characters is printed as is; this may be used for examples
1067  *    ans such.
1068  *  - A description which starts with a '|' outputs the string between this
1069  *    bar and the next one as arguments of the long option.
1070  */
1071 static void
1072 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
1073 {
1074   const char *s;
1075
1076   show_version ();
1077   putchar ('\n');
1078   s = strusage(41);
1079   puts (s);
1080   if ( opts[0].description )
1081     {
1082       /* Auto format the option description.  */
1083       int i,j, indent;
1084
1085       /* Get max. length of long options.  */
1086       for (i=indent=0; opts[i].short_opt; i++ )
1087         {
1088           if ( opts[i].long_opt )
1089             if ( !opts[i].description || *opts[i].description != '@' )
1090               if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
1091                 indent = j;
1092         }
1093
1094       /* Example: " -v, --verbose   Viele Sachen ausgeben" */
1095       indent += 10;
1096       if ( *opts[0].description != '@' )
1097         puts ("Options:");
1098       for (i=0; opts[i].short_opt; i++ )
1099         {
1100           s = _( opts[i].description );
1101           if ( s && *s== '@' && !s[1] ) /* Hide this line.  */
1102             continue;
1103           if ( s && *s == '@' )  /* Unindented comment only line.  */
1104             {
1105               for (s++; *s; s++ )
1106                 {
1107                   if ( *s == '\n' )
1108                     {
1109                       if( s[1] )
1110                         putchar('\n');
1111                     }
1112                   else
1113                     putchar(*s);
1114                 }
1115               putchar('\n');
1116               continue;
1117             }
1118
1119           j = 3;
1120           if ( opts[i].short_opt < 256 )
1121             {
1122               printf (" -%c", opts[i].short_opt);
1123               if ( !opts[i].long_opt )
1124                 {
1125                   if (s && *s == '|' )
1126                     {
1127                       putchar (' '); j++;
1128                       for (s++ ; *s && *s != '|'; s++, j++ )
1129                         putchar (*s);
1130                       if ( *s )
1131                         s++;
1132                     }
1133                 }
1134             }
1135           else
1136             fputs("   ", stdout);
1137           if ( opts[i].long_opt )
1138             {
1139               j += printf ("%c --%s", opts[i].short_opt < 256?',':' ',
1140                            opts[i].long_opt );
1141               if (s && *s == '|' )
1142                 {
1143                   if ( *++s != '=' )
1144                     {
1145                       putchar(' ');
1146                       j++;
1147                     }
1148                   for ( ; *s && *s != '|'; s++, j++ )
1149                     putchar(*s);
1150                   if ( *s )
1151                     s++;
1152                 }
1153               fputs ("   ", stdout);
1154               j += 3;
1155             }
1156           for (;j < indent; j++ )
1157             putchar(' ');
1158           if ( s )
1159             {
1160               if ( *s && j > indent )
1161                 {
1162                   putchar('\n');
1163                   for (j=0;j < indent; j++ )
1164                     putchar (' ');
1165                 }
1166               for (; *s; s++ )
1167                 {
1168                   if ( *s == '\n' )
1169                     {
1170                       if ( s[1] )
1171                         {
1172                           putchar ('\n');
1173                           for (j=0; j < indent; j++ )
1174                             putchar (' ');
1175                         }
1176                     }
1177                   else
1178                     putchar (*s);
1179                 }
1180             }
1181           putchar ('\n');
1182         }
1183         if ( (flags & ARGPARSE_FLAG_ONEDASH) )
1184             puts ("\n(A single dash may be used instead of the double ones)");
1185     }
1186   if ( (s=strusage(19)) )
1187     {
1188       /* bug reports to ... */
1189       char *s2;
1190
1191       putchar('\n');
1192       s2 = strstr (s, "@EMAIL@");
1193       if (s2)
1194         {
1195           if (s2-s)
1196             fwrite (s, s2-s, 1, stdout);
1197 #ifdef PACKAGE_BUGREPORT
1198           fputs (PACKAGE_BUGREPORT, stdout);
1199 #else
1200           fputs ("bug@example.org", stdout);
1201 #endif
1202           s2 += 7;
1203           if (*s2)
1204             fputs (s2, stdout);
1205         }
1206       else
1207         fputs(s, stdout);
1208     }
1209   fflush(stdout);
1210   exit(0);
1211 }
1212
1213 static void
1214 show_version ()
1215 {
1216   const char *s;
1217   int i;
1218
1219   /* Version line.  */
1220   fputs (strusage (11), stdout);
1221   if ((s=strusage (12)))
1222     printf (" (%s)", s );
1223   printf (" %s\n", strusage (13) );
1224   /* Additional version lines. */
1225   for (i=20; i < 30; i++)
1226     if ((s=strusage (i)))
1227       printf ("%s\n", s );
1228   /* Copyright string.  */
1229   if( (s=strusage (14)) )
1230     printf("%s\n", s );
1231   /* Licence string.  */
1232   if( (s=strusage (10)) )
1233     printf("%s\n", s );
1234   /* Copying conditions. */
1235   if ( (s=strusage(15)) )
1236     fputs (s, stdout);
1237   /* Thanks. */
1238   if ((s=strusage(18)))
1239     fputs (s, stdout);
1240   /* Additional program info. */
1241   for (i=30; i < 40; i++ )
1242     if ( (s=strusage (i)) )
1243       fputs (s, stdout);
1244   fflush (stdout);
1245 }
1246
1247
1248 void
1249 usage (int level)
1250 {
1251   const char *p;
1252
1253   if (!level)
1254     {
1255       fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14));
1256       fflush (stderr);
1257     }
1258   else if (level == 1)
1259     {
1260       p = strusage (40);
1261       fputs (p, stderr);
1262       if (*p && p[strlen(p)] != '\n')
1263         putc ('\n', stderr);
1264       exit (2);
1265     }
1266   else if (level == 2)
1267     {
1268       puts (strusage(41));
1269       exit (0);
1270     }
1271 }
1272
1273 /* Level
1274  *     0: Print copyright string to stderr
1275  *     1: Print a short usage hint to stderr and terminate
1276  *     2: Print a long usage hint to stdout and terminate
1277  *    10: Return license info string
1278  *    11: Return the name of the program
1279  *    12: Return optional name of package which includes this program.
1280  *    13: version  string
1281  *    14: copyright string
1282  *    15: Short copying conditions (with LFs)
1283  *    16: Long copying conditions (with LFs)
1284  *    17: Optional printable OS name
1285  *    18: Optional thanks list (with LFs)
1286  *    19: Bug report info
1287  *20..29: Additional lib version strings.
1288  *30..39: Additional program info (with LFs)
1289  *    40: short usage note (with LF)
1290  *    41: long usage note (with LF)
1291  */
1292 const char *
1293 strusage( int level )
1294 {
1295   const char *p = strusage_handler? strusage_handler(level) : NULL;
1296
1297   if ( p )
1298     return p;
1299
1300   switch ( level )
1301     {
1302     case 10: p = ("License GPLv3+: GNU GPL version 3 or later "
1303                   "<http://gnu.org/licenses/gpl.html>");
1304       break;
1305     case 11: p = "foo"; break;
1306     case 13: p = "0.0"; break;
1307     case 14: p = "Copyright (C) 2015 Free Software Foundation, Inc."; break;
1308     case 15: p =
1309 "This is free software: you are free to change and redistribute it.\n"
1310 "There is NO WARRANTY, to the extent permitted by law.\n";
1311       break;
1312     case 16: p =
1313 "This is free software; you can redistribute it and/or modify\n"
1314 "it under the terms of the GNU General Public License as published by\n"
1315 "the Free Software Foundation; either version 3 of the License, or\n"
1316 "(at your option) any later version.\n\n"
1317 "It is distributed in the hope that it will be useful,\n"
1318 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1319 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1320 "GNU General Public License for more details.\n\n"
1321 "You should have received a copy of the GNU General Public License\n"
1322 "along with this software.  If not, see <http://www.gnu.org/licenses/>.\n";
1323       break;
1324     case 40: /* short and long usage */
1325     case 41: p = ""; break;
1326     }
1327
1328   return p;
1329 }
1330
1331 void
1332 set_strusage ( const char *(*f)( int ) )
1333 {
1334   strusage_handler = f;
1335 }
1336
1337
1338 #ifdef TEST
1339 static struct {
1340     int verbose;
1341     int debug;
1342     char *outfile;
1343     char *crf;
1344     int myopt;
1345     int echo;
1346     int a_long_one;
1347 }opt;
1348
1349 int
1350 main(int argc, char **argv)
1351 {
1352   ARGPARSE_OPTS opts[] = {
1353     ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1354     ARGPARSE_s_n('e', "echo"   , ("Zeile ausgeben, damit wir sehen, "
1355                                   "was wir ein gegeben haben")),
1356     ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1357     ARGPARSE_s_s('o', "output", 0 ),
1358     ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1359     /* Note that on a non-utf8 terminal the ß might garble the output. */
1360     ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1361     ARGPARSE_o_i('m', "my-option", 0),
1362     ARGPARSE_s_n(500, "a-long-option", 0 ),
1363     ARGPARSE_end
1364   };
1365   ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
1366     int i;
1367
1368     while( arg_parse ( &pargs, opts) ) {
1369         switch( pargs.r_opt ) {
1370           case -1 : printf( "arg=`%s'\n", pargs.r.ret_str); break;
1371           case 'v': opt.verbose++; break;
1372           case 'e': opt.echo++; break;
1373           case 'd': opt.debug++; break;
1374           case 'o': opt.outfile = pargs.r.ret_str; break;
1375           case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1376           case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1377           case 500: opt.a_long_one++;  break;
1378           default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1379         }
1380     }
1381     for(i=0; i < argc; i++ )
1382         printf("%3d -> (%s)\n", i, argv[i] );
1383     puts("Options:");
1384     if( opt.verbose )
1385         printf("  verbose=%d\n", opt.verbose );
1386     if( opt.debug )
1387         printf("  debug=%d\n", opt.debug );
1388     if( opt.outfile )
1389         printf("  outfile=`%s'\n", opt.outfile );
1390     if( opt.crf )
1391         printf("  crffile=`%s'\n", opt.crf );
1392     if( opt.myopt )
1393         printf("  myopt=%d\n", opt.myopt );
1394     if( opt.a_long_one )
1395         printf("  a-long-one=%d\n", opt.a_long_one );
1396     if( opt.echo       )
1397         printf("  echo=%d\n", opt.echo );
1398     return 0;
1399 }
1400 #endif
1401
1402 /**** bottom of file ****/