3 * Color and attribute parsing
6 * Copyright (C) 1996-2002,2012 Michael R. Elkins <me@mutt.org>
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 2 of the License, or (at your option) any later
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
24 * @page color Color and attribute parsing
26 * Color and attribute parsing
35 #include "mutt/mutt.h"
36 #include "email/lib.h"
43 #include "mutt_commands.h"
44 #include "mutt_curses.h"
45 #include "mutt_menu.h"
48 #ifdef USE_SLANG_CURSES
53 int *ColorQuote = NULL;
55 int ColorDefs[MT_COLOR_MAX];
56 struct ColorLineList ColorAttachList = STAILQ_HEAD_INITIALIZER(ColorAttachList);
57 struct ColorLineList ColorBodyList = STAILQ_HEAD_INITIALIZER(ColorBodyList);
58 struct ColorLineList ColorHdrList = STAILQ_HEAD_INITIALIZER(ColorHdrList);
59 struct ColorLineList ColorIndexAuthorList = STAILQ_HEAD_INITIALIZER(ColorIndexAuthorList);
60 struct ColorLineList ColorIndexFlagsList = STAILQ_HEAD_INITIALIZER(ColorIndexFlagsList);
61 struct ColorLineList ColorIndexList = STAILQ_HEAD_INITIALIZER(ColorIndexList);
62 struct ColorLineList ColorIndexSubjectList = STAILQ_HEAD_INITIALIZER(ColorIndexSubjectList);
63 struct ColorLineList ColorIndexTagList = STAILQ_HEAD_INITIALIZER(ColorIndexTagList);
64 struct ColorLineList ColorStatusList = STAILQ_HEAD_INITIALIZER(ColorStatusList);
66 /* local to this file */
67 static int ColorQuoteSize;
71 #define COLOR_DEFAULT (-2)
72 #define COLOR_UNSET UINT32_MAX
75 * Flags for the high 8bits of the color value.
77 * Note that no flag means it's a palette color.
79 #define RGB24 (1u << 24)
82 * struct ColorList - A set of colors
86 /* TrueColor uses 24bit. Use fixed-width integer type to make sure it fits.
87 * Use the upper 8 bits to store flags. */
92 struct ColorList *next;
95 static struct ColorList *ColorList = NULL;
96 static int UserColors = 0;
98 static const struct Mapping Colors[] = {
99 { "black", COLOR_BLACK },
100 { "blue", COLOR_BLUE },
101 { "cyan", COLOR_CYAN },
102 { "green", COLOR_GREEN },
103 { "magenta", COLOR_MAGENTA },
104 { "red", COLOR_RED },
105 { "white", COLOR_WHITE },
106 { "yellow", COLOR_YELLOW },
107 #if defined(USE_SLANG_CURSES) || defined(HAVE_USE_DEFAULT_COLORS)
108 { "default", COLOR_DEFAULT },
113 #endif /* HAVE_COLOR */
115 static const struct Mapping Fields[] = {
116 { "attachment", MT_COLOR_ATTACHMENT },
117 { "attach_headers", MT_COLOR_ATTACH_HEADERS },
118 { "body", MT_COLOR_BODY },
119 { "bold", MT_COLOR_BOLD },
120 { "error", MT_COLOR_ERROR },
121 { "hdrdefault", MT_COLOR_HDEFAULT },
122 { "header", MT_COLOR_HEADER },
123 { "index", MT_COLOR_INDEX },
124 { "index_author", MT_COLOR_INDEX_AUTHOR },
125 { "index_collapsed", MT_COLOR_INDEX_COLLAPSED },
126 { "index_date", MT_COLOR_INDEX_DATE },
127 { "index_flags", MT_COLOR_INDEX_FLAGS },
128 { "index_label", MT_COLOR_INDEX_LABEL },
129 { "index_number", MT_COLOR_INDEX_NUMBER },
130 { "index_size", MT_COLOR_INDEX_SIZE },
131 { "index_subject", MT_COLOR_INDEX_SUBJECT },
132 { "index_tag", MT_COLOR_INDEX_TAG },
133 { "index_tags", MT_COLOR_INDEX_TAGS },
134 { "indicator", MT_COLOR_INDICATOR },
135 { "markers", MT_COLOR_MARKERS },
136 { "message", MT_COLOR_MESSAGE },
137 { "normal", MT_COLOR_NORMAL },
138 { "options", MT_COLOR_OPTIONS },
139 { "progress", MT_COLOR_PROGRESS },
140 { "prompt", MT_COLOR_PROMPT },
141 { "quoted", MT_COLOR_QUOTED },
142 { "search", MT_COLOR_SEARCH },
144 { "sidebar_divider", MT_COLOR_DIVIDER },
145 { "sidebar_flagged", MT_COLOR_FLAGGED },
146 { "sidebar_highlight", MT_COLOR_HIGHLIGHT },
147 { "sidebar_indicator", MT_COLOR_SB_INDICATOR },
148 { "sidebar_new", MT_COLOR_NEW },
149 { "sidebar_ordinary", MT_COLOR_ORDINARY },
150 { "sidebar_spoolfile", MT_COLOR_SB_SPOOLFILE },
152 { "signature", MT_COLOR_SIGNATURE },
153 { "status", MT_COLOR_STATUS },
154 { "tilde", MT_COLOR_TILDE },
155 { "tree", MT_COLOR_TREE },
156 { "underline", MT_COLOR_UNDERLINE },
157 { "warning", MT_COLOR_WARNING },
161 static const struct Mapping ComposeFields[] = {
162 { "header", MT_COLOR_COMPOSE_HEADER },
163 { "security_encrypt", MT_COLOR_COMPOSE_SECURITY_ENCRYPT },
164 { "security_sign", MT_COLOR_COMPOSE_SECURITY_SIGN },
165 { "security_both", MT_COLOR_COMPOSE_SECURITY_BOTH },
166 { "security_none", MT_COLOR_COMPOSE_SECURITY_NONE },
170 #define COLOR_QUOTE_INIT 8
173 * color_line_new - Create a new ColorLine
174 * @retval ptr Newly allocated ColorLine
176 static struct ColorLine *color_line_new(void)
178 struct ColorLine *p = mutt_mem_calloc(1, sizeof(struct ColorLine));
187 * color_line_free - Free a ColorLine
188 * @param ptr ColorLine to free
189 * @param free_colors If true, free its colours too
191 static void color_line_free(struct ColorLine **ptr, bool free_colors)
196 struct ColorLine *cl = *ptr;
199 if (free_colors && (cl->fg != COLOR_UNSET) && (cl->bg != COLOR_UNSET))
200 mutt_color_free(cl->fg, cl->bg);
204 mutt_pattern_free(&cl->color_pattern);
210 * mutt_color_init - Set up the default colours
212 void mutt_color_init(void)
214 memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX);
215 ColorQuote = mutt_mem_malloc(COLOR_QUOTE_INIT * sizeof(int));
216 memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT);
217 ColorQuoteSize = COLOR_QUOTE_INIT;
220 /* set some defaults */
221 ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
222 ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
223 ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
224 ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
226 ColorDefs[MT_COLOR_HIGHLIGHT] = A_UNDERLINE;
228 /* special meaning: toggle the relevant attribute */
229 ColorDefs[MT_COLOR_BOLD] = 0;
230 ColorDefs[MT_COLOR_UNDERLINE] = 0;
239 #ifdef USE_SLANG_CURSES
241 * get_color_name - Get a colour's name from its ID
242 * @param dest Buffer for the result
243 * @param destlen Length of buffer
244 * @param val Colour ID to look up
245 * @retval ptr Pointer to the results buffer
247 static char *get_color_name(char *dest, size_t destlen, uint32_t val)
249 static const char *const missing[3] = { "brown", "lightgray", "default" };
253 assert(snprintf(dest, destlen, "#%06X", val & 0xffffff) == 7);
260 mutt_str_strfcpy(dest, missing[0], destlen);
264 mutt_str_strfcpy(dest, missing[1], destlen);
268 mutt_str_strfcpy(dest, missing[2], destlen);
272 for (int i = 0; Colors[i].name; i++)
274 if (Colors[i].value == val)
276 mutt_str_strfcpy(dest, Colors[i].name, destlen);
281 /* Sigh. If we got this far, the color is of the form 'colorN'
282 * Slang can handle this itself, so just return 'colorN' */
283 snprintf(dest, destlen, "color%d", val);
290 * mutt_color_alloc - Allocate a colour pair
291 * @param fg Foreground colour ID
292 * @param bg Background colour ID
293 * @retval num Combined colour pair
295 int mutt_color_alloc(uint32_t fg, uint32_t bg)
297 #ifdef USE_SLANG_CURSES
298 char fgc[128], bgc[128];
300 struct ColorList *p = ColorList;
303 /* check to see if this color is already allocated to save space */
306 if ((p->fg == fg) && (p->bg == bg))
309 return COLOR_PAIR(p->index);
314 /* check to see if there are colors left */
315 if (++UserColors > COLOR_PAIRS)
318 /* find the smallest available index (object) */
334 p = mutt_mem_malloc(sizeof(struct ColorList));
343 #ifdef USE_SLANG_CURSES
345 * If using s-lang always use SLtt_set_color which allows using truecolor
346 * values. Note that I couldn't figure out if s-lang somehow reports
349 SLtt_set_color(i, NULL, get_color_name(fgc, sizeof(fgc), fg),
350 get_color_name(bgc, sizeof(bgc), bg));
352 #ifdef HAVE_USE_DEFAULT_COLORS
353 if (fg == COLOR_DEFAULT)
355 if (bg == COLOR_DEFAULT)
358 init_pair(i, fg, bg);
361 mutt_debug(LL_DEBUG3, "Color pairs used so far: %d\n", UserColors);
363 return COLOR_PAIR(p->index);
367 * mutt_lookup_color - Get the colours from a colour pair
368 * @param[in] pair Colour pair
369 * @param[out] fg Foreground colour (OPTIONAL)
370 * @param[out] bg Background colour (OPTIONAL)
374 static int mutt_lookup_color(short pair, uint32_t *fg, uint32_t *bg)
376 struct ColorList *p = ColorList;
380 if (COLOR_PAIR(p->index) == pair)
394 * mutt_color_combine - Combine two colours
395 * @param fg_attr Colour pair of foreground to use
396 * @param bg_attr Colour pair of background to use
397 * @retval num Colour pair of combined colour
399 int mutt_color_combine(uint32_t fg_attr, uint32_t bg_attr)
401 uint32_t fg = COLOR_DEFAULT;
402 uint32_t bg = COLOR_DEFAULT;
404 mutt_lookup_color(fg_attr, &fg, NULL);
405 mutt_lookup_color(bg_attr, NULL, &bg);
407 if ((fg == COLOR_DEFAULT) && (bg == COLOR_DEFAULT))
409 return mutt_color_alloc(fg, bg);
413 * mutt_color_free - Free a colour
414 * @param fg Foreground colour ID
415 * @param bg Background colour ID
417 * If there are no more users, the resource will be freed.
419 void mutt_color_free(uint32_t fg, uint32_t bg)
421 struct ColorList *q = NULL;
423 struct ColorList *p = ColorList;
426 if ((p->fg == fg) && (p->bg == bg))
433 mutt_debug(LL_DEBUG1, "Color pairs used so far: %d\n", UserColors);
437 ColorList = ColorList->next;
458 #endif /* HAVE_COLOR */
463 * parse_color_name - Parse a colour name
464 * @param[in] s String to parse
465 * @param[out] col Number for 'colorNNN' colours
466 * @param[out] attr Attribute flags, e.g. A_BOLD
467 * @param[in] is_fg true if this is a foreground colour
468 * @param[out] err Buffer for error messages
472 * Parse a colour name, such as "red", "brightgreen", "color123".
474 static int parse_color_name(const char *s, uint32_t *col, int *attr, bool is_fg,
478 bool is_alert = false, is_bright = false, is_light = false;
481 if ((clen = mutt_str_startswith(s, "bright", CASE_IGNORE)))
486 else if ((clen = mutt_str_startswith(s, "alert", CASE_IGNORE)))
492 else if ((clen = mutt_str_startswith(s, "light", CASE_IGNORE)))
498 /* allow aliases for xterm color resources */
499 if ((clen = mutt_str_startswith(s, "color", CASE_IGNORE)))
502 *col = strtoul(s, &eptr, 10);
503 if (!*s || *eptr || ((*col >= COLORS) && !OptNoCurses && has_colors()))
505 mutt_buffer_printf(err, _("%s: color not supported by term"), s);
509 #ifdef HAVE_DIRECTCOLOR
513 *col = strtoul(s, &eptr, 16);
514 if (!*s || *eptr || ((*col == COLOR_UNSET) && !OptNoCurses && has_colors()))
516 snprintf(err->data, err->dsize, _("%s: color not supported by term"), s);
522 else if ((*col = mutt_map_get_value(s, Colors)) == -1)
524 mutt_buffer_printf(err, _("%s: no such color"), s);
528 if (is_bright || is_light)
537 if ((COLORS >= 16) && is_light)
541 /* Advance the color 0-7 by 8 to get the light version */
550 else if (!(*col & RGB24))
556 /* Advance the color 0-7 by 8 to get the light version */
569 * do_uncolor - Parse the "uncolor" or "unmono" command
570 * @param[in] buf Buffer for temporary storage
571 * @param[in] s Buffer containing the uncolor command
572 * @param[in] cl List of existing colours
573 * @param[in,out] do_cache Set to true if colours were freed
574 * @param[in] parse_uncolor If true, 'uncolor', else 'unmono'
576 static void do_uncolor(struct Buffer *buf, struct Buffer *s,
577 struct ColorLineList *cl, bool *do_cache, bool parse_uncolor)
579 struct ColorLine *np = NULL, *tmp = NULL;
582 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
583 if (mutt_str_strcmp("*", buf->data) == 0)
585 np = STAILQ_FIRST(cl);
588 tmp = STAILQ_NEXT(np, entries);
593 color_line_free(&np, parse_uncolor);
602 STAILQ_FOREACH(np, cl, entries)
604 if (mutt_str_strcmp(buf->data, np->pattern) == 0)
610 mutt_debug(LL_DEBUG1, "Freeing pattern \"%s\" from ColorList\n", buf->data);
612 STAILQ_REMOVE_AFTER(cl, tmp, entries);
614 STAILQ_REMOVE_HEAD(cl, entries);
615 color_line_free(&np, parse_uncolor);
621 } while (MoreArgs(s));
625 * parse_uncolor - Parse an 'uncolor' command
626 * @param buf Temporary Buffer space
627 * @param s Buffer containing string to be parsed
628 * @param data Flags associated with the command
629 * @param err Buffer for error messages
630 * @param parse_uncolor If true, 'uncolor', else 'unmono'
631 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
634 * * uncolor index pattern [pattern...]
635 * * unmono index pattern [pattern...]
637 static enum CommandResult parse_uncolor(struct Buffer *buf, struct Buffer *s,
638 unsigned long data, struct Buffer *err, bool parse_uncolor)
641 bool do_cache = false;
643 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
645 object = mutt_map_get_value(buf->data, Fields);
648 mutt_buffer_printf(err, _("%s: no such object"), buf->data);
649 return MUTT_CMD_ERROR;
652 if (object > MT_COLOR_INDEX_SUBJECT)
653 { /* uncolor index column */
654 ColorDefs[object] = 0;
655 mutt_menu_set_redraw_full(MENU_MAIN);
656 return MUTT_CMD_SUCCESS;
659 if (!mutt_str_startswith(buf->data, "body", CASE_MATCH) &&
660 !mutt_str_startswith(buf->data, "header", CASE_MATCH) &&
661 !mutt_str_startswith(buf->data, "index", CASE_MATCH))
663 mutt_buffer_printf(err, _("%s: command valid only for index, body, header objects"),
664 parse_uncolor ? "uncolor" : "unmono");
665 return MUTT_CMD_WARNING;
670 mutt_buffer_printf(err, _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono");
671 return MUTT_CMD_WARNING;
676 /* we're running without curses */
677 OptNoCurses || /* we're parsing an uncolor command, and have no colors */
678 (parse_uncolor && !has_colors())
679 /* we're parsing an unmono command, and have colors */
680 || (!parse_uncolor && has_colors())
682 /* We don't even have colors compiled in */
687 /* just eat the command, but don't do anything real about it */
690 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
691 } while (MoreArgs(s));
693 return MUTT_CMD_SUCCESS;
696 if (object == MT_COLOR_BODY)
697 do_uncolor(buf, s, &ColorBodyList, &do_cache, parse_uncolor);
698 else if (object == MT_COLOR_HEADER)
699 do_uncolor(buf, s, &ColorHdrList, &do_cache, parse_uncolor);
700 else if (object == MT_COLOR_ATTACH_HEADERS)
701 do_uncolor(buf, s, &ColorAttachList, &do_cache, parse_uncolor);
702 else if (object == MT_COLOR_INDEX)
703 do_uncolor(buf, s, &ColorIndexList, &do_cache, parse_uncolor);
704 else if (object == MT_COLOR_INDEX_AUTHOR)
705 do_uncolor(buf, s, &ColorIndexAuthorList, &do_cache, parse_uncolor);
706 else if (object == MT_COLOR_INDEX_FLAGS)
707 do_uncolor(buf, s, &ColorIndexFlagsList, &do_cache, parse_uncolor);
708 else if (object == MT_COLOR_INDEX_SUBJECT)
709 do_uncolor(buf, s, &ColorIndexSubjectList, &do_cache, parse_uncolor);
710 else if (object == MT_COLOR_INDEX_TAG)
711 do_uncolor(buf, s, &ColorIndexTagList, &do_cache, parse_uncolor);
713 bool is_index = ((object == MT_COLOR_INDEX) || (object == MT_COLOR_INDEX_AUTHOR) ||
714 (object == MT_COLOR_INDEX_FLAGS) || (object == MT_COLOR_INDEX_SUBJECT) ||
715 (object == MT_COLOR_INDEX_TAG));
717 if (is_index && do_cache && !OptNoCurses)
719 mutt_menu_set_redraw_full(MENU_MAIN);
720 /* force re-caching of index colors */
721 for (int i = 0; Context && i < Context->mailbox->msg_count; i++)
722 Context->mailbox->emails[i]->pair = 0;
724 return MUTT_CMD_SUCCESS;
730 * mutt_parse_uncolor - Parse the 'uncolor' command - Implements ::command_t
732 enum CommandResult mutt_parse_uncolor(struct Buffer *buf, struct Buffer *s,
733 unsigned long data, struct Buffer *err)
735 return parse_uncolor(buf, s, data, err, true);
741 * mutt_parse_unmono - Parse the 'unmono' command - Implements ::command_t
743 enum CommandResult mutt_parse_unmono(struct Buffer *buf, struct Buffer *s,
744 unsigned long data, struct Buffer *err)
746 return parse_uncolor(buf, s, data, err, false);
750 * add_pattern - Associate a colour to a pattern
751 * @param top List of existing colours
752 * @param s String to match
753 * @param sensitive true if the pattern case-sensitive
754 * @param fg Foreground colour ID
755 * @param bg Background colour ID
756 * @param attr Attribute flags, e.g. A_BOLD
757 * @param err Buffer for error messages
758 * @param is_index true of this is for the index
759 * @param match Number of regex subexpression to match (0 for entire pattern)
760 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
762 static enum CommandResult add_pattern(struct ColorLineList *top, const char *s,
763 bool sensitive, uint32_t fg, uint32_t bg, int attr,
764 struct Buffer *err, bool is_index, int match)
766 /* is_index used to store compiled pattern
767 * only for 'index' color object
768 * when called from mutt_parse_color() */
770 struct ColorLine *tmp = NULL;
772 STAILQ_FOREACH(tmp, top, entries)
776 if (mutt_str_strcmp(s, tmp->pattern) == 0)
781 if (mutt_str_strcasecmp(s, tmp->pattern) == 0)
789 if ((fg != COLOR_UNSET) && (bg != COLOR_UNSET))
791 if ((tmp->fg != fg) || (tmp->bg != bg))
793 mutt_color_free(tmp->fg, tmp->bg);
796 attr |= mutt_color_alloc(fg, bg);
799 attr |= (tmp->pair & ~A_BOLD);
801 #endif /* HAVE_COLOR */
806 tmp = color_line_new();
809 struct Buffer *buf = mutt_buffer_pool_get();
810 mutt_buffer_strcpy(buf, s);
811 mutt_check_simple(buf, NONULL(C_SimpleSearch));
812 tmp->color_pattern = mutt_pattern_comp(buf->data, MUTT_PC_FULL_MSG, err);
813 mutt_buffer_pool_release(&buf);
814 if (!tmp->color_pattern)
816 color_line_free(&tmp, true);
817 return MUTT_CMD_ERROR;
824 flags = mutt_mb_is_lower(s) ? REG_ICASE : 0;
828 const int r = REG_COMP(&tmp->regex, s, flags);
831 regerror(r, &tmp->regex, err->data, err->dsize);
832 color_line_free(&tmp, true);
833 return MUTT_CMD_ERROR;
836 tmp->pattern = mutt_str_strdup(s);
839 if ((fg != COLOR_UNSET) && (bg != COLOR_UNSET))
843 attr |= mutt_color_alloc(fg, bg);
847 STAILQ_INSERT_HEAD(top, tmp, entries);
850 /* force re-caching of index colors */
853 for (int i = 0; Context && i < Context->mailbox->msg_count; i++)
854 Context->mailbox->emails[i]->pair = 0;
857 return MUTT_CMD_SUCCESS;
861 * parse_object - Parse a colour config line
862 * @param[in] buf Temporary Buffer space
863 * @param[in] s Buffer containing string to be parsed
864 * @param[out] o Index into the fields map
865 * @param[out] ql Quote level
866 * @param[out] err Buffer for error messages
870 static int parse_object(struct Buffer *buf, struct Buffer *s, uint32_t *o,
871 uint32_t *ql, struct Buffer *err)
875 mutt_buffer_printf(err, _("%s: too few arguments"), "color");
879 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
880 if (mutt_str_startswith(buf->data, "quoted", CASE_MATCH))
885 *ql = strtoul(buf->data + 6, &eptr, 10);
886 if (*eptr || (*ql == COLOR_UNSET))
888 mutt_buffer_printf(err, _("%s: no such object"), buf->data);
895 *o = MT_COLOR_QUOTED;
897 else if (mutt_str_strcasecmp(buf->data, "compose") == 0)
901 mutt_buffer_printf(err, _("%s: too few arguments"), "color");
905 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
907 *o = mutt_map_get_value(buf->data, ComposeFields);
910 mutt_buffer_printf(err, _("%s: no such object"), buf->data);
914 else if ((*o = mutt_map_get_value(buf->data, Fields)) == -1)
916 mutt_buffer_printf(err, _("%s: no such object"), buf->data);
924 * typedef parser_callback_t - Prototype for a function to parse color config
925 * @param[in] buf Temporary Buffer space
926 * @param[in] s Buffer containing string to be parsed
927 * @param[out] fg Foreground colour (set to -1)
928 * @param[out] bg Background colour (set to -1)
929 * @param[out] attr Attribute flags
930 * @param[out] err Buffer for error messages
934 typedef int (*parser_callback_t)(struct Buffer *buf, struct Buffer *s, uint32_t *fg,
935 uint32_t *bg, int *attr, struct Buffer *err);
940 * parse_color_pair - Parse a pair of colours - Implements ::parser_callback_t
942 static int parse_color_pair(struct Buffer *buf, struct Buffer *s, uint32_t *fg,
943 uint32_t *bg, int *attr, struct Buffer *err)
949 mutt_buffer_printf(err, _("%s: too few arguments"), "color");
953 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
955 if (mutt_str_strcasecmp("bold", buf->data) == 0)
957 else if (mutt_str_strcasecmp("underline", buf->data) == 0)
958 *attr |= A_UNDERLINE;
959 else if (mutt_str_strcasecmp("none", buf->data) == 0)
961 else if (mutt_str_strcasecmp("reverse", buf->data) == 0)
963 else if (mutt_str_strcasecmp("standout", buf->data) == 0)
965 else if (mutt_str_strcasecmp("normal", buf->data) == 0)
966 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
969 if (parse_color_name(buf->data, fg, attr, true, err) != 0)
977 mutt_buffer_printf(err, _("%s: too few arguments"), "color");
981 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
983 if (parse_color_name(buf->data, bg, attr, false, err) != 0)
992 * parse_attr_spec - Parse an attribute description - Implements ::parser_callback_t
994 static int parse_attr_spec(struct Buffer *buf, struct Buffer *s, uint32_t *fg,
995 uint32_t *bg, int *attr, struct Buffer *err)
1004 mutt_buffer_printf(err, _("%s: too few arguments"), "mono");
1008 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1010 if (mutt_str_strcasecmp("bold", buf->data) == 0)
1012 else if (mutt_str_strcasecmp("underline", buf->data) == 0)
1013 *attr |= A_UNDERLINE;
1014 else if (mutt_str_strcasecmp("none", buf->data) == 0)
1016 else if (mutt_str_strcasecmp("reverse", buf->data) == 0)
1018 else if (mutt_str_strcasecmp("standout", buf->data) == 0)
1019 *attr |= A_STANDOUT;
1020 else if (mutt_str_strcasecmp("normal", buf->data) == 0)
1021 *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
1024 mutt_buffer_printf(err, _("%s: no such attribute"), buf->data);
1032 * fgbgattr_to_color - Convert a foreground, background, attribute triplet into a colour
1033 * @param fg Foreground colour ID
1034 * @param bg Background colour ID
1035 * @param attr Attribute flags, e.g. A_BOLD
1036 * @retval num Combined colour pair
1038 static int fgbgattr_to_color(int fg, int bg, int attr)
1041 if ((fg != COLOR_UNSET) && (bg != COLOR_UNSET))
1042 return attr | mutt_color_alloc(fg, bg);
1049 * parse_color - Parse a "color" command
1050 * @param buf Temporary Buffer space
1051 * @param s Buffer containing string to be parsed
1052 * @param err Buffer for error messages
1053 * @param callback Function to handle command - Implements ::parser_callback_t
1054 * @param dry_run If true, test the command, but don't apply it
1055 * @param color If true "color", else "mono"
1056 * @retval #CommandResult Result e.g. #MUTT_CMD_SUCCESS
1058 * usage: color OBJECT FG BG [ REGEX ]
1059 * mono OBJECT ATTR [ REGEX ]
1061 static enum CommandResult parse_color(struct Buffer *buf, struct Buffer *s,
1062 struct Buffer *err, parser_callback_t callback,
1063 bool dry_run, bool color)
1066 uint32_t fg = 0, bg = 0, object = 0, q_level = 0, match = 0;
1067 enum CommandResult rc = MUTT_CMD_SUCCESS;
1069 if (parse_object(buf, s, &object, &q_level, err) == -1)
1070 return MUTT_CMD_ERROR;
1072 if (callback(buf, s, &fg, &bg, &attr, err) == -1)
1073 return MUTT_CMD_ERROR;
1075 /* extract a regular expression if needed */
1077 if ((object == MT_COLOR_BODY) || (object == MT_COLOR_HEADER) ||
1078 (object == MT_COLOR_ATTACH_HEADERS) || (object == MT_COLOR_INDEX) ||
1079 (object == MT_COLOR_INDEX_AUTHOR) || (object == MT_COLOR_INDEX_FLAGS) ||
1080 (object == MT_COLOR_INDEX_TAG) || (object == MT_COLOR_INDEX_SUBJECT))
1084 mutt_buffer_printf(err, _("%s: too few arguments"), color ? "color" : "mono");
1085 return MUTT_CMD_WARNING;
1088 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1091 if (MoreArgs(s) && (object != MT_COLOR_STATUS))
1093 mutt_buffer_printf(err, _("%s: too many arguments"), color ? "color" : "mono");
1094 return MUTT_CMD_WARNING;
1101 *s->dptr = '\0'; /* fake that we're done parsing */
1102 return MUTT_CMD_SUCCESS;
1106 #ifdef HAVE_USE_DEFAULT_COLORS
1109 /* delay use_default_colors() until needed, since it initializes things */
1110 && ((fg == COLOR_DEFAULT) || (bg == COLOR_DEFAULT) || (object == MT_COLOR_TREE)) &&
1111 (use_default_colors() != OK))
1112 /* the case of the tree object is special, because a non-default fg color of
1113 * the tree element may be combined dynamically with the default bg color of
1114 * an index line, not necessarily defined in a rc file. */
1116 mutt_buffer_strcpy(err, _("default colors not supported"));
1117 return MUTT_CMD_ERROR;
1119 #endif /* HAVE_USE_DEFAULT_COLORS */
1122 if (object == MT_COLOR_HEADER)
1123 rc = add_pattern(&ColorHdrList, buf->data, false, fg, bg, attr, err, false, match);
1124 else if (object == MT_COLOR_BODY)
1125 rc = add_pattern(&ColorBodyList, buf->data, true, fg, bg, attr, err, false, match);
1126 else if (object == MT_COLOR_ATTACH_HEADERS)
1127 rc = add_pattern(&ColorAttachList, buf->data, true, fg, bg, attr, err, false, match);
1128 else if ((object == MT_COLOR_STATUS) && MoreArgs(s))
1130 /* 'color status fg bg' can have up to 2 arguments:
1131 * 0 arguments: sets the default status color (handled below by else part)
1132 * 1 argument : colorize pattern on match
1133 * 2 arguments: colorize nth submatch of pattern */
1134 mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
1138 struct Buffer tmp = mutt_buffer_make(0);
1139 mutt_extract_token(&tmp, s, MUTT_TOKEN_NO_FLAGS);
1140 if (mutt_str_atoui(tmp.data, &match) < 0)
1142 mutt_buffer_printf(err, _("%s: invalid number: %s"),
1143 color ? "color" : "mono", tmp.data);
1144 mutt_buffer_dealloc(&tmp);
1145 return MUTT_CMD_WARNING;
1147 mutt_buffer_dealloc(&tmp);
1152 mutt_buffer_printf(err, _("%s: too many arguments"), color ? "color" : "mono");
1153 return MUTT_CMD_WARNING;
1156 rc = add_pattern(&ColorStatusList, buf->data, true, fg, bg, attr, err, false, match);
1158 else if (object == MT_COLOR_INDEX)
1160 rc = add_pattern(&ColorIndexList, buf->data, true, fg, bg, attr, err, true, match);
1161 mutt_menu_set_redraw_full(MENU_MAIN);
1163 else if (object == MT_COLOR_INDEX_AUTHOR)
1165 rc = add_pattern(&ColorIndexAuthorList, buf->data, true, fg, bg, attr, err, true, match);
1166 mutt_menu_set_redraw_full(MENU_MAIN);
1168 else if (object == MT_COLOR_INDEX_FLAGS)
1170 rc = add_pattern(&ColorIndexFlagsList, buf->data, true, fg, bg, attr, err, true, match);
1171 mutt_menu_set_redraw_full(MENU_MAIN);
1173 else if (object == MT_COLOR_INDEX_SUBJECT)
1175 rc = add_pattern(&ColorIndexSubjectList, buf->data, true, fg, bg, attr, err, true, match);
1176 mutt_menu_set_redraw_full(MENU_MAIN);
1178 else if (object == MT_COLOR_INDEX_TAG)
1180 rc = add_pattern(&ColorIndexTagList, buf->data, true, fg, bg, attr, err, true, match);
1181 mutt_menu_set_redraw_full(MENU_MAIN);
1183 else if (object == MT_COLOR_QUOTED)
1185 if (q_level >= ColorQuoteSize)
1187 mutt_mem_realloc(&ColorQuote, (ColorQuoteSize += 2) * sizeof(int));
1188 ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
1189 ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
1191 if (q_level >= ColorQuoteUsed)
1192 ColorQuoteUsed = q_level + 1;
1195 ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr);
1197 ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
1198 for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
1200 if (ColorQuote[q_level] == A_NORMAL)
1201 ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
1205 ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr);
1209 ColorDefs[object] = fgbgattr_to_color(fg, bg, attr);
1210 if (object > MT_COLOR_INDEX_AUTHOR)
1211 mutt_menu_set_redraw_full(MENU_MAIN);
1220 * mutt_parse_color - Parse the 'color' command - Implements ::command_t
1222 enum CommandResult mutt_parse_color(struct Buffer *buf, struct Buffer *s,
1223 unsigned long data, struct Buffer *err)
1225 bool dry_run = false;
1227 if (OptNoCurses || !has_colors())
1230 return parse_color(buf, s, err, parse_color_pair, dry_run, true);
1236 * mutt_parse_mono - Parse the 'mono' command - Implements ::command_t
1238 enum CommandResult mutt_parse_mono(struct Buffer *buf, struct Buffer *s,
1239 unsigned long data, struct Buffer *err)
1241 bool dry_run = false;
1244 if (OptNoCurses || has_colors())
1251 return parse_color(buf, s, err, parse_attr_spec, dry_run, false);
1255 * mutt_color_list_free - Free a list of colours
1256 * @param list ColorLine List
1258 static void mutt_color_list_free(struct ColorLineList *list)
1260 struct ColorLine *np = NULL, *tmp = NULL;
1261 STAILQ_FOREACH_SAFE(np, list, entries, tmp)
1263 STAILQ_REMOVE(list, np, ColorLine, entries);
1264 color_line_free(&np, true);
1269 * mutt_colors_free - Free all the colours (on shutdown)
1271 void mutt_colors_free(void)
1273 mutt_color_list_free(&ColorAttachList);
1274 mutt_color_list_free(&ColorBodyList);
1275 mutt_color_list_free(&ColorHdrList);
1276 mutt_color_list_free(&ColorIndexAuthorList);
1277 mutt_color_list_free(&ColorIndexFlagsList);
1278 mutt_color_list_free(&ColorIndexList);
1279 mutt_color_list_free(&ColorIndexSubjectList);
1280 mutt_color_list_free(&ColorIndexTagList);
1281 mutt_color_list_free(&ColorStatusList);
1283 struct ColorList *cl = ColorList;
1284 struct ColorList *next = NULL;