NeoMutt  2024-10-02-37-gfa9146
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
quoted.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stdbool.h>
31#include <stddef.h>
32#include "mutt/lib.h"
33#include "core/lib.h"
34#include "quoted.h"
35#include "attr.h"
36#include "color.h"
37#include "command2.h"
38#include "curses2.h"
39#include "debug.h"
40#include "notify2.h"
41#include "simple2.h"
42
45
50{
51 for (size_t i = 0; i < COLOR_QUOTES_MAX; i++)
52 {
53 struct AttrColor *ac = &QuotedColors[i];
56 }
58}
59
64static int find_highest_used(void)
65{
66 for (int i = COLOR_QUOTES_MAX - 1; i >= 0; i--)
67 {
69 return i + 1;
70 }
71 return 0;
72}
73
78{
79 color_debug(LL_DEBUG5, "QuotedColors: clean up\n");
80 for (size_t i = 0; i < COLOR_QUOTES_MAX; i++)
81 {
83 }
85}
86
93{
94 if (NumQuotedColors == 0)
95 return NULL;
96 return &QuotedColors[q % NumQuotedColors];
97}
98
104{
105 return NumQuotedColors;
106}
107
117bool quoted_colors_parse_color(enum ColorId cid, struct AttrColor *ac_val,
118 int q_level, int *rc, struct Buffer *err)
119{
120 if (cid != MT_COLOR_QUOTED)
121 return false;
122
123 color_debug(LL_DEBUG5, "quoted %d\n", q_level);
124 if (q_level >= COLOR_QUOTES_MAX)
125 {
126 buf_printf(err, _("Maximum quoting level is %d"), COLOR_QUOTES_MAX - 1);
127 return false;
128 }
129
130 if (q_level >= NumQuotedColors)
131 NumQuotedColors = q_level + 1;
132
133 struct AttrColor *ac = &QuotedColors[q_level];
134
135 attr_color_overwrite(ac, ac_val);
136
137 struct CursesColor *cc = ac->curses_color;
138 if (!cc)
140
141 struct Buffer *buf = buf_pool_get();
142 get_colorid_name(cid, buf);
143 color_debug(LL_DEBUG5, "NT_COLOR_SET: %s\n", buf->data);
144 buf_pool_release(&buf);
145
146 if (q_level == 0)
147 {
148 // Copy the colour into the SimpleColors
149 struct AttrColor *ac_quoted = simple_color_get(MT_COLOR_QUOTED);
150 curses_color_free(&ac_quoted->curses_color);
151 *ac_quoted = *ac;
152 ac_quoted->ref_count = 1;
153 if (ac_quoted->curses_color)
154 {
155 ac_quoted->curses_color->ref_count++;
156 curses_color_dump(cc, "curses rc++");
157 }
158 }
159
160 struct EventColor ev_c = { cid, ac };
162
164
165 *rc = MUTT_CMD_SUCCESS;
166 return true;
167}
168
177 struct Buffer *err)
178{
179 color_debug(LL_DEBUG5, "unquoted %d\n", q_level);
180
181 struct AttrColor *ac = &QuotedColors[q_level];
183
185
186 struct EventColor ev_c = { cid, ac };
188
189 return MUTT_CMD_SUCCESS;
190}
191
198static void qstyle_free(struct QuoteStyle **ptr)
199{
200 if (!ptr || !*ptr)
201 return;
202
203 struct QuoteStyle *qc = *ptr;
204 FREE(&qc->prefix);
205
206 FREE(ptr);
207}
208
215void qstyle_free_tree(struct QuoteStyle **quote_list)
216{
217 struct QuoteStyle *next = NULL;
218
219 while (*quote_list)
220 {
221 if ((*quote_list)->down)
222 qstyle_free_tree(&((*quote_list)->down));
223 next = (*quote_list)->next;
224 qstyle_free(quote_list);
225 *quote_list = next;
226 }
227}
228
233static struct QuoteStyle *qstyle_new(void)
234{
235 return mutt_mem_calloc(1, sizeof(struct QuoteStyle));
236}
237
245static void qstyle_insert(struct QuoteStyle *quote_list,
246 struct QuoteStyle *new_class, int index, int *q_level)
247{
248 struct QuoteStyle *q_list = quote_list;
249 new_class->quote_n = -1;
250
251 while (q_list)
252 {
253 if (q_list->quote_n >= index)
254 {
255 q_list->quote_n++;
256 q_list->attr_color = quoted_colors_get(q_list->quote_n);
257 }
258 if (q_list->down)
259 {
260 q_list = q_list->down;
261 }
262 else if (q_list->next)
263 {
264 q_list = q_list->next;
265 }
266 else
267 {
268 while (!q_list->next)
269 {
270 q_list = q_list->up;
271 if (!q_list)
272 break;
273 }
274 if (q_list)
275 q_list = q_list->next;
276 }
277 }
278
279 new_class->quote_n = index;
280 new_class->attr_color = quoted_colors_get(index);
281 (*q_level)++;
282}
283
293struct QuoteStyle *qstyle_classify(struct QuoteStyle **quote_list, const char *qptr,
294 size_t length, bool *force_redraw, int *q_level)
295{
296 struct QuoteStyle *q_list = *quote_list;
297 struct QuoteStyle *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
298 const char *tail_qptr = NULL;
299 size_t offset, tail_lng;
300 int index = -1;
301
302 /* classify quoting prefix */
303 while (q_list)
304 {
305 if (length <= q_list->prefix_len)
306 {
307 /* case 1: check the top level nodes */
308
309 if (mutt_strn_equal(qptr, q_list->prefix, length))
310 {
311 if (length == q_list->prefix_len)
312 return q_list; /* same prefix: return the current class */
313
314 /* found shorter prefix */
315 if (tmp)
316 {
317 /* found another branch for which tmp is a shorter prefix */
318
319 /* save the next sibling for later */
320 save = q_list->next;
321
322 /* unlink q_list from the top level list */
323 if (q_list->next)
324 q_list->next->prev = q_list->prev;
325 if (q_list->prev)
326 q_list->prev->next = q_list->next;
327
328 /* at this point, we have a tmp->down; link q_list to it */
329 ptr = tmp->down;
330 /* sibling order is important here, q_list should be linked last */
331 while (ptr->next)
332 ptr = ptr->next;
333 ptr->next = q_list;
334 q_list->next = NULL;
335 q_list->prev = ptr;
336 q_list->up = tmp;
337
338 index = q_list->quote_n;
339
340 /* next class to test; as above, we shouldn't go down */
341 q_list = save;
342 }
343 else
344 {
345 /* add a node above q_list */
346 tmp = qstyle_new();
347 tmp->prefix = mutt_strn_dup(qptr, length);
348 tmp->prefix_len = length;
349
350 /* replace q_list by tmp in the top level list */
351 if (q_list->next)
352 {
353 tmp->next = q_list->next;
354 q_list->next->prev = tmp;
355 }
356 if (q_list->prev)
357 {
358 tmp->prev = q_list->prev;
359 q_list->prev->next = tmp;
360 }
361
362 /* make q_list a child of tmp */
363 tmp->down = q_list;
364 q_list->up = tmp;
365
366 /* q_list has no siblings for now */
367 q_list->next = NULL;
368 q_list->prev = NULL;
369
370 /* update the root if necessary */
371 if (q_list == *quote_list)
372 *quote_list = tmp;
373
374 index = q_list->quote_n;
375
376 /* tmp should be the return class too */
377 qc = tmp;
378
379 /* next class to test; if tmp is a shorter prefix for another
380 * node, that node can only be in the top level list, so don't
381 * go down after this point */
382 q_list = tmp->next;
383 }
384
385 /* we found a shorter prefix, so certain quotes have changed classes */
386 *force_redraw = true;
387 continue;
388 }
389 else
390 {
391 /* shorter, but not a substring of the current class: try next */
392 q_list = q_list->next;
393 continue;
394 }
395 }
396 else
397 {
398 /* case 2: try subclassing the current top level node */
399
400 /* tmp != NULL means we already found a shorter prefix at case 1 */
401 if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->prefix_len))
402 {
403 /* ok, it's a subclass somewhere on this branch */
404
405 ptr = q_list;
406 offset = q_list->prefix_len;
407
408 q_list = q_list->down;
409 tail_lng = length - offset;
410 tail_qptr = qptr + offset;
411
412 while (q_list)
413 {
414 if (length <= q_list->prefix_len)
415 {
416 if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
417 {
418 /* same prefix: return the current class */
419 if (length == q_list->prefix_len)
420 return q_list;
421
422 /* found shorter common prefix */
423 if (!tmp)
424 {
425 /* add a node above q_list */
426 tmp = qstyle_new();
427 tmp->prefix = mutt_strn_dup(qptr, length);
428 tmp->prefix_len = length;
429
430 /* replace q_list by tmp */
431 if (q_list->next)
432 {
433 tmp->next = q_list->next;
434 q_list->next->prev = tmp;
435 }
436 if (q_list->prev)
437 {
438 tmp->prev = q_list->prev;
439 q_list->prev->next = tmp;
440 }
441
442 /* make q_list a child of tmp */
443 tmp->down = q_list;
444 tmp->up = q_list->up;
445 q_list->up = tmp;
446 if (tmp->up->down == q_list)
447 tmp->up->down = tmp;
448
449 /* q_list has no siblings */
450 q_list->next = NULL;
451 q_list->prev = NULL;
452
453 index = q_list->quote_n;
454
455 /* tmp should be the return class too */
456 qc = tmp;
457
458 /* next class to test */
459 q_list = tmp->next;
460 }
461 else
462 {
463 /* found another branch for which tmp is a shorter prefix */
464
465 /* save the next sibling for later */
466 save = q_list->next;
467
468 /* unlink q_list from the top level list */
469 if (q_list->next)
470 q_list->next->prev = q_list->prev;
471 if (q_list->prev)
472 q_list->prev->next = q_list->next;
473
474 /* at this point, we have a tmp->down; link q_list to it */
475 ptr = tmp->down;
476 while (ptr->next)
477 ptr = ptr->next;
478 ptr->next = q_list;
479 q_list->next = NULL;
480 q_list->prev = ptr;
481 q_list->up = tmp;
482
483 index = q_list->quote_n;
484
485 /* next class to test */
486 q_list = save;
487 }
488
489 /* we found a shorter prefix, so we need a redraw */
490 *force_redraw = true;
491 continue;
492 }
493 else
494 {
495 q_list = q_list->next;
496 continue;
497 }
498 }
499 else
500 {
501 /* longer than the current prefix: try subclassing it */
502 if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
503 q_list->prefix_len - offset))
504 {
505 /* still a subclass: go down one level */
506 ptr = q_list;
507 offset = q_list->prefix_len;
508
509 q_list = q_list->down;
510 tail_lng = length - offset;
511 tail_qptr = qptr + offset;
512
513 continue;
514 }
515 else
516 {
517 /* nope, try the next prefix */
518 q_list = q_list->next;
519 continue;
520 }
521 }
522 }
523
524 /* still not found so far: add it as a sibling to the current node */
525 if (!qc)
526 {
527 tmp = qstyle_new();
528 tmp->prefix = mutt_strn_dup(qptr, length);
529 tmp->prefix_len = length;
530
531 if (ptr->down)
532 {
533 tmp->next = ptr->down;
534 ptr->down->prev = tmp;
535 }
536 ptr->down = tmp;
537 tmp->up = ptr;
538
539 tmp->quote_n = (*q_level)++;
540 tmp->attr_color = quoted_colors_get(tmp->quote_n);
541
542 return tmp;
543 }
544 else
545 {
546 if (index != -1)
547 qstyle_insert(*quote_list, tmp, index, q_level);
548
549 return qc;
550 }
551 }
552 else
553 {
554 /* nope, try the next prefix */
555 q_list = q_list->next;
556 continue;
557 }
558 }
559 }
560
561 if (!qc)
562 {
563 /* not found so far: add it as a top level class */
564 qc = qstyle_new();
565 qc->prefix = mutt_strn_dup(qptr, length);
566 qc->prefix_len = length;
567 qc->quote_n = (*q_level)++;
569
570 if (*quote_list)
571 {
572 if ((*quote_list)->next)
573 {
574 qc->next = (*quote_list)->next;
575 qc->next->prev = qc;
576 }
577 (*quote_list)->next = qc;
578 qc->prev = *quote_list;
579 }
580 else
581 {
582 *quote_list = qc;
583 }
584 }
585
586 if (index != -1)
587 qstyle_insert(*quote_list, tmp, index, q_level);
588
589 return qc;
590}
591
598static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
599{
600 if (!quote_list)
601 return;
602
603 if (num_qlevel > 0)
604 {
605 quote_list->attr_color = quoted_colors_get(*cur_qlevel);
606 *cur_qlevel = (*cur_qlevel + 1) % num_qlevel;
607 }
608 else
609 {
610 quote_list->attr_color = NULL;
611 }
612
613 qstyle_recurse(quote_list->down, num_qlevel, cur_qlevel);
614 qstyle_recurse(quote_list->next, num_qlevel, cur_qlevel);
615}
616
621void qstyle_recolor(struct QuoteStyle *quote_list)
622{
623 if (!quote_list)
624 return;
625
626 int num = quoted_colors_num_used();
627 int cur = 0;
628
629 qstyle_recurse(quote_list, num, &cur);
630}
void attr_color_overwrite(struct AttrColor *ac_old, struct AttrColor *ac_new)
Update an AttrColor in-place.
Definition: attr.c:395
void attr_color_clear(struct AttrColor *ac)
Free the contents of an AttrColor.
Definition: attr.c:48
bool attr_color_is_set(const struct AttrColor *ac)
Is the object coloured?
Definition: attr.c:179
Colour and attributes.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
void get_colorid_name(unsigned int cid, struct Buffer *buf)
Get the name of a color id.
Definition: command.c:127
struct Notify * ColorsNotify
Notifications: ColorId, EventColor.
Definition: notify.c:35
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:88
Color and attribute parsing.
#define COLOR_DEFAULT
Definition: color.h:100
ColorId
List of all colored objects.
Definition: color.h:40
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:63
Parse colour commands.
CommandResult
Error codes for command_t parse functions.
Definition: command.h:36
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:39
Convenience wrapper for the core headers.
Curses Colour.
void curses_color_free(struct CursesColor **ptr)
Free a CursesColor.
Definition: curses.c:120
void curses_color_dump(struct CursesColor *cc, const char *prefix)
Log one Curses colour.
Definition: debug.c:122
void curses_colors_dump(struct Buffer *buf)
Dump all the Curses colours.
Definition: debug.c:144
Colour Debugging.
static int color_debug(enum LogLevel level, const char *format,...)
Definition: debug.h:52
@ LL_DEBUG5
Log at debug level 5.
Definition: logging2.h:47
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
#define FREE(x)
Definition: memory.h:45
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:173
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:380
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:425
Colour notifications.
@ NT_COLOR_RESET
Color has been reset/removed.
Definition: notify2.h:42
@ NT_COLOR_SET
Color has been set.
Definition: notify2.h:41
@ NT_COLOR
Colour has changed, NotifyColor, EventColor.
Definition: notify_type.h:41
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
enum CommandResult quoted_colors_parse_uncolor(enum ColorId cid, int q_level, struct Buffer *err)
Parse the 'uncolor quoted' command.
Definition: quoted.c:176
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: quoted.c:293
struct AttrColor * quoted_colors_get(int q)
Return the color of a quote, cycling through the used quotes.
Definition: quoted.c:92
void qstyle_recolor(struct QuoteStyle *quote_list)
Recolour quotes after colour changes.
Definition: quoted.c:621
bool quoted_colors_parse_color(enum ColorId cid, struct AttrColor *ac_val, int q_level, int *rc, struct Buffer *err)
Parse the 'color quoted' command.
Definition: quoted.c:117
int quoted_colors_num_used(void)
Return the number of used quotes.
Definition: quoted.c:103
int NumQuotedColors
Number of colours for quoted email text.
Definition: quoted.c:44
struct AttrColor QuotedColors[COLOR_QUOTES_MAX]
Array of colours for quoted email text.
Definition: quoted.c:43
static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
Update the quoting styles after colour changes.
Definition: quoted.c:598
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition: quoted.c:215
void quoted_colors_init(void)
Initialise the Quoted colours.
Definition: quoted.c:49
static void qstyle_insert(struct QuoteStyle *quote_list, struct QuoteStyle *new_class, int index, int *q_level)
Insert a new quote colour class into a list.
Definition: quoted.c:245
static struct QuoteStyle * qstyle_new(void)
Create a new QuoteStyle.
Definition: quoted.c:233
void quoted_colors_cleanup(void)
Reset the quoted-email colours.
Definition: quoted.c:77
static void qstyle_free(struct QuoteStyle **ptr)
Free a single QuoteStyle object.
Definition: quoted.c:198
static int find_highest_used(void)
Find the highest-numbered quotedN in use.
Definition: quoted.c:64
Quoted-Email colours.
#define COLOR_QUOTES_MAX
Ten colours, quoted0..quoted9 (quoted and quoted0 are equivalent)
Definition: quoted.h:36
Simple colour.
A curses colour and its attributes.
Definition: attr.h:66
struct ColorElement bg
Background colour.
Definition: attr.h:68
struct ColorElement fg
Foreground colour.
Definition: attr.h:67
short ref_count
Number of users.
Definition: attr.h:71
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:70
String manipulation buffer.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:37
color_t color
Colour.
Definition: attr.h:57
Colour in the ncurses palette.
Definition: curses2.h:41
short ref_count
Number of users.
Definition: curses2.h:45
An Event that happened to a Colour.
Definition: notify2.h:53
enum ColorId cid
Colour ID that has changed.
Definition: notify2.h:54
Style of quoted text.
Definition: quoted.h:67
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: quoted.h:69
struct QuoteStyle * next
Different quoting styles at the same level.
Definition: quoted.h:72
struct QuoteStyle * up
Definition: quoted.h:73
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:71
struct QuoteStyle * prev
Definition: quoted.h:72
char * prefix
Prefix string, e.g. "> ".
Definition: quoted.h:70
struct QuoteStyle * down
Parent (less quoted) and child (more quoted) levels.
Definition: quoted.h:73
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:68