]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - readline/parse-colors.c
TUI resize unification
[thirdparty/binutils-gdb.git] / 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, 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
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(void)
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 }