1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU 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
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "mh.h"
25 #include <mailutils/stream.h>
26 #include <mailutils/wordsplit.h>
27 #include <mailutils/io.h>
28 #include <mailutils/cli.h>
29 #include <mailutils/gitinfo.h>
30
31 struct getopt_data
32 {
33 char *extra_doc;
34 };
35
36 static void
mh_extra_help_hook(struct mu_parseopt * po,mu_stream_t stream)37 mh_extra_help_hook (struct mu_parseopt *po, mu_stream_t stream)
38 {
39 struct getopt_data *data = po->po_data;
40 char const *extra_doc = gettext (data->extra_doc);
41 while (*extra_doc)
42 {
43 size_t len = strcspn (extra_doc, "\n");
44 unsigned margin = strspn (extra_doc, " \t");
45
46 mu_stream_ioctl (stream, MU_IOCTL_WORDWRAPSTREAM,
47 MU_IOCTL_WORDWRAP_SET_MARGIN, &margin);
48 extra_doc += margin;
49 len -= margin;
50 mu_stream_write (stream, extra_doc, len, NULL);
51 mu_stream_write (stream, "\n", 1, NULL);
52 extra_doc += len;
53 if (*extra_doc)
54 extra_doc++;
55 }
56 }
57
58 static void
augment_argv(int * pargc,char *** pargv)59 augment_argv (int *pargc, char ***pargv)
60 {
61 int argc;
62 char **argv;
63 int i, j;
64 struct mu_wordsplit ws;
65 char const *val = mh_global_profile_get (mu_program_name, NULL);
66
67 if (!val)
68 return;
69
70 if (mu_wordsplit (val, &ws, MU_WRDSF_DEFFLAGS))
71 {
72 mu_error (_("cannot split line `%s': %s"), val,
73 mu_wordsplit_strerror (&ws));
74 exit (1);
75 }
76
77 argc = *pargc + ws.ws_wordc;
78 argv = calloc (argc + 1, sizeof *argv);
79 if (!argv)
80 mh_err_memory (1);
81
82 i = 0;
83 argv[i++] = (*pargv)[0];
84 for (j = 0; j < ws.ws_wordc; i++, j++)
85 argv[i] = ws.ws_wordv[j];
86 for (j = 1; i < argc; i++, j++)
87 argv[i] = (*pargv)[j];
88 argv[i] = NULL;
89
90 ws.ws_wordc = 0;
91 mu_wordsplit_free (&ws);
92
93 *pargc = argc;
94 *pargv = argv;
95 }
96
97 static void
process_std_options(int argc,char ** argv,struct mu_parseopt * po)98 process_std_options (int argc, char **argv, struct mu_parseopt *po)
99 {
100 if (argc != 1)
101 return;
102 if (strcmp (argv[0], "--help") == 0)
103 {
104 mu_program_help (po, mu_strout);
105 exit (0);
106 }
107 if (strcmp (argv[0], "--version") == 0)
108 {
109 mu_version_hook (po, mu_strout);
110 exit (0);
111 }
112 }
113
114 static void
process_folder_arg(int * pargc,char ** argv,struct mu_parseopt * po)115 process_folder_arg (int *pargc, char **argv, struct mu_parseopt *po)
116 {
117 int i, j;
118 int argc = *pargc;
119 struct mu_option *opt;
120
121 /* Find folder option */
122 for (i = 0; ; i++)
123 {
124 if (!po->po_optv[i])
125 return; /* Nothing to do */
126 if (MU_OPTION_IS_VALID_LONG_OPTION (po->po_optv[i])
127 && strcmp (po->po_optv[i]->opt_long, "folder") == 0)
128 break;
129 }
130 opt = po->po_optv[i];
131
132 for (i = j = 0; i < argc; i++)
133 {
134 if (argv[i][0] == '+')
135 {
136 opt->opt_set (po, opt, argv[i] + 1);
137 }
138 else
139 argv[j++] = argv[i];
140 }
141 argv[j] = NULL;
142 *pargc = j;
143 }
144
145 void
mh_opt_set_folder(struct mu_parseopt * po,struct mu_option * opt,char const * arg)146 mh_opt_set_folder (struct mu_parseopt *po, struct mu_option *opt,
147 char const *arg)
148 {
149 mh_set_current_folder (arg);
150 }
151
152 static struct mu_option folder_option[] = {
153 { "folder", 0, N_("FOLDER"), MU_OPTION_DEFAULT,
154 N_("set current folder"),
155 mu_c_string, NULL, mh_opt_set_folder },
156 MU_OPTION_END
157 };
158
159 void
mh_version_hook(struct mu_parseopt * po,mu_stream_t stream)160 mh_version_hook (struct mu_parseopt *po, mu_stream_t stream)
161 {
162 mu_stream_printf (stream, "%s (%s %s)\n", mu_program_name,
163 PACKAGE_NAME, PACKAGE_VERSION);
164 #if MU_GIT_COMMIT_DISTANCE > 0
165 mu_stream_printf (stream, "version: %s %s-%d [%s]\n",
166 PACKAGE_NAME, PACKAGE_VERSION,
167 MU_GIT_COMMIT_DISTANCE,
168 MU_GIT_DESCRIBE_STRING);
169 #endif
170 /* TRANSLATORS: Translate "(C)" to the copyright symbol
171 (C-in-a-circle), if this symbol is available in the user's
172 locale. Otherwise, do not translate "(C)"; leave it as-is. */
173 mu_stream_printf (stream, mu_version_copyright, _("(C)"));
174 mu_stream_printf (stream, _("\
175 \n\
176 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\
177 There is NO WARRANTY, to the extent permitted by law.\n\
178 \n\
179 "));
180 }
181
182 static int
has_folder_option(struct mu_option * opt)183 has_folder_option (struct mu_option *opt)
184 {
185 while (!MU_OPTION_IS_END (opt))
186 {
187 if (MU_OPTION_IS_VALID_LONG_OPTION (opt)
188 && strcmp (opt->opt_long, "folder") == 0)
189 return 1;
190 ++opt;
191 }
192 return 0;
193 }
194
195 static void
opt_init(struct mu_parseopt * po,struct mu_option ** optv,struct mh_optinit * optinit)196 opt_init (struct mu_parseopt *po,
197 struct mu_option **optv, struct mh_optinit *optinit)
198 {
199 if (!optinit)
200 return;
201 for (; optinit->opt; optinit++)
202 {
203 size_t i;
204 for (i = 0; optv[i]; i++)
205 {
206 struct mu_option *opt;
207 for (opt = optv[i]; !MU_OPTION_IS_END (opt); opt++)
208 {
209 if (strcmp (opt->opt_long, optinit->opt) == 0)
210 {
211 char const *val = mh_global_profile_get (optinit->var, NULL);
212 if (val)
213 {
214 (opt->opt_set ?
215 opt->opt_set : mu_option_set_value) (po, opt, val);
216 }
217 break;
218 }
219 }
220 }
221 }
222 }
223
224 void
mh_getopt_ext(int * pargc,char *** pargv,struct mu_option * options,int mhflags,struct mh_optinit * optinit,char * argdoc,char * progdoc,char * extradoc)225 mh_getopt_ext (int *pargc, char ***pargv, struct mu_option *options,
226 int mhflags, struct mh_optinit *optinit,
227 char *argdoc, char *progdoc, char *extradoc)
228 {
229 int argc = *pargc;
230 char **argv = *pargv;
231 struct mu_parseopt po;
232 struct mu_option *optv[3];
233 struct getopt_data getopt_data;
234 char const *args[3];
235 int flags = MU_PARSEOPT_SINGLE_DASH | MU_PARSEOPT_IMMEDIATE;
236 int i;
237
238 /* Native Language Support */
239 MU_APP_INIT_NLS ();
240
241 po.po_negation = "no";
242 flags |= MU_PARSEOPT_NEGATION;
243
244 if ((mhflags & MH_GETOPT_DEFAULT_FOLDER) || has_folder_option (options))
245 {
246 po.po_special_args = N_("[+FOLDER]");
247 flags |= MU_PARSEOPT_SPECIAL_ARGS;
248 }
249
250 if (argdoc)
251 {
252 args[0] = argdoc;
253 args[1] = NULL;
254 po.po_prog_args = args;
255 flags |= MU_PARSEOPT_PROG_ARGS;
256 }
257 if (progdoc)
258 {
259 po.po_prog_doc = progdoc;
260 flags |= MU_PARSEOPT_PROG_DOC;
261 }
262
263 getopt_data.extra_doc = extradoc;
264 if (extradoc)
265 {
266 po.po_help_hook = mh_extra_help_hook;
267 flags |= MU_PARSEOPT_HELP_HOOK;
268 }
269
270 po.po_data = &getopt_data;
271 flags |= MU_PARSEOPT_DATA;
272
273 po.po_exit_error = 1;
274 flags |= MU_PARSEOPT_EXIT_ERROR;
275
276 po.po_package_name = PACKAGE_NAME;
277 flags |= MU_PARSEOPT_PACKAGE_NAME;
278
279 po.po_package_url = PACKAGE_URL;
280 flags |= MU_PARSEOPT_PACKAGE_URL;
281
282 po.po_bug_address = PACKAGE_BUGREPORT;
283 flags |= MU_PARSEOPT_BUG_ADDRESS;
284
285 //po.po_extra_info = gnu_general_help_url;
286 //flags |= MU_PARSEOPT_EXTRA_INFO;
287
288 po.po_version_hook = mh_version_hook;
289 flags |= MU_PARSEOPT_VERSION_HOOK;
290
291 mu_set_program_name (argv[0]);
292 mh_init ();
293 augment_argv (&argc, &argv);
294
295 i = 0;
296 if (mhflags & MH_GETOPT_DEFAULT_FOLDER)
297 optv[i++] = folder_option;
298 if (options)
299 optv[i++] = options;
300 optv[i] = NULL;
301 opt_init (&po, optv, optinit);
302
303 if (mu_parseopt (&po, argc, argv, optv, flags))
304 exit (po.po_exit_error);
305
306 argc -= po.po_arg_start;
307 argv += po.po_arg_start;
308
309 process_std_options (argc, argv, &po);
310
311 process_folder_arg (&argc, argv, &po);
312
313 if (!argdoc && argc)
314 {
315 mu_diag_init ();
316 mu_stream_printf (mu_strerr, "\033s<%d>", MU_DIAG_ERROR);
317 mu_stream_printf (mu_strerr, "%s", _("unrecognized extra arguments:"));
318 for (i = 0; i < argc; i++)
319 mu_stream_printf (mu_strerr, " %s", argv[i]);
320 mu_stream_write (mu_strerr, "\n", 1, NULL);
321 exit (1);
322 }
323
324 *pargc = argc;
325 *pargv = argv;
326
327 mh_init2 ();
328 }
329
330 void
mh_getopt(int * pargc,char *** pargv,struct mu_option * options,int mhflags,char * argdoc,char * progdoc,char * extradoc)331 mh_getopt (int *pargc, char ***pargv, struct mu_option *options,
332 int mhflags, char *argdoc, char *progdoc, char *extradoc)
333 {
334 mh_getopt_ext (pargc, pargv, options, mhflags, NULL, argdoc, progdoc,
335 extradoc);
336 }
337
338 void
mh_opt_notimpl(struct mu_parseopt * po,struct mu_option * opt,char const * arg)339 mh_opt_notimpl (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
340 {
341 mu_error (_("option is not yet implemented: %s"), opt->opt_long);
342 exit (1);
343 }
344
345 void
mh_opt_notimpl_warning(struct mu_parseopt * po,struct mu_option * opt,char const * arg)346 mh_opt_notimpl_warning (struct mu_parseopt *po, struct mu_option *opt,
347 char const *arg)
348 {
349 if (opt->opt_type == mu_c_bool)
350 {
351 int val;
352 if (mu_str_to_c (arg, opt->opt_type, &val, NULL) == 0 && !val)
353 return;
354 }
355 mu_error (_("ignoring not implemented option %s"), opt->opt_long);
356 }
357
358 void
mh_opt_clear_string(struct mu_parseopt * po,struct mu_option * opt,char const * arg)359 mh_opt_clear_string (struct mu_parseopt *po, struct mu_option *opt,
360 char const *arg)
361 {
362 char **sptr = opt->opt_ptr;
363 free (*sptr);
364 *sptr = NULL;
365 }
366
367 void
mh_opt_find_file(struct mu_parseopt * po,struct mu_option * opt,char const * arg)368 mh_opt_find_file (struct mu_parseopt *po, struct mu_option *opt,
369 char const *arg)
370 {
371 mh_find_file (arg, opt->opt_ptr);
372 }
373
374 void
mh_opt_read_formfile(struct mu_parseopt * po,struct mu_option * opt,char const * arg)375 mh_opt_read_formfile (struct mu_parseopt *po, struct mu_option *opt,
376 char const *arg)
377 {
378 mh_read_formfile (arg, opt->opt_ptr);
379 }
380
381 void
mh_opt_parse_formfile(struct mu_parseopt * po,struct mu_option * opt,char const * arg)382 mh_opt_parse_formfile (struct mu_parseopt *po, struct mu_option *opt,
383 char const *arg)
384 {
385 mh_format_destroy ((mh_format_t*) opt->opt_ptr);
386 if (mh_format_file_parse (opt->opt_ptr, arg, MH_FMT_PARSE_DEFAULT))
387 exit (1);
388 }
389
390 void
mh_opt_parse_format(struct mu_parseopt * po,struct mu_option * opt,char const * arg)391 mh_opt_parse_format (struct mu_parseopt *po, struct mu_option *opt,
392 char const *arg)
393 {
394 mh_format_destroy ((mh_format_t*) opt->opt_ptr);
395 if (mh_format_string_parse (opt->opt_ptr, arg, NULL, MH_FMT_PARSE_DEFAULT))
396 exit (1);
397 }
398