NeoMutt  2021-10-29-220-g2b1eec
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 "gui/lib.h"
36 #include "options.h"
37 
40 
45 {
46  memset(QuotedColors, A_NORMAL, COLOR_QUOTES_MAX * sizeof(int));
47  NumQuotedColors = 0;
48 }
49 
54 {
55  memset(QuotedColors, A_NORMAL, COLOR_QUOTES_MAX * sizeof(int));
56  NumQuotedColors = 0;
57 }
58 
65 {
66  const int used = NumQuotedColors;
67  if (used == 0)
68  return 0;
69  return QuotedColors[q % used];
70 }
71 
77 {
78  return NumQuotedColors;
79 }
80 
92 bool quoted_colors_parse_color(enum ColorId cid, uint32_t fg, uint32_t bg,
93  int attrs, int q_level, int *rc, struct Buffer *err)
94 {
95  if (cid != MT_COLOR_QUOTED)
96  return false;
97 
98  if (q_level >= COLOR_QUOTES_MAX)
99  {
100  mutt_buffer_printf(err, _("Maximum quoting level is %d"), COLOR_QUOTES_MAX - 1);
101  return MUTT_CMD_WARNING;
102  }
103 
104  if (q_level >= NumQuotedColors)
105  NumQuotedColors = q_level + 1;
106 
107  if (q_level == 0)
108  {
110 
112  for (q_level = 1; q_level < NumQuotedColors; q_level++)
113  {
114  if (QuotedColors[q_level] == A_NORMAL)
116  }
117  }
118  else
119  {
120  QuotedColors[q_level] = fgbgattr_to_color(fg, bg, attrs);
121  }
122  *rc = MUTT_CMD_SUCCESS;
123  return true;
124 }
125 
132 static void qstyle_free(struct QuoteStyle **ptr)
133 {
134  if (!ptr || !*ptr)
135  return;
136 
137  struct QuoteStyle *qc = *ptr;
138 
139  FREE(&qc->prefix);
140  FREE(ptr);
141 }
142 
149 void qstyle_free_tree(struct QuoteStyle **quote_list)
150 {
151  struct QuoteStyle *next = NULL;
152 
153  while (*quote_list)
154  {
155  if ((*quote_list)->down)
156  qstyle_free_tree(&((*quote_list)->down));
157  next = (*quote_list)->next;
158  qstyle_free(quote_list);
159  *quote_list = next;
160  }
161 }
162 
167 static struct QuoteStyle *qstyle_new(void)
168 {
169  return mutt_mem_calloc(1, sizeof(struct QuoteStyle));
170 }
171 
179 static void qstyle_insert(struct QuoteStyle *quote_list,
180  struct QuoteStyle *new_class, int index, int *q_level)
181 {
182  struct QuoteStyle *q_list = quote_list;
183  new_class->quote_n = -1;
184 
185  while (q_list)
186  {
187  if (q_list->quote_n >= index)
188  {
189  q_list->quote_n++;
190  q_list->color = quoted_colors_get(q_list->quote_n);
191  }
192  if (q_list->down)
193  q_list = q_list->down;
194  else if (q_list->next)
195  q_list = q_list->next;
196  else
197  {
198  while (!q_list->next)
199  {
200  q_list = q_list->up;
201  if (!q_list)
202  break;
203  }
204  if (q_list)
205  q_list = q_list->next;
206  }
207  }
208 
209  new_class->quote_n = index;
210  new_class->color = quoted_colors_get(index);
211  (*q_level)++;
212 }
213 
223 struct QuoteStyle *qstyle_classify(struct QuoteStyle **quote_list, const char *qptr,
224  size_t length, bool *force_redraw, int *q_level)
225 {
226  struct QuoteStyle *q_list = *quote_list;
227  struct QuoteStyle *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
228  const char *tail_qptr = NULL;
229  size_t offset, tail_lng;
230  int index = -1;
231 
232  if (quoted_colors_num_used() <= 1)
233  {
234  /* not much point in classifying quotes... */
235 
236  if (!*quote_list)
237  {
238  qc = qstyle_new();
239  qc->color = quoted_colors_get(0);
240  *quote_list = qc;
241  }
242  return *quote_list;
243  }
244 
245  /* classify quoting prefix */
246  while (q_list)
247  {
248  if (length <= q_list->prefix_len)
249  {
250  /* case 1: check the top level nodes */
251 
252  if (mutt_strn_equal(qptr, q_list->prefix, length))
253  {
254  if (length == q_list->prefix_len)
255  return q_list; /* same prefix: return the current class */
256 
257  /* found shorter prefix */
258  if (!tmp)
259  {
260  /* add a node above q_list */
261  tmp = qstyle_new();
262  tmp->prefix = mutt_strn_dup(qptr, length);
263  tmp->prefix_len = length;
264 
265  /* replace q_list by tmp in the top level list */
266  if (q_list->next)
267  {
268  tmp->next = q_list->next;
269  q_list->next->prev = tmp;
270  }
271  if (q_list->prev)
272  {
273  tmp->prev = q_list->prev;
274  q_list->prev->next = tmp;
275  }
276 
277  /* make q_list a child of tmp */
278  tmp->down = q_list;
279  q_list->up = tmp;
280 
281  /* q_list has no siblings for now */
282  q_list->next = NULL;
283  q_list->prev = NULL;
284 
285  /* update the root if necessary */
286  if (q_list == *quote_list)
287  *quote_list = tmp;
288 
289  index = q_list->quote_n;
290 
291  /* tmp should be the return class too */
292  qc = tmp;
293 
294  /* next class to test; if tmp is a shorter prefix for another
295  * node, that node can only be in the top level list, so don't
296  * go down after this point */
297  q_list = tmp->next;
298  }
299  else
300  {
301  /* found another branch for which tmp is a shorter prefix */
302 
303  /* save the next sibling for later */
304  save = q_list->next;
305 
306  /* unlink q_list from the top level list */
307  if (q_list->next)
308  q_list->next->prev = q_list->prev;
309  if (q_list->prev)
310  q_list->prev->next = q_list->next;
311 
312  /* at this point, we have a tmp->down; link q_list to it */
313  ptr = tmp->down;
314  /* sibling order is important here, q_list should be linked last */
315  while (ptr->next)
316  ptr = ptr->next;
317  ptr->next = q_list;
318  q_list->next = NULL;
319  q_list->prev = ptr;
320  q_list->up = tmp;
321 
322  index = q_list->quote_n;
323 
324  /* next class to test; as above, we shouldn't go down */
325  q_list = save;
326  }
327 
328  /* we found a shorter prefix, so certain quotes have changed classes */
329  *force_redraw = true;
330  continue;
331  }
332  else
333  {
334  /* shorter, but not a substring of the current class: try next */
335  q_list = q_list->next;
336  continue;
337  }
338  }
339  else
340  {
341  /* case 2: try subclassing the current top level node */
342 
343  /* tmp != NULL means we already found a shorter prefix at case 1 */
344  if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->prefix_len))
345  {
346  /* ok, it's a subclass somewhere on this branch */
347 
348  ptr = q_list;
349  offset = q_list->prefix_len;
350 
351  q_list = q_list->down;
352  tail_lng = length - offset;
353  tail_qptr = qptr + offset;
354 
355  while (q_list)
356  {
357  if (length <= q_list->prefix_len)
358  {
359  if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
360  {
361  /* same prefix: return the current class */
362  if (length == q_list->prefix_len)
363  return q_list;
364 
365  /* found shorter common prefix */
366  if (!tmp)
367  {
368  /* add a node above q_list */
369  tmp = qstyle_new();
370  tmp->prefix = mutt_strn_dup(qptr, length);
371  tmp->prefix_len = length;
372 
373  /* replace q_list by tmp */
374  if (q_list->next)
375  {
376  tmp->next = q_list->next;
377  q_list->next->prev = tmp;
378  }
379  if (q_list->prev)
380  {
381  tmp->prev = q_list->prev;
382  q_list->prev->next = tmp;
383  }
384 
385  /* make q_list a child of tmp */
386  tmp->down = q_list;
387  tmp->up = q_list->up;
388  q_list->up = tmp;
389  if (tmp->up->down == q_list)
390  tmp->up->down = tmp;
391 
392  /* q_list has no siblings */
393  q_list->next = NULL;
394  q_list->prev = NULL;
395 
396  index = q_list->quote_n;
397 
398  /* tmp should be the return class too */
399  qc = tmp;
400 
401  /* next class to test */
402  q_list = tmp->next;
403  }
404  else
405  {
406  /* found another branch for which tmp is a shorter prefix */
407 
408  /* save the next sibling for later */
409  save = q_list->next;
410 
411  /* unlink q_list from the top level list */
412  if (q_list->next)
413  q_list->next->prev = q_list->prev;
414  if (q_list->prev)
415  q_list->prev->next = q_list->next;
416 
417  /* at this point, we have a tmp->down; link q_list to it */
418  ptr = tmp->down;
419  while (ptr->next)
420  ptr = ptr->next;
421  ptr->next = q_list;
422  q_list->next = NULL;
423  q_list->prev = ptr;
424  q_list->up = tmp;
425 
426  index = q_list->quote_n;
427 
428  /* next class to test */
429  q_list = save;
430  }
431 
432  /* we found a shorter prefix, so we need a redraw */
433  *force_redraw = true;
434  continue;
435  }
436  else
437  {
438  q_list = q_list->next;
439  continue;
440  }
441  }
442  else
443  {
444  /* longer than the current prefix: try subclassing it */
445  if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
446  q_list->prefix_len - offset))
447  {
448  /* still a subclass: go down one level */
449  ptr = q_list;
450  offset = q_list->prefix_len;
451 
452  q_list = q_list->down;
453  tail_lng = length - offset;
454  tail_qptr = qptr + offset;
455 
456  continue;
457  }
458  else
459  {
460  /* nope, try the next prefix */
461  q_list = q_list->next;
462  continue;
463  }
464  }
465  }
466 
467  /* still not found so far: add it as a sibling to the current node */
468  if (!qc)
469  {
470  tmp = qstyle_new();
471  tmp->prefix = mutt_strn_dup(qptr, length);
472  tmp->prefix_len = length;
473 
474  if (ptr->down)
475  {
476  tmp->next = ptr->down;
477  ptr->down->prev = tmp;
478  }
479  ptr->down = tmp;
480  tmp->up = ptr;
481 
482  tmp->quote_n = (*q_level)++;
483  tmp->color = quoted_colors_get(tmp->quote_n);
484 
485  return tmp;
486  }
487  else
488  {
489  if (index != -1)
490  qstyle_insert(*quote_list, tmp, index, q_level);
491 
492  return qc;
493  }
494  }
495  else
496  {
497  /* nope, try the next prefix */
498  q_list = q_list->next;
499  continue;
500  }
501  }
502  }
503 
504  if (!qc)
505  {
506  /* not found so far: add it as a top level class */
507  qc = qstyle_new();
508  qc->prefix = mutt_strn_dup(qptr, length);
509  qc->prefix_len = length;
510  qc->quote_n = (*q_level)++;
511  qc->color = quoted_colors_get(qc->quote_n);
512 
513  if (*quote_list)
514  {
515  qc->next = *quote_list;
516  (*quote_list)->prev = qc;
517  }
518  *quote_list = qc;
519  }
520 
521  if (index != -1)
522  qstyle_insert(*quote_list, tmp, index, q_level);
523 
524  return qc;
525 }
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int fgbgattr_to_color(int fg, int bg, int attrs)
Convert a foreground, background, attribute triplet into a colour.
Definition: command.c:556
int SimpleColors[MT_COLOR_MAX]
Array of all fixed colours, see enum ColorId.
Definition: simple.c:36
ColorId
List of all colored objects.
Definition: color.h:35
@ MT_COLOR_QUOTED
Pager: quoted text.
Definition: color.h:57
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition: command.h:37
@ MUTT_CMD_WARNING
Warning: Help given to the user.
Definition: command.h:36
Convenience wrapper for the core headers.
Convenience wrapper for the gui headers.
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:40
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:359
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:404
Handling of global boolean variables.
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:223
int quoted_colors_num_used(void)
Return the number of used quotes.
Definition: quoted.c:76
int NumQuotedColors
Number of colours for quoted email text.
Definition: quoted.c:39
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition: quoted.c:149
void quoted_colors_init(void)
Initialise the quoted-email colours.
Definition: quoted.c:53
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:179
int QuotedColors[COLOR_QUOTES_MAX]
Array of colours for quoted email text.
Definition: quoted.c:38
int quoted_colors_get(int q)
Return the color of a quote, cycling through the used quotes.
Definition: quoted.c:64
void quoted_colors_clear(void)
Reset the quoted-email colours.
Definition: quoted.c:44
static struct QuoteStyle * qstyle_new(void)
Create a new QuoteStyle.
Definition: quoted.c:167
static void qstyle_free(struct QuoteStyle **ptr)
Free a single QuoteStyle object.
Definition: quoted.c:132
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:92
#define COLOR_QUOTES_MAX
Ten colours, quoted0..quoted9 (quoted and quoted0 are equivalent)
Definition: quoted.h:35
String manipulation buffer.
Definition: buffer.h:34
Style of quoted text.
Definition: quoted.h:66
struct QuoteStyle * next
Different quoting styles at the same level.
Definition: quoted.h:71
struct QuoteStyle * up
Definition: quoted.h:72
size_t prefix_len
Length of the prefix string.
Definition: quoted.h:70
struct QuoteStyle * prev
Definition: quoted.h:71
char * prefix
Prefix string, e.g. "> ".
Definition: quoted.h:69
int color
Curses colour pair.
Definition: quoted.h:68
struct QuoteStyle * down
Parent (less quoted) and child (more quoted) levels.
Definition: quoted.h:72
int quote_n
The quoteN colour index for this level.
Definition: quoted.h:67