root/color.c

Revision 5291:3869e46f6fd4, 17.5 kB (checked in by Brendan Cully <brendan@…>, 10 months ago)

Turn down some debug logging levels

Line 
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 */
32int *ColorQuote;
33int ColorQuoteUsed;
34int ColorDefs[MT_COLOR_MAX];
35COLOR_LINE *ColorHdrList = NULL;
36COLOR_LINE *ColorBodyList = NULL;
37COLOR_LINE *ColorIndexList = NULL;
38
39/* local to this file */
40static int ColorQuoteSize;
41
42#ifdef HAVE_COLOR
43
44#define COLOR_DEFAULT (-2)
45
46typedef 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
55static COLOR_LIST *ColorList = NULL;
56static int UserColors = 0;
57
58static 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
76static 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
101static 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
110static 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
135void 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
160static 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
198int 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
263void 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
307static int
308parse_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
346static 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
353int 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
361int 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
367static 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
470static int 
471add_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
559static int
560parse_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
597typedef int (*parser_callback_t)(BUFFER *, BUFFER *, int *, int *, int *, BUFFER *);
598
599#ifdef HAVE_COLOR
600
601static int
602parse_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
631static int
632parse_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
667static 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
681static 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);