1 /* [argparse.c wk 17.06.97] Argument Parser for option handling
2 * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc.
3 * Copyright (C) 1997-2001, 2006-2008, 2013-2017 Werner Koch
5 * This file is part of GnuPG.
7 * GnuPG is free software; you can redistribute and/or modify this
8 * part of GnuPG under the terms of either
10 * - the GNU Lesser General Public License as published by the Free
11 * Software Foundation; either version 3 of the License, or (at
12 * your option) any later version.
16 * - the GNU General Public License as published by the Free
17 * Software Foundation; either version 2 of the License, or (at
18 * your option) any later version.
20 * or both in parallel, as here.
22 * GnuPG is distributed in the hope that it will be useful, but
23 * WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * General Public License for more details.
27 * You should have received a copies of the GNU General Public License
28 * and the GNU Lesser General Public License along with this program;
29 * if not, see <https://gnu.org/licenses/>.
32 /* This file may be used as part of GnuPG or standalone. A GnuPG
33 build is detected by the presence of the macro GNUPG_MAJOR_VERSION.
34 Some feature are only availalbe in the GnuPG build mode.
49 #ifdef GNUPG_MAJOR_VERSION
51 # include "common-defs.h"
53 # include "mischelp.h"
54 # include "stringhelp.h"
56 # include "utf8conv.h"
57 #endif /*GNUPG_MAJOR_VERSION*/
61 /* GnuPG uses GPLv3+ but a standalone version of this defaults to
62 GPLv2+ because that is the license of this file. Change this if
63 you include it in a program which uses GPLv3. If you don't want to
64 set a copyright string for your usage() you may also hardcode it
66 #ifndef GNUPG_MAJOR_VERSION
68 # define ARGPARSE_GPL_VERSION 2
69 # define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME"
71 #else /* Used by GnuPG */
73 # define ARGPARSE_GPL_VERSION 3
74 # define ARGPARSE_CRIGHT_STR "Copyright (C) 2019 Free Software Foundation, Inc."
76 #endif /*GNUPG_MAJOR_VERSION*/
78 /* Replacements for standalone builds. */
79 #ifndef GNUPG_MAJOR_VERSION
84 # define DIM(v) (sizeof(v)/sizeof((v)[0]))
86 # define xtrymalloc(a) malloc ((a))
87 # define xtryrealloc(a,b) realloc ((a), (b))
88 # define xtrystrdup(a) strdup ((a))
89 # define xfree(a) free ((a))
90 # define log_error my_log_error
91 # define log_bug my_log_bug
92 # define trim_spaces(a) my_trim_spaces ((a))
93 # define map_static_macro_string(a) (a)
94 #endif /*!GNUPG_MAJOR_VERSION*/
97 #define ARGPARSE_STR(v) #v
98 #define ARGPARSE_STR2(v) ARGPARSE_STR(v)
101 /* Replacements for standalone builds. */
102 #ifndef GNUPG_MAJOR_VERSION
104 my_log_error (const char *fmt, ...)
108 va_start (arg_ptr, fmt);
109 fprintf (stderr, "%s: ", strusage (11));
110 vfprintf (stderr, fmt, arg_ptr);
115 my_log_bug (const char *fmt, ...)
119 va_start (arg_ptr, fmt);
120 fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
121 vfprintf (stderr, fmt, arg_ptr);
126 /* Return true if the native charset is utf-8. */
128 is_native_utf8 (void)
134 my_trim_spaces (char *str)
136 char *string, *p, *mark;
139 /* Find first non space character. */
140 for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
142 /* Move characters. */
143 for ((mark = NULL); (*string = *p); string++, p++)
144 if (isspace (*(unsigned char*)p))
152 *mark = '\0' ; /* Remove trailing spaces. */
157 #endif /*!GNUPG_MAJOR_VERSION*/
161 /*********************************
163 * #include "argparse.h"
166 * char *argc; pointer to argc (value subject to change)
167 * char ***argv; pointer to argv (value subject to change)
168 * unsigned flags; Global flags (DO NOT CHANGE)
169 * int err; print error about last option
170 * 1 = warning, 2 = abort
171 * int r_opt; return option
172 * int r_type; type of return value (0 = no argument found)
183 * } internal; DO NOT CHANGE
188 * const char *long_opt;
192 * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts );
195 * This is my replacement for getopt(). See the example for a typical usage.
197 * Bit 0 : Do not remove options form argv
198 * Bit 1 : Do not stop at last option but return other args
199 * with r_opt set to -1.
200 * Bit 2 : Assume options and real args are mixed.
201 * Bit 3 : Do not use -- to stop option processing.
202 * Bit 4 : Do not skip the first arg.
203 * Bit 5 : allow usage of long option with only one dash
204 * Bit 6 : ignore --version
205 * all other bits must be set to zero, this value is modified by the
206 * function, so assume this is write only.
207 * Local flags (for each option):
208 * Bit 2-0 : 0 = does not take an argument
209 * 1 = takes int argument
210 * 2 = takes string argument
211 * 3 = takes long argument
212 * 4 = takes ulong argument
213 * Bit 3 : argument is optional (r_type will the be set to 0)
214 * Bit 4 : allow 0x etc. prefixed values.
215 * Bit 6 : Ignore this option
216 * Bit 7 : This is a command and not an option
217 * You stop the option processing by setting opts to NULL, the function will
220 * Returns the args.r_opt or 0 if ready
221 * r_opt may be -2/-7 to indicate an unknown option/command.
225 * You do not need to process the options 'h', '--help' or '--version'
226 * because this function includes standard help processing; but if you
227 * specify '-h', '--help' or '--version' you have to do it yourself.
228 * The option '--' stops argument processing; if bit 1 is set the function
229 * continues to return normal arguments.
230 * To process float args or unsigned args you must use a string args and do
231 * the conversion yourself.
234 * ARGPARSE_OPTS opts[] = {
235 * { 'v', "verbose", 0 },
236 * { 'd', "debug", 0 },
237 * { 'o', "output", 2 },
238 * { 'c', "cross-ref", 2|8 },
239 * { 'm', "my-option", 1|8 },
240 * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
241 * { 500, "have-no-short-option-for-this-long-option", 0 },
243 * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
245 * while( ArgParse( &pargs, &opts) ) {
246 * switch( pargs.r_opt ) {
247 * case 'v': opt.verbose++; break;
248 * case 'd': opt.debug++; break;
249 * case 'o': opt.outfile = pargs.r.ret_str; break;
250 * case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
251 * case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
252 * case 500: opt.a_long_one++; break
253 * default : pargs.err = 1; break; -- force warning output --
257 * log_fatal( "Too many args");
261 typedef struct alias_def_s *ALIAS_DEF;
264 char *name; /* malloced buffer with name, \0, value */
265 const char *value; /* ptr into name */
269 /* Object to store the names for the --ignore-invalid-option option.
270 This is a simple linked list. */
271 typedef struct iio_item_def_s *IIO_ITEM_DEF;
272 struct iio_item_def_s
275 char name[1]; /* String with the long option name. */
278 static const char *(*strusage_handler)( int ) = NULL;
279 static int (*custom_outfnc) (int, const char *);
281 static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s);
282 static void show_help(ARGPARSE_OPTS *opts, unsigned flags);
283 static void show_version(void);
284 static int writestrings (int is_error, const char *string, ...)
286 __attribute__ ((sentinel(0)))
292 argparse_register_outfnc (int (*fnc)(int, const char *))
298 /* Write STRING and all following const char * arguments either to
299 stdout or, if IS_ERROR is set, to stderr. The list of strings must
300 be terminated by a NULL. */
302 writestrings (int is_error, const char *string, ...)
311 va_start (arg_ptr, string);
315 custom_outfnc (is_error? 2:1, s);
317 fputs (s, is_error? stderr : stdout);
320 while ((s = va_arg (arg_ptr, const char *)));
328 flushstrings (int is_error)
331 custom_outfnc (is_error? 2:1, NULL);
333 fflush (is_error? stderr : stdout);
338 initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
340 if( !(arg->flags & (1<<15)) )
342 /* Initialize this instance. */
343 arg->internal.idx = 0;
344 arg->internal.last = NULL;
345 arg->internal.inarg = 0;
346 arg->internal.stopped = 0;
347 arg->internal.aliases = NULL;
348 arg->internal.cur_alias = NULL;
349 arg->internal.iio_list = NULL;
351 arg->flags |= 1<<15; /* Mark as initialized. */
352 if ( *arg->argc < 0 )
353 log_bug ("invalid argument for arg_parse\n");
359 /* Last option was erroneous. */
364 if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
365 s = _("argument not expected");
366 else if ( arg->r_opt == ARGPARSE_READ_ERROR )
368 else if ( arg->r_opt == ARGPARSE_KEYWORD_TOO_LONG )
369 s = _("keyword too long");
370 else if ( arg->r_opt == ARGPARSE_MISSING_ARG )
371 s = _("missing argument");
372 else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
373 s = _("invalid argument");
374 else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
375 s = _("invalid command");
376 else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
377 s = _("invalid alias definition");
378 else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
379 s = _("out of core");
381 s = _("invalid option");
382 log_error ("%s:%u: %s\n", filename, *lineno, s);
386 s = arg->internal.last? arg->internal.last:"[??]";
388 if ( arg->r_opt == ARGPARSE_MISSING_ARG )
389 log_error (_("missing argument for option \"%.50s\"\n"), s);
390 else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
391 log_error (_("invalid argument for option \"%.50s\"\n"), s);
392 else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
393 log_error (_("option \"%.50s\" does not expect an argument\n"), s);
394 else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND )
395 log_error (_("invalid command \"%.50s\"\n"), s);
396 else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
397 log_error (_("option \"%.50s\" is ambiguous\n"), s);
398 else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
399 log_error (_("command \"%.50s\" is ambiguous\n"),s );
400 else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
401 log_error ("%s\n", _("out of core\n"));
403 log_error (_("invalid option \"%.50s\"\n"), s);
405 if (arg->err != ARGPARSE_PRINT_WARNING)
410 /* Zero out the return value union. */
411 arg->r.ret_str = NULL;
417 store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
419 /* TODO: replace this dummy function with a rea one
420 * and fix the probelms IRIX has with (ALIAS_DEV)arg..
427 ALIAS_DEF a = xmalloc( sizeof *a );
430 a->next = (ALIAS_DEF)arg->internal.aliases;
431 (ALIAS_DEF)arg->internal.aliases = a;
436 /* Return true if KEYWORD is in the ignore-invalid-option list. */
438 ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword)
440 IIO_ITEM_DEF item = arg->internal.iio_list;
442 for (; item; item = item->next)
443 if (!strcmp (item->name, keyword))
449 /* Add the keywords up to the next LF to the list of to be ignored
450 options. After returning FP will either be at EOF or the next
451 character read wll be the first of a new line. The function
452 returns 0 on success or true on malloc failure. */
454 ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
461 enum { skipWS, collectNAME, skipNAME, addNAME} state = skipWS;
477 if (!isascii (c) || !isspace(c))
491 else if (namelen < DIM(name)-1)
507 if (!ignore_invalid_option_p (arg, name))
509 item = xtrymalloc (sizeof *item + namelen);
512 strcpy (item->name, name);
513 item->next = (IIO_ITEM_DEF)arg->internal.iio_list;
514 arg->internal.iio_list = item;
524 /* Clear the entire ignore-invalid-option list. */
526 ignore_invalid_option_clear (ARGPARSE_ARGS *arg)
528 IIO_ITEM_DEF item, tmpitem;
530 for (item = arg->internal.iio_list; item; item = tmpitem)
532 tmpitem = item->next;
535 arg->internal.iio_list = NULL;
541 * Get options from a file.
542 * Lines starting with '#' are comment lines.
543 * Syntax is simply a keyword and the argument.
544 * Valid keywords are all keywords from the long_opt list without
545 * the leading dashes. The special keywords "help", "warranty" and "version"
546 * are not valid here.
547 * The special keyword "alias" may be used to store alias definitions,
548 * which are later expanded like long options.
550 * ignore-invalid-option OPTIONNAMEs
551 * is recognized and updates a list of option which should be ignored if they
553 * Caller must free returned strings.
554 * If called with FP set to NULL command line args are parse instead.
556 * Q: Should we allow the syntax
558 * and accept for boolean options a value of 1/0, yes/no or true/false?
559 * Note: Abbreviation of options is here not allowed.
562 optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
563 ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
571 int unread_buf[3]; /* We use an int so that we can store EOF. */
572 int unread_buf_count = 0;
574 if (!fp) /* Divert to arg_parse() in this case. */
575 return arg_parse (arg, opts);
577 initialize (arg, filename, lineno);
579 /* If the LINENO is zero we assume that we are at the start of a
580 * file and we skip over a possible Byte Order Mark. */
583 unread_buf[0] = getc (fp);
584 unread_buf[1] = getc (fp);
585 unread_buf[2] = getc (fp);
586 if (unread_buf[0] != 0xef
587 || unread_buf[1] != 0xbb
588 || unread_buf[2] != 0xbf)
589 unread_buf_count = 3;
592 /* Find the next keyword. */
596 if (unread_buf_count)
597 c = unread_buf[3 - unread_buf_count--];
600 if (c == '\n' || c== EOF )
609 for (i=0; opts[i].short_opt; i++ )
611 if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
615 arg->r_opt = opts[idx].short_opt;
616 if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
621 else if (!opts[idx].short_opt )
623 if (!strcmp (keyword, "ignore-invalid-option"))
625 /* No argument - ignore this meta option. */
629 else if (ignore_invalid_option_p (arg, keyword))
631 /* This invalid option is in the iio list. */
635 arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
636 ? ARGPARSE_INVALID_COMMAND
637 : ARGPARSE_INVALID_OPTION);
639 else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
640 arg->r_type = 0; /* Does not take an arg. */
641 else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
642 arg->r_type = 0; /* Arg is optional. */
644 arg->r_opt = ARGPARSE_MISSING_ARG;
650 /* No argument found. */
652 arg->r_opt = ARGPARSE_MISSING_ARG;
653 else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
654 arg->r_type = 0; /* Does not take an arg. */
655 else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
656 arg->r_type = 0; /* No optional argument. */
658 arg->r_opt = ARGPARSE_MISSING_ARG;
664 /* Has an argument. */
668 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
674 p = strpbrk (buffer, " \t");
683 arg->r_opt = ARGPARSE_INVALID_ALIAS;
687 store_alias (arg, buffer, p);
691 else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
692 arg->r_opt = ARGPARSE_UNEXPECTED_ARG;
700 buffer = xtrystrdup (keyword);
702 arg->r_opt = ARGPARSE_OUT_OF_CORE;
709 trim_spaces (buffer);
715 if (*p && p[strlen(p)-1] == '\"' )
718 if (!set_opt_arg (arg, opts[idx].flags, p))
721 gpgrt_annotate_leaked_object (buffer);
728 ignore_invalid_option_clear (arg);
730 arg->r_opt = ARGPARSE_READ_ERROR;
732 arg->r_opt = 0; /* EOF. */
738 else if (state == -1)
740 else if (state == 0 && isascii (c) && isspace(c))
741 ; /* Skip leading white space. */
742 else if (state == 0 && c == '#' )
743 state = 1; /* Start of a comment. */
745 ; /* Skip comments. */
746 else if (state == 2 && isascii (c) && isspace(c))
750 for (i=0; opts[i].short_opt; i++ )
751 if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
754 arg->r_opt = opts[idx].short_opt;
755 if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
757 state = 1; /* Process like a comment. */
759 else if (!opts[idx].short_opt)
761 if (!strcmp (keyword, "alias"))
766 else if (!strcmp (keyword, "ignore-invalid-option"))
768 if (ignore_invalid_option_add (arg, fp))
770 arg->r_opt = ARGPARSE_OUT_OF_CORE;
776 else if (ignore_invalid_option_p (arg, keyword))
777 state = 1; /* Process like a comment. */
780 arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
781 ? ARGPARSE_INVALID_COMMAND
782 : ARGPARSE_INVALID_OPTION);
783 state = -1; /* Skip rest of line and leave. */
791 /* Skip leading spaces of the argument. */
792 if (!isascii (c) || !isspace(c))
801 /* Collect the argument. */
809 size_t tmplen = buflen + 50;
811 tmp = xtryrealloc (buffer, tmplen);
821 arg->r_opt = ARGPARSE_OUT_OF_CORE;
826 else if (i < DIM(keyword)-1)
830 size_t tmplen = DIM(keyword) + 50;
831 buffer = xtrymalloc (tmplen);
835 memcpy(buffer, keyword, i);
840 arg->r_opt = ARGPARSE_OUT_OF_CORE;
845 else if (i >= DIM(keyword)-1)
847 arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
848 state = -1; /* Skip rest of line and leave. */
863 find_long_option( ARGPARSE_ARGS *arg,
864 ARGPARSE_OPTS *opts, const char *keyword )
871 /* Would be better if we can do a binary search, but it is not
872 possible to reorder our option table because we would mess
873 up our help strings - What we can do is: Build a nice option
874 lookup table when this function is first invoked */
877 for(i=0; opts[i].short_opt; i++ )
878 if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
883 /* see whether it is an alias */
884 for( a = args->internal.aliases; a; a = a->next ) {
885 if( !strcmp( a->name, keyword) ) {
886 /* todo: must parse the alias here */
887 args->internal.cur_alias = a;
888 return -3; /* alias available */
893 /* not found, see whether it is an abbreviation */
894 /* aliases may not be abbreviated */
895 n = strlen( keyword );
896 for(i=0; opts[i].short_opt; i++ ) {
897 if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
899 for(j=i+1; opts[j].short_opt; j++ ) {
901 && !strncmp( opts[j].long_opt, keyword, n )
902 && !(opts[j].short_opt == opts[i].short_opt
903 && opts[j].flags == opts[i].flags ) )
904 return -2; /* abbreviation is ambiguous */
909 return -1; /* Not found. */
913 arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
921 /* Fill in missing standard options: help, version, warranty and
923 ARGPARSE_OPTS help_opt
924 = ARGPARSE_s_n (ARGPARSE_SHORTOPT_HELP, "help", "@");
925 ARGPARSE_OPTS version_opt
926 = ARGPARSE_s_n (ARGPARSE_SHORTOPT_VERSION, "version", "@");
927 ARGPARSE_OPTS warranty_opt
928 = ARGPARSE_s_n (ARGPARSE_SHORTOPT_WARRANTY, "warranty", "@");
929 ARGPARSE_OPTS dump_options_opt
930 = ARGPARSE_s_n(ARGPARSE_SHORTOPT_DUMP_OPTIONS, "dump-options", "@");
932 int seen_version = 0;
933 int seen_warranty = 0;
934 int seen_dump_options = 0;
937 while (opts[i].short_opt)
939 if (opts[i].long_opt)
941 if (!strcmp(opts[i].long_opt, help_opt.long_opt))
943 else if (!strcmp(opts[i].long_opt, version_opt.long_opt))
945 else if (!strcmp(opts[i].long_opt, warranty_opt.long_opt))
947 else if (!strcmp(opts[i].long_opt, dump_options_opt.long_opt))
948 seen_dump_options = 1;
953 opts[i++] = help_opt;
955 opts[i++] = version_opt;
957 opts[i++] = warranty_opt;
958 if (! seen_dump_options)
959 opts[i++] = dump_options_opt;
961 initialize( arg, NULL, NULL );
964 idx = arg->internal.idx;
966 if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
968 /* Skip the first argument. */
969 argc--; argv++; idx++;
977 goto leave; /* Ready. */
981 arg->internal.last = s;
983 if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL))
985 arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */
988 argc--; argv++; idx++; /* set to next one */
990 else if( arg->internal.stopped )
993 goto leave; /* Ready. */
995 else if ( *s == '-' && s[1] == '-' )
1000 arg->internal.inarg = 0;
1001 if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP))
1003 /* Stop option processing. */
1004 arg->internal.stopped = 1;
1005 arg->flags |= ARGPARSE_FLAG_STOP_SEEN;
1006 argc--; argv++; idx++;
1010 argpos = strchr( s+2, '=' );
1013 i = find_long_option ( arg, opts, s+2 );
1017 if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_HELP)
1018 show_help (opts, arg->flags);
1019 else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_VERSION)
1021 if (!(arg->flags & ARGPARSE_FLAG_NOVERSION))
1027 else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_WARRANTY)
1029 writestrings (0, strusage (16), "\n", NULL);
1032 else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTIONS)
1034 for (i=0; opts[i].short_opt; i++ )
1036 if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE))
1037 writestrings (0, "--", opts[i].long_opt, "\n", NULL);
1043 arg->r_opt = ARGPARSE_AMBIGUOUS_OPTION;
1046 arg->r_opt = ARGPARSE_INVALID_OPTION;
1047 arg->r.ret_str = s+2;
1050 arg->r_opt = opts[i].short_opt;
1053 else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1063 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1065 arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */
1069 arg->r_opt = ARGPARSE_MISSING_ARG;
1071 else if ( !argpos && *s2 == '-'
1072 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1074 /* The argument is optional and the next seems to be an
1075 option. We do not check this possible option but
1076 assume no argument */
1077 arg->r_type = ARGPARSE_TYPE_NONE;
1081 set_opt_arg (arg, opts[i].flags, s2);
1084 argc--; argv++; idx++; /* Skip one. */
1090 /* Does not take an argument. */
1092 arg->r_type = ARGPARSE_UNEXPECTED_ARG;
1096 argc--; argv++; idx++; /* Set to next one. */
1098 else if ( (*s == '-' && s[1]) || arg->internal.inarg )
1101 int dash_kludge = 0;
1104 if ( !arg->internal.inarg )
1106 arg->internal.inarg++;
1107 if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
1109 for (i=0; opts[i].short_opt; i++ )
1110 if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
1117 s += arg->internal.inarg;
1121 for (i=0; opts[i].short_opt; i++ )
1122 if ( opts[i].short_opt == *s )
1126 if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
1127 show_help (opts, arg->flags);
1129 arg->r_opt = opts[i].short_opt;
1130 if (!opts[i].short_opt )
1132 arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
1133 ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
1134 arg->internal.inarg++; /* Point to the next arg. */
1137 else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
1139 if ( s[1] && !dash_kludge )
1142 set_opt_arg (arg, opts[i].flags, s2);
1147 if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1149 arg->r_type = ARGPARSE_TYPE_NONE;
1153 arg->r_opt = ARGPARSE_MISSING_ARG;
1155 else if ( *s2 == '-' && s2[1]
1156 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
1158 /* The argument is optional and the next seems to
1159 be an option. We do not check this possible
1160 option but assume no argument. */
1161 arg->r_type = ARGPARSE_TYPE_NONE;
1165 set_opt_arg (arg, opts[i].flags, s2);
1166 argc--; argv++; idx++; /* Skip one. */
1169 s = "x"; /* This is so that !s[1] yields false. */
1173 /* Does not take an argument. */
1174 arg->r_type = ARGPARSE_TYPE_NONE;
1175 arg->internal.inarg++; /* Point to the next arg. */
1177 if ( !s[1] || dash_kludge )
1179 /* No more concatenated short options. */
1180 arg->internal.inarg = 0;
1181 argc--; argv++; idx++;
1184 else if ( arg->flags & ARGPARSE_FLAG_MIXED )
1186 arg->r_opt = ARGPARSE_IS_ARG;
1189 argc--; argv++; idx++; /* Set to next one. */
1193 arg->internal.stopped = 1; /* Stop option processing. */
1200 arg->internal.idx = idx;
1205 /* Returns: -1 on error, 0 for an integer type and 1 for a non integer
1208 set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
1210 int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
1213 switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
1215 case ARGPARSE_TYPE_LONG:
1216 case ARGPARSE_TYPE_INT:
1218 l = strtol (s, NULL, base);
1219 if ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)
1221 arg->r_opt = ARGPARSE_INVALID_ARG;
1224 if (arg->r_type == ARGPARSE_TYPE_LONG)
1225 arg->r.ret_long = l;
1226 else if ( (l < 0 && l < INT_MIN) || l > INT_MAX )
1228 arg->r_opt = ARGPARSE_INVALID_ARG;
1232 arg->r.ret_int = (int)l;
1235 case ARGPARSE_TYPE_ULONG:
1236 while (isascii (*s) && isspace(*s))
1240 arg->r.ret_ulong = 0;
1241 arg->r_opt = ARGPARSE_INVALID_ARG;
1245 arg->r.ret_ulong = strtoul (s, NULL, base);
1246 if (arg->r.ret_ulong == ULONG_MAX && errno == ERANGE)
1248 arg->r_opt = ARGPARSE_INVALID_ARG;
1253 case ARGPARSE_TYPE_STRING:
1262 long_opt_strlen( ARGPARSE_OPTS *o )
1264 size_t n = strlen (o->long_opt);
1266 if ( o->description && *o->description == '|' )
1269 int is_utf8 = is_native_utf8 ();
1274 /* For a (mostly) correct length calculation we exclude
1275 continuation bytes (10xxxxxx) if we are on a native utf8
1277 for (; *s && *s != '|'; s++ )
1278 if ( is_utf8 && (*s&0xc0) != 0x80 )
1286 * Print formatted help. The description string has some special
1288 * - A description string which is "@" suppresses help output for
1290 * - a description,ine which starts with a '@' and is followed by
1291 * any other characters is printed as is; this may be used for examples
1293 * - A description which starts with a '|' outputs the string between this
1294 * bar and the next one as arguments of the long option.
1297 show_help (ARGPARSE_OPTS *opts, unsigned int flags)
1303 writestrings (0, "\n", NULL);
1308 writestrings (1, s, NULL);
1309 if (*s && s[strlen(s)] != '\n')
1310 writestrings (1, "\n", NULL);
1313 writestrings (0, s, "\n", NULL);
1314 if ( opts[0].description )
1316 /* Auto format the option description. */
1319 /* Get max. length of long options. */
1320 for (i=indent=0; opts[i].short_opt; i++ )
1322 if ( opts[i].long_opt )
1323 if ( !opts[i].description || *opts[i].description != '@' )
1324 if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
1328 /* Example: " -v, --verbose Viele Sachen ausgeben" */
1330 if ( *opts[0].description != '@' )
1331 writestrings (0, "Options:", "\n", NULL);
1332 for (i=0; opts[i].short_opt; i++ )
1334 s = map_static_macro_string (_( opts[i].description ));
1335 if ( s && *s== '@' && !s[1] ) /* Hide this line. */
1337 if ( s && *s == '@' ) /* Unindented comment only line. */
1344 writestrings (0, "\n", NULL);
1350 writestrings (0, tmp, NULL);
1353 writestrings (0, "\n", NULL);
1358 if ( opts[i].short_opt < 256 )
1360 tmp[0] = opts[i].short_opt;
1362 writestrings (0, " -", tmp, NULL );
1363 if ( !opts[i].long_opt )
1365 if (s && *s == '|' )
1367 writestrings (0, " ", NULL); j++;
1368 for (s++ ; *s && *s != '|'; s++, j++ )
1372 writestrings (0, tmp, NULL);
1380 writestrings (0, " ", NULL);
1381 if ( opts[i].long_opt )
1383 tmp[0] = opts[i].short_opt < 256?',':' ';
1385 j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
1386 if (s && *s == '|' )
1390 writestrings (0, " ", NULL);
1393 for ( ; *s && *s != '|'; s++, j++ )
1397 writestrings (0, tmp, NULL);
1402 writestrings (0, " ", NULL);
1405 for (;j < indent; j++ )
1406 writestrings (0, " ", NULL);
1409 if ( *s && j > indent )
1411 writestrings (0, "\n", NULL);
1412 for (j=0;j < indent; j++ )
1413 writestrings (0, " ", NULL);
1421 writestrings (0, "\n", NULL);
1422 for (j=0; j < indent; j++ )
1423 writestrings (0, " ", NULL);
1430 writestrings (0, tmp, NULL);
1434 writestrings (0, "\n", NULL);
1436 if ( (flags & ARGPARSE_FLAG_ONEDASH) )
1437 writestrings (0, "\n(A single dash may be used "
1438 "instead of the double ones)\n", NULL);
1440 if ( (s=strusage(19)) )
1442 writestrings (0, "\n", NULL);
1443 writestrings (0, s, NULL);
1456 writestrings (0, strusage (11), NULL);
1457 if ((s=strusage (12)))
1458 writestrings (0, " (", s, ")", NULL);
1459 writestrings (0, " ", strusage (13), "\n", NULL);
1460 /* Additional version lines. */
1461 for (i=20; i < 30; i++)
1462 if ((s=strusage (i)))
1463 writestrings (0, s, "\n", NULL);
1464 /* Copyright string. */
1465 if ((s=strusage (14)))
1466 writestrings (0, s, "\n", NULL);
1467 /* Licence string. */
1468 if( (s=strusage (10)) )
1469 writestrings (0, s, "\n", NULL);
1470 /* Copying conditions. */
1471 if ( (s=strusage(15)) )
1472 writestrings (0, s, NULL);
1474 if ((s=strusage(18)))
1475 writestrings (0, s, NULL);
1476 /* Additional program info. */
1477 for (i=30; i < 40; i++ )
1478 if ( (s=strusage (i)) )
1479 writestrings (0, s, NULL);
1491 writestrings (1, strusage(11), " ", strusage(13), "; ",
1492 strusage (14), "\n", NULL);
1495 else if (level == 1)
1498 writestrings (1, p, NULL);
1499 if (*p && p[strlen(p)] != '\n')
1500 writestrings (1, "\n", NULL);
1503 else if (level == 2)
1509 writestrings (1, p, NULL);
1510 if (*p && p[strlen(p)] != '\n')
1511 writestrings (1, "\n", NULL);
1513 writestrings (0, strusage(41), "\n", NULL);
1519 * 0: Print copyright string to stderr
1520 * 1: Print a short usage hint to stderr and terminate
1521 * 2: Print a long usage hint to stdout and terminate
1522 * 10: Return license info string
1523 * 11: Return the name of the program
1524 * 12: Return optional name of package which includes this program.
1525 * 13: version string
1526 * 14: copyright string
1527 * 15: Short copying conditions (with LFs)
1528 * 16: Long copying conditions (with LFs)
1529 * 17: Optional printable OS name
1530 * 18: Optional thanks list (with LFs)
1531 * 19: Bug report info
1532 *20..29: Additional lib version strings.
1533 *30..39: Additional program info (with LFs)
1534 * 40: short usage note (with LF)
1535 * 41: long usage note (with LF)
1537 * First char is '1':
1538 * The short usage notes needs to be printed
1539 * before the long usage note.
1542 strusage( int level )
1544 const char *p = strusage_handler? strusage_handler(level) : NULL;
1547 return map_static_macro_string (p);
1553 #if ARGPARSE_GPL_VERSION == 3
1554 p = ("License GPLv3+: GNU GPL version 3 or later "
1555 "<https://gnu.org/licenses/gpl.html>");
1557 p = ("License GPLv2+: GNU GPL version 2 or later "
1558 "<https://gnu.org/licenses/>");
1561 case 11: p = "foo"; break;
1562 case 13: p = "0.0"; break;
1563 case 14: p = ARGPARSE_CRIGHT_STR; break;
1565 "This is free software: you are free to change and redistribute it.\n"
1566 "There is NO WARRANTY, to the extent permitted by law.\n";
1569 "This is free software; you can redistribute it and/or modify\n"
1570 "it under the terms of the GNU General Public License as published by\n"
1571 "the Free Software Foundation; either version "
1572 ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
1573 " of the License, or\n"
1574 "(at your option) any later version.\n\n"
1575 "It is distributed in the hope that it will be useful,\n"
1576 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1577 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1578 "GNU General Public License for more details.\n\n"
1579 "You should have received a copy of the GNU General Public License\n"
1580 "along with this software. If not, see <https://gnu.org/licenses/>.\n";
1582 case 40: /* short and long usage */
1583 case 41: p = ""; break;
1590 /* Set the usage handler. This function is basically a constructor. */
1592 set_strusage ( const char *(*f)( int ) )
1594 strusage_handler = f;
1610 main(int argc, char **argv)
1612 ARGPARSE_OPTS opts[] = {
1613 ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"),
1614 ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, "
1615 "was wir eingegeben haben")),
1616 ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"),
1617 ARGPARSE_s_s('o', "output", 0 ),
1618 ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ),
1619 /* Note that on a non-utf8 terminal the ß might garble the output. */
1620 ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"),
1621 ARGPARSE_o_i('m', "my-option", 0),
1622 ARGPARSE_s_n(500, "a-long-option", 0 ),
1625 ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL
1626 | ARGPARSE_FLAG_MIXED
1627 | ARGPARSE_FLAG_ONEDASH) };
1630 while (arg_parse (&pargs, opts))
1632 switch (pargs.r_opt)
1634 case ARGPARSE_IS_ARG :
1635 printf ("arg='%s'\n", pargs.r.ret_str);
1637 case 'v': opt.verbose++; break;
1638 case 'e': opt.echo++; break;
1639 case 'd': opt.debug++; break;
1640 case 'o': opt.outfile = pargs.r.ret_str; break;
1641 case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break;
1642 case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break;
1643 case 500: opt.a_long_one++; break;
1644 default : pargs.err = ARGPARSE_PRINT_WARNING; break;
1647 for (i=0; i < argc; i++ )
1648 printf ("%3d -> (%s)\n", i, argv[i] );
1651 printf (" verbose=%d\n", opt.verbose );
1653 printf (" debug=%d\n", opt.debug );
1655 printf (" outfile='%s'\n", opt.outfile );
1657 printf (" crffile='%s'\n", opt.crf );
1659 printf (" myopt=%d\n", opt.myopt );
1661 printf (" a-long-one=%d\n", opt.a_long_one );
1663 printf (" echo=%d\n", opt.echo );
1669 /**** bottom of file ****/