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 {
51 struct color_list *next;
54 static COLOR_LIST *ColorList = NULL;
55 static int UserColors = 0;
57 static struct mapping_t Colors[] = {
58 {"black", COLOR_BLACK},
61 {"green", COLOR_GREEN},
62 {"magenta", COLOR_MAGENTA},
64 {"white", COLOR_WHITE},
65 {"yellow", COLOR_YELLOW},
66 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
67 {"default", COLOR_DEFAULT},
72 #endif /* HAVE_COLOR */
74 static struct mapping_t Fields[] = {
75 {"hdrdefault", MT_COLOR_HDEFAULT},
76 {"quoted", MT_COLOR_QUOTED},
77 {"signature", MT_COLOR_SIGNATURE},
78 {"indicator", MT_COLOR_INDICATOR},
79 {"status", MT_COLOR_STATUS},
80 {"tree", MT_COLOR_TREE},
81 {"error", MT_COLOR_ERROR},
82 {"normal", MT_COLOR_NORMAL},
83 {"tilde", MT_COLOR_TILDE},
84 {"markers", MT_COLOR_MARKERS},
85 {"header", MT_COLOR_HEADER},
86 {"body", MT_COLOR_BODY},
87 {"message", MT_COLOR_MESSAGE},
88 {"attachment", MT_COLOR_ATTACHMENT},
89 {"search", MT_COLOR_SEARCH},
90 {"bold", MT_COLOR_BOLD},
91 {"underline", MT_COLOR_UNDERLINE},
92 {"index", MT_COLOR_INDEX},
93 {"sidebar_new", MT_COLOR_NEW},
94 {"sidebar", MT_COLOR_SIDEBAR},
95 {"sidebar_flagged", MT_COLOR_FLAGGED},
99 #define COLOR_QUOTE_INIT 8
101 static COLOR_LINE *mutt_new_color_line (void)
103 COLOR_LINE *p = safe_calloc (1, sizeof (COLOR_LINE));
110 static void mutt_free_color_line (COLOR_LINE ** l, int free_colors)
120 if (free_colors && tmp->fg != -1 && tmp->bg != -1)
121 mutt_free_color (tmp->fg, tmp->bg);
124 /* we should really introduce a container
125 * type for regular expressions.
129 mutt_pattern_free (&tmp->color_pattern);
130 FREE (&tmp->pattern);
134 void ci_start_color (void)
136 memset (ColorDefs, A_NORMAL, sizeof (int) * MT_COLOR_MAX);
137 ColorQuote = (int *) safe_malloc (COLOR_QUOTE_INIT * sizeof (int));
138 memset (ColorQuote, A_NORMAL, sizeof (int) * COLOR_QUOTE_INIT);
139 ColorQuoteSize = COLOR_QUOTE_INIT;
142 /* set some defaults */
143 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
144 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
145 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
146 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
147 /* special meaning: toggle the relevant attribute */
148 ColorDefs[MT_COLOR_BOLD] = 0;
149 ColorDefs[MT_COLOR_UNDERLINE] = 0;
158 #ifdef USE_SLANG_CURSES
159 static char *get_color_name (char *dest, size_t destlen, int val)
161 static char *missing[3] = { "brown", "lightgray", "default" };
166 strfcpy (dest, missing[0], destlen);
170 strfcpy (dest, missing[1], destlen);
174 strfcpy (dest, missing[2], destlen);
178 for (i = 0; Colors[i].name; i++) {
179 if (Colors[i].value == val) {
180 strfcpy (dest, Colors[i].name, destlen);
185 /* Sigh. If we got this far, the color is of the form 'colorN'
186 * Slang can handle this itself, so just return 'colorN'
189 snprintf (dest, destlen, "color%d", val);
194 int mutt_alloc_color (int fg, int bg)
196 COLOR_LIST *p = ColorList;
199 #if defined (USE_SLANG_CURSES)
200 char fgc[SHORT_STRING], bgc[SHORT_STRING];
203 /* check to see if this color is already allocated to save space */
205 if (p->fg == fg && p->bg == bg) {
207 return (COLOR_PAIR (p->index));
212 /* check to see if there are colors left */
213 if (++UserColors > COLOR_PAIRS)
216 /* find the smallest available index (object) */
230 p = (COLOR_LIST *) safe_malloc (sizeof (COLOR_LIST));
239 #if defined (USE_SLANG_CURSES)
240 if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
241 SLtt_set_color (i, NULL, get_color_name (fgc, sizeof (fgc), fg),
242 get_color_name (bgc, sizeof (bgc), bg));
244 #elif defined (HAVE_USE_DEFAULT_COLORS)
245 if (fg == COLOR_DEFAULT)
247 if (bg == COLOR_DEFAULT)
251 init_pair (i, fg, bg);
253 dprint (1, (debugfile, "mutt_alloc_color(): Color pairs used so far: %d\n",
256 return (COLOR_PAIR (p->index));
259 void mutt_free_color (int fg, int bg)
265 if (p->fg == fg && p->bg == bg) {
272 (debugfile, "mutt_free_color(): Color pairs used so far: %d\n",
275 if (p == ColorList) {
276 ColorList = ColorList->next;
295 #endif /* HAVE_COLOR */
301 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
305 if (mutt_strncasecmp (s, "bright", 6) == 0) {
310 /* allow aliases for xterm color resources */
311 if (mutt_strncasecmp (s, "color", 5) == 0) {
313 *col = strtol (s, &eptr, 10);
314 if (!*s || *eptr || *col < 0 ||
315 (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
316 snprintf (err->data, err->dsize, _("%s: color not supported by term"),
321 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
322 snprintf (err->data, err->dsize, _("%s: no such color"), s);
332 /* usage: uncolor index pattern [pattern...]
333 * unmono index pattern [pattern...]
337 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
338 BUFFER * err, short parse_uncolor);
343 int mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
346 return _mutt_parse_uncolor (buf, s, data, err, 1);
351 int mutt_parse_unmono (BUFFER * buf, BUFFER * s, unsigned long data,
354 return _mutt_parse_uncolor (buf, s, data, err, 0);
358 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
359 BUFFER * err, short parse_uncolor)
361 int object = 0, do_cache = 0;
362 COLOR_LINE *tmp, *last = NULL;
364 mutt_extract_token (buf, s, 0);
366 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
367 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
371 if (mutt_strncmp (buf->data, "index", 5) != 0) {
372 snprintf (err->data, err->dsize,
373 _("%s: command valid only for index object"),
374 parse_uncolor ? "uncolor" : "unmono");
379 snprintf (err->data, err->dsize,
380 _("%s: too few arguments"),
381 parse_uncolor ? "uncolor" : "unmono");
387 /* we're running without curses */
389 || /* we're parsing an uncolor command, and have no colors */
390 (parse_uncolor && !has_colors ())
391 /* we're parsing an unmono command, and have colors */
392 || (!parse_uncolor && has_colors ())
394 /* We don't even have colors compiled in */
398 /* just eat the command, but don't do anything real about it */
400 mutt_extract_token (buf, s, 0);
401 while (MoreArgs (s));
408 mutt_extract_token (buf, s, 0);
409 if (!mutt_strcmp ("*", buf->data)) {
410 for (tmp = ColorIndexList; tmp;) {
415 mutt_free_color_line (&last, parse_uncolor);
417 ColorIndexList = NULL;
420 for (last = NULL, tmp = ColorIndexList; tmp;
421 last = tmp, tmp = tmp->next) {
422 if (!mutt_strcmp (buf->data, tmp->pattern)) {
426 (debugfile, "Freeing pattern \"%s\" from ColorIndexList\n",
429 last->next = tmp->next;
431 ColorIndexList = tmp->next;
432 mutt_free_color_line (&tmp, parse_uncolor);
438 while (MoreArgs (s));
441 if (do_cache && !option (OPTNOCURSES)) {
444 set_option (OPTFORCEREDRAWINDEX);
445 /* force re-caching of index colors */
446 for (i = 0; Context && i < Context->msgcount; i++)
447 Context->hdrs[i]->pair = 0;
454 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
455 int fg, int bg, int attr, BUFFER * err, int is_index)
458 /* is_index used to store compiled pattern
459 * only for `index' color object
460 * when called from mutt_parse_color() */
462 COLOR_LINE *tmp = *top;
466 if (mutt_strcmp (s, tmp->pattern) == 0)
470 if (mutt_strcasecmp (s, tmp->pattern) == 0)
478 if (fg != -1 && bg != -1) {
479 if (tmp->fg != fg || tmp->bg != bg) {
480 mutt_free_color (tmp->fg, tmp->bg);
483 attr |= mutt_alloc_color (fg, bg);
486 attr |= (tmp->pair & ~A_BOLD);
488 #endif /* HAVE_COLOR */
495 tmp = mutt_new_color_line ();
499 strfcpy (buf, NONULL (s), sizeof (buf));
500 mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
501 if ((tmp->color_pattern =
502 mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
503 mutt_free_color_line (&tmp, 1);
506 /* force re-caching of index colors */
507 for (i = 0; Context && i < Context->msgcount; i++)
508 Context->hdrs[i]->pair = 0;
512 REGCOMP (&tmp->rx, s,
513 (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
514 regerror (r, &tmp->rx, err->data, err->dsize);
515 mutt_free_color_line (&tmp, 1);
519 tmp->pattern = safe_strdup (s);
521 if (fg != -1 && bg != -1) {
524 attr |= mutt_alloc_color (fg, bg);
535 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
541 strfcpy (err->data, _("Missing arguments."), err->dsize);
545 mutt_extract_token (buf, s, 0);
546 if (!mutt_strncmp (buf->data, "quoted", 6)) {
548 *ql = strtol (buf->data + 6, &eptr, 10);
549 if (*eptr || q_level < 0) {
550 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
557 *o = MT_COLOR_QUOTED;
559 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
560 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
567 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
573 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
577 strfcpy (err->data, _("color: too few arguments"), err->dsize);
581 mutt_extract_token (buf, s, 0);
583 if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
587 strfcpy (err->data, _("color: too few arguments"), err->dsize);
591 mutt_extract_token (buf, s, 0);
593 if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
602 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
612 strfcpy (err->data, _("mono: too few arguments"), err->dsize);
616 mutt_extract_token (buf, s, 0);
618 if (ascii_strcasecmp ("bold", buf->data) == 0)
620 else if (ascii_strcasecmp ("underline", buf->data) == 0)
621 *attr |= A_UNDERLINE;
622 else if (ascii_strcasecmp ("none", buf->data) == 0)
624 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
626 else if (ascii_strcasecmp ("standout", buf->data) == 0)
628 else if (ascii_strcasecmp ("normal", buf->data) == 0)
629 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
631 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
638 static int fgbgattr_to_color (int fg, int bg, int attr)
641 if (fg != -1 && bg != -1)
642 return attr | mutt_alloc_color (fg, bg);
648 /* usage: color <object> <fg> <bg> [ <regexp> ]
649 * mono <object> <attr> [ <regexp> ]
653 _mutt_parse_color (BUFFER * buf, BUFFER * s, BUFFER * err,
654 parser_callback_t callback, short dry_run)
656 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
659 if (parse_object (buf, s, &object, &q_level, err) == -1)
662 if (callback (buf, s, &fg, &bg, &attr, err) == -1)
665 /* extract a regular expression if needed */
667 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
668 || object == MT_COLOR_INDEX) {
670 strfcpy (err->data, _("too few arguments"), err->dsize);
674 mutt_extract_token (buf, s, 0);
678 strfcpy (err->data, _("too many arguments"), err->dsize);
689 # ifdef HAVE_USE_DEFAULT_COLORS
690 if (!option (OPTNOCURSES) && has_colors ()
691 /* delay use_default_colors() until needed, since it initializes things */
692 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
693 && use_default_colors () != OK) {
694 strfcpy (err->data, _("default colors not supported"), err->dsize);
697 # endif /* HAVE_USE_DEFAULT_COLORS */
700 if (object == MT_COLOR_HEADER)
701 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
702 else if (object == MT_COLOR_BODY)
703 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
704 else if (object == MT_COLOR_INDEX) {
705 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
706 set_option (OPTFORCEREDRAWINDEX);
708 else if (object == MT_COLOR_QUOTED) {
709 if (q_level >= ColorQuoteSize) {
710 safe_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
711 ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
712 ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
714 if (q_level >= ColorQuoteUsed)
715 ColorQuoteUsed = q_level + 1;
717 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
719 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
720 for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
721 if (ColorQuote[q_level] == A_NORMAL)
722 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
726 ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
729 ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
733 if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
734 BKGDSET (MT_COLOR_NORMAL);
743 int mutt_parse_color (BUFFER * buff, BUFFER * s, unsigned long data,
748 if (option (OPTNOCURSES) || !has_colors ())
751 return _mutt_parse_color (buff, s, err, parse_color_pair, dry_run);
756 int mutt_parse_mono (BUFFER * buff, BUFFER * s, unsigned long data,
762 if (option (OPTNOCURSES) || has_colors ())
765 if (option (OPTNOCURSES))
769 return _mutt_parse_color (buff, s, err, parse_attr_spec, dry_run);