Commit 62e22dcc 62e22dcc8419437e03225ae55223aaf97227b1e1 by Sergey Poznyakoff

mail: provide mechanism for listing and editing attachments from the shell

* NEWS: Update.
* mail/escape.c (escape_check_args): Take two additional arguments
specifying minimum and maximum number of parameters to expect.
All uses updated. Remove static qualifier.
* mail/mail.h (send_attach_file): Rename to send_attach_file_default
(escape_check_args,escape_list_attachments)
(escape_attach,escape_remove_attachment): New protos.
* mail/send.c (send_attach_file): Take encoding and content type
as arguments.
(send_attach_file_default): New function.
(escape_list_attachments)
(escape_attach,escape_remove_attachment): New functions.
* mail/table.c: New escapes: l, +, ^

* include/mailutils/list.h (mu_list_remove_nth)
(mu_list_remove_nth_nd): New protos.
* libmailutils/list/Makefile.am (liblist_la_SOURCES): Add removenth.c
* libmailutils/list/removenth.c: New file.
1 parent 8ad47471
GNU mailutils NEWS -- history of user-visible changes. 2012-07-18
GNU mailutils NEWS -- history of user-visible changes. 2012-07-19
Copyright (C) 2002-2012 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -135,6 +135,20 @@ Here, the file "prog" will be attached witg the content type
"main.c" and "ext.h" will be marked with content type "text/c" and
encoded using "quoted-printable" algorithm.
The mail shell provides the following new escapes to handle attachments:
~l
Lists all attachments. The output is a numbered list of
attachments with their corresponding content types and
encodings.
~+ filename [content-type [encoding]]
Attach the file `filename'. Optional arguments supply the content
type and encoding to use instead of the defaults.
~^ N
Delete Nth attachment.
** MH: improved compatibility with other implementations
** MH inc: new option --moveto
......
imprimatur @ 04255b6d
Subproject commit f32ef1983968e755cd580b06e369476d7e7f88b6
Subproject commit 04255b6d5551952b4e0c94da15988f573e3e9fc4
......
......@@ -118,6 +118,10 @@ int mu_list_remove (mu_list_t _list, void *_item);
/* A non-destructive version of mu_list_remove: removes the _item but does
not deallocate it. */
int mu_list_remove_nd (mu_list_t _list, void *_item);
/* Remove Nth element from the list. */
int mu_list_remove_nth (mu_list_t list, size_t n);
/* Remove Nth element from the list, non-destructive. */
int mu_list_remove_nth_nd (mu_list_t list, size_t n);
/* Find _old_item in _list and if found, replace it with _new_item,
deallocating the removed item via the `destroy_item' method. */
int mu_list_replace (mu_list_t _list, void *_old_item, void *_new_item);
......
......@@ -42,6 +42,7 @@ liblist_la_SOURCES = \
prepend.c\
push.c\
remove.c\
removenth.c\
replace.c\
rfold.c\
setcomp.c\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2012 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <mailutils/sys/list.h>
#include <mailutils/errno.h>
int
mu_list_remove_nth (mu_list_t list, size_t n)
{
struct list_data *current;
mu_list_comparator_t comp;
int status = MU_ERR_NOENT;
size_t i;
if (list == NULL)
return EINVAL;
if (n >= list->count)
return ERANGE;
mu_monitor_wrlock (list->monitor);
for (current = list->head.next, i = 0; current != &list->head;
current = current->next, i++)
{
if (i == n)
{
struct list_data *previous = current->prev;
mu_iterator_advance (list->itr, current);
previous->next = current->next;
current->next->prev = previous;
DESTROY_ITEM (list, current);
free (current);
list->count--;
status = 0;
break;
}
}
mu_monitor_unlock (list->monitor);
return status;
}
int
mu_list_remove_nth_nd (mu_list_t list, size_t n)
{
mu_list_destroy_item_t dptr = mu_list_set_destroy_item (list, NULL);
int rc = mu_list_remove_nth (list, n);
mu_list_set_destroy_item (list, dptr);
return rc;
}
......@@ -156,16 +156,29 @@ escape_continue (void)
mu_printf (_("(continue)\n"));
}
static int
escape_check_args (int argc, char **argv)
int
escape_check_args (int argc, char **argv, int minargs, int maxargs)
{
if (argc == 1)
char *escape = "~";
if (argc < minargs)
{
char *escape = "~";
minargs--;
mailvar_get (&escape, "escape", mailvar_type_string, 0);
mu_error (_("%c%s requires an argument"), escape[0], argv[0]);
mu_error (ngettext ("%c%s requires at least %d argument",
"%c%s requires at least %d arguments",
minargs), escape[0], argv[0], minargs);
return 1;
}
if (maxargs > 1 && argc > maxargs)
{
maxargs--;
mailvar_get (&escape, "escape", mailvar_type_string, 0);
mu_error (ngettext ("%c%s accepts at most %d argument",
"%c%s accepts at most %d arguments",
maxargs), escape[0], argv[0], maxargs);
return 1;
}
return 0;
}
......@@ -184,7 +197,7 @@ escape_command (int argc, char **argv, compose_env_t *env)
const struct mail_command_entry *entry;
int status;
if (escape_check_args (argc, argv))
if (escape_check_args (argc, argv, 2, 2))
return 1;
if (argv[1][0] == '#')
return 0;
......@@ -416,6 +429,8 @@ escape_editor (int argc, char **argv, compose_env_t *env)
return escape_run_editor (getenv ("EDITOR"), argc, argv, env);
}
/* ~l -- escape_list_attachments (send.c) */
/* ~v */
int
escape_visual (int argc, char **argv, compose_env_t *env)
......@@ -457,7 +472,7 @@ escape_headers (int argc, char **argv, compose_env_t *env)
int
escape_insert (int argc, char **argv, compose_env_t *env)
{
if (escape_check_args (argc, argv))
if (escape_check_args (argc, argv, 2, 2))
return 1;
mailvar_variable_format (env->compstr, mailvar_find_variable (argv[1], 0),
NULL);
......@@ -574,7 +589,7 @@ escape_read (int argc, char **argv, compose_env_t *env MU_ARG_UNUSED)
mu_stream_t instr;
int rc;
if (escape_check_args (argc, argv))
if (escape_check_args (argc, argv, 2, 2))
return 1;
filename = util_fullpath (argv[1]);
......@@ -597,7 +612,7 @@ int
escape_subj (int argc, char **argv, compose_env_t *env)
{
char *buf;
if (escape_check_args (argc, argv))
if (escape_check_args (argc, argv, 2, 2))
return 1;
mu_argcv_string (argc - 1, argv + 1, &buf);
compose_header_set (env, MU_HEADER_SUBJECT, buf, COMPOSE_REPLACE);
......@@ -623,7 +638,7 @@ escape_write (int argc, char **argv, compose_env_t *env)
int rc;
mu_off_t size;
if (escape_check_args (argc, argv))
if (escape_check_args (argc, argv, 2, 2))
return 1;
filename = util_fullpath (argv[1]);
/* FIXME: check for existence first */
......
......@@ -95,7 +95,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 'A':
args->hint |= HINT_SEND_MODE;
if (send_attach_file (arg))
if (send_attach_file_default (arg))
exit (1);
break;
......
......@@ -258,7 +258,9 @@ extern char *mail_expand_name (const char *name);
extern void send_append_header (char *text);
extern void send_append_header2 (char *name, char *value, int mode);
extern int send_attach_file (const char *name);
extern int send_attach_file_default (const char *name);
extern int escape_check_args (int argc, char **argv, int minargs, int maxargs);
extern int escape_shell (int argc, char **argv, compose_env_t *env);
extern int escape_command (int argc, char **argv, compose_env_t *env);
......@@ -280,6 +282,11 @@ extern int escape_visual (int argc, char **argv, compose_env_t *env);
extern int escape_write (int argc, char **argv, compose_env_t *env);
extern int escape_exit (int argc, char **argv, compose_env_t *env);
extern int escape_pipe (int argc, char **argv, compose_env_t *env);
extern int escape_list_attachments (int argc, char **argv,
compose_env_t *env);
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);
/* Cursor */
extern void set_cursor (unsigned value);
......
......@@ -154,11 +154,13 @@ atchinfo_free (void *p)
}
int
send_attach_file (const char *name)
send_attach_file (const char *name,
const char *content_type, const char *encoding)
{
int rc;
struct stat st;
struct atchinfo *aptr;
mu_list_t list;
if (stat (name, &st))
{
......@@ -180,6 +182,16 @@ send_attach_file (const char *name)
return 1;
}
if (!encoding)
encoding = "base64";
mu_filter_get_list (&list);
rc = mu_list_locate (list, encoding, NULL);
if (rc)
{
mu_error (_("unsupported encoding: %s"), encoding);
return 1;
}
if (!attlist)
{
rc = mu_list_create (&attlist);
......@@ -191,10 +203,10 @@ send_attach_file (const char *name)
mu_list_set_destroy_item (attlist, atchinfo_free);
}
aptr = mu_alloc (sizeof (*aptr));
aptr->encoding = mu_strdup (default_encoding ?
default_encoding : "base64");
aptr->content_type = mu_strdup (default_content_type ?
default_content_type :
aptr->encoding = mu_strdup (encoding);
aptr->content_type = mu_strdup (content_type ?
content_type :
"application/octet-stream");
aptr->filename = mu_strdup (name);
rc = mu_list_append (attlist, aptr);
......@@ -206,6 +218,86 @@ send_attach_file (const char *name)
return 0;
}
int
send_attach_file_default (const char *name)
{
return send_attach_file (name, default_content_type, default_encoding);
}
int
escape_list_attachments (int argc, char **argv, compose_env_t *env)
{
mu_iterator_t itr;
int i;
if (mu_list_is_empty (attlist) ||
mu_list_get_iterator (attlist, &itr))
{
mu_printf ("%s\n", _("No attachments"));
return 0;
}
for (mu_iterator_first (itr), i = 1; !mu_iterator_is_done (itr);
mu_iterator_next (itr), i++)
{
struct atchinfo *aptr;
if (mu_iterator_current (itr, (void**)&aptr))
continue;
mu_printf ("%3d %-12s %-30s %-s\n",
i, aptr->filename, aptr->content_type, aptr->encoding);
}
mu_iterator_destroy (&itr);
return 0;
}
int
escape_attach (int argc, char **argv, compose_env_t *env)
{
const char *encoding = default_encoding;
const char *content_type = default_content_type;
switch (argc)
{
case 4:
encoding = argv[3];
case 3:
content_type = argv[2];
case 2:
return send_attach_file (argv[1], content_type, encoding);
default:
return escape_check_args (argc, argv, 2, 4);
}
return 1;
}
int
escape_remove_attachment (int argc, char **argv, compose_env_t *env)
{
size_t count;
unsigned long n;
char *p;
if (escape_check_args (argc, argv, 2, 2))
return 1;
n = strtoul (argv[1], &p, 10);
if (*p)
{
mu_error (_("not a valid number: %s"), argv[1]);
return 1;
}
mu_list_count (attlist, &count);
if (n == 0 || n > count)
{
mu_error (_("index out of range"));
return 1;
}
return mu_list_remove_nth (attlist, n - 1);
}
static int
saveatt (void *item, void *data)
{
......
......@@ -226,6 +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 },
{"?", "?", "?", escape_help },
{"A", "A", "A", escape_sign },
{"a", "a", "a", escape_sign },
......@@ -237,6 +239,7 @@ static const struct mail_escape_entry mail_escape_table[] = {
{"F", "F", "F[mesg-list]", escape_print },
{"h", "h", "h", escape_headers },
{"i", "i", "i[var-name]", escape_insert },
{"l", "l", "l", escape_list_attachments },
{"m", "m", "m[mesg-list]", escape_quote },
{"M", "M", "M[mesg-list]", escape_quote },
{"p", "p", "p", escape_type_input },
......