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
1 GNU mailutils NEWS -- history of user-visible changes. 2012-07-18 1 GNU mailutils NEWS -- history of user-visible changes. 2012-07-19
2 Copyright (C) 2002-2012 Free Software Foundation, Inc. 2 Copyright (C) 2002-2012 Free Software Foundation, Inc.
3 See the end of file for copying conditions. 3 See the end of file for copying conditions.
4 4
...@@ -135,6 +135,20 @@ Here, the file "prog" will be attached witg the content type ...@@ -135,6 +135,20 @@ Here, the file "prog" will be attached witg the content type
135 "main.c" and "ext.h" will be marked with content type "text/c" and 135 "main.c" and "ext.h" will be marked with content type "text/c" and
136 encoded using "quoted-printable" algorithm. 136 encoded using "quoted-printable" algorithm.
137 137
138 The mail shell provides the following new escapes to handle attachments:
139
140 ~l
141 Lists all attachments. The output is a numbered list of
142 attachments with their corresponding content types and
143 encodings.
144
145 ~+ filename [content-type [encoding]]
146 Attach the file `filename'. Optional arguments supply the content
147 type and encoding to use instead of the defaults.
148
149 ~^ N
150 Delete Nth attachment.
151
138 ** MH: improved compatibility with other implementations 152 ** MH: improved compatibility with other implementations
139 153
140 ** MH inc: new option --moveto 154 ** MH inc: new option --moveto
......
imprimatur @ 04255b6d
1 Subproject commit f32ef1983968e755cd580b06e369476d7e7f88b6 1 Subproject commit 04255b6d5551952b4e0c94da15988f573e3e9fc4
......
...@@ -118,6 +118,10 @@ int mu_list_remove (mu_list_t _list, void *_item); ...@@ -118,6 +118,10 @@ int mu_list_remove (mu_list_t _list, void *_item);
118 /* A non-destructive version of mu_list_remove: removes the _item but does 118 /* A non-destructive version of mu_list_remove: removes the _item but does
119 not deallocate it. */ 119 not deallocate it. */
120 int mu_list_remove_nd (mu_list_t _list, void *_item); 120 int mu_list_remove_nd (mu_list_t _list, void *_item);
121 /* Remove Nth element from the list. */
122 int mu_list_remove_nth (mu_list_t list, size_t n);
123 /* Remove Nth element from the list, non-destructive. */
124 int mu_list_remove_nth_nd (mu_list_t list, size_t n);
121 /* Find _old_item in _list and if found, replace it with _new_item, 125 /* Find _old_item in _list and if found, replace it with _new_item,
122 deallocating the removed item via the `destroy_item' method. */ 126 deallocating the removed item via the `destroy_item' method. */
123 int mu_list_replace (mu_list_t _list, void *_old_item, void *_new_item); 127 int mu_list_replace (mu_list_t _list, void *_old_item, void *_new_item);
......
...@@ -42,6 +42,7 @@ liblist_la_SOURCES = \ ...@@ -42,6 +42,7 @@ liblist_la_SOURCES = \
42 prepend.c\ 42 prepend.c\
43 push.c\ 43 push.c\
44 remove.c\ 44 remove.c\
45 removenth.c\
45 replace.c\ 46 replace.c\
46 rfold.c\ 47 rfold.c\
47 setcomp.c\ 48 setcomp.c\
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2012 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library 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 GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <mailutils/sys/list.h>
23 #include <mailutils/errno.h>
24
25 int
26 mu_list_remove_nth (mu_list_t list, size_t n)
27 {
28 struct list_data *current;
29 mu_list_comparator_t comp;
30 int status = MU_ERR_NOENT;
31 size_t i;
32
33 if (list == NULL)
34 return EINVAL;
35 if (n >= list->count)
36 return ERANGE;
37 mu_monitor_wrlock (list->monitor);
38 for (current = list->head.next, i = 0; current != &list->head;
39 current = current->next, i++)
40 {
41 if (i == n)
42 {
43 struct list_data *previous = current->prev;
44
45 mu_iterator_advance (list->itr, current);
46 previous->next = current->next;
47 current->next->prev = previous;
48 DESTROY_ITEM (list, current);
49 free (current);
50 list->count--;
51 status = 0;
52 break;
53 }
54 }
55 mu_monitor_unlock (list->monitor);
56 return status;
57 }
58
59 int
60 mu_list_remove_nth_nd (mu_list_t list, size_t n)
61 {
62 mu_list_destroy_item_t dptr = mu_list_set_destroy_item (list, NULL);
63 int rc = mu_list_remove_nth (list, n);
64 mu_list_set_destroy_item (list, dptr);
65 return rc;
66 }
67
...@@ -156,16 +156,29 @@ escape_continue (void) ...@@ -156,16 +156,29 @@ escape_continue (void)
156 mu_printf (_("(continue)\n")); 156 mu_printf (_("(continue)\n"));
157 } 157 }
158 158
159 static int 159 int
160 escape_check_args (int argc, char **argv) 160 escape_check_args (int argc, char **argv, int minargs, int maxargs)
161 { 161 {
162 if (argc == 1) 162 char *escape = "~";
163 if (argc < minargs)
163 { 164 {
164 char *escape = "~"; 165 minargs--;
165 mailvar_get (&escape, "escape", mailvar_type_string, 0); 166 mailvar_get (&escape, "escape", mailvar_type_string, 0);
166 mu_error (_("%c%s requires an argument"), escape[0], argv[0]); 167 mu_error (ngettext ("%c%s requires at least %d argument",
168 "%c%s requires at least %d arguments",
169 minargs), escape[0], argv[0], minargs);
167 return 1; 170 return 1;
168 } 171 }
172 if (maxargs > 1 && argc > maxargs)
173 {
174 maxargs--;
175 mailvar_get (&escape, "escape", mailvar_type_string, 0);
176 mu_error (ngettext ("%c%s accepts at most %d argument",
177 "%c%s accepts at most %d arguments",
178 maxargs), escape[0], argv[0], maxargs);
179 return 1;
180 }
181
169 return 0; 182 return 0;
170 } 183 }
171 184
...@@ -184,7 +197,7 @@ escape_command (int argc, char **argv, compose_env_t *env) ...@@ -184,7 +197,7 @@ escape_command (int argc, char **argv, compose_env_t *env)
184 const struct mail_command_entry *entry; 197 const struct mail_command_entry *entry;
185 int status; 198 int status;
186 199
187 if (escape_check_args (argc, argv)) 200 if (escape_check_args (argc, argv, 2, 2))
188 return 1; 201 return 1;
189 if (argv[1][0] == '#') 202 if (argv[1][0] == '#')
190 return 0; 203 return 0;
...@@ -416,6 +429,8 @@ escape_editor (int argc, char **argv, compose_env_t *env) ...@@ -416,6 +429,8 @@ escape_editor (int argc, char **argv, compose_env_t *env)
416 return escape_run_editor (getenv ("EDITOR"), argc, argv, env); 429 return escape_run_editor (getenv ("EDITOR"), argc, argv, env);
417 } 430 }
418 431
432 /* ~l -- escape_list_attachments (send.c) */
433
419 /* ~v */ 434 /* ~v */
420 int 435 int
421 escape_visual (int argc, char **argv, compose_env_t *env) 436 escape_visual (int argc, char **argv, compose_env_t *env)
...@@ -457,7 +472,7 @@ escape_headers (int argc, char **argv, compose_env_t *env) ...@@ -457,7 +472,7 @@ escape_headers (int argc, char **argv, compose_env_t *env)
457 int 472 int
458 escape_insert (int argc, char **argv, compose_env_t *env) 473 escape_insert (int argc, char **argv, compose_env_t *env)
459 { 474 {
460 if (escape_check_args (argc, argv)) 475 if (escape_check_args (argc, argv, 2, 2))
461 return 1; 476 return 1;
462 mailvar_variable_format (env->compstr, mailvar_find_variable (argv[1], 0), 477 mailvar_variable_format (env->compstr, mailvar_find_variable (argv[1], 0),
463 NULL); 478 NULL);
...@@ -574,7 +589,7 @@ escape_read (int argc, char **argv, compose_env_t *env MU_ARG_UNUSED) ...@@ -574,7 +589,7 @@ escape_read (int argc, char **argv, compose_env_t *env MU_ARG_UNUSED)
574 mu_stream_t instr; 589 mu_stream_t instr;
575 int rc; 590 int rc;
576 591
577 if (escape_check_args (argc, argv)) 592 if (escape_check_args (argc, argv, 2, 2))
578 return 1; 593 return 1;
579 filename = util_fullpath (argv[1]); 594 filename = util_fullpath (argv[1]);
580 595
...@@ -597,7 +612,7 @@ int ...@@ -597,7 +612,7 @@ int
597 escape_subj (int argc, char **argv, compose_env_t *env) 612 escape_subj (int argc, char **argv, compose_env_t *env)
598 { 613 {
599 char *buf; 614 char *buf;
600 if (escape_check_args (argc, argv)) 615 if (escape_check_args (argc, argv, 2, 2))
601 return 1; 616 return 1;
602 mu_argcv_string (argc - 1, argv + 1, &buf); 617 mu_argcv_string (argc - 1, argv + 1, &buf);
603 compose_header_set (env, MU_HEADER_SUBJECT, buf, COMPOSE_REPLACE); 618 compose_header_set (env, MU_HEADER_SUBJECT, buf, COMPOSE_REPLACE);
...@@ -623,7 +638,7 @@ escape_write (int argc, char **argv, compose_env_t *env) ...@@ -623,7 +638,7 @@ escape_write (int argc, char **argv, compose_env_t *env)
623 int rc; 638 int rc;
624 mu_off_t size; 639 mu_off_t size;
625 640
626 if (escape_check_args (argc, argv)) 641 if (escape_check_args (argc, argv, 2, 2))
627 return 1; 642 return 1;
628 filename = util_fullpath (argv[1]); 643 filename = util_fullpath (argv[1]);
629 /* FIXME: check for existence first */ 644 /* FIXME: check for existence first */
......
...@@ -95,7 +95,7 @@ parse_opt (int key, char *arg, struct argp_state *state) ...@@ -95,7 +95,7 @@ parse_opt (int key, char *arg, struct argp_state *state)
95 95
96 case 'A': 96 case 'A':
97 args->hint |= HINT_SEND_MODE; 97 args->hint |= HINT_SEND_MODE;
98 if (send_attach_file (arg)) 98 if (send_attach_file_default (arg))
99 exit (1); 99 exit (1);
100 break; 100 break;
101 101
......
...@@ -258,7 +258,9 @@ extern char *mail_expand_name (const char *name); ...@@ -258,7 +258,9 @@ extern char *mail_expand_name (const char *name);
258 258
259 extern void send_append_header (char *text); 259 extern void send_append_header (char *text);
260 extern void send_append_header2 (char *name, char *value, int mode); 260 extern void send_append_header2 (char *name, char *value, int mode);
261 extern int send_attach_file (const char *name); 261 extern int send_attach_file_default (const char *name);
262
263 extern int escape_check_args (int argc, char **argv, int minargs, int maxargs);
262 264
263 extern int escape_shell (int argc, char **argv, compose_env_t *env); 265 extern int escape_shell (int argc, char **argv, compose_env_t *env);
264 extern int escape_command (int argc, char **argv, compose_env_t *env); 266 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); ...@@ -280,6 +282,11 @@ extern int escape_visual (int argc, char **argv, compose_env_t *env);
280 extern int escape_write (int argc, char **argv, compose_env_t *env); 282 extern int escape_write (int argc, char **argv, compose_env_t *env);
281 extern int escape_exit (int argc, char **argv, compose_env_t *env); 283 extern int escape_exit (int argc, char **argv, compose_env_t *env);
282 extern int escape_pipe (int argc, char **argv, compose_env_t *env); 284 extern int escape_pipe (int argc, char **argv, compose_env_t *env);
285 extern int escape_list_attachments (int argc, char **argv,
286 compose_env_t *env);
287 extern int escape_attach (int argc, char **argv, compose_env_t *env);
288 extern int escape_remove_attachment (int argc, char **argv,
289 compose_env_t *env);
283 290
284 /* Cursor */ 291 /* Cursor */
285 extern void set_cursor (unsigned value); 292 extern void set_cursor (unsigned value);
......
...@@ -154,11 +154,13 @@ atchinfo_free (void *p) ...@@ -154,11 +154,13 @@ atchinfo_free (void *p)
154 } 154 }
155 155
156 int 156 int
157 send_attach_file (const char *name) 157 send_attach_file (const char *name,
158 const char *content_type, const char *encoding)
158 { 159 {
159 int rc; 160 int rc;
160 struct stat st; 161 struct stat st;
161 struct atchinfo *aptr; 162 struct atchinfo *aptr;
163 mu_list_t list;
162 164
163 if (stat (name, &st)) 165 if (stat (name, &st))
164 { 166 {
...@@ -180,6 +182,16 @@ send_attach_file (const char *name) ...@@ -180,6 +182,16 @@ send_attach_file (const char *name)
180 return 1; 182 return 1;
181 } 183 }
182 184
185 if (!encoding)
186 encoding = "base64";
187 mu_filter_get_list (&list);
188 rc = mu_list_locate (list, encoding, NULL);
189 if (rc)
190 {
191 mu_error (_("unsupported encoding: %s"), encoding);
192 return 1;
193 }
194
183 if (!attlist) 195 if (!attlist)
184 { 196 {
185 rc = mu_list_create (&attlist); 197 rc = mu_list_create (&attlist);
...@@ -191,10 +203,10 @@ send_attach_file (const char *name) ...@@ -191,10 +203,10 @@ send_attach_file (const char *name)
191 mu_list_set_destroy_item (attlist, atchinfo_free); 203 mu_list_set_destroy_item (attlist, atchinfo_free);
192 } 204 }
193 aptr = mu_alloc (sizeof (*aptr)); 205 aptr = mu_alloc (sizeof (*aptr));
194 aptr->encoding = mu_strdup (default_encoding ? 206
195 default_encoding : "base64"); 207 aptr->encoding = mu_strdup (encoding);
196 aptr->content_type = mu_strdup (default_content_type ? 208 aptr->content_type = mu_strdup (content_type ?
197 default_content_type : 209 content_type :
198 "application/octet-stream"); 210 "application/octet-stream");
199 aptr->filename = mu_strdup (name); 211 aptr->filename = mu_strdup (name);
200 rc = mu_list_append (attlist, aptr); 212 rc = mu_list_append (attlist, aptr);
...@@ -206,6 +218,86 @@ send_attach_file (const char *name) ...@@ -206,6 +218,86 @@ send_attach_file (const char *name)
206 return 0; 218 return 0;
207 } 219 }
208 220
221 int
222 send_attach_file_default (const char *name)
223 {
224 return send_attach_file (name, default_content_type, default_encoding);
225 }
226
227 int
228 escape_list_attachments (int argc, char **argv, compose_env_t *env)
229 {
230 mu_iterator_t itr;
231 int i;
232
233 if (mu_list_is_empty (attlist) ||
234 mu_list_get_iterator (attlist, &itr))
235 {
236 mu_printf ("%s\n", _("No attachments"));
237 return 0;
238 }
239
240 for (mu_iterator_first (itr), i = 1; !mu_iterator_is_done (itr);
241 mu_iterator_next (itr), i++)
242 {
243 struct atchinfo *aptr;
244 if (mu_iterator_current (itr, (void**)&aptr))
245 continue;
246
247 mu_printf ("%3d %-12s %-30s %-s\n",
248 i, aptr->filename, aptr->content_type, aptr->encoding);
249 }
250 mu_iterator_destroy (&itr);
251
252 return 0;
253 }
254
255 int
256 escape_attach (int argc, char **argv, compose_env_t *env)
257 {
258 const char *encoding = default_encoding;
259 const char *content_type = default_content_type;
260
261 switch (argc)
262 {
263 case 4:
264 encoding = argv[3];
265 case 3:
266 content_type = argv[2];
267 case 2:
268 return send_attach_file (argv[1], content_type, encoding);
269 default:
270 return escape_check_args (argc, argv, 2, 4);
271 }
272 return 1;
273 }
274
275 int
276 escape_remove_attachment (int argc, char **argv, compose_env_t *env)
277 {
278 size_t count;
279 unsigned long n;
280 char *p;
281
282 if (escape_check_args (argc, argv, 2, 2))
283 return 1;
284 n = strtoul (argv[1], &p, 10);
285 if (*p)
286 {
287 mu_error (_("not a valid number: %s"), argv[1]);
288 return 1;
289 }
290
291 mu_list_count (attlist, &count);
292 if (n == 0 || n > count)
293 {
294 mu_error (_("index out of range"));
295 return 1;
296 }
297
298 return mu_list_remove_nth (attlist, n - 1);
299 }
300
209 static int 301 static int
210 saveatt (void *item, void *data) 302 saveatt (void *item, void *data)
211 { 303 {
......
...@@ -226,6 +226,8 @@ static const struct mail_escape_entry mail_escape_table[] = { ...@@ -226,6 +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 },
230 {"^", "^", "^[N]", escape_remove_attachment },
229 {"?", "?", "?", escape_help }, 231 {"?", "?", "?", escape_help },
230 {"A", "A", "A", escape_sign }, 232 {"A", "A", "A", escape_sign },
231 {"a", "a", "a", escape_sign }, 233 {"a", "a", "a", escape_sign },
...@@ -237,6 +239,7 @@ static const struct mail_escape_entry mail_escape_table[] = { ...@@ -237,6 +239,7 @@ static const struct mail_escape_entry mail_escape_table[] = {
237 {"F", "F", "F[mesg-list]", escape_print }, 239 {"F", "F", "F[mesg-list]", escape_print },
238 {"h", "h", "h", escape_headers }, 240 {"h", "h", "h", escape_headers },
239 {"i", "i", "i[var-name]", escape_insert }, 241 {"i", "i", "i[var-name]", escape_insert },
242 {"l", "l", "l", escape_list_attachments },
240 {"m", "m", "m[mesg-list]", escape_quote }, 243 {"m", "m", "m[mesg-list]", escape_quote },
241 {"M", "M", "M[mesg-list]", escape_quote }, 244 {"M", "M", "M[mesg-list]", escape_quote },
242 {"p", "p", "p", escape_type_input }, 245 {"p", "p", "p", escape_type_input },
......