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