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