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 162 static int getstring (char **, const Char **, Str *, int); 163 static void put_color (const Str *); 164 static void print_color (const Char *, size_t, Char); 165 166 /* set_color_context(): 167 */ 168 void 169 set_color_context(void) 170 { 171 struct varent *vp = adrof(STRcolor); 172 173 if (vp == NULL || vp->vec == NULL) { 174 color_context_ls = FALSE; 175 color_context_lsmF = FALSE; 176 } else if (!vp->vec[0] || vp->vec[0][0] == '\0') { 177 color_context_ls = TRUE; 178 color_context_lsmF = TRUE; 179 } else { 180 size_t i; 181 182 color_context_ls = FALSE; 183 color_context_lsmF = FALSE; 184 for (i = 0; vp->vec[i]; i++) 185 if (Strcmp(vp->vec[i], STRls) == 0) 186 color_context_ls = TRUE; 187 else if (Strcmp(vp->vec[i], STRlsmF) == 0) 188 color_context_lsmF = TRUE; 189 } 190 } 191 192 193 /* getstring(): 194 */ 195 static int 196 getstring(char **dp, const Char **sp, Str *pd, int f) 197 { 198 const Char *s = *sp; 199 char *d = *dp; 200 eChar sc; 201 202 while (*s && (*s & CHAR) != (Char)f && (*s & CHAR) != ':') { 203 if ((*s & CHAR) == '\\' || (*s & CHAR) == '^') { 204 if ((sc = parseescape(&s)) == CHAR_ERR) 205 return 0; 206 } 207 else 208 sc = *s++ & CHAR; 209 d += one_wctomb(d, sc); 210 } 211 212 pd->s = *dp; 213 pd->len = d - *dp; 214 *sp = s; 215 *dp = d; 216 return *s == (Char)f; 217 } 218 219 static void 220 init(size_t colorlen, size_t extnum) 221 { 222 size_t i; 223 224 xfree(extensions); 225 for (i = 0; i < nvariables; i++) 226 variables[i].color = variables[i].defaultcolor; 227 if (colorlen == 0 && extnum == 0) { 228 extensions = NULL; 229 colors = NULL; 230 } else { 231 extensions = xmalloc(colorlen + extnum * sizeof(*extensions)); 232 colors = extnum * sizeof(*extensions) + (char *)extensions; 233 } 234 nextensions = 0; 235 } 236 237 static int 238 color(Char x) 239 { 240 static const char ccolors[] = "abcdefghx"; 241 char *p; 242 if (Isupper(x)) { 243 x = Tolower(x); 244 } 245 246 if (x == '\0' || (p = strchr(ccolors, x)) == NULL) 247 return -1; 248 return 30 + (p - ccolors); 249 } 250 251 static void 252 makecolor(char **c, int fg, int bg, Str *v) 253 { 254 int l; 255 if (fg & 0x80) { 256 l = xsnprintf(*c, 12, "%.2d;%.2d;%.2d;%.2d", ANSI_BOLD_ON, 257 fg & ~TCSH_BOLD, (10 + bg) & ~TCSH_BOLD, ANSI_BOLD_OFF); 258 } else { 259 l = xsnprintf(*c, 6, "%.2d;%.2d", 260 fg & ~TCSH_BOLD, (10 + bg) & ~TCSH_BOLD); 261 } 262 263 v->s = *c; 264 v->len = l; 265 *c += l + 1; 266 } 267 268 /* parseLSCOLORS(): 269 * Parse the LSCOLORS environment variable 270 */ 271 static const Char *xv; /* setjmp clobbering */ 272 void 273 parseLSCOLORS(const Char *value) 274 { 275 size_t i, len, clen; 276 jmp_buf_t osetexit; 277 size_t omark; 278 xv = value; 279 280 if (xv == NULL) { 281 init(0, 0); 282 return; 283 } 284 285 len = Strlen(xv); 286 len >>= 1; 287 clen = len * 12; /* "??;??;??;??\0" */ 288 init(clen, 0); 289 290 /* Prevent from crashing if unknown parameters are given. */ 291 omark = cleanup_push_mark(); 292 getexit(osetexit); 293 294 /* init pointers */ 295 296 if (setexit() == 0) { 297 const Char *v = xv; 298 char *c = colors; 299 300 int fg, bg; 301 for (i = 0; i < len; i++) { 302 fg = color(*v++); 303 if (fg == -1) 304 stderror(ERR_BADCOLORVAR, v[-1], '?'); 305 306 bg = color(*v++); 307 if (bg == -1) 308 stderror(ERR_BADCOLORVAR, '?', v[-1]); 309 makecolor(&c, fg, bg, &variables[map[i]].color); 310 } 311 312 } 313 cleanup_pop_mark(omark); 314 resexit(osetexit); 315 } 316 317 /* parseLS_COLORS(): 318 * Parse the LS_COLORS environment variable 319 */ 320 void 321 parseLS_COLORS(const Char *value) 322 { 323 size_t i, len; 324 const Char *v; /* pointer in value */ 325 char *c; /* pointer in colors */ 326 Extension *volatile e; /* pointer in extensions */ 327 jmp_buf_t osetexit; 328 size_t omark; 329 330 (void) &e; 331 332 333 if (value == NULL) { 334 init(0, 0); 335 return; 336 } 337 338 len = Strlen(value); 339 /* allocate memory */ 340 i = 1; 341 for (v = value; *v; v++) 342 if ((*v & CHAR) == ':') 343 i++; 344 345 init(len, i); 346 347 /* init pointers */ 348 v = value; 349 c = colors; 350 e = extensions; 351 352 /* Prevent from crashing if unknown parameters are given. */ 353 354 omark = cleanup_push_mark(); 355 getexit(osetexit); 356 357 if (setexit() == 0) { 358 359 /* parse */ 360 while (*v) { 361 switch (*v & CHAR) { 362 case ':': 363 v++; 364 continue; 365 366 case '*': /* :*ext=color: */ 367 v++; 368 if (getstring(&c, &v, &e->extension, '=') && 369 0 < e->extension.len) { 370 v++; 371 getstring(&c, &v, &e->color, ':'); 372 e++; 373 continue; 374 } 375 break; 376 377 default: /* :vl=color: */ 378 if (v[0] && v[1] && (v[2] & CHAR) == '=') { 379 for (i = 0; i < nvariables; i++) 380 if ((Char)variables[i].variable[0] == (v[0] & CHAR) && 381 (Char)variables[i].variable[1] == (v[1] & CHAR)) 382 break; 383 if (i < nvariables) { 384 v += 3; 385 getstring(&c, &v, &variables[i].color, ':'); 386 continue; 387 } 388 else 389 stderror(ERR_BADCOLORVAR, v[0], v[1]); 390 } 391 break; 392 } 393 while (*v && (*v & CHAR) != ':') 394 v++; 395 } 396 } 397 398 cleanup_pop_mark(omark); 399 resexit(osetexit); 400 401 nextensions = e - extensions; 402 } 403 404 /* put_color(): 405 */ 406 static void 407 put_color(const Str *colorp) 408 { 409 size_t i; 410 const char *c = colorp->s; 411 int original_output_raw = output_raw; 412 413 output_raw = TRUE; 414 cleanup_push(&original_output_raw, output_raw_restore); 415 for (i = colorp->len; 0 < i; i--) 416 xputchar(*c++); 417 cleanup_until(&original_output_raw); 418 } 419 420 421 /* print_color(): 422 */ 423 static void 424 print_color(const Char *fname, size_t len, Char suffix) 425 { 426 size_t i; 427 char *filename = short2str(fname); 428 char *last = filename + len; 429 Str *colorp = &variables[VFile].color; 430 431 switch (suffix) { 432 case '>': /* File is a symbolic link pointing to 433 * a directory */ 434 colorp = &variables[VDir].color; 435 break; 436 case '+': /* File is a hidden directory [aix] or 437 * context dependent [hpux] */ 438 case ':': /* File is network special [hpux] */ 439 break; 440 default: 441 for (i = 0; i < nvariables; i++) 442 if (variables[i].suffix != NOS && 443 (Char)variables[i].suffix == suffix) { 444 colorp = &variables[i].color; 445 break; 446 } 447 if (i == nvariables) { 448 for (i = 0; i < nextensions; i++) 449 if (len >= extensions[i].extension.len 450 && strncmp(last - extensions[i].extension.len, 451 extensions[i].extension.s, 452 extensions[i].extension.len) == 0) { 453 colorp = &extensions[i].color; 454 break; 455 } 456 } 457 break; 458 } 459 460 put_color(&variables[VLeft].color); 461 put_color(colorp); 462 put_color(&variables[VRight].color); 463 } 464 465 466 /* print_with_color(): 467 */ 468 void 469 print_with_color(const Char *filename, size_t len, Char suffix) 470 { 471 if (color_context_lsmF && 472 (haderr ? (didfds ? is2atty : isdiagatty) : 473 (didfds ? is1atty : isoutatty))) { 474 print_color(filename, len, suffix); 475 xprintf("%S", filename); 476 if (0 < variables[VEnd].color.len) 477 put_color(&variables[VEnd].color); 478 else { 479 put_color(&variables[VLeft].color); 480 put_color(&variables[VNormal].color); 481 put_color(&variables[VRight].color); 482 } 483 } 484 else 485 xprintf("%S", filename); 486 xputwchar(suffix); 487 } 488 489 490 #endif /* COLOR_LS_F */ 491