2 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program 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
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 /* Close approximation of the mailx(1) builtin editor for sending mail. */
26 #include "mutt_curses.h"
27 #include "mutt_idna.h"
38 * SLcurses_waddnstr() can't take a "const char *", so this is only
39 * declared "static" (sigh)
41 static char* EditorHelp1 = N_("\
42 ~~ insert a line begining with a single ~\n\
43 ~b users add users to the Bcc: field\n\
44 ~c users add users to the Cc: field\n\
45 ~f messages include messages\n\
46 ~F messages same as ~f, except also include headers\n\
47 ~h edit the message header\n\
48 ~m messages include and quote messages\n\
49 ~M messages same as ~m, except include headers\n\
50 ~p print the message\n");
52 static char* EditorHelp2 = N_("\
53 ~q write file and quit editor\n\
54 ~r file read a file into the editor\n\
55 ~t users add users to the To: field\n\
56 ~u recall the previous line\n\
57 ~v edit message with the $visual editor\n\
58 ~w file write message to file\n\
59 ~x abort changes and quit editor\n\
61 . on a line by itself ends input\n");
64 be_snarf_data (FILE *f, char **buf, int *bufmax, int *buflen, LOFF_T offset,
65 int bytes, int prefix)
67 char tmp[HUGE_STRING];
69 int tmplen = sizeof (tmp);
71 tmp[sizeof (tmp) - 1] = 0;
74 strfcpy (tmp, NONULL(Prefix), sizeof (tmp));
75 tmplen = mutt_strlen (tmp);
77 tmplen = sizeof (tmp) - tmplen;
80 fseeko (f, offset, 0);
83 if (fgets (p, tmplen - 1, f) == NULL) break;
84 bytes -= mutt_strlen (p);
85 if (*bufmax == *buflen)
86 safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
87 buf[(*buflen)++] = safe_strdup (tmp);
89 if (buf && *bufmax == *buflen) { /* Do not smash memory past buf */
90 safe_realloc (&buf, sizeof (char *) * (++*bufmax));
92 if (buf) buf[*buflen] = NULL;
97 be_snarf_file (const char *path, char **buf, int *max, int *len, int verbose)
100 char tmp[LONG_STRING];
103 if ((f = fopen (path, "r")))
105 fstat (fileno (f), &sb);
106 buf = be_snarf_data (f, buf, max, len, 0, sb.st_size, 0);
109 snprintf(tmp, sizeof(tmp), "\"%s\" %lu bytes\n", path, (unsigned long) sb.st_size);
116 snprintf(tmp, sizeof(tmp), "%s: %s\n", path, strerror(errno));
122 static int be_barf_file (const char *path, char **buf, int buflen)
127 if ((f = fopen (path, "w")) == NULL) /* __FOPEN_CHECKED__ */
129 addstr (strerror (errno));
133 for (i = 0; i < buflen; i++) fputs (buf[i], f);
134 if (fclose (f) == 0) return 0;
135 printw ("fclose: %s\n", strerror (errno));
139 static void be_free_memory (char **buf, int buflen)
148 be_include_messages (char *msg, char **buf, int *bufmax, int *buflen,
149 int pfx, int inc_hdrs)
151 int offset, bytes, n;
152 char tmp[LONG_STRING];
154 while ((msg = strtok (msg, " ,")) != NULL)
156 if (mutt_atoi (msg, &n) == 0 && n > 0 && n <= Context->msgcount)
160 /* add the attribution */
163 mutt_make_string (tmp, sizeof (tmp) - 1, Attribution, Context, Context->hdrs[n]);
164 strcat (tmp, "\n"); /* __STRCAT_CHECKED__ */
167 if (*bufmax == *buflen)
168 safe_realloc ( &buf, sizeof (char *) * (*bufmax += 25));
169 buf[(*buflen)++] = safe_strdup (tmp);
171 bytes = Context->hdrs[n]->content->length;
174 offset = Context->hdrs[n]->offset;
175 bytes += Context->hdrs[n]->content->offset - offset;
178 offset = Context->hdrs[n]->content->offset;
179 buf = be_snarf_data (Context->fp, buf, bufmax, buflen, offset, bytes,
182 if (*bufmax == *buflen)
183 safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
184 buf[(*buflen)++] = safe_strdup ("\n");
187 printw (_("%d: invalid message number.\n"), n);
193 static void be_print_header (ENVELOPE *env)
195 char tmp[HUGE_STRING];
201 rfc822_write_address (tmp, sizeof (tmp), env->to, 1);
209 rfc822_write_address (tmp, sizeof (tmp), env->cc, 1);
217 rfc822_write_address (tmp, sizeof (tmp), env->bcc, 1);
223 addstr ("Subject: ");
224 addstr (env->subject);
231 * force override the $ask* vars (used for the ~h command)
233 static void be_edit_header (ENVELOPE *e, int force)
235 char tmp[HUGE_STRING];
241 mutt_addrlist_to_local (e->to);
242 rfc822_write_address (tmp, sizeof (tmp), e->to, 0);
245 if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
247 rfc822_free_address (&e->to);
248 e->to = mutt_parse_adrlist (e->to, tmp);
249 e->to = mutt_expand_aliases (e->to);
250 mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
252 rfc822_write_address (tmp, sizeof (tmp), e->to, 1);
253 mvaddstr (LINES - 1, 4, tmp);
258 mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
263 if (!e->subject || force)
265 addstr ("Subject: ");
266 strfcpy (tmp, e->subject ? e->subject: "", sizeof (tmp));
267 if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 9, 0) == 0)
268 mutt_str_replace (&e->subject, tmp);
272 if ((!e->cc && option (OPTASKCC)) || force)
276 mutt_addrlist_to_local (e->cc);
277 rfc822_write_address (tmp, sizeof (tmp), e->cc, 0);
278 if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 4, 0) == 0)
280 rfc822_free_address (&e->cc);
281 e->cc = mutt_parse_adrlist (e->cc, tmp);
282 e->cc = mutt_expand_aliases (e->cc);
284 mutt_addrlist_to_idna (e->cc, NULL);
285 rfc822_write_address (tmp, sizeof (tmp), e->cc, 1);
286 mvaddstr (LINES - 1, 4, tmp);
289 mutt_addrlist_to_idna (e->cc, NULL);
293 if (option (OPTASKBCC) || force)
297 mutt_addrlist_to_local (e->bcc);
298 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 0);
299 if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 5, 0) == 0)
301 rfc822_free_address (&e->bcc);
302 e->bcc = mutt_parse_adrlist (e->bcc, tmp);
303 e->bcc = mutt_expand_aliases (e->bcc);
304 mutt_addrlist_to_idna (e->bcc, NULL);
306 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1);
307 mvaddstr (LINES - 1, 5, tmp);
310 mutt_addrlist_to_idna (e->bcc, NULL);
315 int mutt_builtin_editor (const char *path, HEADER *msg, HEADER *cur)
318 int bufmax = 0, buflen = 0;
319 char tmp[LONG_STRING];
325 scrollok (stdscr, TRUE);
327 be_edit_header (msg->env, 0);
329 addstr (_("(End message with a . on a line by itself)\n"));
331 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
336 if (mutt_enter_string (tmp, sizeof (tmp), LINES-1, 0, 0) == -1)
343 if (EscChar && tmp[0] == EscChar[0] && tmp[1] != EscChar[0])
345 /* remove trailing whitespace from the line */
346 p = tmp + mutt_strlen (tmp) - 1;
347 while (p >= tmp && ISSPACE (*p))
356 addstr (_(EditorHelp1));
357 addstr (_(EditorHelp2));
360 msg->env->bcc = mutt_parse_adrlist (msg->env->bcc, p);
361 msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
364 msg->env->cc = mutt_parse_adrlist (msg->env->cc, p);
365 msg->env->cc = mutt_expand_aliases (msg->env->cc);
368 be_edit_header (msg->env, 1);
378 /* include the current message */
379 p = tmp + mutt_strlen (tmp) + 1;
380 snprintf (tmp + mutt_strlen (tmp), sizeof (tmp) - mutt_strlen (tmp), " %d",
383 buf = be_include_messages (p, buf, &bufmax, &buflen,
384 (ascii_tolower (tmp[1]) == 'm'),
385 (ascii_isupper ((unsigned char) tmp[1])));
388 addstr (_("No mailbox.\n"));
392 addstr (_("Message contains:\n"));
393 be_print_header (msg->env);
394 for (i = 0; i < buflen; i++)
396 addstr (_("(continue)\n"));
404 strncpy(tmp, p, sizeof(tmp));
405 mutt_expand_path(tmp, sizeof(tmp));
406 buf = be_snarf_file (tmp, buf, &bufmax, &buflen, 1);
409 addstr (_("missing filename.\n"));
412 mutt_str_replace (&msg->env->subject, p);
415 msg->env->to = rfc822_parse_adrlist (msg->env->to, p);
416 msg->env->to = mutt_expand_aliases (msg->env->to);
422 strfcpy (tmp, buf[buflen], sizeof (tmp));
423 tmp[mutt_strlen (tmp)-1] = 0;
429 addstr (_("No lines in message.\n"));
434 if (be_barf_file (path, buf, buflen) == 0)
437 be_free_memory (buf, buflen);
441 if (option (OPTEDITHDRS))
443 mutt_env_to_local (msg->env);
444 mutt_edit_headers (NONULL(Visual), path, msg, NULL, 0);
445 if (mutt_env_to_idna (msg->env, &tag, &err))
446 printw (_("Bad IDN in %s: '%s'\n"), tag, err);
449 mutt_edit_file (NONULL(Visual), path);
451 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
453 addstr (_("(continue)\n"));
457 be_barf_file (*p ? p : path, buf, buflen);
464 printw (_("%s: unknown editor command (~? for help)\n"), tmp);
468 else if (mutt_strcmp (".", tmp) == 0)
472 safe_strcat (tmp, sizeof (tmp), "\n");
473 if (buflen == bufmax)
474 safe_realloc (&buf, sizeof (char *) * (bufmax += 25));
475 buf[buflen++] = safe_strdup (tmp[1] == '~' ? tmp + 1 : tmp);
481 if (!abort) be_barf_file (path, buf, buflen);
482 be_free_memory (buf, buflen);
484 return (abort ? -1 : 0);