summaryrefslogtreecommitdiff
path: root/parse-colors.c
diff options
context:
space:
mode:
Diffstat (limited to 'parse-colors.c')
-rw-r--r--parse-colors.c440
1 files changed, 440 insertions, 0 deletions
diff --git a/parse-colors.c b/parse-colors.c
new file mode 100644
index 0000000..98b2e9d
--- /dev/null
+++ b/parse-colors.c
@@ -0,0 +1,440 @@
+/* `dir', `vdir' and `ls' directory listing programs for GNU.
+
+ Modified by Chet Ramey for Readline.
+
+ Copyright (C) 1985, 1988, 1990-1991, 1995-2010, 2012 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 <http://www.gnu.org/licenses/>. */
+
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
+ Flaherty <dennisf@denix.elk.miles.com> based on original patches by
+ Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */
+
+#define READLINE_LIBRARY
+
+#if defined (HAVE_CONFIG_H)
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+// strdup() / strcpy()
+#if defined (HAVE_STRING_H)
+# include <string.h>
+#else /* !HAVE_STRING_H */
+# include <strings.h>
+#endif /* !HAVE_STRING_H */
+
+// abort()
+#if defined (HAVE_STDLIB_H)
+# include <stdlib.h>
+#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()
+{
+#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 */
+}