]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
`colorize' patch from Drepper.
authorJim Meyering <jim@meyering.net>
Mon, 28 Nov 1994 04:25:31 +0000 (04:25 +0000)
committerJim Meyering <jim@meyering.net>
Mon, 28 Nov 1994 04:25:31 +0000 (04:25 +0000)
src/ls.c

index e4fc6995b9825813c0919d8cdd4ca342e43287c4..6c05c9d7ba775c173b50d2418761dd6c41eaf64d 100644 (file)
--- a/src/ls.c
+++ b/src/ls.c
@@ -112,6 +112,7 @@ static int rev_cmp_name ();
 static int compare_extension ();
 static int rev_cmp_extension ();
 static int decode_switches ();
+static void parse_ls_color ();
 static int file_interesting ();
 static int gobble_file ();
 static int is_not_dot_or_dotdot ();
@@ -130,6 +131,8 @@ static void print_long_format ();
 static void print_many_per_line ();
 static void print_name_with_quoting ();
 static void print_type_indicator ();
+static void print_color_indicator ();
+static void put_indicator ();
 static void print_with_commas ();
 static void queue_directory ();
 static void sort_files ();
@@ -303,6 +306,63 @@ enum indicator_style
 
 static enum indicator_style indicator_style;
 
+/* Nonzero means use colors to mark types.  Also define the different
+   colors as well as the stuff for the LS_COLORS environment variable.
+   The LS_COLORS variable is now in a termcap-like format.  -o or
+   --color-if-tty. */
+
+int print_with_color;
+
+enum color_type
+{
+  color_no,                   /* 0: default or --color=no */
+  color_yes,                  /* 1: -o or --color=yes */
+  color_if_tty                        /* 2: --color=tty */
+};
+
+/* Note that color_no and color_yes equals boolean values; they will
+   be assigned to print_with_color which is a boolean variable */
+
+#define MAXCOLORLEN  16      /* Max # of characters in a color sequence */
+
+enum indicator_no
+{ C_LEFT, C_RIGHT, C_END, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK, 
+  C_BLK, C_CHR, C_EXEC };
+
+char *indicator_name[]=
+{
+  "lc","rc","ec","fi","di","ln","pi","so","bd","cd","ex",NULL
+};
+
+struct indicator_type
+{
+  int len;
+  char string[MAXCOLORLEN];
+};
+
+struct indicator_type color_indicator[] =
+{
+  { 2, "\033[" },             /* lc: Left of color sequence */
+  { 1, "m" },                 /* rc: Right of color sequence */
+  { 4, "\033[0m" },           /* ec: End color */
+  { 1, "0" },                 /* fi: File: default */
+  { 2, "32" },                /* di: Directory: green */
+  { 2, "36" },                /* ln: Symlink: cyan */
+  { 2, "31" },                /* pi: Pipe: red */
+  { 2, "33" },                /* so: Socket: yellow/brown */
+  { 5, "44;37" },             /* bd: Block device: white on blue */
+  { 5, "44;37" },             /* cd: Char device: white on blue */
+  { 2, "35" },                /* ex: Executable: purple */
+};
+
+/* Nonzero means print using ISO 8859 characters.  The default is specified
+   here as well.  -8 enables, -7 disables.  */
+
+int print_iso8859;
+#ifndef DEFAULT_ISO8859
+#define DEFAULT_ISO8859  1
+#endif
+
 /* Nonzero means mention the inode number of each file.  -i  */
 
 static int print_inode;
@@ -423,6 +483,10 @@ static struct option const long_options[] =
   {"time", required_argument, 0, 11},
   {"help", no_argument, &show_help, 1},
   {"version", no_argument, &show_version, 1},
+  {"color", optional_argument, 0, 'o'},
+  {"colour", optional_argument, 0, 'o'},
+  {"7bit", no_argument, 0, '7'},
+  {"8bit", no_argument, 0, '8'},
   {0, 0, 0, 0}
 };
 
@@ -501,6 +565,17 @@ static enum time_type const time_types[] =
   time_atime, time_atime, time_atime, time_ctime, time_ctime
 };
 
+static char const* color_args[] = 
+{
+  /* Note: "no" is a prefix of "none" so we don't include it */
+  "yes", "force", "none", "tty", "if-tty"
+};
+
+static enum color_type const color_types[] =
+{
+  color_yes, color_yes, color_no, color_if_tty, color_if_tty
+};
+
 \f
 /* Write to standard output the string PREFIX followed by a space-separated
    list of the integers stored in OS all on one line.  */
@@ -550,7 +625,7 @@ main (argc, argv)
   format_needs_stat = sort_type == sort_time || sort_type == sort_size
     || format == long_format
     || trace_links || trace_dirs || indicator_style != none
-    || print_block_size || print_inode;
+    || print_block_size || print_inode || print_with_color;
 
   if (dired && format == long_format)
     {
@@ -682,6 +757,8 @@ decode_switches (argc, argv)
   really_all_files = 0;
   ignore_patterns = 0;
   quote_as_string = 0;
+  print_with_color = 0;
+  print_iso8859 = DEFAULT_ISO8859;  
 
   p = getenv ("COLUMNS");
   line_length = p ? atoi (p) : 80;
@@ -697,8 +774,11 @@ decode_switches (argc, argv)
 
   p = getenv ("TABSIZE");
   tabsize = p ? atoi (p) : 8;
+  if (tabsize < 1)
+    error (1, 0, "invalid tab size in enironment variable TABSIZE: %s",
+          optarg);
 
-  while ((c = getopt_long (argc, argv, "abcdfgiklmnpqrstuw:xABCDFGI:LNQRST:UX1",
+  while ((c = getopt_long (argc, argv, "abcdfgiklmnopqrstuw:xABCDFGI:LNQRST:UX178",
                           long_options, (int *) 0)) != EOF)
     {
       switch (c)
@@ -759,6 +839,30 @@ decode_switches (argc, argv)
          numeric_users = 1;
          break;
 
+      case 'o':
+        if (optarg)
+          {
+            i = argmatch(optarg, color_args);
+            if (i < 0)
+              {
+                invalid_arg("colorization criterion", optarg, i);
+                usage(1);
+              }
+            i = color_types[i];
+          }
+        else
+          i = color_yes;      /* -o or --color -> do colorize */
+
+        if ( i == color_if_tty )
+          print_with_color = isatty(1) ? 1 : 0;
+        else
+          print_with_color = i;
+
+        if ( print_with_color )
+          tabsize = line_length; /* Some systems don't like tabs and
+                                    color codes in combination */
+        break;
+
        case 'p':
          indicator_style = not_programs;
          break;
@@ -864,7 +968,15 @@ decode_switches (argc, argv)
          format = one_per_line;
          break;
 
-       case 10:                /* +sort */
+       case '7':
+         print_iso8859 = 0;
+          break;
+
+        case '8':
+          print_iso8859 = 1;
+          break;
+
+       case 10:                /* --sort */
          i = argmatch (optarg, sort_args);
          if (i < 0)
            {
@@ -874,7 +986,7 @@ decode_switches (argc, argv)
          sort_type = sort_types[i];
          break;
 
-       case 11:                /* +time */
+       case 11:                /* --time */
          i = argmatch (optarg, time_args);
          if (i < 0)
            {
@@ -884,7 +996,7 @@ decode_switches (argc, argv)
          time_type = time_types[i];
          break;
 
-       case 12:                /* +format */
+       case 12:                /* --format */
          i = argmatch (optarg, format_args);
          if (i < 0)
            {
@@ -899,9 +1011,259 @@ decode_switches (argc, argv)
        }
     }
 
+  if ( print_with_color )
+    {
+      parse_ls_color();
+    } 
+
   return optind;
 }
 \f
+/* Parse the LS_COLORS/LS_COLOURS variable */
+
+static void
+parse_ls_color ()
+{
+  register char *p;           /* Pointer to character being parsed */
+  char *whichvar;             /* LS_COLORS or LS_COLOURS? */
+  int state;                  /* State of parser */
+  int ind_no;                 /* Indicator number */
+  int ccount;                 /* Character count */
+  int num;                    /* Escape char numeral */
+  char label[3] = "??";               /* Indicator label */
+  
+  if ( (p = getenv(whichvar = "LS_COLORS")) ||
+       (p = getenv(whichvar = "LS_COLOURS")) )
+    {
+      state = 1;
+      while ( state > 0 )
+      {
+        switch(state)
+          {
+          case 1:             /* First label character */
+            if ( *p )
+              {
+                label[0] = *(p++);
+                state = 2;
+              }
+            else
+              state = 0;      /* Done */
+            break;
+            
+          case 2:             /* Second label character */
+            if ( *p )
+              {
+                label[1] = *(p++);
+                state = 3;
+              }
+            else
+              state = -1;     /* Error */
+            break;
+            
+          case 3:             /* Should be equal sign */
+            if ( *(p++) != '=' )
+              state = -1; /* Error state */
+            else
+              {
+                ind_no = 0;
+                state = -1; /* In case we fail */
+                while ( indicator_name[ind_no] != NULL )
+                  {
+                    if ( strcmp(label,indicator_name[ind_no]) == 0 )
+                      {
+                        state = 4;  /* We found it */
+                        ccount = 0; /* Nothing stored yet */
+                        break;
+                     }
+                    else
+                      ind_no++;
+                  }
+              }
+            break;
+            
+          case 4:             /* Character to store */
+            switch ( *p )
+              {
+              case ':':
+                color_indicator[ind_no].len = ccount;
+                state = 1;
+                break;
+              case '\\':
+                /* The escape sequence will always generate a character,
+                   so enter error state if the buffer is full */
+                state = ( ccount >= MAXCOLORLEN ) ? -1 : 5;
+                break;
+              case '^':
+                /* Control character in the ^X notation */
+                state = ( ccount >= MAXCOLORLEN ) ? -1 : 8;
+                break;
+              case '\0':
+                color_indicator[ind_no].len = ccount;
+                state = 0; /* Done */
+                break;
+              default:
+                if ( ccount >= MAXCOLORLEN )
+                  state = -1; /* Too long */
+                else
+                  color_indicator[ind_no].string[ccount++] = *p;
+              }
+            p++;
+            break;
+            
+          case 5:             /* Escape character */
+            num = -1;
+            switch( *p )
+              {
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+                state = 6; /* Octal numeral */
+                num = *p - '0';
+                break;
+              case 'x':
+              case 'X':
+                state = 7; /* Hex numeral */
+                num = 0;
+                break;
+              case 'a':       /* Bell */
+                num = 7;
+                break;
+              case 'b':       /* Backspace */
+                num = '\b';
+                break;
+              case 'e':       /* Escape */
+                num = 27;
+                break;
+              case 'f':       /* Formfeed */
+                num = '\f';
+                break;
+              case 'n':       /* Newline */
+                num = '\n';
+                break;
+              case 'r':       /* Return */
+                num = '\r';
+                break;
+              case 't':       /* Tab */
+                num = '\t';
+                break;
+              case 'v':       /* Vtab */
+                num = '\v';
+                break;
+              case '?':       /* Delete */
+                num = 127;
+                break;
+              case '\0':      /* End of string */
+                state = -1; /* Error */
+                break;
+              default:        /* Escaped character */
+                num = *p;
+                break;
+              }
+            if ( state == 5 )
+              {
+                color_indicator[ind_no].string[ccount++] = num;
+                state = 4;
+              }
+            p++;
+            break;
+            
+          case 6:             /* Octal numeral */
+            switch ( *p )
+              {
+              case ':':
+              case '\0':
+                color_indicator[ind_no].string[ccount++] = num;
+                color_indicator[ind_no].len = ccount;
+                state = ( *(p++) == ':' ) ? 1 : 0;
+                p++;
+                break;
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+                num = ( num << 3 ) + ( *(p++) - '0' );
+                break;
+              default:
+                color_indicator[ind_no].string[ccount++] = num;
+                state = 4;
+                break;
+              }
+            break;
+            
+          case 7:             /* Hex numeral */
+            switch ( *p )
+              {
+              case ':':
+              case '\0':
+                color_indicator[ind_no].string[ccount++] = num;
+                color_indicator[ind_no].len = ccount;
+                state = ( *(p++) == ':' ) ? 1 : 0;
+                break;
+              case '0':
+              case '1':
+              case '2':
+              case '3':
+              case '4':
+              case '5':
+              case '6':
+              case '7':
+              case '8':
+              case '9':
+                num = ( num << 4 ) + ( *(p++) - '0' );
+                break;
+              case 'A':
+              case 'B':
+              case 'C':
+              case 'D':
+              case 'E':
+              case 'F':
+                num = ( num << 4 ) + ( *(p++) - 'A' + 10 );
+                break;
+              case 'a':
+              case 'b':
+              case 'c':
+              case 'd':
+              case 'e':
+              case 'f':
+                num = ( num << 4 ) + ( *(p++) - 'a' + 10 );
+                break;
+              default:
+                color_indicator[ind_no].string[ccount++] = num;
+                state = 4;
+                break;
+              }
+            break;
+
+          case 8:             /* ^ notation */
+            state = 4;        /* Usually the next state */
+            if ( *p >= '@' && *p <= '~' )
+                color_indicator[ind_no].string[ccount++] = *p & 037;
+            else if ( *p == '?' )
+              color_indicator[ind_no].string[ccount++] = 127;
+            else
+              state = -1;     /* Error */
+            p++;
+            break;
+          }
+      }
+      
+      if ( state < 0 )
+      {
+        fprintf(stderr,"Bad %s variable\n",whichvar);
+        print_with_color = 0;
+      }
+    }
+}
+\f
 /* Request that the directory named `name' have its contents listed later.
    If `realname' is nonzero, it will be used instead of `name' when the
    directory name is printed.  This allows symbolic links to directories
