1 /* cli.c -- Command line interface for GNU Mailutils
2    Copyright (C) 2016-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License as
6    published by the Free Software Foundation; either version 3, or (at
7    your option) any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <sysexits.h>
23 #include <mailutils/cfg.h>
24 #include <mailutils/opt.h>
25 #include <mailutils/cli.h>
26 #include <mailutils/list.h>
27 #include <mailutils/alloc.h>
28 #include <mailutils/nls.h>
29 #include <mailutils/errno.h>
30 #include <mailutils/version.h>
31 #include <mailutils/stream.h>
32 #include <mailutils/stdstream.h>
33 #include <mailutils/io.h>
34 #include <mailutils/syslog.h>
35 #include <mailutils/mu_auth.h>
36 #include <mailutils/gitinfo.h>
37 
38 #ifndef MU_SITE_CONFIG_FILE
39 # define MU_SITE_CONFIG_FILE SYSCONFDIR "/mailutils.conf"
40 #endif
41 
42 char *
mu_site_config_file(void)43 mu_site_config_file (void)
44 {
45   char *p = getenv ("MU_SITE_CONFIG_FILE");
46   if (p)
47     return p;
48   return MU_SITE_CONFIG_FILE;
49 }
50 
51 
52 const char mu_version_copyright[] =
53   /* Do *not* mark this string for translation.  %s is a copyright
54      symbol suitable for this locale, and %d is the copyright
55      year.  */
56   "Copyright %s 2007-%d Free Software Foundation, inc.";
57 int mu_copyright_year = 2021;
58 
59 void
mu_version_print(mu_stream_t stream)60 mu_version_print (mu_stream_t stream)
61 {
62 #if MU_GIT_COMMIT_DISTANCE > 0
63   mu_stream_printf (stream, "%s (%s) %s-%d [%s]\n",
64 		    mu_program_name, PACKAGE_NAME, PACKAGE_VERSION,
65 		    MU_GIT_COMMIT_DISTANCE,
66 		    MU_GIT_DESCRIBE_STRING);
67 #else
68   mu_stream_printf (stream, "%s (%s) %s\n", mu_program_name,
69 		    PACKAGE_NAME, PACKAGE_VERSION);
70 #endif
71   /* TRANSLATORS: Translate "(C)" to the copyright symbol
72      (C-in-a-circle), if this symbol is available in the user's
73      locale.  Otherwise, do not translate "(C)"; leave it as-is.  */
74   mu_stream_printf (stream, mu_version_copyright, _("(C)"), mu_copyright_year);
75   mu_stream_printf (stream, _("\
76 \n\
77 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\
78 There is NO WARRANTY, to the extent permitted by law.\n\
79 \n\
80 "));
81 }
82 
83 void
mu_version_hook(struct mu_parseopt * po,mu_stream_t stream)84 mu_version_hook (struct mu_parseopt *po, mu_stream_t stream)
85 {
86   mu_version_print (stream);
87 }
88 
89 const char mu_general_help_text[] =
90   N_("General help using GNU software: <http://www.gnu.org/gethelp/>");
91 
92 struct app_data
93 {
94   struct mu_cli_setup *setup;
95   struct mu_cfg_parse_hints *hints;
96   struct mu_cfg_tree *append_tree;
97   int lint;
98 };
99 
100 static void
extra_help_hook(struct mu_parseopt * po,mu_stream_t stream)101 extra_help_hook (struct mu_parseopt *po, mu_stream_t stream)
102 {
103   struct app_data *dp = po->po_data;
104   mu_stream_printf (stream, "%s\n", gettext (dp->setup->prog_extra_doc));
105 }
106 
107 static void
prog_doc_hook(struct mu_parseopt * po,mu_stream_t stream)108 prog_doc_hook (struct mu_parseopt *po, mu_stream_t stream)
109 {
110   struct app_data *dp = po->po_data;
111   dp->setup->prog_doc_hook (stream);
112 }
113 
114 static void
change_progname(struct mu_parseopt * po,struct mu_option * opt,char const * arg)115 change_progname (struct mu_parseopt *po, struct mu_option *opt,
116 		 char const *arg)
117 {
118   po->po_prog_name = mu_strdup (arg);
119   free (mu_program_name);
120   mu_program_name = mu_strdup (arg);
121 }
122 
123 static void
no_user_config(struct mu_parseopt * po,struct mu_option * opt,char const * arg)124 no_user_config (struct mu_parseopt *po, struct mu_option *opt,
125 		char const *arg)
126 {
127   struct app_data *dp = po->po_data;
128   dp->hints->flags &= ~MU_CFHINT_PER_USER_FILE;
129 }
130 
131 static void
no_site_config(struct mu_parseopt * po,struct mu_option * opt,char const * arg)132 no_site_config (struct mu_parseopt *po, struct mu_option *opt,
133 		char const *arg)
134 {
135   struct app_data *dp = po->po_data;
136   dp->hints->flags &= ~MU_CFHINT_SITE_FILE;
137 }
138 
139 static void
no_config(struct mu_parseopt * po,struct mu_option * opt,char const * arg)140 no_config (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
141 {
142   struct app_data *dp = po->po_data;
143   dp->hints->flags &= ~(MU_CFHINT_SITE_FILE|MU_CFHINT_PER_USER_FILE);
144 }
145 
146 static void
config_file(struct mu_parseopt * po,struct mu_option * opt,char const * arg)147 config_file (struct mu_parseopt *po, struct mu_option *opt,
148 	     char const *arg)
149 {
150   struct app_data *dp = po->po_data;
151   dp->hints->flags = (dp->hints->flags
152 		        & ~(MU_CFHINT_SITE_FILE|MU_CFHINT_PROGRAM))
153                       | MU_CFHINT_CUSTOM_FILE;
154   dp->hints->custom_file = mu_strdup (arg);
155 }
156 
157 static void
config_verbose(struct mu_parseopt * po,struct mu_option * opt,char const * arg)158 config_verbose (struct mu_parseopt *po, struct mu_option *opt,
159 		char const *arg)
160 {
161   struct app_data *dp = po->po_data;
162   if (dp->hints->flags & MU_CF_VERBOSE)
163     dp->hints->flags |= MU_CF_DUMP;
164   else
165     dp->hints->flags |= MU_CF_VERBOSE;
166 }
167 
168 static void
config_lint(struct mu_parseopt * po,struct mu_option * opt,char const * arg)169 config_lint (struct mu_parseopt *po, struct mu_option *opt,
170 	     char const *arg)
171 {
172   struct app_data *dp = po->po_data;
173   dp->lint = 1;
174   dp->hints->flags |= MU_CF_VERBOSE;
175 }
176 
177 static void
param_set(struct mu_parseopt * po,struct mu_option * opt,char const * arg)178 param_set (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
179 {
180   struct app_data *dp = po->po_data;
181   mu_cfg_node_t *node;
182   int rc = mu_cfg_create_subtree (arg, &node);
183   if (rc)
184     mu_parseopt_error (po, "%s: cannot create node: %s",
185 		       arg, mu_strerror (rc));
186   if (!dp->append_tree)
187     {
188       mu_cfg_tree_create (&dp->append_tree);
189     }
190   mu_cfg_tree_add_node (dp->append_tree, node);
191 }
192 
193 static void
hangproc(struct mu_parseopt * po,struct mu_option * opt,char const * arg)194 hangproc (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
195 {
196   int n;
197   if (mu_str_to_c (arg, mu_c_int, &n, NULL))
198     {
199       mu_parseopt_error (po, _("%s: bad number"), arg);
200       exit (po->po_exit_error);
201     }
202   mu_wd (n);
203 }
204 
205 struct mu_option mu_common_options[] = {
206   /*  MU_OPTION_GROUP(N_("Common options")),*/
207   { "program-name",   0,  N_("NAME"),    MU_OPTION_IMMEDIATE|MU_OPTION_HIDDEN,
208     N_("set program name"),
209     mu_c_string, NULL, change_progname },
210   { "HANG",           0,  N_("SECONDS"),
211     MU_OPTION_IMMEDIATE|MU_OPTION_ARG_OPTIONAL|MU_OPTION_HIDDEN,
212     N_("wait this number of seconds before startup"),
213     mu_c_string, NULL, hangproc, "3600" },
214   MU_OPTION_END
215 };
216 
217 /* This varibales are used to construct the set of configuration
218    handling options.
219 */
220 
221 /* Option group header */
222 static struct mu_option mu_config_option_header =
223   MU_OPTION_GROUP (N_("Configuration handling"));
224 
225 /* Disables site-wide configuration file */
226 static struct mu_option mu_site_config_options[] = {
227   { "no-site-config", 0,  NULL,        MU_OPTION_IMMEDIATE,
228     N_("do not load site-wide configuration file"),
229     mu_c_string, NULL, no_site_config },
230   MU_OPTION_END
231 };
232 
233 /* Disables per-user configuration file */
234 static struct mu_option mu_user_config_options[] = {
235   { "no-user-config", 0,  NULL,        MU_OPTION_IMMEDIATE,
236     N_("do not load user configuration file"),
237     mu_c_string, NULL, no_user_config },
238   MU_OPTION_END
239 };
240 
241 /* 1. If both site-wide and per-user configuration files are used,
242       this option is equivalent to --no-site-config --no-user-config
243       used together.
244    2. If only site-wide configuration is used, this option is an alias
245       to --no-site-config
246    3. If only per-user configuration is used, this option is an alias
247       to --no-user-config
248 
249    Thus, --no-config-option always disables parsing of the default
250    configuration files.
251  */
252 static struct mu_option mu_no_config_option = {
253   "no-config", 0,  NULL,        MU_OPTION_IMMEDIATE,
254   N_("do not load site and user configuration files"),
255   mu_c_string, NULL, no_config
256 };
257 
258 /* These options are always available for utilities that use at least
259    one of default configuration files */
260 static struct mu_option mu_config_lint_options[] = {
261   { "config-verbose", 0,  NULL,        MU_OPTION_IMMEDIATE,
262     N_("verbosely log parsing of the configuration files"),
263     mu_c_string, NULL, config_verbose },
264   { "config-lint",    0,  NULL,        MU_OPTION_IMMEDIATE,
265     N_("check configuration file syntax and exit"),
266     mu_c_string, NULL, config_lint },
267   MU_OPTION_END
268 };
269 
270 static struct mu_option mu_config_override_options[] = {
271   { "config-file",    0,  N_("FILE"),  MU_OPTION_IMMEDIATE,
272     N_("load this configuration file; implies --no-config"),
273     mu_c_string, NULL, config_file },
274   { "set",            0,  N_("PARAM=VALUE"), MU_OPTION_IMMEDIATE,
275     N_("set configuration parameter"),
276     mu_c_string, NULL, param_set },
277   MU_OPTION_END
278 };
279 
280 
281 static void
show_comp_defaults(struct mu_parseopt * po,struct mu_option * opt,char const * unused)282 show_comp_defaults (struct mu_parseopt *po, struct mu_option *opt,
283 		    char const *unused)
284 {
285   mu_print_options ();
286   exit (0);
287 }
288 
289 static void
show_config_help(struct mu_parseopt * po,struct mu_option * opt,char const * unused)290 show_config_help (struct mu_parseopt *po, struct mu_option *opt,
291 		  char const *unused)
292 {
293   struct app_data *dp = po->po_data;
294 
295   char *comment;
296   mu_stream_t stream;
297   struct mu_cfg_cont *cont;
298   static struct mu_cfg_param dummy_include_param[] = {
299     { "include", mu_c_string, NULL, 0, NULL,
300       N_("Include contents of the given file.  If a directory is given, "
301 	 "include contents of the file <file>/<program>, where "
302 	 "<program> is the name of the program.  This latter form is "
303 	 "allowed only in the site-wide configuration file."),
304       N_("file-or-directory") },
305     { NULL }
306   };
307 
308   mu_stdio_stream_create (&stream, MU_STDOUT_FD, 0);
309 
310   mu_asprintf (&comment,
311 	       "Configuration file structure for %s utility.",
312 	       po->po_prog_name);
313   mu_cfg_format_docstring (stream, comment, 0);
314   free (comment);
315 
316   mu_asprintf (&comment,
317 	       "For use in global configuration file (%s), enclose it "
318 	       "in `program %s { ... };",
319 	       mu_site_config_file (),
320 	       po->po_prog_name);
321   mu_cfg_format_docstring (stream, comment, 0);
322   free (comment);
323 
324   /* FIXME: %s should be replaced by the canonical utility name */
325   mu_asprintf (&comment, "For more information, use `info %s'.",
326 	       po->po_prog_name);
327   mu_cfg_format_docstring (stream, comment, 0);
328   free (comment);
329 
330   cont = mu_config_clone_root_container ();
331   mu_config_container_register_section (&cont, NULL, NULL, NULL, NULL,
332 					dummy_include_param, NULL);
333   if (dp->setup)
334     {
335       mu_config_container_register_section (&cont, NULL, NULL, NULL, NULL,
336 					    dp->setup->cfg, NULL);
337     }
338 
339   mu_cfg_format_container (stream, cont);
340   mu_config_destroy_container (&cont);
341 
342   mu_stream_destroy (&stream);
343   exit (0);
344 }
345 
346 static struct mu_option mu_extra_help_options[] = {
347   MU_OPTION_GROUP (N_("Informational options")),
348   { "show-config-options",   0,  NULL,  MU_OPTION_IMMEDIATE,
349     N_("show compilation options"),
350     mu_c_string, NULL, show_comp_defaults },
351   MU_OPTION_END
352 };
353 
354 static struct mu_option mu_config_help_options[] = {
355   { "config-help",           0,  NULL,  MU_OPTION_IMMEDIATE,
356     N_("show configuration file summary"),
357     mu_c_string, NULL, show_config_help },
358   MU_OPTION_END
359 };
360 
361 
362 static int
add_opt_group(void * item,void * data)363 add_opt_group (void *item, void *data)
364 {
365   struct mu_parseopt *po = data;
366   struct mu_option *opt = item;
367   po->po_optv[po->po_optc++] = opt;
368   return 0;
369 }
370 
371 #define CONFIG_ENABLED \
372   (MU_CFHINT_SITE_FILE | MU_CFHINT_CUSTOM_FILE | MU_CFHINT_PER_USER_FILE)
373 
374 static void
opool_add_option(mu_opool_t pool,struct mu_option * opt)375 opool_add_option (mu_opool_t pool, struct mu_option *opt)
376 {
377   mu_opool_append (pool, opt, sizeof *opt);
378 }
379 
380 static void
opool_add_options(mu_opool_t pool,struct mu_option * opt)381 opool_add_options (mu_opool_t pool, struct mu_option *opt)
382 {
383   while (!MU_OPTION_IS_END (opt))
384     {
385       opool_add_option (pool, opt);
386       opt++;
387     }
388 }
389 
390 static struct mu_option *
opool_end_option(mu_opool_t pool)391 opool_end_option (mu_opool_t pool)
392 {
393   struct mu_option end = MU_OPTION_END;
394   opool_add_option (pool, &end);
395   return mu_opool_finish (pool, NULL);
396 }
397 
398 /* Build the list of option groups and configuration sections */
399 static struct mu_option **
init_options(mu_opool_t pool,char ** capa,struct mu_cli_setup * setup,struct mu_cfg_parse_hints const * hints,mu_list_t * ret_comlist)400 init_options (mu_opool_t pool,
401 	      char **capa, struct mu_cli_setup *setup,
402 	      struct mu_cfg_parse_hints const *hints,
403 	      mu_list_t *ret_comlist)
404 {
405   size_t i, s;
406   mu_list_t oplist;
407   mu_list_t comlist;
408   struct mu_parseopt po;
409 
410   mu_list_create (&oplist);
411   if (setup->optv)
412     {
413       for (i = 0; setup->optv[i]; i++)
414 	mu_list_append (oplist, setup->optv[i]);
415     }
416 
417   mu_list_create (&comlist);
418   mu_auth_extend_settings (oplist, comlist);
419   if (capa)
420     {
421       for (i = 0; capa[i]; i++)
422 	mu_cli_capa_extend_settings (capa[i], oplist, comlist);
423     }
424 
425   *ret_comlist = comlist;
426 
427   mu_list_append (oplist, mu_common_options);
428 
429   /* Construct configuration option section */
430   if (hints->flags & CONFIG_ENABLED)
431     {
432       opool_add_option (pool, &mu_config_option_header);
433       opool_add_options (pool, mu_config_lint_options);
434       if (!(hints->flags & MU_CFHINT_NO_CONFIG_OVERRIDE))
435 	{
436 	  opool_add_options (pool, mu_config_override_options);
437 	  if (hints->flags & MU_CFHINT_SITE_FILE)
438 	    {
439 	      opool_add_options (pool, mu_site_config_options);
440 	      if (hints->flags & MU_CFHINT_PER_USER_FILE)
441 		{
442 		  opool_add_options (pool, mu_user_config_options);
443 		  opool_add_option (pool, &mu_no_config_option);
444 		}
445 	      else
446 		{
447 		  struct mu_option opt = mu_no_config_option;
448 		  opt.opt_flags = MU_OPTION_ALIAS;
449 		  opool_add_option (pool, &opt);
450 		}
451 	    }
452 	  else if (hints->flags & MU_CFHINT_PER_USER_FILE)
453 	    {
454 	      struct mu_option opt = mu_no_config_option;
455 	      opool_add_options (pool, mu_user_config_options);
456 	      opt.opt_flags = MU_OPTION_ALIAS;
457 	      opool_add_option (pool, &opt);
458 	    }
459 	}
460       mu_list_append (oplist, opool_end_option (pool));
461     }
462 
463   mu_list_append (oplist, mu_extra_help_options);
464   if (hints->flags & CONFIG_ENABLED)
465     mu_list_append (oplist, mu_config_help_options);
466 
467   mu_list_count (oplist, &s);
468 
469   po.po_optv = mu_calloc (s + 1, sizeof (po.po_optv[0]));
470   po.po_optc = 0;
471   mu_list_foreach (oplist, add_opt_group, &po);
472   if (po.po_optc != s)
473     abort ();
474   po.po_optv[po.po_optc] = NULL;
475   mu_list_destroy (&oplist);
476   return po.po_optv;
477 }
478 
479 static int
run_commit(void * item,void * data)480 run_commit (void *item, void *data)
481 {
482   mu_cli_capa_commit_fp commit = item;
483   commit (data);
484   return 0;
485 }
486 
487 #define PRESERVE_FLAGS \
488   ( MU_PARSEOPT_NO_SORT					\
489     | MU_PARSEOPT_SINGLE_DASH				\
490     | MU_PARSEOPT_PACKAGE_NAME				\
491     | MU_PARSEOPT_PACKAGE_URL				\
492     | MU_PARSEOPT_BUG_ADDRESS				\
493     | MU_PARSEOPT_EXTRA_INFO				\
494     | MU_PARSEOPT_VERSION_HOOK				\
495     | MU_PARSEOPT_PROG_NAME   				\
496     | MU_PARSEOPT_NEGATION                              \
497     | MU_PARSEOPT_SPECIAL_ARGS )
498 
499 void
mu_cli_ext(int argc,char ** argv,struct mu_cli_setup * setup,struct mu_parseopt * pohint,struct mu_cfg_parse_hints * cfhint,char ** capa,void * data,int * ret_argc,char *** ret_argv)500 mu_cli_ext (int argc, char **argv,
501 	    struct mu_cli_setup *setup,
502 	    struct mu_parseopt *pohint,
503 	    struct mu_cfg_parse_hints *cfhint,
504 	    char **capa,
505 	    void *data,
506 	    int *ret_argc, char ***ret_argv)
507 {
508   struct mu_parseopt po;
509   int flags = 0;
510   struct mu_cfg_tree *parse_tree = NULL;
511   struct mu_cfg_parse_hints hints;
512   struct mu_option **optv;
513   mu_list_t com_list;
514 #define DFLARGC 2
515   char const *dfl_args[DFLARGC];
516   char **args = NULL;
517   size_t argcnt;
518   struct app_data appd;
519   mu_opool_t pool;
520 
521   /* Set up defaults */
522   if (setup->ex_usage == 0)
523     setup->ex_usage = EX_USAGE;
524   if (setup->ex_config == 0)
525     setup->ex_config = EX_CONFIG;
526 
527   hints = *cfhint;
528   if (setup->server)
529     hints.flags &= ~MU_CFHINT_PER_USER_FILE;
530 
531   /* Set program name */
532   if (!(hints.flags & MU_CFHINT_PROGRAM))
533     {
534       if (pohint->po_flags & MU_PARSEOPT_PROG_NAME)
535 	hints.program = (char *) pohint->po_prog_name;
536       else
537 	{
538 	  mu_set_program_name (argv[0]);
539 	  hints.program = (char*) mu_program_name;
540 	}
541       hints.flags |= MU_CFHINT_PROGRAM;
542     }
543 
544   /* Initialize standard streams */
545   mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
546 
547   /* Initialize standard capabilities */
548   mu_cli_capa_init ();
549 
550   /* Initialize po */
551 
552   if (setup->prog_doc)
553     {
554       po.po_prog_doc = setup->prog_doc;
555       flags |= MU_PARSEOPT_PROG_DOC;
556     }
557   else if (pohint->po_flags & MU_PARSEOPT_PROG_DOC)
558     {
559       po.po_prog_doc = pohint->po_prog_doc;
560       flags |= MU_PARSEOPT_PROG_DOC;
561     }
562 
563   if (setup->prog_args)
564     {
565       size_t i;
566       argcnt = 1;
567 
568       if (setup->prog_alt_args)
569 	{
570 	  for (i = 0; setup->prog_alt_args[i]; i++)
571 	    argcnt++;
572 	}
573 
574       if (argcnt < DFLARGC)
575 	po.po_prog_args = dfl_args;
576       else
577 	{
578 	  args = mu_calloc (argcnt + 1, sizeof (args[0]));
579 	  po.po_prog_args = (char const **) args;
580 	}
581 
582       po.po_prog_args[0] = setup->prog_args;
583       for (i = 1; i < argcnt; i++)
584 	po.po_prog_args[i] = setup->prog_alt_args[i-1];
585       po.po_prog_args[i] = NULL;
586 
587       flags |= MU_PARSEOPT_PROG_ARGS;
588     }
589   else if (pohint->po_flags & MU_PARSEOPT_PROG_ARGS)
590     {
591       po.po_prog_args = pohint->po_prog_args;
592       flags |= MU_PARSEOPT_PROG_ARGS;
593     }
594 
595   if (setup->prog_extra_doc)
596     {
597       po.po_help_hook = extra_help_hook;
598       flags |= MU_PARSEOPT_HELP_HOOK;
599     }
600   else if (pohint->po_flags & MU_PARSEOPT_HELP_HOOK)
601     {
602       po.po_help_hook = pohint->po_help_hook;
603       flags |= MU_PARSEOPT_HELP_HOOK;
604     }
605 
606   if (setup->prog_doc_hook)
607     {
608       po.po_prog_doc_hook = prog_doc_hook;
609       flags |= MU_PARSEOPT_PROG_DOC_HOOK;
610     }
611   else if (pohint->po_flags & MU_PARSEOPT_PROG_DOC_HOOK)
612     {
613       po.po_prog_doc_hook = pohint->po_prog_doc_hook;
614       flags |= MU_PARSEOPT_PROG_DOC_HOOK;
615     }
616 
617   if (setup->inorder)
618     flags |= MU_PARSEOPT_IN_ORDER;
619 
620   flags |= pohint->po_flags & PRESERVE_FLAGS;
621 
622   if (flags & MU_PARSEOPT_PACKAGE_NAME)
623     po.po_package_name = pohint->po_package_name;
624   if (flags & MU_PARSEOPT_PACKAGE_URL)
625     po.po_package_url = pohint->po_package_url;
626   if (flags & MU_PARSEOPT_BUG_ADDRESS)
627     po.po_bug_address = pohint->po_bug_address;
628   if (flags & MU_PARSEOPT_EXTRA_INFO)
629     po.po_extra_info = pohint->po_extra_info;
630   if (flags & MU_PARSEOPT_VERSION_HOOK)
631     po.po_version_hook = pohint->po_version_hook;
632   if (flags & MU_PARSEOPT_NEGATION)
633     po.po_negation = pohint->po_negation;
634   if (flags & MU_PARSEOPT_PROG_NAME)
635     po.po_prog_name = pohint->po_prog_name;
636   if (flags & MU_PARSEOPT_SPECIAL_ARGS)
637     po.po_special_args = pohint->po_special_args;
638 
639   if (setup->ex_usage)
640     {
641       po.po_exit_error = setup->ex_usage;
642       flags |= MU_PARSEOPT_EXIT_ERROR;
643     }
644   else if (pohint->po_flags & MU_PARSEOPT_EXIT_ERROR)
645     {
646       po.po_exit_error = pohint->po_exit_error;
647       flags |= MU_PARSEOPT_EXIT_ERROR;
648     }
649 
650   appd.setup = setup;
651   appd.hints = &hints;
652   appd.append_tree = NULL;
653   appd.lint = 0;
654   po.po_data = &appd;
655   flags |= MU_PARSEOPT_DATA;
656 
657   mu_opool_create (&pool, MU_OPOOL_ENOMEMABRT);
658   optv = init_options (pool, capa, setup, &hints, &com_list);
659 
660   if (mu_parseopt (&po, argc, argv, optv, flags))
661     exit (po.po_exit_error);
662 
663   argc -= po.po_arg_start;
664   argv += po.po_arg_start;
665 
666   if (ret_argc)
667     {
668       *ret_argc = argc;
669       *ret_argv = argv;
670     }
671   else if (argc)
672     mu_parseopt_error (&po, "%s", _("unexpected arguments"));
673 
674   if (mu_cfg_parse_config (&parse_tree, &hints))
675     exit (setup->ex_config);
676 
677   if (appd.append_tree)
678     mu_cfg_tree_union (&parse_tree, &appd.append_tree);
679 
680   if (mu_cfg_tree_reduce (parse_tree, &hints, setup->cfg, data))
681     exit (setup->ex_config);
682 
683   if (mu_cfg_error_count)
684     exit (setup->ex_config);
685 
686   mu_parseopt_apply (&po);
687 
688   mu_list_foreach (com_list, run_commit, NULL);
689   mu_list_destroy (&com_list);
690 
691   mu_cfg_destroy_tree (&parse_tree);
692   free (optv);
693   free (args);
694   mu_parseopt_free (&po);
695   mu_opool_destroy (&pool);
696 
697   if (appd.lint)
698     exit (0);
699 }
700 
701 void
mu_cli(int argc,char ** argv,struct mu_cli_setup * setup,char ** capa,void * data,int * ret_argc,char *** ret_argv)702 mu_cli (int argc, char **argv, struct mu_cli_setup *setup, char **capa,
703 	void *data,
704 	int *ret_argc, char ***ret_argv)
705 {
706   struct mu_parseopt pohint;
707   struct mu_cfg_parse_hints cfhint;
708 
709   pohint.po_flags = 0;
710 
711   pohint.po_package_name = PACKAGE_NAME;
712   pohint.po_flags |= MU_PARSEOPT_PACKAGE_NAME;
713 
714   pohint.po_package_url = PACKAGE_URL;
715   pohint.po_flags |= MU_PARSEOPT_PACKAGE_URL;
716 
717   pohint.po_bug_address = PACKAGE_BUGREPORT;
718   pohint.po_flags |= MU_PARSEOPT_BUG_ADDRESS;
719 
720   pohint.po_extra_info = mu_general_help_text;
721   pohint.po_flags |= MU_PARSEOPT_EXTRA_INFO;
722 
723   pohint.po_version_hook = mu_version_hook;
724   pohint.po_flags |= MU_PARSEOPT_VERSION_HOOK;
725 
726   pohint.po_negation = "no-";
727   pohint.po_flags |= MU_PARSEOPT_NEGATION;
728 
729   cfhint.site_file = mu_site_config_file ();
730   cfhint.flags = MU_CFHINT_SITE_FILE | MU_CFHINT_PER_USER_FILE;
731 
732   mu_cli_ext (argc, argv, setup, &pohint, &cfhint, capa, data,
733 	      ret_argc, ret_argv);
734 }
735