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