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 l = xsnprintf(*c, 6, "%.2d;%.2d", 259 fg & ~TCSH_BOLD, (10 + bg) & ~TCSH_BOLD); 260 261 v->s = *c; 262 v->len = l; 263 *c += l + 1; 264 } 265 266 /* parseLSCOLORS(): 267 * Parse the LSCOLORS environment variable 268 */ 269 static const Char *xv; /* setjmp clobbering */ 270 void 271 parseLSCOLORS(const Char *value) 272 { 273 size_t i, len, clen; 274 jmp_buf_t osetexit; 275 size_t omark; 276 xv = value; 277 278 if (xv == NULL) { 279 init(0, 0); 280 return; 281 } 282 283 len = Strlen(xv); 284 len >>= 1; 285 clen = len * 12; /* "??;??;??;??\0" */ 286 init(clen, 0); 287 288 /* Prevent from crashing if unknown parameters are given. */ 289 omark = cleanup_push_mark(); 290 getexit(osetexit); 291 292 /* init pointers */ 293 294 if (setexit() == 0) { 295 const Char *v = xv; 296 char *c = colors; 297 298 int fg, bg; 299 for (i = 0; i < len; i++) { 300 fg = color(*v++); 301 if (fg == -1) 302 stderror(ERR_BADCOLORVAR, v[-1], '?'); 303 304 bg = color(*v++); 305 if (bg == -1) 306 stderror(ERR_BADCOLORVAR, '?', v[-1]); 307 makecolor(&c, fg, bg, &variables[map[i]].color); 308 } 309 310 } 311 cleanup_pop_mark(omark); 312 resexit(osetexit); 313 } 314 315 /* parseLS_COLORS(): 316 * Parse the LS_COLORS environment variable 317 */ 318 void 319 parseLS_COLORS(const Char *value) 320 { 321 size_t i, len; 322 const Char *v; /* pointer in value */ 323 char *c; /* pointer in colors */ 324 Extension *volatile e; /* pointer in extensions */ 325 jmp_buf_t osetexit; 326 size_t omark; 327 328 (void) &e; 329 330 331 if (value == NULL) { 332 init(0, 0); 333 return; 334 } 335 336 len = Strlen(value); 337 /* allocate memory */ 338 i = 1; 339 for (v = value; *v; v++) 340 if ((*v & CHAR) == ':') 341 i++; 342 343 init(len, i); 344 345 /* init pointers */ 346 v = value; 347 c = colors; 348 e = extensions; 349 350 /* Prevent from crashing if unknown parameters are given. */ 351 352 omark = cleanup_push_mark(); 353 getexit(osetexit); 354 355 if (setexit() == 0) { 356 357 /* parse */ 358 while (*v) { 359 switch (*v & CHAR) { 360 case ':': 361 v++; 362 continue; 363 364 case '*': /* :*ext=color: */ 365 v++; 366 if (getstring(&c, &v, &e->extension, '=') && 367 0 < e->extension.len) { 368 v++; 369 getstring(&c, &v, &e->color, ':'); 370 e++; 371 continue; 372 } 373 break; 374 375 default: /* :vl=color: */ 376 if (v[0] && v[1] && (v[2] & CHAR) == '=') { 377 for (i = 0; i < nvariables; i++) 378 if ((Char)variables[i].variable[0] == (v[0] & CHAR) && 379 (Char)variables[i].variable[1] == (v[1] & CHAR)) 380 break; 381 if (i < nvariables) { 382 v += 3; 383 getstring(&c, &v, &variables[i].color, ':'); 384 continue; 385 } 386 else 387 stderror(ERR_BADCOLORVAR, v[0], v[1]); 388 } 389 break; 390 } 391 while (*v && (*v & CHAR) != ':') 392 v++; 393 } 394 } 395 396 cleanup_pop_mark(omark); 397 resexit(osetexit); 398 399 nextensions = e - extensions; 400 } 401 402 /* put_color(): 403 */ 404 static void 405 put_color(const Str *colorp) 406 { 407 size_t i; 408 const char *c = colorp->s; 409 int original_output_raw = output_raw; 410 411 output_raw = TRUE; 412 cleanup_push(&original_output_raw, output_raw_restore); 413 for (i = colorp->len; 0 < i; i--) 414 xputchar(*c++); 415 cleanup_until(&original_output_raw); 416 } 417 418 419 /* print_color(): 420 */ 421 static void 422 print_color(const Char *fname, size_t len, Char suffix) 423 { 424 size_t i; 425 char *filename = short2str(fname); 426 char *last = filename + len; 427 Str *colorp = &variables[VFile].color; 428 429 switch (suffix) { 430 case '>': /* File is a symbolic link pointing to 431 * a directory */ 432 colorp = &variables[VDir].color; 433 break; 434 case '+': /* File is a hidden directory [aix] or 435 * context dependent [hpux] */ 436 case ':': /* File is network special [hpux] */ 437 break; 438 default: 439 for (i = 0; i < nvariables; i++) 440 if (variables[i].suffix != NOS && 441 (Char)variables[i].suffix == suffix) { 442 colorp = &variables[i].color; 443 break; 444 } 445 if (i == nvariables) { 446 for (i = 0; i < nextensions; i++) 447 if (len >= extensions[i].extension.len 448 && strncmp(last - extensions[i].extension.len, 449 extensions[i].extension.s, 450 extensions[i].extension.len) == 0) { 451 colorp = &extensions[i].color; 452 break; 453 } 454 } 455 break; 456 } 457 458 put_color(&variables[VLeft].color); 459 put_color(colorp); 460 put_color(&variables[VRight].color); 461 } 462 463 464 /* print_with_color(): 465 */ 466 void 467 print_with_color(const Char *filename, size_t len, Char suffix) 468 { 469 if (color_context_lsmF && 470 (haderr ? (didfds ? is2atty : isdiagatty) : 471 (didfds ? is1atty : isoutatty))) { 472 print_color(filename, len, suffix); 473 xprintf("%S", filename); 474 if (0 < variables[VEnd].color.len) 475 put_color(&variables[VEnd].color); 476 else { 477 put_color(&variables[VLeft].color); 478 put_color(&variables[VNormal].color); 479 put_color(&variables[VRight].color); 480 } 481 } 482 else 483 xprintf("%S", filename); 484 xputwchar(suffix); 485 } 486 487 488 #endif /* COLOR_LS_F */ 489