From 3885812c8c5a4fe35139eaa147974c5a577fefd0 Mon Sep 17 00:00:00 2001 From: Josef Karthauser Date: Fri, 2 Jun 2000 14:53:42 +0000 Subject: [PATCH] Add colour support to /bin/ls (at a cost of 1056 bytes on my system). It is not switched on by default and must be enabled with the -G flag. When using ls -G the output behaviour is modified with ANSI colour sequences wrapped around filenames to help distinguish file types. (Colours can be redefined in the LSCOLORS environment variable as described in the manual page.) Colour support is silently disabled (if switched on) if stdout isn't a tty. Based on: asami's colorls port. PR: bin/18900 && ports/18616. --- bin/ls/extern.h | 2 + bin/ls/ls.1 | 76 +++++++++++++++++++++++++++- bin/ls/ls.c | 16 ++++-- bin/ls/ls.h | 1 + bin/ls/print.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 4 deletions(-) diff --git a/bin/ls/extern.h b/bin/ls/extern.h index fd26b372c5f..25e79b0729d 100644 --- a/bin/ls/extern.h +++ b/bin/ls/extern.h @@ -51,3 +51,5 @@ void printscol __P((DISPLAY *)); void usage __P((void)); int len_octal __P((char *, int)); int prn_octal __P((char *)); +void parsecolors __P((char *cs)); +int colortype __P((mode_t mode)); diff --git a/bin/ls/ls.1 b/bin/ls/ls.1 index 6b29b1fce92..eebdf20f2f1 100644 --- a/bin/ls/ls.1 +++ b/bin/ls/ls.1 @@ -43,7 +43,7 @@ .Nd list directory contents .Sh SYNOPSIS .Nm ls -.Op Fl ABCFHLPRTWabcdfgiklnoqrstu1 +.Op Fl ABCFGHLPRTWabcdfgiklnoqrstu1 .Op Ar file ... .Sh DESCRIPTION For each operand that names a @@ -90,6 +90,12 @@ an equals sign (=) after each socket, a percent sign (%) after each whiteout, and a vertical bar (|) after each that is a .Tn FIFO . +.It Fl G +Use ANSI color sequences to distinguish file types. (See +.Ev LSCOLORS +below.) In addition to those mentioned above in +.Fl F , +some extra attributes (setuid bit set, etc.) are also displayed. .It Fl H Symbolic links on the command line are followed. This option is assumed if none of the @@ -386,6 +392,74 @@ The timezone to use when displaying dates. See .Xr environ 7 for more information. +.It LSCOLORS +The value of this variable describes what color to use for which +attribute when the color output +.Pq Fl G +is specified. This string is a concatenation of pairs of the format +.Sy fb , +where +.Sy f +is the foreground color and +.Sy b +is the background color. +.Pp +The color designators are as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy 0 +black +.It Sy 1 +red +.It Sy 2 +green +.It Sy 3 +yellow +.It Sy 4 +blue +.It Sy 5 +magenta +.It Sy 6 +cyan +.It Sy 7 +white +.It Sy x +default foreground or background +.El +.Pp +(Note: the above are standard ANSI colors. The actual display may +differ depending on the color capabilities of your terminal.) +.Pp +The order of the attributes are as follows: +.Pp +.Bl -enum -offset indent -compact +.It +directory +.It +symbolic link +.It +socket +.It +pipe +.It +executable +.It +block special +.It +character special +.It +executable with setuid bit set +.It +executable with setgid bit set +.It +directory writable to others, with sticky bit +.It +directory writable to others, without sticky bit +.El +.Pp +The default is "4x5x2x3x1x464301060203", i.e., blue foreground and +default background for regular directories, black foreground and red +background for setuid executables, etc. .It Ev LS_COLWIDTHS If this variable is set, it is considered to be a colon-delimited list of minimum column widths. Unreasonable diff --git a/bin/ls/ls.c b/bin/ls/ls.c index 754236b9450..e6b12f792b7 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -111,6 +111,7 @@ int f_statustime; /* use time of last mode change */ int f_timesort; /* sort by time vice name */ int f_type; /* add type character for non-regular files */ int f_whiteout; /* show whiteout entries */ +int f_color; /* add type in color for non-regular files */ int rval; @@ -148,7 +149,7 @@ main(argc, argv) f_listdot = 1; fts_options = FTS_PHYSICAL; - while ((ch = getopt(argc, argv, "1ABCFHLPRTWabcdfgiklnoqrstu")) != -1) { + while ((ch = getopt(argc, argv, "1ABCFGHLPRTWabcdfgiklnoqrstu")) != -1) { switch (ch) { /* * The -1, -C and -l options all override each other so shell @@ -186,6 +187,10 @@ main(argc, argv) case 'H': fts_options |= FTS_COMFOLLOW; break; + case 'G': + if (isatty(STDOUT_FILENO)) + f_color = 1; + break; case 'L': fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; @@ -259,11 +264,16 @@ main(argc, argv) argc -= optind; argv += optind; + if (f_color) + parsecolors(getenv("LSCOLORS")); + /* * If not -F, -i, -l, -s or -t options, don't require stat - * information. + * information, unless in color mode in which case we do + * need this to determine which colors to display. */ - if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type + && !f_color) fts_options |= FTS_NOSTAT; /* diff --git a/bin/ls/ls.h b/bin/ls/ls.h index 33c98c45aab..9fbe985df1b 100644 --- a/bin/ls/ls.h +++ b/bin/ls/ls.h @@ -53,6 +53,7 @@ extern int f_size; /* list size in short listing */ extern int f_statustime; /* use time of last mode change */ extern int f_notabs; /* don't use tab-separated multi-col output */ extern int f_type; /* add type character for non-regular files */ +extern int f_color; /* add type in color for non-regular files */ typedef struct { FTSENT *list; diff --git a/bin/ls/print.c b/bin/ls/print.c index 702b6b1ccc9..1cb367254f1 100644 --- a/bin/ls/print.c +++ b/bin/ls/print.c @@ -56,6 +56,7 @@ static const char rcsid[] = #include #include #include +#include #include "ls.h" #include "extern.h" @@ -67,6 +68,26 @@ static int printtype __P((u_int)); #define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) +/* Most of these are taken from */ +typedef enum Colors { + C_DIR, /* directory */ + C_LNK, /* symbolic link */ + C_SOCK, /* socket */ + C_FIFO, /* pipe */ + C_EXEC, /* executable */ + C_BLK, /* block special */ + C_CHR, /* character special */ + C_SUID, /* setuid executable */ + C_SGID, /* setgid executable */ + C_WSDIR, /* directory writeble to others, with sticky bit */ + C_WDIR, /* directory writeble to others, without sticky bit */ + C_NUMCOLORS /* just a place-holder */ +} Colors ; + +char *defcolors = "4x5x2x3x1x464301060203"; + +static int colors[C_NUMCOLORS][2]; + void printscol(dp) DISPLAY *dp; @@ -128,8 +149,12 @@ printlong(dp) printtime(sp->st_ctime); else printtime(sp->st_mtime); + if (f_color) + (void)colortype(sp->st_mode); if (f_octal || f_octal_escape) (void)prn_octal(p->fts_name); else (void)printf("%s", p->fts_name); + if (f_color) + (void)printf("\033[m"); if (f_type) (void)printtype(sp->st_mode); if (S_ISLNK(sp->st_mode)) @@ -199,6 +224,16 @@ printcol(dp) dp->s_block); if ((base += numrows) >= num) break; + /* + * some terminals get confused if we mix tabs + * with color sequences + */ + if (f_color) + while ((cnt = (chcnt + 1)) <= endcol) { + (void)putchar(' '); + chcnt = cnt; + } + else while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) <= endcol){ (void)putchar(f_notabs ? ' ' : '\t'); @@ -229,8 +264,12 @@ printaname(p, inodefield, sizefield) if (f_size) chcnt += printf("%*qd ", (int)sizefield, howmany(sp->st_blocks, blocksize)); + if (f_color) + (void)colortype(sp->st_mode); chcnt += (f_octal || f_octal_escape) ? prn_octal(p->fts_name) : printf("%s", p->fts_name); + if (f_color) + printf("\033[m"); if (f_type) chcnt += printtype(sp->st_mode); return (chcnt); @@ -294,6 +333,96 @@ printtype(mode) return (0); } +void +printcolor(c) + Colors c; +{ + printf("\033["); + if (colors[c][0] != -1) { + printf("3%d", colors[c][0]); + if (colors[c][1] != -1) + printf(";"); + } + if (colors[c][1] != -1) + printf("4%d", colors[c][1]); + printf("m"); +} + +int +colortype(mode) + mode_t mode; +{ + switch(mode & S_IFMT) { + case S_IFDIR: + if (mode & S_IWOTH) + if (mode & S_ISTXT) + printcolor(C_WSDIR); + else + printcolor(C_WDIR); + else + printcolor(C_DIR); + return(1); + case S_IFLNK: + printcolor(C_LNK); + return(1); + case S_IFSOCK: + printcolor(C_SOCK); + return(1); + case S_IFIFO: + printcolor(C_FIFO); + return(1); + case S_IFBLK: + printcolor(C_BLK); + return(1); + case S_IFCHR: + printcolor(C_CHR); + return(1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (mode & S_ISUID) + printcolor(C_SUID); + else if (mode & S_ISGID) + printcolor(C_SGID); + else + printcolor(C_EXEC); + return(1); + } + return(0); +} + +void +parsecolors(cs) +char *cs; +{ + int i, j, len; + char c[2]; + if (cs == NULL) cs = ""; /* LSCOLORS not set */ + len = strlen(cs); + for (i = 0 ; i < C_NUMCOLORS ; i++) { + if (len <= 2*i) { + c[0] = defcolors[2*i]; + c[1] = defcolors[2*i+1]; + } + else { + c[0] = cs[2*i]; + c[1] = cs[2*i+1]; + } + for (j = 0 ; j < 2 ; j++) { + if ((c[j] < '0' || c[j] > '7') && + tolower(c[j]) != 'x') { + fprintf(stderr, + "error: invalid character '%c' in LSCOLORS env var\n", + c[j]); + c[j] = defcolors[2*i+j]; + } + if (c[j] == 'x') + colors[i][j] = -1; + else + colors[i][j] = c[j]-'0'; + } + } +} + static void printlink(p) FTSENT *p;