NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
enriched.c
Go to the documentation of this file.
1
33#include "config.h"
34#include <stddef.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <wchar.h>
38#include <wctype.h>
39#include "mutt/lib.h"
40#include "email/lib.h"
41#include "enriched.h"
42
43#define INDENT_SIZE 4
44
49{
63};
64
68struct Etags
69{
70 const wchar_t *tag_name;
71 int index;
72};
73
74static const struct Etags EnrichedTags[] = {
75 // clang-format off
76 { L"param", RICH_PARAM },
77 { L"bold", RICH_BOLD },
78 { L"italic", RICH_ITALIC },
79 { L"underline", RICH_UNDERLINE },
80 { L"nofill", RICH_NOFILL },
81 { L"excerpt", RICH_EXCERPT },
82 { L"indent", RICH_INDENT },
83 { L"indentright", RICH_INDENT_RIGHT },
84 { L"center", RICH_CENTER },
85 { L"flushleft", RICH_FLUSHLEFT },
86 { L"flushright", RICH_FLUSHRIGHT },
87 { L"flushboth", RICH_FLUSHLEFT },
88 { L"color", RICH_COLOR },
89 { L"x-color", RICH_COLOR },
90 { NULL, -1 },
91 // clang-format on
92};
93
98{
99 wchar_t *buffer;
100 wchar_t *line;
101 wchar_t *param;
102 size_t buf_len;
103 size_t line_len;
104 size_t line_used;
105 size_t line_max;
107 size_t word_len;
108 size_t buf_used;
110 size_t param_len;
113 struct State *s;
114};
115
120static void enriched_wrap(struct EnrichedState *stte)
121{
122 if (!stte)
123 return;
124
125 int x;
126
127 if (stte->line_len)
128 {
129 if (stte->tag_level[RICH_CENTER] || stte->tag_level[RICH_FLUSHRIGHT])
130 {
131 /* Strip trailing white space */
132 size_t y = stte->line_used - 1;
133
134 while (y && iswspace(stte->line[y]))
135 {
136 stte->line[y] = (wchar_t) '\0';
137 y--;
138 stte->line_used--;
139 stte->line_len--;
140 }
141 if (stte->tag_level[RICH_CENTER])
142 {
143 /* Strip leading whitespace */
144 y = 0;
145
146 while (stte->line[y] && iswspace(stte->line[y]))
147 y++;
148 if (y)
149 {
150 for (size_t z = y; z <= stte->line_used; z++)
151 {
152 stte->line[z - y] = stte->line[z];
153 }
154
155 stte->line_len -= y;
156 stte->line_used -= y;
157 }
158 }
159 }
160
161 const int extra = stte->wrap_margin - stte->line_len - stte->indent_len -
163 if (extra > 0)
164 {
165 if (stte->tag_level[RICH_CENTER])
166 {
167 x = extra / 2;
168 while (x)
169 {
170 state_putc(stte->s, ' ');
171 x--;
172 }
173 }
174 else if (stte->tag_level[RICH_FLUSHRIGHT])
175 {
176 x = extra - 1;
177 while (x)
178 {
179 state_putc(stte->s, ' ');
180 x--;
181 }
182 }
183 }
184 state_putws(stte->s, (const wchar_t *) stte->line);
185 }
186
187 state_putc(stte->s, '\n');
188 stte->line[0] = (wchar_t) '\0';
189 stte->line_len = 0;
190 stte->line_used = 0;
191 stte->indent_len = 0;
192 if (stte->s->prefix)
193 {
194 state_puts(stte->s, stte->s->prefix);
195 stte->indent_len += mutt_str_len(stte->s->prefix);
196 }
197
198 if (stte->tag_level[RICH_EXCERPT])
199 {
200 x = stte->tag_level[RICH_EXCERPT];
201 while (x)
202 {
203 if (stte->s->prefix)
204 {
205 state_puts(stte->s, stte->s->prefix);
206 stte->indent_len += mutt_str_len(stte->s->prefix);
207 }
208 else
209 {
210 state_puts(stte->s, "> ");
211 stte->indent_len += mutt_str_len("> ");
212 }
213 x--;
214 }
215 }
216 else
217 stte->indent_len = 0;
218 if (stte->tag_level[RICH_INDENT])
219 {
220 x = stte->tag_level[RICH_INDENT] * INDENT_SIZE;
221 stte->indent_len += x;
222 while (x)
223 {
224 state_putc(stte->s, ' ');
225 x--;
226 }
227 }
228}
229
235static void enriched_flush(struct EnrichedState *stte, bool wrap)
236{
237 if (!stte || !stte->buffer)
238 return;
239
240 if (!stte->tag_level[RICH_NOFILL] &&
241 ((stte->line_len + stte->word_len) >
242 (stte->wrap_margin - (stte->tag_level[RICH_INDENT_RIGHT] * INDENT_SIZE) - stte->indent_len)))
243 {
244 enriched_wrap(stte);
245 }
246
247 if (stte->buf_used)
248 {
249 stte->buffer[stte->buf_used] = (wchar_t) '\0';
250 stte->line_used += stte->buf_used;
251 if (stte->line_used > stte->line_max)
252 {
253 stte->line_max = stte->line_used;
254 mutt_mem_realloc(&stte->line, (stte->line_max + 1) * sizeof(wchar_t));
255 }
256 wcscat(stte->line, stte->buffer);
257 stte->line_len += stte->word_len;
258 stte->word_len = 0;
259 stte->buf_used = 0;
260 }
261 if (wrap)
262 enriched_wrap(stte);
263 fflush(stte->s->fp_out);
264}
265
271static void enriched_putwc(wchar_t c, struct EnrichedState *stte)
272{
273 if (!stte)
274 return;
275
276 if (stte->tag_level[RICH_PARAM])
277 {
278 if (stte->tag_level[RICH_COLOR])
279 {
280 if (stte->param_used + 1 >= stte->param_len)
281 mutt_mem_realloc(&stte->param, (stte->param_len += 256) * sizeof(wchar_t));
282
283 stte->param[stte->param_used++] = c;
284 }
285 return; /* nothing to do */
286 }
287
288 /* see if more space is needed (plus extra for possible rich characters) */
289 if ((stte->buf_len < (stte->buf_used + 3)) || !stte->buffer)
290 {
291 stte->buf_len += 1024;
292 mutt_mem_realloc(&stte->buffer, (stte->buf_len + 1) * sizeof(wchar_t));
293 }
294
295 if ((!stte->tag_level[RICH_NOFILL] && iswspace(c)) || (c == (wchar_t) '\0'))
296 {
297 if (c == (wchar_t) '\t')
298 stte->word_len += 8 - (stte->line_len + stte->word_len) % 8;
299 else
300 stte->word_len++;
301
302 stte->buffer[stte->buf_used++] = c;
303 enriched_flush(stte, false);
304 }
305 else
306 {
307 if (stte->s->flags & MUTT_DISPLAY)
308 {
309 if (stte->tag_level[RICH_BOLD])
310 {
311 stte->buffer[stte->buf_used++] = c;
312 stte->buffer[stte->buf_used++] = (wchar_t) '\010'; // Ctrl-H (backspace)
313 stte->buffer[stte->buf_used++] = c;
314 }
315 else if (stte->tag_level[RICH_UNDERLINE])
316 {
317 stte->buffer[stte->buf_used++] = '_';
318 stte->buffer[stte->buf_used++] = (wchar_t) '\010'; // Ctrl-H (backspace)
319 stte->buffer[stte->buf_used++] = c;
320 }
321 else if (stte->tag_level[RICH_ITALIC])
322 {
323 stte->buffer[stte->buf_used++] = c;
324 stte->buffer[stte->buf_used++] = (wchar_t) '\010'; // Ctrl-H (backspace)
325 stte->buffer[stte->buf_used++] = '_';
326 }
327 else
328 {
329 stte->buffer[stte->buf_used++] = c;
330 }
331 }
332 else
333 {
334 stte->buffer[stte->buf_used++] = c;
335 }
336 stte->word_len++;
337 }
338}
339
345static void enriched_puts(const char *s, struct EnrichedState *stte)
346{
347 if (!stte)
348 return;
349
350 const char *c = NULL;
351
352 if ((stte->buf_len < (stte->buf_used + mutt_str_len(s))) || !stte->buffer)
353 {
354 stte->buf_len += 1024;
355 mutt_mem_realloc(&stte->buffer, (stte->buf_len + 1) * sizeof(wchar_t));
356 }
357 c = s;
358 while (*c)
359 {
360 stte->buffer[stte->buf_used++] = (wchar_t) *c;
361 c++;
362 }
363}
364
370static void enriched_set_flags(const wchar_t *tag, struct EnrichedState *stte)
371{
372 if (!stte)
373 return;
374
375 const wchar_t *tagptr = tag;
376 int i, j;
377
378 if (*tagptr == (wchar_t) '/')
379 tagptr++;
380
381 for (i = 0, j = -1; EnrichedTags[i].tag_name; i++)
382 {
383 if (wcscasecmp(EnrichedTags[i].tag_name, tagptr) == 0)
384 {
385 j = EnrichedTags[i].index;
386 break;
387 }
388 }
389
390 if (j != -1)
391 {
392 if ((j == RICH_CENTER) || (j == RICH_FLUSHLEFT) || (j == RICH_FLUSHRIGHT))
393 enriched_flush(stte, true);
394
395 if (*tag == (wchar_t) '/')
396 {
397 if (stte->tag_level[j]) /* make sure not to go negative */
398 stte->tag_level[j]--;
399 if ((stte->s->flags & MUTT_DISPLAY) && (j == RICH_PARAM) && stte->tag_level[RICH_COLOR])
400 {
401 stte->param[stte->param_used] = (wchar_t) '\0';
402 if (wcscasecmp(L"black", stte->param) == 0)
403 {
404 enriched_puts("\033[30m", stte); // Escape
405 }
406 else if (wcscasecmp(L"red", stte->param) == 0)
407 {
408 enriched_puts("\033[31m", stte); // Escape
409 }
410 else if (wcscasecmp(L"green", stte->param) == 0)
411 {
412 enriched_puts("\033[32m", stte); // Escape
413 }
414 else if (wcscasecmp(L"yellow", stte->param) == 0)
415 {
416 enriched_puts("\033[33m", stte); // Escape
417 }
418 else if (wcscasecmp(L"blue", stte->param) == 0)
419 {
420 enriched_puts("\033[34m", stte); // Escape
421 }
422 else if (wcscasecmp(L"magenta", stte->param) == 0)
423 {
424 enriched_puts("\033[35m", stte); // Escape
425 }
426 else if (wcscasecmp(L"cyan", stte->param) == 0)
427 {
428 enriched_puts("\033[36m", stte); // Escape
429 }
430 else if (wcscasecmp(L"white", stte->param) == 0)
431 {
432 enriched_puts("\033[37m", stte); // Escape
433 }
434 }
435 if ((stte->s->flags & MUTT_DISPLAY) && (j == RICH_COLOR))
436 {
437 enriched_puts("\033[0m", stte); // Escape
438 }
439
440 /* flush parameter buffer when closing the tag */
441 if (j == RICH_PARAM)
442 {
443 stte->param_used = 0;
444 stte->param[0] = (wchar_t) '\0';
445 }
446 }
447 else
448 stte->tag_level[j]++;
449
450 if (j == RICH_EXCERPT)
451 enriched_flush(stte, true);
452 }
453}
454
459int text_enriched_handler(struct Body *a, struct State *s)
460{
461 enum
462 {
463 TEXT,
464 LANGLE,
465 TAG,
466 BOGUS_TAG,
467 NEWLINE,
468 ST_EOF,
469 DONE
470 } state = TEXT;
471
472 long bytes = a->length;
473 struct EnrichedState stte = { 0 };
474 wint_t wc = 0;
475 int tag_len = 0;
476 wchar_t tag[1024 + 1];
477
478 stte.s = s;
479 stte.wrap_margin = ((s->wraplen > 4) && ((s->flags & MUTT_DISPLAY) || (s->wraplen < 76))) ?
480 s->wraplen - 4 :
481 72;
482 stte.line_max = stte.wrap_margin * 4;
483 stte.line = mutt_mem_calloc((stte.line_max + 1), sizeof(wchar_t));
484 stte.param = mutt_mem_calloc(256, sizeof(wchar_t));
485
486 stte.param_len = 256;
487 stte.param_used = 0;
488
489 if (s->prefix)
490 {
491 state_puts(s, s->prefix);
493 }
494
495 while (state != DONE)
496 {
497 if (state != ST_EOF)
498 {
499 if (!bytes || ((wc = fgetwc(s->fp_in)) == WEOF))
500 state = ST_EOF;
501 else
502 bytes--;
503 }
504
505 switch (state)
506 {
507 case TEXT:
508 switch (wc)
509 {
510 case '<':
511 state = LANGLE;
512 break;
513
514 case '\n':
515 if (stte.tag_level[RICH_NOFILL])
516 {
517 enriched_flush(&stte, true);
518 }
519 else
520 {
521 enriched_putwc((wchar_t) ' ', &stte);
522 state = NEWLINE;
523 }
524 break;
525
526 default:
527 enriched_putwc(wc, &stte);
528 }
529 break;
530
531 case LANGLE:
532 if (wc == (wchar_t) '<')
533 {
534 enriched_putwc(wc, &stte);
535 state = TEXT;
536 break;
537 }
538 else
539 {
540 tag_len = 0;
541 state = TAG;
542 }
543 /* Yes, (it wasn't a <<, so this char is first in TAG) */
544 /* fallthrough */
545 case TAG:
546 if (wc == (wchar_t) '>')
547 {
548 tag[tag_len] = (wchar_t) '\0';
549 enriched_set_flags(tag, &stte);
550 state = TEXT;
551 }
552 else if (tag_len < 1024) /* ignore overly long tags */
553 tag[tag_len++] = wc;
554 else
555 state = BOGUS_TAG;
556 break;
557
558 case BOGUS_TAG:
559 if (wc == (wchar_t) '>')
560 state = TEXT;
561 break;
562
563 case NEWLINE:
564 if (wc == (wchar_t) '\n')
565 enriched_flush(&stte, true);
566 else
567 {
568 ungetwc(wc, s->fp_in);
569 bytes++;
570 state = TEXT;
571 }
572 break;
573
574 case ST_EOF:
575 enriched_putwc((wchar_t) '\0', &stte);
576 enriched_flush(&stte, true);
577 state = DONE;
578 break;
579
580 default:
581 /* not reached */
582 break;
583 }
584 }
585
586 state_putc(s, '\n'); /* add a final newline */
587
588 FREE(&(stte.buffer));
589 FREE(&(stte.line));
590 FREE(&(stte.param));
591
592 return 0;
593}
Structs that make up an email.
static void enriched_wrap(struct EnrichedState *stte)
Wrap enriched text.
Definition: enriched.c:120
RichAttribs
Rich text attributes.
Definition: enriched.c:49
@ RICH_FLUSHRIGHT
Right-justified text.
Definition: enriched.c:60
@ RICH_NOFILL
Text will not be reformatted.
Definition: enriched.c:54
@ RICH_COLOR
Coloured text.
Definition: enriched.c:61
@ RICH_PARAM
Parameter label.
Definition: enriched.c:50
@ RICH_INDENT
Indented text.
Definition: enriched.c:55
@ RICH_BOLD
Bold text.
Definition: enriched.c:51
@ RICH_CENTER
Centred text.
Definition: enriched.c:58
@ RICH_ITALIC
Italic text.
Definition: enriched.c:53
@ RICH_UNDERLINE
Underlined text.
Definition: enriched.c:52
@ RICH_EXCERPT
Excerpt text.
Definition: enriched.c:57
@ RICH_INDENT_RIGHT
Right-indented text.
Definition: enriched.c:56
@ RICH_MAX
Definition: enriched.c:62
@ RICH_FLUSHLEFT
Left-justified text.
Definition: enriched.c:59
static void enriched_flush(struct EnrichedState *stte, bool wrap)
Write enriched text to the State.
Definition: enriched.c:235
static void enriched_putwc(wchar_t c, struct EnrichedState *stte)
Write one wide character to the state.
Definition: enriched.c:271
static const struct Etags EnrichedTags[]
Definition: enriched.c:74
#define INDENT_SIZE
A (not so) minimal implementation of RFC1563.
Definition: enriched.c:43
static void enriched_puts(const char *s, struct EnrichedState *stte)
Write an enriched text string to the State.
Definition: enriched.c:345
static void enriched_set_flags(const wchar_t *tag, struct EnrichedState *stte)
Set flags on the enriched text state.
Definition: enriched.c:370
Rich text handler.
int text_enriched_handler(struct Body *a, struct State *s)
Handler for enriched text - Implements handler_t -.
Definition: enriched.c:459
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
int state_putws(struct State *s, const wchar_t *ws)
Write a wide string to the state.
Definition: state.c:143
#define state_puts(STATE, STR)
Definition: state.h:56
#define state_putc(STATE, STR)
Definition: state.h:57
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
int wcscasecmp(const wchar_t *a, const wchar_t *b)
Compare two wide-character strings, ignoring case.
Definition: wcscasecmp.c:41
The body of an email.
Definition: body.h:36
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
State of enriched-text parser.
Definition: enriched.c:98
wchar_t * buffer
Definition: enriched.c:99
size_t buf_len
Definition: enriched.c:102
size_t param_used
Definition: enriched.c:109
wchar_t * param
Definition: enriched.c:101
wchar_t * line
Definition: enriched.c:100
int tag_level[RICH_MAX]
Definition: enriched.c:111
size_t buf_used
Definition: enriched.c:108
struct State * s
Definition: enriched.c:113
size_t line_used
Definition: enriched.c:104
size_t word_len
Definition: enriched.c:107
size_t param_len
Definition: enriched.c:110
size_t line_len
Definition: enriched.c:103
size_t line_max
Definition: enriched.c:105
int wrap_margin
Definition: enriched.c:112
size_t indent_len
Definition: enriched.c:106
Enriched text tags.
Definition: enriched.c:69
int index
Index number.
Definition: enriched.c:71
const wchar_t * tag_name
Tag name.
Definition: enriched.c:70
Keep track when processing files.
Definition: state.h:46
int wraplen
Width to wrap lines to (when flags & MUTT_DISPLAY)
Definition: state.h:51
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:50
FILE * fp_out
File to write to.
Definition: state.h:48
char * prefix
String to add to the beginning of each output line.
Definition: state.h:49
FILE * fp_in
File to read from.
Definition: state.h:47