aboutsummaryrefslogtreecommitdiffstats
path: root/src/argparse.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/argparse.c384
1 files changed, 378 insertions, 6 deletions
diff --git a/src/argparse.c b/src/argparse.c
index fecd3b5..10e18c0 100644
--- a/src/argparse.c
+++ b/src/argparse.c
@@ -1,7 +1,7 @@
/* 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-2018 g10 Code GmbH
+ * Copyright (C) 2015-2020 g10 Code GmbH
*
* This file is part of Libgpg-error.
*
@@ -36,6 +36,15 @@
#include "gpgrt-int.h"
+
+/* The malloced configuration directories or NULL. */
+static struct
+{
+ char *user;
+ char *sys;
+} confdir;
+
+
/* Special short options which are auto-inserterd. */
#define ARGPARSE_SHORTOPT_HELP 32768
#define ARGPARSE_SHORTOPT_VERSION 32769
@@ -45,16 +54,36 @@
/* A mask for the types. */
#define ARGPARSE_TYPE_MASK 7 /* Mask for the type values. */
+/* The states for the gpgrt_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
+ };
+
+
/* Internal object of the public gpgrt_argparse_t object. */
struct _gpgrt_argparse_internal_s
{
- int idx;
+ int idx; /* Note that this is saved and restored in _gpgrt_argparser. */
int inarg;
int stopped;
+ int explicit_confopt; /* A conffile option has been given. */
+ char *explicit_conffile; /* Malloced name of an explicit conffile. */
+ unsigned int opt_flags; /* Current option flags. */
+ enum argparser_states state; /* of gpgrt_argparser. */
const char *last;
void *aliases;
const void *cur_alias;
void *iio_list;
+ estream_t conffp;
+ char *confname;
gpgrt_opt_t **opts; /* Malloced array of pointer to user provided opts. */
};
@@ -189,6 +218,7 @@ deinitialize (gpgrt_argparse_t *arg)
{
if (arg->internal)
{
+ xfree (arg->internal->explicit_conffile);
xfree (arg->internal->opts);
xfree (arg->internal);
arg->internal = NULL;
@@ -228,9 +258,15 @@ initialize (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, estream_t fp)
arg->internal->last = NULL;
arg->internal->inarg = 0;
arg->internal->stopped = 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. */
@@ -240,7 +276,7 @@ initialize (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, estream_t fp)
* 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))
+ if (fp || arg->internal->conffp || !(arg->flags & ARGPARSE_FLAG_NOLINENO))
arg->lineno = 0;
/* Need to clear the reset request. */
@@ -312,6 +348,9 @@ initialize (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, estream_t fp)
/* Last option was erroneous. */
const char *s;
+ if (!fp && arg->internal->conffp)
+ fp = arg->internal->conffp;
+
if (fp)
{
if ( arg->r_opt == ARGPARSE_UNEXPECTED_ARG )
@@ -330,10 +369,13 @@ initialize (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, estream_t fp)
s = _("invalid alias definition");
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
s = _("invalid option");
- _gpgrt_log_error ("%s:%u: %s\n",
- _gpgrt_fname_get (fp), arg->lineno, s);
+ if (s)
+ _gpgrt_log_error ("%s:%u: %s\n",
+ _gpgrt_fname_get (fp), arg->lineno, s);
}
else
{
@@ -353,7 +395,9 @@ initialize (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, estream_t fp)
else if ( arg->r_opt == ARGPARSE_AMBIGUOUS_COMMAND )
_gpgrt_log_error (_("command \"%.50s\" is ambiguous\n"),s );
else if ( arg->r_opt == ARGPARSE_OUT_OF_CORE )
- _gpgrt_log_error ("%s\n", _("out of core\n"));
+ _gpgrt_log_error ("%s\n", _("out of core"));
+ else if ( arg->r_opt == ARGPARSE_NO_CONFFILE)
+ ; /* Error has already been printed. */
else
_gpgrt_log_error (_("invalid option \"%.50s\"\n"), s);
}
@@ -824,6 +868,281 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig)
}
+/* Return true if the list of options OPTS has any option marked with
+ * ARGPARSE_OPT_CONFFILE. */
+static int
+any_opt_conffile (gpgrt_opt_t *opts)
+{
+ int i;
+
+ for (i=0; opts[i].short_opt; i++ )
+ if ((opts[i].flags & ARGPARSE_OPT_CONFFILE))
+ return 1;
+ return 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 gpgrt_set_confdir.
+ */
+int
+_gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_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 (any_opt_conffile (opts))
+ {
+ /* 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. */
+ 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);
+ while (arg_parse (arg, opts))
+ {
+ if ((arg->internal->opt_flags & ARGPARSE_OPT_CONFFILE))
+ {
+ arg->internal->explicit_confopt = 1;
+ if (arg->r_type == 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_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:
+ xfree (arg->internal->confname);
+ arg->internal->confname = _gpgrt_fnameconcat
+ (confdir.sys? confdir.sys : "/etc", confname, NULL);
+ arg->lineno = 0;
+ _gpgrt_fclose (arg->internal->conffp);
+ arg->internal->conffp = _gpgrt_fopen (arg->internal->confname, "r");
+ /* FIXME: Add a callback. */
+ /* if (arg->internal->conffp && is_secured_file (fileno (configfp)))*/
+ /* { */
+ /* es_fclose (arg->internal->conffp); */
+ /* arg->internal->conffp = NULL; */
+ /* gpg_err_set_errno (EPERM); */
+ /* } */
+ if (!arg->internal->conffp)
+ {
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE))
+ _gpgrt_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))
+ _gpgrt_log_info (_("reading options from '%s'\n"),
+ arg->internal->confname);
+ arg->internal->state = STATE_read_sys;
+ 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. */
+ xfree (arg->internal->confname);
+ arg->internal->confname = _gpgrt_fnameconcat
+ (confdir.user? confdir.user : "/FIXME", confname, NULL);
+ }
+ arg->lineno = 0;
+ _gpgrt_fclose (arg->internal->conffp);
+ arg->internal->conffp = _gpgrt_fopen (arg->internal->confname, "r");
+ /* FIXME: Add a callback. */
+ /* if (arg->internal->conffp && is_secured_file (fileno (configfp)))*/
+ /* { */
+ /* es_fclose (arg->internal->conffp); */
+ /* arg->internal->conffp = NULL; */
+ /* gpg_err_set_errno (EPERM); */
+ /* } */
+ if (!arg->internal->conffp)
+ {
+ arg->internal->state = STATE_open_cmdline;
+ if (arg->internal->explicit_confopt)
+ {
+ _gpgrt_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))
+ _gpgrt_log_info (_("Note: no default option file '%s'\n"),
+ arg->internal->confname);
+ goto next_state;
+ }
+ }
+
+ if ((arg->flags & ARGPARSE_FLAG_VERBOSE))
+ _gpgrt_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:
+ xfree (arg->internal->confname);
+ arg->internal->confname = NULL;
+ 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 = _gpgrt_argparse (arg->internal->conffp, arg, opts);
+ if (!arg->r_opt)
+ {
+ 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 = _gpgrt_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 = _gpgrt_argparse (arg->internal->conffp, arg, opts);
+ 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 OPTS and a keyword, return the index of
* the long option macthing KEYWORD. On error -1 is retruned for not
* found or -2 for ambigious keyword. */
@@ -1149,6 +1468,7 @@ set_opt_arg (gpgrt_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:
@@ -1591,3 +1911,55 @@ _gpgrt_set_fixed_string_mapper (const char *(*f)(const char*))
{
fixed_string_mapper = f;
}
+
+
+/* Register a configuration directory for use by the argparse
+ * functions. The defined values for WHAT are:
+ *
+ * GPGRT_CONFDIR_SYS The systems's configuration dir.
+ * The default is /etc
+ *
+ * GPGRT_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
+_gpgrt_set_confdir (int what, const char *name)
+{
+ char *buf, *p;
+
+ if (what == GPGRT_CONFDIR_SYS)
+ {
+ _gpgrt_free (confdir.sys);
+ buf = confdir.sys = _gpgrt_strdup (name);
+ }
+ else if (what == GPGRT_CONFDIR_USER)
+ {
+ _gpgrt_free (confdir.user);
+ buf = confdir.user = _gpgrt_strdup (name);
+ }
+ else
+ return;
+
+ if (!buf)
+ _gpgrt_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;
+ }
+}