Rename occurrences of USE_ to HAVE_ (again)
[s-mailx.git] / filter.c
blob5c6eff0b302fa5da0664d737c4ad4f9357295b44
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Filter objects.
4 * Copyright (c) 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include "rcv.h"
21 #include "extern.h"
24 * Quotation filter
27 #undef HAVE_QUOTE_FOLD
29 #ifdef HAVE_QUOTE_FOLD
30 CTASSERT(QUOTE_MAX > 3);
32 enum qf_state {
33 _QF_NONE, /* Clean line, nothing read yet */
34 _QF_PREFIX, /* Still collecting prefix */
35 _QF_DATA /* Prefix passed */
38 struct qf_vc {
39 struct quoteflt * self;
40 char const * buf;
41 size_t len;
44 static ssize_t _qf_state_prefix(struct qf_vc *vc);
45 static ssize_t _qf_state_data(struct qf_vc *vc);
47 #if 0
48 static size_t
49 prefixwrite(char const *ptr, size_t size, FILE *f,
50 char const *prefix, size_t prefixlen)
52 p = ptr;
53 maxp = p + size;
55 } else {
56 /* After writing a real newline followed by our prefix,
57 * compress the quoted prefixes;
58 * note that \n is only matched by spacechar(), not by
59 * blankchar() or blankspacechar() */
61 * TODO the problem we have is again the odd 4:3 relation of
62 * TODO base64 -- if we quote a mail that is in base64 then
63 * TODO prefixwrite() doesn't get invoked with partial multi-
64 * TODO byte characters (S-nail uses the `rest' mechanism to
65 * TODO avoid that), but it may of course be invoked with a
66 * TODO partial line, and even with multiple thereof.
67 * TODO this has to be addressed in 15.0 with the MIME and send
68 * TODO layer rewrite. The solution is that `prefixwrite' has
69 * TODO to be an object with state -- if a part is to be quoted
70 * TODO you create it, and simply feed in data; once the part
71 * TODO is done, you'll release it; the object itself gobbles
72 * TODO data unless a *hard* newline is seen.
73 * TODO in fact we can then even implement paragraph-wise
74 * TODO quoting! FIXME in fact that is the way: objects
75 * TODO then, evaluate quote-fold when sending starts, ONCE!
76 * FIXME NOTE: base64 (yet TODO for qp) may have CRLF line
77 * FIXME endings, these need to be removed in a LOWER LAYER!!
79 fprintf(stderr, "ENTRY: lastlen=%zu, size=%zu <%.*s>\n",lastlen,size,(int)size,p);
80 if ((lnlen = lastlen) != 0)
81 goto jcontb64;
82 jhardnl:
83 lastlen = prefixlen;
84 for (zipl = i = 0; p + i < maxp; ++i) {
85 c = p[i];
86 if (blankspacechar(c))
87 continue;
88 if (! ISQUOTE(c))
89 break;
90 if (zipl == sizeof(zipb) - 1) {
91 zipb[sizeof(zipb) - 2] = '.';
92 zipb[sizeof(zipb) - 3] = '.';
93 zipb[sizeof(zipb) - 4] = '.';
94 continue;
96 zipb[zipl++] = c;
98 zipb[zipl] = '\0';
99 p += i;
100 jsoftnl:
101 if (zipl > 0) {
102 wsz += fwrite(zipb, sizeof *zipb, zipl, f);
103 lnlen += zipl;
104 lastlen = lnlen;
106 jcontb64:
107 /* Search forward until either *quote-fold* or NL.
108 * In the former case try to break at whitespace,
109 * but only if's located in the 2nd half of the data */
110 for (c = i = 0; p + i < maxp;) {
111 c = p[i++];
112 if (c == '\n')
113 break;
114 if (lnlen + i <= qfold_max)
115 continue;
117 /* We're excessing bounds -- but don't "continue"
118 * trailing WS nor a continuation */
119 if (c == '\\' || spacechar(c)) {
120 char const *cp;
122 for (cp = p + i; cp < maxp; ++cp)
123 if (! spacechar(*cp))
124 break;
125 if (cp == maxp || (*cp == '\\' &&
126 cp[1] == '\n')) {
127 i = (size_t)(maxp - p);
128 c = 0;
129 break;
131 } else if (p + i < maxp && p[i] == '\n') {
132 ++i;
133 c = 0;
134 break;
137 /* We have to fold this line */
138 if (qfold_min < lnlen) {
139 /* This is because of base64 and odd 4:3.
140 * i.e., entered with some partial line yet
141 * written.. This is weird, as we may have
142 * written out `qfold_max' already.. */
143 j = 0;
144 size = --i;
145 fprintf(stderr, "-- size=%zu p<%.*s>\n",size,(int)size,p);
146 } else {
147 j = qfold_min - lnlen;
149 size = j + ((qfold_max - qfold_min) >> 1);
150 assert(p + i < maxp);
152 while (i > j && ! spacechar(p[i - 1]))
153 --i;
154 if (i == j)
155 i = size;
156 c = 0;
157 break;
160 if (i > 0) {
161 wsz += fwrite(p, sizeof *p, i, f);
162 p += i;
163 lastlen += i;
166 if (p < maxp) {
167 if (c != '\n') {
168 putc('\\', f);
169 putc('\n', f);
170 wsz += 2;
172 wsz += fwrite(prefix, sizeof *prefix, prefixlen, f);
173 lnlen = prefixlen;
174 if (c != '\n')
175 goto jsoftnl;
176 goto jhardnl;
180 lastc = p[-1];
181 return wsz;
183 #endif
186 static ssize_t
187 _qf_state_prefix(struct qf_vc *vc)
189 struct quoteflt *self = vc->self;
190 char const *buf = vc->buf;
191 size_t len = vc->len, i;
192 ssize_t rv = 0;
193 wchar_t wc;
197 for (;;) {
198 if (len < mb_cur_max) {
199 len = vc->len - len;
200 memcpy(self->dat.s + self->dat.l, vc->buf, len);
201 self->dat.l += len;
202 break;
205 i = mbrtowc(&wc, buf, len, &self->mbps);
206 if (i == (size_t)-1 || i == (size_t)-2)
209 buf += i;
210 len -= i;
212 if (iswspace(wc))
213 continue;
215 if (i == 1 && ISQUOTE(wc)) {
216 if (self->currq.l == QUOTE_MAX - 3) {
217 self->currq.s[QUOTE_MAX - 3] = '.';
218 self->currq.s[QUOTE_MAX - 2] = '.';
219 self->currq.s[QUOTE_MAX - 1] = '.';
220 } else
221 self->currq.s[self->currq.l++] = buf[-1];
222 continue;
225 self->qf_state = _QF_DATA;
226 break;
232 jleave:
234 return rv;
237 static ssize_t
238 _qf_state_data(struct qf_vc *vc)
244 return ;
246 #endif /* HAVE_QUOTE_FOLD */
248 struct quoteflt *
249 quoteflt_dummy(void) /* TODO LEGACY */
251 static struct quoteflt qf_i;
253 return &qf_i;
256 void
257 quoteflt_init(struct quoteflt *self, char const *prefix)
259 #ifdef HAVE_QUOTE_FOLD
260 char *xcp, *cp;
261 #endif
263 memset(self, 0, sizeof *self);
265 if ((self->qf_pfix = prefix) != NULL)
266 self->qf_pfix_len = (ui_it)strlen(prefix);
268 /* Check wether the user wants the more fancy quoting algorithm */
269 #ifdef HAVE_QUOTE_FOLD
270 if ((cp = voption("quote-fold")) != NULL) {
271 ui_it qmin, qmax = (ui_it)strtol(cp, (char**)&xcp, 10);
272 if (qmax < self->qf_pfix_len + 6)
273 qmax = self->qf_pfix_len + 6;
274 --qmax; /* The newline escape */
275 if (cp == xcp || *xcp == '\0')
276 qmin = (qmax >> 1) + (qmax >> 2) + (qmax >> 5);
277 else {
278 qmin = (ui_it)strtol(xcp + 1, NULL, 10);
279 if (qmin < qmax >> 1)
280 qmin = qmax >> 1;
281 else if (qmin > qmax - 2)
282 qmin = qmax - 2;
284 self->qf_qfold_min = qmin;
285 self->qf_qfold_max = qmax;
287 self->qf_dat.s = salloc((qmax + 2) * mb_cur_len);
288 self->qf_currq.s = salloc((QUOTE_MAX + 1) * mb_cur_len);
290 #endif
293 void
294 quoteflt_destroy(struct quoteflt *self)
296 #ifndef HAVE_QUOTE_FOLD
297 (void)self;
298 #else
299 void *p;
301 if ((p = self->qf_dat.s) != NULL)
302 free(p);
303 if ((p = self->qf_currq.s) != NULL)
304 free(p);
305 #endif
308 void
309 quoteflt_reset(struct quoteflt *self, FILE *f) /* XXX inline */
311 self->qf_os = f;
312 #ifdef HAVE_QUOTE_FOLD
313 self->qf_state = _QF_NONE;
314 self->qf_dat.l =
315 self->qf_currq.l = 0;
316 memset(self->qf_mbps, 0, sizeof self->qf_mbps);
317 #endif
320 ssize_t
321 quoteflt_push(struct quoteflt *self, char const *dat, size_t len)
323 /* (xxx Ideally the actual push() [and flush()] would be functions on their
324 * xxx own, via indirect vtbl call ..) */
325 ssize_t i, rv = 0;
327 if (len == 0)
328 goto jleave;
330 /* Simple bypass? XXX Finally, this filter simply should not be used, then */
331 if (self->qf_pfix_len == 0)
332 rv = fwrite(dat, sizeof *dat, len, self->qf_os);
333 /* The simple algorithm: place *indentprefix* at every BOL */
334 else
335 #ifdef HAVE_QUOTE_FOLD
336 if (self->qf_qfold_max == 0)
337 #endif
339 void *vp;
340 size_t ll;
341 bool_t pxok = (self->qf_qfold_min != 0);
343 for (;;) {
344 if (! pxok) {
345 i = fwrite(self->qf_pfix, sizeof *self->qf_pfix, self->qf_pfix_len,
346 self->qf_os);
347 if (i < 0)
348 goto jerr;
349 rv += i;
350 pxok = TRU1;
353 /* xxx Strictly speaking this is invalid, because only `/' and `.' are
354 * xxx mandated by POSIX.1-2008 as "invariant across all locales
355 * xxx supported"; though there is no charset known which uses this
356 * xxx control char as part of a multibyte character; note that S-nail
357 * XXX (and the Mail codebase as such) do not support EBCDIC */
358 if ((vp = memchr(dat, '\n', len)) == NULL)
359 ll = len;
360 else {
361 pxok = FAL0;
362 ll = (size_t)((char*)vp - dat) + 1;
365 i = fwrite(dat, sizeof *dat, ll, self->qf_os);
366 if (i < 0)
367 goto jerr;
368 rv += i;
369 if ((len -= ll) == 0)
370 break;
371 dat += ll;
374 self->qf_qfold_min = pxok;
376 /* More complicated, though still only line-per-line: *quote-fold*.
377 * - If .qf_currq.l is 0, then we are in a clean state. Reset .qf_mbps;
378 * note this means we assume that lines start with reset escape seq
379 * - Lookout for a newline
381 #ifdef HAVE_QUOTE_FOLD
382 else {
383 struct qf_vc vc;
385 vc.self = self;
386 vc.buf = dat;
387 vc.len = len;
389 while (vc.len > 0) {
390 switch (self->qf_state) {
391 case _QF_NONE:
392 case _QF_PREFIX:
393 i = _qf_state_prefix(&vc);
394 break;
395 case _QF_DATA:
396 i = _qf_state_data(&vc);
397 break;
400 if (i < 0)
401 goto jerr;
402 rv += i;
405 #endif /* HAVE_QUOTE_FOLD */
407 jleave:
408 return rv;
410 jerr:
411 rv = -1;
412 goto jleave;
415 ssize_t
416 quoteflt_flush(struct quoteflt *self)
418 ssize_t rv = 0;
419 (void)self;
421 #ifdef HAVE_QUOTE_FOLD
422 if (self->qf_dat.l > 0) {
423 rv = fwrite(self->qf_pfix, sizeof *self->qf_pfix, self->qf_pfix_len,
424 self->qf_os);
425 if (rv > 0) {
426 ssize_t j = fwrite(self->qf_dat.s, sizeof *self->qf_dat.s,
427 self->qf_dat.l, self->qf_os);
428 rv = (j < 0) ? j : rv + j;
430 self->qf_dat.l = 0;
432 #endif
433 return rv;
436 /* vim:set fenc=utf-8:s-it-mode */