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.
17 #include "mutt_curses.h"
23 #include "lib/debug.h"
32 int ColorDefs[MT_COLOR_MAX];
33 COLOR_LINE *ColorHdrList = NULL;
34 COLOR_LINE *ColorBodyList = NULL;
35 COLOR_LINE *ColorIndexList = NULL;
37 /* local to this file */
38 static int ColorQuoteSize;
42 #define COLOR_DEFAULT (-2)
44 typedef struct color_list {
49 struct color_list *next;
52 static COLOR_LIST *ColorList = NULL;
53 static int UserColors = 0;
55 static struct mapping_t Colors[] = {
56 {"black", COLOR_BLACK},
59 {"green", COLOR_GREEN},
60 {"magenta", COLOR_MAGENTA},
62 {"white", COLOR_WHITE},
63 {"yellow", COLOR_YELLOW},
64 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
65 {"default", COLOR_DEFAULT},
70 #endif /* HAVE_COLOR */
72 static struct mapping_t Fields[] = {
73 {"hdrdefault", MT_COLOR_HDEFAULT},
74 {"quoted", MT_COLOR_QUOTED},
75 {"signature", MT_COLOR_SIGNATURE},
76 {"indicator", MT_COLOR_INDICATOR},
77 {"status", MT_COLOR_STATUS},
78 {"tree", MT_COLOR_TREE},
79 {"error", MT_COLOR_ERROR},
80 {"normal", MT_COLOR_NORMAL},
81 {"tilde", MT_COLOR_TILDE},
82 {"markers", MT_COLOR_MARKERS},
83 {"header", MT_COLOR_HEADER},
84 {"body", MT_COLOR_BODY},
85 {"message", MT_COLOR_MESSAGE},
86 {"attachment", MT_COLOR_ATTACHMENT},
87 {"search", MT_COLOR_SEARCH},
88 {"bold", MT_COLOR_BOLD},
89 {"underline", MT_COLOR_UNDERLINE},
90 {"index", MT_COLOR_INDEX},
91 {"sidebar_new", MT_COLOR_NEW},
92 {"sidebar", MT_COLOR_SIDEBAR},
93 {"sidebar_flagged", MT_COLOR_FLAGGED},
97 #define COLOR_QUOTE_INIT 8
99 static COLOR_LINE *mutt_new_color_line (void)
101 COLOR_LINE *p = mem_calloc (1, sizeof (COLOR_LINE));
108 static void mutt_free_color_line (COLOR_LINE ** l, int free_colors)
118 if (free_colors && tmp->fg != -1 && tmp->bg != -1)
119 mutt_free_color (tmp->fg, tmp->bg);
122 /* we should really introduce a container
123 * type for regular expressions.
127 mutt_pattern_free (&tmp->color_pattern);
128 mem_free (&tmp->pattern);
132 void ci_start_color (void)
134 memset (ColorDefs, A_NORMAL, sizeof (int) * MT_COLOR_MAX);
135 ColorQuote = (int *) mem_malloc (COLOR_QUOTE_INIT * sizeof (int));
136 memset (ColorQuote, A_NORMAL, sizeof (int) * COLOR_QUOTE_INIT);
137 ColorQuoteSize = COLOR_QUOTE_INIT;
140 /* set some defaults */
141 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
142 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
143 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
144 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
145 /* special meaning: toggle the relevant attribute */
146 ColorDefs[MT_COLOR_BOLD] = 0;
147 ColorDefs[MT_COLOR_UNDERLINE] = 0;
156 #ifdef USE_SLANG_CURSES
157 static char *get_color_name (char *dest, size_t destlen, int val)
159 static char *missing[3] = { "brown", "lightgray", "default" };
164 strfcpy (dest, missing[0], destlen);
168 strfcpy (dest, missing[1], destlen);
172 strfcpy (dest, missing[2], destlen);
176 for (i = 0; Colors[i].name; i++) {
177 if (Colors[i].value == val) {
178 strfcpy (dest, Colors[i].name, destlen);
183 /* Sigh. If we got this far, the color is of the form 'colorN'
184 * Slang can handle this itself, so just return 'colorN'
187 snprintf (dest, destlen, "color%d", val);
192 int mutt_alloc_color (int fg, int bg)
194 COLOR_LIST *p = ColorList;
197 #if defined (USE_SLANG_CURSES)
198 char fgc[SHORT_STRING], bgc[SHORT_STRING];
201 /* check to see if this color is already allocated to save space */
203 if (p->fg == fg && p->bg == bg) {
205 return (COLOR_PAIR (p->index));
210 /* check to see if there are colors left */
211 if (++UserColors > COLOR_PAIRS)
214 /* find the smallest available index (object) */
228 p = (COLOR_LIST *) mem_malloc (sizeof (COLOR_LIST));
237 #if defined (USE_SLANG_CURSES)
238 if (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
239 SLtt_set_color (i, NULL, get_color_name (fgc, sizeof (fgc), fg),
240 get_color_name (bgc, sizeof (bgc), bg));
242 #elif defined (HAVE_USE_DEFAULT_COLORS)
243 if (fg == COLOR_DEFAULT)
245 if (bg == COLOR_DEFAULT)
249 init_pair (i, fg, bg);
251 debug_print (1, ("Color pairs used so far: %d\n", UserColors));
253 return (COLOR_PAIR (p->index));
256 void mutt_free_color (int fg, int bg)
262 if (p->fg == fg && p->bg == bg) {
268 debug_print (1, ("Color pairs used so far: %d\n", UserColors));
270 if (p == ColorList) {
271 ColorList = ColorList->next;
290 #endif /* HAVE_COLOR */
296 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
300 if (str_ncasecmp (s, "bright", 6) == 0) {
305 /* allow aliases for xterm color resources */
306 if (str_ncasecmp (s, "color", 5) == 0) {
308 *col = strtol (s, &eptr, 10);
309 if (!*s || *eptr || *col < 0 ||
310 (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
311 snprintf (err->data, err->dsize, _("%s: color not supported by term"),
316 else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
317 snprintf (err->data, err->dsize, _("%s: no such color"), s);
327 /* usage: uncolor index pattern [pattern...]
328 * unmono index pattern [pattern...]
332 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
333 BUFFER * err, short parse_uncolor);
338 int mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
341 return _mutt_parse_uncolor (buf, s, data, err, 1);
346 int mutt_parse_unmono (BUFFER * buf, BUFFER * s, unsigned long data,
349 return _mutt_parse_uncolor (buf, s, data, err, 0);
353 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
354 BUFFER * err, short parse_uncolor)
356 int object = 0, do_cache = 0;
357 COLOR_LINE *tmp, *last = NULL;
359 mutt_extract_token (buf, s, 0);
361 if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
362 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
366 if (str_ncmp (buf->data, "index", 5) != 0) {
367 snprintf (err->data, err->dsize,
368 _("%s: command valid only for index object"),
369 parse_uncolor ? "uncolor" : "unmono");
374 snprintf (err->data, err->dsize,
375 _("%s: too few arguments"),
376 parse_uncolor ? "uncolor" : "unmono");
382 /* we're running without curses */
384 || /* we're parsing an uncolor command, and have no colors */
385 (parse_uncolor && !has_colors ())
386 /* we're parsing an unmono command, and have colors */
387 || (!parse_uncolor && has_colors ())
389 /* We don't even have colors compiled in */
393 /* just eat the command, but don't do anything real about it */
395 mutt_extract_token (buf, s, 0);
396 while (MoreArgs (s));
403 mutt_extract_token (buf, s, 0);
404 if (!str_cmp ("*", buf->data)) {
405 for (tmp = ColorIndexList; tmp;) {
410 mutt_free_color_line (&last, parse_uncolor);
412 ColorIndexList = NULL;
415 for (last = NULL, tmp = ColorIndexList; tmp;
416 last = tmp, tmp = tmp->next) {
417 if (!str_cmp (buf->data, tmp->pattern)) {
420 debug_print (1, ("Freeing pattern \"%s\" from ColorIndexList\n", tmp->pattern));
422 last->next = tmp->next;
424 ColorIndexList = tmp->next;
425 mutt_free_color_line (&tmp, parse_uncolor);
431 while (MoreArgs (s));
434 if (do_cache && !option (OPTNOCURSES)) {
437 set_option (OPTFORCEREDRAWINDEX);
438 /* force re-caching of index colors */
439 for (i = 0; Context && i < Context->msgcount; i++)
440 Context->hdrs[i]->pair = 0;
447 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
448 int fg, int bg, int attr, BUFFER * err, int is_index)
451 /* is_index used to store compiled pattern
452 * only for `index' color object
453 * when called from mutt_parse_color() */
455 COLOR_LINE *tmp = *top;
459 if (str_cmp (s, tmp->pattern) == 0)
463 if (str_casecmp (s, tmp->pattern) == 0)
471 if (fg != -1 && bg != -1) {
472 if (tmp->fg != fg || tmp->bg != bg) {
473 mutt_free_color (tmp->fg, tmp->bg);
476 attr |= mutt_alloc_color (fg, bg);
479 attr |= (tmp->pair & ~A_BOLD);
481 #endif /* HAVE_COLOR */
488 tmp = mutt_new_color_line ();
492 strfcpy (buf, NONULL (s), sizeof (buf));
493 mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
494 if ((tmp->color_pattern =
495 mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
496 mutt_free_color_line (&tmp, 1);
499 /* force re-caching of index colors */
500 for (i = 0; Context && i < Context->msgcount; i++)
501 Context->hdrs[i]->pair = 0;
505 REGCOMP (&tmp->rx, s,
506 (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
507 regerror (r, &tmp->rx, err->data, err->dsize);
508 mutt_free_color_line (&tmp, 1);
512 tmp->pattern = str_dup (s);
514 if (fg != -1 && bg != -1) {
517 attr |= mutt_alloc_color (fg, bg);
528 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
534 strfcpy (err->data, _("Missing arguments."), err->dsize);
538 mutt_extract_token (buf, s, 0);
539 if (!str_ncmp (buf->data, "quoted", 6)) {
541 *ql = strtol (buf->data + 6, &eptr, 10);
542 if (*eptr || q_level < 0) {
543 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
550 *o = MT_COLOR_QUOTED;
552 else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
553 snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
560 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
566 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
570 strfcpy (err->data, _("color: too few arguments"), err->dsize);
574 mutt_extract_token (buf, s, 0);
576 if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
580 strfcpy (err->data, _("color: too few arguments"), err->dsize);
584 mutt_extract_token (buf, s, 0);
586 if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
595 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
605 strfcpy (err->data, _("mono: too few arguments"), err->dsize);
609 mutt_extract_token (buf, s, 0);
611 if (ascii_strcasecmp ("bold", buf->data) == 0)
613 else if (ascii_strcasecmp ("underline", buf->data) == 0)
614 *attr |= A_UNDERLINE;
615 else if (ascii_strcasecmp ("none", buf->data) == 0)
617 else if (ascii_strcasecmp ("reverse", buf->data) == 0)
619 else if (ascii_strcasecmp ("standout", buf->data) == 0)
621 else if (ascii_strcasecmp ("normal", buf->data) == 0)
622 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
624 snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
631 static int fgbgattr_to_color (int fg, int bg, int attr)
634 if (fg != -1 && bg != -1)
635 return attr | mutt_alloc_color (fg, bg);
641 /* usage: color <object> <fg> <bg> [ <regexp> ]
642 * mono <object> <attr> [ <regexp> ]
646 _mutt_parse_color (BUFFER * buf, BUFFER * s, BUFFER * err,
647 parser_callback_t callback, short dry_run)
649 int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
652 if (parse_object (buf, s, &object, &q_level, err) == -1)
655 if (callback (buf, s, &fg, &bg, &attr, err) == -1)
658 /* extract a regular expression if needed */
660 if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
661 || object == MT_COLOR_INDEX) {
663 strfcpy (err->data, _("too few arguments"), err->dsize);
667 mutt_extract_token (buf, s, 0);
671 strfcpy (err->data, _("too many arguments"), err->dsize);
682 # ifdef HAVE_USE_DEFAULT_COLORS
683 if (!option (OPTNOCURSES) && has_colors ()
684 /* delay use_default_colors() until needed, since it initializes things */
685 && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
686 && use_default_colors () != OK) {
687 strfcpy (err->data, _("default colors not supported"), err->dsize);
690 # endif /* HAVE_USE_DEFAULT_COLORS */
693 if (object == MT_COLOR_HEADER)
694 r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
695 else if (object == MT_COLOR_BODY)
696 r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
697 else if (object == MT_COLOR_INDEX) {
698 r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
699 set_option (OPTFORCEREDRAWINDEX);
701 else if (object == MT_COLOR_QUOTED) {
702 if (q_level >= ColorQuoteSize) {
703 mem_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
704 ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
705 ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
707 if (q_level >= ColorQuoteUsed)
708 ColorQuoteUsed = q_level + 1;
710 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
712 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
713 for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
714 if (ColorQuote[q_level] == A_NORMAL)
715 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
719 ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
722 ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
726 if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
727 BKGDSET (MT_COLOR_NORMAL);
736 int mutt_parse_color (BUFFER * buff, BUFFER * s, unsigned long data,
741 if (option (OPTNOCURSES) || !has_colors ())
744 return _mutt_parse_color (buff, s, err, parse_color_pair, dry_run);
749 int mutt_parse_mono (BUFFER * buff, BUFFER * s, unsigned long data,
755 if (option (OPTNOCURSES) || has_colors ())
758 if (option (OPTNOCURSES))
762 return _mutt_parse_color (buff, s, err, parse_attr_spec, dry_run);