NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
parse.c
Go to the documentation of this file.
1
22
28
29#include "config.h"
30#include <getopt.h>
31#include <stdbool.h>
32#include <string.h>
33#include <sys/param.h> // IWYU pragma: keep
34#include <unistd.h>
35#include "mutt/lib.h"
36#include "objects.h"
37
44static const struct option LongOptions[] = {
45 // clang-format off
46 // Shared options
47 { "command", required_argument, NULL, 'e' },
48 { "config", required_argument, NULL, 'F' },
49 { "debug-file", required_argument, NULL, 'l' },
50 { "debug-level", required_argument, NULL, 'd' },
51 { "mbox-type", required_argument, NULL, 'm' },
52 { "no-system-config", no_argument, NULL, 'n' },
53
54 // Help options
55 { "help", no_argument, NULL, 'h' },
56 { "license", no_argument, NULL, 'L' },
57 { "version", no_argument, NULL, 'v' },
58
59 // Info options
60 { "alias", required_argument, NULL, 'A' },
61 { "dump-changed-config", no_argument, NULL, 'X' },
62 { "dump-config", no_argument, NULL, 'D' },
63 { "hide-sensitive", no_argument, NULL, 'S' },
64 { "query", required_argument, NULL, 'Q' },
65 { "with-docs", no_argument, NULL, 'O' },
66
67 // Send options
68 { "attach", required_argument, NULL, 'a' },
69 { "bcc", required_argument, NULL, 'b' },
70 { "cc", required_argument, NULL, 'c' },
71 { "crypto", no_argument, NULL, 'C' },
72 { "draft", required_argument, NULL, 'H' },
73 { "edit-message", no_argument, NULL, 'E' },
74 { "include", required_argument, NULL, 'i' },
75 { "subject", required_argument, NULL, 's' },
76
77 // TUI options
78 { "browser", no_argument, NULL, 'y' },
79 { "check-any-mail", no_argument, NULL, 'z' },
80 { "check-new-mail", no_argument, NULL, 'Z' },
81 { "folder", required_argument, NULL, 'f' },
82 { "nntp-browser", no_argument, NULL, 'G' },
83 { "nntp-server", required_argument, NULL, 'g' },
84 { "postponed", no_argument, NULL, 'p' },
85 { "read-only", no_argument, NULL, 'R' },
86
87 { NULL, 0, NULL, 0 },
88 // clang-format on
89};
90
96int check_help_mode(const char *mode)
97{
98 if (mutt_istr_equal(mode, "shared"))
99 return HM_SHARED;
100 if (mutt_istr_equal(mode, "help"))
101 return HM_HELP;
102 if (mutt_istr_equal(mode, "info"))
103 return HM_INFO;
104 if (mutt_istr_equal(mode, "send"))
105 return HM_SEND;
106 if (mutt_istr_equal(mode, "tui"))
107 return HM_TUI;
108 if (mutt_istr_equal(mode, "all"))
109 return HM_ALL;
110 return 0;
111}
112
126static int mop_up(int argc, char *const *argv, int index, struct StringArray *sa)
127{
128 int count = 0;
129 for (int i = index; i < argc; i++, count++)
130 {
131 // Stop if we find '--' or '-X'
132 if ((argv[i][0] == '-') && (argv[i][1] != '\0'))
133 break;
134
135 ARRAY_ADD(sa, mutt_str_dup(argv[i]));
136 }
137
138 return count;
139}
140
148bool cli_parse(int argc, char *const *argv, struct CommandLine *cli)
149{
150 if ((argc < 1) || !argv || !cli)
151 return false;
152
153 // Check for unsupported '=' syntax in options
154 for (int i = 1; i < argc; i++)
155 {
156 if ((argv[i][0] == '-') && (argv[i][1] != '\0'))
157 {
158 const char *equals = strchr(argv[i], '=');
159 if (equals)
160 {
161 // L10N: Neomutt doesn't support `-X=VALUE` or `--OPTION=VALUE`
162 // Use `-X VALUE` or `--OPTION VALUE` instead
163 mutt_warning(_("Invalid option syntax: %s (use space instead of '=')"), argv[i]);
164 cli->help.help = true;
165 cli->help.is_set = true;
166 return false;
167 }
168 }
169 }
170
171 // Any leading non-options must be addresses
172 int count = mop_up(argc, argv, 1, &cli->send.addresses);
173 if (count > 0)
174 {
175 cli->send.is_set = true;
176 argc -= count;
177 argv += count;
178 }
179
180 bool rc = true;
181
182 opterr = 0; // We'll handle the errors
183// Always initialise getopt() or the tests will fail
184#if defined(BSD) || defined(__APPLE__)
185 optreset = 1;
186 optind = 1;
187#else
188 optind = 0;
189#endif
190
191 while (rc && (argc > 1) && (optind < argc))
192 {
193 int opt = getopt_long(argc, argv, "+:A:a:b:Cc:Dd:Ee:F:f:Gg:H:hi:l:m:nOpQ:RSs:vyZz",
194 LongOptions, NULL);
195 switch (opt)
196 {
197 // ------------------------------------------------------------
198 // Shared
199 case 'F': // user config file
200 {
201 ARRAY_ADD(&cli->shared.user_files, mutt_str_dup(optarg));
202 cli->shared.is_set = true;
203 break;
204 }
205 case 'n': // no system config file
206 {
207 cli->shared.disable_system = true;
208 cli->shared.is_set = true;
209 break;
210 }
211 case 'e': // enter commands
212 {
213 ARRAY_ADD(&cli->shared.commands, mutt_str_dup(optarg));
214 cli->shared.is_set = true;
215 break;
216 }
217 case 'm': // mbox type
218 {
219 buf_strcpy(&cli->shared.mbox_type, optarg);
220 cli->shared.is_set = true;
221 break;
222 }
223 case 'd': // log level
224 {
225 buf_strcpy(&cli->shared.log_level, optarg);
226 cli->shared.is_set = true;
227 break;
228 }
229 case 'l': // log file
230 {
231 buf_strcpy(&cli->shared.log_file, optarg);
232 cli->shared.is_set = true;
233 break;
234 }
235
236 // ------------------------------------------------------------
237 // Help
238 case 'h': // help
239 {
240 if (optind < argc)
241 {
242 cli->help.mode = check_help_mode(argv[optind]);
243 if (cli->help.mode != HM_NONE)
244 optind++;
245 }
246 cli->help.help = true;
247 cli->help.is_set = true;
248 break;
249 }
250 case 'L': // license
251 {
252 cli->help.license = true;
253 cli->help.is_set = true;
254 break;
255 }
256 case 'v': // version, license
257 {
258 if (cli->help.version)
259 cli->help.license = true;
260 else
261 cli->help.version = true;
262
263 cli->help.is_set = true;
264 break;
265 }
266
267 // ------------------------------------------------------------
268 // Info
269 case 'A': // alias lookup
270 {
272 // '-A' can take multiple arguments
273 optind += mop_up(argc, argv, optind, &cli->info.alias_queries);
274 cli->info.is_set = true;
275 break;
276 }
277 case 'D': // dump config, dump changed
278 {
279 if (cli->info.dump_config)
280 cli->info.dump_changed = true;
281 else
282 cli->info.dump_config = true;
283
284 cli->info.is_set = true;
285 break;
286 }
287 case 'O': // one-liner help
288 {
289 cli->info.show_help = true;
290 cli->info.is_set = true;
291 break;
292 }
293 case 'Q': // query config&cli->send.attach
294 {
295 ARRAY_ADD(&cli->info.queries, mutt_str_dup(optarg));
296 // '-Q' can take multiple arguments
297 optind += mop_up(argc, argv, optind, &cli->info.queries);
298 cli->info.is_set = true;
299 break;
300 }
301 case 'S': // hide sensitive
302 {
303 cli->info.hide_sensitive = true;
304 cli->info.is_set = true;
305 break;
306 }
307 case 'X': // dump changed
308 {
309 cli->info.dump_config = true;
310 cli->info.dump_changed = true;
311 cli->info.is_set = true;
312 break;
313 }
314
315 // ------------------------------------------------------------
316 // Send
317 case 'a': // attach file
318 {
319 ARRAY_ADD(&cli->send.attach, mutt_str_dup(optarg));
320 // `-a` can take multiple arguments
321 optind += mop_up(argc, argv, optind, &cli->send.attach);
322 cli->send.is_set = true;
323 break;
324 }
325 case 'b': // bcc:
326 {
327 ARRAY_ADD(&cli->send.bcc_list, mutt_str_dup(optarg));
328 cli->send.is_set = true;
329 break;
330 }
331 case 'C': // crypto
332 {
333 cli->send.use_crypto = true;
334 cli->send.is_set = true;
335 break;
336 }
337 case 'c': // cc:
338 {
339 ARRAY_ADD(&cli->send.cc_list, mutt_str_dup(optarg));
340 cli->send.is_set = true;
341 break;
342 }
343 case 'E': // edit file
344 {
345 cli->send.edit_infile = true;
346 cli->send.is_set = true;
347 break;
348 }
349 case 'H': // draft file
350 {
351 buf_strcpy(&cli->send.draft_file, optarg);
352 cli->send.is_set = true;
353 break;
354 }
355 case 'i': // include file
356 {
357 buf_strcpy(&cli->send.include_file, optarg);
358 cli->send.is_set = true;
359 break;
360 }
361 case 's': // subject:
362 {
363 buf_strcpy(&cli->send.subject, optarg);
364 cli->send.is_set = true;
365 break;
366 }
367
368 // ------------------------------------------------------------
369 // TUI
370 case 'f': // start folder
371 {
372 buf_strcpy(&cli->tui.folder, optarg);
373 cli->tui.is_set = true;
374 break;
375 }
376 case 'G': // list newsgroups
377 {
378 cli->tui.start_nntp = true;
379 cli->tui.is_set = true;
380 break;
381 }
382 case 'g': // news server
383 {
384 cli->tui.start_nntp = true;
385 buf_strcpy(&cli->tui.nntp_server, optarg);
386 cli->tui.is_set = true;
387 break;
388 }
389 case 'p': // postponed
390 {
391 cli->tui.start_postponed = true;
392 cli->tui.is_set = true;
393 break;
394 }
395 case 'R': // read-only
396 {
397 cli->tui.read_only = true;
398 cli->tui.is_set = true;
399 break;
400 }
401 case 'y': // browser
402 {
403 cli->tui.start_browser = true;
404 cli->tui.is_set = true;
405 break;
406 }
407 case 'Z': // new mail
408 {
409 cli->tui.start_new_mail = true;
410 cli->tui.is_set = true;
411 break;
412 }
413 case 'z': // any mail
414 {
415 cli->tui.start_any_mail = true;
416 cli->tui.is_set = true;
417 break;
418 }
419
420 // ------------------------------------------------------------
421 case -1: // end of options
422 {
423 for (int i = optind; i < argc; i++)
424 {
425 ARRAY_ADD(&cli->send.addresses, mutt_str_dup(argv[i]));
426 }
427 cli->send.is_set = true;
428 optind = argc; // finish parsing
429 break;
430 }
431 default: // error
432 {
433 if (opt == '?')
434 {
435 // L10N: e.g. `neomutt -X`
436 mutt_warning(_("Invalid option: %c"), optopt);
437 }
438 else if (opt == ':')
439 {
440 // L10N: e.g. `neomutt -F`
441 mutt_warning(_("Option %c requires an argument"), optopt);
442 }
443
444 cli->help.help = true;
445 cli->help.is_set = true;
446 rc = false; // stop parsing
447 break;
448 }
449 }
450 }
451
452 return rc;
453}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const struct option LongOptions[]
Long option definitions for getopt_long()
Definition parse.c:44
bool cli_parse(int argc, char *const *argv, struct CommandLine *cli)
Parse the Command Line.
Definition parse.c:148
static int mop_up(int argc, char *const *argv, int index, struct StringArray *sa)
Eat multiple arguments.
Definition parse.c:126
int check_help_mode(const char *mode)
Check for help mode.
Definition parse.c:96
#define mutt_warning(...)
Definition logging2.h:92
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:674
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
Parse the Command Line.
@ HM_SEND
Help about sending email options.
Definition objects.h:38
@ HM_ALL
Help about all options.
Definition objects.h:40
@ HM_HELP
Help about help.
Definition objects.h:36
@ HM_INFO
Help about info options.
Definition objects.h:37
@ HM_TUI
Help about starting the tui options.
Definition objects.h:39
@ HM_NONE
No extra help.
Definition objects.h:34
@ HM_SHARED
Help about shared config options.
Definition objects.h:35
bool version
-v Print version
Definition objects.h:67
bool license
-vv Print license
Definition objects.h:68
enum HelpMode mode
Display detailed help.
Definition objects.h:70
bool help
-h Print help
Definition objects.h:66
bool is_set
This struct has been used.
Definition objects.h:65
bool show_help
-O Show one-liner help
Definition objects.h:81
bool is_set
This struct has been used.
Definition objects.h:78
struct StringArray queries
-Q Query a config option
Definition objects.h:85
struct StringArray alias_queries
-A Lookup an alias
Definition objects.h:84
bool dump_config
-D Dump the config options
Definition objects.h:79
bool dump_changed
-DD Dump the changed config options
Definition objects.h:80
bool hide_sensitive
-S Hide sensitive config
Definition objects.h:82
struct Buffer draft_file
-H Use this draft file
Definition objects.h:102
bool is_set
This struct has been used.
Definition objects.h:93
struct Buffer include_file
-i Use this include file
Definition objects.h:103
struct StringArray cc_list
-c Add a Cc:
Definition objects.h:99
struct StringArray attach
-a Attach a file
Definition objects.h:97
bool use_crypto
-C Use CLI crypto
Definition objects.h:94
bool edit_infile
-E Edit the draft/include
Definition objects.h:95
struct StringArray bcc_list
-b Add a Bcc:
Definition objects.h:98
struct StringArray addresses
Send to these addresses.
Definition objects.h:100
struct Buffer subject
-s Use this Subject:
Definition objects.h:104
struct Buffer log_level
-d Debug log level
Definition objects.h:56
struct Buffer log_file
-l Debug log file
Definition objects.h:57
struct StringArray commands
-e Run these commands
Definition objects.h:53
bool is_set
This struct has been used.
Definition objects.h:48
bool disable_system
-n Don't read the system config file
Definition objects.h:51
struct StringArray user_files
-F Use these user config files
Definition objects.h:50
struct Buffer mbox_type
-m Set the default Mailbox type
Definition objects.h:54
bool read_only
-R Open Mailbox read-only
Definition objects.h:113
bool start_any_mail
-z Check for Any Mail
Definition objects.h:118
bool start_nntp
-G Open an NNTP Mailbox
Definition objects.h:116
struct Buffer nntp_server
-g Open this NNTP Mailbox
Definition objects.h:121
bool is_set
This struct has been used.
Definition objects.h:112
struct Buffer folder
-f Open this Mailbox
Definition objects.h:120
bool start_postponed
-p Open Postponed emails
Definition objects.h:114
bool start_new_mail
-Z Check for New Mail
Definition objects.h:117
bool start_browser
-y Open the Mailbox Browser
Definition objects.h:115
Command Line options.
Definition objects.h:128
struct CliSend send
Send Mode command line options.
Definition objects.h:132
struct CliShared shared
Shared command line options.
Definition objects.h:129
struct CliHelp help
Help Mode command line options.
Definition objects.h:130
struct CliInfo info
Info Mode command line options.
Definition objects.h:131
struct CliTui tui
Tui Mode command line options.
Definition objects.h:133