2 * Copyright (C) 1996-2002 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 #include "mutt_curses.h"
34 int ColorDefs[MT_COLOR_MAX];
35 COLOR_LINE *ColorHdrList = NULL;
36 COLOR_LINE *ColorBodyList = NULL;
37 COLOR_LINE *ColorIndexList = NULL;
39 /* local to this file */
40 static int ColorQuoteSize;
44 #define COLOR_DEFAULT (-2)
46 typedef struct color_list
52 struct color_list *next;
55 static COLOR_LIST *ColorList = NULL;
56 static int UserColors = 0;
58 static struct mapping_t Colors[] =
60 { "black", COLOR_BLACK },
61 { "blue", COLOR_BLUE },
62 { "cyan", COLOR_CYAN },
63 { "green", COLOR_GREEN },
64 { "magenta", COLOR_MAGENTA },
66 { "white", COLOR_WHITE },
67 { "yellow", COLOR_YELLOW },
68 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
69 { "default", COLOR_DEFAULT },
74 #endif /* HAVE_COLOR */
76 static struct mapping_t Fields[] =
78 { "hdrdefault", MT_COLOR_HDEFAULT },
79 { "quoted", MT_COLOR_QUOTED },
80 { "signature", MT_COLOR_SIGNATURE },
81 { "indicator", MT_COLOR_INDICATOR },
82 { "status", MT_COLOR_STATUS },
83 { "tree", MT_COLOR_TREE },
84 { "error", MT_COLOR_ERROR },
85 { "normal", MT_COLOR_NORMAL },
86 { "tilde", MT_COLOR_TILDE },
87 { "markers", MT_COLOR_MARKERS },
88 { "header", MT_COLOR_HEADER },
89 { "body", MT_COLOR_BODY },
90 { "message", MT_COLOR_MESSAGE },
91 { "attachment", MT_COLOR_ATTACHMENT },
92 { "search", MT_COLOR_SEARCH },
93 { "bold", MT_COLOR_BOLD },
94 { "underline", MT_COLOR_UNDERLINE },
95 { "index", MT_COLOR_INDEX },
96 { "sidebar_new", MT_COLOR_NEW },
97 { "sidebar", MT_COLOR_SIDEBAR },
98 { "sidebar_flagged", MT_COLOR_FLAGGED },
102 #define COLOR_QUOTE_INIT 8
104 static COLOR_LINE *mutt_new_color_line (void)
106 COLOR_LINE *p = safe_calloc (1, sizeof (COLOR_LINE));
113 static void mutt_free_color_line(COLOR_LINE **l,
124 if(free_colors && tmp->fg != -1 && tmp->bg != -1)
125 mutt_free_color(tmp->fg, tmp->bg);
128 /* we should really introduce a container
129 * type for regular expressions.
133 mutt_pattern_free(&tmp->color_pattern);
134 FREE (&tmp->pattern);
138 void ci_start_color (void)
140 memset (ColorDefs, A_NORMAL, sizeof (int) * MT_COLOR_MAX);
141 ColorQuote = (int *) safe_malloc (COLOR_QUOTE_INIT * sizeof (int));
142 memset (ColorQuote, A_NORMAL, sizeof (int) * COLOR_QUOTE_INIT);
143 ColorQuoteSize = COLOR_QUOTE_INIT;
146 /* set some defaults */
147 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
148 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
149 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
150 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
151 /* special meaning: toggle the relevant attribute */
152 ColorDefs[MT_COLOR_BOLD] = 0;
153 ColorDefs[MT_COLOR_UNDERLINE] = 0;
162 #ifdef USE_SLANG_CURSES
163 static char *get_color_name (char *dest, size_t destlen, int val)
165 static char * missing[3] = {"brown", "lightgray", "default"};
171 strfcpy (dest, missing[0], destlen);
175 strfcpy (dest, missing[1], destlen);
179 strfcpy (dest, missing[2], destlen);
183 for (i = 0; Colors[i].name; i++)
185 if (Colors[i].value == val)
187 strfcpy (dest, Colors[i].name, destlen);
192 /* Sigh. If we got this far, the color is of the form 'colorN'
193 * Slang can handle this itself, so just return 'colorN'
196 snprintf (dest, destlen, "color%d", val);
201 int mutt_alloc_color (int fg, int bg)
203 COLOR_LIST *p = ColorList;
206 #if defined (USE_SLANG_CURSES)
207 char fgc[SHORT_STRING], bgc[SHORT_STRING];
210 /* check to see if this color is already allocated to save space */
213 if (p->fg == fg && p->bg == bg)
216 return (COLOR_PAIR (p->index));
221 /* check to see if there are colors left */
222 if (++UserColors > COLOR_PAIRS) return (A_NORMAL);
224 /* find the smallest available index (object) */
231 if (p->index == i) break;
234 if (p == NULL) break;
238 p = (COLOR_LIST *) safe_malloc (sizeof (COLOR_LIST));
247 #if defined (USE_SLANG_CURSES)
248 if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
249 SLtt_set_color (i, NULL, get_color_name (fgc, sizeof (fgc), fg), get_color_name (bgc, sizeof (bgc), bg));
251 #elif defined (HAVE_USE_DEFAULT_COLORS)
252 if (fg == COLOR_DEFAULT)
254 if (bg == COLOR_DEFAULT)
258 init_pair(i, fg, bg);
260 dprint(1,(debugfile,"mutt_alloc_color(): Color pairs used so far: %d\n",
263 return (COLOR_PAIR (p->index));
266 void mutt_free_color (int fg, int bg)
273 if (p->fg == fg && p->bg == bg)
276 if (p->count > 0) return;
279 dprint(1,(debugfile,"mutt_free_color(): Color pairs used so far: %d\n",
284 ColorList = ColorList->next;
305 #endif /* HAVE_COLOR */
311 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER *err)
315 if (mutt_strncasecmp (s, "bright", 6) == 0)
321 /* allow aliases for xterm color resources */
322 if (mutt_strncasecmp (s, "color", 5) == 0)
325 *col = strtol (s, &eptr, 10);
326 if (!*s || *eptr || *col < 0 ||
327 (*col >= COLORS && !option(OPTNOCURSES) && has_colors()))
329 snprintf (err->data, err->dsize, _("%s: color not supported by term"), s);
333 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1)
335 snprintf (err->data, err->dsize, _("%s: no such color"), s);
345 /* usage: uncolor index pattern [pattern...]
346 * unmono index pattern [pattern...]
350 _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err,
351 short parse_uncolor);
356 int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data,
359 return _mutt_parse_uncolor(buf, s, data, err, 1);
364 int mutt_parse_unmono (BUFFER *buf, BUFFER *s, unsigned long data,
367 return _mutt_parse_uncolor(buf, s, data, err, 0);
371 _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err,
374 int object = 0, do_cache = 0;
375 COLOR_LINE *tmp, *last = NULL;
377 mutt_extract_token (buf, s, 0);
379 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
381 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
385 if (mutt_strncmp (buf->data, "index", 5) != 0)
387 snprintf (err->data, err->dsize,
388 _("%s: command valid only for index object"),
389 parse_uncolor ? "uncolor" : "unmono");
395 snprintf (err->data, err->dsize,
396 _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono");
402 /* we're running without curses */
404 || /* we're parsing an uncolor command, and have no colors */
405 (parse_uncolor && !has_colors())
406 /* we're parsing an unmono command, and have colors */
407 || (!parse_uncolor && has_colors())
409 /* We don't even have colors compiled in */
414 /* just eat the command, but don't do anything real about it */
416 mutt_extract_token (buf, s, 0);
417 while (MoreArgs (s));
425 mutt_extract_token (buf, s, 0);
426 if (!mutt_strcmp ("*", buf->data))
428 for (tmp = ColorIndexList; tmp; )
434 mutt_free_color_line(&last, parse_uncolor);
436 ColorIndexList = NULL;
440 for (last = NULL, tmp = ColorIndexList; tmp; last = tmp, tmp = tmp->next)
442 if (!mutt_strcmp (buf->data, tmp->pattern))
446 dprint(1,(debugfile,"Freeing pattern \"%s\" from ColorIndexList\n",
449 last->next = tmp->next;
451 ColorIndexList = tmp->next;
452 mutt_free_color_line(&tmp, parse_uncolor);
458 while (MoreArgs (s));
461 if (do_cache && !option (OPTNOCURSES))
464 set_option (OPTFORCEREDRAWINDEX);
465 /* force re-caching of index colors */
466 for (i = 0; Context && i < Context->msgcount; i++)
467 Context->hdrs[i]->pair = 0;
474 add_pattern (COLOR_LINE **top, const char *s, int sensitive,
475 int fg, int bg, int attr, BUFFER *err,
479 /* is_index used to store compiled pattern
480 * only for `index' color object
481 * when called from mutt_parse_color() */
483 COLOR_LINE *tmp = *top;
489 if (mutt_strcmp (s, tmp->pattern) == 0)
494 if (mutt_strcasecmp (s, tmp->pattern) == 0)
503 if (fg != -1 && bg != -1)
505 if (tmp->fg != fg || tmp->bg != bg)
507 mutt_free_color (tmp->fg, tmp->bg);
510 attr |= mutt_alloc_color (fg, bg);
513 attr |= (tmp->pair & ~A_BOLD);
515 #endif /* HAVE_COLOR */
523 tmp = mutt_new_color_line ();
528 strfcpy(buf, NONULL(s), sizeof(buf));
529 mutt_check_simple (buf, sizeof (buf), NONULL(SimpleSearch));
530 if((tmp->color_pattern = mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL)
532 mutt_free_color_line(&tmp, 1);
535 /* force re-caching of index colors */
536 for (i = 0; Context && i < Context->msgcount; i++)
537 Context->hdrs[i]->pair = 0;
539 else if ((r = REGCOMP (&tmp->rx, s, (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0)
541 regerror (r, &tmp->rx, err->data, err->dsize);
542 mutt_free_color_line(&tmp, 1);
546 tmp->pattern = safe_strdup (s);
548 if(fg != -1 && bg != -1)
552 attr |= mutt_alloc_color (fg, bg);
563 parse_object(BUFFER *buf, BUFFER *s, int *o, int *ql, BUFFER *err)
570 strfcpy(err->data, _("Missing arguments."), err->dsize);
574 mutt_extract_token(buf, s, 0);
575 if(!mutt_strncmp(buf->data, "quoted", 6))
579 *ql = strtol(buf->data + 6, &eptr, 10);
580 if(*eptr || q_level < 0)
582 snprintf(err->data, err->dsize, _("%s: no such object"), buf->data);
589 *o = MT_COLOR_QUOTED;
591 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1)
593 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
600 typedef int (*parser_callback_t)(BUFFER *, BUFFER *, int *, int *, int *, BUFFER *);
605 parse_color_pair(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
609 strfcpy (err->data, _("color: too few arguments"), err->dsize);
613 mutt_extract_token (buf, s, 0);
615 if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
620 strfcpy (err->data, _("color: too few arguments"), err->dsize);
624 mutt_extract_token (buf, s, 0);
626 if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
635 parse_attr_spec(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
643 strfcpy (err->data, _("mono: too few arguments"), err->dsize);
647 mutt_extract_token (buf, s, 0);
649 if (ascii_strcasecmp ("bold", buf->data) == 0)
651 else if (ascii_strcasecmp ("underline", buf->data) == 0)
652 *attr |= A_UNDERLINE;
653 else if (ascii_strcasecmp ("none", buf->data) == 0)
655 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
657 else if (ascii_strcasecmp ("standout", buf->data) == 0)
659 else if (ascii_strcasecmp ("normal", buf->data) == 0)
660 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
663 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
670 static int fgbgattr_to_color(int fg, int bg, int attr)
673 if(fg != -1 && bg != -1)
674 return attr | mutt_alloc_color(fg, bg);
680 /* usage: color <object> <fg> <bg> [ <regexp> ]
681 * mono <object> <attr> [ <regexp> ]
685 _mutt_parse_color (BUFFER *buf, BUFFER *s, BUFFER *err,
686 parser_callback_t callback, short dry_run)
688 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
691 if(parse_object(buf, s, &object, &q_level, err) == -1)
694 if(callback(buf, s, &fg, &bg, &attr, err) == -1)
697 /* extract a regular expression if needed */
699 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
703 strfcpy (err->data, _("too few arguments"), err->dsize);
707 mutt_extract_token (buf, s, 0);
712 strfcpy (err->data, _("too many arguments"), err->dsize);
718 if(dry_run) return 0;
722 # ifdef HAVE_USE_DEFAULT_COLORS
723 if (!option (OPTNOCURSES) && has_colors()
724 /* delay use_default_colors() until needed, since it initializes things */
725 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
726 && use_default_colors () != OK)
728 strfcpy (err->data, _("default colors not supported"), err->dsize);
731 # endif /* HAVE_USE_DEFAULT_COLORS */
734 if (object == MT_COLOR_HEADER)
735 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err,0);
736 else if (object == MT_COLOR_BODY)
737 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
738 else if (object == MT_COLOR_INDEX)
740 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
741 set_option (OPTFORCEREDRAWINDEX);
743 else if (object == MT_COLOR_QUOTED)
745 if (q_level >= ColorQuoteSize)
747 safe_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
748 ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
749 ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
751 if (q_level >= ColorQuoteUsed)
752 ColorQuoteUsed = q_level + 1;
755 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr);
757 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
758 for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
760 if (ColorQuote[q_level] == A_NORMAL)
761 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
765 ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr);
768 ColorDefs[object] = fgbgattr_to_color(fg, bg, attr);
772 if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors())
773 BKGDSET (MT_COLOR_NORMAL);
782 int mutt_parse_color(BUFFER *buff, BUFFER *s, unsigned long data, BUFFER *err)
786 if(option(OPTNOCURSES) || !has_colors())
789 return _mutt_parse_color(buff, s, err, parse_color_pair, dry_run);
794 int mutt_parse_mono(BUFFER *buff, BUFFER *s, unsigned long data, BUFFER *err)
799 if(option(OPTNOCURSES) || has_colors())
802 if(option(OPTNOCURSES))
806 return _mutt_parse_color(buff, s, err, parse_attr_spec, dry_run);