@@ -1124,7 +1486,7 @@ gobble_file (name, explicit_arg, dirname)
             they won't be traced and when no indicator is needed. */
          if (linkpath
              && ((explicit_arg && format != long_format)
-                  || indicator_style != none)
+                  || indicator_style != none || print_with_color)
              && SAFE_STAT (linkpath, &linkstats) == 0)
            {
              /* Symbolic links to directories that are mentioned on the
@@ -1617,7 +1979,11 @@ print_long_format (f)
   DIRED_INDENT ();
   FPUTS (bigbuf, stdout, p - bigbuf);
   PUSH_CURRENT_DIRED_POS (&dired_obstack);
+  if (print_with_color)
+    print_color_indicator (f->stat.st_mode);
   print_name_with_quoting (f->name);
+  if (print_with_color)
+    put_indicator(C_END);
   PUSH_CURRENT_DIRED_POS (&dired_obstack);
 
   if (f->filetype == symbolic_link)
@@ -1625,7 +1991,11 @@ print_long_format (f)
       if (f->linkname)
        {
          FPUTS_LITERAL (" -> ", stdout);
+         if (print_with_color)
+           print_color_indicator (f->linkmode);
          print_name_with_quoting (f->linkname);
+         if (print_with_color)
+           put_indicator(C_END);
          if (indicator_style != none)
            print_type_indicator (f->linkmode);
        }
@@ -1675,14 +2045,17 @@ quote_filename (p, quoted_length)
 
            default:
              /* FIXME: why not just use the ISPRINT macro here?  */
-             if (!(c > 040 && c < 0177))
+             if (!( (c > 040 && c < 0177)
+                    || (print_iso8859 && c >= 0200 && c <= 0377) ) )
                found_quotable = 1;
              break;
            }
        }
       else
        {
-         if (!(c >= 040 && c < 0177) && qmark_funny_chars)
+         if (!( (c >= 040 && c < 0177) 
+                || (print_iso8859 && c >= 0xA1 && c <= 0xFF) )
+             && qmark_funny_chars)
            found_quotable = 1;
        }
       if (found_quotable)
