2 * Copyright notice from original mutt:
3 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
5 * This file is part of mutt-ng, see http://www.muttng.org/.
6 * It's licensed under the GNU General Public License,
7 * please see the file GPL in the top level source directory.
10 #include <lib-ui/lib-ui.h>
17 int ColorDefs[MT_COLOR_MAX];
18 COLOR_LINE *ColorHdrList = NULL;
19 COLOR_LINE *ColorBodyList = NULL;
20 COLOR_LINE *ColorIndexList = NULL;
22 /* local to this file */
23 static int ColorQuoteSize;
25 #define COLOR_DEFAULT (-2)
27 typedef struct color_list {
32 struct color_list *next;
35 static COLOR_LIST *ColorList = NULL;
36 static int UserColors = 0;
38 static struct mapping_t Colors[] = {
39 {"black", COLOR_BLACK},
42 {"green", COLOR_GREEN},
43 {"magenta", COLOR_MAGENTA},
45 {"white", COLOR_WHITE},
46 {"yellow", COLOR_YELLOW},
47 {"default", COLOR_DEFAULT},
51 static struct mapping_t Fields[] = {
52 {"hdrdefault", MT_COLOR_HDEFAULT},
53 {"quoted", MT_COLOR_QUOTED},
54 {"signature", MT_COLOR_SIGNATURE},
55 {"indicator", MT_COLOR_INDICATOR},
56 {"status", MT_COLOR_STATUS},
57 {"tree", MT_COLOR_TREE},
58 {"error", MT_COLOR_ERROR},
59 {"normal", MT_COLOR_NORMAL},
60 {"tilde", MT_COLOR_TILDE},
61 {"markers", MT_COLOR_MARKERS},
62 {"header", MT_COLOR_HEADER},
63 {"body", MT_COLOR_BODY},
64 {"message", MT_COLOR_MESSAGE},
65 {"attachment", MT_COLOR_ATTACHMENT},
66 {"search", MT_COLOR_SEARCH},
67 {"bold", MT_COLOR_BOLD},
68 {"underline", MT_COLOR_UNDERLINE},
69 {"index", MT_COLOR_INDEX},
70 {"sidebar_new", MT_COLOR_NEW},
71 {"sidebar", MT_COLOR_SIDEBAR},
72 {"sidebar_flagged", MT_COLOR_FLAGGED},
76 #define COLOR_QUOTE_INIT 8
78 static COLOR_LINE *mutt_new_color_line (void)
80 COLOR_LINE *p = p_new(COLOR_LINE, 1);
87 static void mutt_free_color_line (COLOR_LINE ** l, int free_colors)
96 if (free_colors && tmp->fg != -1 && tmp->bg != -1)
97 mutt_free_color (tmp->fg, tmp->bg);
99 /* we should really use the container type for regular expressions. */
102 pattern_list_wipe(&tmp->color_pattern);
103 p_delete(&tmp->pattern);
107 void ci_start_color (void)
109 memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX);
110 ColorQuote = p_new(int, COLOR_QUOTE_INIT);
111 memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT);
112 ColorQuoteSize = COLOR_QUOTE_INIT;
115 /* set some defaults */
116 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
117 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
118 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
119 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
120 /* special meaning: toggle the relevant attribute */
121 ColorDefs[MT_COLOR_BOLD] = 0;
122 ColorDefs[MT_COLOR_UNDERLINE] = 0;
127 int mutt_alloc_color (int fg, int bg)
129 COLOR_LIST *p = ColorList;
132 /* check to see if this color is already allocated to save space */
134 if (p->fg == fg && p->bg == bg) {
136 return (COLOR_PAIR (p->index));
141 /* check to see if there are colors left */
142 if (++UserColors > COLOR_PAIRS)
145 /* find the smallest available index (object) */
159 p = p_new(COLOR_LIST, 1);
168 if (fg == COLOR_DEFAULT)
170 if (bg == COLOR_DEFAULT)
173 init_pair (i, fg, bg);
175 return (COLOR_PAIR (p->index));
178 void mutt_free_color (int fg, int bg)
184 if (p->fg == fg && p->bg == bg) {
191 if (p == ColorList) {
192 ColorList = ColorList->next;
212 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
216 if (m_strncasecmp(s, "bright", 6) == 0) {
221 /* allow aliases for xterm color resources */
222 if (m_strncasecmp(s, "color", 5) == 0) {
224 *col = strtol (s, &eptr, 10);
225 if (!*s || *eptr || *col < 0 ||
226 (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
227 snprintf (err->data, err->dsize, _("%s: color not supported by term"),
232 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
233 snprintf (err->data, err->dsize, _("%s: no such color"), s);
240 /* usage: uncolor index pattern [pattern...]
241 * unmono index pattern [pattern...]
245 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
246 BUFFER * err, short parse_uncolor);
248 int mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
251 return _mutt_parse_uncolor (buf, s, data, err, 1);
254 int mutt_parse_unmono (BUFFER * buf, BUFFER * s, unsigned long data,
257 return _mutt_parse_uncolor (buf, s, data, err, 0);
261 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
262 BUFFER * err, short parse_uncolor)
264 int object = 0, do_cache = 0;
265 COLOR_LINE *tmp, *last = NULL;
267 mutt_extract_token (buf, s, 0);
269 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
270 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
274 if (m_strncmp(buf->data, "index", 5) != 0) {
275 snprintf (err->data, err->dsize,
276 _("%s: command valid only for index object"),
277 parse_uncolor ? "uncolor" : "unmono");
282 snprintf (err->data, err->dsize,
283 _("%s: too few arguments"),
284 parse_uncolor ? "uncolor" : "unmono");
288 if (option (OPTNOCURSES) || (parse_uncolor != has_colors())) {
289 /* just eat the command, but don't do anything real about it */
291 mutt_extract_token (buf, s, 0);
292 } while (MoreArgs(s));
297 mutt_extract_token (buf, s, 0);
298 if (!m_strcmp("*", buf->data)) {
299 for (tmp = ColorIndexList; tmp;) {
304 mutt_free_color_line (&last, parse_uncolor);
306 ColorIndexList = NULL;
309 for (last = NULL, tmp = ColorIndexList; tmp;
310 last = tmp, tmp = tmp->next) {
311 if (!m_strcmp(buf->data, tmp->pattern)) {
315 last->next = tmp->next;
317 ColorIndexList = tmp->next;
318 mutt_free_color_line (&tmp, parse_uncolor);
324 while (MoreArgs (s));
327 if (do_cache && !option (OPTNOCURSES)) {
330 set_option (OPTFORCEREDRAWINDEX);
331 /* force re-caching of index colors */
332 for (i = 0; Context && i < Context->msgcount; i++)
333 Context->hdrs[i]->pair = 0;
340 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
341 int fg, int bg, int attr, BUFFER * err, int is_index)
344 /* is_index used to store compiled pattern
345 * only for `index' color object
346 * when called from mutt_parse_color() */
348 COLOR_LINE *tmp = *top;
352 if (m_strcmp(s, tmp->pattern) == 0)
356 if (m_strcasecmp(s, tmp->pattern) == 0)
363 if (fg != -1 && bg != -1) {
364 if (tmp->fg != fg || tmp->bg != bg) {
365 mutt_free_color (tmp->fg, tmp->bg);
368 attr |= mutt_alloc_color (fg, bg);
371 attr |= (tmp->pair & ~A_BOLD);
378 tmp = mutt_new_color_line ();
382 m_strcpy(buf, sizeof(buf), NONULL(s));
383 mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
384 if ((tmp->color_pattern =
385 mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
386 mutt_free_color_line (&tmp, 1);
389 /* force re-caching of index colors */
390 for (i = 0; Context && i < Context->msgcount; i++)
391 Context->hdrs[i]->pair = 0;
395 REGCOMP (&tmp->rx, s,
396 (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
397 regerror (r, &tmp->rx, err->data, err->dsize);
398 mutt_free_color_line (&tmp, 1);
402 tmp->pattern = m_strdup(s);
403 if (fg != -1 && bg != -1) {
406 attr |= mutt_alloc_color (fg, bg);
416 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
422 m_strcpy(err->data, err->dsize, _("Missing arguments."));
426 mutt_extract_token (buf, s, 0);
427 if (!m_strncmp(buf->data, "quoted", 6)) {
429 *ql = strtol (buf->data + 6, &eptr, 10);
430 if (*eptr || q_level < 0) {
431 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
438 *o = MT_COLOR_QUOTED;
440 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
441 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
448 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
452 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
456 m_strcpy(err->data, err->dsize, _("color: too few arguments"));
460 mutt_extract_token (buf, s, 0);
462 if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
466 m_strcpy(err->data, err->dsize, _("color: too few arguments"));
470 mutt_extract_token (buf, s, 0);
472 if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
479 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
489 m_strcpy(err->data, err->dsize, _("mono: too few arguments"));
493 mutt_extract_token (buf, s, 0);
495 if (ascii_strcasecmp ("bold", buf->data) == 0)
497 else if (ascii_strcasecmp ("underline", buf->data) == 0)
498 *attr |= A_UNDERLINE;
499 else if (ascii_strcasecmp ("none", buf->data) == 0)
501 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
503 else if (ascii_strcasecmp ("standout", buf->data) == 0)
505 else if (ascii_strcasecmp ("normal", buf->data) == 0)
506 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
508 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
515 static int fgbgattr_to_color (int fg, int bg, int attr)
517 if (fg != -1 && bg != -1)
518 return attr | mutt_alloc_color (fg, bg);
523 /* usage: color <object> <fg> <bg> [ <regexp> ]
524 * mono <object> <attr> [ <regexp> ]
528 _mutt_parse_color (BUFFER * buf, BUFFER * s, BUFFER * err,
529 parser_callback_t callback, short dry_run)
531 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
534 if (parse_object (buf, s, &object, &q_level, err) == -1)
537 if (callback (buf, s, &fg, &bg, &attr, err) == -1)
540 /* extract a regular expression if needed */
542 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
543 || object == MT_COLOR_INDEX) {
545 m_strcpy(err->data, err->dsize, _("too few arguments"));
549 mutt_extract_token (buf, s, 0);
553 m_strcpy(err->data, err->dsize, _("too many arguments"));
563 if (!option (OPTNOCURSES) && has_colors ()
564 /* delay use_default_colors() until needed, since it initializes things */
565 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
566 && use_default_colors () != OK) {
567 m_strcpy(err->data, err->dsize, _("default colors not supported"));
571 if (object == MT_COLOR_HEADER)
572 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
573 else if (object == MT_COLOR_BODY)
574 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
575 else if (object == MT_COLOR_INDEX) {
576 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
577 set_option (OPTFORCEREDRAWINDEX);
579 else if (object == MT_COLOR_QUOTED) {
580 if (q_level >= ColorQuoteSize) {
581 p_realloc(&ColorQuote, ColorQuoteSize += 2);
582 ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
583 ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
585 if (q_level >= ColorQuoteUsed)
586 ColorQuoteUsed = q_level + 1;
588 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
590 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
591 for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
592 if (ColorQuote[q_level] == A_NORMAL)
593 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
597 ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
600 ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
602 if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
603 BKGDSET(main_w, MT_COLOR_NORMAL);
608 int mutt_parse_color (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
613 if (option (OPTNOCURSES) || !has_colors ())
616 return _mutt_parse_color (buff, s, err, parse_color_pair, dry_run);
619 int mutt_parse_mono (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
624 if (option (OPTNOCURSES) || has_colors ())
627 return _mutt_parse_color (buff, s, err, parse_attr_spec, dry_run);