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