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