Commit af202127 af20212718728b315449ff35685772302883fc3d by Sergey Poznyakoff

Fix operation of mail -t; some other minor fixes

* mail/escape.c (parse_headers): Moved to send.c
(check_headers): New function.
(escape_continue): Use check_headers.
* mail/mail.c (read_recipients): New variable.
The -t option sets read_recipients and editheaders
* mail/mail.h (parse_headers): New proto.
* mail/send.c: Special handling for -t option.
(parse_headers): New function.
* movemail/movemail.c (onerror statement): Accept a list as
argument.

* doc/texinfo/mailutils.texi: Update.
* doc/texinfo/programs.texi: Update.
1 parent edb05144
...@@ -236,7 +236,6 @@ Reading Mail ...@@ -236,7 +236,6 @@ Reading Mail
236 @command{movemail} --- Moves Mail from the User Maildrop to the Local File 236 @command{movemail} --- Moves Mail from the User Maildrop to the Local File
237 237
238 * Movemail Configuration:: 238 * Movemail Configuration::
239 * Movemail Options:: Description of the Available Options
240 * Ownership:: Setting Destination Mailbox Ownership 239 * Ownership:: Setting Destination Mailbox Ownership
241 * Summary:: Short Movemail Invocation Summary 240 * Summary:: Short Movemail Invocation Summary
242 241
......
...@@ -37,117 +37,24 @@ dump_headers (mu_stream_t out, compose_env_t *env) ...@@ -37,117 +37,24 @@ dump_headers (mu_stream_t out, compose_env_t *env)
37 mu_stream_destroy (&stream); 37 mu_stream_destroy (&stream);
38 } 38 }
39 39
40 #define STATE_INIT 0
41 #define STATE_READ 1
42 #define STATE_BODY 2
43
44 static int 40 static int
45 parse_headers (mu_stream_t input, compose_env_t *env) 41 check_headers (mu_stream_t input, compose_env_t *env)
46 { 42 {
47 int status;
48 mu_header_t header;
49 char *name = NULL;
50 char *value = NULL;
51 int state = STATE_INIT;
52 char *buf = NULL;
53 size_t size = 0, n;
54 int errcnt = 0, line = 0;
55
56 if ((status = mu_header_create (&header, NULL, 0)) != 0)
57 {
58 mu_error (_("Cannot create header: %s"), mu_strerror (status));
59 return 1;
60 }
61
62 mu_stream_seek (input, 0, MU_SEEK_SET, NULL);
63 while (state != STATE_BODY &&
64 errcnt == 0 &&
65 mu_stream_getline (input, &buf, &size, &n) == 0 && n > 0)
66 {
67 mu_rtrim_class (buf, MU_CTYPE_SPACE);
68
69 line++;
70 switch (state)
71 {
72 case STATE_INIT:
73 if (!buf[0] || mu_isspace (buf[0]))
74 continue;
75 else
76 state = STATE_READ;
77 /*FALLTHRU*/
78
79 case STATE_READ:
80 if (buf[0] == 0)
81 state = STATE_BODY;
82 else if (mu_isspace (buf[0]))
83 {
84 /* A continuation line */
85 if (name)
86 {
87 char *p = NULL;
88 mu_asprintf (&p, "%s\n%s", value, buf);
89 free (value);
90 value = p;
91 }
92 else
93 {
94 mu_error (_("%d: not a header line"), line);
95 errcnt++;
96 }
97 }
98 else
99 {
100 char *p; 43 char *p;
101 44
102 if (name) 45 mu_stream_seek (input, 0, MU_SEEK_SET, NULL);
103 { 46 switch (parse_headers (input, env))
104 mu_header_set_value (header, name, value[0] ? value : NULL, 0);
105 free (name);
106 free (value);
107 name = value = NULL;
108 }
109 p = strchr (buf, ':');
110 if (p)
111 {
112 *p++ = 0;
113 while (*p && mu_isspace (*p))
114 p++;
115 value = mu_strdup (p);
116 name = mu_strdup (buf);
117 }
118 else
119 { 47 {
120 mu_error (_("%d: not a header line"), line); 48 case parse_headers_ok:
121 errcnt++; 49 return 0;
122 } 50 case parse_headers_fatal:
123 } 51 return -1;
52 case parse_headers_error:
124 break; 53 break;
125 } 54 }
126 }
127
128 free (buf);
129 if (name)
130 {
131 mu_header_set_value (header, name, value, 0);
132 free (name);
133 free (value);
134 }
135
136 if (errcnt)
137 {
138 char *p;
139 55
140 mu_header_destroy (&header);
141 p = ml_readline (_("Edit again?")); 56 p = ml_readline (_("Edit again?"));
142 if (mu_true_answer_p (p) == 1) 57 return mu_true_answer_p (p);
143 return -1;
144 else
145 return 1;
146 }
147
148 mu_header_destroy (&env->header);
149 env->header = header;
150 return 0;
151 } 58 }
152 59
153 static void 60 static void
...@@ -386,7 +293,7 @@ escape_run_editor (char *ed, int argc, char **argv, compose_env_t *env) ...@@ -386,7 +293,7 @@ escape_run_editor (char *ed, int argc, char **argv, compose_env_t *env)
386 return rc; 293 return rc;
387 } 294 }
388 } 295 }
389 while ((rc = parse_headers (tempstream, env)) < 0); 296 while (check_headers (tempstream, env));
390 } 297 }
391 else 298 else
392 { 299 {
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
23 mu_mailbox_t mbox; /* Mailbox being operated upon */ 23 mu_mailbox_t mbox; /* Mailbox being operated upon */
24 size_t total; /* Total number of messages in the mailbox */ 24 size_t total; /* Total number of messages in the mailbox */
25 int interactive; /* Is the session interactive */ 25 int interactive; /* Is the session interactive */
26 int read_recipients; /* Read recipients from the message (mail -t) */
26 27
27 static mu_list_t command_list;/* List of commands to be executed after parsing 28 static mu_list_t command_list;/* List of commands to be executed after parsing
28 command line */ 29 command line */
...@@ -72,6 +73,8 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt, ...@@ -72,6 +73,8 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
72 break; 73 break;
73 74
74 case 't': 75 case 't':
76 read_recipients = 1;
77 util_cache_command (&command_list, "set editheaders");
75 util_cache_command (&command_list, "setq mode=send"); 78 util_cache_command (&command_list, "setq mode=send");
76 break; 79 break;
77 80
...@@ -186,7 +189,7 @@ static struct mu_option mail_options[] = { ...@@ -186,7 +189,7 @@ static struct mu_option mail_options[] = {
186 mu_c_string, NULL, cli_subject }, 189 mu_c_string, NULL, cli_subject },
187 190
188 { "to", 't', NULL, MU_OPTION_DEFAULT, 191 { "to", 't', NULL, MU_OPTION_DEFAULT,
189 N_("precede message by a list of addresses"), 192 N_("read recipients from the message header"),
190 mu_c_string, NULL, cli_command_option }, 193 mu_c_string, NULL, cli_command_option },
191 194
192 { "user", 'u', N_("USER"), MU_OPTION_DEFAULT, 195 { "user", 'u', N_("USER"), MU_OPTION_DEFAULT,
...@@ -227,7 +230,7 @@ static struct mu_cli_setup cli = { ...@@ -227,7 +230,7 @@ static struct mu_cli_setup cli = {
227 options, 230 options,
228 NULL, 231 NULL,
229 N_("GNU mail -- process mail messages.\n" 232 N_("GNU mail -- process mail messages.\n"
230 "If -f or --file is given, mail operates on the mailbox named " 233 "If -f or --file is given, mail operates on the mailbox named "
231 "by the first argument, or the user's mbox, if no argument given."), 234 "by the first argument, or the user's mbox, if no argument given."),
232 N_("[address...]"), 235 N_("[address...]"),
233 alt_args, 236 alt_args,
...@@ -421,6 +424,12 @@ main (int argc, char **argv) ...@@ -421,6 +424,12 @@ main (int argc, char **argv)
421 /* argument parsing */ 424 /* argument parsing */
422 mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv); 425 mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv);
423 426
427 if (read_recipients)
428 {
429 argv += argc;
430 argc = 0;
431 }
432
424 if ((hint & (HINT_SEND_MODE|HINT_FILE_OPTION)) == 433 if ((hint & (HINT_SEND_MODE|HINT_FILE_OPTION)) ==
425 (HINT_SEND_MODE|HINT_FILE_OPTION)) 434 (HINT_SEND_MODE|HINT_FILE_OPTION))
426 { 435 {
......
...@@ -288,6 +288,15 @@ extern int escape_attach (int argc, char **argv, compose_env_t *env); ...@@ -288,6 +288,15 @@ extern int escape_attach (int argc, char **argv, compose_env_t *env);
288 extern int escape_remove_attachment (int argc, char **argv, 288 extern int escape_remove_attachment (int argc, char **argv,
289 compose_env_t *env); 289 compose_env_t *env);
290 290
291 enum
292 {
293 parse_headers_ok,
294 parse_headers_error,
295 parse_headers_fatal
296 };
297
298 extern int parse_headers (mu_stream_t input, compose_env_t *env);
299
291 /* Cursor */ 300 /* Cursor */
292 extern void set_cursor (unsigned value); 301 extern void set_cursor (unsigned value);
293 extern size_t get_cursor (void); 302 extern size_t get_cursor (void);
......
...@@ -488,8 +488,42 @@ mail_send (int argc, char **argv) ...@@ -488,8 +488,42 @@ mail_send (int argc, char **argv)
488 compose_init (&env); 488 compose_init (&env);
489 489
490 if (argc < 2) 490 if (argc < 2)
491 {
492 if (interactive)
491 compose_header_set (&env, MU_HEADER_TO, ml_readline_with_intr ("To: "), 493 compose_header_set (&env, MU_HEADER_TO, ml_readline_with_intr ("To: "),
492 COMPOSE_REPLACE); 494 COMPOSE_REPLACE);
495 else if (!mailvar_get (NULL, "editheaders", mailvar_type_boolean, 0))
496 {
497 if (parse_headers (mu_strin, &env) != parse_headers_ok)
498 {
499 mu_error ("%s", _("Errors parsing message"));
500 exit (EXIT_FAILURE);
501 }
502 if (add_header_list)
503 {
504 mu_iterator_t itr;
505 mu_list_get_iterator (add_header_list, &itr);
506 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
507 mu_iterator_next (itr))
508 {
509 struct add_header *hp;
510 int mode;
511 if (mu_iterator_current (itr, (void**) &hp))
512 break;
513 mode = hp->mode;
514 if (mu_header_sget_value (env.header, hp->name, NULL) == 0)
515 mode = COMPOSE_REPLACE;
516 compose_header_set (&env, hp->name, hp->value, hp->mode);
517 }
518 mu_iterator_destroy (&itr);
519 }
520 }
521 else
522 {
523 mu_error ("%s", _("No recipients specified"));
524 exit (EXIT_FAILURE);
525 }
526 }
493 else 527 else
494 { 528 {
495 while (--argc) 529 while (--argc)
...@@ -511,27 +545,137 @@ mail_send (int argc, char **argv) ...@@ -511,27 +545,137 @@ mail_send (int argc, char **argv)
511 } 545 }
512 } 546 }
513 547
548 if (interactive)
549 {
514 if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0)) 550 if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0))
515 read_cc_bcc (&env); 551 read_cc_bcc (&env);
516 552
517 if (mailvar_get (NULL, "asksub", mailvar_type_boolean, 0) == 0) 553 if (mailvar_get (NULL, "asksub", mailvar_type_boolean, 0) == 0)
518 compose_header_set (&env, MU_HEADER_SUBJECT, 554 compose_header_set (&env, MU_HEADER_SUBJECT,
519 ml_readline_with_intr ("Subject: "), COMPOSE_REPLACE); 555 ml_readline_with_intr ("Subject: "),
556 COMPOSE_REPLACE);
557 }
520 558
521 status = mail_send0 (&env, save_to); 559 status = mail_send0 (&env, save_to);
522 compose_destroy (&env); 560 compose_destroy (&env);
523 return status; 561 return status;
524 } 562 }
525 563
564 int
565 parse_headers (mu_stream_t input, compose_env_t *env)
566 {
567 int status;
568 mu_header_t header;
569 char *name = NULL;
570 char *value = NULL;
571 enum { STATE_INIT, STATE_READ, STATE_BODY } state = STATE_INIT;
572 char *buf = NULL;
573 size_t size = 0, n;
574 int errcnt = 0, line = 0;
575
576 if ((status = mu_header_create (&header, NULL, 0)) != 0)
577 {
578 mu_error (_("Cannot create header: %s"), mu_strerror (status));
579 return parse_headers_fatal;
580 }
581
582 while (state != STATE_BODY &&
583 errcnt == 0 &&
584 mu_stream_getline (input, &buf, &size, &n) == 0 && n > 0)
585 {
586 mu_rtrim_class (buf, MU_CTYPE_SPACE);
587
588 line++;
589 switch (state)
590 {
591 case STATE_INIT:
592 if (!buf[0] || mu_isspace (buf[0]))
593 continue;
594 else
595 state = STATE_READ;
596 /*FALLTHRU*/
597
598 case STATE_READ:
599 if (buf[0] == 0)
600 state = STATE_BODY;
601 else if (mu_isspace (buf[0]))
602 {
603 /* A continuation line */
604 if (name)
605 {
606 char *p = NULL;
607 mu_asprintf (&p, "%s\n%s", value, buf);
608 free (value);
609 value = p;
610 }
611 else
612 {
613 mu_error (_("%d: not a header line"), line);
614 errcnt++;
615 }
616 }
617 else
618 {
619 char *p;
620
621 if (name)
622 {
623 mu_header_set_value (header, name, value[0] ? value : NULL, 0);
624 free (name);
625 free (value);
626 name = value = NULL;
627 }
628 p = strchr (buf, ':');
629 if (p)
630 {
631 *p++ = 0;
632 while (*p && mu_isspace (*p))
633 p++;
634 value = mu_strdup (p);
635 name = mu_strdup (buf);
636 }
637 else
638 {
639 mu_error (_("%d: not a header line"), line);
640 errcnt++;
641 }
642 }
643 break;
644
645 default:
646 abort ();
647 }
648 }
649
650 free (buf);
651 if (name)
652 {
653 mu_header_set_value (header, name, value, 0);
654 free (name);
655 free (value);
656 }
657
658 if (errcnt)
659 {
660 mu_header_destroy (&header);
661 return parse_headers_error;
662 }
663
664 mu_header_destroy (&env->header);
665 env->header = header;
666 return parse_headers_ok;
667 }
668
669
526 void 670 void
527 compose_init (compose_env_t * env) 671 compose_init (compose_env_t *env)
528 { 672 {
529 memset (env, 0, sizeof (*env)); 673 memset (env, 0, sizeof (*env));
530 mu_list_foreach (add_header_list, seed_headers, env); 674 mu_list_foreach (add_header_list, seed_headers, env);
531 } 675 }
532 676
533 int 677 int
534 compose_header_set (compose_env_t * env, const char *name, 678 compose_header_set (compose_env_t *env, const char *name,
535 const char *value, int mode) 679 const char *value, int mode)
536 { 680 {
537 int status; 681 int status;
...@@ -601,7 +745,7 @@ compose_header_set (compose_env_t * env, const char *name, ...@@ -601,7 +745,7 @@ compose_header_set (compose_env_t * env, const char *name,
601 } 745 }
602 746
603 char * 747 char *
604 compose_header_get (compose_env_t * env, char *name, char *defval) 748 compose_header_get (compose_env_t *env, char *name, char *defval)
605 { 749 {
606 char *p; 750 char *p;
607 751
......
...@@ -226,8 +226,8 @@ static const struct mail_escape_entry mail_escape_table[] = { ...@@ -226,8 +226,8 @@ static const struct mail_escape_entry mail_escape_table[] = {
226 {"!", "!", "![shell-command]", escape_shell }, 226 {"!", "!", "![shell-command]", escape_shell },
227 {":", ":", ":[mail-command]", escape_command }, 227 {":", ":", ":[mail-command]", escape_command },
228 {"-", "-", "-[mail-command]", escape_command }, 228 {"-", "-", "-[mail-command]", escape_command },
229 {"+", "+", "+[name [content-type [encoding]]]", escape_attach }, 229 {"+", "+", "+name [content-type [encoding]]", escape_attach },
230 {"^", "^", "^[N]", escape_remove_attachment }, 230 {"^", "^", "^N", escape_remove_attachment },
231 {"?", "?", "?", escape_help }, 231 {"?", "?", "?", escape_help },
232 {"A", "A", "A", escape_sign }, 232 {"A", "A", "A", escape_sign },
233 {"a", "a", "a", escape_sign }, 233 {"a", "a", "a", escape_sign },
......
...@@ -194,51 +194,54 @@ set_mailbox_ownership_list (char const *str) ...@@ -194,51 +194,54 @@ set_mailbox_ownership_list (char const *str)
194 } 194 }
195 195
196 static int 196 static int
197 set_onerror_action (char const *str) 197 set_onerror_action (void *item, void *data)
198 { 198 {
199 struct mu_wordsplit ws; 199 char *str = item;
200
201 if (strcmp (str, "abort") == 0)
202 onerror_flags = 0;
203 else
204 {
200 static struct mu_kwd onerror_kw[] = { 205 static struct mu_kwd onerror_kw[] = {
201 { "skip", ONERROR_SKIP }, 206 { "skip", ONERROR_SKIP },
202 { "delete", ONERROR_DELETE }, 207 { "delete", ONERROR_DELETE },
203 { "count", ONERROR_COUNT }, 208 { "count", ONERROR_COUNT },
204 { NULL } 209 { NULL }
205 }; 210 };
206 int i, flag; 211 int flag, clr = 0;
207 212
208 if (strcmp (str, "abort") == 0) 213 if (strncmp (str, "no", 2) == 0)
209 { 214 {
210 onerror_flags = 0; 215 clr = 1;
211 return 0; 216 str += 2;
212 } 217 }
213 ws.ws_delim = ","; 218 if (mu_kwd_xlat_name (onerror_kw, str, &flag))
214 if (mu_wordsplit (str, &ws,
215 MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
216 MU_WRDSF_DELIM | MU_WRDSF_WS))
217 { 219 {
218 mu_error (_("cannot split argument: %s"), mu_wordsplit_strerror (&ws)); 220 mu_error (_("unknown keyword: %s"), str);
219 return 1; 221 return 1;
220 } 222 }
221 for (i = 0; i < ws.ws_wordc; i++)
222 {
223 int clr = 0;
224 char *name = ws.ws_wordv[i];
225
226 if (strncmp (name, "no", 2) == 0)
227 {
228 clr = 1;
229 name += 2;
230 }
231 if (mu_kwd_xlat_name (onerror_kw, name, &flag))
232 mu_error (_("unknown keyword: %s"), ws.ws_wordv[i]);
233 if (clr) 223 if (clr)
234 onerror_flags &= ~flag; 224 onerror_flags &= ~flag;
235 else 225 else
236 onerror_flags |= flag; 226 onerror_flags |= flag;
237 } 227 }
238 mu_wordsplit_free (&ws);
239 return 0; 228 return 0;
240 } 229 }
241 230
231 static int
232 set_onerror_actions (char const *str)
233 {
234 mu_list_t list;
235 int rc;
236
237 mu_list_create (&list);
238 mu_list_set_destroy_item (list, mu_list_free_item);
239 mu_string_split (str, ",", list);
240 rc = mu_list_foreach (list, set_onerror_action, NULL);
241 mu_list_destroy (&list);
242 return rc;
243 }
244
242 static void 245 static void
243 cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt, 246 cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt,
244 char const *arg) 247 char const *arg)
...@@ -250,7 +253,7 @@ cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt, ...@@ -250,7 +253,7 @@ cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt,
250 static void 253 static void
251 cli_onerror (struct mu_parseopt *po, struct mu_option *opt, char const *arg) 254 cli_onerror (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
252 { 255 {
253 if (set_onerror_action (arg)) 256 if (set_onerror_actions (arg))
254 exit (po->po_exit_error); 257 exit (po->po_exit_error);
255 } 258 }
256 259
...@@ -327,9 +330,20 @@ cb_mailbox_ownership (void *data, mu_config_value_t *val) ...@@ -327,9 +330,20 @@ cb_mailbox_ownership (void *data, mu_config_value_t *val)
327 static int 330 static int
328 cb_onerror (void *data, mu_config_value_t *val) 331 cb_onerror (void *data, mu_config_value_t *val)
329 { 332 {
330 if (mu_cfg_assert_value_type (val, MU_CFG_STRING)) 333 switch (val->type)
331 return 1; 334 {
332 return set_onerror_action (val->v.string); 335 case MU_CFG_LIST:
336 mu_list_foreach (val->v.list, set_onerror_action, NULL);
337 break;
338
339 case MU_CFG_STRING:
340 set_onerror_actions (val->v.string);
341 break;
342
343 default:
344 mu_error ("%s", _("too many arguments"));
345 }
346 return 0;
333 } 347 }
334 348
335 struct mu_cfg_param movemail_cfg_param[] = { 349 struct mu_cfg_param movemail_cfg_param[] = {
...@@ -360,12 +374,12 @@ struct mu_cfg_param movemail_cfg_param[] = { ...@@ -360,12 +374,12 @@ struct mu_cfg_param movemail_cfg_param[] = {
360 { "ignore-errors", mu_c_bool, &ignore_errors, 0, NULL, 374 { "ignore-errors", mu_c_bool, &ignore_errors, 0, NULL,
361 N_("Continue after an error.") }, 375 N_("Continue after an error.") },
362 { "onerror", mu_cfg_callback, NULL, 0, cb_onerror, 376 { "onerror", mu_cfg_callback, NULL, 0, cb_onerror,
363 N_("What to do after an error. Argument is a comma-separated list of:\n" 377 N_("What to do after an error. Argument is a list of:\n"
378 " abort - terminate the program (the default)\n"
364 " skip - skip to the next message\n" 379 " skip - skip to the next message\n"
365 " delete - delete this one and to the next message\n" 380 " delete - delete this one and to the next message\n"
366 " count - count this message as processed\n" 381 " count - count this message as processed\n"
367 "Each keyword can be prefixed with \"no\" to reverse its meaning\n" 382 "Each keyword can be prefixed with \"no\" to reverse its meaning."),
368 "Setting onerror=abort reverts to the default behavior."),
369 N_("arg: list") }, 383 N_("arg: list") },
370 { NULL } 384 { NULL }
371 }; 385 };
......