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
@command{movemail} --- Moves Mail from the User Maildrop to the Local File
* Movemail Configuration::
* Movemail Options:: Description of the Available Options
* Ownership:: Setting Destination Mailbox Ownership
* Summary:: Short Movemail Invocation Summary
......
......@@ -37,117 +37,24 @@ dump_headers (mu_stream_t out, compose_env_t *env)
mu_stream_destroy (&stream);
}
#define STATE_INIT 0
#define STATE_READ 1
#define STATE_BODY 2
static int
parse_headers (mu_stream_t input, compose_env_t *env)
check_headers (mu_stream_t input, compose_env_t *env)
{
int status;
mu_header_t header;
char *name = NULL;
char *value = NULL;
int state = STATE_INIT;
char *buf = NULL;
size_t size = 0, n;
int errcnt = 0, line = 0;
if ((status = mu_header_create (&header, NULL, 0)) != 0)
{
mu_error (_("Cannot create header: %s"), mu_strerror (status));
return 1;
}
mu_stream_seek (input, 0, MU_SEEK_SET, NULL);
while (state != STATE_BODY &&
errcnt == 0 &&
mu_stream_getline (input, &buf, &size, &n) == 0 && n > 0)
{
mu_rtrim_class (buf, MU_CTYPE_SPACE);
line++;
switch (state)
{
case STATE_INIT:
if (!buf[0] || mu_isspace (buf[0]))
continue;
else
state = STATE_READ;
/*FALLTHRU*/
case STATE_READ:
if (buf[0] == 0)
state = STATE_BODY;
else if (mu_isspace (buf[0]))
{
/* A continuation line */
if (name)
{
char *p = NULL;
mu_asprintf (&p, "%s\n%s", value, buf);
free (value);
value = p;
}
else
{
mu_error (_("%d: not a header line"), line);
errcnt++;
}
}
else
{
char *p;
if (name)
{
mu_header_set_value (header, name, value[0] ? value : NULL, 0);
free (name);
free (value);
name = value = NULL;
}
p = strchr (buf, ':');
if (p)
{
*p++ = 0;
while (*p && mu_isspace (*p))
p++;
value = mu_strdup (p);
name = mu_strdup (buf);
}
else
mu_stream_seek (input, 0, MU_SEEK_SET, NULL);
switch (parse_headers (input, env))
{
mu_error (_("%d: not a header line"), line);
errcnt++;
}
}
case parse_headers_ok:
return 0;
case parse_headers_fatal:
return -1;
case parse_headers_error:
break;
}
}
free (buf);
if (name)
{
mu_header_set_value (header, name, value, 0);
free (name);
free (value);
}
if (errcnt)
{
char *p;
mu_header_destroy (&header);
p = ml_readline (_("Edit again?"));
if (mu_true_answer_p (p) == 1)
return -1;
else
return 1;
}
mu_header_destroy (&env->header);
env->header = header;
return 0;
return mu_true_answer_p (p);
}
static void
......@@ -386,7 +293,7 @@ escape_run_editor (char *ed, int argc, char **argv, compose_env_t *env)
return rc;
}
}
while ((rc = parse_headers (tempstream, env)) < 0);
while (check_headers (tempstream, env));
}
else
{
......
......@@ -23,6 +23,7 @@
mu_mailbox_t mbox; /* Mailbox being operated upon */
size_t total; /* Total number of messages in the mailbox */
int interactive; /* Is the session interactive */
int read_recipients; /* Read recipients from the message (mail -t) */
static mu_list_t command_list;/* List of commands to be executed after parsing
command line */
......@@ -72,6 +73,8 @@ cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
break;
case 't':
read_recipients = 1;
util_cache_command (&command_list, "set editheaders");
util_cache_command (&command_list, "setq mode=send");
break;
......@@ -186,7 +189,7 @@ static struct mu_option mail_options[] = {
mu_c_string, NULL, cli_subject },
{ "to", 't', NULL, MU_OPTION_DEFAULT,
N_("precede message by a list of addresses"),
N_("read recipients from the message header"),
mu_c_string, NULL, cli_command_option },
{ "user", 'u', N_("USER"), MU_OPTION_DEFAULT,
......@@ -227,7 +230,7 @@ static struct mu_cli_setup cli = {
options,
NULL,
N_("GNU mail -- process mail messages.\n"
"If -f or --file is given, mail operates on the mailbox named "
"If -f or --file is given, mail operates on the mailbox named "
"by the first argument, or the user's mbox, if no argument given."),
N_("[address...]"),
alt_args,
......@@ -421,6 +424,12 @@ main (int argc, char **argv)
/* argument parsing */
mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv);
if (read_recipients)
{
argv += argc;
argc = 0;
}
if ((hint & (HINT_SEND_MODE|HINT_FILE_OPTION)) ==
(HINT_SEND_MODE|HINT_FILE_OPTION))
{
......
......@@ -288,6 +288,15 @@ extern int escape_attach (int argc, char **argv, compose_env_t *env);
extern int escape_remove_attachment (int argc, char **argv,
compose_env_t *env);
enum
{
parse_headers_ok,
parse_headers_error,
parse_headers_fatal
};
extern int parse_headers (mu_stream_t input, compose_env_t *env);
/* Cursor */
extern void set_cursor (unsigned value);
extern size_t get_cursor (void);
......
......@@ -488,8 +488,42 @@ mail_send (int argc, char **argv)
compose_init (&env);
if (argc < 2)
{
if (interactive)
compose_header_set (&env, MU_HEADER_TO, ml_readline_with_intr ("To: "),
COMPOSE_REPLACE);
else if (!mailvar_get (NULL, "editheaders", mailvar_type_boolean, 0))
{
if (parse_headers (mu_strin, &env) != parse_headers_ok)
{
mu_error ("%s", _("Errors parsing message"));
exit (EXIT_FAILURE);
}
if (add_header_list)
{
mu_iterator_t itr;
mu_list_get_iterator (add_header_list, &itr);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
struct add_header *hp;
int mode;
if (mu_iterator_current (itr, (void**) &hp))
break;
mode = hp->mode;
if (mu_header_sget_value (env.header, hp->name, NULL) == 0)
mode = COMPOSE_REPLACE;
compose_header_set (&env, hp->name, hp->value, hp->mode);
}
mu_iterator_destroy (&itr);
}
}
else
{
mu_error ("%s", _("No recipients specified"));
exit (EXIT_FAILURE);
}
}
else
{
while (--argc)
......@@ -511,27 +545,137 @@ mail_send (int argc, char **argv)
}
}
if (interactive)
{
if (mailvar_get (NULL, "mailx", mailvar_type_boolean, 0))
read_cc_bcc (&env);
if (mailvar_get (NULL, "asksub", mailvar_type_boolean, 0) == 0)
compose_header_set (&env, MU_HEADER_SUBJECT,
ml_readline_with_intr ("Subject: "), COMPOSE_REPLACE);
ml_readline_with_intr ("Subject: "),
COMPOSE_REPLACE);
}
status = mail_send0 (&env, save_to);
compose_destroy (&env);
return status;
}
int
parse_headers (mu_stream_t input, compose_env_t *env)
{
int status;
mu_header_t header;
char *name = NULL;
char *value = NULL;
enum { STATE_INIT, STATE_READ, STATE_BODY } state = STATE_INIT;
char *buf = NULL;
size_t size = 0, n;
int errcnt = 0, line = 0;
if ((status = mu_header_create (&header, NULL, 0)) != 0)
{
mu_error (_("Cannot create header: %s"), mu_strerror (status));
return parse_headers_fatal;
}
while (state != STATE_BODY &&
errcnt == 0 &&
mu_stream_getline (input, &buf, &size, &n) == 0 && n > 0)
{
mu_rtrim_class (buf, MU_CTYPE_SPACE);
line++;
switch (state)
{
case STATE_INIT:
if (!buf[0] || mu_isspace (buf[0]))
continue;
else
state = STATE_READ;
/*FALLTHRU*/
case STATE_READ:
if (buf[0] == 0)
state = STATE_BODY;
else if (mu_isspace (buf[0]))
{
/* A continuation line */
if (name)
{
char *p = NULL;
mu_asprintf (&p, "%s\n%s", value, buf);
free (value);
value = p;
}
else
{
mu_error (_("%d: not a header line"), line);
errcnt++;
}
}
else
{
char *p;
if (name)
{
mu_header_set_value (header, name, value[0] ? value : NULL, 0);
free (name);
free (value);
name = value = NULL;
}
p = strchr (buf, ':');
if (p)
{
*p++ = 0;
while (*p && mu_isspace (*p))
p++;
value = mu_strdup (p);
name = mu_strdup (buf);
}
else
{
mu_error (_("%d: not a header line"), line);
errcnt++;
}
}
break;
default:
abort ();
}
}
free (buf);
if (name)
{
mu_header_set_value (header, name, value, 0);
free (name);
free (value);
}
if (errcnt)
{
mu_header_destroy (&header);
return parse_headers_error;
}
mu_header_destroy (&env->header);
env->header = header;
return parse_headers_ok;
}
void
compose_init (compose_env_t * env)
compose_init (compose_env_t *env)
{
memset (env, 0, sizeof (*env));
mu_list_foreach (add_header_list, seed_headers, env);
}
int
compose_header_set (compose_env_t * env, const char *name,
compose_header_set (compose_env_t *env, const char *name,
const char *value, int mode)
{
int status;
......@@ -601,7 +745,7 @@ compose_header_set (compose_env_t * env, const char *name,
}
char *
compose_header_get (compose_env_t * env, char *name, char *defval)
compose_header_get (compose_env_t *env, char *name, char *defval)
{
char *p;
......
......@@ -226,8 +226,8 @@ static const struct mail_escape_entry mail_escape_table[] = {
{"!", "!", "![shell-command]", escape_shell },
{":", ":", ":[mail-command]", escape_command },
{"-", "-", "-[mail-command]", escape_command },
{"+", "+", "+[name [content-type [encoding]]]", escape_attach },
{"^", "^", "^[N]", escape_remove_attachment },
{"+", "+", "+name [content-type [encoding]]", escape_attach },
{"^", "^", "^N", escape_remove_attachment },
{"?", "?", "?", escape_help },
{"A", "A", "A", escape_sign },
{"a", "a", "a", escape_sign },
......
......@@ -194,51 +194,54 @@ set_mailbox_ownership_list (char const *str)
}
static int
set_onerror_action (char const *str)
set_onerror_action (void *item, void *data)
{
struct mu_wordsplit ws;
char *str = item;
if (strcmp (str, "abort") == 0)
onerror_flags = 0;
else
{
static struct mu_kwd onerror_kw[] = {
{ "skip", ONERROR_SKIP },
{ "delete", ONERROR_DELETE },
{ "count", ONERROR_COUNT },
{ NULL }
};
int i, flag;
int flag, clr = 0;
if (strcmp (str, "abort") == 0)
if (strncmp (str, "no", 2) == 0)
{
onerror_flags = 0;
return 0;
clr = 1;
str += 2;
}
ws.ws_delim = ",";
if (mu_wordsplit (str, &ws,
MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
MU_WRDSF_DELIM | MU_WRDSF_WS))
if (mu_kwd_xlat_name (onerror_kw, str, &flag))
{
mu_error (_("cannot split argument: %s"), mu_wordsplit_strerror (&ws));
mu_error (_("unknown keyword: %s"), str);
return 1;
}
for (i = 0; i < ws.ws_wordc; i++)
{
int clr = 0;
char *name = ws.ws_wordv[i];
if (strncmp (name, "no", 2) == 0)
{
clr = 1;
name += 2;
}
if (mu_kwd_xlat_name (onerror_kw, name, &flag))
mu_error (_("unknown keyword: %s"), ws.ws_wordv[i]);
if (clr)
onerror_flags &= ~flag;
else
onerror_flags |= flag;
}
mu_wordsplit_free (&ws);
return 0;
}
static int
set_onerror_actions (char const *str)
{
mu_list_t list;
int rc;
mu_list_create (&list);
mu_list_set_destroy_item (list, mu_list_free_item);
mu_string_split (str, ",", list);
rc = mu_list_foreach (list, set_onerror_action, NULL);
mu_list_destroy (&list);
return rc;
}
static void
cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
......@@ -250,7 +253,7 @@ cli_mailbox_ownership (struct mu_parseopt *po, struct mu_option *opt,
static void
cli_onerror (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
if (set_onerror_action (arg))
if (set_onerror_actions (arg))
exit (po->po_exit_error);
}
......@@ -327,9 +330,20 @@ cb_mailbox_ownership (void *data, mu_config_value_t *val)
static int
cb_onerror (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
return set_onerror_action (val->v.string);
switch (val->type)
{
case MU_CFG_LIST:
mu_list_foreach (val->v.list, set_onerror_action, NULL);
break;
case MU_CFG_STRING:
set_onerror_actions (val->v.string);
break;
default:
mu_error ("%s", _("too many arguments"));
}
return 0;
}
struct mu_cfg_param movemail_cfg_param[] = {
......@@ -360,12 +374,12 @@ struct mu_cfg_param movemail_cfg_param[] = {
{ "ignore-errors", mu_c_bool, &ignore_errors, 0, NULL,
N_("Continue after an error.") },
{ "onerror", mu_cfg_callback, NULL, 0, cb_onerror,
N_("What to do after an error. Argument is a comma-separated list of:\n"
N_("What to do after an error. Argument is a list of:\n"
" abort - terminate the program (the default)\n"
" skip - skip to the next message\n"
" delete - delete this one and to the next message\n"
" count - count this message as processed\n"
"Each keyword can be prefixed with \"no\" to reverse its meaning\n"
"Setting onerror=abort reverts to the default behavior."),
"Each keyword can be prefixed with \"no\" to reverse its meaning."),
N_("arg: list") },
{ NULL }
};
......