diff options
-rw-r--r-- | NEWS | 10 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/argparse.c | 667 | ||||
-rw-r--r-- | src/argparse.h | 207 | ||||
-rw-r--r-- | src/gpg-error.def.in | 6 | ||||
-rw-r--r-- | src/gpg-error.h.in | 181 | ||||
-rw-r--r-- | src/gpg-error.vers | 5 | ||||
-rw-r--r-- | src/gpgrt-int.h | 9 | ||||
-rw-r--r-- | src/visibility.c | 32 | ||||
-rw-r--r-- | src/visibility.h | 11 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/t-argparse.c | 125 |
13 files changed, 686 insertions, 574 deletions
@@ -2,6 +2,16 @@ Noteworthy changes in version 1.29 (unreleased) [C23/A23/R_] ----------------------------------------------- + * Interface changes relative to the 1.28 release: + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + gpgrt_argparse New API. + gpgrt_strusage New API. + gpgrt_set_strusage New API. + gpgrt_set_usage_outfnc New API. + gpgrt_set_fixed_string_mapper New API. + GPGRT_ENABLE_ARGPARSE_MACROS New macro. + + Noteworthy changes in version 1.28 (2018-03-13) [C23/A23/R0] ----------------------------------------------- diff --git a/configure.ac b/configure.ac index 1e648d1..12abb48 100644 --- a/configure.ac +++ b/configure.ac @@ -155,6 +155,7 @@ AH_BOTTOM([ #define GPG_ERR_ENABLE_ERRNO_MACROS 1 #define GPGRT_ENABLE_ES_MACROS 1 #define GPGRT_ENABLE_LOG_MACROS 1 +#define GPGRT_ENABLE_ARGPARSE_MACROS 1 ]) diff --git a/src/Makefile.am b/src/Makefile.am index 268c2ab..66c4414 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -190,7 +190,8 @@ libgpg_error_la_SOURCES = gettext.h $(arch_sources) \ sysutils.c \ syscall-clamp.c \ logging.c \ - b64dec.c + b64dec.c \ + argparse.c nodist_libgpg_error_la_SOURCES = gpg-error.h diff --git a/src/argparse.c b/src/argparse.c index 331998b..c5a942f 100644 --- a/src/argparse.c +++ b/src/argparse.c @@ -1,9 +1,9 @@ /* argparse.c - Argument Parser for option handling * Copyright (C) 1997-2001, 2006-2008, 2013-2017 Werner Koch * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. - * Copyright (C) 2015-2017 g10 Code GmbH + * Copyright (C) 2015-2018 g10 Code GmbH * - * This file is part of GnuPG. + * This file is part of Libgpg-error. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as @@ -17,12 +17,9 @@ * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. - * SPDX-License-Identifier: LGPL-2.1+ - */ - -/* This file may be used as part of GnuPG or standalone. A GnuPG - build is detected by the presence of the macro GNUPG_MAJOR_VERSION. - Some feature are only availalbe in the GnuPG build mode. + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This file was originally a part of GnuPG. */ #ifdef HAVE_CONFIG_H @@ -37,92 +34,86 @@ #include <limits.h> #include <errno.h> -#ifdef GNUPG_MAJOR_VERSION -# include "util.h" -# include "common-defs.h" -# include "i18n.h" -# include "mischelp.h" -# include "stringhelp.h" -# include "logging.h" -# include "utf8conv.h" -#endif /*GNUPG_MAJOR_VERSION*/ - -#include "argparse.h" - -/* GnuPG uses GPLv3+ but a standalone version of this defaults to - GPLv2+ because that is the license of this file. Change this if - you include it in a program which uses GPLv3. If you don't want to - set a copyright string for your usage() you may also hardcode it - here. */ -#ifndef GNUPG_MAJOR_VERSION - -# define ARGPARSE_GPL_VERSION 2 -# define ARGPARSE_CRIGHT_STR "Copyright (C) YEAR NAME" - -#else /* Used by GnuPG */ - -# define ARGPARSE_GPL_VERSION 3 -# define ARGPARSE_CRIGHT_STR "Copyright (C) 2018 Free Software Foundation, Inc." - -#endif /*GNUPG_MAJOR_VERSION*/ - -/* Replacements for standalone builds. */ -#ifndef GNUPG_MAJOR_VERSION -# ifndef _ -# define _(a) (a) -# endif -# ifndef DIM -# define DIM(v) (sizeof(v)/sizeof((v)[0])) -# endif -# define xtrymalloc(a) malloc ((a)) -# define xtryrealloc(a,b) realloc ((a), (b)) -# define xtrystrdup(a) strdup ((a)) -# define xfree(a) free ((a)) -# define log_error my_log_error -# define log_bug my_log_bug -# define trim_spaces(a) my_trim_spaces ((a)) -# define map_static_macro_string(a) (a) -#endif /*!GNUPG_MAJOR_VERSION*/ - - -#define ARGPARSE_STR(v) #v -#define ARGPARSE_STR2(v) ARGPARSE_STR(v) - - -/* Replacements for standalone builds. */ -#ifndef GNUPG_MAJOR_VERSION -static void -my_log_error (const char *fmt, ...) +#include "gpgrt-int.h" + +/* Special short options which are auto-inserterd. */ +#define ARGPARSE_SHORTOPT_HELP 32768 +#define ARGPARSE_SHORTOPT_VERSION 32769 +#define ARGPARSE_SHORTOPT_WARRANTY 32770 +#define ARGPARSE_SHORTOPT_DUMP_OPTIONS 32771 + +/* A mask for the types. */ +#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values. */ + +/* Internal object of the public gpgrt_argparse_t object. */ +struct _gpgrt_argparse_internal_s { - va_list arg_ptr ; + int idx; + int inarg; + int stopped; + const char *last; + void *aliases; + const void *cur_alias; + void *iio_list; +}; - va_start (arg_ptr, fmt); - fprintf (stderr, "%s: ", strusage (11)); - vfprintf (stderr, fmt, arg_ptr); - va_end (arg_ptr); -} -static void -my_log_bug (const char *fmt, ...) +typedef struct alias_def_s *ALIAS_DEF; +struct alias_def_s { + ALIAS_DEF next; + char *name; /* malloced buffer with name, \0, value */ + const char *value; /* ptr into name */ +}; + + +/* Object to store the names for the --ignore-invalid-option option. + This is a simple linked list. */ +typedef struct iio_item_def_s *IIO_ITEM_DEF; +struct iio_item_def_s { - va_list arg_ptr ; + IIO_ITEM_DEF next; + char name[1]; /* String with the long option name. */ +}; + + +/* The almost always needed user handler for strusage. */ +static const char *(*strusage_handler)( int ) = NULL; +/* Optional handler to write strings. See _gpgrt_set_usage_outfnc. */ +static int (*custom_outfnc) (int, const char *); +/* Optional handler to map strings. See _gpgrt_set_fixed_string_mapper. */ +static const char *(*fixed_string_mapper)(const char*); + +static int set_opt_arg(gpgrt_argparse_t *arg, unsigned flags, char *s); +static void show_help(gpgrt_opt_t *opts, unsigned flags); +static void show_version(void); +static int writestrings (int is_error, const char *string, + ...) GPGRT_ATTR_SENTINEL(0); +static int arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts); + - va_start (arg_ptr, fmt); - fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11)); - vfprintf (stderr, fmt, arg_ptr); - va_end (arg_ptr); - abort (); -} + + /* Return true if the native charset is utf-8. */ static int is_native_utf8 (void) { - return 1; + static char result; + + if (!result) + { + const char *p = _gpgrt_strusage (8); + if (!p || !*p || !strcmp (p, "utf-8")) + result = 1; + result |= 128; + } + + return (result & 1); } + static char * -my_trim_spaces (char *str) +trim_spaces (char *str) { char *string, *p, *mark; @@ -145,7 +136,12 @@ my_trim_spaces (char *str) return str ; } -#endif /*!GNUPG_MAJOR_VERSION*/ + +static const char * +map_fixed_string (const char *string) +{ + return fixed_string_mapper? fixed_string_mapper (string) : string; +} @@ -153,66 +149,6 @@ my_trim_spaces (char *str) * @Summary arg_parse * #include "argparse.h" * - * typedef struct { - * char *argc; pointer to argc (value subject to change) - * char ***argv; pointer to argv (value subject to change) - * unsigned flags; Global flags (DO NOT CHANGE) - * int err; print error about last option - * 1 = warning, 2 = abort - * int r_opt; return option - * int r_type; type of return value (0 = no argument found) - * union { - * int ret_int; - * long ret_long - * ulong ret_ulong; - * char *ret_str; - * } r; Return values - * struct { - * int idx; - * const char *last; - * void *aliases; - * } internal; DO NOT CHANGE - * } ARGPARSE_ARGS; - * - * typedef struct { - * int short_opt; - * const char *long_opt; - * unsigned flags; - * } ARGPARSE_OPTS; - * - * int arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts ); - * - * @Description - * This is my replacement for getopt(). See the example for a typical usage. - * Global flags are: - * Bit 0 : Do not remove options form argv - * Bit 1 : Do not stop at last option but return other args - * with r_opt set to -1. - * Bit 2 : Assume options and real args are mixed. - * Bit 3 : Do not use -- to stop option processing. - * Bit 4 : Do not skip the first arg. - * Bit 5 : allow usage of long option with only one dash - * Bit 6 : ignore --version - * all other bits must be set to zero, this value is modified by the - * function, so assume this is write only. - * Local flags (for each option): - * Bit 2-0 : 0 = does not take an argument - * 1 = takes int argument - * 2 = takes string argument - * 3 = takes long argument - * 4 = takes ulong argument - * Bit 3 : argument is optional (r_type will the be set to 0) - * Bit 4 : allow 0x etc. prefixed values. - * Bit 6 : Ignore this option - * Bit 7 : This is a command and not an option - * You stop the option processing by setting opts to NULL, the function will - * then return 0. - * @Return Value - * Returns the args.r_opt or 0 if ready - * r_opt may be -2/-7 to indicate an unknown option/command. - * @See Also - * ArgExpand - * @Notes * You do not need to process the options 'h', '--help' or '--version' * because this function includes standard help processing; but if you * specify '-h', '--help' or '--version' you have to do it yourself. @@ -222,7 +158,7 @@ my_trim_spaces (char *str) * the conversion yourself. * @Example * - * ARGPARSE_OPTS opts[] = { + * gpgrt_opt_t opts[] = { * { 'v', "verbose", 0 }, * { 'd', "debug", 0 }, * { 'o', "output", 2 }, @@ -231,7 +167,7 @@ my_trim_spaces (char *str) * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE}, * { 500, "have-no-short-option-for-this-long-option", 0 }, * {0} }; - * ARGPARSE_ARGS pargs = { &argc, &argv, 0 } + * gpgrt_argparse_t pargs = { &argc, &argv, 0 } * * while( ArgParse( &pargs, &opts) ) { * switch( pargs.r_opt ) { @@ -249,42 +185,6 @@ my_trim_spaces (char *str) * */ -typedef struct alias_def_s *ALIAS_DEF; -struct alias_def_s { - ALIAS_DEF next; - char *name; /* malloced buffer with name, \0, value */ - const char *value; /* ptr into name */ -}; - - -/* Object to store the names for the --ignore-invalid-option option. - This is a simple linked list. */ -typedef struct iio_item_def_s *IIO_ITEM_DEF; -struct iio_item_def_s -{ - IIO_ITEM_DEF next; - char name[1]; /* String with the long option name. */ -}; - -static const char *(*strusage_handler)( int ) = NULL; -static int (*custom_outfnc) (int, const char *); - -static int set_opt_arg(ARGPARSE_ARGS *arg, unsigned flags, char *s); -static void show_help(ARGPARSE_OPTS *opts, unsigned flags); -static void show_version(void); -static int writestrings (int is_error, const char *string, ...) -#if __GNUC__ >= 4 - __attribute__ ((sentinel(0))) -#endif - ; - - -void -argparse_register_outfnc (int (*fnc)(int, const char *)) -{ - custom_outfnc = fnc; -} - /* Write STRING and all following const char * arguments either to stdout or, if IS_ERROR is set, to stderr. The list of strings must @@ -321,27 +221,68 @@ flushstrings (int is_error) if (custom_outfnc) custom_outfnc (is_error? 2:1, NULL); else - fflush (is_error? stderr : stdout); + _gpgrt_fflush (is_error? es_stderr : es_stdout); } static void -initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) +deinitialize (gpgrt_argparse_t *arg) { - if( !(arg->flags & (1<<15)) ) + xfree (arg->internal); + arg->internal = NULL; + + arg->lineno = 0; + arg->err = 0; +} + +/* Our own exit handler to clean up used memory. */ +static void +my_exit (gpgrt_argparse_t *arg, int code) +{ + deinitialize (arg); + exit (code); +} + + +static gpg_err_code_t +initialize (gpgrt_argparse_t *arg, estream_t fp) +{ + if (!arg->internal || (arg->flags & ARGPARSE_FLAG_RESET)) { + /* Allocate internal data. */ + if (!arg->internal) + { + arg->internal = xtrymalloc (sizeof *arg->internal); + if (!arg->internal) + return _gpg_err_code_from_syserror (); + } + /* Initialize this instance. */ - arg->internal.idx = 0; - arg->internal.last = NULL; - arg->internal.inarg = 0; - arg->internal.stopped = 0; - arg->internal.aliases = NULL; - arg->internal.cur_alias = NULL; - arg->internal.iio_list = NULL; + arg->internal->idx = 0; + arg->internal->last = NULL; + arg->internal->inarg = 0; + arg->internal->stopped = 0; + arg->internal->aliases = NULL; + arg->internal->cur_alias = NULL; + arg->internal->iio_list = NULL; + + /* Clear the error indicator. */ arg->err = 0; - arg->flags |= 1<<15; /* Mark as initialized. */ + + /* Usually an option file will be parsed from the start. + * However, we do not open the stream and thus we have no way to + * know the current lineno. Using this flag we can allow the + * user to provide a lineno which we don't reset. */ + if (fp || !(arg->flags & ARGPARSE_FLAG_NOLINENO)) + arg->lineno = 0; + + /* Need to clear the reset request. */ + arg->flags &= ~ARGPARSE_FLAG_RESET; + + /* Check initial args. */ if ( *arg->argc < 0 ) - log_bug ("invalid argument for arg_parse\n"); + _gpgrt_log_bug ("invalid argument passed to gpgrt_argparse\n"); + } @@ -350,7 +291,7 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) /* Last option was erroneous. */ const char *s; - if (filename) + if (fp) { if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) s = _("argument not expected"); @@ -370,42 +311,46 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno ) s = _("out of core"); else s = _("invalid option"); - log_error ("%s:%u: %s\n", filename, *lineno, s); + _gpgrt_log_error ("%s:%u: %s\n", + _gpgrt_fname_get (fp), arg->lineno, s); } else { - s = arg->internal.last? arg->internal.last:"[??]"; + s = arg->internal->last? arg->internal->last:"[??]"; if ( arg->r_opt == ARGPARSE_MISSING_ARG ) - log_error (_("missing argument for option \"%.50s\"\n"), s); + _gpgrt_log_error (_("missing argument for option \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_INVALID_ARG ) - log_error (_("invalid argument for option \"%.50s\"\n"), s); + _gpgrt_log_error (_("invalid argument for option \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG ) - log_error (_("option \"%.50s\" does not expect an argument\n"), s); + _gpgrt_log_error (_("option \"%.50s\" does not expect " + "an argument\n"), s); else if ( arg->r_opt == ARGPARSE_INVALID_COMMAND ) - log_error (_("invalid command \"%.50s\"\n"), s); + _gpgrt_log_error (_("invalid command \"%.50s\"\n"), s); else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION ) - log_error (_("option \"%.50s\" is ambiguous\n"), s); + _gpgrt_log_error (_("option \"%.50s\" is ambiguous\n"), s); else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND ) - log_error (_("command \"%.50s\" is ambiguous\n"),s ); + _gpgrt_log_error (_("command \"%.50s\" is ambiguous\n"),s ); else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE ) - log_error ("%s\n", _("out of core\n")); + _gpgrt_log_error ("%s\n", _("out of core\n")); else - log_error (_("invalid option \"%.50s\"\n"), s); + _gpgrt_log_error (_("invalid option \"%.50s\"\n"), s); } if (arg->err != ARGPARSE_PRINT_WARNING) - exit (2); + my_exit (arg, 2); arg->err = 0; } /* Zero out the return value union. */ arg->r.ret_str = NULL; arg->r.ret_long = 0; + + return 0; } static void -store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) +store_alias( gpgrt_argparse_t *arg, char *name, char *value ) { /* TODO: replace this dummy function with a rea one * and fix the probelms IRIX has with (ALIAS_DEV)arg.. @@ -418,17 +363,17 @@ store_alias( ARGPARSE_ARGS *arg, char *name, char *value ) ALIAS_DEF a = xmalloc( sizeof *a ); a->name = name; a->value = value; - a->next = (ALIAS_DEF)arg->internal.aliases; - (ALIAS_DEF)arg->internal.aliases = a; + a->next = (ALIAS_DEF)arg->internal->aliases; + (ALIAS_DEF)arg->internal->aliases = a; #endif } /* Return true if KEYWORD is in the ignore-invalid-option list. */ static int -ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) +ignore_invalid_option_p (gpgrt_argparse_t *arg, const char *keyword) { - IIO_ITEM_DEF item = arg->internal.iio_list; + IIO_ITEM_DEF item = arg->internal->iio_list; for (; item; item = item->next) if (!strcmp (item->name, keyword)) @@ -442,7 +387,7 @@ ignore_invalid_option_p (ARGPARSE_ARGS *arg, const char *keyword) character read wll be the first of a new line. The function returns 0 on success or true on malloc failure. */ static int -ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) +ignore_invalid_option_add (gpgrt_argparse_t *arg, estream_t fp) { IIO_ITEM_DEF item; int c; @@ -453,7 +398,7 @@ ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) while (!ready) { - c = getc (fp); + c = _gpgrt_fgetc (fp); if (c == '\n') ready = 1; else if (c == EOF) @@ -501,8 +446,8 @@ ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) if (!item) return 1; strcpy (item->name, name); - item->next = (IIO_ITEM_DEF)arg->internal.iio_list; - arg->internal.iio_list = item; + item->next = (IIO_ITEM_DEF)arg->internal->iio_list; + arg->internal->iio_list = item; } state = skipWS; goto again; @@ -514,16 +459,16 @@ ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp) /* Clear the entire ignore-invalid-option list. */ static void -ignore_invalid_option_clear (ARGPARSE_ARGS *arg) +ignore_invalid_option_clear (gpgrt_argparse_t *arg) { IIO_ITEM_DEF item, tmpitem; - for (item = arg->internal.iio_list; item; item = tmpitem) + for (item = arg->internal->iio_list; item; item = tmpitem) { tmpitem = item->next; xfree (item); } - arg->internal.iio_list = NULL; + arg->internal->iio_list = NULL; } @@ -550,8 +495,7 @@ ignore_invalid_option_clear (ARGPARSE_ARGS *arg) * Note: Abbreviation of options is here not allowed. */ int -optfile_parse (FILE *fp, const char *filename, unsigned *lineno, - ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) +_gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts) { int state, i, c; int idx=0; @@ -562,18 +506,26 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno, int unread_buf[3]; /* We use an int so that we can store EOF. */ int unread_buf_count = 0; + if (arg && !opts) + { + deinitialize (arg); + return 0; + } + if (!fp) /* Divert to arg_parse() in this case. */ return arg_parse (arg, opts); - initialize (arg, filename, lineno); + if (initialize (arg, fp)) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + /* If the LINENO is zero we assume that we are at the start of a * file and we skip over a possible Byte Order Mark. */ - if (!*lineno) + if (!arg->lineno) { - unread_buf[0] = getc (fp); - unread_buf[1] = getc (fp); - unread_buf[2] = getc (fp); + unread_buf[0] = _gpgrt_fgetc (fp); + unread_buf[1] = _gpgrt_fgetc (fp); + unread_buf[2] = _gpgrt_fgetc (fp); if (unread_buf[0] != 0xef || unread_buf[1] != 0xbb || unread_buf[2] != 0xbf) @@ -587,11 +539,11 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno, if (unread_buf_count) c = unread_buf[3 - unread_buf_count--]; else - c = getc (fp); + c = _gpgrt_fgetc (fp); if (c == '\n' || c== EOF ) { if ( c != EOF ) - ++*lineno; + arg->lineno++; if (state == -1) break; else if (state == 2) @@ -717,7 +669,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno, else if (c == EOF) { ignore_invalid_option_clear (arg); - if (ferror (fp)) + if (_gpgrt_ferror (fp)) arg->r_opt = ARGPARSE_READ_ERROR; else arg->r_opt = 0; /* EOF. */ @@ -762,7 +714,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno, break; } state = i = 0; - ++*lineno; + arg->lineno++; } else if (ignore_invalid_option_p (arg, keyword)) state = 1; /* Process like a comment. */ @@ -851,8 +803,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno, static int -find_long_option( ARGPARSE_ARGS *arg, - ARGPARSE_OPTS *opts, const char *keyword ) +find_long_option (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, const char *keyword) { int i; size_t n; @@ -872,10 +823,10 @@ find_long_option( ARGPARSE_ARGS *arg, { ALIAS_DEF a; /* see whether it is an alias */ - for( a = args->internal.aliases; a; a = a->next ) { + for( a = args->internal->aliases; a; a = a->next ) { if( !strcmp( a->name, keyword) ) { /* todo: must parse the alias here */ - args->internal.cur_alias = a; + args->internal->cur_alias = a; return -3; /* alias available */ } } @@ -900,8 +851,9 @@ find_long_option( ARGPARSE_ARGS *arg, return -1; /* Not found. */ } -int -arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) + +static int +arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts) { int idx; int argc; @@ -911,13 +863,13 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) /* Fill in missing standard options: help, version, warranty and * dump-options. */ - ARGPARSE_OPTS help_opt + gpgrt_opt_t help_opt = ARGPARSE_s_n (ARGPARSE_SHORTOPT_HELP, "help", "@"); - ARGPARSE_OPTS version_opt + gpgrt_opt_t version_opt = ARGPARSE_s_n (ARGPARSE_SHORTOPT_VERSION, "version", "@"); - ARGPARSE_OPTS warranty_opt + gpgrt_opt_t warranty_opt = ARGPARSE_s_n (ARGPARSE_SHORTOPT_WARRANTY, "warranty", "@"); - ARGPARSE_OPTS dump_options_opt + gpgrt_opt_t dump_options_opt = ARGPARSE_s_n(ARGPARSE_SHORTOPT_DUMP_OPTIONS, "dump-options", "@"); int seen_help = 0; int seen_version = 0; @@ -949,10 +901,12 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) if (! seen_dump_options) opts[i++] = dump_options_opt; - initialize( arg, NULL, NULL ); + if (initialize( arg, NULL)) + return (arg->r_opt = ARGPARSE_OUT_OF_CORE); + argc = *arg->argc; argv = *arg->argv; - idx = arg->internal.idx; + idx = arg->internal->idx; if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0)) { @@ -969,16 +923,16 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) } s = *argv; - arg->internal.last = s; + arg->internal->last = s; - if (arg->internal.stopped && (arg->flags & ARGPARSE_FLAG_ALL)) + if (arg->internal->stopped && (arg->flags & ARGPARSE_FLAG_ALL)) { arg->r_opt = ARGPARSE_IS_ARG; /* Not an option but an argument. */ arg->r_type = 2; arg->r.ret_str = s; argc--; argv++; idx++; /* set to next one */ } - else if( arg->internal.stopped ) + else if( arg->internal->stopped ) { arg->r_opt = 0; goto leave; /* Ready. */ @@ -988,11 +942,11 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) /* Long option. */ char *argpos; - arg->internal.inarg = 0; + arg->internal->inarg = 0; if (!s[2] && !(arg->flags & ARGPARSE_FLAG_NOSTOP)) { /* Stop option processing. */ - arg->internal.stopped = 1; + arg->internal->stopped = 1; arg->flags |= ARGPARSE_FLAG_STOP_SEEN; argc--; argv++; idx++; goto next_one; @@ -1012,13 +966,13 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) if (!(arg->flags & ARGPARSE_FLAG_NOVERSION)) { show_version (); - exit(0); + my_exit (arg, 0); } } else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_WARRANTY) { - writestrings (0, strusage (16), "\n", NULL); - exit (0); + writestrings (0, _gpgrt_strusage (16), "\n", NULL); + my_exit (arg, 0); } else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTIONS) { @@ -1027,7 +981,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) if (opts[i].long_opt && !(opts[i].flags & ARGPARSE_OPT_IGNORE)) writestrings (0, "--", opts[i].long_opt, "\n", NULL); } - exit (0); + my_exit (arg, 0); } if ( i == -2 ) @@ -1086,15 +1040,15 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) } argc--; argv++; idx++; /* Set to next one. */ } - else if ( (*s == '-' && s[1]) || arg->internal.inarg ) + else if ( (*s == '-' && s[1]) || arg->internal->inarg ) { /* Short option. */ int dash_kludge = 0; i = 0; - if ( !arg->internal.inarg ) + if ( !arg->internal->inarg ) { - arg->internal.inarg++; + arg->internal->inarg++; if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) { for (i=0; opts[i].short_opt; i++ ) @@ -1105,7 +1059,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) } } } - s += arg->internal.inarg; + s += arg->internal->inarg; if (!dash_kludge ) { @@ -1122,7 +1076,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) { arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)? ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION; - arg->internal.inarg++; /* Point to the next arg. */ + arg->internal->inarg++; /* Point to the next arg. */ arg->r.ret_str = s; } else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) @@ -1163,12 +1117,12 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) { /* Does not take an argument. */ arg->r_type = ARGPARSE_TYPE_NONE; - arg->internal.inarg++; /* Point to the next arg. */ + arg->internal->inarg++; /* Point to the next arg. */ } if ( !s[1] || dash_kludge ) { /* No more concatenated short options. */ - arg->internal.inarg = 0; + arg->internal->inarg = 0; argc--; argv++; idx++; } } @@ -1181,14 +1135,14 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) } else { - arg->internal.stopped = 1; /* Stop option processing. */ + arg->internal->stopped = 1; /* Stop option processing. */ goto next_one; } leave: *arg->argc = argc; *arg->argv = argv; - arg->internal.idx = idx; + arg->internal->idx = idx; return arg->r_opt; } @@ -1196,7 +1150,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts) /* Returns: -1 on error, 0 for an integer type and 1 for a non integer type argument. */ static int -set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s) +set_opt_arg (gpgrt_argparse_t *arg, unsigned flags, char *s) { int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10; long l; @@ -1250,7 +1204,7 @@ set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s) static size_t -long_opt_strlen( ARGPARSE_OPTS *o ) +long_opt_strlen( gpgrt_opt_t *o ) { size_t n = strlen (o->long_opt); @@ -1285,22 +1239,22 @@ long_opt_strlen( ARGPARSE_OPTS *o ) * bar and the next one as arguments of the long option. */ static void -show_help (ARGPARSE_OPTS *opts, unsigned int flags) +show_help (gpgrt_opt_t *opts, unsigned int flags) { const char *s; char tmp[2]; show_version (); writestrings (0, "\n", NULL); - s = strusage (42); + s = _gpgrt_strusage (42); if (s && *s == '1') { - s = strusage (40); + s = _gpgrt_strusage (40); writestrings (1, s, NULL); if (*s && s[strlen(s)] != '\n') writestrings (1, "\n", NULL); } - s = strusage(41); + s = _gpgrt_strusage(41); writestrings (0, s, "\n", NULL); if ( opts[0].description ) { @@ -1322,7 +1276,7 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags) writestrings (0, "Options:", "\n", NULL); for (i=0; opts[i].short_opt; i++ ) { - s = map_static_macro_string (_( opts[i].description )); + s = map_fixed_string (_( opts[i].description )); if ( s && *s== '@' && !s[1] ) /* Hide this line. */ continue; if ( s && *s == '@' ) /* Unindented comment only line. */ @@ -1428,15 +1382,16 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags) writestrings (0, "\n(A single dash may be used " "instead of the double ones)\n", NULL); } - if ( (s=strusage(19)) ) + if ( (s=_gpgrt_strusage(19)) ) { writestrings (0, "\n", NULL); writestrings (0, s, NULL); } flushstrings (0); - exit(0); + exit (0); } + static void show_version () { @@ -1444,48 +1399,48 @@ show_version () int i; /* Version line. */ - writestrings (0, strusage (11), NULL); - if ((s=strusage (12))) + writestrings (0, _gpgrt_strusage (11), NULL); + if ((s=_gpgrt_strusage (12))) writestrings (0, " (", s, ")", NULL); - writestrings (0, " ", strusage (13), "\n", NULL); + writestrings (0, " ", _gpgrt_strusage (13), "\n", NULL); /* Additional version lines. */ for (i=20; i < 30; i++) - if ((s=strusage (i))) + if ((s=_gpgrt_strusage (i))) writestrings (0, s, "\n", NULL); /* Copyright string. */ - if ((s=strusage (14))) + if ((s=_gpgrt_strusage (14))) writestrings (0, s, "\n", NULL); /* Licence string. */ - if( (s=strusage (10)) ) + if( (s=_gpgrt_strusage (10)) ) writestrings (0, s, "\n", NULL); /* Copying conditions. */ - if ( (s=strusage(15)) ) + if ( (s=_gpgrt_strusage(15)) ) writestrings (0, s, NULL); /* Thanks. */ - if ((s=strusage(18))) + if ((s=_gpgrt_strusage(18))) writestrings (0, s, NULL); /* Additional program info. */ for (i=30; i < 40; i++ ) - if ( (s=strusage (i)) ) + if ( (s=_gpgrt_strusage (i)) ) writestrings (0, s, NULL); flushstrings (0); } void -usage (int level) +_gpgrt_usage (int level) { const char *p; if (!level) { - writestrings (1, strusage(11), " ", strusage(13), "; ", - strusage (14), "\n", NULL); + writestrings (1, _gpgrt_strusage(11), " ", _gpgrt_strusage(13), "; ", + _gpgrt_strusage (14), "\n", NULL); flushstrings (1); } else if (level == 1) { - p = strusage (40); + p = _gpgrt_strusage (40); writestrings (1, p, NULL); if (*p && p[strlen(p)] != '\n') writestrings (1, "\n", NULL); @@ -1493,15 +1448,15 @@ usage (int level) } else if (level == 2) { - p = strusage (42); + p = _gpgrt_strusage (42); if (p && *p == '1') { - p = strusage (40); + p = _gpgrt_strusage (40); writestrings (1, p, NULL); if (*p && p[strlen(p)] != '\n') writestrings (1, "\n", NULL); } - writestrings (0, strusage(41), "\n", NULL); + writestrings (0, _gpgrt_strusage(41), "\n", NULL); exit (0); } } @@ -1510,6 +1465,8 @@ usage (int level) * 0: Print copyright string to stderr * 1: Print a short usage hint to stderr and terminate * 2: Print a long usage hint to stdout and terminate + * 8: Return NULL for UTF-8 or string with the native charset. + * 9: Return the SPDX License tag. * 10: Return license info string * 11: Return the name of the program * 12: Return optional name of package which includes this program. @@ -1530,38 +1487,69 @@ usage (int level) * before the long usage note. */ const char * -strusage( int level ) +_gpgrt_strusage (int level) { const char *p = strusage_handler? strusage_handler(level) : NULL; + const char *tmp; if ( p ) - return map_static_macro_string (p); + return map_fixed_string (p); switch ( level ) { + case 8: break; /* Default to utf-8. */ + case 9: + p = "GPL-3.0-or-later"; /* Suggested license. */ + break; + case 10: -#if ARGPARSE_GPL_VERSION == 3 - p = ("License GPLv3+: GNU GPL version 3 or later " - "<https://gnu.org/licenses/gpl.html>"); -#else - p = ("License GPLv2+: GNU GPL version 2 or later " - "<https://gnu.org/licenses/>"); -#endif + tmp = _gpgrt_strusage (9); + if (tmp && !strcmp (tmp, "GPL-2.0-or-later")) + p = ("License GPL-2.0-or-later <https://gnu.org/licenses/>"); + else if (tmp && !strcmp (tmp, "LGPL-2.1-or-later")) + p = ("License LGPL-2.1-or-later <https://gnu.org/licenses/>"); + else /* Default to GPLv3+. */ + p = ("License GPL-3.0-or-later <https://gnu.org/licenses/gpl.html>"); break; case 11: p = "foo"; break; case 13: p = "0.0"; break; - case 14: p = ARGPARSE_CRIGHT_STR; break; + case 14: p = "Copyright (C) YEAR NAME"; break; case 15: p = "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; break; - case 16: p = + case 16: + tmp = _gpgrt_strusage (9); + if (tmp && !strcmp (tmp, "GPL-2.0-or-later")) + p = "This is free software; you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" -"the Free Software Foundation; either version " -ARGPARSE_STR2(ARGPARSE_GPL_VERSION) -" of the License, or\n" +"the Free Software Foundation; either version 2 of the License, or\n" +"(at your option) any later version.\n\n" +"It is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU General Public License for more details.\n\n" +"You should have received a copy of the GNU General Public License\n" +"along with this software. If not, see <https://gnu.org/licenses/>.\n"; + else if (tmp && !strcmp (tmp, "LGPL-2.1-or-later")) + p = +"This is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU Lesser General Public License as\n" +"published by the Free Software Foundation; either version 2.1 of\n" +"the License, or (at your option) any later version.\n\n" +"It is distributed in the hope that it will be useful,\n" +"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" +"GNU Lesser General Public License for more details.\n\n" +"You should have received a copy of the GNU General Public License\n" +"along with this software. If not, see <https://gnu.org/licenses/>.\n"; + else /* Default */ + p = +"This is free software; you can redistribute it and/or modify\n" +"it under the terms of the GNU General Public License as published by\n" +"the Free Software Foundation; either version 3 of the License, or\n" "(at your option) any later version.\n\n" "It is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" @@ -1580,81 +1568,30 @@ ARGPARSE_STR2(ARGPARSE_GPL_VERSION) /* Set the usage handler. This function is basically a constructor. */ void -set_strusage ( const char *(*f)( int ) ) +_gpgrt_set_strusage (const char *(*f)(int) ) { strusage_handler = f; } -#ifdef TEST -static struct { - int verbose; - int debug; - char *outfile; - char *crf; - int myopt; - int echo; - int a_long_one; -} opt; - -int -main(int argc, char **argv) +/* Set a function to write strings which is the used instead of + * estream. The first arg of that function is MODE and the second the + * STRING to write. A mode of 1 is used for writing to stdout and a + * mode of 2 to write to stderr. Other modes are reserved and should + * not output anything. A NULL for STRING requests a flush. */ +void +_gpgrt_set_usage_outfnc (int (*f)(int, const char *)) { - ARGPARSE_OPTS opts[] = { - ARGPARSE_x('v', "verbose", NONE, 0, "Laut sein"), - ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " - "was wir eingegeben haben")), - ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), - ARGPARSE_s_s('o', "output", 0 ), - ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), - /* Note that on a non-utf8 terminal the ß might garble the output. */ - ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), - ARGPARSE_o_i('m', "my-option", 0), - ARGPARSE_s_n(500, "a-long-option", 0 ), - ARGPARSE_end() - }; - ARGPARSE_ARGS pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL - | ARGPARSE_FLAG_MIXED - | ARGPARSE_FLAG_ONEDASH) }; - int i; + custom_outfnc = f; +} - while (arg_parse (&pargs, opts)) - { - switch (pargs.r_opt) - { - case ARGPARSE_IS_ARG : - printf ("arg='%s'\n", pargs.r.ret_str); - break; - case 'v': opt.verbose++; break; - case 'e': opt.echo++; break; - case 'd': opt.debug++; break; - case 'o': opt.outfile = pargs.r.ret_str; break; - case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; - case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; - case 500: opt.a_long_one++; break; - default : pargs.err = ARGPARSE_PRINT_WARNING; break; - } - } - for (i=0; i < argc; i++ ) - printf ("%3d -> (%s)\n", i, argv[i] ); - puts ("Options:"); - if (opt.verbose) - printf (" verbose=%d\n", opt.verbose ); - if (opt.debug) - printf (" debug=%d\n", opt.debug ); - if (opt.outfile) - printf (" outfile='%s'\n", opt.outfile ); - if (opt.crf) - printf (" crffile='%s'\n", opt.crf ); - if (opt.myopt) - printf (" myopt=%d\n", opt.myopt ); - if (opt.a_long_one) - printf (" a-long-one=%d\n", opt.a_long_one ); - if (opt.echo) - printf (" echo=%d\n", opt.echo ); - return 0; +/* Register function F as a string mapper which takes a string as + * argument, replaces known "@FOO@" style macros and returns a new + * fixed string. Warning: The input STRING must have been allocated + * statically. */ +void +_gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)) +{ + fixed_string_mapper = f; } -#endif /*TEST*/ - -/**** bottom of file ****/ diff --git a/src/argparse.h b/src/argparse.h deleted file mode 100644 index 4167d66..0000000 --- a/src/argparse.h +++ /dev/null @@ -1,207 +0,0 @@ -/* argparse.h - Argument parser for option handling. - * Copyright (C) 1997-2001, 2006-2008, 2013-2017 Werner Koch - * Copyright (C) 1998-2001, 2006-2008, 2012 Free Software Foundation, Inc. - * Copyright (C) 2015-2017 g10 Code GmbH - * - * This file is part of GnuPG. - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see <https://www.gnu.org/licenses/>. - * SPDX-License-Identifier: LGPL-2.1+ - */ - -#ifndef GNUPG_COMMON_ARGPARSE_H -#define GNUPG_COMMON_ARGPARSE_H - -#include <stdio.h> - -typedef struct -{ - int *argc; /* Pointer to ARGC (value subject to change). */ - char ***argv; /* Pointer to ARGV (value subject to change). */ - unsigned int flags; /* Global flags. May be set prior to calling the - parser. The parser may change the value. */ - int err; /* Print error description for last option. - Either 0, ARGPARSE_PRINT_WARNING or - ARGPARSE_PRINT_ERROR. */ - - int r_opt; /* Returns option code. */ - int r_type; /* Returns type of option value. */ - union { - int ret_int; - long ret_long; - unsigned long ret_ulong; - char *ret_str; - } r; /* Return values */ - - struct { - int idx; - int inarg; - int stopped; - const char *last; - void *aliases; - const void *cur_alias; - void *iio_list; - } internal; /* Private - do not change. */ -} ARGPARSE_ARGS; - -typedef struct -{ - int short_opt; - const char *long_opt; - unsigned int flags; - const char *description; /* Optional option description. */ -} ARGPARSE_OPTS; - -/* Short options. */ -#define ARGPARSE_SHORTOPT_HELP 32768 -#define ARGPARSE_SHORTOPT_VERSION 32769 -#define ARGPARSE_SHORTOPT_WARRANTY 32770 -#define ARGPARSE_SHORTOPT_DUMP_OPTIONS 32771 - - -/* Global flags (ARGPARSE_ARGS). */ -#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ -#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return - remaining args with R_OPT set to -1. */ -#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ -#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ -#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ -#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ -#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ - -#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ - -/* Flags for each option (ARGPARSE_OPTS). The type code may be - ORed with the OPT flags. */ -#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ -#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ -#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ -#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ -#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ -#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ -#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ -#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ -#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ - -#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values (internal). */ - -/* A set of macros to make option definitions easier to read. */ -#define ARGPARSE_x(s,l,t,f,d) \ - { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } - -#define ARGPARSE_s(s,l,t,d) \ - { (s), (l), ARGPARSE_TYPE_ ## t, (d) } -#define ARGPARSE_s_n(s,l,d) \ - { (s), (l), ARGPARSE_TYPE_NONE, (d) } -#define ARGPARSE_s_i(s,l,d) \ - { (s), (l), ARGPARSE_TYPE_INT, (d) } -#define ARGPARSE_s_s(s,l,d) \ - { (s), (l), ARGPARSE_TYPE_STRING, (d) } -#define ARGPARSE_s_l(s,l,d) \ - { (s), (l), ARGPARSE_TYPE_LONG, (d) } -#define ARGPARSE_s_u(s,l,d) \ - { (s), (l), ARGPARSE_TYPE_ULONG, (d) } - -#define ARGPARSE_o(s,l,t,d) \ - { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } -#define ARGPARSE_o_n(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } -#define ARGPARSE_o_i(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } -#define ARGPARSE_o_s(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } -#define ARGPARSE_o_l(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } -#define ARGPARSE_o_u(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } - -#define ARGPARSE_p(s,l,t,d) \ - { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_p_n(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_p_i(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_p_s(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_p_l(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_p_u(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } - -#define ARGPARSE_op(s,l,t,d) \ - { (s), (l), (ARGPARSE_TYPE_ ## t \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_op_n(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_NONE \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_op_i(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_INT \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_op_s(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_STRING \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_op_l(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_LONG \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } -#define ARGPARSE_op_u(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_ULONG \ - | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } - -#define ARGPARSE_c(s,l,d) \ - { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } - -#define ARGPARSE_ignore(s,l) \ - { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } - -#define ARGPARSE_group(s,d) \ - { (s), NULL, 0, (d) } - -/* Placeholder options for help, version, warranty and dump-options. See arg_parse(). */ -#define ARGPARSE_end() \ - { 0, NULL, 0, NULL }, \ - { 0, NULL, 0, NULL }, \ - { 0, NULL, 0, NULL }, \ - { 0, NULL, 0, NULL }, \ - { 0, NULL, 0, NULL } - - -/* Other constants. */ -#define ARGPARSE_PRINT_WARNING 1 -#define ARGPARSE_PRINT_ERROR 2 - - -/* Error values. */ -#define ARGPARSE_IS_ARG (-1) -#define ARGPARSE_INVALID_OPTION (-2) -#define ARGPARSE_MISSING_ARG (-3) -#define ARGPARSE_KEYWORD_TOO_LONG (-4) -#define ARGPARSE_READ_ERROR (-5) -#define ARGPARSE_UNEXPECTED_ARG (-6) -#define ARGPARSE_INVALID_COMMAND (-7) -#define ARGPARSE_AMBIGUOUS_OPTION (-8) -#define ARGPARSE_AMBIGUOUS_COMMAND (-9) -#define ARGPARSE_INVALID_ALIAS (-10) -#define ARGPARSE_OUT_OF_CORE (-11) -#define ARGPARSE_INVALID_ARG (-12) - - -int arg_parse (ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); -int optfile_parse (FILE *fp, const char *filename, unsigned *lineno, - ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts); -void usage (int level); -const char *strusage (int level); -void set_strusage (const char *(*f)( int )); -void argparse_register_outfnc (int (*fnc)(int, const char *)); - -#endif /*GNUPG_COMMON_ARGPARSE_H*/ diff --git a/src/gpg-error.def.in b/src/gpg-error.def.in index eea6e4a..7a6a2ef 100644 --- a/src/gpg-error.def.in +++ b/src/gpg-error.def.in @@ -207,5 +207,11 @@ EXPORTS ;; gpgrt_kill_process @158 ;; gpgrt_release_process @159 + gpgrt_argparse @160 + gpgrt_strusage @161 + gpgrt_set_strusage @162 + gpgrt_set_usage_outfnc @163 + gpgrt_set_fixed_string_mapper @164 + ;; end of file with public symbols for Windows. diff --git a/src/gpg-error.h.in b/src/gpg-error.h.in index cc2e361..86bb5b2 100644 --- a/src/gpg-error.h.in +++ b/src/gpg-error.h.in @@ -83,6 +83,12 @@ extern "C" { GPGRT_ENABLE_ES_MACROS: Define to provide "es_" macros for the estream functions. + GPGRT_ENABLE_LOG_MACROS: Define to provide short versions of the + log functions. + + GPGRT_ENABLE_ARGPARSE_MACROS: Needs to be defined to provide the + mandatory macros of the argparse interface. + In addition to the error codes, Libgpg-error also provides a set of functions used by most GnuPG components. */ @@ -1079,6 +1085,181 @@ void gpgrt_release_process (pid_t pid); #endif /*0*/ + + +/* + * Option parsing. + */ + +struct _gpgrt_argparse_internal_s; +typedef struct +{ + int *argc; /* Pointer to ARGC (value subject to change). */ + char ***argv; /* Pointer to ARGV (value subject to change). */ + unsigned int flags; /* Global flags. May be set prior to calling the + parser. The parser may change the value. */ + int err; /* Print error description for last option. + Either 0, ARGPARSE_PRINT_WARNING or + ARGPARSE_PRINT_ERROR. */ + unsigned int lineno;/* The current line number. */ + int r_opt; /* Returns option code. */ + int r_type; /* Returns type of option value. */ + union { + int ret_int; + long ret_long; + unsigned long ret_ulong; + char *ret_str; + } r; /* Return values */ + + struct _gpgrt_argparse_internal_s *internal; +} gpgrt_argparse_t; + + +typedef struct +{ + int short_opt; + const char *long_opt; + unsigned int flags; + const char *description; /* Optional description. */ +} gpgrt_opt_t; + + +#ifdef GPGRT_ENABLE_ARGPARSE_MACROS + +/* Global flags for (gpgrt_argparse_t).flags. */ +#define ARGPARSE_FLAG_KEEP 1 /* Do not remove options form argv. */ +#define ARGPARSE_FLAG_ALL 2 /* Do not stop at last option but return + remaining args with R_OPT set to -1. */ +#define ARGPARSE_FLAG_MIXED 4 /* Assume options and args are mixed. */ +#define ARGPARSE_FLAG_NOSTOP 8 /* Do not stop processing at "--". */ +#define ARGPARSE_FLAG_ARG0 16 /* Do not skip the first arg. */ +#define ARGPARSE_FLAG_ONEDASH 32 /* Allow long options with one dash. */ +#define ARGPARSE_FLAG_NOVERSION 64 /* No output for "--version". */ +#define ARGPARSE_FLAG_RESET 128 /* Request to reset the internal state. */ +#define ARGPARSE_FLAG_STOP_SEEN 256 /* Set to true if a "--" has been seen. */ +#define ARGPARSE_FLAG_NOLINENO 512 /* Do not zero the lineno field. */ + +/* Constants for (gpgrt_argparse_t).err. */ +#define ARGPARSE_PRINT_WARNING 1 /* Print a diagnostic. */ +#define ARGPARSE_PRINT_ERROR 2 /* Print a diagnostic and call exit. */ + +/* Special return values of gpgrt_argparse. */ +#define ARGPARSE_IS_ARG (-1) +#define ARGPARSE_INVALID_OPTION (-2) +#define ARGPARSE_MISSING_ARG (-3) +#define ARGPARSE_KEYWORD_TOO_LONG (-4) +#define ARGPARSE_READ_ERROR (-5) +#define ARGPARSE_UNEXPECTED_ARG (-6) +#define ARGPARSE_INVALID_COMMAND (-7) +#define ARGPARSE_AMBIGUOUS_OPTION (-8) +#define ARGPARSE_AMBIGUOUS_COMMAND (-9) +#define ARGPARSE_INVALID_ALIAS (-10) +#define ARGPARSE_OUT_OF_CORE (-11) +#define ARGPARSE_INVALID_ARG (-12) + +/* Flags for the option descriptor (gpgrt_opt_t)->flags. Note that + * a TYPE constant may be or-ed with the OPT constants. */ +#define ARGPARSE_TYPE_NONE 0 /* Does not take an argument. */ +#define ARGPARSE_TYPE_INT 1 /* Takes an int argument. */ +#define ARGPARSE_TYPE_STRING 2 /* Takes a string argument. */ +#define ARGPARSE_TYPE_LONG 3 /* Takes a long argument. */ +#define ARGPARSE_TYPE_ULONG 4 /* Takes an unsigned long argument. */ +#define ARGPARSE_OPT_OPTIONAL (1<<3) /* Argument is optional. */ +#define ARGPARSE_OPT_PREFIX (1<<4) /* Allow 0x etc. prefixed values. */ +#define ARGPARSE_OPT_IGNORE (1<<6) /* Ignore command or option. */ +#define ARGPARSE_OPT_COMMAND (1<<7) /* The argument is a command. */ + +/* A set of macros to make option definitions easier to read. */ +#define ARGPARSE_x(s,l,t,f,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t | (f), (d) } + +#define ARGPARSE_s(s,l,t,d) \ + { (s), (l), ARGPARSE_TYPE_ ## t, (d) } +#define ARGPARSE_s_n(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_NONE, (d) } +#define ARGPARSE_s_i(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_INT, (d) } +#define ARGPARSE_s_s(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_STRING, (d) } +#define ARGPARSE_s_l(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_LONG, (d) } +#define ARGPARSE_s_u(s,l,d) \ + { (s), (l), ARGPARSE_TYPE_ULONG, (d) } + +#define ARGPARSE_o(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_OPTIONAL), (d) } +#define ARGPARSE_o_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_OPTIONAL), (d) } + +#define ARGPARSE_p(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_p_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_op(s,l,t,d) \ + { (s), (l), (ARGPARSE_TYPE_ ## t \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_n(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_i(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_INT \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_s(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_STRING \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_l(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_LONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } +#define ARGPARSE_op_u(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_ULONG \ + | ARGPARSE_OPT_OPTIONAL | ARGPARSE_OPT_PREFIX), (d) } + +#define ARGPARSE_c(s,l,d) \ + { (s), (l), (ARGPARSE_TYPE_NONE | ARGPARSE_OPT_COMMAND), (d) } + +#define ARGPARSE_ignore(s,l) \ + { (s), (l), (ARGPARSE_OPT_IGNORE), "@" } + +#define ARGPARSE_group(s,d) \ + { (s), NULL, 0, (d) } + +/* Placeholder options for help, version, warranty and dump-options. + * See arg_parse(). FIXME: We need to hide this from the API. */ +#define ARGPARSE_end() \ + { 0, NULL, 0, NULL }, \ + { 0, NULL, 0, NULL }, \ + { 0, NULL, 0, NULL }, \ + { 0, NULL, 0, NULL }, \ + { 0, NULL, 0, NULL } +#endif /* GPGRT_ENABLE_ARGPARSE_MACROS */ + + +int gpgrt_argparse (gpgrt_stream_t fp, + gpgrt_argparse_t *arg, gpgrt_opt_t *opts); +const char *gpgrt_strusage (int level); +void gpgrt_set_strusage (const char *(*f)(int)); +void gpgrt_set_usage_outfnc (int (*f)(int, const char *)); +void gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)); + + + #ifdef __cplusplus } #endif diff --git a/src/gpg-error.vers b/src/gpg-error.vers index 1105e80..cdef0cd 100644 --- a/src/gpg-error.vers +++ b/src/gpg-error.vers @@ -179,6 +179,11 @@ GPG_ERROR_1.0 { # gpgrt_kill_process; # gpgrt_release_process; + gpgrt_argparse; + gpgrt_strusage; + gpgrt_set_strusage; + gpgrt_set_usage_outfnc; + gpgrt_set_fixed_string_mapper; local: *; diff --git a/src/gpgrt-int.h b/src/gpgrt-int.h index fcce082..794874e 100644 --- a/src/gpgrt-int.h +++ b/src/gpgrt-int.h @@ -711,6 +711,15 @@ void _gpgrt_kill_process (pid_t pid); void _gpgrt_release_process (pid_t pid); +/* + * Local prototypes for argparse. + */ +int _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts); +const char *_gpgrt_strusage (int level); +void _gpgrt_set_strusage (const char *(*f)(int)); +void _gpgrt_set_usage_outfnc (int (*fnc)(int, const char *)); +void _gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)); + /* diff --git a/src/visibility.c b/src/visibility.c index 9358163..7058c91 100644 --- a/src/visibility.c +++ b/src/visibility.c @@ -1066,6 +1066,38 @@ gpgrt_release_process (pid_t pid) } #endif /*0*/ + +int +gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts) +{ + return _gpgrt_argparse (fp, arg, opts); +} + +const char * +gpgrt_strusage (int level) +{ + return _gpgrt_strusage (level); +} + +void +gpgrt_set_strusage (const char *(*f)(int)) +{ + _gpgrt_set_strusage (f); +} + +void +gpgrt_set_usage_outfnc (int (*f)(int, const char *)) +{ + _gpgrt_set_usage_outfnc (f); +} + +void +gpgrt_set_fixed_string_mapper (const char *(*f)(const char*)) +{ + _gpgrt_set_fixed_string_mapper (f); +} + + /* For consistency reasons we use function wrappers also for Windows * specific function despite that they are technically not needed. */ diff --git a/src/visibility.h b/src/visibility.h index f37555a..51d9e38 100644 --- a/src/visibility.h +++ b/src/visibility.h @@ -199,6 +199,12 @@ MARK_VISIBLE (gpgrt_kill_process) MARK_VISIBLE (gpgrt_release_process) #endif +MARK_VISIBLE (gpgrt_argparse) +MARK_VISIBLE (gpgrt_set_strusage) +MARK_VISIBLE (gpgrt_strusage) +MARK_VISIBLE (gpgrt_set_fixed_string_mapper); +MARK_VISIBLE (gpgrt_set_usage_outfnc); + #undef MARK_VISIBLE #else /*!_GPGRT_INCL_BY_VISIBILITY_C*/ @@ -359,6 +365,11 @@ MARK_VISIBLE (gpgrt_release_process) #define gpgrt_kill_process _gpgrt_USE_UNDERSCORED_FUNCTION #define gpgrt_release_process _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_argparse _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_set_strusage _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_strusage _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_set_usage_outfnc _gpgrt_USE_UNDERSCORED_FUNCTION +#define gpgrt_set_fixed_string_mapper _gpgrt_USE_UNDERSCORED_FUNCTION /* Windows specific functions. */ #define gpgrt_w32_reg_query_string _gpgrt_USE_UNDERSCORED_FUNCTION diff --git a/tests/Makefile.am b/tests/Makefile.am index a3c6cbd..cc3a2ad 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,8 @@ endif gpg_error_lib = ../src/libgpg-error.la -TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64dec +TESTS = t-version t-strerror t-syserror t-lock t-printf t-poll t-b64dec \ + t-argparse AM_CPPFLAGS = -I$(top_builddir)/src $(extra_includes) diff --git a/tests/t-argparse.c b/tests/t-argparse.c new file mode 100644 index 0000000..177992a --- /dev/null +++ b/tests/t-argparse.c @@ -0,0 +1,125 @@ +/* t-argparse.c - Check the argparse API + * Copyright (C) 2018 g10 Code GmbH + * + * This file is part of Libgpg-error. + * + * Libgpg-error is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgpg-error is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "../src/gpg-error.h" + + +static struct { + int verbose; + int debug; + char *outfile; + char *crf; + int myopt; + int echo; + int a_long_one; +} opt; + + + +static const char * +my_strusage (int level) +{ + const char *p; + + switch (level) + { + case 9: p = "GPL-2.0-or-later"; break; + + case 11: p = "t-argparse"; break; + + default: p = NULL; + } + return p; +} + + + +int +main (int argc, char **argv) +{ + gpgrt_opt_t opts[] = { + ARGPARSE_x ('v', "verbose", NONE, 0, "Laut sein"), + ARGPARSE_s_n('e', "echo" , ("Zeile ausgeben, damit wir sehen, " + "was wir eingegeben haben")), + ARGPARSE_s_n('d', "debug", "Debug\nfalls mal etwas\nschief geht"), + ARGPARSE_s_s('o', "output", 0 ), + ARGPARSE_o_s('c', "cross-ref", "cross-reference erzeugen\n" ), + /* Note that on a non-utf8 terminal the ß might garble the output. */ + ARGPARSE_s_n('s', "street","|Straße|set the name of the street to Straße"), + ARGPARSE_o_i('m', "my-option", 0), + ARGPARSE_s_n(500, "a-long-option", 0 ), + ARGPARSE_end() + }; + gpgrt_argparse_t pargs = { &argc, &argv, (ARGPARSE_FLAG_ALL + | ARGPARSE_FLAG_MIXED + | ARGPARSE_FLAG_ONEDASH) }; + int i; + + gpgrt_set_strusage (my_strusage); + + + while (gpgrt_argparse (NULL, &pargs, opts)) + { + switch (pargs.r_opt) + { + case ARGPARSE_IS_ARG : + printf ("arg='%s'\n", pargs.r.ret_str); + break; + case 'v': opt.verbose++; break; + case 'e': opt.echo++; break; + case 'd': opt.debug++; break; + case 'o': opt.outfile = pargs.r.ret_str; break; + case 'c': opt.crf = pargs.r_type? pargs.r.ret_str:"a.crf"; break; + case 'm': opt.myopt = pargs.r_type? pargs.r.ret_int : 1; break; + case 500: opt.a_long_one++; break; + default : pargs.err = ARGPARSE_PRINT_ERROR; break; + } + } + for (i=0; i < argc; i++ ) + printf ("%3d -> (%s)\n", i, argv[i] ); + puts ("Options:"); + if (opt.verbose) + printf (" verbose=%d\n", opt.verbose ); + if (opt.debug) + printf (" debug=%d\n", opt.debug ); + if (opt.outfile) + printf (" outfile='%s'\n", opt.outfile ); + if (opt.crf) + printf (" crffile='%s'\n", opt.crf ); + if (opt.myopt) + printf (" myopt=%d\n", opt.myopt ); + if (opt.a_long_one) + printf (" a-long-one=%d\n", opt.a_long_one ); + if (opt.echo) + printf (" echo=%d\n", opt.echo ); + + gpgrt_argparse (NULL, &pargs, NULL); + + return 0; +} |