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.
Showing
10 changed files
with
222 additions
and
19 deletions
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
... | @@ -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); | ... | ... |
libmailutils/list/removenth.c
0 → 100644
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 }, | ... | ... |
-
Please register or sign in to post a comment