Avoid catch-22 with README.main not being distributed but having the
[gnupg.git] / jnlib / argparse.c
blob8b2a9f412c5e6c979a08f30b25c04b358f028666
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.
5 * This file is part of JNLIB.
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.
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.
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/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
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"
41 /*********************************
42 * @Summary arg_parse
43 * #include <wk/lib.h>
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;
66 * typedef struct {
67 * int short_opt;
68 * const char *long_opt;
69 * unsigned flags;
70 * } ARGPARSE_OPTS;
72 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
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
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 }
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 --
134 * if( argc > 1 )
135 * log_fatal( "Too many args");
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 */
146 static const char *(*strusage_handler)( int ) = NULL;
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);
153 static void
154 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
156 if( !(arg->flags & (1<<15)) )
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");
172 if (arg->err)
174 /* Last option was erroneous. */
175 const char *s;
177 if (filename)
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);
197 else
199 s = arg->internal.last? arg->internal.last:"[??]";
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);
217 if ( arg->err != 1 )
218 exit (2);
219 arg->err = 0;
222 /* Zero out the return value union. */
223 arg->r.ret_str = NULL;
224 arg->r.ret_long = 0;
228 static void
229 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
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
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
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.
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.
265 optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
266 ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
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;
275 if (!fp) /* Divert to to arg_parse() in this case. */
276 return arg_parse (arg, opts);
278 initialize (arg, filename, lineno);
280 /* Find the next keyword. */
281 state = i = 0;
282 for (;;)
284 c = getc (fp);
285 if (c == '\n' || c== EOF )
287 if ( c != EOF )
288 ++*lineno;
289 if (state == -1)
290 break;
291 else if (state == 2)
293 keyword[i] = 0;
294 for (i=0; opts[i].short_opt; i++ )
296 if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
297 break;
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;
312 break;
314 else if (state == 3)
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;
326 break;
328 else if (state == 4)
330 /* Has an argument. */
331 if (in_alias)
333 if (!buffer)
334 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
335 else
337 char *p;
339 buffer[i] = 0;
340 p = strpbrk (buffer, " \t");
341 if (p)
343 *p++ = 0;
344 trim_spaces (p);
346 if (!p || !*p)
348 jnlib_free (buffer);
349 arg->r_opt = ARGPARSE_INVALID_ALIAS;
351 else
353 store_alias (arg, buffer, p);
357 else if (!(opts[idx].flags & 7))
358 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
359 else
361 char *p;
363 if (!buffer)
365 keyword[i] = 0;
366 buffer = jnlib_strdup (keyword);
367 if (!buffer)
368 arg->r_opt = ARGPARSE_OUT_OF_CORE;
370 else
371 buffer[i] = 0;
373 if (buffer)
375 trim_spaces (buffer);
376 p = buffer;
377 if (*p == '"')
379 /* Remove quotes. */
380 p++;
381 if (*p && p[strlen(p)-1] == '\"' )
382 p[strlen(p)-1] = 0;
384 if (!set_opt_arg (arg, opts[idx].flags, p))
385 jnlib_free(buffer);
388 break;
390 else if (c == EOF)
392 if (ferror (fp))
393 arg->r_opt = ARGPARSE_READ_ERROR;
394 else
395 arg->r_opt = 0; /* EOF. */
396 break;
398 state = 0;
399 i = 0;
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))
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)
420 if (!strcmp (keyword, "alias"))
422 in_alias = 1;
423 state = 3;
425 else
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. */
433 else
434 state = 3;
436 else if (state == 3)
438 /* Skip leading spaces of the argument. */
439 if (!isascii (c) || !isspace(c))
441 i = 0;
442 keyword[i++] = c;
443 state = 4;
446 else if (state == 4)
448 /* Collect the argument. */
449 if (buffer)
451 if (i < buflen-1)
452 buffer[i++] = c;
453 else
455 char *tmp;
456 size_t tmplen = buflen + 50;
458 tmp = jnlib_realloc (buffer, tmplen);
459 if (tmp)
461 buflen = tmplen;
462 buffer = tmp;
463 buffer[i++] = c;
465 else
467 jnlib_free (buffer);
468 arg->r_opt = ARGPARSE_OUT_OF_CORE;
469 break;
473 else if (i < DIM(keyword)-1)
474 keyword[i++] = c;
475 else
477 size_t tmplen = DIM(keyword) + 50;
478 buffer = jnlib_malloc (tmplen);
479 if (buffer)
481 buflen = tmplen;
482 memcpy(buffer, keyword, i);
483 buffer[i++] = c;
485 else
487 arg->r_opt = ARGPARSE_OUT_OF_CORE;
488 break;
492 else if (i >= DIM(keyword)-1)
494 arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
495 state = -1; /* Skip rest of line and leave. */
497 else
499 keyword[i++] = c;
500 state = 2;
504 return arg->r_opt;
509 static int
510 find_long_option( ARGPARSE_ARGS *arg,
511 ARGPARSE_OPTS *opts, const char *keyword )
513 int i;
514 size_t n;
516 (void)arg;
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
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 */
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 */
551 return i;
554 return -1;
558 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
560 int idx;
561 int argc;
562 char **argv;
563 char *s, *s2;
564 int i;
566 initialize( arg, NULL, NULL );
567 argc = *arg->argc;
568 argv = *arg->argv;
569 idx = arg->internal.idx;
571 if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
573 /* Skip the first argument. */
574 argc--; argv++; idx++;
577 next_one:
578 if (!argc)
580 /* No more args. */
581 arg->r_opt = 0;
582 goto leave; /* Ready. */
585 s = *argv;
586 arg->internal.last = s;
588 if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
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 */
595 else if( arg->internal.stopped )
597 arg->r_opt = 0;
598 goto leave; /* Ready. */
600 else if ( *s == '-' && s[1] == '-' )
602 /* Long option. */
603 char *argpos;
605 arg->internal.inarg = 0;
606 if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
608 /* Stop option processing. */
609 arg->internal.stopped = 1;
610 argc--; argv++; idx++;
611 goto next_one;
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 = '=';
621 if ( i < 0 && !strcmp ( "help", s+2) )
622 show_help (opts, arg->flags);
623 else if ( i < 0 && !strcmp ( "version", s+2) )
625 if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
627 show_version ();
628 exit(0);
631 else if ( i < 0 && !strcmp( "warranty", s+2))
633 puts ( strusage (16) );
634 exit (0);
636 else if ( i < 0 && !strcmp( "dump-options", s+2) )
638 for (i=0; opts[i].short_opt; i++ )
640 if ( opts[i].long_opt )
641 printf ("--%s\n", opts[i].long_opt);
643 fputs ("--dump-options\n--help\n--version\n--warranty\n", stdout);
644 exit (0);
647 if ( i == -2 )
648 arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
649 else if ( i == -1 )
651 arg->r_opt = ARGPARSE_INVALID_OPTION;
652 arg->r.ret_str = s+2;
654 else
655 arg->r_opt = opts[i].short_opt;
656 if ( i < 0 )
658 else if ( (opts[i].flags & 0x07) )
660 if ( argpos )
662 s2 = argpos+1;
663 if ( !*s2 )
664 s2 = NULL;
666 else
667 s2 = argv[1];
668 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
670 arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */
672 else if ( !s2 )
674 arg->r_opt = ARGPARSE_MISSING_ARG;
676 else if ( !argpos && *s2 == '-'
677 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
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;
684 else
686 set_opt_arg (arg, opts[i].flags, s2);
687 if ( !argpos )
689 argc--; argv++; idx++; /* Skip one. */
693 else
695 /* Does not take an argument. */
696 if ( argpos )
697 arg->r_type = ARGPARSE_UNEXPECTED_ARG;
698 else
699 arg->r_type = 0;
701 argc--; argv++; idx++; /* Set to next one. */
703 else if ( (*s == '-' && s[1]) || arg->internal.inarg )
705 /* Short option. */
706 int dash_kludge = 0;
708 i = 0;
709 if ( !arg->internal.inarg )
711 arg->internal.inarg++;
712 if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
714 for (i=0; opts[i].short_opt; i++ )
715 if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
717 dash_kludge = 1;
718 break;
722 s += arg->internal.inarg;
724 if (!dash_kludge )
726 for (i=0; opts[i].short_opt; i++ )
727 if ( opts[i].short_opt == *s )
728 break;
731 if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
732 show_help (opts, arg->flags);
734 arg->r_opt = opts[i].short_opt;
735 if (!opts[i].short_opt )
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;
742 else if ( (opts[i].flags & 7) )
744 if ( s[1] && !dash_kludge )
746 s2 = s+1;
747 set_opt_arg (arg, opts[i].flags, s2);
749 else
751 s2 = argv[1];
752 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
754 arg->r_type = ARGPARSE_TYPE_NONE;
756 else if ( !s2 )
758 arg->r_opt = ARGPARSE_MISSING_ARG;
760 else if ( *s2 == '-' && s2[1]
761 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
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;
768 else
770 set_opt_arg (arg, opts[i].flags, s2);
771 argc--; argv++; idx++; /* Skip one. */
774 s = "x"; /* This is so that !s[1] yields false. */
776 else
778 /* Does not take an argument. */
779 arg->r_type = ARGPARSE_TYPE_NONE;
780 arg->internal.inarg++; /* Point to the next arg. */
782 if ( !s[1] || dash_kludge )
784 /* No more concatenated short options. */
785 arg->internal.inarg = 0;
786 argc--; argv++; idx++;
789 else if ( arg->flags & ARGPARSE_FLAG_MIXED )
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. */
796 else
798 arg->internal.stopped = 1; /* Stop option processing. */
799 goto next_one;
802 leave:
803 *arg->argc = argc;
804 *arg->argv = argv;
805 arg->internal.idx = idx;
806 return arg->r_opt;
811 static int
812 set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s)
814 int base = (flags & 16)? 0 : 10;
816 switch ( (arg->r_type = (flags & 7)) )
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;
835 static size_t
836 long_opt_strlen( ARGPARSE_OPTS *o )
838 size_t n = strlen (o->long_opt);
840 if ( o->description && *o->description == '|' )
842 const char *s;
843 #ifdef JNLIB_NEED_UTF8CONV
844 int is_utf8 = is_native_utf8 ();
845 #endif
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++;
859 return n;
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.
874 static void
875 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
877 const char *s;
879 show_version ();
880 putchar ('\n');
881 s = strusage(41);
882 puts (s);
883 if ( opts[0].description )
885 /* Auto format the option description. */
886 int i,j, indent;
888 /* Get max. length of long options. */
889 for (i=indent=0; opts[i].short_opt; i++ )
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;
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++ )
903 s = _( opts[i].description );
904 if ( s && *s== '@' && !s[1] ) /* Hide this line. */
905 continue;
906 if ( s && *s == '@' ) /* Unindented comment only line. */
908 for (s++; *s; s++ )
910 if ( *s == '\n' )
912 if( s[1] )
913 putchar('\n');
915 else
916 putchar(*s);
918 putchar('\n');
919 continue;
922 j = 3;
923 if ( opts[i].short_opt < 256 )
925 printf (" -%c", opts[i].short_opt);
926 if ( !opts[i].long_opt )
928 if (s && *s == '|' )
930 putchar (' '); j++;
931 for (s++ ; *s && *s != '|'; s++, j++ )
932 putchar (*s);
933 if ( *s )
934 s++;
938 else
939 fputs(" ", stdout);
940 if ( opts[i].long_opt )
942 j += printf ("%c --%s", opts[i].short_opt < 256?',':' ',
943 opts[i].long_opt );
944 if (s && *s == '|' )
946 if ( *++s != '=' )
948 putchar(' ');
949 j++;
951 for ( ; *s && *s != '|'; s++, j++ )
952 putchar(*s);
953 if ( *s )
954 s++;
956 fputs (" ", stdout);
957 j += 3;
959 for (;j < indent; j++ )
960 putchar(' ');
961 if ( s )
963 if ( *s && j > indent )
965 putchar('\n');
966 for (j=0;j < indent; j++ )
967 putchar (' ');
969 for (; *s; s++ )
971 if ( *s == '\n' )
973 if ( s[1] )
975 putchar ('\n');
976 for (j=0; j < indent; j++ )
977 putchar (' ');
980 else
981 putchar (*s);
984 putchar ('\n');
986 if ( (flags & ARGPARSE_FLAG_ONEDASH) )
987 puts ("\n(A single dash may be used instead of the double ones)");
989 if ( (s=strusage(19)) )
991 /* bug reports to ... */
992 char *s2;
994 putchar('\n');
995 s2 = strstr (s, "@EMAIL@");
996 if (s2)
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);
1009 else
1010 fputs(s, stdout);
1012 fflush(stdout);
1013 exit(0);
1016 static void
1017 show_version ()
1019 const char *s;
1020 int i;
1022 /* Version line. */
1023 fputs (strusage (11), stdout);
1024 if ((s=strusage (12)))
1025 printf (" (%s)", s );
1026 printf (" %s\n", strusage (13) );
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);
1051 void
1052 usage (int level)
1054 const char *p;
1056 if (!level)
1058 fprintf(stderr,"%s %s; %s\n", strusage(11), strusage(13), strusage (14));
1059 fflush (stderr);
1061 else if (level == 1)
1063 p = strusage (40);
1064 fputs (p, stderr);
1065 if (*p && p[strlen(p)] != '\n')
1066 putc ('\n', stderr);
1067 exit (2);
1069 else if (level == 2)
1071 puts (strusage(41));
1072 exit (0);
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)
1095 const char *
1096 strusage( int level )
1098 const char *p = strusage_handler? strusage_handler(level) : NULL;
1100 if ( p )
1101 return p;
1103 switch ( level )
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;
1131 return p;
1134 void
1135 set_strusage ( const char *(*f)( int ) )
1137 strusage_handler = f;
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;
1153 main(int argc, char **argv)
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
1168 ARGPARSE_ARGS pargs = { &argc, &argv, 2|4|32 };
1169 int i;
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;
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;
1203 #endif
1205 /**** bottom of file ****/