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