@@ -1746,7 +2119,8 @@ quote_filename (p, quoted_length)
              break;
 
            default:
-             if (c > 040 && c < 0177)
+             if ( (c > 040 && c < 0177)
+                  || (print_iso8859 && c >= 0200 && c <= 0377) )
                SAVECHAR (c);
              else
                {
@@ -1758,7 +2132,8 @@ quote_filename (p, quoted_length)
        }
       else
        {
-         if (c >= 040 && c < 0177)
+         if ( (c >= 040 && c < 0177)
+              || (print_iso8859 && c >= 0200 && c <= 0377) )
            SAVECHAR (c);
          else if (!qmark_funny_chars)
            SAVECHAR (c);
@@ -1806,7 +2181,11 @@ print_file_name_and_frills (f)
            (unsigned) convert_blocks (ST_NBLOCKS (f->stat),
                                       kilobyte_blocks));
 
+  if (print_with_color)
+    print_color_indicator (f->stat.st_mode);
   print_name_with_quoting (f->name);
+  if (print_with_color)
+    put_indicator(C_END);
 
   if (indicator_style != none)
     print_type_indicator (f->stat.st_mode);
@@ -1839,12 +2218,72 @@ print_type_indicator (mode)
     PUTCHAR ('*');
 }
 
+static void
+print_color_indicator (mode)
+     unsigned int mode;
+{
+  int i;
+  int shi;                    /* Bits to shift mode */
+  int type = C_FILE;
+  
+  if (S_ISDIR (mode))
+    type = C_DIR;
+  
+#ifdef S_ISLNK
+  else if (S_ISLNK (mode))
+    type = C_LINK;
+#endif
+
+#ifdef S_ISFIFO
+  else if (S_ISFIFO (mode))
+    type = C_FIFO;
+#endif
+
+#ifdef S_ISSOCK
+  else if (S_ISSOCK (mode))
+    type = C_SOCK;
+#endif
+
+#ifdef S_ISBLK
+  else if (S_ISBLK (mode))
+    type = C_BLK;
+#endif
+
+#ifdef S_ISCHR
+  else if (S_ISCHR (mode))
+    type = C_CHR;
+#endif
+
+  if ( type == C_FILE && (mode & (S_IEXEC|S_IEXEC>>3|S_IEXEC>>6)) )
+    type = C_EXEC;
+  
+  put_indicator(C_LEFT);
+  put_indicator(type);
+  put_indicator(C_RIGHT);
+}
+
+/* Output a color indicator (which may contain nulls) */
+static void
+put_indicator(n)
+     int n;
+{
+  register int i;
+  register char *p;
+
+  p = color_indicator[n].string;
+
+  for ( i = color_indicator[n].len ; i ; i-- )
+    {
+       putchar(*(p++));
+    }
+}
+
 static int
 length_of_file_name_and_frills (f)
      struct fileinfo *f;
 {
   register char *p = f->name;
-  register char c;
+  register unsigned char c;
   register int len = 0;
 
   if (print_inode)
@@ -1880,7 +2319,8 @@ length_of_file_name_and_frills (f)
              break;
 
            default:
-             if (c >= 040 && c < 0177)
+             if ( (c >= 040 && c < 0177)
+                 || (print_iso8859 && c >= 0200 && c <= 0377) )
                len += 1;
              else
                len += 4;
@@ -2134,6 +2574,8 @@ usage (status)
   -m                         fill width with a comma separated list of entries\n\
   -N, --literal              do not quote entry names\n\
   -n, --numeric-uid-gid      list numeric UIDs and GIDs instead of names\n\
+  -o, --color, --colour      colorize entries according to type\n\
+      --colo(u)r=WORD        yes -o, no, tty (if output is a terminal)\n\
   -p                         append a character for typing each entry\n\
   -Q, --quote-name           enclose entry names in double quotes\n\
   -q, --hide-control-chars   print ? instead of non graphic characters\n\
@@ -2154,6 +2596,8 @@ usage (status)
   -x                         list entries by lines instead of by columns\n\
   -X                         sort alphabetically by entry extension\n\
   -1                         list one file per line\n\
+  -7, --7bit                 allow only 7-bit ASCII characters to be printed\n\
+  -8, --8bit                 allow 8-bit ISO 8859 characters to be printed\n\
       --help                 display this help and exit\n\
       --version              output version information and exit\n\
 \n\