1 /* $Header: /p/tcsh/cvsroot/tcsh/tw.color.c,v 1.32 2014/07/07 19:53:51 christos Exp $ */ 2 /* 3 * tw.color.c: builtin color ls-F 4 */ 5 /*- 6 * Copyright (c) 1998 The Regents of the University of California. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 #include "sh.h" 34 35 RCSID("$tcsh: tw.color.c,v 1.32 2014/07/07 19:53:51 christos Exp $") 36 37 #include "tw.h" 38 #include "ed.h" 39 #include "tc.h" 40 41 #ifdef COLOR_LS_F 42 43 typedef struct { 44 const char *s; 45 size_t len; 46 } Str; 47 48 49 #define VAR(suffix,variable,defaultcolor) \ 50 { \ 51 suffix, variable, { defaultcolor, sizeof(defaultcolor) - 1 }, \ 52 { defaultcolor, sizeof(defaultcolor) - 1 } \ 53 } 54 #define NOS '\0' /* no suffix */ 55 56 typedef struct { 57 const char suffix; 58 const char *variable; 59 Str color; 60 Str defaultcolor; 61 } Variable; 62 63 static Variable variables[] = { 64 VAR('/', "di", "01;34"), /* Directory */ 65 VAR('@', "ln", "01;36"), /* Symbolic link */ 66 VAR('&', "or", ""), /* Orphanned symbolic link (defaults to ln) */ 67 VAR('|', "pi", "33"), /* Named pipe (FIFO) */ 68 VAR('=', "so", "01;35"), /* Socket */ 69 VAR('>', "do", "01;35"), /* Door (solaris fast ipc mechanism) */ 70 VAR('#', "bd", "01;33"), /* Block device */ 71 VAR('%', "cd", "01;33"), /* Character device */ 72 VAR('*', "ex", "01;32"), /* Executable file */ 73 VAR(NOS, "fi", "0"), /* Regular file */ 74 VAR(NOS, "no", "0"), /* Normal (non-filename) text */ 75 VAR(NOS, "mi", ""), /* Missing file (defaults to fi) */ 76 #ifdef IS_ASCII 77 VAR(NOS, "lc", "\033["), /* Left code (ASCII) */ 78 #else 79 VAR(NOS, "lc", "\x27["), /* Left code (EBCDIC)*/ 80 #endif 81 VAR(NOS, "rc", "m"), /* Right code */ 82 VAR(NOS, "ec", ""), /* End code (replaces lc+no+rc) */ 83 VAR(NOS, "su", ""), /* Setuid file (u+s) */ 84 VAR(NOS, "sg", ""), /* Setgid file (g+s) */ 85 VAR(NOS, "tw", ""), /* Sticky and other writable dir (+t,o+w) */ 86 VAR(NOS, "ow", ""), /* Other writable dir (o+w) but not sticky */ 87 VAR(NOS, "st", ""), /* Sticky dir (+t) but not other writable */ 88 VAR(NOS, "rs", "0"), /* Reset to normal color */ 89 VAR(NOS, "hl", "44;37"), /* Reg file extra hard links, obsolete? */ 90 VAR(NOS, "mh", "44;37"), /* Reg file extra hard links */ 91 VAR(NOS, "ca", "30;41"), /* File with capability */ 92 }; 93 94 #define nvariables (sizeof(variables)/sizeof(variables[0])) 95 96 enum FileType { 97 VDir, VSym, VOrph, VPipe, VSock, VDoor, VBlock, VChr, VExe, 98 VFile, VNormal, VMiss, VLeft, VRight, VEnd, VSuid, VSgid, VSticky, 99 VOther, Vstird, VReset, Vhard, Vhard2, VCap 100 }; 101 102 /* 103 * Map from LSCOLORS entry index to Variable array index 104 */ 105 static const uint8_t map[] = { 106 VDir, /* Directory */ 107 VSym, /* Symbolic Link */ 108 VSock, /* Socket */ 109 VPipe, /* Named Pipe */ 110 VExe, /* Executable */ 111 VBlock, /* Block Special */ 112 VChr, /* Character Special */ 113 VSuid, /* Setuid Executable */ 114 VSgid, /* Setgid Executable */ 115 VSticky, /* Directory writable to others and sticky */ 116 VOther, /* Directory writable to others but not sticky */ 117 }; 118 119 120 121 enum ansi { 122 ANSI_RESET_ON = 0, /* reset colors/styles (white on black) */ 123 ANSI_BOLD_ON = 1, /* bold on */ 124 ANSI_ITALICS_ON = 3, /* italics on */ 125 ANSI_UNDERLINE_ON = 4, /* underline on */ 126 ANSI_INVERSE_ON = 7, /* inverse on */ 127 ANSI_STRIKETHROUGH_ON = 9, /* strikethrough on */ 128 ANSI_BOLD_OFF = 21, /* bold off */ 129 ANSI_ITALICS_OFF = 23, /* italics off */ 130 ANSI_UNDERLINE_OFF = 24, /* underline off */ 131 ANSI_INVERSE_OFF = 27, /* inverse off */ 132 ANSI_STRIKETHROUGH_OFF = 29,/* strikethrough off */ 133 ANSI_FG_BLACK = 30, /* fg black */ 134 ANSI_FG_RED = 31, /* fg red */ 135 ANSI_FG_GREEN = 32, /* fg green */ 136 ANSI_FG_YELLOW = 33, /* fg yellow */ 137 ANSI_FG_BLUE = 34, /* fg blue */ 138 ANSI_FG_MAGENTA = 35, /* fg magenta */ 139 ANSI_FG_CYAN = 36, /* fg cyan */ 140 ANSI_FG_WHITE = 37, /* fg white */ 141 ANSI_FG_DEFAULT = 39, /* fg default (white) */ 142 ANSI_BG_BLACK = 40, /* bg black */ 143 ANSI_BG_RED = 41, /* bg red */ 144 ANSI_BG_GREEN = 42, /* bg green */ 145 ANSI_BG_YELLOW = 43, /* bg yellow */ 146 ANSI_BG_BLUE = 44, /* bg blue */ 147 ANSI_BG_MAGENTA = 45, /* bg magenta */ 148 ANSI_BG_CYAN = 46, /* bg cyan */ 149 ANSI_BG_WHITE = 47, /* bg white */ 150 ANSI_BG_DEFAULT = 49, /* bg default (black) */ 151 }; 152 #define TCSH_BOLD 0x80 153 154 typedef struct { 155 Str extension; /* file extension */ 156 Str color; /* color string */ 157 } Extension; 158 159 static Extension *extensions = NULL; 160 static size_t nextensions = 0; 161 162 static char *colors = NULL; 163 int color_context_ls = FALSE; /* do colored ls */ 164 static int color_context_lsmF = FALSE; /* do colored ls-F */ 165 166 static int getstring (char **, const Char **, Str *, int); 167 static void put_color (const Str *); 168 static void print_color (const Char *, size_t, Char); 169 170 /* set_color_context(): 171 */ 172 void 173 set_color_context(void) 174 { 175 struct varent *vp = adrof(STRcolor); 176 177 if (vp == NULL || vp->vec == NULL) { 178 color_context_ls = FALSE; 179 color_context_lsmF = FALSE; 180 } else if (!vp->vec[0] || vp->vec[0][0] == '\0') { 181 color_context_ls = TRUE; 182 color_context_lsmF = TRUE; 183 } else { 184 size_t i; 185 186 color_context_ls = FALSE; 187 color_context_lsmF = FALSE; 188 for (i = 0; vp->vec[i]; i++) 189 if (Strcmp(vp->vec[i], STRls) == 0) 190 color_context_ls = TRUE; 191 else if (Strcmp(vp->vec[i], STRlsmF) == 0) 192 color_context_lsmF = TRUE; 193 } 194 } 195 196 197 /* getstring(): 198 */ 199 static int 200 getstring(char **dp, const Char **sp, Str *pd, int f) 201 { 202 const Char *s = *sp; 203 char *d = *dp; 204 eChar sc; 205 206 while (*s && (*s & CHAR) != (Char)f && (*s & CHAR) != ':') { 207 if ((*s & CHAR) == '\\' || (*s & CHAR) == '^') { 208 if ((sc = parseescape(&s)) == CHAR_ERR) 209 return 0; 210 } 211 else 212 sc = *s++ & CHAR; 213 d += one_wctomb(d, sc); 214 } 215 216 pd->s = *dp; 217 pd->len = d - *dp; 218 *sp = s; 219 *dp = d; 220 return *s == (Char)f; 221 } 222 223 static void 224 init(size_t colorlen, size_t extnum) 225 { 226 size_t i; 227 228 xfree(extensions); 229 for (i = 0; i < nvariables; i++) 230 variables[i].color = variables[i].defaultcolor; 231 if (colorlen == 0 && extnum == 0) { 232 extensions = NULL; 233 colors = NULL; 234 } else { 235 extensions = xmalloc(colorlen + extnum * sizeof(*extensions)); 236 colors = extnum * sizeof(*extensions) + (char *)extensions; 237 } 238 nextensions = 0; 239 } 240 241 static int 242 color(Char x) 243 { 244 int c; 245 static const char ccolors[] = "abcdefghx"; 246 char *p; 247 if (Isupper(x)) { 248 x = Tolower(x); 249 c |= TCSH_BOLD; 250 } else 251 c = 0; 252 253 if (x == '\0' || (p = strchr(ccolors, x)) == NULL) 254 return -1; 255 return 30 + (p - ccolors); 256 } 257 258 static void 259 makecolor(char **c, int fg, int bg, Str *v) 260 { 261 int l; 262 if (fg & 0x80) 263 l = xsnprintf(*c, 12, "%.2d;%.2d;%.2d;%.2d", ANSI_BOLD_ON, 264 fg & ~TCSH_BOLD, (10 + bg) & ~TCSH_BOLD, ANSI_BOLD_OFF); 265 l = xsnprintf(*c, 6, "%.2d;%.2d", 266 fg & ~TCSH_BOLD, (10 + bg) & ~TCSH_BOLD); 267 268 v->s = *c; 269 v->len = l; 270 *c += l + 1; 271 } 272 273 /* parseLSCOLORS(): 274 * Parse the LSCOLORS environment variable 275 */ 276 static const Char *xv; /* setjmp clobbering */ 277 void 278 parseLSCOLORS(const Char *value) 279 { 280 size_t i, len, clen; 281 jmp_buf_t osetexit; 282 size_t omark; 283 xv = value; 284 285 if (xv == NULL) { 286 init(0, 0); 287 return; 288 } 289 290 len = Strlen(xv); 291 len >>= 1; 292 clen = len * 12; /* "??;??;??;??\0" */ 293 init(clen, 0); 294 295 /* Prevent from crashing if unknown parameters are given. */ 296 omark = cleanup_push_mark(); 297 getexit(osetexit); 298 299 /* init pointers */ 300 301 if (setexit() == 0) { 302 const Char *v = xv; 303 char *c = colors; 304 305 int fg, bg; 306 for (i = 0; i < len; i++) { 307 fg = color(*v++); 308 if (fg == -1) 309 stderror(ERR_BADCOLORVAR, v[-1], '?'); 310 311 bg = color(*v++); 312 if (bg == -1) 313 stderror(ERR_BADCOLORVAR, '?', v[-1]); 314 makecolor(&c, fg, bg, &variables[map[i]].color); 315 } 316 317 } 318 cleanup_pop_mark(omark); 319 resexit(osetexit); 320 } 321 322 /* parseLS_COLORS(): 323 * Parse the LS_COLORS environment variable 324 */ 325 void 326 parseLS_COLORS(const Char *value) 327 { 328 size_t i, len; 329 const Char *v; /* pointer in value */ 330 char *c; /* pointer in colors */ 331 Extension *volatile e; /* pointer in extensions */ 332 jmp_buf_t osetexit; 333 size_t omark; 334 335 (void) &e; 336 337 338 if (value == NULL) { 339 init(0, 0); 340 return; 341 } 342 343 len = Strlen(value); 344 /* allocate memory */ 345 i = 1; 346 for (v = value; *v; v++) 347 if ((*v & CHAR) == ':') 348 i++; 349 350 init(len, i); 351 352 /* init pointers */ 353 v = value; 354 c = colors; 355 e = extensions; 356 357 /* Prevent from crashing if unknown parameters are given. */ 358 359 omark = cleanup_push_mark(); 360 getexit(osetexit); 361 362 if (setexit() == 0) { 363 364 /* parse */ 365 while (*v) { 366 switch (*v & CHAR) { 367 case ':': 368 v++; 369 continue; 370 371 case '*': /* :*ext=color: */ 372 v++; 373 if (getstring(&c, &v, &e->extension, '=') && 374 0 < e->extension.len) { 375 v++; 376 getstring(&c, &v, &e->color, ':'); 377 e++; 378 continue; 379 } 380 break; 381 382 default: /* :vl=color: */ 383 if (v[0] && v[1] && (v[2] & CHAR) == '=') { 384 for (i = 0; i < nvariables; i++) 385 if ((Char)variables[i].variable[0] == (v[0] & CHAR) && 386 (Char)variables[i].variable[1] == (v[1] & CHAR)) 387 break; 388 if (i < nvariables) { 389 v += 3; 390 getstring(&c, &v, &variables[i].color, ':'); 391 continue; 392 } 393 else 394 stderror(ERR_BADCOLORVAR, v[0], v[1]); 395 } 396 break; 397 } 398 while (*v && (*v & CHAR) != ':') 399 v++; 400 } 401 } 402 403 cleanup_pop_mark(omark); 404 resexit(osetexit); 405 406 nextensions = e - extensions; 407 } 408 409 /* put_color(): 410 */ 411 static void 412 put_color(const Str *colorp) 413 { 414 size_t i; 415 const char *c = colorp->s; 416 int original_output_raw = output_raw; 417 418 output_raw = TRUE; 419 cleanup_push(&original_output_raw, output_raw_restore); 420 for (i = colorp->len; 0 < i; i--) 421 xputchar(*c++); 422 cleanup_until(&original_output_raw); 423 } 424 425 426 /* print_color(): 427 */ 428 static void 429 print_color(const Char *fname, size_t len, Char suffix) 430 { 431 size_t i; 432 char *filename = short2str(fname); 433 char *last = filename + len; 434 Str *colorp = &variables[VFile].color; 435 436 switch (suffix) { 437 case '>': /* File is a symbolic link pointing to 438 * a directory */ 439 colorp = &variables[VDir].color; 440 break; 441 case '+': /* File is a hidden directory [aix] or 442 * context dependent [hpux] */ 443 case ':': /* File is network special [hpux] */ 444 break; 445 default: 446 for (i = 0; i < nvariables; i++) 447 if (variables[i].suffix != NOS && 448 (Char)variables[i].suffix == suffix) { 449 colorp = &variables[i].color; 450 break; 451 } 452 if (i == nvariables) { 453 for (i = 0; i < nextensions; i++) 454 if (len >= extensions[i].extension.len 455 && strncmp(last - extensions[i].extension.len, 456 extensions[i].extension.s, 457 extensions[i].extension.len) == 0) { 458 colorp = &extensions[i].color; 459 break; 460 } 461 } 462 break; 463 } 464 465 put_color(&variables[VLeft].color); 466 put_color(colorp); 467 put_color(&variables[VRight].color); 468 } 469 470 471 /* print_with_color(): 472 */ 473 void 474 print_with_color(const Char *filename, size_t len, Char suffix) 475 { 476 if (color_context_lsmF && 477 (haderr ? (didfds ? is2atty : isdiagatty) : 478 (didfds ? is1atty : isoutatty))) { 479 print_color(filename, len, suffix); 480 xprintf("%S", filename); 481 if (0 < variables[VEnd].color.len) 482 put_color(&variables[VEnd].color); 483 else { 484 put_color(&variables[VLeft].color); 485 put_color(&variables[VNormal].color); 486 put_color(&variables[VRight].color); 487 } 488 } 489 else 490 xprintf("%S", filename); 491 xputwchar(suffix); 492 } 493 494 495 #endif /* COLOR_LS_F */ 496