Annotation of embedaddon/readline/parse-colors.c, revision 1.1
1.1 ! misho 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>