No specific user configuration
[platform/upstream/bash.git] / lib / readline / parse-colors.c
1 /* `dir', `vdir' and `ls' directory listing programs for GNU.
2
3    Modified by Chet Ramey for Readline.
4
5    Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 Free Software Foundation,
6    Inc.
7
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21 /* Written by Richard Stallman and David MacKenzie.  */
22
23 /* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
24    Flaherty <dennisf@denix.elk.miles.com> based on original patches by
25    Greg Lee <lee@uhunix.uhcc.hawaii.edu>.  */
26
27 #define READLINE_LIBRARY
28
29 #if defined (HAVE_CONFIG_H)
30 #  include <config.h>
31 #endif
32
33 #include <stdio.h>
34
35 // strdup() / strcpy()
36 #if defined (HAVE_STRING_H)
37 #  include <string.h>
38 #else /* !HAVE_STRING_H */
39 #  include <strings.h>
40 #endif /* !HAVE_STRING_H */
41
42 // abort()
43 #if defined (HAVE_STDLIB_H)
44 #  include <stdlib.h>
45 #else
46 #  include "ansi_stdlib.h"
47 #endif /* HAVE_STDLIB_H */
48
49 #include "rldefs.h"     // STREQ, savestring
50 #include "readline.h"
51 #include "rlprivate.h"
52 #include "rlshell.h"
53 #include "xmalloc.h"
54
55 #include "colors.h"
56 #include "parse-colors.h"
57
58 #if defined (COLOR_SUPPORT)
59
60 static bool get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count);
61
62 struct bin_str _rl_color_indicator[] =
63   {
64     { LEN_STR_PAIR ("\033[") },         //  lc: Left of color sequence
65     { LEN_STR_PAIR ("m") },             //  rc: Right of color sequence
66     { 0, NULL },                        //  ec: End color (replaces lc+no+rc)
67     { LEN_STR_PAIR ("0") },             //  rs: Reset to ordinary colors
68     { 0, NULL },                        //  no: Normal
69     { 0, NULL },                        //  fi: File: default
70     { LEN_STR_PAIR ("01;34") },         //  di: Directory: bright blue
71     { LEN_STR_PAIR ("01;36") },         //  ln: Symlink: bright cyan
72     { LEN_STR_PAIR ("33") },            //  pi: Pipe: yellow/brown
73     { LEN_STR_PAIR ("01;35") },         //  so: Socket: bright magenta
74     { LEN_STR_PAIR ("01;33") },         //  bd: Block device: bright yellow
75     { LEN_STR_PAIR ("01;33") },         //  cd: Char device: bright yellow
76     { 0, NULL },                        //  mi: Missing file: undefined
77     { 0, NULL },                        //  or: Orphaned symlink: undefined
78     { LEN_STR_PAIR ("01;32") },         //  ex: Executable: bright green
79     { LEN_STR_PAIR ("01;35") },         //  do: Door: bright magenta
80     { LEN_STR_PAIR ("37;41") },         //  su: setuid: white on red
81     { LEN_STR_PAIR ("30;43") },         //  sg: setgid: black on yellow
82     { LEN_STR_PAIR ("37;44") },         //  st: sticky: black on blue
83     { LEN_STR_PAIR ("34;42") },         //  ow: other-writable: blue on green
84     { LEN_STR_PAIR ("30;42") },         //  tw: ow w/ sticky: black on green
85     { LEN_STR_PAIR ("30;41") },         //  ca: black on red
86     { 0, NULL },                        //  mh: disabled by default
87     { LEN_STR_PAIR ("\033[K") },        //  cl: clear to end of line
88   };
89
90 /* Parse a string as part of the LS_COLORS variable; this may involve
91    decoding all kinds of escape characters.  If equals_end is set an
92    unescaped equal sign ends the string, otherwise only a : or \0
93    does.  Set *OUTPUT_COUNT to the number of bytes output.  Return
94    true if successful.
95
96    The resulting string is *not* null-terminated, but may contain
97    embedded nulls.
98
99    Note that both dest and src are char **; on return they point to
100    the first free byte after the array and the character that ended
101    the input string, respectively.  */
102
103 static bool
104 get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count) {
105   char num;                     /* For numerical codes */
106   size_t count;                 /* Something to count with */
107   enum {
108     ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
109   } state;
110   const char *p;
111   char *q;
112
113   p = *src;                     /* We don't want to double-indirect */
114   q = *dest;                    /* the whole darn time.  */
115
116   count = 0;                    /* No characters counted in yet.  */
117   num = 0;
118
119   state = ST_GND;               /* Start in ground state.  */
120   while (state < ST_END)
121     {
122       switch (state)
123         {
124         case ST_GND:            /* Ground state (no escapes) */
125           switch (*p)
126             {
127             case ':':
128             case '\0':
129               state = ST_END;   /* End of string */
130               break;
131             case '\\':
132               state = ST_BACKSLASH; /* Backslash scape sequence */
133               ++p;
134               break;
135             case '^':
136               state = ST_CARET; /* Caret escape */
137               ++p;
138               break;
139             case '=':
140               if (equals_end)
141                 {
142                   state = ST_END; /* End */
143                   break;
144                 }
145               /* else fall through */
146             default:
147               *(q++) = *(p++);
148               ++count;
149               break;
150             }
151           break;
152
153         case ST_BACKSLASH:      /* Backslash escaped character */
154           switch (*p)
155             {
156             case '0':
157             case '1':
158             case '2':
159             case '3':
160             case '4':
161             case '5':
162             case '6':
163             case '7':
164               state = ST_OCTAL; /* Octal sequence */
165               num = *p - '0';
166               break;
167             case 'x':
168             case 'X':
169               state = ST_HEX;   /* Hex sequence */
170               num = 0;
171               break;
172             case 'a':           /* Bell */
173               num = '\a';
174               break;
175             case 'b':           /* Backspace */
176               num = '\b';
177               break;
178             case 'e':           /* Escape */
179               num = 27;
180               break;
181             case 'f':           /* Form feed */
182               num = '\f';
183               break;
184             case 'n':           /* Newline */
185               num = '\n';
186               break;
187             case 'r':           /* Carriage return */
188               num = '\r';
189               break;
190             case 't':           /* Tab */
191               num = '\t';
192               break;
193             case 'v':           /* Vtab */
194               num = '\v';
195               break;
196             case '?':           /* Delete */
197               num = 127;
198               break;
199             case '_':           /* Space */
200               num = ' ';
201               break;
202             case '\0':          /* End of string */
203               state = ST_ERROR; /* Error! */
204               break;
205             default:            /* Escaped character like \ ^ : = */
206               num = *p;
207               break;
208             }
209           if (state == ST_BACKSLASH)
210             {
211               *(q++) = num;
212               ++count;
213               state = ST_GND;
214             }
215           ++p;
216           break;
217
218         case ST_OCTAL:          /* Octal sequence */
219           if (*p < '0' || *p > '7')
220             {
221               *(q++) = num;
222               ++count;
223               state = ST_GND;
224             }
225           else
226             num = (num << 3) + (*(p++) - '0');
227           break;
228
229         case ST_HEX:            /* Hex sequence */
230           switch (*p)
231             {
232             case '0':
233             case '1':
234             case '2':
235             case '3':
236             case '4':
237             case '5':
238             case '6':
239             case '7':
240             case '8':
241             case '9':
242               num = (num << 4) + (*(p++) - '0');
243               break;
244             case 'a':
245             case 'b':
246             case 'c':
247             case 'd':
248             case 'e':
249             case 'f':
250               num = (num << 4) + (*(p++) - 'a') + 10;
251               break;
252             case 'A':
253             case 'B':
254             case 'C':
255             case 'D':
256             case 'E':
257             case 'F':
258               num = (num << 4) + (*(p++) - 'A') + 10;
259               break;
260             default:
261               *(q++) = num;
262               ++count;
263               state = ST_GND;
264               break;
265             }
266           break;
267
268         case ST_CARET:          /* Caret escape */
269           state = ST_GND;       /* Should be the next state... */
270           if (*p >= '@' && *p <= '~')
271             {
272               *(q++) = *(p++) & 037;
273               ++count;
274             }
275           else if (*p == '?')
276             {
277               *(q++) = 127;
278               ++count;
279             }
280           else
281             state = ST_ERROR;
282           break;
283
284         default:
285           /* should we ? */
286           /* abort ();  no, we should not */
287           state = ST_ERROR;
288           break;
289         }
290     }
291
292   *dest = q;
293   *src = p;
294   *output_count = count;
295
296   return state != ST_ERROR;
297 }
298 #endif /* COLOR_SUPPORT */
299
300 void _rl_parse_colors()
301 {
302 #if defined (COLOR_SUPPORT)
303   const char *p;                /* Pointer to character being parsed */
304   char *buf;                    /* color_buf buffer pointer */
305   int state;                    /* State of parser */
306   int ind_no;                   /* Indicator number */
307   char label[3];                /* Indicator label */
308   COLOR_EXT_TYPE *ext;          /* Extension we are working on */
309
310   p = sh_get_env_value ("LS_COLORS");
311   if (p == 0 || *p == '\0')
312     {
313       _rl_color_ext_list = NULL;
314       return;
315     }
316
317   ext = NULL;
318   strcpy (label, "??");
319
320   /* This is an overly conservative estimate, but any possible
321      LS_COLORS string will *not* generate a color_buf longer than
322      itself, so it is a safe way of allocating a buffer in
323      advance.  */
324   buf = color_buf = savestring (p);
325
326   state = 1;
327   while (state > 0)
328     {
329       switch (state)
330         {
331         case 1:         /* First label character */
332           switch (*p)
333             {
334             case ':':
335               ++p;
336               break;
337
338             case '*':
339               /* Allocate new extension block and add to head of
340                  linked list (this way a later definition will
341                  override an earlier one, which can be useful for
342                  having terminal-specific defs override global).  */
343
344               ext = (COLOR_EXT_TYPE *)xmalloc (sizeof *ext);
345               ext->next = _rl_color_ext_list;
346               _rl_color_ext_list = ext;
347
348               ++p;
349               ext->ext.string = buf;
350
351               state = (get_funky_string (&buf, &p, true, &ext->ext.len)
352                        ? 4 : -1);
353               break;
354
355             case '\0':
356               state = 0;        /* Done! */
357               break;
358
359             default:    /* Assume it is file type label */
360               label[0] = *(p++);
361               state = 2;
362               break;
363             }
364           break;
365
366         case 2:         /* Second label character */
367           if (*p)
368             {
369               label[1] = *(p++);
370               state = 3;
371             }
372           else
373             state = -1; /* Error */
374           break;
375
376         case 3:         /* Equal sign after indicator label */
377           state = -1;   /* Assume failure...  */
378           if (*(p++) == '=')/* It *should* be...  */
379             {
380               for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
381                 {
382                   if (STREQ (label, indicator_name[ind_no]))
383                     {
384                       _rl_color_indicator[ind_no].string = buf;
385                       state = (get_funky_string (&buf, &p, false,
386                                                  &_rl_color_indicator[ind_no].len)
387                                ? 1 : -1);
388                       break;
389                     }
390                 }
391               if (state == -1)
392                 {
393                   _rl_errmsg ("LS_COLORS: unrecognized prefix: %s", label);
394                   /* recover from an unrecognized prefix */
395                   while (p && *p && *p != ':')
396                     p++;
397                   if (p && *p == ':')
398                     state = 1;
399                   else if (p && *p == 0)
400                     state = 0;
401                 }
402             }
403           break;
404
405         case 4:         /* Equal sign after *.ext */
406           if (*(p++) == '=')
407             {
408               ext->seq.string = buf;
409               state = (get_funky_string (&buf, &p, false, &ext->seq.len)
410                        ? 1 : -1);
411             }
412           else
413             state = -1;
414           /* XXX - recover here as with an unrecognized prefix? */
415           if (state == -1 && ext->ext.string)
416             _rl_errmsg ("LS_COLORS: syntax error: %s", ext->ext.string);
417           break;
418         }
419     }
420
421   if (state < 0)
422     {
423       COLOR_EXT_TYPE *e;
424       COLOR_EXT_TYPE *e2;
425
426       _rl_errmsg ("unparsable value for LS_COLORS environment variable");
427       free (color_buf);
428       for (e = _rl_color_ext_list; e != NULL; /* empty */)
429         {
430           e2 = e;
431           e = e->next;
432           free (e2);
433         }
434       _rl_color_ext_list = NULL;
435       _rl_colored_stats = 0;    /* can't have colored stats without colors */
436     }
437 #else /* !COLOR_SUPPORT */
438   ;
439 #endif /* !COLOR_SUPPORT */
440 }