diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2016-10-11 17:20:38 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2016-10-11 17:36:10 +0300 |
commit | bc73fc65600b6021dfe7a9fa1fba7e9b823993e2 (patch) | |
tree | 934b239ddd68b1bc91a42b66ddccfe3bcf01accf | |
parent | 6e8147334cf9ffe36367760c06002a1e5b7ae0d5 (diff) | |
download | mailutils-bc73fc65600b6021dfe7a9fa1fba7e9b823993e2.tar.gz mailutils-bc73fc65600b6021dfe7a9fa1fba7e9b823993e2.tar.bz2 |
Introduce new CLI/configuration code for mailutils applications.
The new API is to replace libmu_cfg and libmu_argp. A smooth transition
is scheduled, during which the two APIs will coexist,
* configure.ac: Build libmailutils/cli/Makefile
* include/mailutils/cli.h: New file.
* include/mailutils/Makefile.am: Add cli.h
* libmailutils/cli/Makefile.am: New file.
* libmailutils/cli/capa.c: New file.
* libmailutils/cli/cli.c: New file.
* libmailutils/cli/stdcapa.c: New file.
* libmailutils/Makefile.am (SUBDIRS): Add cli.
* include/mailutils/cfg.h (mu_cfg_parse_hints): New members: append_tree
and data.
(MU_PARSE_CONFIG_LINT): New flag.
* include/mailutils/diag.h (mu_program_name)
(mu_full_program_name): Remove const qualifier.
* include/mailutils/locker.h (mu_locker_set_default_external_program):
Argument is const.
* libmailutils/base/locker.c: Likewise.
* include/mailutils/opt.h (mu_progname, mu_absprogname): Replace
with mu_program_name and mu_full_program_name.
(mu_set_progname): Rename to mu_set_program_name.
(mu_parseopt) <po_data>: Change type to void *.
(po_help_hook, po_version_hook): Change signatures.
(mu_parseopt_error): New function.
* libmailutils/opt/progname.c (mu_progname, mu_absprogname): Replace
with mu_program_name and mu_full_program_name.
(mu_set_progname): Rename to mu_set_program_name.
* libmailutils/opt/help.c: Minor changes
* libmailutils/opt/opt.c (parse_error): Rename to mu_parse_error (extern).
(next_opt): Fix permutations.
* libmailutils/tests/parseopt.c: Reflect changes.
* libmailutils/tests/parseopt17.at: Improve test case
* libmailutils/tests/parseopt_help05.at: Reflect changes.
* libmailutils/tests/parseopt_help07.at: Reflect changes.
* include/mailutils/stdstream.h (mu_program_name): Remove qualifier.
* libmailutils/cfg/driver.c (mu_cfg_tree_reduce): Remove useless condition
* libmailutils/cfg/lexer.l (mu_cfg_parse_file): Additional info messages.
* libmailutils/cfg/parser.y (mu_cfg_parse_config): Join in
the append_tree.
* libmailutils/diag/diag.c (mu_program_name, mu_full_program_name)
(mu_set_program_name): Remove. Declared elsewhere.
* libmu_sieve/conf.c: Add new configuration code. Mark old text for
removal.
* libmailutils/tests/tcli.c: New program.
* libmailutils/tests/Makefile.am: Add tcli.c
29 files changed, 1390 insertions, 80 deletions
diff --git a/configure.ac b/configure.ac index dc244dea5..d729747cf 100644 --- a/configure.ac +++ b/configure.ac @@ -1503,12 +1503,13 @@ AC_CONFIG_FILES([ mail/testsuite/Makefile libmailutils/auth/Makefile libmailutils/base/Makefile libmailutils/address/Makefile libmailutils/sockaddr/Makefile libmailutils/cidr/Makefile + libmailutils/cli/Makefile libmailutils/cfg/Makefile libmailutils/datetime/Makefile libmailutils/diag/Makefile libmailutils/filter/Makefile libmailutils/imapio/Makefile libmailutils/list/Makefile diff --git a/include/mailutils/Makefile.am b/include/mailutils/Makefile.am index ba87486ce..9ae3929af 100644 --- a/include/mailutils/Makefile.am +++ b/include/mailutils/Makefile.am @@ -33,12 +33,13 @@ pkginclude_HEADERS = \ attribute.h\ auth.h\ body.h\ cctype.h\ cfg.h\ cidr.h\ + cli.h\ cstr.h\ datetime.h\ daemon.h\ dbm.h\ debug.h\ diag.h\ diff --git a/include/mailutils/cfg.h b/include/mailutils/cfg.h index 7ea8e080b..e9f3587ef 100644 --- a/include/mailutils/cfg.h +++ b/include/mailutils/cfg.h @@ -76,12 +76,14 @@ struct mu_cfg_node struct mu_cfg_parse_hints { int flags; char *site_rcfile; char *custom_rcfile; char *program; + struct mu_cfg_tree *append_tree; + void *data; }; struct mu_cfg_tree { mu_list_t nodes; /* a list of mu_cfg_node_t */ mu_opool_t pool; @@ -235,13 +237,14 @@ int mu_config_register_plain_section (const char *parent_path, #define MU_CFG_PARSE_SITE_RCFILE 0x010 #define MU_CFG_PARSE_CUSTOM_RCFILE 0x020 #define MU_CFG_PARSE_PROGRAM 0x040 #define MU_CFG_FMT_LOCUS 0x080 #define MU_CFG_FMT_VALUE_ONLY 0x100 #define MU_CFG_FMT_PARAM_PATH 0x200 - +#define MU_PARSE_CONFIG_LINT 0x400 + #ifdef MU_CFG_COMPATIBILITY # define MU_CFG_DEPRECATED #else # define MU_CFG_DEPRECATED MU_DEPRECATED #endif diff --git a/include/mailutils/cli.h b/include/mailutils/cli.h new file mode 100644 index 000000000..2943bc469 --- /dev/null +++ b/include/mailutils/cli.h @@ -0,0 +1,55 @@ +/* opt.h -- general-purpose command line option parser + Copyright (C) 2016 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or (at + your option) any later version. + + GNU Mailutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _MAILUTILS_CLI_H +#define _MAILUTILS_CLI_H +#include <stdio.h> +#include <mailutils/types.h> +#include <mailutils/cfg.h> +#include <mailutils/opt.h> + +typedef void (*mu_cli_capa_commit_fp) (void *); + +struct mu_cli_capa +{ + char *name; + struct mu_option *opt; + struct mu_cfg_param *cfg; + mu_cfg_section_fp parser; + mu_cli_capa_commit_fp commit; +}; + +void mu_cli_capa_init (void); +void mu_cli_capa_register (struct mu_cli_capa *capa); +void mu_cli_capa_apply (char const *name, mu_list_t opts, mu_list_t commits); + +struct mu_cli_setup +{ + struct mu_option **optv; + struct mu_cfg_param *cfg; + char *prog_doc; + char *prog_args; +}; + +void mu_version_func (struct mu_parseopt *po, FILE *stream); +void mu_cli (int argc, char **argv, struct mu_cli_setup *setup, + char **capa, void *data, + int *ret_argc, char ***ret_argv); + +char *mu_site_config_file (void); + +#endif diff --git a/include/mailutils/diag.h b/include/mailutils/diag.h index 3e2bc5188..0a5ebd2de 100644 --- a/include/mailutils/diag.h +++ b/include/mailutils/diag.h @@ -26,14 +26,14 @@ #include <mailutils/debug.h> #ifdef __cplusplus extern "C" { #endif -extern const char *mu_program_name; -extern const char *mu_full_program_name; +extern char *mu_program_name; +extern char *mu_full_program_name; #define MU_DIAG_EMERG MU_LOG_EMERG #define MU_DIAG_ALERT MU_LOG_ALERT #define MU_DIAG_CRIT MU_LOG_CRIT #define MU_DIAG_ERROR MU_LOG_ERROR #define MU_DIAG_ERR MU_DIAG_ERROR diff --git a/include/mailutils/locker.h b/include/mailutils/locker.h index b8c4a93c5..718338f72 100644 --- a/include/mailutils/locker.h +++ b/include/mailutils/locker.h @@ -100,13 +100,13 @@ enum mu_locker_set_mode * the default. */ extern int mu_locker_set_default_flags (int flags, enum mu_locker_set_mode mode); extern void mu_locker_set_default_retry_timeout (time_t to); extern void mu_locker_set_default_retry_count (size_t n); extern void mu_locker_set_default_expire_timeout (time_t t); -extern int mu_locker_set_default_external_program (char *path); +extern int mu_locker_set_default_external_program (char const *path); /* A flags of 0 means that the default will be used. */ extern int mu_locker_create (mu_locker_t *, const char *filename, int flags); extern void mu_locker_destroy (mu_locker_t *); /* Time is measured in seconds. */ diff --git a/include/mailutils/opt.h b/include/mailutils/opt.h index 8e3b03b5d..cc5634211 100644 --- a/include/mailutils/opt.h +++ b/include/mailutils/opt.h @@ -20,16 +20,16 @@ #include <stdio.h> #include <mailutils/types.h> #include <mailutils/list.h> #include <mailutils/util.h> #include <mailutils/cctype.h> -extern char *mu_progname; -extern char *mu_absprogname; +extern char *mu_program_name; +extern char *mu_full_program_name; -void mu_set_progname (char const *arg); +void mu_set_program_name (char const *arg); #define MU_OPTION_DEFAULT 0 #define MU_OPTION_ARG_OPTIONAL 0x01 #define MU_OPTION_HIDDEN 0x02 #define MU_OPTION_ALIAS 0x04 #define MU_OPTION_IMMEDIATE 0x08 @@ -115,27 +115,28 @@ struct mu_parseopt int po_argc; /* Number of argiments */ char **po_argv; /* Array of arguments */ size_t po_optc; /* Number of elements in optv */ struct mu_option **po_optv; /* Array of ptrs to option structures */ int po_flags; - char *po_data; /* Call-specific data */ + void *po_data; /* Call-specific data */ int po_exit_error; /* Exit on error with this code */ /* Informational: */ char const *po_prog_name; char const *po_prog_doc; char const *po_prog_args; char const *po_bug_address; char const *po_package_name; char const *po_package_url; char const *po_extra_info; - - void (*po_help_hook) (FILE *stream); /* FIXME: should take mu_stream_t ?*/ - void (*po_version_hook) (FILE *stream); + + /* FIXME: should these take mu_stream_t ?*/ + void (*po_help_hook) (struct mu_parseopt *po, FILE *stream); + void (*po_version_hook) (struct mu_parseopt *po, FILE *stream); /* Output data */ int po_ind; /* Index of the next option */ int po_opterr; /* Index of the element in po_argv that caused last error, or -1 if no errors */ mu_list_t po_optlist; @@ -159,12 +160,13 @@ struct mu_parseopt unsigned po_permuted:1; /* Whether the arguments were permuted */ }; int mu_parseopt (struct mu_parseopt *p, int argc, char **argv, struct mu_option **optv, int flags); +void mu_parseopt_error (struct mu_parseopt *po, char const *fmt, ...); int mu_parseopt_apply (struct mu_parseopt *p); void mu_parseopt_free (struct mu_parseopt *p); void mu_option_describe_options (struct mu_option **optbuf, size_t optcnt); void mu_program_help (struct mu_parseopt *p); diff --git a/include/mailutils/stdstream.h b/include/mailutils/stdstream.h index ac35ff78d..97718811d 100644 --- a/include/mailutils/stdstream.h +++ b/include/mailutils/stdstream.h @@ -25,13 +25,13 @@ extern "C" { #endif extern mu_stream_t mu_strin; extern mu_stream_t mu_strout; extern mu_stream_t mu_strerr; -extern const char *mu_program_name; +extern char *mu_program_name; #define MU_STRERR_STDERR 0 #define MU_STRERR_SYSLOG 1 /* #define MU_STRERR_FILE 2 */ #define MU_STDSTREAM_RESET_STRIN 0x01 diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am index 7a5270c4c..82550c23f 100644 --- a/libmailutils/Makefile.am +++ b/libmailutils/Makefile.am @@ -14,13 +14,13 @@ # # You should have received a copy of the GNU Lesser General # Public License along with this library. If not, see # <http://www.gnu.org/licenses/>. SUBDIRS = \ - auth base address list sockaddr cidr cfg diag\ + auth base address list sockaddr cidr cfg cli diag\ filter mailbox mailer mime msgset opt server string stream stdstream\ property url imapio datetime . tests lib_LTLIBRARIES = libmailutils.la libmailutils_la_SOURCES = @@ -30,12 +30,13 @@ libmailutils_la_LIBADD = \ auth/libauth.la\ base/libbase.la\ address/libaddress.la\ sockaddr/libsockaddr.la\ cidr/libcidr.la\ cfg/libcfg.la\ + cli/libcli.la\ datetime/libdatetime.la\ diag/libdiag.la\ filter/libfilter.la\ imapio/libimapio.la\ list/liblist.la\ mailbox/libmailbox.la\ diff --git a/libmailutils/base/locker.c b/libmailutils/base/locker.c index b4663b948..c4fe993bf 100644 --- a/libmailutils/base/locker.c +++ b/libmailutils/base/locker.c @@ -239,13 +239,13 @@ void mu_locker_set_default_expire_timeout (time_t t) { mu_locker_expire_timeout = t; } int -mu_locker_set_default_external_program (char *path) +mu_locker_set_default_external_program (char const *path) { char *p = strdup (path); if (!p) return ENOMEM; free (mu_locker_external_program); mu_locker_external_program = p; diff --git a/libmailutils/cfg/driver.c b/libmailutils/cfg/driver.c index c0cd2d5d9..f2a43a367 100644 --- a/libmailutils/cfg/driver.c +++ b/libmailutils/cfg/driver.c @@ -460,13 +460,13 @@ int mu_cfg_tree_reduce (mu_cfg_tree_t *parse_tree, struct mu_cfg_parse_hints *hints, struct mu_cfg_param *progparam, void *target_ptr) { int rc = 0; - + struct mu_cfg_cont *cont; if (!parse_tree) return 0; if (hints && (hints->flags & MU_PARSE_CONFIG_DUMP)) { int yes = 1; mu_stream_t stream; @@ -474,19 +474,15 @@ mu_cfg_tree_reduce (mu_cfg_tree_t *parse_tree, mu_stdio_stream_create (&stream, MU_STDERR_FD, 0); mu_stream_ioctl (stream, MU_IOCTL_FD, MU_IOCTL_FD_SET_BORROW, &yes); mu_cfg_format_parse_tree (stream, parse_tree, MU_CFG_FMT_LOCUS); mu_stream_destroy (&stream); } - if (root_container) - { - struct mu_cfg_cont *cont = mu_build_container (progparam); - rc = mu_cfg_scan_tree (parse_tree, &cont->v.section, target_ptr, - NULL); - mu_config_destroy_container (&cont); - } + cont = mu_build_container (progparam); + rc = mu_cfg_scan_tree (parse_tree, &cont->v.section, target_ptr, NULL); + mu_config_destroy_container (&cont); return rc; } void mu_format_config_tree (mu_stream_t stream, struct mu_cfg_param *progparam) diff --git a/libmailutils/cfg/lexer.l b/libmailutils/cfg/lexer.l index 40598a1a2..b6c49ecc1 100644 --- a/libmailutils/cfg/lexer.l +++ b/libmailutils/cfg/lexer.l @@ -327,16 +327,22 @@ mu_cfg_parse_file (mu_cfg_tree_t **return_tree, const char *file, int flags) { struct stat st; FILE *fp; int rc; char *full_name = mu_tilde_expansion (file, MU_HIERARCHY_DELIMITER, NULL); + if (flags & MU_PARSE_CONFIG_VERBOSE) + mu_diag_output (MU_DIAG_INFO, _("opening configuration file %s"), + full_name); if (stat (full_name, &st)) { if (errno != ENOENT) mu_error (_("cannot stat `%s': %s"), full_name, mu_strerror (errno)); + else if (flags & MU_PARSE_CONFIG_VERBOSE) + mu_diag_output (MU_DIAG_INFO, _("configuration file %s doesn't exist"), + full_name); free (full_name); return ENOENT; } else if (!S_ISREG (st.st_mode)) { if (flags & MU_PARSE_CONFIG_VERBOSE) diff --git a/libmailutils/cfg/parser.y b/libmailutils/cfg/parser.y index e36b513b0..043f7e0c7 100644 --- a/libmailutils/cfg/parser.y +++ b/libmailutils/cfg/parser.y @@ -1586,13 +1586,16 @@ mu_cfg_parse_config (mu_cfg_tree_t **ptree, struct mu_cfg_parse_hints *hints) else { mu_cfg_tree_postprocess (tmp, hints); mu_cfg_tree_union (&tree, &tmp); } } - + + if (hints->append_tree) + mu_cfg_tree_union (&tree, &hints->append_tree); + *ptree = tree; return rc; } diff --git a/libmailutils/cli/Makefile.am b/libmailutils/cli/Makefile.am new file mode 100644 index 000000000..48770e844 --- /dev/null +++ b/libmailutils/cli/Makefile.am @@ -0,0 +1,26 @@ +# GNU Mailutils -- a suite of utilities for electronic mail +# Copyright (C) 2016 Free Software Foundation, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General +# Public License along with this library. If not, see +# <http://www.gnu.org/licenses/>. + +noinst_LTLIBRARIES = libcli.la + +libcli_la_SOURCES = \ + capa.c\ + cli.c\ + stdcapa.c + +AM_CPPFLAGS = \ + @MU_LIB_COMMON_INCLUDES@ -I/libmailutils diff --git a/libmailutils/cli/capa.c b/libmailutils/cli/capa.c new file mode 100644 index 000000000..bd5c557e9 --- /dev/null +++ b/libmailutils/cli/capa.c @@ -0,0 +1,96 @@ +/* capa.c -- CLI capabilities for GNU Mailutils + Copyright (C) 2016 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or (at + your option) any later version. + + GNU Mailutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <mailutils/cli.h> +#include <mailutils/list.h> +#include <mailutils/alloc.h> +#include <mailutils/nls.h> + +static mu_list_t capa_list; + +static void +capa_free (void *ptr) +{ + struct mu_cli_capa *cp = ptr; + free (cp->name); + free (cp); +} + +void +mu_cli_capa_register (struct mu_cli_capa *capa) +{ + struct mu_cli_capa *cp = mu_alloc (sizeof (*cp)); + cp->name = mu_strdup (capa->name); + cp->opt = capa->opt; + cp->cfg = capa->cfg; + cp->parser = capa->parser; + cp->commit = capa->commit; + if (!capa_list) + { + mu_list_create (&capa_list); + mu_list_set_destroy_item (capa_list, capa_free); + } + mu_list_append (capa_list, cp); +} + +struct capa_apply +{ + char const *name; + mu_list_t opts; + mu_list_t commits; + int found; +}; + +static int +capa_apply (void *item, void *data) +{ + struct mu_cli_capa *cp = item; + struct capa_apply *ap = data; + + if (strcmp (cp->name, ap->name) == 0) + { + ap->found = 1; + if (cp->opt) + mu_list_append (ap->opts, cp->opt); + if (cp->commit) + mu_list_append (ap->commits, cp->commit); + if (cp->parser || cp->cfg) + mu_config_root_register_section (NULL, cp->name, NULL, + cp->parser, cp->cfg); + } + return 0; +} + +void +mu_cli_capa_apply (char const *name, mu_list_t opts, mu_list_t commits) +{ + struct capa_apply app; + app.name = name; + app.opts = opts; + app.commits = commits; + app.found = 0; + mu_list_foreach (capa_list, capa_apply, &app); + if (!app.found) + mu_error (_("INTERNAL ERROR at %s:%d: unknown standard capability `%s'"), + __FILE__, __LINE__, name); +} + + diff --git a/libmailutils/cli/cli.c b/libmailutils/cli/cli.c new file mode 100644 index 000000000..2be1af833 --- /dev/null +++ b/libmailutils/cli/cli.c @@ -0,0 +1,420 @@ +/* cli.c -- Command line interface for GNU Mailutils + Copyright (C) 2016 Free Software Foundation, Inc. + + GNU Mailutils is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, or (at + your option) any later version. + + GNU Mailutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <stdlib.h> +#include <sysexits.h> +#include <mailutils/cfg.h> +#include <mailutils/opt.h> +#include <mailutils/cli.h> +#include <mailutils/list.h> +#include <mailutils/alloc.h> +#include <mailutils/nls.h> +#include <mailutils/errno.h> +#include <mailutils/version.h> +#include <mailutils/stream.h> +#include <mailutils/stdstream.h> +#include <mailutils/io.h> +#include <mailutils/syslog.h> + +#ifndef MU_SITE_CONFIG_FILE +# define MU_SITE_CONFIG_FILE SYSCONFDIR "/mailutils.rc" +#endif + +char * +mu_site_config_file (void) +{ + char *p = getenv ("MU_SITE_CONFIG_FILE"); + if (p) + return p; + return MU_SITE_CONFIG_FILE; +} + + +const char mu_version_copyright[] = + /* Do *not* mark this string for translation. %s is a copyright + symbol suitable for this locale, and %d is the copyright + year. */ + "Copyright %s 2007-2016 Free Software Foundation, inc."; + +void +mu_version_func (struct mu_parseopt *po, FILE *stream) +{ +#ifdef GIT_DESCRIBE + fprintf (stream, "%s (%s) %s [%s]\n", + mu_program_name, PACKAGE_NAME, PACKAGE_VERSION, GIT_DESCRIBE); +#else + fprintf (stream, "%s (%s) %s\n", mu_program_name, + PACKAGE_NAME, PACKAGE_VERSION); +#endif + /* TRANSLATORS: Translate "(C)" to the copyright symbol + (C-in-a-circle), if this symbol is available in the user's + locale. Otherwise, do not translate "(C)"; leave it as-is. */ + fprintf (stream, mu_version_copyright, _("(C)")); + fputs (_("\ +\n\ +License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n\ +\n\ +"), + stream); +} + +static char gnu_general_help_url[] = + N_("General help using GNU software: <http://www.gnu.org/gethelp/>"); + + +static void +change_progname (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + po->po_prog_name = mu_strdup (arg); +} + +static void +no_user_config (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + hints->flags &= ~MU_CFG_PARSE_PROGRAM; +} + +static void +no_site_config (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + hints->flags &= ~MU_CFG_PARSE_SITE_RCFILE; +} + +static void +config_file (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + hints->flags |= MU_CFG_PARSE_CUSTOM_RCFILE; + hints->custom_rcfile = mu_strdup (arg); +} + +static void +config_verbose (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + if (hints->flags & MU_PARSE_CONFIG_VERBOSE) + hints->flags |= MU_PARSE_CONFIG_DUMP; + else + hints->flags |= MU_PARSE_CONFIG_VERBOSE; +} + +static void +config_lint (struct mu_parseopt *po, struct mu_option *opt, + char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + hints->flags |= MU_PARSE_CONFIG_VERBOSE|MU_PARSE_CONFIG_LINT; +} + +static void +param_set (struct mu_parseopt *po, struct mu_option *opt, char const *arg) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + mu_cfg_node_t *node; + int rc = mu_cfg_create_subtree (arg, &node); + if (rc) + mu_parseopt_error (po, "%s: cannot create node: %s", + arg, mu_strerror (rc)); + if (!hints->append_tree) + mu_cfg_tree_create (&hints->append_tree); + mu_cfg_tree_add_node (hints->append_tree, node); +} + +struct mu_option mu_common_options[] = { + MU_OPTION_GROUP(N_("Common options")), + { "program-name", 0, N_("NAME"), MU_OPTION_IMMEDIATE|MU_OPTION_HIDDEN, + N_("set program name"), + mu_c_string, NULL, change_progname }, + + { "no-user-config", 0, NULL, MU_OPTION_IMMEDIATE, + N_("do not load user configuration file"), + mu_c_string, NULL, no_user_config }, + { "no-user-rcfile", 0, NULL, MU_OPTION_ALIAS }, + + { "no-site-config", 0, NULL, MU_OPTION_IMMEDIATE, + N_("do not load user configuration file"), + mu_c_string, NULL, no_site_config }, + { "no-site-rcfile", 0, NULL, MU_OPTION_ALIAS }, + + { "config-file", 0, N_("FILE"), MU_OPTION_IMMEDIATE, + N_("load this configuration file"), + mu_c_string, NULL, config_file }, + { "rcfile", 0, NULL, MU_OPTION_ALIAS }, + + { "config-verbose", 0, NULL, MU_OPTION_IMMEDIATE, + N_("verbosely log parsing of the configuration files"), + mu_c_string, NULL, config_verbose }, + { "rcfile-verbose", 0, NULL, MU_OPTION_ALIAS }, + + { "config-lint", 0, NULL, MU_OPTION_IMMEDIATE, + N_("check configuration file syntax and exit"), + mu_c_string, NULL, config_lint }, + { "rcfile-lint", 0, NULL, MU_OPTION_ALIAS }, + + { "set", 0, N_("PARAM=VALUE"), MU_OPTION_IMMEDIATE, + N_("set configuration parameter"), + mu_c_string, NULL, param_set }, + + MU_OPTION_END +}; + +static void +show_comp_defaults (struct mu_parseopt *po, struct mu_option *opt, + char const *unused) +{ + mu_print_options (); + exit (0); +} + +static void +show_config_help (struct mu_parseopt *po, struct mu_option *opt, + char const *unused) +{ + struct mu_cfg_parse_hints *hints = po->po_data; + + char *comment; + mu_stream_t stream; + struct mu_cfg_cont *cont; + static struct mu_cfg_param dummy_include_param[] = { + { "include", mu_c_string, NULL, 0, NULL, + N_("Include contents of the given file. If a directory is given, " + "include contents of the file <file>/<program>, where " + "<program> is the name of the program. This latter form is " + "allowed only in the site-wide configuration file."), + N_("file-or-directory") }, + { NULL } + }; + + mu_stdio_stream_create (&stream, MU_STDOUT_FD, 0); + + mu_asprintf (&comment, + "Configuration file structure for %s utility.", + po->po_prog_name); + mu_cfg_format_docstring (stream, comment, 0); + free (comment); + + mu_asprintf (&comment, + "For use in global configuration file (%s), enclose it " + "in `program %s { ... };", + mu_site_config_file (), + po->po_prog_name); + mu_cfg_format_docstring (stream, comment, 0); + free (comment); + + /* FIXME: %s should be replaced by the canonical utility name */ + mu_asprintf (&comment, "For more information, use `info %s'.", + po->po_prog_name); + mu_cfg_format_docstring (stream, comment, 0); + free (comment); + + cont = mu_config_clone_root_container (); + mu_config_container_register_section (&cont, NULL, NULL, NULL, NULL, + dummy_include_param, NULL); + if (hints->data) + mu_config_container_register_section (&cont, NULL, NULL, NULL, NULL, + hints->data, NULL); + mu_cfg_format_container (stream, cont); + mu_config_destroy_container (&cont); + + mu_stream_destroy (&stream); + exit (0); +} + +struct mu_option mu_extra_help_options[] = { + MU_OPTION_GROUP (N_("Informational options")), + { "show-c |