diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | src/argparse.c | 257 | ||||
-rw-r--r-- | tests/etc/t-argparse.conf | 14 | ||||
-rw-r--r-- | tests/t-argparse.c | 4 | ||||
-rw-r--r-- | tests/t-argparse.conf | 5 |
5 files changed, 207 insertions, 75 deletions
@@ -23,7 +23,7 @@ Noteworthy changes in version 1.38 (unreleased) [C28/A28/R_] GPGRT_CONFDIR_USER NEW. GPGRT_CONFDIR_SYS NEW. - Release-info: https://dev.gnupg.org/T + Release-info: https://dev.gnupg.org/T4859 Noteworthy changes in version 1.37 (2020-02-07) [C28/A28/R0] diff --git a/src/argparse.c b/src/argparse.c index 512b5d7..03e033a 100644 --- a/src/argparse.c +++ b/src/argparse.c @@ -90,6 +90,10 @@ typedef struct 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; @@ -97,13 +101,20 @@ typedef struct struct _gpgrt_argparse_internal_s { int idx; /* Note that this is saved and restored in _gpgrt_argparser. */ - int inarg; - int stopped; - int insysconfig; /* Processing global config file. */ - 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. */ + 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 explicit_confopt:1; /* 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; /* State of the gpgrt_argparser. */ const char *last; void *aliases; const void *cur_alias; @@ -294,7 +305,11 @@ 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->insysconfig = 0; + arg->internal->in_sysconf = 0; + 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; @@ -631,9 +646,10 @@ handle_meta_user (gpgrt_argparse_t *arg, unsigned int alternate, char *args) { (void)args; - _gpgrt_log_info ("%s:%u: meta command %s is not yet supported\n", - arg->internal->confname, arg->lineno, - alternate? "group":"user"); + if (arg->internal->verbose) + _gpgrt_log_info ("%s:%u: meta command %s is not yet supported\n", + arg->internal->confname, arg->lineno, + alternate? "group":"user"); return 0; } @@ -645,38 +661,66 @@ handle_meta_user (gpgrt_argparse_t *arg, unsigned int alternate, char *args) static int handle_meta_force (gpgrt_argparse_t *arg, unsigned int alternate, char *args) { - (void)arg; - (void)alternate; (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 3 requests an "ignore-all". ARGS is the + * an "unignore, a value of 2 requests an "ignore-all". ARGS is the * empty string and not used. */ static int handle_meta_ignore (gpgrt_argparse_t *arg, unsigned int alternate, char *args) { - (void)arg; - (void)alternate; (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 "ignore" command. ARG is the context. - * ALTERNATE is not used. ARGS is the string to log. */ +/* Implementation of the "ignore" 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 (gpgrt_argparse_t *arg, unsigned int alternate, char *args) { - (void)alternate; + if (alternate) + _gpgrt_log_info ("%s\n", args); + else + _gpgrt_log_info ("%s:%u: %s\n", + arg->internal->confname, arg->lineno, args); + return 0; +} + + +/* 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 (gpgrt_argparse_t *arg, unsigned int alternate, char *args) +{ + (void)args; - _gpgrt_log_info ("%s:%u: %s\n", - arg->internal->confname, arg->lineno, args); + if (alternate) + arg->internal->verbose = 0; + else + arg->internal->verbose = 1; return 0; } @@ -688,29 +732,32 @@ static int handle_metacmd (gpgrt_argparse_t *arg, char *keyword) { static struct { - const char *name; /* Name of the command. */ + const char *name; /* Name of the command. */ unsigned short alternate; /* Use alternate version of the command. */ - unsigned short needarg; /* Command requires an argument. */ + unsigned short needarg:1; /* Command requires an argument. */ + unsigned short always:1; /* Command allowed in all conf files. */ int (*func)(gpgrt_argparse_t *arg, unsigned int alternate, char *args); /*handler*/ } cmds[] = - {{ "user", 0, 1, handle_meta_user }, - { "group", 1, 1, handle_meta_user }, - { "force", 0, 0, handle_meta_force }, - { "+force", 0, 0, handle_meta_force }, - { "-force", 1, 0, handle_meta_force }, - { "ignore", 0, 0, handle_meta_ignore }, - { "+ignore", 0, 0, handle_meta_ignore }, - { "-ignore", 1, 0, handle_meta_ignore }, - { "ignore-all", 2, 0, handle_meta_ignore }, - { "+ignore-all", 2, 0, handle_meta_ignore }, - { "echo", 0, 1, handle_meta_echo } + {{ "user", 0, 1, 0, handle_meta_user }, + { "group", 1, 1, 0, handle_meta_user }, + { "force", 0, 0, 0, handle_meta_force }, + { "+force", 0, 0, 0, handle_meta_force }, + { "-force", 1, 0, 0, handle_meta_force }, + { "ignore", 0, 0, 0, handle_meta_ignore }, + { "+ignore", 0, 0, 0, handle_meta_ignore }, + { "-ignore", 1, 0, 0, handle_meta_ignore }, + { "ignore-all", 2, 0, 0, handle_meta_ignore }, + { "+ignore-all", 2, 0, 0, handle_meta_ignore }, + { "verbose", 0, 0, 1, handle_meta_verbose }, + { "+verbose", 0, 0, 1, handle_meta_verbose }, + { "-verbose", 1, 0, 1, handle_meta_verbose }, + { "echo", 0, 1, 1, handle_meta_echo }, + { "-echo", 1, 1, 1, handle_meta_echo } }; char *rest; int i; - _gpgrt_log_debug ("Handle meta command '%s'\n", keyword); - for (rest = keyword; *rest && !(isascii (*rest) && isspace (*rest)); rest++) ; if (*rest) @@ -728,6 +775,9 @@ handle_metacmd (gpgrt_argparse_t *arg, char *keyword) 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; + return cmds[i].func (arg, cmds[i].alternate, rest); } @@ -819,24 +869,21 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) /* Before scanning the next char handle the keyword seen states. */ if (state == Akeyword_eol || state == Akeyword_spc) { + /* 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 (i=0; i < nopts; i++ ) + for (idx=0; idx < nopts; idx++ ) { - if (opts[i].long_opt && !strcmp (opts[i].long_opt, keyword)) + if (opts[idx].long_opt && !strcmp (opts[idx].long_opt, keyword)) break; } - idx = i; arg->r_opt = opts[idx].short_opt; - if ((opts[idx].flags & ARGPARSE_OPT_IGNORE)) - { - /* 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 if (!(i < nopts)) + if (!(idx < nopts)) { - /* The option is not known - check for internal keywords. */ + /* The option (keyword) is not known - check for + * internal keywords before returning an error. */ if (state == Akeyword_spc && !strcmp (keyword, "alias")) { in_alias = 1; @@ -847,7 +894,7 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) /* 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 stae. */ + * call the function in the Akeyword_eol state. */ if (state == Akeyword_spc) { if (ignore_invalid_option_add (arg, fp)) @@ -872,9 +919,9 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) ? ARGPARSE_INVALID_COMMAND : ARGPARSE_INVALID_OPTION); if (state == Akeyword_spc) - { - state = Askipandleave; - } + state = Askipandleave; + else + goto leave; } } else if (state == Akeyword_spc) @@ -882,9 +929,44 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) /* Known option but need to scan for args. */ state = Awaitarg; } - else + 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 */ { - /* Known option and at end of line - return option. */ + 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) + _gpgrt_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":""); + 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 = 0; /* Does not take an arg. */ else if ((opts[idx].flags & ARGPARSE_OPT_OPTIONAL) ) @@ -896,6 +978,7 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) } /* (end state Akeyword_eol/Akeyword_spc) */ else if (state == Ametacmd) { + /* We are at the end of a line. */ gpgrt_assert (*keyword == '['); trim_spaces (keyword+1); if (!keyword[1]) @@ -903,11 +986,6 @@ _gpgrt_argparse (estream_t fp, gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig) arg->r_opt = ARGPARSE_INVALID_META; /* Empty. */ goto leave; } - if (!arg->internal->insysconfig) - { - arg->r_opt = ARGPARSE_UNEXPECTED_META; - goto leave; - } c = handle_metacmd (arg, keyword+1); if (c) { @@ -1198,7 +1276,6 @@ is_twopartfname (const char *fname) } - /* 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 @@ -1251,6 +1328,34 @@ try_versioned_conffile (const char *configname) } +/* This function is called after a sysconf file has been read. */ +static void +finish_read_sys (gpgrt_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->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: @@ -1382,13 +1487,14 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, } 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)) + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) _gpgrt_log_info (_("Note: no default option file '%s'\n"), arg->internal->confname); if ((arg->flags & ARGPARSE_FLAG_USER)) @@ -1398,11 +1504,11 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, goto next_state; } - if ((arg->flags & ARGPARSE_FLAG_VERBOSE)) + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) _gpgrt_log_info (_("reading options from '%s'\n"), arg->internal->confname); arg->internal->state = STATE_read_sys; - arg->internal->insysconfig = 1; + 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; @@ -1465,9 +1571,10 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, } arg->lineno = 0; arg->internal->idx = 0; + arg->internal->verbose = 0; arg->internal->stopped = 0; arg->internal->inarg = 0; - arg->internal->insysconfig = 0; + arg->internal->in_sysconf = 0; _gpgrt_fclose (arg->internal->conffp); arg->internal->conffp = _gpgrt_fopen (arg->internal->confname, "r"); if (!arg->internal->conffp) @@ -1481,14 +1588,15 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, } else { - if ((arg->flags & ARGPARSE_FLAG_VERBOSE)) + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) + || arg->internal->verbose) _gpgrt_log_info (_("Note: no default option file '%s'\n"), arg->internal->confname); goto next_state; } } - if ((arg->flags & ARGPARSE_FLAG_VERBOSE)) + if ((arg->flags & ARGPARSE_FLAG_VERBOSE) || arg->internal->verbose) _gpgrt_log_info (_("reading options from '%s'\n"), arg->internal->confname); arg->internal->state = STATE_read_user; @@ -1509,9 +1617,10 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, 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->insysconfig = 0; + arg->internal->in_sysconf = 0; if (!arg->argc || !arg->argv || !*arg->argv) { /* No or empty argument vector - don't bother to parse things. */ @@ -1528,6 +1637,7 @@ _gpgrt_argparser (gpgrt_argparse_t *arg, gpgrt_opt_t *opts, arg->r_opt = _gpgrt_argparse (arg->internal->conffp, arg, opts); if (!arg->r_opt) { + finish_read_sys (arg); arg->internal->state = STATE_open_user; goto next_state; } @@ -1734,6 +1844,7 @@ arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig, int no_init) } else arg->r_opt = opts[i].short_opt; + if ( i < 0 ) ; else if ( (opts[i].flags & ARGPARSE_TYPE_MASK) ) @@ -1746,6 +1857,7 @@ arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig, int no_init) } else s2 = argv[1]; + if ( !s2 && (opts[i].flags & ARGPARSE_OPT_OPTIONAL) ) { arg->r_type = ARGPARSE_TYPE_NONE; /* Argument is optional. */ @@ -1795,7 +1907,7 @@ arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig, int no_init) arg->internal->inarg++; if ( (arg->flags & ARGPARSE_FLAG_ONEDASH) ) { - for (i=0; opts[i].short_opt; i++ ) + for (i=0; i < nopts; i++ ) if ( opts[i].long_opt && !strcmp (opts[i].long_opt, s+1)) { dash_kludge = 1; @@ -1886,6 +1998,15 @@ arg_parse (gpgrt_argparse_t *arg, gpgrt_opt_t *opts_orig, int no_init) goto next_one; } + if (arg->r_opt > 0 && i >= 0 && i < nopts + && ((opts[i].ignore && opts[i].explicit_ignore) || opts[i].forced)) + { + _gpgrt_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; diff --git a/tests/etc/t-argparse.conf b/tests/etc/t-argparse.conf index c556466..90b9935 100644 --- a/tests/etc/t-argparse.conf +++ b/tests/etc/t-argparse.conf @@ -3,12 +3,12 @@ # Options applied to all user's config files #verbose -[echo Begin global config] - +[-echo Begin global config] +[verbose] [group :staff] # These option are applied to all users of the group staff up until # the next [group] or [user] statement. - +[-verbose] [+force] # All following option are forced and thus ignored when set in user # config files. Valid until the next [user] statement. Take care @@ -21,11 +21,14 @@ # The compliance is set immutable for these users verbose +[-force] +not-my-option + # The next shall raise an error due to the garpage at the end. #<off>[+ignore] fooo -#[+ignore-all] +[+ignore-all] # All options are ignored. @@ -55,4 +58,5 @@ my-option 42 # The default algorithm for new keys is set to this. a-long-option -[echo End global config] + +[-echo End global config] diff --git a/tests/t-argparse.c b/tests/t-argparse.c index 4719819..46b7258 100644 --- a/tests/t-argparse.c +++ b/tests/t-argparse.c @@ -74,6 +74,7 @@ main (int argc, char **argv) /* 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_o_i('M', "not-my-option", 0), ARGPARSE_s_n(500, "a-long-option", 0 ), ARGPARSE_conffile(501, "options", "|FILE|read options from FILE"), ARGPARSE_noconffile(502, "no-options", "Ignore conf files"), @@ -108,7 +109,7 @@ main (int argc, char **argv) { case ARGPARSE_CONFFILE: printf ("current conffile='%s'\n", - pargs.r.ret_str? pargs.r.ret_str: "[cmdline]"); + pargs.r_type? pargs.r.ret_str: "[cmdline]"); break; case ARGPARSE_IS_ARG : printf ("arg='%s'\n", pargs.r.ret_str); @@ -120,6 +121,7 @@ main (int argc, char **argv) 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 'M': opt.myopt = 0; break; case 500: opt.a_long_one++; break; default : pargs.err = ARGPARSE_PRINT_WARNING; any_warn = 1; break; } diff --git a/tests/t-argparse.conf b/tests/t-argparse.conf index e2c96e2..c689a63 100644 --- a/tests/t-argparse.conf +++ b/tests/t-argparse.conf @@ -1,5 +1,7 @@ # User test config file for t-argparse +[-echo begin of user config] +[+verbose] # Options applied to all user's config files echo @@ -8,4 +10,7 @@ echo my-option 4711 +not-my-option + verbose +[-echo end of user config]
\ No newline at end of file |