]> git.llucax.com Git - software/mutt-debian.git/blob - color.c
lintian clean, removed the deprecated dh_clean -k
[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 
368 _mutt_parse_uncolor (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err, 
369                          short parse_uncolor)
370 {
371   int object = 0, do_cache = 0;
372   COLOR_LINE *tmp, *last = NULL;
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   {
384     snprintf (err->data, err->dsize,
385               _("%s: command valid only for index object"), 
386               parse_uncolor ? "uncolor" : "unmono");
387     return (-1);
388   }
389   
390   if (!MoreArgs (s))
391   {
392     snprintf (err->data, err->dsize,
393               _("%s: too few arguments"), parse_uncolor ? "uncolor" : "unmono");
394     return (-1);
395   }
396
397   if(
398 #ifdef HAVE_COLOR
399      /* we're running without curses */
400      option (OPTNOCURSES) 
401      || /* we're parsing an uncolor command, and have no colors */
402      (parse_uncolor && !has_colors())
403      /* we're parsing an unmono command, and have colors */
404      || (!parse_uncolor && has_colors())
405 #else
406      /* We don't even have colors compiled in */
407      parse_uncolor
408 #endif
409      )
410   {
411     /* just eat the command, but don't do anything real about it */
412     do
413       mutt_extract_token (buf, s, 0);
414     while (MoreArgs (s));
415
416     return 0;
417   }
418      
419   
420   do
421   {
422     mutt_extract_token (buf, s, 0);
423     if (!mutt_strcmp ("*", buf->data))
424     {
425       for (tmp = ColorIndexList; tmp; )
426       {
427         if (!do_cache)
428           do_cache = 1;
429         last = tmp;
430         tmp = tmp->next;
431         mutt_free_color_line(&last, parse_uncolor);
432       }
433       ColorIndexList = NULL;
434     }
435     else
436     {
437       for (last = NULL, tmp = ColorIndexList; tmp; last = tmp, tmp = tmp->next)
438       {
439         if (!mutt_strcmp (buf->data, tmp->pattern))
440         {
441           if (!do_cache)
442             do_cache = 1;
443           dprint(1,(debugfile,"Freeing pattern \"%s\" from ColorIndexList\n",
444                                tmp->pattern));
445           if (last)
446             last->next = tmp->next;
447           else
448             ColorIndexList = tmp->next;
449           mutt_free_color_line(&tmp, parse_uncolor);
450           break;
451         }
452       }
453     }
454   }
455   while (MoreArgs (s));
456
457
458   if (do_cache && !option (OPTNOCURSES))
459   {
460     int i;
461     set_option (OPTFORCEREDRAWINDEX);
462     /* force re-caching of index colors */
463     for (i = 0; Context && i < Context->msgcount; i++)
464       Context->hdrs[i]->pair = 0;
465   }
466   return (0);
467 }
468
469
470 static int 
471 add_pattern (COLOR_LINE **top, const char *s, int sensitive,
472              int fg, int bg, int attr, BUFFER *err,
473              int is_index)
474 {
475
476   /* is_index used to store compiled pattern
477    * only for `index' color object 
478    * when called from mutt_parse_color() */
479
480   COLOR_LINE *tmp = *top;
481
482   while (tmp)
483   {
484     if (sensitive)
485     {
486       if (mutt_strcmp (s, tmp->pattern) == 0)
487         break;
488     }
489     else
490     {
491       if (mutt_strcasecmp (s, tmp->pattern) == 0)
492         break;
493     }
494     tmp = tmp->next;
495   }
496
497   if (tmp)
498   {
499 #ifdef HAVE_COLOR
500     if (fg != -1 && bg != -1)
501     {
502       if (tmp->fg != fg || tmp->bg != bg)
503       {
504         mutt_free_color (tmp->fg, tmp->bg);
505         tmp->fg = fg;
506         tmp->bg = bg;
507         attr |= mutt_alloc_color (fg, bg);
508       }
509       else
510         attr |= (tmp->pair & ~A_BOLD);
511     }
512 #endif /* HAVE_COLOR */
513     tmp->pair = attr;
514   }
515   else
516   {
517     int r;
518     char buf[LONG_STRING];
519
520     tmp = mutt_new_color_line ();
521     if (is_index) 
522     {
523       int i;
524
525       strfcpy(buf, NONULL(s), sizeof(buf));
526       mutt_check_simple (buf, sizeof (buf), NONULL(SimpleSearch));
527       if((tmp->color_pattern = mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL)
528       {
529         mutt_free_color_line(&tmp, 1);
530         return -1;
531       }
532       /* force re-caching of index colors */
533       for (i = 0; Context && i < Context->msgcount; i++)
534         Context->hdrs[i]->pair = 0;
535     }
536     else if ((r = REGCOMP (&tmp->rx, s, (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0)
537     {
538       regerror (r, &tmp->rx, err->data, err->dsize);
539       mutt_free_color_line(&tmp, 1);
540       return (-1);
541     }
542     tmp->next = *top;
543     tmp->pattern = safe_strdup (s);
544 #ifdef HAVE_COLOR
545     if(fg != -1 && bg != -1)
546     {
547       tmp->fg = fg;
548       tmp->bg = bg;
549       attr |= mutt_alloc_color (fg, bg);
550     }
551 #endif
552     tmp->pair = attr;
553     *top = tmp;
554   }
555
556   return 0;
557 }
558
559 static int
560 parse_object(BUFFER *buf, BUFFER *s, int *o, int *ql, BUFFER *err)
561 {
562   int q_level = 0;
563   char *eptr;
564   
565   if(!MoreArgs(s))
566   {
567     strfcpy(err->data, _("Missing arguments."), err->dsize);
568     return -1;
569   }
570   
571   mutt_extract_token(buf, s, 0);
572   if(!mutt_strncmp(buf->data, "quoted", 6))
573   {
574     if(buf->data[6])
575     {
576       *ql = strtol(buf->data + 6, &eptr, 10);
577       if(*eptr || q_level < 0)
578       {
579         snprintf(err->data, err->dsize, _("%s: no such object"), buf->data);
580         return -1;
581       }
582     }
583     else
584       *ql = 0;
585     
586     *o = MT_COLOR_QUOTED;
587   }
588   else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1)
589   {
590     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
591     return (-1);
592   }
593
594   return 0;
595 }
596
597 typedef int (*parser_callback_t)(BUFFER *, BUFFER *, int *, int *, int *, BUFFER *);
598
599 #ifdef HAVE_COLOR
600
601 static int
602 parse_color_pair(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
603 {
604   if (! MoreArgs (s))
605   {
606     strfcpy (err->data, _("color: too few arguments"), err->dsize);
607     return (-1);
608   }
609
610   mutt_extract_token (buf, s, 0);
611
612   if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
613     return (-1);
614
615   if (! MoreArgs (s))
616   {
617     strfcpy (err->data, _("color: too few arguments"), err->dsize);
618     return (-1);
619   }
620   
621   mutt_extract_token (buf, s, 0);
622
623   if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
624     return (-1);
625   
626   return 0;
627 }
628
629 #endif
630
631 static int
632 parse_attr_spec(BUFFER *buf, BUFFER *s, int *fg, int *bg, int *attr, BUFFER *err)
633 {
634   
635   if(fg) *fg = -1; 
636   if(bg) *bg = -1;
637
638   if (! MoreArgs (s))
639   {
640     strfcpy (err->data, _("mono: too few arguments"), err->dsize);
641     return (-1);
642   }
643
644   mutt_extract_token (buf, s, 0);
645
646   if (ascii_strcasecmp ("bold", buf->data) == 0)
647     *attr |= A_BOLD;
648   else if (ascii_strcasecmp ("underline", buf->data) == 0)
649     *attr |= A_UNDERLINE;
650   else if (ascii_strcasecmp ("none", buf->data) == 0)
651     *attr = A_NORMAL;
652   else if (ascii_strcasecmp ("reverse", buf->data) == 0)
653     *attr |= A_REVERSE;
654   else if (ascii_strcasecmp ("standout", buf->data) == 0)
655     *attr |= A_STANDOUT;
656   else if (ascii_strcasecmp ("normal", buf->data) == 0)
657     *attr = A_NORMAL; /* needs use = instead of |= to clear other bits */
658   else
659   {
660     snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
661     return (-1);
662   }
663   
664   return 0;
665 }
666
667 static int fgbgattr_to_color(int fg, int bg, int attr)
668 {
669 #ifdef HAVE_COLOR
670   if(fg != -1 && bg != -1)
671     return attr | mutt_alloc_color(fg, bg);
672   else
673 #endif
674     return attr;
675 }
676
677 /* usage: color <object> <fg> <bg> [ <regexp> ] 
678  *        mono  <object> <attr> [ <regexp> ]
679  */
680
681 static int 
682 _mutt_parse_color (BUFFER *buf, BUFFER *s, BUFFER *err, 
683                    parser_callback_t callback, short dry_run)
684 {
685   int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
686   int r = 0;
687
688   if(parse_object(buf, s, &object, &q_level, err) == -1)
689     return -1;
690
691   if(callback(buf, s, &fg, &bg, &attr, err) == -1)
692     return -1;
693
694   /* extract a regular expression if needed */
695   
696   if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY || object == MT_COLOR_INDEX)
697   {
698     if (!MoreArgs (s))
699     {
700       strfcpy (err->data, _("too few arguments"), err->dsize);
701       return (-1);
702     }
703
704     mutt_extract_token (buf, s, 0);
705   }
706    
707   if (MoreArgs (s))
708   {
709     strfcpy (err->data, _("too many arguments"), err->dsize);
710     return (-1);
711   }
712   
713   /* dry run? */
714   
715   if(dry_run) return 0;
716
717   
718 #ifdef HAVE_COLOR
719 # ifdef HAVE_USE_DEFAULT_COLORS
720   if (!option (OPTNOCURSES) && has_colors()
721     /* delay use_default_colors() until needed, since it initializes things */
722     && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
723     && use_default_colors () != OK)
724   {
725     strfcpy (err->data, _("default colors not supported"), err->dsize);
726     return (-1);
727   }
728 # endif /* HAVE_USE_DEFAULT_COLORS */
729 #endif
730   
731   if (object == MT_COLOR_HEADER)
732     r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err,0);
733   else if (object == MT_COLOR_BODY)
734     r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
735   else if (object == MT_COLOR_INDEX)
736   {
737     r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
738     set_option (OPTFORCEREDRAWINDEX);
739   }
740   else if (object == MT_COLOR_QUOTED)
741   {
742     if (q_level >= ColorQuoteSize)
743     {
744       safe_realloc (&ColorQuote, (ColorQuoteSize += 2) * sizeof (int));
745       ColorQuote[ColorQuoteSize-2] = ColorDefs[MT_COLOR_QUOTED];
746       ColorQuote[ColorQuoteSize-1] = ColorDefs[MT_COLOR_QUOTED];
747     }
748     if (q_level >= ColorQuoteUsed)
749       ColorQuoteUsed = q_level + 1;
750     if (q_level == 0)
751     {
752       ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color(fg, bg, attr);
753       
754       ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
755       for (q_level = 1; q_level < ColorQuoteUsed; q_level++)
756       {
757         if (ColorQuote[q_level] == A_NORMAL)
758           ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
759       }
760     }
761     else
762       ColorQuote[q_level] = fgbgattr_to_color(fg, bg, attr);
763   }
764   else
765     ColorDefs[object] = fgbgattr_to_color(fg, bg, attr);
766
767 #ifdef HAVE_COLOR
768 # ifdef HAVE_BKGDSET
769   if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors())
770     BKGDSET (MT_COLOR_NORMAL);
771 # endif
772 #endif
773
774   return (r);
775 }
776
777 #ifdef HAVE_COLOR
778
779 int mutt_parse_color(BUFFER *buff, BUFFER *s, unsigned long data, BUFFER *err)
780 {
781   int dry_run = 0;
782   
783   if(option(OPTNOCURSES) || !has_colors())
784     dry_run = 1;
785   
786   return _mutt_parse_color(buff, s, err, parse_color_pair, dry_run);
787 }
788
789 #endif
790
791 int mutt_parse_mono(BUFFER *buff, BUFFER *s, unsigned long data, BUFFER *err)
792 {
793   int dry_run = 0;
794   
795 #ifdef HAVE_COLOR
796   if(option(OPTNOCURSES) || has_colors())
797     dry_run = 1;
798 #else
799   if(option(OPTNOCURSES))
800     dry_run = 1;
801 #endif
802
803   return _mutt_parse_color(buff, s, err, parse_attr_spec, dry_run);
804 }
805