Import madtty, use it to deal with colors from now on as it needs to know what is...
[apps/madmutt.git] / lib-ui / color.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-ui/lib-ui.h>
11 #include "pattern.h"
12 #include "mutt.h"
13 #include "madtty.h"
14
15 /* globals */
16 int *ColorQuote;
17 int ColorQuoteUsed;
18 int ColorDefs[MT_COLOR_MAX];
19 COLOR_LINE *ColorHdrList = NULL;
20 COLOR_LINE *ColorBodyList = NULL;
21 COLOR_LINE *ColorIndexList = NULL;
22
23 /* local to this file */
24 static int ColorQuoteSize;
25
26 #define COLOR_DEFAULT (-2)
27
28 typedef struct color_list {
29   short fg;
30   short bg;
31   short index;
32   short count;
33   struct color_list *next;
34 } COLOR_LIST;
35
36 static struct mapping_t Colors[] = {
37   {"black", COLOR_BLACK},
38   {"blue", COLOR_BLUE},
39   {"cyan", COLOR_CYAN},
40   {"green", COLOR_GREEN},
41   {"magenta", COLOR_MAGENTA},
42   {"red", COLOR_RED},
43   {"white", COLOR_WHITE},
44   {"yellow", COLOR_YELLOW},
45   {"default", COLOR_DEFAULT},
46   {0, 0}
47 };
48
49 static struct mapping_t Fields[] = {
50   {"hdrdefault", MT_COLOR_HDEFAULT},
51   {"quoted", MT_COLOR_QUOTED},
52   {"signature", MT_COLOR_SIGNATURE},
53   {"indicator", MT_COLOR_INDICATOR},
54   {"status", MT_COLOR_STATUS},
55   {"tree", MT_COLOR_TREE},
56   {"error", MT_COLOR_ERROR},
57   {"normal", MT_COLOR_NORMAL},
58   {"tilde", MT_COLOR_TILDE},
59   {"markers", MT_COLOR_MARKERS},
60   {"header", MT_COLOR_HEADER},
61   {"body", MT_COLOR_BODY},
62   {"message", MT_COLOR_MESSAGE},
63   {"attachment", MT_COLOR_ATTACHMENT},
64   {"search", MT_COLOR_SEARCH},
65   {"bold", MT_COLOR_BOLD},
66   {"underline", MT_COLOR_UNDERLINE},
67   {"index", MT_COLOR_INDEX},
68   {"sidebar_new", MT_COLOR_NEW},
69   {"sidebar", MT_COLOR_SIDEBAR},
70   {"sidebar_flagged", MT_COLOR_FLAGGED},
71   {NULL, 0}
72 };
73
74 #define COLOR_QUOTE_INIT        8
75
76 static COLOR_LINE *mutt_new_color_line (void)
77 {
78   COLOR_LINE *p = p_new(COLOR_LINE, 1);
79
80   p->fg = p->bg = -1;
81
82   return (p);
83 }
84
85 static void mutt_free_color_line(COLOR_LINE ** l)
86 {
87   COLOR_LINE *tmp;
88
89   if (!l || !*l)
90     return;
91   tmp = *l;
92
93   /* we should really use the container type for regular expressions. */
94   regfree (&tmp->rx);
95   pattern_list_wipe(&tmp->color_pattern);
96   p_delete(&tmp->pattern);
97   p_delete(l);
98 }
99
100 void ci_start_color (void)
101 {
102     memset(ColorDefs, A_NORMAL, sizeof(int) * MT_COLOR_MAX);
103     ColorQuote = p_new(int, COLOR_QUOTE_INIT);
104     memset(ColorQuote, A_NORMAL, sizeof(int) * COLOR_QUOTE_INIT);
105     ColorQuoteSize = COLOR_QUOTE_INIT;
106     ColorQuoteUsed = 0;
107
108     /* set some defaults */
109     ColorDefs[MT_COLOR_STATUS] = A_REVERSE;
110     ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE;
111     ColorDefs[MT_COLOR_SEARCH] = A_REVERSE;
112     ColorDefs[MT_COLOR_MARKERS] = A_REVERSE;
113     /* special meaning: toggle the relevant attribute */
114     ColorDefs[MT_COLOR_BOLD] = 0;
115     ColorDefs[MT_COLOR_UNDERLINE] = 0;
116 }
117
118 static int
119 parse_color_name (const char *s, int *col, int *attr, int brite, BUFFER * err)
120 {
121   char *eptr;
122
123   if (m_strncasecmp(s, "bright", 6) == 0) {
124     *attr |= brite;
125     s += 6;
126   }
127
128   /* allow aliases for xterm color resources */
129   if (m_strncasecmp(s, "color", 5) == 0) {
130     s += 5;
131     *col = strtol (s, &eptr, 10);
132     if (!*s || *eptr || *col < 0 ||
133         (*col >= COLORS && !option (OPTNOCURSES) && has_colors ())) {
134       snprintf (err->data, err->dsize, _("%s: color not supported by term"),
135                 s);
136       return (-1);
137     }
138   }
139   else if ((*col = mutt_getvaluebyname (s, Colors)) == -1) {
140     snprintf (err->data, err->dsize, _("%s: no such color"), s);
141     return (-1);
142   }
143
144   return 0;
145 }
146
147 /* usage: uncolor index pattern [pattern...]
148  *        unmono  index pattern [pattern...]
149  */
150
151 static int
152 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
153                      BUFFER * err, short parse_uncolor);
154
155 int mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data,
156                         BUFFER * err)
157 {
158   return _mutt_parse_uncolor (buf, s, data, err, 1);
159 }
160
161 int mutt_parse_unmono (BUFFER * buf, BUFFER * s, unsigned long data,
162                        BUFFER * err)
163 {
164   return _mutt_parse_uncolor (buf, s, data, err, 0);
165 }
166
167 static int
168 _mutt_parse_uncolor (BUFFER * buf, BUFFER * s, unsigned long data __attribute__ ((unused)),
169                      BUFFER * err, short parse_uncolor)
170 {
171   int object = 0, do_cache = 0;
172   COLOR_LINE *tmp, *last = NULL;
173
174   mutt_extract_token (buf, s, 0);
175
176   if ((object = mutt_getvaluebyname (buf->data, Fields)) == -1) {
177     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
178     return (-1);
179   }
180
181   if (m_strncmp(buf->data, "index", 5) != 0) {
182     snprintf (err->data, err->dsize,
183               _("%s: command valid only for index object"),
184               parse_uncolor ? "uncolor" : "unmono");
185     return (-1);
186   }
187
188   if (!MoreArgs (s)) {
189     snprintf (err->data, err->dsize,
190               _("%s: too few arguments"),
191               parse_uncolor ? "uncolor" : "unmono");
192     return (-1);
193   }
194
195   if (option (OPTNOCURSES) || (parse_uncolor != has_colors())) {
196     /* just eat the command, but don't do anything real about it */
197     do {
198       mutt_extract_token (buf, s, 0);
199     } while (MoreArgs(s));
200     return 0;
201   }
202
203   do {
204     mutt_extract_token (buf, s, 0);
205     if (!m_strcmp("*", buf->data)) {
206       for (tmp = ColorIndexList; tmp;) {
207         if (!do_cache)
208           do_cache = 1;
209         last = tmp;
210         tmp = tmp->next;
211         mutt_free_color_line(&last);
212       }
213       ColorIndexList = NULL;
214     }
215     else {
216       for (last = NULL, tmp = ColorIndexList; tmp;
217            last = tmp, tmp = tmp->next) {
218         if (!m_strcmp(buf->data, tmp->pattern)) {
219           if (!do_cache)
220             do_cache = 1;
221           if (last)
222             last->next = tmp->next;
223           else
224             ColorIndexList = tmp->next;
225           mutt_free_color_line(&tmp);
226           break;
227         }
228       }
229     }
230   }
231   while (MoreArgs (s));
232
233
234   if (do_cache && !option (OPTNOCURSES)) {
235     int i;
236
237     set_option (OPTFORCEREDRAWINDEX);
238     /* force re-caching of index colors */
239     for (i = 0; Context && i < Context->msgcount; i++)
240       Context->hdrs[i]->pair = 0;
241   }
242   return (0);
243 }
244
245
246 static int
247 add_pattern (COLOR_LINE ** top, const char *s, int sensitive,
248              int fg, int bg, int attr, BUFFER * err, int is_index)
249 {
250
251   /* is_index used to store compiled pattern
252    * only for `index' color object 
253    * when called from mutt_parse_color() */
254
255   COLOR_LINE *tmp = *top;
256
257   while (tmp) {
258     if (sensitive) {
259       if (m_strcmp(s, tmp->pattern) == 0)
260         break;
261     }
262     else {
263       if (m_strcasecmp(s, tmp->pattern) == 0)
264         break;
265     }
266     tmp = tmp->next;
267   }
268
269   if (tmp) {
270     if (fg != -1 && bg != -1) {
271       if (tmp->fg != fg || tmp->bg != bg) {
272         tmp->fg = fg;
273         tmp->bg = bg;
274         attr |= madtty_color_pair(fg, bg);
275       } else {
276         attr |= (tmp->pair & ~A_BOLD);
277       }
278     }
279     tmp->pair = attr;
280   } else {
281     int r;
282     char buf[STRING];
283
284     tmp = mutt_new_color_line ();
285     if (is_index) {
286       int i;
287
288       m_strcpy(buf, sizeof(buf), NONULL(s));
289       mutt_check_simple (buf, sizeof (buf), NONULL (SimpleSearch));
290       if ((tmp->color_pattern =
291            mutt_pattern_comp (buf, M_FULL_MSG, err)) == NULL) {
292         mutt_free_color_line(&tmp);
293         return -1;
294       }
295       /* force re-caching of index colors */
296       for (i = 0; Context && i < Context->msgcount; i++)
297         Context->hdrs[i]->pair = 0;
298     }
299     else
300       if ((r =
301            REGCOMP (&tmp->rx, s,
302                     (sensitive ? mutt_which_case (s) : REG_ICASE))) != 0) {
303       regerror (r, &tmp->rx, err->data, err->dsize);
304       mutt_free_color_line(&tmp);
305       return (-1);
306     }
307     tmp->next = *top;
308     tmp->pattern = m_strdup(s);
309     if (fg != -1 && bg != -1) {
310       tmp->fg = fg;
311       tmp->bg = bg;
312       attr |= madtty_color_pair(fg, bg);
313     }
314     tmp->pair = attr;
315     *top = tmp;
316   }
317
318   return 0;
319 }
320
321 static int
322 parse_object (BUFFER * buf, BUFFER * s, int *o, int *ql, BUFFER * err)
323 {
324   int q_level = 0;
325   char *eptr;
326
327   if (!MoreArgs (s)) {
328     m_strcpy(err->data, err->dsize, _("Missing arguments."));
329     return -1;
330   }
331
332   mutt_extract_token (buf, s, 0);
333   if (!m_strncmp(buf->data, "quoted", 6)) {
334     if (buf->data[6]) {
335       *ql = strtol (buf->data + 6, &eptr, 10);
336       if (*eptr || q_level < 0) {
337         snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
338         return -1;
339       }
340     }
341     else
342       *ql = 0;
343
344     *o = MT_COLOR_QUOTED;
345   }
346   else if ((*o = mutt_getvaluebyname (buf->data, Fields)) == -1) {
347     snprintf (err->data, err->dsize, _("%s: no such object"), buf->data);
348     return (-1);
349   }
350
351   return 0;
352 }
353
354 typedef int (*parser_callback_t) (BUFFER *, BUFFER *, int *, int *, int *,
355                                   BUFFER *);
356
357 static int
358 parse_color_pair (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
359                   BUFFER * err)
360 {
361   if (!MoreArgs (s)) {
362     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
363     return (-1);
364   }
365
366   mutt_extract_token (buf, s, 0);
367
368   if (parse_color_name (buf->data, fg, attr, A_BOLD, err) != 0)
369     return (-1);
370
371   if (!MoreArgs (s)) {
372     m_strcpy(err->data, err->dsize, _("color: too few arguments"));
373     return (-1);
374   }
375
376   mutt_extract_token (buf, s, 0);
377
378   if (parse_color_name (buf->data, bg, attr, A_BLINK, err) != 0)
379     return (-1);
380
381   return 0;
382 }
383
384 static int
385 parse_attr_spec (BUFFER * buf, BUFFER * s, int *fg, int *bg, int *attr,
386                  BUFFER * err)
387 {
388
389   if (fg)
390     *fg = -1;
391   if (bg)
392     *bg = -1;
393
394   if (!MoreArgs (s)) {
395     m_strcpy(err->data, err->dsize, _("mono: too few arguments"));
396     return (-1);
397   }
398
399   mutt_extract_token (buf, s, 0);
400
401   if (ascii_strcasecmp ("bold", buf->data) == 0)
402     *attr |= A_BOLD;
403   else if (ascii_strcasecmp ("underline", buf->data) == 0)
404     *attr |= A_UNDERLINE;
405   else if (ascii_strcasecmp ("none", buf->data) == 0)
406     *attr = A_NORMAL;
407   else if (ascii_strcasecmp ("reverse", buf->data) == 0)
408     *attr |= A_REVERSE;
409   else if (ascii_strcasecmp ("standout", buf->data) == 0)
410     *attr |= A_STANDOUT;
411   else if (ascii_strcasecmp ("normal", buf->data) == 0)
412     *attr = A_NORMAL;           /* needs use = instead of |= to clear other bits */
413   else {
414     snprintf (err->data, err->dsize, _("%s: no such attribute"), buf->data);
415     return (-1);
416   }
417
418   return 0;
419 }
420
421 static int fgbgattr_to_color (int fg, int bg, int attr)
422 {
423   if (fg != -1 && bg != -1)
424     return attr | madtty_color_pair(fg, bg);
425   else
426     return attr;
427 }
428
429 /* usage: color <object> <fg> <bg> [ <regexp> ] 
430  *        mono  <object> <attr> [ <regexp> ]
431  */
432
433 static int
434 _mutt_parse_color (BUFFER * buf, BUFFER * s, BUFFER * err,
435                    parser_callback_t callback, short dry_run)
436 {
437   int object = 0, attr = 0, fg = 0, bg = 0, q_level = 0;
438   int r = 0;
439
440   if (parse_object (buf, s, &object, &q_level, err) == -1)
441     return -1;
442
443   if (callback (buf, s, &fg, &bg, &attr, err) == -1)
444     return -1;
445
446   /* extract a regular expression if needed */
447
448   if (object == MT_COLOR_HEADER || object == MT_COLOR_BODY
449       || object == MT_COLOR_INDEX) {
450     if (!MoreArgs (s)) {
451       m_strcpy(err->data, err->dsize, _("too few arguments"));
452       return (-1);
453     }
454
455     mutt_extract_token (buf, s, 0);
456   }
457
458   if (MoreArgs (s)) {
459     m_strcpy(err->data, err->dsize, _("too many arguments"));
460     return (-1);
461   }
462
463   /* dry run? */
464
465   if (dry_run)
466     return 0;
467
468
469   if (!option (OPTNOCURSES) && has_colors ()
470       /* delay use_default_colors() until needed, since it initializes things */
471       && (fg == COLOR_DEFAULT || bg == COLOR_DEFAULT)
472       && use_default_colors () != OK) {
473     m_strcpy(err->data, err->dsize, _("default colors not supported"));
474     return (-1);
475   }
476
477   if (object == MT_COLOR_HEADER)
478     r = add_pattern (&ColorHdrList, buf->data, 0, fg, bg, attr, err, 0);
479   else if (object == MT_COLOR_BODY)
480     r = add_pattern (&ColorBodyList, buf->data, 1, fg, bg, attr, err, 0);
481   else if (object == MT_COLOR_INDEX) {
482     r = add_pattern (&ColorIndexList, buf->data, 1, fg, bg, attr, err, 1);
483     set_option (OPTFORCEREDRAWINDEX);
484   }
485   else if (object == MT_COLOR_QUOTED) {
486     if (q_level >= ColorQuoteSize) {
487       p_realloc(&ColorQuote, ColorQuoteSize += 2);
488       ColorQuote[ColorQuoteSize - 2] = ColorDefs[MT_COLOR_QUOTED];
489       ColorQuote[ColorQuoteSize - 1] = ColorDefs[MT_COLOR_QUOTED];
490     }
491     if (q_level >= ColorQuoteUsed)
492       ColorQuoteUsed = q_level + 1;
493     if (q_level == 0) {
494       ColorDefs[MT_COLOR_QUOTED] = fgbgattr_to_color (fg, bg, attr);
495
496       ColorQuote[0] = ColorDefs[MT_COLOR_QUOTED];
497       for (q_level = 1; q_level < ColorQuoteUsed; q_level++) {
498         if (ColorQuote[q_level] == A_NORMAL)
499           ColorQuote[q_level] = ColorDefs[MT_COLOR_QUOTED];
500       }
501     }
502     else
503       ColorQuote[q_level] = fgbgattr_to_color (fg, bg, attr);
504   }
505   else
506     ColorDefs[object] = fgbgattr_to_color (fg, bg, attr);
507
508   if (object == MT_COLOR_NORMAL && !option (OPTNOCURSES) && has_colors ())
509     BKGDSET(main_w, MT_COLOR_NORMAL);
510
511   return (r);
512 }
513
514 int mutt_parse_color (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
515                       BUFFER * err)
516 {
517   int dry_run = 0;
518
519   if (option (OPTNOCURSES) || !has_colors ())
520     dry_run = 1;
521
522   return _mutt_parse_color (buff, s, err, parse_color_pair, dry_run);
523 }
524
525 int mutt_parse_mono (BUFFER * buff, BUFFER * s, unsigned long data __attribute__ ((unused)),
526                      BUFFER * err)
527 {
528   int dry_run = 0;
529
530   if (option (OPTNOCURSES) || has_colors ())
531     dry_run = 1;
532
533   return _mutt_parse_color (buff, s, err, parse_attr_spec, dry_run);
534 }