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