aboutsummaryrefslogtreecommitdiffstats
path: root/common/argparse.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/argparse.c')
-rw-r--r--common/argparse.c2415
1 files changed, 1747 insertions, 668 deletions
diff --git a/common/argparse.c b/common/argparse.c
index c04cf830d..fff720c19 100644
--- a/common/argparse.c
+++ b/common/argparse.c
@@ -29,9 +29,8 @@
* if not, see <https://gnu.org/licenses/>.
*/
-/* 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.
+/* This is a modified version of gpgrt/libgpg-error src/argparse.c.
+ * We use this to require a dependency on a newer gpgrt version.
*/
#ifdef HAVE_CONFIG_H
@@ -45,232 +44,115 @@
#include <stdarg.h>
#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 <unistd.h>
+#include <time.h>
+
+#include "util.h"
+#include "common-defs.h"
+#include "i18n.h"
+#include "mischelp.h"
+#include "stringhelp.h"
+#include "logging.h"
+#include "utf8conv.h"
+#include "sysutils.h"
#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 */
-
-/* GnuPG has always been a part of the GNU project and thus we have
- * shown the FSF as holder of the copyright. We continue to do so for
- * the reason that without the FSF the free software used all over the
- * world would not have come into existence. However, under Windows
- * we print a different copyright string with --version because the
- * copyright assignments of g10 Code and Werner Koch were terminated
- * many years ago, g10 Code is still the major contributor to the
- * code, and Windows is not an FSF endorsed platform. Note that the
- * list of copyright holders can be found in the AUTHORS file. */
-
-# define ARGPARSE_GPL_VERSION 3
-# ifdef HAVE_W32_SYSTEM
-# define ARGPARSE_CRIGHT_STR "Copyright (C) 2020 g10 Code GmbH"
-# else
-# define ARGPARSE_CRIGHT_STR "Copyright (C) 2020 Free Software Foundation, Inc."
-# endif
-
-#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, ...)
-{
- va_list arg_ptr ;
- va_start (arg_ptr, fmt);
- fprintf (stderr, "%s: ", strusage (11));
- vfprintf (stderr, fmt, arg_ptr);
- va_end (arg_ptr);
-}
+/* Optional handler to write strings. See gnupg_set_usage_outfnc. */
+static int (*custom_outfnc) (int, const char *);
-static void
-my_log_bug (const char *fmt, ...)
-{
- va_list arg_ptr ;
- va_start (arg_ptr, fmt);
- fprintf (stderr, "%s: Ohhhh jeeee: ", strusage (11));
- vfprintf (stderr, fmt, arg_ptr);
- va_end (arg_ptr);
- abort ();
-}
+#if USE_INTERNAL_ARGPARSE
-/* Return true if the native charset is utf-8. */
-static int
-is_native_utf8 (void)
-{
- return 1;
-}
+/* The almost always needed user handler for strusage. */
+static const char *(*strusage_handler)( int ) = NULL;
+/* Optional handler to map strings. See gnupg_set_fixed_string_mapper. */
+static const char *(*fixed_string_mapper)(const char*);
-static char *
-my_trim_spaces (char *str)
-{
- char *string, *p, *mark;
- string = str;
- /* Find first non space character. */
- for (p=string; *p && isspace (*(unsigned char*)p) ; p++)
- ;
- /* Move characters. */
- for ((mark = NULL); (*string = *p); string++, p++)
- if (isspace (*(unsigned char*)p))
- {
- if (!mark)
- mark = string;
- }
- else
- mark = NULL;
- if (mark)
- *mark = '\0' ; /* Remove trailing spaces. */
+/* Hidden argparse flag used to mark the object as initialized. */
+#define ARGPARSE_FLAG__INITIALIZED (1u << ((8*4)-1))
- return str ;
-}
+/* Special short options which are auto-inserterd. Must fit into an
+ * unsigned short. */
+#define ARGPARSE_SHORTOPT_HELP 32768
+#define ARGPARSE_SHORTOPT_VERSION 32769
+#define ARGPARSE_SHORTOPT_WARRANTY 32770
+#define ARGPARSE_SHORTOPT_DUMP_OPTIONS 32771
+#define ARGPARSE_SHORTOPT_DUMP_OPTTBL 32772
-#endif /*!GNUPG_MAJOR_VERSION*/
+/* The malloced configuration directories or NULL. */
+static struct
+{
+ char *user;
+ char *sys;
+} confdir;
+
+
+/* The states for the gnupg_argparser machinery. */
+enum argparser_states
+ {
+ STATE_init = 0,
+ STATE_open_sys,
+ STATE_open_user,
+ STATE_open_cmdline,
+ STATE_read_sys,
+ STATE_read_user,
+ STATE_read_cmdline,
+ STATE_finished
+ };
-/*********************************
- * @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.
- * The option '--' stops argument processing; if bit 1 is set the function
- * continues to return normal arguments.
- * To process float args or unsigned args you must use a string args and do
- * the conversion yourself.
- * @Example
- *
- * ARGPARSE_OPTS opts[] = {
- * { 'v', "verbose", 0 },
- * { 'd', "debug", 0 },
- * { 'o', "output", 2 },
- * { 'c', "cross-ref", 2|8 },
- * { 'm', "my-option", 1|8 },
- * { 300, "ignored-long-option, ARGPARSE_OP_IGNORE},
- * { 500, "have-no-short-option-for-this-long-option", 0 },
- * {0} };
- * ARGPARSE_ARGS pargs = { &argc, &argv, 0 }
- *
- * while( ArgParse( &pargs, &opts) ) {
- * switch( pargs.r_opt ) {
- * case 'v': opt.verbose++; 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 = 1; break; -- force warning output --
- * }
- * }
- * if( argc > 1 )
- * log_fatal( "Too many args");
- *
- */
+/* An internal object used to store the user provided option table and
+ * some meta information. */
+typedef struct
+{
+ unsigned short short_opt;
+ unsigned short ordinal; /* (for --help) */
+ unsigned int flags;
+ const char *long_opt; /* Points into the user provided table. */
+ const char *description; /* Points into the user provided table. */
+ unsigned int forced:1; /* Forced to use the sysconf value. */
+ unsigned int ignore:1; /* Ignore this option everywhere but in
+ * the sysconf file. */
+ unsigned int explicit_ignore:1; /* Ignore was explicitly set. */
+} opttable_t;
+
+
+/* Internal object of the public gnupg_argparse_t object. */
+struct _argparse_internal_s
+{
+ int idx; /* Note that this is saved and restored in gnupg_argparser. */
+ int inarg; /* (index into args) */
+ unsigned int verbose:1; /* Print diagnostics. */
+ unsigned int stopped:1; /* Option processing has stopped. */
+ unsigned int in_sysconf:1; /* Processing global config file. */
+ unsigned int mark_forced:1; /* Mark options as forced. */
+ unsigned int mark_ignore:1; /* Mark options as to be ignored. */
+ unsigned int explicit_ignore:1; /* Option has explicitly been set
+ * to ignore or unignore. */
+ unsigned int ignore_all_seen:1; /* [ignore-all] has been seen. */
+ unsigned int user_seen:1; /* A [user] has been seen. */
+ unsigned int user_wildcard:1; /* A [user *] has been seen. */
+ unsigned int user_any_active:1; /* Any user section was active. */
+ unsigned int user_active:1; /* User section active. */
+ unsigned int explicit_confopt:1; /* A conffile option has been given. */
+ char *explicit_conffile; /* Malloced name of an explicit
+ * conffile. */
+ char *username; /* Malloced current user name. */
+ unsigned int opt_flags; /* Current option flags. */
+ enum argparser_states state; /* State of the gnupg_argparser. */
+ const char *last;
+ void *aliases;
+ const void *cur_alias;
+ void *iio_list;
+ estream_t conffp;
+ char *confname;
+ opttable_t *opts; /* Malloced option table. */
+ unsigned int nopts; /* Number of items in OPTS. */
+};
+
typedef struct alias_def_s *ALIAS_DEF;
struct alias_def_s {
@@ -289,26 +171,103 @@ struct iio_item_def_s
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
- ;
+static int set_opt_arg (gnupg_argparse_t *arg, unsigned int flags, char *s);
+static void show_help (opttable_t *opts, unsigned int nopts,unsigned int flags);
+static void show_version (void);
+static void dump_option_table (gnupg_argparse_t *arg);
+static int writestrings (int is_error, const char *string,
+ ...) GPGRT_ATTR_SENTINEL(0);
+
+static int arg_parse (gnupg_argparse_t *arg, gnupg_opt_t *opts, int no_init);
+
+
+
+/* Set a function to write strings which is then 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
+gnupg_set_usage_outfnc (int (*f)(int, const char *))
+{
+ custom_outfnc = f;
+}
+/* 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
-argparse_register_outfnc (int (*fnc)(int, const char *))
+gnupg_set_fixed_string_mapper (const char *(*f)(const char*))
{
- custom_outfnc = fnc;
+ fixed_string_mapper = f;
}
+/* Register a configuration directory for use by the argparse
+ * functions. The defined values for WHAT are:
+ *
+ * GNUPG_CONFDIR_SYS The systems's configuration dir.
+ * The default is /etc
+ *
+ * GNUPG_CONFDIR_USER The user's configuration directory.
+ * The default is $HOME.
+ *
+ * A trailing slash is ignored; to have the function lookup
+ * configuration files in the current directory, use ".". There is no
+ * error return; more configuraion values may be added in future
+ * revisions of this library.
+ */
+void
+gnupg_set_confdir (int what, const char *name)
+{
+ char *buf, *p;
+
+ if (what == GNUPG_CONFDIR_SYS)
+ {
+ xfree (confdir.sys);
+ buf = confdir.sys = xtrystrdup (name);
+ }
+ else if (what == GNUPG_CONFDIR_USER)
+ {
+ xfree (confdir.user);
+ buf = confdir.user = xtrystrdup (name);
+ }
+ else
+ return;
+
+ if (!buf)
+ log_fatal ("out of core in %s\n", __func__);
+#ifdef HAVE_W32_SYSTEM
+ for (p=buf; *p; p++)
+ if (*p == '\\')
+ *p = '/';
+#endif
+ /* Strip trailing slashes unless buf is "/" or any other single char
+ * string. */
+ if (*buf)
+ {
+ for (p=buf + strlen (buf)-1; p > buf; p--)
+ if (*p == '/')
+ *p = 0;
+ else
+ break;
+ }
+}
+
+
+
+static const char *
+map_fixed_string (const char *string)
+{
+ return fixed_string_mapper? fixed_string_mapper (string) : string;
+}
+
+#endif /* USE_INTERNAL_ARGPARSE */
+
+
/* Write STRING and all following const char * arguments either to
stdout or, if IS_ERROR is set, to stderr. The list of strings must
be terminated by a NULL. */
@@ -324,7 +283,7 @@ writestrings (int is_error, const char *string, ...)
s = string;
va_start (arg_ptr, string);
do
- {
+ { /* Fixme: Swicth to estream? */
if (custom_outfnc)
custom_outfnc (is_error? 2:1, s);
else
@@ -348,32 +307,214 @@ flushstrings (int is_error)
}
+#if USE_INTERNAL_ARGPARSE
+
static void
-initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
+deinitialize (gnupg_argparse_t *arg)
{
- if( !(arg->flags & (1<<15)) )
+ if (arg->internal)
{
+ xfree (arg->internal->username);
+ xfree (arg->internal->explicit_conffile);
+ xfree (arg->internal->opts);
+ xfree (arg->internal);
+ arg->internal = NULL;
+ }
+
+ arg->flags &= ARGPARSE_FLAG__INITIALIZED;
+ arg->lineno = 0;
+ arg->err = 0;
+}
+
+/* Our own exit handler to clean up used memory. */
+static void
+my_exit (gnupg_argparse_t *arg, int code)
+{
+ deinitialize (arg);
+ exit (code);
+}
+
+
+static gpg_err_code_t
+initialize (gnupg_argparse_t *arg, gnupg_opt_t *opts, estream_t fp)
+{
+ /* We use a dedicated flag to detect whether *ARG has been
+ * initialized. This is because the old version of that struct, as
+ * used in GnuPG, had no requirement to zero out all fields of the
+ * object and existing code still sets only argc,argv and flags. */
+ if (!(arg->flags & ARGPARSE_FLAG__INITIALIZED)
+ || (arg->flags & ARGPARSE_FLAG_RESET)
+ || !arg->internal)
+ {
+ /* Allocate internal data. */
+ if (!(arg->flags & ARGPARSE_FLAG__INITIALIZED) || !arg->internal)
+ {
+ arg->internal = xtrymalloc (sizeof *arg->internal);
+ if (!arg->internal)
+ return gpg_err_code_from_syserror ();
+ arg->flags |= ARGPARSE_FLAG__INITIALIZED; /* Mark as initialized. */
+ }
+ else if (arg->internal->opts)
+ xfree (arg->internal->opts);
+ arg->internal->opts = NULL;
+ arg->internal->nopts = 0;
+
/* 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->in_sysconf = 0;
+ arg->internal->user_seen = 0;
+ arg->internal->user_wildcard = 0;
+ arg->internal->user_any_active = 0;
+ arg->internal->user_active = 0;
+ arg->internal->username = NULL;
+ arg->internal->mark_forced = 0;
+ arg->internal->mark_ignore = 0;
+ arg->internal->explicit_ignore = 0;
+ arg->internal->ignore_all_seen = 0;
+ arg->internal->explicit_confopt = 0;
+ arg->internal->explicit_conffile = NULL;
+ arg->internal->opt_flags = 0;
+ arg->internal->state = STATE_init;
+ arg->internal->aliases = NULL;
+ arg->internal->cur_alias = NULL;
+ arg->internal->iio_list = NULL;
+ arg->internal->conffp = NULL;
+ arg->internal->confname = NULL;
+
+ /* Clear the copy of the option list. */
+ /* 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->internal->conffp || !(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");
+ log_bug ("invalid argument passed to gnupg_argparse\n");
+
}
+ /* Create an array with pointers to the provided list of options.
+ * Keeping a copy is useful to sort that array and thus do a binary
+ * search and to allow for extra space at the end to insert the
+ * hidden options. An ARGPARSE_FLAG_RESET can be used to reinit
+ * this array. */
+ if (!arg->internal->opts)
+ {
+ int seen_help = 0;
+ int seen_version = 0;
+ int seen_warranty = 0;
+ int seen_dump_options = 0;
+ int seen_dump_option_table = 0;
+ int i;
+
+ for (i=0; opts[i].short_opt; i++)
+ {
+ if (opts[i].long_opt)
+ {
+ if (!strcmp(opts[i].long_opt, "help"))
+ seen_help = 1;
+ else if (!strcmp(opts[i].long_opt, "version"))
+ seen_version = 1;
+ else if (!strcmp(opts[i].long_opt, "warranty"))
+ seen_warranty = 1;
+ else if (!strcmp(opts[i].long_opt, "dump-options"))
+ seen_dump_options = 1;
+ else if (!strcmp(opts[i].long_opt, "dump-option-table"))
+ seen_dump_option_table = 1;
+ }
+ }
+ i += 5; /* The number of the above internal options. */
+ i++; /* End of list marker. */
+ arg->internal->opts = xtrycalloc (i, sizeof *arg->internal->opts);
+ if (!arg->internal->opts)
+ return gpg_err_code_from_syserror ();
+ for(i=0; opts[i].short_opt; i++)
+ {
+ arg->internal->opts[i].short_opt = opts[i].short_opt;
+ arg->internal->opts[i].flags = opts[i].flags;
+ arg->internal->opts[i].long_opt = opts[i].long_opt;
+ arg->internal->opts[i].description = opts[i].description;
+ arg->internal->opts[i].ordinal = i;
+ }
+
+ if (!seen_help)
+ {
+ arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_HELP;
+ arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE;
+ arg->internal->opts[i].long_opt = "help";
+ arg->internal->opts[i].description = "@";
+ arg->internal->opts[i].ordinal = i;
+ i++;
+ }
+ if (!seen_version)
+ {
+ arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_VERSION;
+ arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE;
+ arg->internal->opts[i].long_opt = "version";
+ arg->internal->opts[i].description = "@";
+ arg->internal->opts[i].ordinal = i;
+ i++;
+ }
+
+ if (!seen_warranty)
+ {
+ arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_WARRANTY;
+ arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE;
+ arg->internal->opts[i].long_opt = "warranty";
+ arg->internal->opts[i].description = "@";
+ arg->internal->opts[i].ordinal = i;
+ i++;
+ }
+
+ if (!seen_dump_option_table)
+ {
+ arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_DUMP_OPTTBL;
+ arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE;
+ arg->internal->opts[i].long_opt = "dump-option-table";
+ arg->internal->opts[i].description = "@";
+ arg->internal->opts[i].ordinal = i;
+ i++;
+ }
+
+ if (!seen_dump_options)
+ {
+ arg->internal->opts[i].short_opt = ARGPARSE_SHORTOPT_DUMP_OPTIONS;
+ arg->internal->opts[i].flags = ARGPARSE_TYPE_NONE;
+ arg->internal->opts[i].long_opt = "dump-options";
+ arg->internal->opts[i].description = "@";
+ arg->internal->opts[i].ordinal = i;
+ i++;
+ }
+ /* Take care: When adding new options remember to increase the
+ * size of the array. */
+
+ arg->internal->opts[i].short_opt = 0;
+
+ /* Note that we do not count the end marker but keep it in the
+ * table anyway as an extra item. */
+ arg->internal->nopts = i;
+ }
if (arg->err)
{
/* Last option was erroneous. */
const char *s;
- if (filename)
+ if (!fp && arg->internal->conffp)
+ fp = arg->internal->conffp;
+
+ if (fp)
{
if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
s = _("argument not expected");
@@ -389,22 +530,35 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
s = _("invalid command");
else if ( arg->r_opt == ARGPARSE_INVALID_ALIAS )
s = _("invalid alias definition");
+ else if ( arg->r_opt == ARGPARSE_PERMISSION_ERROR )
+ s = _("permission error");
else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
s = _("out of core");
+ else if ( arg->r_opt == ARGPARSE_NO_CONFFILE )
+ s = NULL; /* Error has already been printed. */
+ else if ( arg->r_opt == ARGPARSE_INVALID_META )
+ s = _("invalid meta command");
+ else if ( arg->r_opt == ARGPARSE_UNKNOWN_META )
+ s = _("unknown meta command");
+ else if ( arg->r_opt == ARGPARSE_UNEXPECTED_META )
+ s = _("unexpected meta command");
else
s = _("invalid option");
- log_error ("%s:%u: %s\n", filename, *lineno, s);
+ if (s)
+ 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);
else if ( arg->r_opt == ARGPARSE_INVALID_ARG )
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);
+ 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);
else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_OPTION )
@@ -412,18 +566,30 @@ initialize( ARGPARSE_ARGS *arg, const char *filename, unsigned *lineno )
else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
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"));
+ log_error ("%s\n", _("out of core"));
+ else if ( arg->r_opt == ARGPARSE_PERMISSION_ERROR )
+ log_error ("%s\n", _("permission error"));
+ else if ( arg->r_opt == ARGPARSE_NO_CONFFILE)
+ ; /* Error has already been printed. */
+ else if ( arg->r_opt == ARGPARSE_INVALID_META )
+ log_error ("%s\n", _("invalid meta command"));
+ else if ( arg->r_opt == ARGPARSE_UNKNOWN_META )
+ log_error ("%s\n", _("unknown meta command"));
+ else if ( arg->r_opt == ARGPARSE_UNEXPECTED_META )
+ log_error ("%s\n",_("unexpected meta command"));
else
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;
}
@@ -451,7 +617,7 @@ store_alias( ARGPARSE_ARGS *arg, char *name, char *value )
static int
ignore_invalid_option_p (ARGPARSE_ARGS *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))
@@ -465,7 +631,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 (ARGPARSE_ARGS *arg, estream_t fp)
{
IIO_ITEM_DEF item;
int c;
@@ -476,7 +642,7 @@ ignore_invalid_option_add (ARGPARSE_ARGS *arg, FILE *fp)
while (!ready)
{
- c = getc (fp);
+ c = gpgrt_getc (fp);
if (c == '\n')
ready = 1;
else if (c == EOF)
@@ -524,8 +690,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;
@@ -541,15 +707,261 @@ ignore_invalid_option_clear (ARGPARSE_ARGS *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;
+}
+
+
+/* Make sure the username field is filled. Return 0 on success. */
+static int
+assure_username (gnupg_argparse_t *arg)
+{
+ if (!arg->internal->username)
+ {
+ arg->internal->username = "dummyuser"; /*FIXMEgpgrt_getusername ();*/
+ if (!arg->internal->username)
+ {
+ log_error ("%s:%u: error getting current user's name: %s\n",
+ arg->internal->confname, arg->lineno,
+ gpg_strerror (gpg_error_from_syserror ()));
+ /* Not necessary the correct error code but given that we
+ * either have a malloc error or some internal system error,
+ * it is the best we can do. */
+ return ARGPARSE_PERMISSION_ERROR;
+ }
+ }
+ return 0;
+}
+
+
+/* Implementation of the "user" command. ARG is the context. ARGS is
+ * a non-empty string which this function is allowed to modify. */
+static int
+handle_meta_user (gnupg_argparse_t *arg, unsigned int alternate, char *args)
+{
+ int rc;
+
+ (void)alternate;
+
+ rc = assure_username (arg);
+ if (rc)
+ return rc;
+
+ arg->internal->user_seen = 1;
+ if (*args == '*' && !args[1])
+ {
+ arg->internal->user_wildcard = 1;
+ arg->internal->user_active = !arg->internal->user_any_active;
+ }
+ else if (arg->internal->user_wildcard)
+ {
+ /* All other user statements are ignored after a wildcard. */
+ arg->internal->user_active = 0;
+ }
+ else if (!strcasecmp (args, arg->internal->username))
+ {
+ arg->internal->user_any_active = 1;
+ arg->internal->user_active = 1;
+ }
+ else
+ {
+ arg->internal->user_active = 0;
+ }
+
+ return 0;
+}
+
+
+/* Implementation of the "force" command. ARG is the context. A
+ * value of 0 for ALTERNATE is "force", a value of 1 requests an
+ * unforce". ARGS is the empty string and not used. */
+static int
+handle_meta_force (gnupg_argparse_t *arg, unsigned int alternate, char *args)
+{
+ (void)args;
+
+ arg->internal->mark_forced = alternate? 0 : 1;
+
+ return 0;
+}
+
+
+/* Implementation of the "ignore" command. ARG is the context. A
+ * value of 0 for ALTERNATE is a plain "ignore", a value of 1 request
+ * an "unignore, a value of 2 requests an "ignore-all". ARGS is the
+ * empty string and not used. */
+static int
+handle_meta_ignore (gnupg_argparse_t *arg, unsigned int alternate, char *args)
+{
+ (void)args;
+
+ if (!alternate)
+ {
+ arg->internal->mark_ignore = 1;
+ arg->internal->explicit_ignore = 1;
+ }
+ else if (alternate == 1)
+ {
+ arg->internal->mark_ignore = 0;
+ arg->internal->explicit_ignore = 1;
+ }
+ else
+ arg->internal->ignore_all_seen = 1;
+
+ return 0;
+}
+
+
+/* Implementation of the "echo" command. ARG is the context. If
+ * ALTERNATE is true the filename is not printed. ARGS is the string
+ * to log. */
+static int
+handle_meta_echo (gnupg_argparse_t *arg, unsigned int alternate, char *args)
+{
+ int rc = 0;
+ char *p, *pend;
+
+ if (alternate)
+ log_info ("%s", "");
+ else
+ log_info ("%s:%u: ", arg->internal->confname, arg->lineno);
+
+ while (*args)
+ {
+ p = strchr (args, '$');
+ if (!p)
+ {
+ log_printf ("%s", args);
+ break;
+ }
+ *p = 0;
+ log_printf ("%s", args);
+ if (p[1] == '$')
+ {
+ log_printf ("$");
+ args = p+2;
+ continue;
+ }
+ if (p[1] != '{')
+ {
+ log_printf ("$");
+ args = p+1;
+ continue;
+ }
+ pend = strchr (p+2, '}');
+ if (!pend) /* No closing brace. */
+ {
+ log_printf ("$");
+ args = p+1;
+ continue;
+ }
+ p += 2;
+ *pend = 0;
+ args = pend+1;
+ if (!strcmp (p, "user"))
+ {
+ rc = assure_username (arg);
+ if (rc)
+ goto leave;
+ log_printf ("%s", arg->internal->username);
+ }
+ else if (!strcmp (p, "file"))
+ log_printf ("%s", arg->internal->confname);
+ else if (!strcmp (p, "line"))
+ log_printf ("%u", arg->lineno);
+ else if (!strcmp (p, "epoch"))
+ log_printf ("%lu", (unsigned long)time (NULL));
+ }
+
+ leave:
+ log_printf ("\n");
+ return rc;
}
+/* Implementation of the "verbose" command. ARG is the context. If
+ * ALTERNATE is true the verbosity is disabled. ARGS is not used. */
+static int
+handle_meta_verbose (gnupg_argparse_t *arg, unsigned int alternate, char *args)
+{
+ (void)args;
+
+ if (alternate)
+ arg->internal->verbose = 0;
+ else
+ arg->internal->verbose = 1;
+ return 0;
+}
+
+/* Handle a meta command. KEYWORD has the content inside the brackets
+ * with leading and trailing spaces removed. The function may modify
+ * KEYWORD. On success 0 is returned, on error an ARGPARSE_ error
+ * code is returned. */
+static int
+handle_metacmd (gnupg_argparse_t *arg, char *keyword)
+{
+ static struct {
+ const char *name; /* Name of the command. */
+ unsigned short alternate; /* Use alternate version of the command. */
+ unsigned short needarg:1; /* Command requires an argument. */
+ unsigned short always:1; /* Command allowed in all conf files. */
+ unsigned short noskip:1; /* Even done in non-active [user] mode. */
+ int (*func)(gnupg_argparse_t *arg,
+ unsigned int alternate, char *args); /*handler*/
+ } cmds[] =
+ {{ "user", 0, 1, 0, 1, handle_meta_user },
+ { "force", 0, 0, 0, 0, handle_meta_force },
+ { "+force", 0, 0, 0, 0, handle_meta_force },
+ { "-force", 1, 0, 0, 0, handle_meta_force },
+ { "ignore", 0, 0, 0, 0, handle_meta_ignore },
+ { "+ignore", 0, 0, 0, 0, handle_meta_ignore },
+ { "-ignore", 1, 0, 0, 0, handle_meta_ignore },
+ { "ignore-all", 2, 0, 0, 0, handle_meta_ignore },
+ { "+ignore-all", 2, 0, 0, 0, handle_meta_ignore },
+ { "verbose", 0, 0, 1, 1, handle_meta_verbose },
+ { "+verbose", 0, 0, 1, 1, handle_meta_verbose },
+ { "-verbose", 1, 0, 1, 1, handle_meta_verbose },
+ { "echo", 0, 1, 1, 1, handle_meta_echo },
+ { "-echo", 1, 1, 1, 1, handle_meta_echo },
+ { "info", 0, 1, 1, 0, handle_meta_echo },
+ { "-info", 1, 1, 1, 0, handle_meta_echo }
+ };
+ char *rest;
+ int i;
+
+ for (rest = keyword; *rest && !(isascii (*rest) && isspace (*rest)); rest++)
+ ;
+ if (*rest)
+ {
+ *rest++ = 0;
+ trim_spaces (rest);
+ }
+
+ for (i=0; i < DIM (cmds); i++)
+ if (!strcmp (cmds[i].name, keyword))
+ break;
+ if (!(i < DIM (cmds)))
+ return ARGPARSE_UNKNOWN_META;
+ if (cmds[i].needarg && !*rest)
+ return ARGPARSE_MISSING_ARG;
+ if (!cmds[i].needarg && *rest)
+ return ARGPARSE_UNEXPECTED_ARG;
+ if (!arg->internal->in_sysconf && !cmds[i].always)
+ return ARGPARSE_UNEXPECTED_META;
+
+ if (!cmds[i].noskip
+ && arg->internal->in_sysconf
+ && arg->internal->user_seen
+ && !arg->internal->user_active)
+ return 0; /* Skip this meta command. */
+
+ return cmds[i].func (arg, cmds[i].alternate, rest);
+}
+
/****************
* Get options from a file.
@@ -573,11 +985,25 @@ 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)
+gnupg_argparse (estream_t fp, gnupg_argparse_t *arg, gnupg_opt_t *opts_orig)
{
- int state, i, c;
- int idx=0;
+ enum { Ainit,
+ Acomment, /* In a comment line. */
+ Acopykeyword, /* Collecting a keyword. */
+ Awaitarg, /* Wait for an argument. */
+ Acopyarg, /* Copy the argument. */
+ Akeyword_eol, /* Got keyword at end of line. */
+ Akeyword_spc, /* Got keyword at space. */
+ Acopymetacmd, /* Copy a meta command. */
+ Askipmetacmd, /* Skip spaces after metacmd. */
+ Askipmetacmd2,/* Skip comment after metacmd. */
+ Ametacmd, /* Process the metacmd. */
+ Askipandleave /* Skip the rest of the line and then leave. */
+ } state;
+ opttable_t *opts;
+ unsigned int nopts;
+ int i, c;
+ int idx = 0;
char keyword[100];
char *buffer = NULL;
size_t buflen = 0;
@@ -585,97 +1011,246 @@ 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_orig)
+ {
+ deinitialize (arg);
+ return 0;
+ }
+
if (!fp) /* Divert to arg_parse() in this case. */
- return arg_parse (arg, opts);
+ return arg_parse (arg, opts_orig, 0);
- initialize (arg, filename, lineno);
+ if (initialize (arg, opts_orig, fp))
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+
+ opts = arg->internal->opts;
+ nopts = arg->internal->nopts;
/* 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)
unread_buf_count = 3;
}
+ arg->internal->opt_flags = 0;
+
/* Find the next keyword. */
- state = i = 0;
+ state = Ainit;
+ i = 0;
for (;;)
{
- if (unread_buf_count)
- c = unread_buf[3 - unread_buf_count--];
- else
- c = getc (fp);
- if (c == '\n' || c== EOF )
+ nextstate:
+ /* Before scanning the next char handle the keyword seen states. */
+ if (state == Akeyword_eol || state == Akeyword_spc)
{
- if ( c != EOF )
- ++*lineno;
- if (state == -1)
- break;
- else if (state == 2)
+ /* We are either at the end of a line or right after a
+ * keyword. In the latter case we need to find the keyword
+ * so that we can decide whether an argument is required. */
+
+ /* Check the keyword. */
+ for (idx=0; idx < nopts; idx++ )
{
- keyword[i] = 0;
- for (i=0; opts[i].short_opt; i++ )
- {
- if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
- break;
- }
- idx = i;
- arg->r_opt = opts[idx].short_opt;
- if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+ if (opts[idx].long_opt && !strcmp (opts[idx].long_opt, keyword))
+ break;
+ }
+ arg->r_opt = opts[idx].short_opt;
+ if (!(idx < nopts))
+ {
+ /* The option (keyword) is not known - check for
+ * internal keywords before returning an error. */
+ if (state == Akeyword_spc && !strcmp (keyword, "alias"))
{
- state = i = 0;
- continue;
+ in_alias = 1;
+ state = Awaitarg;
}
- else if (!opts[idx].short_opt )
+ else if (!strcmp (keyword, "ignore-invalid-option"))
{
- if (!strcmp (keyword, "ignore-invalid-option"))
+ /* We might have keywords as argument - add them to
+ * the list of ignored keywords. Note that we
+ * ignore empty argument lists and thus do not to
+ * call the function in the Akeyword_eol state. */
+ if (state == Akeyword_spc)
{
- /* No argument - ignore this meta option. */
- state = i = 0;
- continue;
- }
- else if (ignore_invalid_option_p (arg, keyword))
- {
- /* This invalid option is in the iio list. */
- state = i = 0;
- continue;
+ if (ignore_invalid_option_add (arg, fp))
+ {
+ arg->r_opt = ARGPARSE_OUT_OF_CORE;
+ goto leave;
+ }
+ arg->lineno++;
}
+ state = Ainit;
+ i = 0;
+ }
+ else if (ignore_invalid_option_p (arg, keyword))
+ {
+ /* This invalid option is already in the iio list. */
+ state = state == Akeyword_eol? Ainit : Acomment;
+ i = 0;
+ }
+ else
+ {
arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
? ARGPARSE_INVALID_COMMAND
: ARGPARSE_INVALID_OPTION);
+ if (state == Akeyword_spc)
+ state = Askipandleave;
+ else
+ goto leave;
}
- else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
- arg->r_type = 0; /* Does not take an arg. */
+ }
+ else if (state == Akeyword_spc)
+ {
+ /* Known option but need to scan for args. */
+ state = Awaitarg;
+ }
+ else if (arg->internal->in_sysconf
+ && arg->internal->user_seen
+ && !arg->internal->user_active)
+ {
+ /* We are in a [user] meta command and it is not active.
+ * Skip the command. */
+ state = state == Akeyword_eol? Ainit : Acomment;
+ i = 0;
+ }
+ else if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
+ {
+ /* Known option is configured to be ignored. Start from
+ * scratch (new line) or process like a comment. */
+ state = state == Akeyword_eol? Ainit : Acomment;
+ i = 0;
+ }
+ else /* Known option */
+ {
+ int set_ignore = 0;
+
+ if (arg->internal->in_sysconf)
+ {
+ /* Set the current forced and ignored attributes. */
+ if (arg->internal->mark_forced)
+ opts[idx].forced = 1;
+ if (arg->internal->mark_ignore)
+ opts[idx].ignore = 1;
+ if (arg->internal->explicit_ignore)
+ opts[idx].explicit_ignore = 1;
+ }
+ else /* Non-sysconf file */
+ { /* Act upon the forced and ignored attributes. */
+ if (opts[idx].ignore || opts[idx].forced)
+ {
+ if (arg->internal->verbose)
+ log_info ("%s:%u: ignoring option \"--%s\""
+ " due to attributes:%s%s\n",
+ arg->internal->confname,
+ arg->lineno,
+ opts[idx].long_opt,
+ opts[idx].forced? " forced":"",
+ opts[idx].ignore? " ignore":"");
+ if ((arg->flags & ARGPARSE_FLAG_WITHATTR))
+ set_ignore = 1;
+ else
+ {
+ state = Ainit;
+ i = 0;
+ goto nextstate; /* Ignore this one. */
+ }
+ }
+ }
+
+ arg->r_opt = opts[idx].short_opt;
+ if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
+ arg->r_type = ARGPARSE_TYPE_NONE; /* Does not take an arg. */
else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) )
- arg->r_type = 0; /* Arg is optional. */
+ arg->r_type = ARGPARSE_TYPE_NONE; /* Arg is optional. */
else
arg->r_opt = ARGPARSE_MISSING_ARG;
- break;
+ /* If the caller wants us to return the attributes or
+ * ignored options, or the flags in. */
+ if ((arg->flags & ARGPARSE_FLAG_WITHATTR))
+ {
+ if (opts[idx].ignore)
+ arg->r_type |= ARGPARSE_ATTR_IGNORE;
+ if (opts[idx].forced)
+ arg->r_type |= ARGPARSE_ATTR_FORCE;
+ if (set_ignore)
+ arg->r_type |= ARGPARSE_OPT_IGNORE;
+ }
+
+ goto leave;
+ }
+ } /* (end state Akeyword_eol/Akeyword_spc) */
+ else if (state == Ametacmd)
+ {
+ /* We are at the end of a line. */
+ log_assert (*keyword == '[');
+ trim_spaces (keyword+1);
+ if (!keyword[1])
+ {
+ arg->r_opt = ARGPARSE_INVALID_META; /* Empty. */
+ goto leave;
+ }
+ c = handle_metacmd (arg, keyword+1);
+ if (c)
+ {
+ arg->r_opt = c; /* Return error. */
+ goto leave;
+ }
+ state = Ainit;
+ i = 0;
+ }
+
+ /* Get the next character from the line. */
+ if (unread_buf_count)
+ c = unread_buf[3 - unread_buf_count--];
+ else
+ c = gpgrt_fgetc (fp);
+
+ if (c == '\n' || c== EOF )
+ { /* Handle end of line. */
+ if ( c != EOF )
+ arg->lineno++;
+ if (state == Askipandleave)
+ goto leave;
+ else if (state == Acopykeyword)
+ {
+ keyword[i] = 0;
+ state = Akeyword_eol;
+ goto nextstate;
+ }
+ else if (state == Acopymetacmd)
+ {
+ arg->r_opt = ARGPARSE_INVALID_META; /* "]" missing */
+ goto leave;
}
- else if (state == 3)
+ else if (state == Askipmetacmd || state == Askipmetacmd2)
{
- /* No argument found. */
+ state = Ametacmd;
+ goto nextstate;
+ }
+ else if (state == Awaitarg)
+ {
+ /* No argument found at the end of the line. */
if (in_alias)
arg->r_opt = ARGPARSE_MISSING_ARG;
else if (!(opts[idx].flags & ARGPARSE_TYPE_MASK))
- arg->r_type = 0; /* Does not take an arg. */
+ arg->r_type = ARGPARSE_TYPE_NONE; /* Does not take an arg. */
else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL))
- arg->r_type = 0; /* No optional argument. */
+ arg->r_type = ARGPARSE_TYPE_NONE; /* No optional argument. */
else
arg->r_opt = ARGPARSE_MISSING_ARG;
- break;
+ goto leave;
}
- else if (state == 4)
+ else if (state == Acopyarg)
{
- /* Has an argument. */
+ /* Has an argument at the end of a line. */
if (in_alias)
{
if (!buffer)
@@ -735,82 +1310,61 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
gpgrt_annotate_leaked_object (buffer);
}
}
- break;
+ goto leave;
}
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. */
- break;
+ goto leave;
}
- state = 0;
+ state = Ainit;
i = 0;
- }
- else if (state == -1)
+ } /* (end handle end of line) */
+ else if (state == Askipandleave)
; /* Skip. */
- else if (state == 0 && isascii (c) && isspace(c))
+ else if (state == Ainit && isascii (c) && isspace(c))
; /* Skip leading white space. */
- else if (state == 0 && c == '#' )
- state = 1; /* Start of a comment. */
- else if (state == 1)
+ else if (state == Ainit && c == '#' )
+ state = Acomment; /* Start of a comment. */
+ else if (state == Acomment || state == Askipmetacmd2)
; /* Skip comments. */
- else if (state == 2 && isascii (c) && isspace(c))
+ else if (state == Askipmetacmd)
{
- /* Check keyword. */
- keyword[i] = 0;
- for (i=0; opts[i].short_opt; i++ )
- if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
- break;
- idx = i;
- arg->r_opt = opts[idx].short_opt;
- if ((opts[idx].flags & ARGPARSE_OPT_IGNORE))
- {
- state = 1; /* Process like a comment. */
- }
- else if (!opts[idx].short_opt)
+ if (c == '#')
+ state = Askipmetacmd2;
+ else if (!(isascii (c) && isspace(c)))
{
- if (!strcmp (keyword, "alias"))
- {
- in_alias = 1;
- state = 3;
- }
- else if (!strcmp (keyword, "ignore-invalid-option"))
- {
- if (ignore_invalid_option_add (arg, fp))
- {
- arg->r_opt = ARGPARSE_OUT_OF_CORE;
- break;
- }
- state = i = 0;
- ++*lineno;
- }
- else if (ignore_invalid_option_p (arg, keyword))
- state = 1; /* Process like a comment. */
- else
- {
- arg->r_opt = ((opts[idx].flags & ARGPARSE_OPT_COMMAND)
- ? ARGPARSE_INVALID_COMMAND
- : ARGPARSE_INVALID_OPTION);
- state = -1; /* Skip rest of line and leave. */
- }
+ arg->r_opt = ARGPARSE_INVALID_META;
+ state = Askipandleave;
}
- else
- state = 3;
}
- else if (state == 3)
+ else if (state == Acopykeyword && isascii (c) && isspace(c))
+ {
+ keyword[i] = 0;
+ state = Akeyword_spc;
+ goto nextstate;
+ }
+ else if (state == Acopymetacmd && c == ']')
+ {
+ keyword[i] = 0;
+ state = Askipmetacmd;
+ goto nextstate;
+ }
+ else if (state == Awaitarg)
{
/* Skip leading spaces of the argument. */
if (!isascii (c) || !isspace(c))
{
i = 0;
keyword[i++] = c;
- state = 4;
+ state = Acopyarg;
}
}
- else if (state == 4)
+ else if (state == Acopyarg)
{
/* Collect the argument. */
if (buffer)
@@ -833,7 +1387,7 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
{
xfree (buffer);
arg->r_opt = ARGPARSE_OUT_OF_CORE;
- break;
+ goto leave;
}
}
}
@@ -852,130 +1406,554 @@ optfile_parse (FILE *fp, const char *filename, unsigned *lineno,
else
{
arg->r_opt = ARGPARSE_OUT_OF_CORE;
- break;
+ goto leave;
}
}
}
else if (i >= DIM(keyword)-1)
{
arg->r_opt = ARGPARSE_KEYWORD_TOO_LONG;
- state = -1; /* Skip rest of line and leave. */
+ state = Askipandleave; /* Skip rest of line and leave. */
+ }
+ else if (!i)
+ {
+ state = c == '[' ? Acopymetacmd : Acopykeyword;
+ keyword[i++] = c;
}
else
{
keyword[i++] = c;
- state = 2;
}
}
+ leave:
return arg->r_opt;
}
+/* Return true if the list of options OPTS has any option marked with
+ * ARGPARSE_OPT_CONFFILE. */
+static int
+any_opt_conffile (opttable_t *opts, unsigned int nopts)
+{
+ int i;
+ for (i=0; i < nopts; i++ )
+ if ((opts[i].flags & ARGPARSE_OPT_CONFFILE))
+ return 1;
+ return 0;
+}
+
+
+/* Return true if FNAME is an absolute filename. */
static int
-find_long_option( ARGPARSE_ARGS *arg,
- ARGPARSE_OPTS *opts, const char *keyword )
+is_absfname (const char *fname)
{
- int i;
- size_t n;
-
- (void)arg;
-
- /* Would be better if we can do a binary search, but it is not
- possible to reorder our option table because we would mess
- up our help strings - What we can do is: Build a nice option
- lookup table when this function is first invoked */
- if( !*keyword )
- return -1;
- for(i=0; opts[i].short_opt; i++ )
- if( opts[i].long_opt && !strcmp( opts[i].long_opt, keyword) )
- return i;
-#if 0
+ const char *s;
+
+#ifdef HAVE_W32_SYSTEM
+ s = strchr (fname, ':');
+ if (s)
+ s++;
+ else
+ s = fname;
+#else
+ s = fname;
+#endif
+
+ return (*s == '/'
+#ifdef HAVE_W32_SYSTEM
+ || *s == DIRSEP_C
+#endif
+ );
+}
+
+
+/* If FNAME specifies two files of the form
+ * NAME1:/NAME2 (Unix)
+ * or
+ * NAME1;[x:]/NAME2 (Windows)
+ * return a pointer to the delimiter or NULL if there is none.
+ */
+static const char *
+is_twopartfname (const char *fname)
+{
+ const char *s;
+
+ if ((s = strchr (fname, PATHSEP_C)) && is_absfname (s+1) && s != fname)
+ return s;
+ return NULL;
+}
+
+
+/* Try to use a version-ed config file name. A version-ed config file
+ * name is one which has the packages version number appended. For
+ * example if the standard config file name is "foo.conf" and the
+ * version of the foo program is 1.2.3-beta1 the following config
+ * files are tried in order until one is readable:
+ *
+ * foo.conf-1.2.3-beta1
+ * foo.conf-1.2.3
+ * foo.conf-1.2
+ * foo.conf-1
+ * foo.conf
+ *
+ * The argument CONFIGNAME should already be expanded. On success a
+ * newly allocated file name is returned. On error NULL is returned.
+ */
+static char *
+try_versioned_conffile (const char *configname)
+{
+ const char *version = strusage (13);
+ char *name;
+ char *dash, *endp;
+
+ if (!version || !*version)
+ return NULL; /* No program version known. */
+
+ name = strconcat (configname, "-", version, NULL);
+ if (!name)
+ return NULL; /* Oops: Out of core - ignore. */
+ dash = name + strlen (configname);
+
+ endp = dash + strlen (dash) - 1;
+ while (endp > dash)
{
- ALIAS_DEF a;
- /* see whether it is an alias */
- 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;
- return -3; /* alias available */
- }
- }
+ if (!gnupg_access (name, R_OK))
+ {
+ return name;
+ }
+ for (; endp > dash; endp--)
+ {
+ if (*endp == '-' || *endp == '.')
+ {
+ *endp = 0;
+ break;
+ }
+ }
}
-#endif
- /* not found, see whether it is an abbreviation */
- /* aliases may not be abbreviated */
- n = strlen( keyword );
- for(i=0; opts[i].short_opt; i++ ) {
- if( opts[i].long_opt && !strncmp( opts[i].long_opt, keyword, n ) ) {
- int j;
- for(j=i+1; opts[j].short_opt; j++ ) {
- if( opts[j].long_opt
- && !strncmp( opts[j].long_opt, keyword, n )
- && !(opts[j].short_opt == opts[i].short_opt
- && opts[j].flags == opts[i].flags ) )
- return -2; /* abbreviation is ambiguous */
+
+ xfree (name);
+ return NULL;
+}
+
+
+/* This function is called after a sysconf file has been read. */
+static void
+finish_read_sys (gnupg_argparse_t *arg)
+{
+ opttable_t *opts = arg->internal->opts;
+ unsigned int nopts = arg->internal->nopts;
+ int i;
+
+ if (arg->internal->ignore_all_seen)
+ {
+ /* [ignore-all] was used: Set all options which have not
+ * explictly been set as ignore or not ignore to ignore. */
+ for (i = 0; i < nopts; i++)
+ {
+ if (!opts[i].explicit_ignore)
+ opts[i].ignore = 1;
+ }
+ }
+
+ /* Reset all flags which pertain only to sysconf files. */
+ arg->internal->in_sysconf = 0;
+ arg->internal->user_active = 0;
+ arg->internal->mark_forced = 0;
+ arg->internal->mark_ignore = 0;
+ arg->internal->explicit_ignore = 0;
+ arg->internal->ignore_all_seen = 0;
+}
+
+/* The full arg parser which handles option files and command line
+ * arguments. The behaviour depends on the combinations of CONFNAME
+ * and the ARGPARSE_FLAG_xxx values:
+ *
+ * | CONFNAME | SYS | USER | Action |
+ * |----------+-----+------+--------------------|
+ * | NULL | - | - | cmdline |
+ * | string | 0 | 1 | user, cmdline |
+ * | string | 1 | 0 | sys, cmdline |
+ * | string | 1 | 1 | sys, user, cmdline |
+ *
+ * Note that if an option has been flagged with ARGPARSE_OPT_CONFFILE
+ * and a type of ARGPARSE_TYPE_STRING that option is not returned but
+ * the specified configuration file is processed directly; if
+ * ARGPARSE_TYPE_NONE is used no user configuration files are
+ * processed and from the system configuration files only those which
+ * are immutable are processed. The string values for CONFNAME shall
+ * not include a directory part because that is taken from the values
+ * set by gnupg_set_confdir. However, if CONFNAME is a twopart
+ * filename delimited by a colon (semicolon on Windows) with the
+ * second part being an absolute filename, the first part is used for
+ * the SYS file and the the entire second part for the USER file.
+ */
+int
+gnupg_argparser (gnupg_argparse_t *arg, gnupg_opt_t *opts,
+ const char *confname)
+{
+ /* First check whether releasing the resources has been requested. */
+ if (arg && !opts)
+ {
+ deinitialize (arg);
+ return 0;
+ }
+
+ /* Make sure that the internal data object is ready and also print
+ * warnings or errors from the last iteration. */
+ if (initialize (arg, opts, NULL))
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+
+ next_state:
+ switch (arg->internal->state)
+ {
+ case STATE_init:
+ if (arg->argc && arg->argv && *arg->argc
+ && any_opt_conffile (arg->internal->opts, arg->internal->nopts))
+ {
+ /* The list of option allow for conf files
+ * (e.g. gpg's "--option FILE" and "--no-options")
+ * Now check whether one was really given on the command
+ * line. Note that we don't need to run this code if no
+ * argument array was provided. */
+ int save_argc = *arg->argc;
+ char **save_argv = *arg->argv;
+ unsigned int save_flags = arg->flags;
+ int save_idx = arg->internal->idx;
+ int any_no_conffile = 0;
+
+ arg->flags = (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION
+ | ARGPARSE_FLAG__INITIALIZED);
+ while (arg_parse (arg, opts, 1))
+ {
+ if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE))
+ {
+ arg->internal->explicit_confopt = 1;
+ if ((arg->r_type & ARGPARSE_TYPE_MASK) == ARGPARSE_TYPE_STRING
+ && !arg->internal->explicit_conffile)
+ {
+ /* Store the first conffile name. All further
+ * conf file options are not handled. */
+ arg->internal->explicit_conffile
+ = xtrystrdup (arg->r.ret_str);
+ if (!arg->internal->explicit_conffile)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+
+ }
+ else if ((arg->r_type & ARGPARSE_TYPE_MASK)
+ == ARGPARSE_TYPE_NONE)
+ any_no_conffile = 1;
+ }
+ }
+ if (any_no_conffile)
+ {
+ /* A NoConffile option overrides any other conf file option. */
+ xfree (arg->internal->explicit_conffile);
+ arg->internal->explicit_conffile = NULL;
+ }
+ /* Restore parser. */
+ *arg->argc = save_argc;
+ *arg->argv = save_argv;
+ arg->flags = save_flags;
+ arg->internal->idx = save_idx;
+ }
+
+ if (confname && *confname)
+ {
+ if ((arg->flags & ARGPARSE_FLAG_SYS))
+ arg->internal->state = STATE_open_sys;
+ else if ((arg->flags & ARGPARSE_FLAG_USER))
+ arg->internal->state = STATE_open_user;
+ else
+ return (arg->r_opt = ARGPARSE_INVALID_ARG);
+ }
+ else
+ arg->internal->state = STATE_open_cmdline;
+ goto next_state;
+
+ case STATE_open_sys:
+ {
+ /* If it is a two part name take the first part. */
+ const char *s;
+ char *tmpname = NULL;
+
+ if ((s = is_twopartfname (confname)))
+ {
+ tmpname = xtrymalloc (s - confname + 1);
+ if (!tmpname)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+ memcpy (tmpname, confname, s-confname);
+ tmpname[s-confname] = 0;
+ s = tmpname;
+ }
+ else
+ s = confname;
+ xfree (arg->internal->confname);
+ arg->internal->confname = make_filename_try
+ (confdir.sys? confdir.sys : "/etc", s, NULL);
+ xfree (tmpname);
+ if (!arg->internal->confname)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+ }
+ arg->lineno = 0;
+ arg->internal->idx = 0;
+ arg->internal->verbose = 0;
+ arg->internal->stopped = 0;
+ arg->internal->inarg = 0;
+ gpgrt_fclose (arg->internal->conffp);
+ arg->internal->conffp = gpgrt_fopen (arg->internal->confname, "r");
+ if (!arg->internal->conffp)
+ {
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose)
+ log_info (_("Note: no default option file '%s'\n"),
+ arg->internal->confname);
+ if ((arg->flags & ARGPARSE_FLAG_USER))
+ arg->internal->state = STATE_open_user;
+ else
+ arg->internal->state = STATE_open_cmdline;
+ goto next_state;
+ }
+
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose)
+ log_info (_("reading options from '%s'\n"),
+ arg->internal->confname);
+ arg->internal->state = STATE_read_sys;
+ arg->internal->in_sysconf = 1;
+ arg->r.ret_str = xtrystrdup (arg->internal->confname);
+ if (!arg->r.ret_str)
+ arg->r_opt = ARGPARSE_OUT_OF_CORE;
+ else
+ {
+ gpgrt_annotate_leaked_object (arg->r.ret_str);
+ arg->r_opt = ARGPARSE_CONFFILE;
+ arg->r_type = ARGPARSE_TYPE_STRING;
+ }
+ break;
+
+ case STATE_open_user:
+ if (arg->internal->explicit_confopt
+ && arg->internal->explicit_conffile)
+ {
+ /* An explict option to use a specific configuration file
+ * has been given - use that one. */
+ xfree (arg->internal->confname);
+ arg->internal->confname
+ = xtrystrdup (arg->internal->explicit_conffile);
+ if (!arg->internal->confname)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+ }
+ else if (arg->internal->explicit_confopt)
+ {
+ /* An explict option not to use a configuration file has
+ * been given - leap direct to command line reading. */
+ arg->internal->state = STATE_open_cmdline;
+ goto next_state;
+ }
+ else
+ {
+ /* Use the standard configure file. If it is a two part
+ * name take the second part. If it is the standard name
+ * and ARGPARSE_FLAG_USERVERS is set try versioned config
+ * files. */
+ const char *s;
+ char *nconf;
+
+ xfree (arg->internal->confname);
+ if ((s = is_twopartfname (confname)))
+ {
+ arg->internal->confname = make_filename_try (s + 1, NULL);
+ if (!arg->internal->confname)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+ }
+ else
+ {
+ arg->internal->confname = make_filename_try
+ (confdir.user? confdir.user : "~/.config", confname, NULL);
+ if (!arg->internal->confname)
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
+ if ((arg->flags & ARGPARSE_FLAG_USERVERS)
+ && (nconf = try_versioned_conffile (arg->internal->confname)))
+ {
+ xfree (arg->internal->confname);
+ arg->internal->confname = nconf;
+ }
+ }
+ }
+ arg->lineno = 0;
+ arg->internal->idx = 0;
+ arg->internal->verbose = 0;
+ arg->internal->stopped = 0;
+ arg->internal->inarg = 0;
+ arg->internal->in_sysconf = 0;
+ gpgrt_fclose (arg->internal->conffp);
+ arg->internal->conffp = gpgrt_fopen (arg->internal->confname, "r");
+ if (!arg->internal->conffp)
+ {
+ arg->internal->state = STATE_open_cmdline;
+ if (arg->internal->explicit_confopt)
+ {
+ log_error (_("option file '%s': %s\n"),
+ arg->internal->confname, strerror (errno));
+ return (arg->r_opt = ARGPARSE_NO_CONFFILE);
+ }
+ else
+ {
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE)
+ || arg->internal->verbose)
+ log_info (_("Note: no default option file '%s'\n"),
+ arg->internal->confname);
+ goto next_state;
+ }
+ }
+
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose)
+ log_info (_("reading options from '%s'\n"),
+ arg->internal->confname);
+ arg->internal->state = STATE_read_user;
+ arg->r.ret_str = xtrystrdup (arg->internal->confname);
+ if (!arg->r.ret_str)
+ arg->r_opt = ARGPARSE_OUT_OF_CORE;
+ else
+ {
+ gpgrt_annotate_leaked_object (arg->r.ret_str);
+ arg->r_opt = ARGPARSE_CONFFILE;
+ arg->r_type = ARGPARSE_TYPE_STRING;
+ }
+ break;
+
+ case STATE_open_cmdline:
+ gpgrt_fclose (arg->internal->conffp);
+ arg->internal->conffp = NULL;
+ xfree (arg->internal->confname);
+ arg->internal->confname = NULL;
+ arg->internal->idx = 0;
+ arg->internal->verbose = 0;
+ arg->internal->stopped = 0;
+ arg->internal->inarg = 0;
+ arg->internal->in_sysconf = 0;
+ if (!arg->argc || !arg->argv || !*arg->argv)
+ {
+ /* No or empty argument vector - don't bother to parse things. */
+ arg->internal->state = STATE_finished;
+ goto next_state;
+ }
+ arg->r_opt = ARGPARSE_CONFFILE;
+ arg->r_type = ARGPARSE_TYPE_NONE;
+ arg->r.ret_str = NULL;
+ arg->internal->state = STATE_read_cmdline;
+ break;
+
+ case STATE_read_sys:
+ arg->r_opt = gnupg_argparse (arg->internal->conffp, arg, opts);
+ if (!arg->r_opt)
+ {
+ finish_read_sys (arg);
+ arg->internal->state = STATE_open_user;
+ goto next_state;
+ }
+ if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE))
+ goto next_state; /* Already handled - again. */
+ break;
+
+ case STATE_read_user:
+ arg->r_opt = gnupg_argparse (arg->internal->conffp, arg, opts);
+ if (!arg->r_opt)
+ {
+ arg->internal->state = STATE_open_cmdline;
+ goto next_state;
+ }
+ if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE))
+ goto next_state; /* Already handled - again. */
+ break;
+
+ case STATE_read_cmdline:
+ arg->r_opt = arg_parse (arg, opts, 1);
+ if (!arg->r_opt)
+ {
+ arg->internal->state = STATE_finished;
+ goto next_state;
+ }
+ if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE))
+ goto next_state; /* Already handled - again. */
+ break;
+
+ case STATE_finished:
+ arg->r_opt = 0;
+ break;
+ }
+
+ return arg->r_opt;
+}
+
+
+
+/* Given the list of options in ARG and a keyword, return the index of
+ * the long option matching KEYWORD. On error -1 is returned for not
+ * found or -2 for ambigious keyword. */
+static int
+find_long_option (gnupg_argparse_t *arg, const char *keyword)
+{
+ int i;
+ size_t n;
+ opttable_t *opts = arg->internal->opts;
+ unsigned int nopts = arg->internal->nopts;
+
+ /* Would be better if we can do a binary search, but it is not
+ * possible to reorder our option table because we would mess up our
+ * help strings. What we can do is: Build an option lookup table
+ * when this function is first invoked. The latter has already been
+ * done. */
+ if (!*keyword)
+ return -1;
+ for (i=0; i < nopts; i++ )
+ if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword))
+ return i;
+ /* Not found. See whether it is an abbreviation. Aliases may not
+ * be abbreviated, though. */
+ n = strlen (keyword);
+ for (i=0; i < nopts; i++)
+ {
+ if (opts[i].long_opt && !strncmp (opts[i].long_opt, keyword, n))
+ {
+ int j;
+ for (j=i+1; j < nopts; j++)
+ {
+ if (opts[j].long_opt
+ && !strncmp (opts[j].long_opt, keyword, n)
+ && !(opts[j].short_opt == opts[i].short_opt
+ && opts[j].flags == opts[i].flags ) )
+ return -2; /* Abbreviation is ambiguous. */
}
- return i;
+ return i;
}
}
- return -1; /* Not found. */
+ return -1; /* Not found. */
}
-int
-arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
+
+/* The option parser for command line options. */
+static int
+arg_parse (gnupg_argparse_t *arg, gnupg_opt_t *opts_orig, int no_init)
{
int idx;
+ opttable_t *opts;
+ unsigned int nopts;
int argc;
char **argv;
char *s, *s2;
int i;
- /* Fill in missing standard options: help, version, warranty and
- * dump-options. */
- ARGPARSE_OPTS help_opt
- = ARGPARSE_s_n (ARGPARSE_SHORTOPT_HELP, "help", "@");
- ARGPARSE_OPTS version_opt
- = ARGPARSE_s_n (ARGPARSE_SHORTOPT_VERSION, "version", "@");
- ARGPARSE_OPTS warranty_opt
- = ARGPARSE_s_n (ARGPARSE_SHORTOPT_WARRANTY, "warranty", "@");
- ARGPARSE_OPTS dump_options_opt
- = ARGPARSE_s_n(ARGPARSE_SHORTOPT_DUMP_OPTIONS, "dump-options", "@");
- int seen_help = 0;
- int seen_version = 0;
- int seen_warranty = 0;
- int seen_dump_options = 0;
+ if (no_init)
+ ;
+ else if (initialize (arg, opts_orig, NULL))
+ return (arg->r_opt = ARGPARSE_OUT_OF_CORE);
- i = 0;
- while (opts[i].short_opt)
- {
- if (opts[i].long_opt)
- {
- if (!strcmp(opts[i].long_opt, help_opt.long_opt))
- seen_help = 1;
- else if (!strcmp(opts[i].long_opt, version_opt.long_opt))
- seen_version = 1;
- else if (!strcmp(opts[i].long_opt, warranty_opt.long_opt))
- seen_warranty = 1;
- else if (!strcmp(opts[i].long_opt, dump_options_opt.long_opt))
- seen_dump_options = 1;
- }
- i++;
- }
- if (! seen_help)
- opts[i++] = help_opt;
- if (! seen_version)
- opts[i++] = version_opt;
- if (! seen_warranty)
- opts[i++] = warranty_opt;
- if (! seen_dump_options)
- opts[i++] = dump_options_opt;
-
- initialize( arg, NULL, NULL );
+ opts = arg->internal->opts;
+ nopts = arg->internal->nopts;
argc = *arg->argc;
argv = *arg->argv;
- idx = arg->internal.idx;
+ idx = arg->internal->idx;
if (!idx && argc && !(arg->flags & ARGPARSE_FLAG_ARG0))
{
@@ -984,24 +1962,24 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
}
next_one:
- if (!argc)
+ if (!argc || (s = *argv) == NULL)
{
/* No more args. */
arg->r_opt = 0;
goto leave; /* Ready. */
}
- s = *argv;
- arg->internal.last = s;
+ arg->internal->last = s;
+ arg->internal->opt_flags = 0;
- 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_type = ARGPARSE_TYPE_STRING;
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. */
@@ -1011,11 +1989,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;
@@ -1024,33 +2002,38 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
argpos = strchr( s+2, '=' );
if ( argpos )
*argpos = 0;
- i = find_long_option ( arg, opts, s+2 );
+ i = find_long_option (arg, s+2);
if ( argpos )
*argpos = '=';
if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_HELP)
- show_help (opts, arg->flags);
+ {
+ show_help (opts, nopts, arg->flags);
+ my_exit (arg, 0);
+ }
else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_VERSION)
{
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);
+ my_exit (arg, 0);
}
+ else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTTBL)
+ dump_option_table (arg);
else if (i > 0 && opts[i].short_opt == ARGPARSE_SHORTOPT_DUMP_OPTIONS)
{
- for (i=0; opts[i].short_opt; i++ )
+ for (i=0; i < nopts; i++ )
{
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 )
@@ -1062,6 +2045,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
}
else
arg->r_opt = opts[i].short_opt;
+
if ( i < 0 )
;
else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
@@ -1074,6 +2058,7 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
}
else
s2 = argv[1];
+
if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
{
arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */
@@ -1105,125 +2090,157 @@ arg_parse( ARGPARSE_ARGS *arg, ARGPARSE_OPTS *opts)
if ( argpos )
arg->r_type = ARGPARSE_UNEXPECTED_ARG;
else
- arg->r_type = 0;
+ {
+ arg->internal->opt_flags = opts[i].flags;
+ arg->r_type = ARGPARSE_TYPE_NONE;
+ }
}
argc--; argv++; idx++; /* Set to next one. */
}
- else if ( (*s == '-' && s[1]) || arg->internal.inarg )
- {
- /* Short option. */
- int dash_kludge = 0;
+ else if ( (*s == '-' && s[1]) || arg->internal->inarg )
+ {
+ /* Short option. */
+ int dash_kludge = 0;
- i = 0;
- if ( !arg->internal.inarg )
- {
- arg->internal.inarg++;
- if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
- {
- for (i=0; opts[i].short_opt; i++ )
- if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
- {
- dash_kludge = 1;
- break;
- }
- }
- }
- s += arg->internal.inarg;
+ i = 0;
+ if ( !arg->internal->inarg )
+ {
+ arg->internal->inarg++;
+ if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) )
+ {
+ for (i=0; i < nopts; i++ )
+ if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1))
+ {
+ dash_kludge = 1;
+ break;
+ }
+ }
+ }
+ s += arg->internal->inarg;
- if (!dash_kludge )
- {
- for (i=0; opts[i].short_opt; i++ )
- if ( opts[i].short_opt == *s )
- break;
- }
+ if (!dash_kludge )
+ {
+ for (i=0; i < nopts; i++ )
+ if ( opts[i].short_opt == *s )
+ break;
+ }
- if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
- show_help (opts, arg->flags);
+ if ( !opts[i].short_opt && ( *s == 'h' || *s == '?' ) )
+ {
+ show_help (opts, nopts, arg->flags);
+ my_exit (arg, 0);
+ }
- arg->r_opt = opts[i].short_opt;
- if (!opts[i].short_opt )
- {
- arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
- ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
- arg->internal.inarg++; /* Point to the next arg. */
- arg->r.ret_str = s;
- }
- else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
- {
- if ( s[1] && !dash_kludge )
- {
- s2 = s+1;
- set_opt_arg (arg, opts[i].flags, s2);
- }
- else
- {
- s2 = argv[1];
- if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
- {
- arg->r_type = ARGPARSE_TYPE_NONE;
- }
- else if ( !s2 )
- {
- arg->r_opt = ARGPARSE_MISSING_ARG;
- }
- else if ( *s2 == '-' && s2[1]
- && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
- {
- /* The argument is optional and the next seems to
- be an option. We do not check this possible
- option but assume no argument. */
- arg->r_type = ARGPARSE_TYPE_NONE;
- }
- else
- {
- set_opt_arg (arg, opts[i].flags, s2);
- argc--; argv++; idx++; /* Skip one. */
- }
- }
- s = "x"; /* This is so that !s[1] yields false. */
- }
- else
- {
- /* Does not take an argument. */
- arg->r_type = ARGPARSE_TYPE_NONE;
- arg->internal.inarg++; /* Point to the next arg. */
- }
- if ( !s[1] || dash_kludge )
- {
- /* No more concatenated short options. */
- arg->internal.inarg = 0;
- argc--; argv++; idx++;
- }
- }
+ arg->r_opt = opts[i].short_opt;
+ if (!opts[i].short_opt )
+ {
+ arg->r_opt = (opts[i].flags & ARGPARSE_OPT_COMMAND)?
+ ARGPARSE_INVALID_COMMAND:ARGPARSE_INVALID_OPTION;
+ arg->internal->inarg++; /* Point to the next arg. */
+ arg->r.ret_str = s;
+ }
+ else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) )
+ {
+ if ( s[1] && !dash_kludge )
+ {
+ s2 = s+1;
+ set_opt_arg (arg, opts[i].flags, s2);
+ }
+ else
+ {
+ s2 = argv[1];
+ if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+ {
+ arg->r_type = ARGPARSE_TYPE_NONE;
+ arg->internal->opt_flags = opts[i].flags;
+ }
+ else if ( !s2 )
+ {
+ arg->r_opt = ARGPARSE_MISSING_ARG;
+ }
+ else if ( *s2 == '-' && s2[1]
+ && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) )
+ {
+ /* The argument is optional and the next seems to
+ be an option. We do not check this possible
+ option but assume no argument. */
+ arg->r_type = ARGPARSE_TYPE_NONE;
+ arg->internal->opt_flags = opts[i].flags;
+ }
+ else
+ {
+ set_opt_arg (arg, opts[i].flags, s2);
+ argc--; argv++; idx++; /* Skip one. */
+ }
+ }
+ s = "x"; /* This is so that !s[1] yields false. */
+ }
+ else
+ {
+ /* Does not take an argument. */
+ arg->r_type = ARGPARSE_TYPE_NONE;
+ arg->internal->opt_flags = opts[i].flags;
+ arg->internal->inarg++; /* Point to the next arg. */
+ }
+ if ( !s[1] || dash_kludge )
+ {
+ /* No more concatenated short options. */
+ arg->internal->inarg = 0;
+ argc--; argv++; idx++;
+ }
+ }
else if ( arg->flags & ARGPARSE_FLAG_MIXED )
{
arg->r_opt = ARGPARSE_IS_ARG;
- arg->r_type = 2;
+ arg->r_type = ARGPARSE_TYPE_STRING;
arg->r.ret_str = s;
argc--; argv++; idx++; /* Set to next one. */
}
else
{
- arg->internal.stopped = 1; /* Stop option processing. */
+ arg->internal->stopped = 1; /* Stop option processing. */
goto next_one;
}
+ if (arg->r_opt > 0 && i >= 0 && i < nopts
+ && ((opts[i].ignore && opts[i].explicit_ignore) || opts[i].forced))
+ {
+
+ if ((arg->flags & ARGPARSE_FLAG_WITHATTR))
+ {
+ if (opts[i].ignore)
+ arg->r_type |= ARGPARSE_ATTR_IGNORE;
+ if (opts[i].forced)
+ arg->r_type |= ARGPARSE_ATTR_FORCE;
+ arg->r_type |= ARGPARSE_OPT_IGNORE;
+ }
+ else
+ {
+ log_info (_("Note: ignoring option \"--%s\""
+ " due to global config\n"),
+ opts[i].long_opt);
+ goto next_one; /* Skip ignored/forced option. */
+ }
+ }
+
leave:
*arg->argc = argc;
*arg->argv = argv;
- arg->internal.idx = idx;
+ arg->internal->idx = idx;
return arg->r_opt;
}
+
/* 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 (gnupg_argparse_t *arg, unsigned flags, char *s)
{
int base = (flags & ARGPARSE_OPT_PREFIX)? 0 : 10;
long l;
+ arg->internal->opt_flags = flags;
switch ( (arg->r_type = (flags & ARGPARSE_TYPE_MASK)) )
{
case ARGPARSE_TYPE_LONG:
@@ -1272,8 +2289,10 @@ set_opt_arg (ARGPARSE_ARGS *arg, unsigned flags, char *s)
}
+/* Return the length of the option O. This needs to consider the
+ * description as well as the option name. */
static size_t
-long_opt_strlen( ARGPARSE_OPTS *o )
+long_opt_strlen (opttable_t *o)
{
size_t n = strlen (o->long_opt);
@@ -1286,8 +2305,8 @@ long_opt_strlen( ARGPARSE_OPTS *o )
if ( *s != '=' )
n++;
/* For a (mostly) correct length calculation we exclude
- continuation bytes (10xxxxxx) if we are on a native utf8
- terminal. */
+ * continuation bytes (10xxxxxx) if we are on a native utf8
+ * terminal. */
for (; *s && *s != '|'; s++ )
if ( is_utf8 && (*s&0xc0) != 0x80 )
n++;
@@ -1296,22 +2315,35 @@ long_opt_strlen( ARGPARSE_OPTS *o )
}
+/* Qsort compare for show_help. */
+static int
+cmp_ordtbl (const void *a_v, const void *b_v)
+{
+ const unsigned short *a = a_v;
+ const unsigned short *b = b_v;
+
+ return *a - *b;
+}
+
+
/****************
* Print formatted help. The description string has some special
* meanings:
* - A description string which is "@" suppresses help output for
* this option
- * - a description,ine which starts with a '@' and is followed by
+ * - a description which starts with a '@' and is followed by
* any other characters is printed as is; this may be used for examples
- * ans such.
+ * and such. This is a legacy methiod, moder codes uses the flags
+ * ARGPARSE_OPT_VERBATIM or ARGPARSE_OPT_HEADER.
* - A description which starts with a '|' outputs the string between this
* bar and the next one as arguments of the long option.
*/
static void
-show_help (ARGPARSE_OPTS *opts, unsigned int flags)
+show_help (opttable_t *opts, unsigned int nopts, unsigned int flags)
{
const char *s;
char tmp[2];
+ unsigned int *ordtbl = NULL;
show_version ();
writestrings (0, "\n", NULL);
@@ -1325,30 +2357,68 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
}
s = strusage(41);
writestrings (0, s, "\n", NULL);
- if ( opts[0].description )
+ if ( nopts )
{
/* Auto format the option description. */
- int i,j, indent;
+ int i,j,indent;
+ const char *last_header = NULL;
+
+ ordtbl = xtrycalloc (nopts, sizeof *ordtbl);
+ if (!ordtbl)
+ {
+ writestrings (1, "\nOoops: Out of memory whilst printing the help.\n",
+ NULL);
+ goto leave;
+ }
/* Get max. length of long options. */
- for (i=indent=0; opts[i].short_opt; i++ )
+ for (i=indent=0; i < nopts; i++ )
{
if ( opts[i].long_opt )
if ( !opts[i].description || *opts[i].description != '@' )
if ( (j=long_opt_strlen(opts+i)) > indent && j < 35 )
indent = j;
+ ordtbl[i] = opts[i].ordinal;
}
+ qsort (ordtbl, nopts, sizeof *ordtbl, cmp_ordtbl);
+
+ /* The first option needs to have a description; if not do not
+ * print the help at all. */
+ if (!opts[ordtbl[0]].description)
+ goto leave;
+
/* Example: " -v, --verbose Viele Sachen ausgeben" */
indent += 10;
- if ( *opts[0].description != '@' )
+ if ( *opts[ordtbl[0]].description != '@'
+ && !(opts[ordtbl[0]].flags
+ & (ARGPARSE_OPT_VERBATIM|ARGPARSE_OPT_HEADER)))
writestrings (0, "Options:", "\n", NULL);
- for (i=0; opts[i].short_opt; i++ )
+ for (i=0; i < nopts; i++ )
{
- s = map_static_macro_string (_( opts[i].description ));
+ s = map_fixed_string (_( opts[ordtbl[i]].description ));
if ( s && *s== '@' && !s[1] ) /* Hide this line. */
continue;
- if ( s && *s == '@' ) /* Unindented comment only line. */
+ if ( s && (opts[ordtbl[i]].flags & ARGPARSE_OPT_HEADER))
+ {
+ /* We delay printing until we have found one real output
+ * line. This avoids having a header above an empty
+ * section. */
+ last_header = s;
+ continue;
+ }
+ if (last_header)
+ {
+ if (*last_header)
+ writestrings (0, "\n", last_header, ":\n", NULL);
+ last_header = NULL;
+ }
+ if ( s && (opts[ordtbl[i]].flags & ARGPARSE_OPT_VERBATIM))
+ {
+ writestrings (0, s, NULL);
+ continue;
+ }
+ if ( s && *s == '@' ) /* Unindented legacy comment only line. */
{
for (s++; *s; s++ )
{
@@ -1369,12 +2439,12 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
}
j = 3;
- if ( opts[i].short_opt < 256 )
+ if ( opts[ordtbl[i]].short_opt < 256 )
{
- tmp[0] = opts[i].short_opt;
+ tmp[0] = opts[ordtbl[i]].short_opt;
tmp[1] = 0;
writestrings (0, " -", tmp, NULL );
- if ( !opts[i].long_opt )
+ if ( !opts[ordtbl[i]].long_opt )
{
if (s && *s == '|' )
{
@@ -1392,11 +2462,11 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
}
else
writestrings (0, " ", NULL);
- if ( opts[i].long_opt )
+ if ( opts[ordtbl[i]].long_opt )
{
- tmp[0] = opts[i].short_opt < 256?',':' ';
+ tmp[0] = opts[ordtbl[i]].short_opt < 256?',':' ';
tmp[1] = 0;
- j += writestrings (0, tmp, " --", opts[i].long_opt, NULL);
+ j += writestrings (0, tmp, " --", opts[ordtbl[i]].long_opt, NULL);
if (s && *s == '|' )
{
if ( *++s != '=' )
@@ -1456,10 +2526,13 @@ show_help (ARGPARSE_OPTS *opts, unsigned int flags)
writestrings (0, "\n", NULL);
writestrings (0, s, NULL);
}
+
+ leave:
flushstrings (0);
- exit(0);
+ xfree (ordtbl);
}
+
static void
show_version ()
{
@@ -1495,44 +2568,73 @@ show_version ()
}
-void
-usage (int level)
+/* Print the table of options with flags etc. */
+static void
+dump_option_table (gnupg_argparse_t *arg)
{
- const char *p;
+ opttable_t *opts;
+ unsigned int nopts;
+ const char *s;
+ char tmp[50];
+ unsigned int *ordtbl = NULL;
+ int i;
- if (!level)
+ opts = arg->internal->opts;
+ nopts = arg->internal->nopts;
+ if (!nopts)
+ return;
+
+ ordtbl = xtrycalloc (nopts, sizeof *ordtbl);
+ if (!ordtbl)
{
- writestrings (1, strusage(11), " ", strusage(13), "; ",
- strusage (14), "\n", NULL);
+ writestrings (1, "\nOoops: Out of memory whilst dumping the table.\n",
+ NULL);
flushstrings (1);
+ my_exit (arg, 2);
}
- else if (level == 1)
- {
- p = strusage (40);
- writestrings (1, p, NULL);
- if (*p && p[strlen(p)] != '\n')
- writestrings (1, "\n", NULL);
- exit (2);
- }
- else if (level == 2)
+ for (i=0; i < nopts; i++ )
+ ordtbl[i] = opts[i].ordinal;
+ qsort (ordtbl, nopts, sizeof *ordtbl, cmp_ordtbl);
+ for (i=0; i < nopts; i++ )
{
- p = strusage (42);
- if (p && *p == '1')
+ if (!opts[ordtbl[i]].long_opt)
+ continue;
+ writestrings (0, opts[ordtbl[i]].long_opt, ":", NULL);
+ snprintf (tmp, sizeof tmp, "%u:%u:",
+ opts[ordtbl[i]].short_opt,
+ opts[ordtbl[i]].flags);
+ writestrings (0, tmp, NULL);
+ s = opts[ordtbl[i]].description;
+ if (s)
{
- p = strusage (40);
- writestrings (1, p, NULL);
- if (*p && p[strlen(p)] != '\n')
- writestrings (1, "\n", NULL);
+ for (; *s; s++)
+ {
+ if (*s == '%' || *s == ':' || *s == '\n')
+ snprintf (tmp, sizeof tmp, "%%%02X", *s);
+ else
+ {
+ tmp[0] = *s;
+ tmp[1] = 0;
+ }
+ writestrings (0, tmp, NULL);
+ }
}
- writestrings (0, strusage(41), "\n", NULL);
- exit (0);
+ writestrings (0, ":\n", NULL);
}
+
+ flushstrings (0);
+ xfree (ordtbl);
+ my_exit (arg, 0);
}
+
+
/* 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.
@@ -1556,6 +2658,7 @@ const char *
strusage( int level )
{
const char *p = strusage_handler? strusage_handler(level) : NULL;
+ const char *tmp;
if ( p )
return map_static_macro_string (p);
@@ -1563,28 +2666,41 @@ strusage( int level )
switch ( level )
{
+ case 8: break; /* Default to utf-8. */
+ case 9: p = "GPL-3.0-or-later"; 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 = strusage (9);
+ if (tmp && !strcmp (tmp, "LGPL-2.1-or-later"))
+ p = ("License GNU LGPL-2.1-or-later <https://gnu.org/licenses/>");
+ else /* Default to GPLv3+. */
+ p =("License GNU 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 = GNUPG_DEF_COPYRIGHT_LINE; 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 = strusage (9);
+ 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 Lesser 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 "
-ARGPARSE_STR2(ARGPARSE_GPL_VERSION)
-" of the License, or\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"
@@ -1608,76 +2724,39 @@ set_strusage ( const char *(*f)( int ) )
strusage_handler = f;
}
+#endif /* USE_INTERNAL_ARGPARSE */
-#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)
+void
+usage (int level)
{
- 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;
+ const char *p;
- while (arg_parse (&pargs, opts))
+ if (!level)
+ {
+ writestrings (1, strusage(11), " ", strusage(13), "; ",
+ strusage (14), "\n", NULL);
+ flushstrings (1);
+ }
+ else if (level == 1)
+ {
+ p = strusage (40);
+ writestrings (1, p, NULL);
+ if (*p && p[strlen(p)] != '\n')
+ writestrings (1, "\n", NULL);
+ exit (2);
+ }
+ else if (level == 2)
{
- switch (pargs.r_opt)
+ p = strusage (42);
+ if (p && *p == '1')
{
- 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;
- }
+ p = strusage (40);
+ writestrings (1, p, NULL);
+ if (*p && p[strlen(p)] != '\n')
+ writestrings (1, "\n", NULL);
+ }
+ writestrings (0, strusage(41), "\n", NULL);
+ exit (0);
}
- 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;
}
-#endif /*TEST*/
-
-/**** bottom of file ****/