2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 /* Close approximation of the mailx(1) builtin editor for sending mail. */
16 #include <lib-lib/mem.h>
17 #include <lib-lib/ascii.h>
18 #include <lib-lib/str.h>
19 #include <lib-lib/macros.h>
20 #include <lib-lib/file.h>
24 #include "mutt_curses.h"
25 #include "mutt_idna.h"
37 * SLcurses_waddnstr() can't take a "const char *", so this is only
38 * declared "static" (sigh)
40 static const char *EditorHelp = N_("\
41 ~~ insert a line begining with a single ~\n\
42 ~b users add users to the Bcc: field\n\
43 ~c users add users to the Cc: field\n\
44 ~f messages include messages\n\
45 ~F messages same as ~f, except also include headers\n\
46 ~h edit the message header\n\
47 ~m messages include and quote messages\n\
48 ~M messages same as ~m, except include headers\n\
49 ~p print the message\n\
50 ~q write file and quit editor\n\
51 ~r file read a file into the editor\n\
52 ~t users add users to the To: field\n\
53 ~u recall the previous line\n\
54 ~v edit message with the $visual editor\n\
55 ~w file write message to file\n\
56 ~x abort changes and quit editor\n\
58 . on a line by itself ends input\n");
60 static char **be_snarf_data (FILE * f, char **buf, int *bufmax, int *buflen,
61 off_t offset, int bytes, int prefix)
63 char tmp[HUGE_STRING];
65 int tmplen = sizeof (tmp);
67 tmp[sizeof (tmp) - 1] = 0;
69 m_strcpy(tmp, sizeof(tmp), NONULL(Prefix));
70 tmplen = m_strlen(tmp);
72 tmplen = sizeof (tmp) - tmplen;
75 fseeko (f, offset, 0);
77 if (fgets (p, tmplen - 1, f) == NULL)
80 if (*bufmax == *buflen)
81 p_realloc(&buf, *bufmax += 25);
82 buf[(*buflen)++] = m_strdup(tmp);
84 if (buf && *bufmax == *buflen) { /* Do not smash memory past buf */
85 p_realloc(&buf, ++*bufmax);
92 static char **be_snarf_file (const char *path, char **buf, int *max, int *len,
96 char tmp[LONG_STRING];
99 if ((f = fopen (path, "r"))) {
100 fstat (fileno (f), &sb);
101 buf = be_snarf_data (f, buf, max, len, 0, sb.st_size, 0);
103 snprintf (tmp, sizeof (tmp), "\"%s\" %lu bytes\n", path,
104 (unsigned long) sb.st_size);
110 snprintf (tmp, sizeof (tmp), "%s: %s\n", path, strerror (errno));
116 static int be_barf_file (const char *path, char **buf, int buflen)
121 if ((f = safe_fopen (path, "w")) == NULL) { /* __FOPEN_CHECKED__ */
122 addstr (strerror (errno));
126 for (i = 0; i < buflen; i++)
130 printw ("fclose: %s\n", strerror (errno));
134 static void be_free_memory (char **buf, int buflen)
137 p_delete(&buf[buflen]);
142 static char **be_include_messages (char *msg, char **buf, int *bufmax,
143 int *buflen, int pfx, int inc_hdrs)
145 int offset, bytes, n;
146 char tmp[LONG_STRING];
148 while ((msg = strtok (msg, " ,")) != NULL) {
150 if (n > 0 && n <= Context->msgcount) {
153 /* add the attribution */
155 mutt_make_string (tmp, sizeof (tmp) - 1, Attribution, Context,
157 strcat (tmp, "\n"); /* __STRCAT_CHECKED__ */
160 if (*bufmax == *buflen)
161 p_realloc(&buf, *bufmax += 25);
162 buf[(*buflen)++] = m_strdup(tmp);
164 bytes = Context->hdrs[n]->content->length;
166 offset = Context->hdrs[n]->offset;
167 bytes += Context->hdrs[n]->content->offset - offset;
170 offset = Context->hdrs[n]->content->offset;
171 buf = be_snarf_data (Context->fp, buf, bufmax, buflen, offset, bytes,
174 if (*bufmax == *buflen)
175 p_realloc(&buf, *bufmax += 25);
176 buf[(*buflen)++] = m_strdup("\n");
179 printw (_("%d: invalid message number.\n"), n);
185 static void be_print_header (ENVELOPE * env)
187 char tmp[HUGE_STRING];
192 rfc822_write_address (tmp, sizeof (tmp), env->to, 1);
199 rfc822_write_address (tmp, sizeof (tmp), env->cc, 1);
206 rfc822_write_address (tmp, sizeof (tmp), env->bcc, 1);
211 addstr ("Subject: ");
212 addstr (env->subject);
219 * force override the $ask* vars (used for the ~h command)
221 static void be_edit_header (ENVELOPE * e, int force)
223 char tmp[HUGE_STRING];
229 mutt_addrlist_to_local (e->to);
230 rfc822_write_address (tmp, sizeof (tmp), e->to, 0);
231 if (!e->to || force) {
232 if (mutt_enter_string (tmp, sizeof (tmp), LINES - 1, 4, 0) == 0) {
233 address_delete (&e->to);
234 e->to = mutt_parse_adrlist (e->to, tmp);
235 e->to = mutt_expand_aliases (e->to);
236 mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
238 rfc822_write_address (tmp, sizeof (tmp), e->to, 1);
239 mvaddstr (LINES - 1, 4, tmp);
243 mutt_addrlist_to_idna (e->to, NULL); /* XXX - IDNA error reporting? */
248 if (!e->subject || force) {
249 addstr ("Subject: ");
250 m_strcpy(tmp, sizeof(tmp), NONULL(e->subject));
251 if (mutt_enter_string (tmp, sizeof (tmp), LINES - 1, 9, 0) == 0)
252 str_replace (&e->subject, tmp);
256 if ((!e->cc && option (OPTASKCC)) || force) {
259 mutt_addrlist_to_local (e->cc);
260 rfc822_write_address (tmp, sizeof (tmp), e->cc, 0);
261 if (mutt_enter_string (tmp, sizeof (tmp), LINES - 1, 4, 0) == 0) {
262 address_delete (&e->cc);
263 e->cc = mutt_parse_adrlist (e->cc, tmp);
264 e->cc = mutt_expand_aliases (e->cc);
266 mutt_addrlist_to_idna (e->cc, NULL);
267 rfc822_write_address (tmp, sizeof (tmp), e->cc, 1);
268 mvaddstr (LINES - 1, 4, tmp);
271 mutt_addrlist_to_idna (e->cc, NULL);
275 if (option (OPTASKBCC) || force) {
278 mutt_addrlist_to_local (e->bcc);
279 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 0);
280 if (mutt_enter_string (tmp, sizeof (tmp), LINES - 1, 5, 0) == 0) {
281 address_delete (&e->bcc);
282 e->bcc = mutt_parse_adrlist (e->bcc, tmp);
283 e->bcc = mutt_expand_aliases (e->bcc);
284 mutt_addrlist_to_idna (e->bcc, NULL);
286 rfc822_write_address (tmp, sizeof (tmp), e->bcc, 1);
287 mvaddstr (LINES - 1, 5, tmp);
290 mutt_addrlist_to_idna (e->bcc, NULL);
295 int mutt_builtin_editor (const char *path, HEADER * msg, HEADER * cur)
298 int bufmax = 0, buflen = 0;
299 char tmp[LONG_STRING];
305 scrollok (stdscr, TRUE);
307 be_edit_header (msg->env, 0);
309 addstr (_("(End message with a . on a line by itself)\n"));
311 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
315 if (mutt_enter_string (tmp, sizeof (tmp), LINES - 1, 0, 0) == -1) {
321 if (EscChar && tmp[0] == EscChar[0] && tmp[1] != EscChar[0]) {
322 /* remove trailing whitespace from the line */
323 p = tmp + m_strlen(tmp) - 1;
324 while (p >= tmp && ISSPACE (*p))
327 p = vskipspaces(tmp + 2);
331 addstr (_(EditorHelp));
334 msg->env->bcc = mutt_parse_adrlist (msg->env->bcc, p);
335 msg->env->bcc = mutt_expand_aliases (msg->env->bcc);
338 msg->env->cc = mutt_parse_adrlist (msg->env->cc, p);
339 msg->env->cc = mutt_expand_aliases (msg->env->cc);
342 be_edit_header (msg->env, 1);
350 /* include the current message */
351 p = tmp + m_strlen(tmp) + 1;
352 snprintf (tmp + m_strlen(tmp),
353 sizeof (tmp) - m_strlen(tmp), " %d",
356 buf = be_include_messages (p, buf, &bufmax, &buflen,
357 (ascii_tolower (tmp[1]) == 'm'),
359 ((unsigned char) tmp[1])));
362 addstr (_("No mailbox.\n"));
366 addstr (_("Message contains:\n"));
367 be_print_header (msg->env);
368 for (i = 0; i < buflen; i++)
370 addstr (_("(continue)\n"));
377 m_strcpy(tmp, sizeof(tmp), p);
378 mutt_expand_path (tmp, sizeof (tmp));
379 buf = be_snarf_file (tmp, buf, &bufmax, &buflen, 1);
382 addstr (_("missing filename.\n"));
385 str_replace (&msg->env->subject, p);
388 msg->env->to = rfc822_parse_adrlist (msg->env->to, p);
389 msg->env->to = mutt_expand_aliases (msg->env->to);
394 m_strcpy(tmp, sizeof(tmp), buf[buflen]);
395 tmp[m_strlen(tmp) - 1] = 0;
396 p_delete(&buf[buflen]);
401 addstr (_("No lines in message.\n"));
406 if (be_barf_file (path, buf, buflen) == 0) {
407 const char *tag, *err;
409 be_free_memory (buf, buflen);
413 if (option (OPTEDITHDRS)) {
414 mutt_env_to_local (msg->env);
415 mutt_edit_headers (NONULL (Visual), path, msg, NULL, 0);
416 if (mutt_env_to_idna (msg->env, &tag, &err))
417 printw (_("Bad IDN in %s: '%s'\n"), tag, err);
420 mutt_edit_file (NONULL (Visual), path);
422 buf = be_snarf_file (path, buf, &bufmax, &buflen, 0);
424 addstr (_("(continue)\n"));
428 be_barf_file (*p ? p : path, buf, buflen);
435 printw (_("%s: unknown editor command (~? for help)\n"), tmp);
439 else if (m_strcmp(".", tmp) == 0)
442 m_strcat(tmp, sizeof(tmp), "\n");
443 if (buflen == bufmax)
444 p_realloc(&buf, bufmax += 25);
445 buf[buflen++] = m_strdup(tmp[1] == '~' ? tmp + 1 : tmp);
452 be_barf_file (path, buf, buflen);
453 be_free_memory (buf, buflen);
455 return (abort ? -1 : 0);