]> granicus.if.org Git - mutt/blob - color.c
Convert pgp_app_handler to use buffer pool.
[mutt] / color.c
1 /*
2  * Copyright (C) 1996-2002,2012 Michael R. Elkins <me@mutt.org>
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #if HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "mutt.h"
24 #include "mutt_curses.h"
25 #include "mutt_menu.h"
26 #include "mapping.h"
27
28 #include <string.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31
32 /* globals */
33 int *ColorQuote;
34 int ColorQuoteUsed;
35 int ColorDefs[MT_COLOR_MAX];
36 COLOR_LINE *ColorHdrList = NULL;
37 COLOR_LINE *ColorBodyList = NULL;
38 COLOR_LINE *ColorIndexList = NULL;
39
40 /* local to this file */
41 static int ColorQuoteSize;
42
43 #ifdef HAVE_COLOR
44
45 #define COLOR_DEFAULT (-2)
46
47 typedef struct color_list
48 {
49   short fg;
50   short bg;
51   short index;
52   short count;
53   struct color_list *next;
54 } COLOR_LIST;
55
56 static COLOR_LIST *ColorList = NULL;
57 static int UserColors = 0;
58
59 static const struct mapping_t Colors[] =
60 {
61   { "black",    COLOR_BLACK },
62   { "blue",     COLOR_BLUE },
63   { "cyan",     COLOR_CYAN },
64   { "green",    COLOR_GREEN },
65   { "magenta",  COLOR_MAGENTA },
66   { "red",      COLOR_RED },
67   { "white",    COLOR_WHITE },
68   { "yellow",   COLOR_YELLOW },
69 #if defined (USE_SLANG_CURSES) || defined (HAVE_USE_DEFAULT_COLORS)
70   { "default",  COLOR_DEFAULT },
71 #endif
72   { 0, 0 }
73 };
74
75 #endif /* HAVE_COLOR */
76
77 static const struct mapping_t Fields[] =
78 {
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 },
98 #ifdef USE_SIDEBAR
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 },
105 #endif
106   { NULL,               0 }
107 };
108
109 static const struct mapping_t ComposeFields[] =
110 {
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 },
116   { NULL,                   0 }
117 };
118
119 #define COLOR_QUOTE_INIT        8
120
121 static COLOR_LINE *mutt_new_color_line (void)
122 {
123   COLOR_LINE *p = safe_calloc (1, sizeof (COLOR_LINE));
124
125   p->fg = p->bg = -1;
126
127   return (p);
128 }
129
130 static void mutt_free_color_line(COLOR_LINE **l,
131                                  int free_colors)
132 {
133   COLOR_LINE *tmp;
134
135   if (!l || !*l)
136     return;
137
138   tmp = *l;
139
140 #ifdef HAVE_COLOR
141   if (free_colors && tmp->fg != -1 && tmp->bg != -1)
142     mutt_free_color(tmp->fg, tmp->bg);
143 #endif
144
145   /* we should really introduce a container
146    * type for regular expressions.
147    */
148
149   regfree(&tmp->rx);
150   mutt_pattern_free(&tmp->color_pattern);
151   FREE (&tmp->pattern);
152   FREE (l);             /* __FREE_CHECKED__ */
153 }
154
155 void ci_start_color (void)
156 {
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;
161   ColorQuoteUsed = 0;
162
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;
168 #ifdef USE_SIDEBAR
169   ColorDefs[MT_COLOR_HIGHLIGHT] = A_UNDERLINE;
170 #endif
171   /* special meaning: toggle the relevant attribute */
172   ColorDefs[MT_COLOR_BOLD] = 0;
173   ColorDefs[MT_COLOR_UNDERLINE] = 0;
174
175 #ifdef HAVE_COLOR
176   start_color ();
177 #endif
178 }
179
180 #ifdef HAVE_COLOR
181
182 #ifdef USE_SLANG_CURSES
183 static char *get_color_name (char *dest, size_t destlen, int val)
184 {
185   static const char * const missing[3] = {"brown", "lightgray", "default"};
186   int i;
187
188   switch (val)
189   {
190     case COLOR_YELLOW:
191       strfcpy (dest, missing[0], destlen);
192       return dest;
193
194     case COLOR_WHITE:
195       strfcpy (dest, missing[1], destlen);
196       return dest;
197
198     case COLOR_DEFAULT:
199       strfcpy (dest, missing[2], destlen);
200       return dest;
201   }
202
203   for (i = 0; Colors[i].name; i++)
204   {
205     if (Colors[i].value == val)
206     {
207       strfcpy (dest, Colors[i].name, destlen);
208       return dest;
209     }
210   }
211
212   /* Sigh. If we got this far, the color is of the form 'colorN'
213    * Slang can handle this itself, so just return 'colorN'
214    */
215
216   snprintf (dest, destlen, "color%d", val);
217   return dest;
218 }
219 #endif
220
221 int mutt_alloc_color (int fg, int bg)
222 {
223   COLOR_LIST *p = ColorList;
224   int i;
225
226 #if defined (USE_SLANG_CURSES)
227   char fgc[SHORT_STRING], bgc[SHORT_STRING];
228 #endif
229
230   /* check to see if this color is already allocated to save space */
231   while (p)
232   {
233     if (p->fg == fg && p->bg == bg)
234     {
235       (p->count)++;
236       return (COLOR_PAIR (p->index));
237     }
238     p = p->next;
239   }
240
241   /* check to see if there are colors left */
242   if (++UserColors > COLOR_PAIRS) return (A_NORMAL);
243
244   /* find the smallest available index (object) */
245   i = 1;
246   FOREVER
247   {
248     p = ColorList;
249     while (p)
250     {
251       if (p->index == i) break;
252       p = p->next;
253     }
254     if (p == NULL) break;
255     i++;
256   }
257
258   p = (COLOR_LIST *) safe_malloc (sizeof (COLOR_LIST));
259   p->next = ColorList;
260   ColorList = p;
261
262   p->index = i;
263   p->count = 1;
264   p->bg = bg;
265   p->fg = fg;
266
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));
270   else
271 #elif defined (HAVE_USE_DEFAULT_COLORS)
272     if (fg == COLOR_DEFAULT)
273       fg = -1;
274   if (bg == COLOR_DEFAULT)
275     bg = -1;
276 #endif
277
278   init_pair(i, fg, bg);
279
280   dprint (3, (debugfile,"mutt_alloc_color(): Color pairs used so far: %d\n",
281               UserColors));
282
283   return (COLOR_PAIR (p->index));
284 }
285
286 void mutt_free_color (int fg, int bg)
287 {
288   COLOR_LIST *p, *q;
289
290   p = ColorList;
291   while (p)
292   {
293     if (p->fg == fg && p->bg == bg)
294     {
295       (p->count)--;
296       if (p->count > 0) return;
297
298       UserColors--;
299       dprint(1,(debugfile,"mutt_free_color(): Color pairs used so far: %d\n",
300                 UserColors));
301
302       if (p == ColorList)
303       {
304         ColorList = ColorList->next;
305         FREE (&p);
306         return;
307       }
308       q = ColorList;
309       while (q)
310       {
311         if (q->next == p)
312         {
313           q->next = p->next;
314           FREE (&p);
315           return;
316         }
317         q = q->next;
318       }
319       /* can't get here */
320     }
321     p = p->next;
322   }
323 }
324
325 #endif /* HAVE_COLOR */
326
327
328 #ifdef HAVE_COLOR
329
330 static int
331 parse_color_name (const char *s, int *col, int *attr, int is_fg, BUFFER *err)
332 {
333   char *eptr;
334   int is_bright = 0, is_light = 0;
335
336   if (ascii_strncasecmp (s, "bright", 6) == 0)
337   {
338     is_bright = 1;
339     s += 6;
340   }
341   else if (ascii_strncasecmp (s, "light", 5) == 0)
342   {
343     is_light = 1;
344     s += 5;
345   }
346
347   /* allow aliases for xterm color resources */
348   if (ascii_strncasecmp (s, "color", 5) == 0)
349   {
350     s += 5;
351     *col = strtol (s, &eptr, 10);
352     if (!*s || *eptr || *col < 0 ||
353         (*col >= COLORS && !option(OPTNOCURSES) && has_colors()))
354     {
355       snprintf (err->data, err->dsize, _("%s: color not supported by term"), s);
356       return (-1);
357     }
358   }
359   else if ((*col = mutt_getvaluebyname (s, Colors)) == -1)
360   {
361     snprintf (err->data, err->dsize, _("%s: no such color"), s);
362     return (-1);
363   }
364
365   if (is_bright || is_light)
366   {
367     if (is_fg)
368     {
369       if ((COLORS >= 16) && is_light)
370       {
371         if (*col >= 0 && *col <= 7)
372         {
373           /* Advance the color 0-7 by 8 to get the light version */
374           *col += 8;
375         }
376       }
377       else
378       {
379         *attr |= A_BOLD;
380       }
381     }
382     else
383     {
384       if (COLORS >= 16)
385       {
386         if (*col >= 0 && *col <= 7)
387         {
388           /* Advance the color 0-7 by 8 to get the light version */
389           *col += 8;
390         }
391       }
392     }
393   }
394
395   return 0;
396 }
397
398 #endif
399
400
401 /* usage: uncolor index pattern [pattern...]
402  *        unmono  index pattern [pattern...]
403  */
404
405 static int
406 _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, BUFFER *err, short parse_uncolor);
407
408
409 #ifdef HAVE_COLOR
410
411 int mutt_parse_uncolor (BUFFER *buf, BUFFER *s, union pointer_long_t udata,
412                         BUFFER *err)
413 {
414   return _mutt_parse_uncolor(buf, s, err, 1);
415 }
416
417 #endif
418
419 int mutt_parse_unmono (BUFFER *buf, BUFFER *s, union pointer_long_t udata,
420                        BUFFER *err)
421 {
422   return _mutt_parse_uncolor(buf, s, err, 0);
423 }
424
425 static int _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, BUFFER *err, short parse_uncolor)
426 {
427   int object = 0, is_index = 0, do_cache = 0;
428   COLOR_LINE *tmp, *last = NULL;
429   COLOR_LINE **list;
430
431   mutt_extract_token (buf, s, 0);
432
433   if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1)
434   {
435     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
436     return (-1);
437   }
438
439   if (mutt_strncmp (buf->data, "index", 5) == 0)
440   {
441     is_index = 1;
442     list = &ColorIndexList;
443   }
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;
448   else
449   {
450     snprintf (err->data, err->dsize,
451               _("%s: command valid only for index, body, header objects"),
452               parse_uncolor ? "uncolor" : "unmono");
453     return (-1);
454   }
455
456   if (!MoreArgs (s))
457   {
458     snprintf (err->data, err->dsize,
459               _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono");
460     return (-1);
461   }
462
463   if (
464 #ifdef HAVE_COLOR
465     /* we're running without curses */
466     option (OPTNOCURSES)
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())
471 #else
472     /* We don't even have colors compiled in */
473     parse_uncolor
474 #endif
475     )
476   {
477     /* just eat the command, but don't do anything real about it */
478     do
479       mutt_extract_token (buf, s, 0);
480     while (MoreArgs (s));
481
482     return 0;
483   }
484
485   do
486   {
487     mutt_extract_token (buf, s, 0);
488     if (!mutt_strcmp ("*", buf->data))
489     {
490       for (tmp = *list; tmp; )
491       {
492         if (!do_cache)
493           do_cache = 1;
494         last = tmp;
495         tmp = tmp->next;
496         mutt_free_color_line(&last, parse_uncolor);
497       }
498       *list = NULL;
499     }
500     else
501     {
502       for (last = NULL, tmp = *list; tmp; last = tmp, tmp = tmp->next)
503       {
504         if (!mutt_strcmp (buf->data, tmp->pattern))
505         {
506           if (!do_cache)
507             do_cache = 1;
508           dprint(1,(debugfile,"Freeing pattern \"%s\" from color list\n",
509                     tmp->pattern));
510           if (last)
511             last->next = tmp->next;
512           else
513             *list = tmp->next;
514           mutt_free_color_line(&tmp, parse_uncolor);
515           break;
516         }
517       }
518     }
519   }
520   while (MoreArgs (s));
521
522
523   if (is_index && do_cache && !option (OPTNOCURSES))
524   {
525     int i;
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;
530   }
531   return (0);
532 }
533
534
535 static int
536 add_pattern (COLOR_LINE **top, const char *s, int sensitive,
537              int fg, int bg, int attr, BUFFER *err,
538              int is_index)
539 {
540
541   /* is_index used to store compiled pattern
542    * only for `index' color object
543    * when called from mutt_parse_color() */
544
545   COLOR_LINE *tmp = *top;
546
547   while (tmp)
548   {
549     if (sensitive)
550     {
551       if (mutt_strcmp (s, tmp->pattern) == 0)
552         break;
553     }
554     else
555     {
556       if (mutt_strcasecmp (s, tmp->pattern) == 0)
557         break;
558     }
559     tmp = tmp->next;
560   }
561
562   if (tmp)
563   {
564 #ifdef HAVE_COLOR
565     if (fg != -1 && bg != -1)
566     {
567       if (tmp->fg != fg || tmp->bg != bg)
568       {
569         mutt_free_color (tmp->fg, tmp->bg);
570         tmp->fg = fg;
571         tmp->bg = bg;
572         attr |= mutt_alloc_color (fg, bg);
573       }
574       else
575         attr |= (tmp->pair & ~A_BOLD);
576     }
577 #endif /* HAVE_COLOR */
578     tmp->pair = attr;
579   }
580   else
581   {
582     int r;
583     BUFFER *buf = NULL;
584
585     tmp = mutt_new_color_line ();
586     if (is_index)
587     {
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)
594       {
595         mutt_free_color_line(&tmp, 1);
596         return -1;
597       }
598     }
599     else if ((r = REGCOMP (&tmp->rx, s, (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0)
600     {
601       regerror (r, &tmp->rx, err->data, err->dsize);
602       mutt_free_color_line(&tmp, 1);
603       return (-1);
604     }
605     tmp->next = *top;
606     tmp->pattern = safe_strdup (s);
607 #ifdef HAVE_COLOR
608     if (fg != -1 && bg != -1)
609     {
610       tmp->fg = fg;
611       tmp->bg = bg;
612       attr |= mutt_alloc_color (fg, bg);
613     }
614 #endif
615     tmp->pair = attr;
616     *top = tmp;
617   }
618
619   /* force re-caching of index colors */
620   if (is_index)
621   {
622     int i;
623
624     for (i = 0; Context && i < Context->msgcount; i++)
625       Context->hdrs[i]->pair = 0;
626   }
627
628   return 0;
629 }
630
631 static int
632 parse_object(BUFFER *buf, BUFFER *s, int *o, int *ql, BUFFER *err)
633 {
634   int q_level = 0;
635   char *eptr;
636
637   if (!MoreArgs(s))
638   {
639     strfcpy(err->data, _("Missing arguments."), err->dsize);
640     return -1;
641   }
642
643   mutt_extract_token(buf, s, 0);
644   if (!mutt_strncmp(buf->data, "quoted", 6))
645   {
646     if (buf->data[6])
647     {
648       *ql = strtol(buf->data + 6, &eptr, 10);
649       if (*eptr || q_level < 0)
650       {
651         snprintf(err->data, err->dsize, _("%s: no such object"), buf->data);
652         return -1;
653       }
654     }
655     else
656       *ql = 0;
657
658     *o = MT_COLOR_QUOTED;
659   }
660   else if (!ascii_strcasecmp(buf->data, "compose"))
661   {
662     if (!MoreArgs(s))
663     {
664       strfcpy(err->data, _("Missing arguments."), err->dsize);
665       return -1;
666     }
667
668     mutt_extract_token(buf, s, 0);
669
670     if ((*o = mutt_getvaluebyname (buf->data, ComposeFields)) == -1)
671     {
672       snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
673       return (-1);
674     }
675   }
676   else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1)
677   {
678     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
679     return (-1);
680   }
681
682   return 0;
683 }
684
685 typedef int (*parser_callback_t)(BUFFER *, BUFFER *, int *, int *, int *, BUFFER *);
686
687 #ifdef HAVE_COLOR
688
689 static int
690 parse_color_pair(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
691 {
692   FOREVER
693   {
694     if (! MoreArgs (s))
695     {
696       strfcpy (err->data, _("color: too few arguments"), err->dsize);
697       return (-1);
698     }
699
700     mutt_extract_token (buf, s, 0);
701
702     if (ascii_strcasecmp ("bold", buf->data) == 0)
703       *attr |= A_BOLD;
704     else if (ascii_strcasecmp ("underline", buf->data) == 0)
705       *attr |= A_UNDERLINE;
706     else if (ascii_strcasecmp ("none", buf->data) == 0)
707       *attr = A_NORMAL;
708     else if (ascii_strcasecmp ("reverse", buf->data) == 0)
709       *attr |= A_REVERSE;
710     else if (ascii_strcasecmp ("standout", buf->data) == 0)
711       *attr |= A_STANDOUT;
712     else if (ascii_strcasecmp ("normal", buf->data) == 0)
713       *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
714     else
715     {
716       if (parse_color_name (buf->data, fg, attr, 1, err) != 0)
717         return (-1);
718       break;
719     }
720   }
721
722   if (! MoreArgs (s))
723   {
724     strfcpy (err->data, _("color: too few arguments"), err->dsize);
725     return (-1);
726   }
727
728   mutt_extract_token (buf, s, 0);
729
730   if (parse_color_name (buf->data, bg, attr, 0, err) != 0)
731     return (-1);
732
733   return 0;
734 }
735
736 #endif
737
738 static int
739 parse_attr_spec(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
740 {
741
742   if (fg) *fg = -1;
743   if (bg) *bg = -1;
744
745   if (! MoreArgs (s))
746   {
747     strfcpy (err->data, _("mono: too few arguments"), err->dsize);
748     return (-1);
749   }
750
751   mutt_extract_token (buf, s, 0);
752
753   if (ascii_strcasecmp ("bold", buf->data) == 0)
754     *attr |= A_BOLD;
755   else if (ascii_strcasecmp ("underline", buf->data) == 0)
756     *attr |= A_UNDERLINE;
757   else if (ascii_strcasecmp ("none", buf->data) == 0)
758     *attr = A_NORMAL;
759   else if (ascii_strcasecmp ("reverse", buf->data) == 0)
760     *attr |= A_REVERSE;
761   else if (ascii_strcasecmp ("standout", buf->data) == 0)
762     *attr |= A_STANDOUT;
763   else if (ascii_strcasecmp ("normal", buf->data) == 0)
764     *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
765   else
766   {
767     snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
768     return (-1);
769   }
770
771   return 0;
772 }
773
774 static int fgbgattr_to_color(int fg, int bg, int attr)
775 {
776 #ifdef HAVE_COLOR
777   if (fg != -1 && bg != -1)
778     return attr | mutt_alloc_color(fg, bg);
779   else
780 #endif
781     return attr;
782 }
783
784 /* usage: color <object> <fg> <bg> [ <regexp> ]
785  *        mono  <object> <attr> [ <regexp> ]
786  */
787
788 static int
789 _mutt_parse_color (BUFFER *buf, BUFFER *s, BUFFER *err,
790                    parser_callback_t callback, short dry_run)
791 {
792   int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
793   int r = 0;
794
795   if (parse_object(buf, s, &object, &q_level, err) == -1)
796     return -1;
797
798   if (callback(buf, s, &fg, &bg, &attr, err) == -1)
799     return -1;
800
801   /* extract a regular expression if needed */
802
803   if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
804   {
805     if (!MoreArgs (s))
806     {
807       strfcpy (err->data, _("too few arguments"), err->dsize);
808       return (-1);
809     }
810
811     mutt_extract_token (buf, s, 0);
812   }
813
814   if (MoreArgs (s))
815   {
816     strfcpy (err->data, _("too many arguments"), err->dsize);
817     return (-1);
818   }
819
820   /* dry run? */
821
822   if (dry_run) return 0;
823
824
825 #ifdef HAVE_COLOR
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)
831   {
832     strfcpy (err->data, _("default colors not supported"), err->dsize);
833     return (-1);
834   }
835 # endif /* HAVE_USE_DEFAULT_COLORS */
836 #endif
837
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)
843   {
844     r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
845     mutt_set_menu_redraw_full (MENU_MAIN);
846   }
847   else if (object == MT_COLOR_QUOTED)
848   {
849     if (q_level >= ColorQuoteSize)
850     {
851       safe_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
852       ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
853       ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
854     }
855     if (q_level >= ColorQuoteUsed)
856       ColorQuoteUsed = q_level + 1;
857     if (q_level == 0)
858     {
859       ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr);
860
861       ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
862       for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
863       {
864         if (ColorQuote[q_level] == A_NORMAL)
865           ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
866       }
867     }
868     else
869       ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr);
870   }
871   else
872     ColorDefs[object] = fgbgattr_to_color(fg, bg, attr);
873
874   return (r);
875 }
876
877 #ifdef HAVE_COLOR
878
879 int mutt_parse_color(BUFFER *buff, BUFFER *s, union pointer_long_t udata, BUFFER *err)
880 {
881   int dry_run = 0;
882
883   if (option(OPTNOCURSES) || !has_colors())
884     dry_run = 1;
885
886   return _mutt_parse_color(buff, s, err, parse_color_pair, dry_run);
887 }
888
889 #endif
890
891 int mutt_parse_mono(BUFFER *buff, BUFFER *s, union pointer_long_t udata, BUFFER *err)
892 {
893   int dry_run = 0;
894
895 #ifdef HAVE_COLOR
896   if (option(OPTNOCURSES) || has_colors())
897     dry_run = 1;
898 #else
899   if (option(OPTNOCURSES))
900     dry_run = 1;
901 #endif
902
903   return _mutt_parse_color(buff, s, err, parse_attr_spec, dry_run);
904 }