/* `dir', `vdir' and `ls' directory listing programs for GNU. Modified by Chet Ramey for Readline. Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012, 2017 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /* Written by Richard Stallman and David MacKenzie. */ /* Color support by Peter Anvin and Dennis Flaherty based on original patches by Greg Lee . */ #define READLINE_LIBRARY #if defined (HAVE_CONFIG_H) # include #endif #include // strdup() / strcpy() #if defined (HAVE_STRING_H) # include #else /* !HAVE_STRING_H */ # include #endif /* !HAVE_STRING_H */ // abort() #if defined (HAVE_STDLIB_H) # include #else # include "ansi_stdlib.h" #endif /* HAVE_STDLIB_H */ #include "rldefs.h" // STREQ, savestring #include "readline.h" #include "rlprivate.h" #include "rlshell.h" #include "xmalloc.h" #include "colors.h" #include "parse-colors.h" #if defined (COLOR_SUPPORT) static bool get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count); struct bin_str _rl_color_indicator[] = { { LEN_STR_PAIR ("\033[") }, // lc: Left of color sequence { LEN_STR_PAIR ("m") }, // rc: Right of color sequence { 0, NULL }, // ec: End color (replaces lc+no+rc) { LEN_STR_PAIR ("0") }, // rs: Reset to ordinary colors { 0, NULL }, // no: Normal { 0, NULL }, // fi: File: default { LEN_STR_PAIR ("01;34") }, // di: Directory: bright blue { LEN_STR_PAIR ("01;36") }, // ln: Symlink: bright cyan { LEN_STR_PAIR ("33") }, // pi: Pipe: yellow/brown { LEN_STR_PAIR ("01;35") }, // so: Socket: bright magenta { LEN_STR_PAIR ("01;33") }, // bd: Block device: bright yellow { LEN_STR_PAIR ("01;33") }, // cd: Char device: bright yellow { 0, NULL }, // mi: Missing file: undefined { 0, NULL }, // or: Orphaned symlink: undefined { LEN_STR_PAIR ("01;32") }, // ex: Executable: bright green { LEN_STR_PAIR ("01;35") }, // do: Door: bright magenta { LEN_STR_PAIR ("37;41") }, // su: setuid: white on red { LEN_STR_PAIR ("30;43") }, // sg: setgid: black on yellow { LEN_STR_PAIR ("37;44") }, // st: sticky: black on blue { LEN_STR_PAIR ("34;42") }, // ow: other-writable: blue on green { LEN_STR_PAIR ("30;42") }, // tw: ow w/ sticky: black on green { LEN_STR_PAIR ("30;41") }, // ca: black on red { 0, NULL }, // mh: disabled by default { LEN_STR_PAIR ("\033[K") }, // cl: clear to end of line }; /* Parse a string as part of the LS_COLORS variable; this may involve decoding all kinds of escape characters. If equals_end is set an unescaped equal sign ends the string, otherwise only a : or \0 does. Set *OUTPUT_COUNT to the number of bytes output. Return true if successful. The resulting string is *not* null-terminated, but may contain embedded nulls. Note that both dest and src are char **; on return they point to the first free byte after the array and the character that ended the input string, respectively. */ static bool get_funky_string (char **dest, const char **src, bool equals_end, size_t *output_count) { char num; /* For numerical codes */ size_t count; /* Something to count with */ enum { ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR } state; const char *p; char *q; p = *src; /* We don't want to double-indirect */ q = *dest; /* the whole darn time. */ count = 0; /* No characters counted in yet. */ num = 0; state = ST_GND; /* Start in ground state. */ while (state < ST_END) { switch (state) { case ST_GND: /* Ground state (no escapes) */ switch (*p) { case ':': case '\0': state = ST_END; /* End of string */ break; case '\\': state = ST_BACKSLASH; /* Backslash scape sequence */ ++p; break; case '^': state = ST_CARET; /* Caret escape */ ++p; break; case '=': if (equals_end) { state = ST_END; /* End */ break; } /* else fall through */ default: *(q++) = *(p++); ++count; break; } break; case ST_BACKSLASH: /* Backslash escaped character */ switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': state = ST_OCTAL; /* Octal sequence */ num = *p - '0'; break; case 'x': case 'X': state = ST_HEX; /* Hex sequence */ num = 0; break; case 'a': /* Bell */ num = '\a'; break; case 'b': /* Backspace */ num = '\b'; break; case 'e': /* Escape */ num = 27; break; case 'f': /* Form feed */ num = '\f'; break; case 'n': /* Newline */ num = '\n'; break; case 'r': /* Carriage return */ num = '\r'; break; case 't': /* Tab */ num = '\t'; break; case 'v': /* Vtab */ num = '\v'; break; case '?': /* Delete */ num = 127; break; case '_': /* Space */ num = ' '; break; case '\0': /* End of string */ state = ST_ERROR; /* Error! */ break; default: /* Escaped character like \ ^ : = */ num = *p; break; } if (state == ST_BACKSLASH) { *(q++) = num; ++count; state = ST_GND; } ++p; break; case ST_OCTAL: /* Octal sequence */ if (*p < '0' || *p > '7') { *(q++) = num; ++count; state = ST_GND; } else num = (num << 3) + (*(p++) - '0'); break; case ST_HEX: /* Hex sequence */ switch (*p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': num = (num << 4) + (*(p++) - '0'); break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': num = (num << 4) + (*(p++) - 'a') + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': num = (num << 4) + (*(p++) - 'A') + 10; break; default: *(q++) = num; ++count; state = ST_GND; break; } break; case ST_CARET: /* Caret escape */ state = ST_GND; /* Should be the next state... */ if (*p >= '@' && *p <= '~') { *(q++) = *(p++) & 037; ++count; } else if (*p == '?') { *(q++) = 127; ++count; } else state = ST_ERROR; break; default: /* should we ? */ /* abort (); no, we should not */ state = ST_ERROR; break; } } *dest = q; *src = p; *output_count = count; return state != ST_ERROR; } #endif /* COLOR_SUPPORT */ void _rl_parse_colors(void) { #if defined (COLOR_SUPPORT) const char *p; /* Pointer to character being parsed */ char *buf; /* color_buf buffer pointer */ int state; /* State of parser */ int ind_no; /* Indicator number */ char label[3]; /* Indicator label */ COLOR_EXT_TYPE *ext; /* Extension we are working on */ p = sh_get_env_value ("LS_COLORS"); if (p == 0 || *p == '\0') { _rl_color_ext_list = NULL; return; } ext = NULL; strcpy (label, "??"); /* This is an overly conservative estimate, but any possible LS_COLORS string will *not* generate a color_buf longer than itself, so it is a safe way of allocating a buffer in advance. */ buf = color_buf = savestring (p); state = 1; while (state > 0) { switch (state) { case 1: /* First label character */ switch (*p) { case ':': ++p; break; case '*': /* Allocate new extension block and add to head of linked list (this way a later definition will override an earlier one, which can be useful for having terminal-specific defs override global). */ ext = (COLOR_EXT_TYPE *)xmalloc (sizeof *ext); ext->next = _rl_color_ext_list; _rl_color_ext_list = ext; ++p; ext->ext.string = buf; state = (get_funky_string (&buf, &p, true, &ext->ext.len) ? 4 : -1); break; case '\0': state = 0; /* Done! */ break; default: /* Assume it is file type label */ label[0] = *(p++); state = 2; break; } break; case 2: /* Second label character */ if (*p) { label[1] = *(p++); state = 3; } else state = -1; /* Error */ break; case 3: /* Equal sign after indicator label */ state = -1; /* Assume failure... */ if (*(p++) == '=')/* It *should* be... */ { for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no) { if (STREQ (label, indicator_name[ind_no])) { _rl_color_indicator[ind_no].string = buf; state = (get_funky_string (&buf, &p, false, &_rl_color_indicator[ind_no].len) ? 1 : -1); break; } } if (state == -1) { _rl_errmsg ("LS_COLORS: unrecognized prefix: %s", label); /* recover from an unrecognized prefix */ while (p && *p && *p != ':') p++; if (p && *p == ':') state = 1; else if (p && *p == 0) state = 0; } } break; case 4: /* Equal sign after *.ext */ if (*(p++) == '=') { ext->seq.string = buf; state = (get_funky_string (&buf, &p, false, &ext->seq.len) ? 1 : -1); } else state = -1; /* XXX - recover here as with an unrecognized prefix? */ if (state == -1 && ext->ext.string) _rl_errmsg ("LS_COLORS: syntax error: %s", ext->ext.string); break; } } if (state < 0) { COLOR_EXT_TYPE *e; COLOR_EXT_TYPE *e2; _rl_errmsg ("unparsable value for LS_COLORS environment variable"); free (color_buf); for (e = _rl_color_ext_list; e != NULL; /* empty */) { e2 = e; e = e->next; free (e2); } _rl_color_ext_list = NULL; _rl_colored_stats = 0; /* can't have colored stats without colors */ } #else /* !COLOR_SUPPORT */ ; #endif /* !COLOR_SUPPORT */ }