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