3869b33cd524fa1cd2068df37b0d619d07301a06
[apps/madmutt.git] / rfc3676.c
1 /*
2  * Parts were written/modified by:
3  * Andreas Krennmair <ak@synflood.at>
4  * Peter J. Holzer <hjp@hjp.net>
5  * Rocco Rutte <pdmef@cs.tu-berlin.de>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <sys/wait.h>
21 #include <sys/stat.h>
22
23 #include "mutt.h"
24 #include "mutt_curses.h"
25 #include "ascii.h"
26 #include "handler.h"
27 #include "state.h"
28 #include "lib.h"
29
30 #include "lib/mem.h"
31 #include "lib/intl.h"
32 #include "lib/str.h"
33 #include "lib/debug.h"
34
35 typedef int handler_f (BODY *, STATE *);
36 typedef handler_f *handler_t;
37
38 #define FLOWED_MAX 77
39
40 static int get_quote_level (char *line)
41 {
42   int quoted;
43
44   for (quoted = 0; line[quoted] == '>'; quoted++);
45   return quoted;
46 }
47
48 static void print_flowed_line (char *line, STATE * s,
49                                int ql, int delsp,
50                                int* spaces, int space_len) {
51   int width;
52   char *pos, *oldpos;
53   int len = str_len (line);
54   int i;
55
56   if (MaxLineLength > 0) {
57     width = MaxLineLength - WrapMargin - ql - 1;
58     if (option (OPTSTUFFQUOTED))
59       --width;
60     if (width < 0)
61       width = MaxLineLength;
62   }
63   else {
64     if (option (OPTMBOXPANE))
65       width = COLS - SidebarWidth - WrapMargin - ql - 1;
66     else
67       width = COLS - WrapMargin - ql - 1;
68
69     if (option (OPTSTUFFQUOTED))
70       --width;
71     if (width < 0)
72       width = COLS;
73   }
74
75   if (str_len (line) == 0) {
76     if (option (OPTQUOTEEMPTY)) {
77       if (s->prefix)
78         state_puts(s->prefix,s);
79       for (i=0;i<ql;++i) state_putc('>',s);
80       if (option(OPTSTUFFQUOTED))
81         state_putc(' ',s);
82     }
83     state_putc('\n',s);
84     return;
85   }
86
87   pos = line + width;
88   oldpos = line;
89
90   for (; oldpos < line + len; pos += width) {
91     /* only search a new position when we're not over
92      * the end of the string w/ pos */
93     if (pos < line + len) {
94       /* fprintf(stderr,"if 1\n"); */
95       if (*pos == ' ') {
96         /* fprintf(stderr,"if 2: good luck! found a space\n"); */
97         *pos = '\0';
98         ++pos;
99       }
100       else {
101         /* fprintf(stderr,"if 2: else\n"); */
102         char *save = pos;
103
104         while (pos >= oldpos && *pos != ' ') {
105           /* fprintf(stderr,"pos(%p) > oldpos(%p)\n",pos,oldpos); */
106           --pos;
107         }
108         if (pos < oldpos) {
109           /* fprintf(stderr,"wow, no space found,
110            * searching the other direction\n"); */
111           pos = save;
112           while (pos < line + len && *pos && *pos != ' ') {
113             /* fprintf(stderr,"pos(%p) < line+len(%p)\n",pos,line+len); */
114             ++pos;
115           }
116           /* fprintf(stderr,"found a space pos = %p\n",pos); */
117         }
118         *pos = '\0';
119         ++pos;
120       }
121     }
122     else {
123       /* fprintf(stderr,"if 1 else\n"); */
124     }
125     if (s->prefix)
126       state_puts (s->prefix, s);
127
128     for (i = 0; i < ql; ++i)
129       state_putc ('>', s);
130     if (option (OPTSTUFFQUOTED) && (ql > 0 || s->prefix))
131       state_putc (' ', s);
132
133     if (delsp && spaces && space_len > 0) {
134       /* here, we need to character-wise step through the line
135        * to eliminate all spaces which were trailing due to DelSp */
136       for (i = 0; i < str_len (oldpos); i++) {
137         if (oldpos[i] == ' ' && spaces[&(oldpos[i])-line] != 0) {
138           debug_print (4, ("DelSp: spaces[%d] forces space removal\n",
139                            &(oldpos[i])-line));
140           continue;
141         }
142         /* print space at oldpos[i] if it was non-trailing */
143         state_putc (oldpos[i], s);
144       }
145     } else
146       /* for no DelSp, just do whole line as per usual */
147       state_puts (oldpos, s);
148     /* fprintf(stderr,"print_flowed_line: `%s'\n",oldpos); */
149     if (pos < line + len)
150       state_putc (' ', s);
151     state_putc ('\n', s);
152     oldpos = pos;
153   }
154 }
155
156 int rfc3676_handler (BODY * a, STATE * s) {
157   int bytes = a->length;
158   char buf[LONG_STRING];
159   char *curline = str_dup ("");
160   char *t = NULL;
161   unsigned int curline_len = 1, space_len = 1,
162                quotelevel = 0, newql = 0;
163   int buf_off, buf_len;
164   int delsp = 0;
165   int* spaces = NULL;
166
167   /* respect DelSP of RfC3676 only with f=f parts */
168   if ((t = (char*) mutt_get_parameter ("delsp", a->parameter))) {
169     delsp = str_len (t) == 3 && ascii_strncasecmp (t, "yes", 3) == 0;
170     t = NULL;
171   }
172
173   debug_print (2, ("DelSp: %s\n", delsp ? "yes" : "no"));
174
175   while (bytes > 0 && fgets (buf, sizeof (buf), s->fpin)) {
176     buf_len = str_len (buf);
177     bytes -= buf_len;
178
179     newql = get_quote_level (buf);
180
181     /* a change of quoting level in a paragraph - shouldn't happen, 
182      * but has to be handled - see RFC 3676, sec. 4.5.
183      */
184     if (newql != quotelevel && curline && *curline) {
185       print_flowed_line (curline, s, quotelevel, delsp, spaces, space_len);
186       *curline = '\0';
187       curline_len = 1;
188       space_len = 0;
189     }
190     quotelevel = newql;
191
192     /* XXX - If a line is longer than buf (shouldn't happen), it is split.
193      * This will almost always cause an unintended line break, and 
194      * possibly a change in quoting level. But that's better than not
195      * displaying it at all.
196      */
197     if ((t = strrchr (buf, '\n')) || (t = strrchr (buf, '\r'))) {
198       *t = '\0';
199       buf_len = t - buf;
200     }
201     buf_off = newql;
202     /* respect space-stuffing */
203     if (buf[buf_off] == ' ')
204       buf_off++;
205
206     /* signature separator also flushes the previous paragraph */
207     if (strcmp(buf + buf_off, "-- ") == 0 && curline && *curline) {
208       print_flowed_line (curline, s, quotelevel, delsp, spaces, space_len);
209       *curline = '\0';
210       curline_len = 1;
211       space_len = 0;
212     }
213
214     mem_realloc (&curline, curline_len + buf_len - buf_off);
215     mem_realloc (&spaces, (curline_len + buf_len - buf_off)*sizeof (int));
216     strcpy (curline + curline_len - 1, buf + buf_off);
217     memset (&spaces[space_len], 0, (buf_len - buf_off)*sizeof (int));
218     curline_len += buf_len - buf_off;
219     space_len += buf_len - buf_off;
220
221     /* if this was a fixed line the paragraph is finished */
222     if (buf_len == 0 || buf[buf_len - 1] != ' ' || strcmp(buf + buf_off, "-- ") == 0) {
223       print_flowed_line (curline, s, quotelevel, delsp, spaces, space_len);
224       *curline = '\0';
225       curline_len = 1;
226       space_len = 0;
227     } else {
228       /* if last line we appended had a space and we have DelSp=yes,
229        * get a 1 into spaces array at proper position so that
230        * print_flowed_line() can handle it; don't kill the space
231        * right here 'cause we maybe need soft linebreaks to search for break
232        */
233       if (delsp && curline && *curline && curline_len-2 >= 0 &&
234           curline[curline_len-2] == ' ') {
235         debug_print (4, ("DelSp: marking spaces[%d] for later removal\n",
236                          curline_len-2));
237         spaces[curline_len-2] = 1;
238       }
239     }
240
241   }
242   mem_free (&spaces);
243   mem_free (&curline);
244   return (0);
245 }