NeoMutt  2022-04-29-247-gc6aae8
Teaching an old dog new tricks
DOXYGEN
quoted.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stddef.h>
31#include <stdbool.h>
32#include <stdint.h>
33#include "mutt/lib.h"
34#include "core/lib.h"
35#include "lib.h"
36
39
45{
46 for (int i = COLOR_QUOTES_MAX - 1; i >= 0; i--)
47 {
49 return i + 1;
50 }
51 return 0;
52}
53
58{
59 color_debug(LL_DEBUG5, "QuotedColors: clean up\n");
60 for (size_t i = 0; i < COLOR_QUOTES_MAX; i++)
61 {
63 }
65}
66
73{
74 if (NumQuotedColors == 0)
75 return NULL;
76 return &QuotedColors[q % NumQuotedColors];
77}
78
84{
85 return NumQuotedColors;
86}
87
99bool quoted_colors_parse_color(enum ColorId cid, uint32_t fg, uint32_t bg,
100 int attrs, int q_level, int *rc, struct Buffer *err)
101{
102 if (cid != MT_COLOR_QUOTED)
103 return false;
104
105 color_debug(LL_DEBUG5, "quoted %d\n", q_level);
106 if (q_level >= COLOR_QUOTES_MAX)
107 {
108 mutt_buffer_printf(err, _("Maximum quoting level is %d"), COLOR_QUOTES_MAX - 1);
109 return false;
110 }
111
112 if (q_level >= NumQuotedColors)
113 NumQuotedColors = q_level + 1;
114
115 struct AttrColor *ac = &QuotedColors[q_level];
116 const bool was_set = ((ac->attrs != 0) || ac->curses_color);
117 ac->attrs = attrs;
118
119 struct CursesColor *cc = curses_color_new(fg, bg);
121 ac->curses_color = cc;
122
123 if (!cc)
125
126 if (was_set)
127 quoted_color_dump(ac, q_level, "QuotedColors changed: ");
128 else
129 quoted_color_dump(ac, q_level, "QuotedColors new: ");
130
131 struct Buffer *buf = mutt_buffer_pool_get();
132 get_colorid_name(cid, buf);
133 color_debug(LL_DEBUG5, "NT_COLOR_SET: %s\n", buf->data);
135
136 if (q_level == 0)
137 {
138 // Copy the colour into the SimpleColors
139 struct AttrColor *ac_quoted = simple_color_get(MT_COLOR_QUOTED);
140 *ac_quoted = *ac;
141 ac_quoted->ref_count = 1;
142 if (ac_quoted->curses_color)
143 ac_quoted->curses_color->ref_count++;
144 }
145
146 struct EventColor ev_c = { cid, ac };
148
151
152 *rc = MUTT_CMD_SUCCESS;
153 return true;
154}
155
164 struct Buffer *err)
165{
166 color_debug(LL_DEBUG5, "unquoted %d\n", q_level);
167
168 struct AttrColor *ac = &QuotedColors[q_level];
170 quoted_color_dump(ac, q_level, "QuotedColors clear: ");
171
174
176
177 struct EventColor ev_c = { cid, ac };
179
180 return MUTT_CMD_SUCCESS;
181}
182
189static void qstyle_free(struct QuoteStyle **ptr)
190{
191 if (!ptr || !*ptr)
192 return;
193
194 struct QuoteStyle *qc = *ptr;
195
196 FREE(&qc->prefix);
197 FREE(ptr);
198}
199
206void qstyle_free_tree(struct QuoteStyle **quote_list)
207{
208 struct QuoteStyle *next = NULL;
209
210 while (*quote_list)
211 {
212 if ((*quote_list)->down)
213 qstyle_free_tree(&((*quote_list)->down));
214 next = (*quote_list)->next;
215 qstyle_free(quote_list);
216 *quote_list = next;
217 }
218}
219
224static struct QuoteStyle *qstyle_new(void)
225{
226 return mutt_mem_calloc(1, sizeof(struct QuoteStyle));
227}
228
236static void qstyle_insert(struct QuoteStyle *quote_list,
237 struct QuoteStyle *new_class, int index, int *q_level)
238{
239 struct QuoteStyle *q_list = quote_list;
240 new_class->quote_n = -1;
241
242 while (q_list)
243 {
244 if (q_list->quote_n >= index)
245 {
246 q_list->quote_n++;
247 q_list->attr_color = quoted_colors_get(q_list->quote_n);
248 }
249 if (q_list->down)
250 q_list = q_list->down;
251 else if (q_list->next)
252 q_list = q_list->next;
253 else
254 {
255 while (!q_list->next)
256 {
257 q_list = q_list->up;
258 if (!q_list)
259 break;
260 }
261 if (q_list)
262 q_list = q_list->next;
263 }
264 }
265
266 new_class->quote_n = index;
267 new_class->attr_color = quoted_colors_get(index);
268 (*q_level)++;
269}
270
280struct QuoteStyle *qstyle_classify(struct QuoteStyle **quote_list, const char *qptr,
281 size_t length, bool *force_redraw, int *q_level)
282{
283 struct QuoteStyle *q_list = *quote_list;
284 struct QuoteStyle *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
285 const char *tail_qptr = NULL;
286 size_t offset, tail_lng;
287 int index = -1;
288
289 /* classify quoting prefix */
290 while (q_list)
291 {
292 if (length <= q_list->prefix_len)
293 {
294 /* case 1: check the top level nodes */
295
296 if (mutt_strn_equal(qptr, q_list->prefix, length))
297 {
298 if (length == q_list->prefix_len)
299 return q_list; /* same prefix: return the current class */
300
301 /* found shorter prefix */
302 if (!tmp)
303 {
304 /* add a node above q_list */
305 tmp = qstyle_new();
306 tmp->prefix = mutt_strn_dup(qptr, length);
307 tmp->prefix_len = length;
308
309 /* replace q_list by tmp in the top level list */
310 if (q_list->next)
311 {
312 tmp->next = q_list->next;
313 q_list->next->prev = tmp;
314 }
315 if (q_list->prev)
316 {
317 tmp->prev = q_list->prev;
318 q_list->prev->next = tmp;
319 }
320
321 /* make q_list a child of tmp */
322 tmp->down = q_list;
323 q_list->up = tmp;
324
325 /* q_list has no siblings for now */
326 q_list->next = NULL;
327 q_list->prev = NULL;
328
329 /* update the root if necessary */
330 if (q_list == *quote_list)
331 *quote_list = tmp;
332
333 index = q_list->quote_n;
334
335 /* tmp should be the return class too */
336 qc = tmp;
337
338 /* next class to test; if tmp is a shorter prefix for another
339 * node, that node can only be in the top level list, so don't
340 * go down after this point */
341 q_list = tmp->next;
342 }
343 else
344 {
345 /* found another branch for which tmp is a shorter prefix */
346
347 /* save the next sibling for later */
348 save = q_list->next;
349
350 /* unlink q_list from the top level list */
351 if (q_list->next)
352 q_list->next->prev = q_list->prev;
353 if (q_list->prev)
354 q_list->prev->next = q_list->next;
355
356 /* at this point, we have a tmp->down; link q_list to it */
357 ptr = tmp->down;
358 /* sibling order is important here, q_list should be linked last */
359 while (ptr->next)
360 ptr = ptr->next;
361 ptr->next = q_list;
362 q_list->next = NULL;
363 q_list->prev = ptr;
364 q_list->up = tmp;
365
366 index = q_list->quote_n;
367
368 /* next class to test; as above, we shouldn't go down */
369 q_list = save;
370 }
371
372 /* we found a shorter prefix, so certain quotes have changed classes */
373 *force_redraw = true;
374 continue;
375 }
376 else
377 {
378 /* shorter, but not a substring of the current class: try next */
379 q_list = q_list->next;
380 continue;
381 }
382 }
383 else
384 {
385 /* case 2: try subclassing the current top level node */
386
387 /* tmp != NULL means we already found a shorter prefix at case 1 */
388 if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->prefix_len))
389 {
390 /* ok, it's a subclass somewhere on this branch */
391
392 ptr = q_list;
393 offset = q_list->prefix_len;
394
395 q_list = q_list->down;
396 tail_lng = length - offset;
397 tail_qptr = qptr + offset;
398
399 while (q_list)
400 {
401 if (length <= q_list->prefix_len)
402 {
403 if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
404 {
405 /* same prefix: return the current class */
406 if (length == q_list->prefix_len)
407 return q_list;
408
409 /* found shorter common prefix */
410 if (!tmp)
411 {
412 /* add a node above q_list */
413 tmp = qstyle_new();
414 tmp->prefix = mutt_strn_dup(qptr, length);
415 tmp->prefix_len = length;
416
417 /* replace q_list by tmp */
418 if (q_list->next)
419 {
420 tmp->next = q_list->next;
421 q_list->next->prev = tmp;
422 }
423 if (q_list->prev)
424 {
425 tmp->prev = q_list->prev;
426 q_list->prev->next = tmp;
427 }
428
429 /* make q_list a child of tmp */
430 tmp->down = q_list;
431 tmp->up = q_list->up;
432 q_list->up = tmp;
433 if (tmp->up->down == q_list)
434 tmp->up->down = tmp;
435
436 /* q_list has no siblings */
437 q_list->next = NULL;
438 q_list->prev = NULL;
439
440 index = q_list->quote_n;
441
442 /* tmp should be the return class too */
443 qc = tmp;
444
445 /* next class to test */
446 q_list = tmp->next;
447 }
448 else
449 {
450 /* found another branch for which tmp is a shorter prefix */
451
452 /* save the next sibling for later */
453 save = q_list->next;
454
455 /* unlink q_list from the top level list */
456 if (q_list->next)
457 q_list->next->prev = q_list->prev;
458 if (q_list->prev)
459 q_list->prev->next = q_list->next;
460
461 /* at this point, we have a tmp->down; link q_list to it */
462 ptr = tmp->down;
463 while (ptr->next)
464 ptr = ptr->next;
465 ptr->next = q_list;
466 q_list->next = NULL;
467 q_list->prev = ptr;
468 q_list->up = tmp;
469
470 index = q_list->quote_n;
471
472 /* next class to test */
473 q_list = save;
474 }
475
476 /* we found a shorter prefix, so we need a redraw */
477 *force_redraw = true;
478 continue;
479 }
480 else
481 {
482 q_list = q_list->next;
483 continue;
484 }
485 }
486 else
487 {
488 /* longer than the current prefix: try subclassing it */
489 if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
490 q_list->prefix_len - offset))
491 {
492 /* still a subclass: go down one level */
493 ptr = q_list;
494 offset = q_list->prefix_len;
495
496 q_list = q_list->down;
497 tail_lng = length - offset;
498 tail_qptr = qptr + offset;
499
500 continue;
501 }
502 else
503 {
504 /* nope, try the next prefix */
505 q_list = q_list->next;
506 continue;
507 }
508 }
509 }
510
511 /* still not found so far: add it as a sibling to the current node */
512 if (!qc)
513 {
514 tmp = qstyle_new();
515 tmp->prefix = mutt_strn_dup(qptr, length);
516 tmp->prefix_len = length;
517
518 if (ptr->down)
519 {
520 tmp->next = ptr->down;
521 ptr->down->prev = tmp;
522 }
523 ptr->down = tmp;
524 tmp->up = ptr;
525
526 tmp->quote_n = (*q_level)++;
527 tmp->attr_color = quoted_colors_get(tmp->quote_n);
528
529 return tmp;
530 }
531 else
532 {
533 if (index != -1)
534 qstyle_insert(*quote_list, tmp, index, q_level);
535
536 return qc;
537 }
538 }
539 else
540 {
541 /* nope, try the next prefix */
542 q_list = q_list->next;
543 continue;
544 }
545 }
546 }
547
548 if (!qc)
549 {
550 /* not found so far: add it as a top level class */
551 qc = qstyle_new();
552 qc->prefix = mutt_strn_dup(qptr, length);
553 qc->prefix_len = length;
554 qc->quote_n = (*q_level)++;
556
557 if (*quote_list)
558 {
559 if ((*quote_list)->next)
560 {
561 qc->next = (*quote_list)->next;
562 qc->next->prev = qc;
563 }
564 (*quote_list)->next = qc;
565 qc->prev = *quote_list;
566 }
567 else
568 {
569 *quote_list = qc;
570 }
571 }
572
573 if (index != -1)
574 qstyle_insert(*quote_list, tmp, index, q_level);
575
576 return qc;
577}
578
585static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
586{
587 if (!quote_list)
588 return;
589
590 if (num_qlevel > 0)
591 {
592 quote_list->attr_color = quoted_colors_get(*cur_qlevel);
593 *cur_qlevel = (*cur_qlevel + 1) % num_qlevel;
594 }
595 else
596 {
597 quote_list->attr_color = NULL;
598 }
599
600 qstyle_recurse(quote_list->down, num_qlevel, cur_qlevel);
601 qstyle_recurse(quote_list->next, num_qlevel, cur_qlevel);
602}
603
608void qstyle_recolour(struct QuoteStyle *quote_list)
609{
610 if (!quote_list)
611 return;
612
613 int num = quoted_colors_num_used();
614 int cur = 0;
615
617 qstyle_recurse(quote_list, num, &cur);
619}
void attr_color_clear(struct AttrColor *ac)
Free the contents of an AttrColor.
Definition: attr.c:44
bool attr_color_is_set(struct AttrColor *ac)
Is the object coloured?
Definition: attr.c:160
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:168
void get_colorid_name(unsigned int cid, struct Buffer *buf)
Get the name of a color id.
Definition: command.c:329
struct Notify * ColorsNotify
Notifications: ColorId, EventColor.
Definition: notify.c:34
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:74
ColorId
List of all colored objects.
Definition: color.h:35
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:58
CommandResult
Error codes for command_t parse functions.
Definition: command.h:34
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:37
Convenience wrapper for the core headers.
void curses_color_free(struct CursesColor **ptr)
Free a CursesColor.
Definition: curses.c:116
struct CursesColor * curses_color_new(int fg, int bg)
Create a new CursesColor.
Definition: curses.c:148
int color_debug(enum LogLevel level, const char *format,...)
Write to the log file.
Definition: debug.c:44
void quoted_color_list_dump(void)
Log all the Quoted colours.
Definition: debug.c:312
void quoted_color_dump(struct AttrColor *ac, int q_level, const char *prefix)
Log a Quoted colour.
Definition: debug.c:288
void curses_colors_dump(void)
Log all the Curses colours.
Definition: debug.c:267
@ LL_DEBUG5
Log at debug level 5.
Definition: logging.h:44
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:43
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:171
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:451
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:496
@ 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
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
enum CommandResult quoted_colors_parse_uncolor(enum ColorId cid, int q_level, struct Buffer *err)
Parse the 'uncolor quoted' command.
Definition: quoted.c:163
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:280
struct AttrColor * quoted_colors_get(int q)
Return the color of a quote, cycling through the used quotes.
Definition: quoted.c:72
int quoted_colors_num_used(void)
Return the number of used quotes.
Definition: quoted.c:83
int find_highest_used(void)
Find the highest-numbered quotedN in use.
Definition: quoted.c:44
int NumQuotedColors
Number of colours for quoted email text.
Definition: quoted.c:38
struct AttrColor QuotedColors[COLOR_QUOTES_MAX]
Array of colours for quoted email text.
Definition: quoted.c:37
static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
Update the quoting styles after colour changes.
Definition: quoted.c:585
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition: quoted.c:206
void qstyle_recolour(struct QuoteStyle *quote_list)
Recolour quotes after colour changes.
Definition: quoted.c:608
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:236
static struct QuoteStyle * qstyle_new(void)
Create a new QuoteStyle.
Definition: quoted.c:224
void quoted_colors_clear(void)
Reset the quoted-email colours.
Definition: quoted.c:57
static void qstyle_free(struct QuoteStyle **ptr)
Free a single QuoteStyle object.
Definition: quoted.c:189
bool quoted_colors_parse_color(enum ColorId cid, uint32_t fg, uint32_t bg, int attrs, int q_level, int *rc, struct Buffer *err)
Parse the 'color quoted' command.
Definition: quoted.c:99
#define COLOR_QUOTES_MAX
Ten colours, quoted0..quoted9 (quoted and quoted0 are equivalent)
Definition: quoted.h:37
Key value store.
A curses colour and its attributes.
Definition: attr.h:35
short ref_count
Number of users.
Definition: attr.h:38
int attrs
Text attributes, e.g. A_BOLD.
Definition: attr.h:37
struct CursesColor * curses_color
Underlying Curses colour.
Definition: attr.h:36
String manipulation buffer.
Definition: buffer.h:34
char * data
Pointer to data.
Definition: buffer.h:35
Colour in the ncurses palette.
Definition: curses2.h:38
uint32_t fg
Foreground colour.
Definition: curses2.h:41
uint32_t bg
Background colour.
Definition: curses2.h:42
short ref_count
Number of users.
Definition: curses2.h:44
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:68
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: quoted.h:70
struct QuoteStyle * next
Different quoting styles at the same level.
Definition: quoted.h:73
struct QuoteStyle * up
Definition: quoted.h:74
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:72
struct QuoteStyle * prev
Definition: quoted.h:73
char * prefix
Prefix string, e.g. "> ".
Definition: quoted.h:71
struct QuoteStyle * down
Parent (less quoted) and child (more quoted) levels.
Definition: quoted.h:74
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:69