Bash  5.0-beta2
Bash - Bourne Again shell
parse-colors.c
Go to the documentation of this file.
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, 2017
6  Free Software Foundation, 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 
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 
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  {
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  }
435  _rl_colored_stats = 0; /* can't have colored stats without colors */
436  }
437 #else /* !COLOR_SUPPORT */
438  ;
439 #endif /* !COLOR_SUPPORT */
440 }
static char * color_buf
Definition: parse-colors.h:44
int _rl_colored_stats
Definition: complete.c:204
struct bin_str ext
Definition: colors.h:70
char * sh_get_env_value()
#define LEN_STR_PAIR(s)
Definition: parse-colors.h:32
static const char *const indicator_name[]
Definition: parse-colors.h:36
p
Definition: glob_loop.c:31
const char * string
Definition: colors.h:59
static _rl_bool_t get_funky_string(char **dest, const char **src, _rl_bool_t equals_end, size_t *output_count)
Definition: parse-colors.c:104
COLOR_EXT_TYPE * _rl_color_ext_list
Definition: colors.c:69
char * savestring(const char *s)
Definition: savestring.c:33
char * strcpy()
struct bin_str _rl_color_indicator[]
Definition: parse-colors.c:62
#define ST_END
Definition: stat.c:66
void _rl_parse_colors(void)
Definition: parse-colors.c:300
void free()
struct _color_ext_type * next
Definition: colors.h:72
void _rl_errmsg()
size_t len
Definition: colors.h:58
#define NULL
Definition: general.h:53
#define STREQ(a, b)
Definition: general.h:166
struct bin_str seq
Definition: colors.h:71
Definition: colors.h:56
static char * xmalloc()
static char label[5]
Definition: man2html.c:2116