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