2 * Copyright (C) 1996-2002,2012 Michael R. Elkins <me@mutt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
35 int ColorDefs[MT_COLOR_MAX];
36 COLOR_LINE *ColorHdrList = NULL;
37 COLOR_LINE *ColorBodyList = NULL;
38 COLOR_LINE *ColorIndexList = NULL;
40 /* local to this file */
41 static int ColorQuoteSize;
45 #define COLOR_DEFAULT (-2)
47 typedef struct color_list
53 struct color_list *next;
56 static COLOR_LIST *ColorList = NULL;
57 static int UserColors = 0;
59 static const struct mapping_t Colors[] =
61 { "black", COLOR_BLACK },
62 { "blue", COLOR_BLUE },
63 { "cyan", COLOR_CYAN },
64 { "green", COLOR_GREEN },
65 { "magenta", COLOR_MAGENTA },
67 { "white", COLOR_WHITE },
68 { "yellow", COLOR_YELLOW },
69 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
70 { "default", COLOR_DEFAULT },
75 #endif /* HAVE_COLOR */
77 static const struct mapping_t Fields[] =
79 { "hdrdefault", MT_COLOR_HDEFAULT },
80 { "quoted", MT_COLOR_QUOTED },
81 { "signature", MT_COLOR_SIGNATURE },
82 { "indicator", MT_COLOR_INDICATOR },
83 { "status", MT_COLOR_STATUS },
84 { "tree", MT_COLOR_TREE },
85 { "error", MT_COLOR_ERROR },
86 { "normal", MT_COLOR_NORMAL },
87 { "tilde", MT_COLOR_TILDE },
88 { "markers", MT_COLOR_MARKERS },
89 { "header", MT_COLOR_HEADER },
90 { "body", MT_COLOR_BODY },
91 { "message", MT_COLOR_MESSAGE },
92 { "attachment", MT_COLOR_ATTACHMENT },
93 { "search", MT_COLOR_SEARCH },
94 { "bold", MT_COLOR_BOLD },
95 { "underline", MT_COLOR_UNDERLINE },
96 { "index", MT_COLOR_INDEX },
97 { "prompt", MT_COLOR_PROMPT },
99 { "sidebar_divider", MT_COLOR_DIVIDER },
100 { "sidebar_flagged", MT_COLOR_FLAGGED },
101 { "sidebar_highlight",MT_COLOR_HIGHLIGHT },
102 { "sidebar_indicator",MT_COLOR_SB_INDICATOR },
103 { "sidebar_new", MT_COLOR_NEW },
104 { "sidebar_spoolfile",MT_COLOR_SB_SPOOLFILE },
109 static const struct mapping_t ComposeFields[] =
111 { "header", MT_COLOR_COMPOSE_HEADER },
112 { "security_encrypt", MT_COLOR_COMPOSE_SECURITY_ENCRYPT },
113 { "security_sign", MT_COLOR_COMPOSE_SECURITY_SIGN },
114 { "security_both", MT_COLOR_COMPOSE_SECURITY_BOTH },
115 { "security_none", MT_COLOR_COMPOSE_SECURITY_NONE },
119 #define COLOR_QUOTE_INIT 8
121 static COLOR_LINE *mutt_new_color_line (void)
123 COLOR_LINE *p = safe_calloc (1, sizeof (COLOR_LINE));
130 static void mutt_free_color_line(COLOR_LINE **l,
141 if (free_colors && tmp->fg != -1 && tmp->bg != -1)
142 mutt_free_color(tmp->fg, tmp->bg);
145 /* we should really introduce a container
146 * type for regular expressions.
150 mutt_pattern_free(&tmp->color_pattern);
151 FREE (&tmp->pattern);
152 FREE (l); /* __FREE_CHECKED__ */
155 void ci_start_color (void)
157 memset (ColorDefs, A_NORMAL, sizeof (int) * MT_COLOR_MAX);
158 ColorQuote = (int *) safe_malloc (COLOR_QUOTE_INIT * sizeof (int));
159 memset (ColorQuote, A_NORMAL, sizeof (int) * COLOR_QUOTE_INIT);
160 ColorQuoteSize = COLOR_QUOTE_INIT;
163 /* set some defaults */
164 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
165 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
166 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
167 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
169 ColorDefs[MT_COLOR_HIGHLIGHT] = A_UNDERLINE;
171 /* special meaning: toggle the relevant attribute */
172 ColorDefs[MT_COLOR_BOLD] = 0;
173 ColorDefs[MT_COLOR_UNDERLINE] = 0;
182 #ifdef USE_SLANG_CURSES
183 static char *get_color_name (char *dest, size_t destlen, int val)
185 static const char * const missing[3] = {"brown", "lightgray", "default"};
191 strfcpy (dest, missing[0], destlen);
195 strfcpy (dest, missing[1], destlen);
199 strfcpy (dest, missing[2], destlen);
203 for (i = 0; Colors[i].name; i++)
205 if (Colors[i].value == val)
207 strfcpy (dest, Colors[i].name, destlen);
212 /* Sigh. If we got this far, the color is of the form 'colorN'
213 * Slang can handle this itself, so just return 'colorN'
216 snprintf (dest, destlen, "color%d", val);
221 int mutt_alloc_color (int fg, int bg)
223 COLOR_LIST *p = ColorList;
226 #if defined (USE_SLANG_CURSES)
227 char fgc[SHORT_STRING], bgc[SHORT_STRING];
230 /* check to see if this color is already allocated to save space */
233 if (p->fg == fg && p->bg == bg)
236 return (COLOR_PAIR (p->index));
241 /* check to see if there are colors left */
242 if (++UserColors > COLOR_PAIRS) return (A_NORMAL);
244 /* find the smallest available index (object) */
251 if (p->index == i) break;
254 if (p == NULL) break;
258 p = (COLOR_LIST *) safe_malloc (sizeof (COLOR_LIST));
267 #if defined (USE_SLANG_CURSES)
268 if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
269 SLtt_set_color (i, NULL, get_color_name (fgc, sizeof (fgc), fg), get_color_name (bgc, sizeof (bgc), bg));
271 #elif defined (HAVE_USE_DEFAULT_COLORS)
272 if (fg == COLOR_DEFAULT)
274 if (bg == COLOR_DEFAULT)
278 init_pair(i, fg, bg);
280 dprint (3, (debugfile,"mutt_alloc_color(): Color pairs used so far: %d\n",
283 return (COLOR_PAIR (p->index));
286 void mutt_free_color (int fg, int bg)
293 if (p->fg == fg && p->bg == bg)
296 if (p->count > 0) return;
299 dprint(1,(debugfile,"mutt_free_color(): Color pairs used so far: %d\n",
304 ColorList = ColorList->next;
325 #endif /* HAVE_COLOR */
331 parse_color_name (const char *s, int *col, int *attr, int is_fg, BUFFER *err)
334 int is_bright = 0, is_light = 0;
336 if (ascii_strncasecmp (s, "bright", 6) == 0)
341 else if (ascii_strncasecmp (s, "light", 5) == 0)
347 /* allow aliases for xterm color resources */
348 if (ascii_strncasecmp (s, "color", 5) == 0)
351 *col = strtol (s, &eptr, 10);
352 if (!*s || *eptr || *col < 0 ||
353 (*col >= COLORS && !option(OPTNOCURSES) && has_colors()))
355 snprintf (err->data, err->dsize, _("%s: color not supported by term"), s);
359 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1)
361 snprintf (err->data, err->dsize, _("%s: no such color"), s);
365 if (is_bright || is_light)
369 if ((COLORS >= 16) && is_light)
371 if (*col >= 0 && *col <= 7)
373 /* Advance the color 0-7 by 8 to get the light version */
386 if (*col >= 0 && *col <= 7)
388 /* Advance the color 0-7 by 8 to get the light version */
401 /* usage: uncolor index pattern [pattern...]
402 * unmono index pattern [pattern...]
406 _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, BUFFER *err, short parse_uncolor);
411 int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, union pointer_long_t udata,
414 return _mutt_parse_uncolor(buf, s, err, 1);
419 int mutt_parse_unmono (BUFFER *buf, BUFFER *s, union pointer_long_t udata,
422 return _mutt_parse_uncolor(buf, s, err, 0);
425 static int _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, BUFFER *err, short parse_uncolor)
427 int object = 0, is_index = 0, do_cache = 0;
428 COLOR_LINE *tmp, *last = NULL;
431 mutt_extract_token (buf, s, 0);
433 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
435 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
439 if (mutt_strncmp (buf->data, "index", 5) == 0)
442 list = &ColorIndexList;
444 else if (mutt_strncmp (buf->data, "body", 4) == 0)
445 list = &ColorBodyList;
446 else if (mutt_strncmp (buf->data, "header", 6) == 0)
447 list = &ColorHdrList;
450 snprintf (err->data, err->dsize,
451 _("%s: command valid only for index, body, header objects"),
452 parse_uncolor ? "uncolor" : "unmono");
458 snprintf (err->data, err->dsize,
459 _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono");
465 /* we're running without curses */
467 || /* we're parsing an uncolor command, and have no colors */
468 (parse_uncolor && !has_colors())
469 /* we're parsing an unmono command, and have colors */
470 || (!parse_uncolor && has_colors())
472 /* We don't even have colors compiled in */
477 /* just eat the command, but don't do anything real about it */
479 mutt_extract_token (buf, s, 0);
480 while (MoreArgs (s));
487 mutt_extract_token (buf, s, 0);
488 if (!mutt_strcmp ("*", buf->data))
490 for (tmp = *list; tmp; )
496 mutt_free_color_line(&last, parse_uncolor);
502 for (last = NULL, tmp = *list; tmp; last = tmp, tmp = tmp->next)
504 if (!mutt_strcmp (buf->data, tmp->pattern))
508 dprint(1,(debugfile,"Freeing pattern \"%s\" from color list\n",
511 last->next = tmp->next;
514 mutt_free_color_line(&tmp, parse_uncolor);
520 while (MoreArgs (s));
523 if (is_index && do_cache && !option (OPTNOCURSES))
526 mutt_set_menu_redraw_full (MENU_MAIN);
527 /* force re-caching of index colors */
528 for (i = 0; Context && i < Context->msgcount; i++)
529 Context->hdrs[i]->pair = 0;
536 add_pattern (COLOR_LINE **top, const char *s, int sensitive,
537 int fg, int bg, int attr, BUFFER *err,
541 /* is_index used to store compiled pattern
542 * only for `index' color object
543 * when called from mutt_parse_color() */
545 COLOR_LINE *tmp = *top;
551 if (mutt_strcmp (s, tmp->pattern) == 0)
556 if (mutt_strcasecmp (s, tmp->pattern) == 0)
565 if (fg != -1 && bg != -1)
567 if (tmp->fg != fg || tmp->bg != bg)
569 mutt_free_color (tmp->fg, tmp->bg);
572 attr |= mutt_alloc_color (fg, bg);
575 attr |= (tmp->pair & ~A_BOLD);
577 #endif /* HAVE_COLOR */
585 tmp = mutt_new_color_line ();
588 buf = mutt_buffer_pool_get ();
589 mutt_buffer_strcpy(buf, NONULL(s));
590 mutt_check_simple (buf, NONULL(SimpleSearch));
591 tmp->color_pattern = mutt_pattern_comp (buf->data, MUTT_FULL_MSG, err);
592 mutt_buffer_pool_release (&buf);
593 if (tmp->color_pattern == NULL)
595 mutt_free_color_line(&tmp, 1);
599 else if ((r = REGCOMP (&tmp->rx, s, (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0)
601 regerror (r, &tmp->rx, err->data, err->dsize);
602 mutt_free_color_line(&tmp, 1);
606 tmp->pattern = safe_strdup (s);
608 if (fg != -1 && bg != -1)
612 attr |= mutt_alloc_color (fg, bg);
619 /* force re-caching of index colors */
624 for (i = 0; Context && i < Context->msgcount; i++)
625 Context->hdrs[i]->pair = 0;
632 parse_object(BUFFER *buf, BUFFER *s, int *o, int *ql, BUFFER *err)
639 strfcpy(err->data, _("Missing arguments."), err->dsize);
643 mutt_extract_token(buf, s, 0);
644 if (!mutt_strncmp(buf->data, "quoted", 6))
648 *ql = strtol(buf->data + 6, &eptr, 10);
649 if (*eptr || q_level < 0)
651 snprintf(err->data, err->dsize, _("%s: no such object"), buf->data);
658 *o = MT_COLOR_QUOTED;
660 else if (!ascii_strcasecmp(buf->data, "compose"))
664 strfcpy(err->data, _("Missing arguments."), err->dsize);
668 mutt_extract_token(buf, s, 0);
670 if ((*o = mutt_getvaluebyname (buf->data, ComposeFields)) == -1)
672 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
676 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1)
678 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
685 typedef int (*parser_callback_t)(BUFFER *, BUFFER *, int *, int *, int *, BUFFER *);
690 parse_color_pair(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
696 strfcpy (err->data, _("color: too few arguments"), err->dsize);
700 mutt_extract_token (buf, s, 0);
702 if (ascii_strcasecmp ("bold", buf->data) == 0)
704 else if (ascii_strcasecmp ("underline", buf->data) == 0)
705 *attr |= A_UNDERLINE;
706 else if (ascii_strcasecmp ("none", buf->data) == 0)
708 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
710 else if (ascii_strcasecmp ("standout", buf->data) == 0)
712 else if (ascii_strcasecmp ("normal", buf->data) == 0)
713 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
716 if (parse_color_name (buf->data, fg, attr, 1, err) != 0)
724 strfcpy (err->data, _("color: too few arguments"), err->dsize);
728 mutt_extract_token (buf, s, 0);
730 if (parse_color_name (buf->data, bg, attr, 0, err) != 0)
739 parse_attr_spec(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
747 strfcpy (err->data, _("mono: too few arguments"), err->dsize);
751 mutt_extract_token (buf, s, 0);
753 if (ascii_strcasecmp ("bold", buf->data) == 0)
755 else if (ascii_strcasecmp ("underline", buf->data) == 0)
756 *attr |= A_UNDERLINE;
757 else if (ascii_strcasecmp ("none", buf->data) == 0)
759 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
761 else if (ascii_strcasecmp ("standout", buf->data) == 0)
763 else if (ascii_strcasecmp ("normal", buf->data) == 0)
764 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
767 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
774 static int fgbgattr_to_color(int fg, int bg, int attr)
777 if (fg != -1 && bg != -1)
778 return attr | mutt_alloc_color(fg, bg);
784 /* usage: color <object> <fg> <bg> [ <regexp> ]
785 * mono <object> <attr> [ <regexp> ]
789 _mutt_parse_color (BUFFER *buf, BUFFER *s, BUFFER *err,
790 parser_callback_t callback, short dry_run)
792 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
795 if (parse_object(buf, s, &object, &q_level, err) == -1)
798 if (callback(buf, s, &fg, &bg, &attr, err) == -1)
801 /* extract a regular expression if needed */
803 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
807 strfcpy (err->data, _("too few arguments"), err->dsize);
811 mutt_extract_token (buf, s, 0);
816 strfcpy (err->data, _("too many arguments"), err->dsize);
822 if (dry_run) return 0;
826 # ifdef HAVE_USE_DEFAULT_COLORS
827 if (!option (OPTNOCURSES) && has_colors()
828 /* delay use_default_colors() until needed, since it initializes things */
829 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
830 && use_default_colors () != OK)
832 strfcpy (err->data, _("default colors not supported"), err->dsize);
835 # endif /* HAVE_USE_DEFAULT_COLORS */
838 if (object == MT_COLOR_HEADER)
839 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err,0);
840 else if (object == MT_COLOR_BODY)
841 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
842 else if (object == MT_COLOR_INDEX)
844 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
845 mutt_set_menu_redraw_full (MENU_MAIN);
847 else if (object == MT_COLOR_QUOTED)
849 if (q_level >= ColorQuoteSize)
851 safe_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
852 ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
853 ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
855 if (q_level >= ColorQuoteUsed)
856 ColorQuoteUsed = q_level + 1;
859 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr);
861 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
862 for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
864 if (ColorQuote[q_level] == A_NORMAL)
865 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
869 ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr);
872 ColorDefs[object] = fgbgattr_to_color(fg, bg, attr);
879 int mutt_parse_color(BUFFER *buff, BUFFER *s, union pointer_long_t udata, BUFFER *err)
883 if (option(OPTNOCURSES) || !has_colors())
886 return _mutt_parse_color(buff, s, err, parse_color_pair, dry_run);
891 int mutt_parse_mono(BUFFER *buff, BUFFER *s, union pointer_long_t udata, BUFFER *err)
896 if (option(OPTNOCURSES) || has_colors())
899 if (option(OPTNOCURSES))
903 return _mutt_parse_color(buff, s, err, parse_attr_spec, dry_run);