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-lib/lib-lib.h>
19 int ColorDefs[MT_COLOR_MAX];
20 COLOR_LINE *ColorHdrList = NULL;
21 COLOR_LINE *ColorBodyList = NULL;
22 COLOR_LINE *ColorIndexList = NULL;
24 /* local to this file */
25 static int ColorQuoteSize;
29 #define COLOR_DEFAULT (-2)
31 typedef struct color_list {
36 struct color_list *next;
39 static COLOR_LIST *ColorList = NULL;
40 static int UserColors = 0;
42 static struct mapping_t Colors[] = {
43 {"black", COLOR_BLACK},
46 {"green", COLOR_GREEN},
47 {"magenta", COLOR_MAGENTA},
49 {"white", COLOR_WHITE},
50 {"yellow", COLOR_YELLOW},
51 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
52 {"default", COLOR_DEFAULT},
57 #endif /* HAVE_COLOR */
59 static struct mapping_t Fields[] = {
60 {"hdrdefault", MT_COLOR_HDEFAULT},
61 {"quoted", MT_COLOR_QUOTED},
62 {"signature", MT_COLOR_SIGNATURE},
63 {"indicator", MT_COLOR_INDICATOR},
64 {"status", MT_COLOR_STATUS},
65 {"tree", MT_COLOR_TREE},
66 {"error", MT_COLOR_ERROR},
67 {"normal", MT_COLOR_NORMAL},
68 {"tilde", MT_COLOR_TILDE},
69 {"markers", MT_COLOR_MARKERS},
70 {"header", MT_COLOR_HEADER},
71 {"body", MT_COLOR_BODY},
72 {"message", MT_COLOR_MESSAGE},
73 {"attachment", MT_COLOR_ATTACHMENT},
74 {"search", MT_COLOR_SEARCH},
75 {"bold", MT_COLOR_BOLD},
76 {"underline", MT_COLOR_UNDERLINE},
77 {"index", MT_COLOR_INDEX},
78 {"sidebar_new", MT_COLOR_NEW},
79 {"sidebar", MT_COLOR_SIDEBAR},
80 {"sidebar_flagged", MT_COLOR_FLAGGED},
84 #define COLOR_QUOTE_INIT 8
86 static COLOR_LINE *mutt_new_color_line (void)
88 COLOR_LINE *p = p_new(COLOR_LINE, 1);
95 static void mutt_free_color_line (COLOR_LINE ** l, int free_colors)
105 if (free_colors && tmp->fg != -1 && tmp->bg != -1)
106 mutt_free_color (tmp->fg, tmp->bg);
109 /* we should really introduce a container
110 * type for regular expressions.
114 mutt_pattern_free (&tmp->color_pattern);
115 p_delete(&tmp->pattern);
119 void ci_start_color (void)
121 memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX);
122 ColorQuote = p_new(int, COLOR_QUOTE_INIT);
123 memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT);
124 ColorQuoteSize = COLOR_QUOTE_INIT;
127 /* set some defaults */
128 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
129 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
130 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
131 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
132 /* special meaning: toggle the relevant attribute */
133 ColorDefs[MT_COLOR_BOLD] = 0;
134 ColorDefs[MT_COLOR_UNDERLINE] = 0;
143 #ifdef USE_SLANG_CURSES
144 static char *get_color_name (char *dest, size_t destlen, int val)
146 static char *missing[3] = { "brown", "lightgray", "default" };
151 m_strcpy(dest, destlen, missing[0]);
155 m_strcpy(dest, destlen, missing[1]);
159 m_strcpy(dest, destlen, missing[2]);
163 for (i = 0; Colors[i].name; i++) {
164 if (Colors[i].value == val) {
165 m_strcpy(dest, destlen, Colors[i].name);
170 /* Sigh. If we got this far, the color is of the form 'colorN'
171 * Slang can handle this itself, so just return 'colorN'
174 snprintf (dest, destlen, "color%d", val);
179 int mutt_alloc_color (int fg, int bg)
181 COLOR_LIST *p = ColorList;
184 #if defined (USE_SLANG_CURSES)
185 char fgc[SHORT_STRING], bgc[SHORT_STRING];
188 /* check to see if this color is already allocated to save space */
190 if (p->fg == fg && p->bg == bg) {
192 return (COLOR_PAIR (p->index));
197 /* check to see if there are colors left */
198 if (++UserColors > COLOR_PAIRS)
201 /* find the smallest available index (object) */
215 p = p_new(COLOR_LIST, 1);
224 #if defined (USE_SLANG_CURSES)
225 if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
226 SLtt_set_color (i, NULL, get_color_name (fgc, sizeof (fgc), fg),
227 get_color_name (bgc, sizeof (bgc), bg));
229 #elif defined (HAVE_USE_DEFAULT_COLORS)
230 if (fg == COLOR_DEFAULT)
232 if (bg == COLOR_DEFAULT)
236 init_pair (i, fg, bg);
238 return (COLOR_PAIR (p->index));
241 void mutt_free_color (int fg, int bg)
247 if (p->fg == fg && p->bg == bg) {
254 if (p == ColorList) {
255 ColorList = ColorList->next;
274 #endif /* HAVE_COLOR */
280 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
284 if (m_strncasecmp(s, "bright", 6) == 0) {
289 /* allow aliases for xterm color resources */
290 if (m_strncasecmp(s, "color", 5) == 0) {
292 *col = strtol (s, &eptr, 10);
293 if (!*s || *eptr || *col < 0 ||
294 (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
295 snprintf (err->data, err->dsize, _("%s: color not supported by term"),
300 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
301 snprintf (err->data, err->dsize, _("%s: no such color"), s);
311 /* usage: uncolor index pattern [pattern...]
312 * unmono index pattern [pattern...]
316 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
317 BUFFER * err, short parse_uncolor);
322 int mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
325 return _mutt_parse_uncolor (buf, s, data, err, 1);
330 int mutt_parse_unmono (BUFFER * buf, BUFFER * s, unsigned long data,
333 return _mutt_parse_uncolor (buf, s, data, err, 0);
337 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
338 BUFFER * err, short parse_uncolor)
340 int object = 0, do_cache = 0;
341 COLOR_LINE *tmp, *last = NULL;
343 mutt_extract_token (buf, s, 0);
345 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
346 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
350 if (m_strncmp(buf->data, "index", 5) != 0) {
351 snprintf (err->data, err->dsize,
352 _("%s: command valid only for index object"),
353 parse_uncolor ? "uncolor" : "unmono");
358 snprintf (err->data, err->dsize,
359 _("%s: too few arguments"),
360 parse_uncolor ? "uncolor" : "unmono");
366 /* we're running without curses */
368 || /* we're parsing an uncolor command, and have no colors */
369 (parse_uncolor && !has_colors ())
370 /* we're parsing an unmono command, and have colors */
371 || (!parse_uncolor && has_colors ())
373 /* We don't even have colors compiled in */
377 /* just eat the command, but don't do anything real about it */
379 mutt_extract_token (buf, s, 0);
380 while (MoreArgs (s));
387 mutt_extract_token (buf, s, 0);
388 if (!m_strcmp("*", buf->data)) {
389 for (tmp = ColorIndexList; tmp;) {
394 mutt_free_color_line (&last, parse_uncolor);
396 ColorIndexList = NULL;
399 for (last = NULL, tmp = ColorIndexList; tmp;
400 last = tmp, tmp = tmp->next) {
401 if (!m_strcmp(buf->data, tmp->pattern)) {
405 last->next = tmp->next;
407 ColorIndexList = tmp->next;
408 mutt_free_color_line (&tmp, parse_uncolor);
414 while (MoreArgs (s));
417 if (do_cache && !option (OPTNOCURSES)) {
420 set_option (OPTFORCEREDRAWINDEX);
421 /* force re-caching of index colors */
422 for (i = 0; Context && i < Context->msgcount; i++)
423 Context->hdrs[i]->pair = 0;
430 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
431 int fg, int bg, int attr, BUFFER * err, int is_index)
434 /* is_index used to store compiled pattern
435 * only for `index' color object
436 * when called from mutt_parse_color() */
438 COLOR_LINE *tmp = *top;
442 if (m_strcmp(s, tmp->pattern) == 0)
446 if (m_strcasecmp(s, tmp->pattern) == 0)
454 if (fg != -1 && bg != -1) {
455 if (tmp->fg != fg || tmp->bg != bg) {
456 mutt_free_color (tmp->fg, tmp->bg);
459 attr |= mutt_alloc_color (fg, bg);
462 attr |= (tmp->pair & ~A_BOLD);
464 #endif /* HAVE_COLOR */
471 tmp = mutt_new_color_line ();
475 m_strcpy(buf, sizeof(buf), NONULL(s));
476 mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
477 if ((tmp->color_pattern =
478 mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
479 mutt_free_color_line (&tmp, 1);
482 /* force re-caching of index colors */
483 for (i = 0; Context && i < Context->msgcount; i++)
484 Context->hdrs[i]->pair = 0;
488 REGCOMP (&tmp->rx, s,
489 (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
490 regerror (r, &tmp->rx, err->data, err->dsize);
491 mutt_free_color_line (&tmp, 1);
495 tmp->pattern = m_strdup(s);
497 if (fg != -1 && bg != -1) {
500 attr |= mutt_alloc_color (fg, bg);
511 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
517 m_strcpy(err->data, err->dsize, _("Missing arguments."));
521 mutt_extract_token (buf, s, 0);
522 if (!m_strncmp(buf->data, "quoted", 6)) {
524 *ql = strtol (buf->data + 6, &eptr, 10);
525 if (*eptr || q_level < 0) {
526 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
533 *o = MT_COLOR_QUOTED;
535 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
536 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
543 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
549 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
553 m_strcpy(err->data, err->dsize, _("color: too few arguments"));
557 mutt_extract_token (buf, s, 0);
559 if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
563 m_strcpy(err->data, err->dsize, _("color: too few arguments"));
567 mutt_extract_token (buf, s, 0);
569 if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
578 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
588 m_strcpy(err->data, err->dsize, _("mono: too few arguments"));
592 mutt_extract_token (buf, s, 0);
594 if (ascii_strcasecmp ("bold", buf->data) == 0)
596 else if (ascii_strcasecmp ("underline", buf->data) == 0)
597 *attr |= A_UNDERLINE;
598 else if (ascii_strcasecmp ("none", buf->data) == 0)
600 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
602 else if (ascii_strcasecmp ("standout", buf->data) == 0)
604 else if (ascii_strcasecmp ("normal", buf->data) == 0)
605 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
607 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
614 static int fgbgattr_to_color (int fg, int bg, int attr)
617 if (fg != -1 && bg != -1)
618 return attr | mutt_alloc_color (fg, bg);
624 /* usage: color <object> <fg> <bg> [ <regexp> ]
625 * mono <object> <attr> [ <regexp> ]
629 _mutt_parse_color (BUFFER * buf, BUFFER * s, BUFFER * err,
630 parser_callback_t callback, short dry_run)
632 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
635 if (parse_object (buf, s, &object, &q_level, err) == -1)
638 if (callback (buf, s, &fg, &bg, &attr, err) == -1)
641 /* extract a regular expression if needed */
643 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
644 || object == MT_COLOR_INDEX) {
646 m_strcpy(err->data, err->dsize, _("too few arguments"));
650 mutt_extract_token (buf, s, 0);
654 m_strcpy(err->data, err->dsize, _("too many arguments"));
665 # ifdef HAVE_USE_DEFAULT_COLORS
666 if (!option (OPTNOCURSES) && has_colors ()
667 /* delay use_default_colors() until needed, since it initializes things */
668 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
669 && use_default_colors () != OK) {
670 m_strcpy(err->data, err->dsize, _("default colors not supported"));
673 # endif /* HAVE_USE_DEFAULT_COLORS */
676 if (object == MT_COLOR_HEADER)
677 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
678 else if (object == MT_COLOR_BODY)
679 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
680 else if (object == MT_COLOR_INDEX) {
681 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
682 set_option (OPTFORCEREDRAWINDEX);
684 else if (object == MT_COLOR_QUOTED) {
685 if (q_level >= ColorQuoteSize) {
686 p_realloc(&ColorQuote, ColorQuoteSize += 2);
687 ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
688 ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
690 if (q_level >= ColorQuoteUsed)
691 ColorQuoteUsed = q_level + 1;
693 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
695 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
696 for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
697 if (ColorQuote[q_level] == A_NORMAL)
698 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
702 ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
705 ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
709 if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
710 BKGDSET (MT_COLOR_NORMAL);
719 int mutt_parse_color (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
724 if (option (OPTNOCURSES) || !has_colors ())
727 return _mutt_parse_color (buff, s, err, parse_color_pair, dry_run);
732 int mutt_parse_mono (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
738 if (option (OPTNOCURSES) || has_colors ())
741 if (option (OPTNOCURSES))
745 return _mutt_parse_color (buff, s, err, parse_attr_spec, dry_run);