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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>