2 * Copyright (C) 2005 Andreas Krennmair <ak@synflood.at>
3 * Copyright (C) 2005 Peter J. Holzer <hjp@hjp.net>
4 * Copyright (C) 2005-9 Rocco Rutte <pdmef@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* This file was originally part of mutt-ng */
36 #include "mutt_curses.h"
42 typedef struct flowed_state
49 static int get_quote_level (const char *line)
52 char *p = (char *) line;
54 while (p && *p == '>')
63 static size_t print_indent (int ql, STATE *s, int sp)
70 /* use given prefix only for format=fixed replies to format=flowed,
71 * for format=flowed replies to format=flowed, use '>' indentation
73 if (option (OPTTEXTFLOWED))
77 state_puts (s->prefix, s);
78 wid = mutt_strwidth (s->prefix);
82 for (i = 0; i < ql; i++)
89 static void flush_par (STATE *s, flowed_state_t *fst)
99 static int quote_width (STATE *s, int ql)
101 size_t width = (Wrap ? mutt_term_width (Wrap) : FLOWED_MAX) - 1;
102 if (s->flags & M_REPLYING && width > FLOWED_MAX)
109 static void print_flowed_line (char *line, STATE *s, int ql,
110 flowed_state_t *fst, int term)
112 size_t width, w, words = 0;
118 /* flush current paragraph (if any) first */
120 print_indent (ql, s, 0);
121 state_putc ('\n', s);
125 width = quote_width (s, ql);
126 last = line[mutt_strlen (line) - 1];
128 dprint (4, (debugfile, "f=f: line [%s], width = %ld, spaces = %d\n",
129 NONULL(line), (long)width, fst->spaces));
131 for (p = (char *)line, words = 0; (p = strsep (&line, " ")) != NULL ; )
133 dprint(4,(debugfile,"f=f: word [%s], width: %d, remaining = [%s]\n",
134 p, fst->width, line));
136 /* remember number of spaces */
139 dprint(4,(debugfile,"f=f: additional space\n"));
143 /* there's exactly one space prior to every but the first word */
147 w = mutt_strwidth (p);
148 /* see if we need to break the line but make sure the first
149 word is put on the line regardless;
150 if for DelSp=yes only one trailing space is used, we probably
151 have a long word that we should break within (we leave that
152 up to the pager or user) */
153 if (!(!fst->spaces && fst->delsp && last != ' ') &&
154 w < width && w + fst->width + fst->spaces > width)
156 dprint(4,(debugfile,"f=f: break line at %d, %d spaces left\n",
157 fst->width, fst->spaces));
158 /* only honor trailing spaces for format=flowed replies */
159 if (option(OPTTEXTFLOWED))
160 for ( ; fst->spaces; fst->spaces--)
162 state_putc ('\n', s);
168 if (!words && !fst->width)
169 fst->width = print_indent (ql, s, !(s->flags & M_REPLYING) &&
170 (ql > 0 || s->prefix));
171 fst->width += w + fst->spaces;
172 for ( ; fst->spaces; fst->spaces--)
182 static void print_fixed_line (const char *line, STATE *s, int ql,
185 print_indent (ql, s, !(s->flags & M_REPLYING) && (ql > 0 || s->prefix));
187 state_puts (line, s);
188 state_putc ('\n', s);
194 int rfc3676_handler (BODY * a, STATE * s)
196 char *buf = NULL, *t = NULL;
197 unsigned int quotelevel = 0, newql = 0, sigsep = 0;
198 int buf_off = 0, delsp = 0, fixed = 0;
199 size_t buf_len = 0, sz = 0;
202 memset (&fst, 0, sizeof (fst));
204 /* respect DelSp of RfC3676 only with f=f parts */
205 if ((t = (char *) mutt_get_parameter ("delsp", a->parameter)))
207 delsp = mutt_strlen (t) == 3 && ascii_strncasecmp (t, "yes", 3) == 0;
212 dprint (4, (debugfile, "f=f: DelSp: %s\n", delsp ? "yes" : "no"));
214 while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0)))
216 buf_len = mutt_strlen (buf);
217 newql = get_quote_level (buf);
219 /* end flowed paragraph (if we're within one) if quoting level
220 * changes (should not but can happen, see RFC 3676, sec. 4.5.)
222 if (newql != quotelevel)
228 /* respect sender's space-stuffing by removing one leading space */
229 if (buf[buf_off] == ' ')
232 /* test for signature separator */
233 sigsep = ascii_strcmp (buf + buf_off, "-- ") == 0;
235 /* a fixed line either has no trailing space or is the
236 * signature separator */
237 fixed = buf_len == buf_off || buf[buf_len - 1] != ' ' || sigsep;
239 /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
241 if ((fixed && (!fst.width || !buf_len)) || sigsep)
243 /* if we're within a flowed paragraph, terminate it */
245 print_fixed_line (buf + buf_off, s, quotelevel, &fst);
249 /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
251 buf[--buf_len] = '\0';
253 print_flowed_line (buf + buf_off, s, quotelevel, &fst, fixed);
263 * This routine does RfC3676 space stuffing since it's a MUST.
264 * Space stuffing means that we have to add leading spaces to
266 * - lines starting with a space
267 * - lines starting with 'From '
268 * This routine is only called once right after editing the
269 * initial message so it's up to the user to take care of stuffing
270 * when editing the message several times before actually sending it
272 * This is more or less a hack as it replaces the message's content with
273 * a freshly created copy in a tempfile and modifies the file's mtime
274 * so we don't trigger code paths watching for mtime changes
276 void rfc3676_space_stuff (HEADER* hdr)
281 unsigned char c = '\0';
283 FILE *in = NULL, *out = NULL;
284 char buf[LONG_STRING];
285 char tmpfile[_POSIX_PATH_MAX];
287 if (!hdr || !hdr->content || !hdr->content->filename)
290 dprint (2, (debugfile, "f=f: postprocess %s\n", hdr->content->filename));
292 if ((in = safe_fopen (hdr->content->filename, "r")) == NULL)
295 mutt_mktemp (tmpfile, sizeof (tmpfile));
296 if ((out = safe_fopen (tmpfile, "w+")) == NULL)
302 while (fgets (buf, sizeof (buf), in))
304 if (ascii_strncmp ("From ", buf, 5) == 0 || buf[0] == ' ') {
308 len = mutt_strlen (buf);
314 dprint (4, (debugfile, "f=f: line %d needs space-stuffing: '%s'\n",
324 mutt_set_mtime (hdr->content->filename, tmpfile);
325 unlink (hdr->content->filename);
326 mutt_str_replace (&hdr->content->filename, tmpfile);