NeoMutt  2025-12-11-1009-ga75d9e
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
qstyle.c
Go to the documentation of this file.
1
22
28
29#include "config.h"
30#include <stdbool.h>
31#include "mutt/lib.h"
32#include "qstyle.h"
33#include "quoted.h"
34
41static void qstyle_free(struct QuoteStyle **ptr)
42{
43 if (!ptr || !*ptr)
44 return;
45
46 struct QuoteStyle *qc = *ptr;
47 FREE(&qc->prefix);
48
49 FREE(ptr);
50}
51
58void qstyle_free_tree(struct QuoteStyle **quote_list)
59{
60 struct QuoteStyle *next = NULL;
61
62 while (*quote_list)
63 {
64 if ((*quote_list)->down)
65 qstyle_free_tree(&((*quote_list)->down));
66 next = (*quote_list)->next;
67 qstyle_free(quote_list);
68 *quote_list = next;
69 }
70}
71
76static struct QuoteStyle *qstyle_new(void)
77{
78 return MUTT_MEM_CALLOC(1, struct QuoteStyle);
79}
80
88static void qstyle_insert(struct QuoteStyle *quote_list,
89 struct QuoteStyle *new_class, int index, int *q_level)
90{
91 struct QuoteStyle *q_list = quote_list;
92 new_class->quote_n = -1;
93
94 while (q_list)
95 {
96 if (q_list->quote_n >= index)
97 {
98 q_list->quote_n++;
99 q_list->attr_color = quoted_colors_get(q_list->quote_n);
100 }
101 if (q_list->down)
102 {
103 q_list = q_list->down;
104 }
105 else if (q_list->next)
106 {
107 q_list = q_list->next;
108 }
109 else
110 {
111 while (!q_list->next)
112 {
113 q_list = q_list->up;
114 if (!q_list)
115 break;
116 }
117 if (q_list)
118 q_list = q_list->next;
119 }
120 }
121
122 new_class->quote_n = index;
123 new_class->attr_color = quoted_colors_get(index);
124 (*q_level)++;
125}
126
136struct QuoteStyle *qstyle_classify(struct QuoteStyle **quote_list, const char *qptr,
137 size_t length, bool *force_redraw, int *q_level)
138{
139 struct QuoteStyle *q_list = *quote_list;
140 struct QuoteStyle *qc = NULL;
141 struct QuoteStyle *tmp = NULL;
142 struct QuoteStyle *ptr = NULL;
143 struct QuoteStyle *save = NULL;
144 const char *tail_qptr = NULL;
145 size_t offset;
146 size_t tail_lng;
147 int index = -1;
148
149 /* classify quoting prefix */
150 while (q_list)
151 {
152 if (length <= q_list->prefix_len)
153 {
154 /* case 1: check the top level nodes */
155
156 if (mutt_strn_equal(qptr, q_list->prefix, length))
157 {
158 if (length == q_list->prefix_len)
159 return q_list; /* same prefix: return the current class */
160
161 /* found shorter prefix */
162 if (tmp)
163 {
164 /* found another branch for which tmp is a shorter prefix */
165
166 /* save the next sibling for later */
167 save = q_list->next;
168
169 /* unlink q_list from the top level list */
170 if (q_list->next)
171 q_list->next->prev = q_list->prev;
172 if (q_list->prev)
173 q_list->prev->next = q_list->next;
174
175 /* at this point, we have a tmp->down; link q_list to it */
176 ptr = tmp->down;
177 /* sibling order is important here, q_list should be linked last */
178 while (ptr->next)
179 ptr = ptr->next;
180 ptr->next = q_list;
181 q_list->next = NULL;
182 q_list->prev = ptr;
183 q_list->up = tmp;
184
185 index = q_list->quote_n;
186
187 /* next class to test; as above, we shouldn't go down */
188 q_list = save;
189 }
190 else
191 {
192 /* add a node above q_list */
193 tmp = qstyle_new();
194 tmp->prefix = mutt_strn_dup(qptr, length);
195 tmp->prefix_len = length;
196
197 /* replace q_list by tmp in the top level list */
198 if (q_list->next)
199 {
200 tmp->next = q_list->next;
201 q_list->next->prev = tmp;
202 }
203 if (q_list->prev)
204 {
205 tmp->prev = q_list->prev;
206 q_list->prev->next = tmp;
207 }
208
209 /* make q_list a child of tmp */
210 tmp->down = q_list;
211 q_list->up = tmp;
212
213 /* q_list has no siblings for now */
214 q_list->next = NULL;
215 q_list->prev = NULL;
216
217 /* update the root if necessary */
218 if (q_list == *quote_list)
219 *quote_list = tmp;
220
221 index = q_list->quote_n;
222
223 /* tmp should be the return class too */
224 qc = tmp;
225
226 /* next class to test; if tmp is a shorter prefix for another
227 * node, that node can only be in the top level list, so don't
228 * go down after this point */
229 q_list = tmp->next;
230 }
231
232 /* we found a shorter prefix, so certain quotes have changed classes */
233 *force_redraw = true;
234 continue;
235 }
236 else
237 {
238 /* shorter, but not a substring of the current class: try next */
239 q_list = q_list->next;
240 continue;
241 }
242 }
243 else
244 {
245 /* case 2: try subclassing the current top level node */
246
247 /* tmp != NULL means we already found a shorter prefix at case 1 */
248 if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->prefix_len))
249 {
250 /* ok, it's a subclass somewhere on this branch */
251
252 ptr = q_list;
253 offset = q_list->prefix_len;
254
255 q_list = q_list->down;
256 tail_lng = length - offset;
257 tail_qptr = qptr + offset;
258
259 while (q_list)
260 {
261 if (length <= q_list->prefix_len)
262 {
263 if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
264 {
265 /* same prefix: return the current class */
266 if (length == q_list->prefix_len)
267 return q_list;
268
269 /* found shorter common prefix */
270 if (!tmp)
271 {
272 /* add a node above q_list */
273 tmp = qstyle_new();
274 tmp->prefix = mutt_strn_dup(qptr, length);
275 tmp->prefix_len = length;
276
277 /* replace q_list by tmp */
278 if (q_list->next)
279 {
280 tmp->next = q_list->next;
281 q_list->next->prev = tmp;
282 }
283 if (q_list->prev)
284 {
285 tmp->prev = q_list->prev;
286 q_list->prev->next = tmp;
287 }
288
289 /* make q_list a child of tmp */
290 tmp->down = q_list;
291 tmp->up = q_list->up;
292 q_list->up = tmp;
293 if (tmp->up->down == q_list)
294 tmp->up->down = tmp;
295
296 /* q_list has no siblings */
297 q_list->next = NULL;
298 q_list->prev = NULL;
299
300 index = q_list->quote_n;
301
302 /* tmp should be the return class too */
303 qc = tmp;
304
305 /* next class to test */
306 q_list = tmp->next;
307 }
308 else
309 {
310 /* found another branch for which tmp is a shorter prefix */
311
312 /* save the next sibling for later */
313 save = q_list->next;
314
315 /* unlink q_list from the top level list */
316 if (q_list->next)
317 q_list->next->prev = q_list->prev;
318 if (q_list->prev)
319 q_list->prev->next = q_list->next;
320
321 /* at this point, we have a tmp->down; link q_list to it */
322 ptr = tmp->down;
323 while (ptr->next)
324 ptr = ptr->next;
325 ptr->next = q_list;
326 q_list->next = NULL;
327 q_list->prev = ptr;
328 q_list->up = tmp;
329
330 index = q_list->quote_n;
331
332 /* next class to test */
333 q_list = save;
334 }
335
336 /* we found a shorter prefix, so we need a redraw */
337 *force_redraw = true;
338 continue;
339 }
340 else
341 {
342 q_list = q_list->next;
343 continue;
344 }
345 }
346 else
347 {
348 /* longer than the current prefix: try subclassing it */
349 if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
350 q_list->prefix_len - offset))
351 {
352 /* still a subclass: go down one level */
353 ptr = q_list;
354 offset = q_list->prefix_len;
355
356 q_list = q_list->down;
357 tail_lng = length - offset;
358 tail_qptr = qptr + offset;
359
360 continue;
361 }
362 else
363 {
364 /* nope, try the next prefix */
365 q_list = q_list->next;
366 continue;
367 }
368 }
369 }
370
371 /* still not found so far: add it as a sibling to the current node */
372 if (!qc)
373 {
374 tmp = qstyle_new();
375 tmp->prefix = mutt_strn_dup(qptr, length);
376 tmp->prefix_len = length;
377
378 if (ptr->down)
379 {
380 tmp->next = ptr->down;
381 ptr->down->prev = tmp;
382 }
383 ptr->down = tmp;
384 tmp->up = ptr;
385
386 tmp->quote_n = (*q_level)++;
388
389 return tmp;
390 }
391 else
392 {
393 if (index != -1)
394 qstyle_insert(*quote_list, tmp, index, q_level);
395
396 return qc;
397 }
398 }
399 else
400 {
401 /* nope, try the next prefix */
402 q_list = q_list->next;
403 continue;
404 }
405 }
406 }
407
408 if (!qc)
409 {
410 /* not found so far: add it as a top level class */
411 qc = qstyle_new();
412 qc->prefix = mutt_strn_dup(qptr, length);
413 qc->prefix_len = length;
414 qc->quote_n = (*q_level)++;
416
417 if (*quote_list)
418 {
419 if ((*quote_list)->next)
420 {
421 qc->next = (*quote_list)->next;
422 qc->next->prev = qc;
423 }
424 (*quote_list)->next = qc;
425 qc->prev = *quote_list;
426 }
427 else
428 {
429 *quote_list = qc;
430 }
431 }
432
433 if (index != -1)
434 qstyle_insert(*quote_list, tmp, index, q_level);
435
436 return qc;
437}
438
445static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
446{
447 if (!quote_list)
448 return;
449
450 if (num_qlevel > 0)
451 {
452 quote_list->attr_color = quoted_colors_get(*cur_qlevel);
453 *cur_qlevel = (*cur_qlevel + 1) % num_qlevel;
454 }
455 else
456 {
457 quote_list->attr_color = NULL;
458 }
459
460 qstyle_recurse(quote_list->down, num_qlevel, cur_qlevel);
461 qstyle_recurse(quote_list->next, num_qlevel, cur_qlevel);
462}
463
468void qstyle_recolor(struct QuoteStyle *quote_list)
469{
470 if (!quote_list)
471 return;
472
473 int num = quoted_colors_num_used();
474 int cur = 0;
475
476 qstyle_recurse(quote_list, num, &cur);
477}
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition string.c:384
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:429
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 qstyle.c:136
void qstyle_recolor(struct QuoteStyle *quote_list)
Recolour quotes after colour changes.
Definition qstyle.c:468
static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
Update the quoting styles after colour changes.
Definition qstyle.c:445
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition qstyle.c:58
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 qstyle.c:88
static struct QuoteStyle * qstyle_new(void)
Create a new QuoteStyle.
Definition qstyle.c:76
static void qstyle_free(struct QuoteStyle **ptr)
Free a single QuoteStyle object.
Definition qstyle.c:41
Quoted style.
struct AttrColor * quoted_colors_get(int q)
Return the color of a quote, cycling through the used quotes.
Definition quoted.c:102
int quoted_colors_num_used(void)
Return the number of used quotes.
Definition quoted.c:118
Quoted-Email colours.
Style of quoted text.
Definition qstyle.h:55
struct AttrColor * attr_color
Colour and attribute of the text.
Definition qstyle.h:57
struct QuoteStyle * next
Different quoting styles at the same level.
Definition qstyle.h:60
struct QuoteStyle * up
Definition qstyle.h:61
size_t prefix_len
Length of the prefix string.
Definition qstyle.h:59
struct QuoteStyle * prev
Definition qstyle.h:60
char * prefix
Prefix string, e.g. "> ".
Definition qstyle.h:58
struct QuoteStyle * down
Parent (less quoted) and child (more quoted) levels.
Definition qstyle.h:61
int quote_n
The quoteN colour index for this level.
Definition qstyle.h:56