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"
39 * SLcurses_waddnstr() can't take a "const char *", so this is only
40 * declared "static" (sigh)
42 static char* EditorHelp1 = N_("\
43 ~~ insert a line beginning with a single ~\n\
44 ~b users add users to the Bcc: field\n\
45 ~c users add users to the Cc: field\n\
46 ~f messages include messages\n\
47 ~F messages same as ~f, except also include headers\n\
48 ~h edit the message header\n\
49 ~m messages include and quote messages\n\
50 ~M messages same as ~m, except include headers\n\
51 ~p print the message\n");
53 static char* EditorHelp2 = N_("\
54 ~q write file and quit editor\n\
55 ~r file read a file into the editor\n\
56 ~t users add users to the To: field\n\
57 ~u recall the previous line\n\
58 ~v edit message with the $visual editor\n\
59 ~w file write message to file\n\
60 ~x abort changes and quit editor\n\
62 . on a line by itself ends input\n");
65 be_snarf_data (FILE *f, char **buf, int *bufmax, int *buflen, LOFF_T offset,
66 int bytes, int prefix)
68 char tmp[HUGE_STRING];
70 int tmplen = sizeof (tmp);
72 tmp[sizeof (tmp) - 1] = 0;
75 strfcpy (tmp, NONULL(Prefix), sizeof (tmp));
76 tmplen = mutt_strlen (tmp);
78 tmplen = sizeof (tmp) - tmplen;
81 fseeko (f, offset, 0);
84 if (fgets (p, tmplen - 1, f) == NULL) break;
85 bytes -= mutt_strlen (p);
86 if (*bufmax == *buflen)
87 safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
88 buf[(*buflen)++] = safe_strdup (tmp);
90 if (buf && *bufmax == *buflen) /* Do not smash memory past buf */
92 safe_realloc (&buf, sizeof (char *) * (++*bufmax));
94 if (buf) buf[*buflen] = NULL;
99 be_snarf_file (const char *path, char **buf, int *max, int *len, int verbose)
102 char tmp[LONG_STRING];
105 if ((f = fopen (path, "r")))
107 fstat (fileno (f), &sb);
108 buf = be_snarf_data (f, buf, max, len, 0, sb.st_size, 0);
111 snprintf(tmp, sizeof(tmp), "\"%s\" %lu bytes\n", path, (unsigned long) sb.st_size);
118 snprintf(tmp, sizeof(tmp), "%s: %s\n", path, strerror(errno));
124 static int be_barf_file (const char *path, char **buf, int buflen)
129 if ((f = fopen (path, "w")) == NULL) /* __FOPEN_CHECKED__ */
131 addstr (strerror (errno));
135 for (i = 0; i < buflen; i++) fputs (buf[i], f);
136 if (fclose (f) == 0) return 0;
137 printw ("fclose: %s\n", strerror (errno));
141 static void be_free_memory (char **buf, int buflen)
150 be_include_messages (char *msg, char **buf, int *bufmax, int *buflen,
151 int pfx, int inc_hdrs)
153 int offset, bytes, n;
154 char tmp[LONG_STRING];
156 while ((msg = strtok (msg, " ,")) != NULL)
158 if (mutt_atoi (msg, &n) == 0 && n > 0 && n <= Context->msgcount)
162 /* add the attribution */
165 setlocale (LC_TIME, NONULL (AttributionLocale));
166 mutt_make_string (tmp, sizeof (tmp) - 1, Attribution, Context, Context->hdrs[n]);
167 setlocale (LC_TIME, "");
168 strcat (tmp, "\n"); /* __STRCAT_CHECKED__ */
171 if (*bufmax == *buflen)
172 safe_realloc ( &buf, sizeof (char *) * (*bufmax += 25));
173 buf[(*buflen)++] = safe_strdup (tmp);
175 bytes = Context->hdrs[n]->content->length;
178 offset = Context->hdrs[n]->offset;
179 bytes += Context->hdrs[n]->content->offset - offset;
182 offset = Context->hdrs[n]->content->offset;
183 buf = be_snarf_data (Context->fp, buf, bufmax, buflen, offset, bytes,
186 if (*bufmax == *buflen)
187 safe_realloc (&buf, sizeof (char *) * (*bufmax += 25));
188 buf[(*buflen)++] = safe_strdup ("\n");
191 printw (_("%d: invalid message number.\n"), n);
197 static void be_print_header (ENVELOPE *env)
199 char tmp[HUGE_STRING];
205 rfc822_write_address (tmp, sizeof (tmp), env->to, 1);
213 rfc822_write_address (tmp, sizeof (tmp), env->cc, 1);
221 rfc822_write_address (tmp, sizeof (tmp), env->bcc, 1);
227 addstr ("Subject: ");
228 addstr (env->subject);
235 * force override the $ask* vars (used for the ~h command)
237 static void be_edit_header (ENVELOPE *e, int force)
239 char tmp[HUGE_STRING];
241 mutt_window_move (MuttMessageWindow, 0, 0);
245 mutt_addrlist_to_local (e->to);
246 rfc822_write_address (tmp, sizeof (tmp), e->to, 0);
249 if (mutt_enter_string (tmp, sizeof (tmp), 4, 0) == 0)
251 rfc822_free_address (&e->to);
252 e->to = mutt_parse_adrlist (e->to, tmp);
253 e->to = mutt_expand_aliases (e->to);
254 mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */
256 rfc822_write_address (tmp, sizeof (tmp), e->to, 1);
257 mutt_window_mvaddstr (MuttMessageWindow, 0, 4, tmp);
262 mutt_addrlist_to_intl (e->to, NULL); /* XXX - IDNA error reporting? */
267 if (!e->subject || force)
269 addstr ("Subject: ");
270 strfcpy (tmp, e->subject ? e->subject: "", sizeof (tmp));
271 if (mutt_enter_string (tmp, sizeof (tmp), 9, 0) == 0)
272 mutt_str_replace (&e->subject, tmp);
276 if ((!e->cc && option (OPTASKCC)) || force)
280 mutt_addrlist_to_local (e->cc);
281 rfc822_write_address (tmp, sizeof (tmp), e->cc, 0);
282 if (mutt_enter_string (tmp, sizeof (tmp), 4, 0) == 0)
284 rfc822_free_address (&e->cc);
285 e->cc = mutt_parse_adrlist (e->cc, tmp);
286 e->cc = mutt_expand_aliases (e->cc);
288 mutt_addrlist_to_intl (e->cc, NULL);
289 rfc822_write_address (tmp, sizeof (tmp), e->cc, 1);
290 mutt_window_mvaddstr (MuttMessageWindow, 0, 4, tmp);
293 mutt_addrlist_to_intl (e->cc, NULL);
297 if (option (OPTASKBCC) || force)
301 mutt_addrlist_to_local (e->bcc);
302 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 0);
303 if (mutt_enter_string (tmp, sizeof (tmp), 5, 0) == 0)
305 rfc822_free_address (&e->bcc);
306 e->bcc = mutt_parse_adrlist (e->bcc, tmp);
307 e->bcc = mutt_expand_aliases (e->bcc);
308 mutt_addrlist_to_intl (e->bcc, NULL);
310 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1);
311 mutt_window_mvaddstr (MuttMessageWindow, 0, 5, tmp);
314 mutt_addrlist_to_intl (e->bcc, NULL);
319 int mutt_builtin_editor (const char *path, HEADER *msg, HEADER *cur)
322 int bufmax = 0, buflen = 0;
323 char tmp[LONG_STRING];
329 scrollok (stdscr, TRUE);
331 be_edit_header (msg->env, 0);
333 addstr (_("(End message with a . on a line by itself)\n"));
335 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
340 if (mutt_enter_string (tmp, sizeof (tmp), 0, 0) == -1)
347 if (EscChar && tmp[0] == EscChar[0] && tmp[1] != EscChar[0])
349 /* remove trailing whitespace from the line */
350 p = tmp + mutt_strlen (tmp) - 1;
351 while (p >= tmp && ISSPACE (*p))
360 addstr (_(EditorHelp1));
361 addstr (_(EditorHelp2));
364 msg->env->bcc = mutt_parse_adrlist (msg->env->bcc, p);
365 msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
368 msg->env->cc = mutt_parse_adrlist (msg->env->cc, p);
369 msg->env->cc = mutt_expand_aliases (msg->env->cc);
372 be_edit_header (msg->env, 1);
382 /* include the current message */
383 p = tmp + mutt_strlen (tmp) + 1;
384 snprintf (tmp + mutt_strlen (tmp), sizeof (tmp) - mutt_strlen (tmp), " %d",
387 buf = be_include_messages (p, buf, &bufmax, &buflen,
388 (ascii_tolower (tmp[1]) == 'm'),
389 (ascii_isupper ((unsigned char) tmp[1])));
392 addstr (_("No mailbox.\n"));
396 addstr (_("Message contains:\n"));
397 be_print_header (msg->env);
398 for (i = 0; i < buflen; i++)
401 This entry is shown AFTER the message content,
402 not IN the middle of the content.
403 So it doesn't mean "(message will continue)"
404 but means "(press any key to continue using mutt)". */
405 addstr (_("(continue)\n"));
413 strncpy(tmp, p, sizeof(tmp));
414 mutt_expand_path(tmp, sizeof(tmp));
415 buf = be_snarf_file (tmp, buf, &bufmax, &buflen, 1);
418 addstr (_("missing filename.\n"));
421 mutt_str_replace (&msg->env->subject, p);
424 msg->env->to = rfc822_parse_adrlist (msg->env->to, p);
425 msg->env->to = mutt_expand_aliases (msg->env->to);
431 strfcpy (tmp, buf[buflen], sizeof (tmp));
432 tmp[mutt_strlen (tmp)-1] = 0;
438 addstr (_("No lines in message.\n"));
443 if (be_barf_file (path, buf, buflen) == 0)
446 be_free_memory (buf, buflen);
450 if (option (OPTEDITHDRS))
452 mutt_env_to_local (msg->env);
453 mutt_edit_headers (NONULL(Visual), path, msg, NULL);
454 if (mutt_env_to_intl (msg->env, &tag, &err))
455 printw (_("Bad IDN in %s: '%s'\n"), tag, err);
458 mutt_edit_file (NONULL(Visual), path);
460 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
462 addstr (_("(continue)\n"));
466 be_barf_file (*p ? p : path, buf, buflen);
473 printw (_("%s: unknown editor command (~? for help)\n"), tmp);
477 else if (mutt_strcmp (".", tmp) == 0)
481 safe_strcat (tmp, sizeof (tmp), "\n");
482 if (buflen == bufmax)
483 safe_realloc (&buf, sizeof (char *) * (bufmax += 25));
484 buf[buflen++] = safe_strdup (tmp[1] == '~' ? tmp + 1 : tmp);
490 if (!abort) be_barf_file (path, buf, buflen);
491 be_free_memory (buf, buflen);
493 return (abort ? -1 : 0);