bug-diffutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-diffutils] bug#20062: bug#20062: bug#20062: [PATCH] diff: add suppo


From: Giuseppe Scrivano
Subject: [bug-diffutils] bug#20062: bug#20062: bug#20062: [PATCH] diff: add support for --color
Date: Thu, 12 Mar 2015 22:18:18 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.4 (gnu/linux)

Paul Eggert <address@hidden> writes:

> Giuseppe Scrivano wrote:
>> is it ok for now to keep these calls to sigprocmask and fix all the
>> other issues you reported (until the ls.c code is Gnulib-ized)?
>
> Sure, if removing sigprocmask calls is merely an efficiency thing.
> (It may be that the cost of the sigprocmask calls is not significant,
> so we can leave them in.)  Correctness is the bigger problem here.

I've attached a new version where I leave sigprocmask when colors are
used on a tty.

Regards,
Giuseppe

>From 68c9ac2ba4c3baf8ce3202c59fa931367a0ec215 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Sun, 8 Mar 2015 22:45:11 +0100
Subject: [PATCH] diff: add support for --color

* bootstrap.conf (gnulib_modules): Add "sigprocmask" and "mbiter".
* doc/diffutils.texi (diff Options): Add documentation for --color.
Copied from coreutils ls --color.
* src/context.c (pr_unidiff_hunk): Add calls to set_color_context.
* src/diff.h (enum colors): New enum to register the current color to use.
(enum colors_style): New enum to record when to use colors.
(colors_style): New variable to memorize the argument value.
(set_color_context): Add function definition.
* src/diff.c: : Define COLOR_OPTION.
(specify_colors_style): New function.
(longopts): Add --color.
(main): Handle --color argument.
(option_help_msgid): Add usage string for --color.
* src/normal.c (print_normal_hunk): Add calls to set_color_context
* src/side.c (print_1sdiff_line): Add calls to set_color_context.
* src/util.c (colors_enabled): New boolean variable.
(colors_enabled): New boolean variable.
(check_color_output): New function.
(begin_output): Call check_color_output every time the output file is changed.
(set_color_context): New function.  If colors are enabled, print the right
command for the terminal to change the color.
---
 bootstrap.conf     |   2 +
 doc/diffutils.texi |  20 ++++++++
 src/context.c      |   4 ++
 src/diff.c         |  30 +++++++++++-
 src/diff.h         |  34 ++++++++++++++
 src/normal.c       |  20 ++++++--
 src/side.c         |  15 ++++++
 src/util.c         | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 8 files changed, 251 insertions(+), 10 deletions(-)

diff --git a/bootstrap.conf b/bootstrap.conf
index 9b2de22..4825401 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -51,6 +51,7 @@ largefile
 lstat
 maintainer-makefile
 manywarnings
+mbiter
 mbrtowc
 mkstemp
 mktime
@@ -61,6 +62,7 @@ readme-release
 regex
 sh-quote
 signal
+sigprocmask
 stat
 stat-macros
 stat-time
diff --git a/doc/diffutils.texi b/doc/diffutils.texi
index 3e25807..0a2f1fc 100644
--- a/doc/diffutils.texi
+++ b/doc/diffutils.texi
@@ -3745,6 +3745,26 @@ Read and write data in binary mode.  @xref{Binary}.
 Use the context output format, showing three lines of context.
 @xref{Context Format}.
 
address@hidden --color address@hidden
address@hidden color, distinguishing file types with
+Specify whether to use color for distinguishing file types.  @var{when}
+may be omitted, or one of:
address@hidden @bullet
address@hidden none
address@hidden none @r{color option}
+Do not use color at all.  This is the default when no --color option
+is present.
address@hidden auto
address@hidden auto @r{color option}
address@hidden terminal, using color iff
+Only use color if standard output is a terminal.
address@hidden always
address@hidden always @r{color option}
+Always use color.
address@hidden itemize
+Specifying @option{--color} and no @var{when} is equivalent to
address@hidden
+
 @item -C @var{lines}
 @itemx address@hidden@address@hidden
 Use the context output format, showing @var{lines} (an integer) lines of
diff --git a/src/context.c b/src/context.c
index e0f21c4..88ddfde 100644
--- a/src/context.c
+++ b/src/context.c
@@ -366,10 +366,12 @@ pr_unidiff_hunk (struct change *hunk)
          while (k--)
            {
              char const * const *line = &files[0].linbuf[i++];
+              set_color_context (DELETE);
              putc ('-', out);
              if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
                putc ('\t', out);
              print_1_line (NULL, line);
+              set_color_context (RESET);
            }
 
          /* Then output the inserted part. */
@@ -378,10 +380,12 @@ pr_unidiff_hunk (struct change *hunk)
          while (k--)
            {
              char const * const *line = &files[1].linbuf[j++];
+              set_color_context (ADD);
              putc ('+', out);
              if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
                putc ('\t', out);
              print_1_line (NULL, line);
+              set_color_context (RESET);
            }
 
          /* We're done with this hunk, so on to the next! */
diff --git a/src/diff.c b/src/diff.c
index ff28377..07fcb57 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -70,6 +70,7 @@ static void add_regexp (struct regexp_list *, char const *);
 static void summarize_regexp_list (struct regexp_list *);
 static void specify_style (enum output_style);
 static void specify_value (char const **, char const *, char const *);
+static void specify_colors_style (char const *);
 static void try_help (char const *, char const *) __attribute__((noreturn));
 static void check_stdout (void);
 static void usage (void);
@@ -136,7 +137,9 @@ enum
   UNCHANGED_GROUP_FORMAT_OPTION,
   OLD_GROUP_FORMAT_OPTION,
   NEW_GROUP_FORMAT_OPTION,
-  CHANGED_GROUP_FORMAT_OPTION
+  CHANGED_GROUP_FORMAT_OPTION,
+
+  COLOR_OPTION,
 };
 
 static char const group_format_option[][sizeof "--unchanged-group-format"] =
@@ -159,6 +162,7 @@ static struct option const longopts[] =
   {"binary", 0, 0, BINARY_OPTION},
   {"brief", 0, 0, 'q'},
   {"changed-group-format", 1, 0, CHANGED_GROUP_FORMAT_OPTION},
+  {"color", 2, 0, COLOR_OPTION},
   {"context", 2, 0, 'C'},
   {"ed", 0, 0, 'e'},
   {"exclude", 1, 0, 'x'},
@@ -627,6 +631,10 @@ main (int argc, char **argv)
          specify_value (&group_format[c], optarg, group_format_option[c]);
          break;
 
+        case COLOR_OPTION:
+          specify_colors_style (optarg);
+         break;
+
        default:
          try_help (NULL, NULL);
        }
@@ -645,6 +653,9 @@ main (int argc, char **argv)
        specify_style (OUTPUT_NORMAL);
     }
 
+  if (colors_style != NEVER && paginate)
+    error (EXIT_TROUBLE, 0, _("Cannot specify both --color and --paginate."));
+
   if (output_style != OUTPUT_CONTEXT || hard_locale (LC_TIME))
     {
 #if (defined STAT_TIMESPEC || defined STAT_TIMESPEC_NS \
@@ -940,6 +951,8 @@ static char const * const option_help_msgid[] = {
   N_("-d, --minimal            try hard to find a smaller set of changes"),
   N_("    --horizon-lines=NUM  keep NUM lines of the common prefix and 
suffix"),
   N_("    --speed-large-files  assume large files and many scattered small 
changes"),
+  N_("    --color[=WHEN]         colorize the output; WHEN can be 'never', 
'always',"),
+  N_("                             or 'auto' (the default)"),
   "",
   N_("    --help               display this help and exit"),
   N_("-v, --version            output version information and exit"),
@@ -1008,6 +1021,21 @@ specify_style (enum output_style style)
       output_style = style;
     }
 }
+
+/* Set the color mode.  */
+static void
+specify_colors_style (char const *value)
+{
+  if (value == NULL || STREQ (value, "auto"))
+    colors_style = AUTO;
+  else if (STREQ (value, "always"))
+    colors_style = ALWAYS;
+  else if (STREQ (value, "never"))
+    colors_style = NEVER;
+  else
+    try_help ("invalid color '%s'", value);
+}
+
 
 /* Set the last-modified time of *ST to be the current time.  */
 
diff --git a/src/diff.h b/src/diff.h
index 465e4bc..0291fdd 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -38,6 +38,36 @@ enum changes
   /* Both deletes and inserts: a hunk containing both old and new lines.  */
   CHANGED
 };
+
+/* What kind of changes a hunk contains.  */
+enum colors
+{
+  /* Reset to the default color.  */
+  RESET,
+
+  /* Delete lines.  Show output in red.  */
+  DELETE,
+
+  /* Added lines.  Show them in green.  */
+  ADD,
+
+  /* Does not modify the context.  Use it to periodically process pending
+     signals.  */
+  SAME,
+};
+
+/* What kind of changes a hunk contains.  */
+enum colors_style
+{
+  /* Never output colors.  */
+  NEVER,
+
+  /* Output colors if the output is a terminal.  */
+  AUTO,
+
+  /* Always output colors.  */
+  ALWAYS,
+};
 
 /* Variables for command line options */
 
@@ -83,6 +113,9 @@ enum output_style
 
 XTERN enum output_style output_style;
 
+/* True if colors are printed.  */
+XTERN enum colors_style colors_style;
+
 /* Nonzero if output cannot be generated for identical files.  */
 XTERN bool no_diff_means_no_output;
 
@@ -390,3 +423,4 @@ extern void print_script (struct change *, struct change * 
(*) (struct change *)
 extern void setup_output (char const *, char const *, bool);
 extern void translate_range (struct file_data const *, lin, lin,
                              long int *, long int *);
+extern void set_color_context (enum colors);
diff --git a/src/normal.c b/src/normal.c
index 721fd1a..0d53033 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -56,14 +56,26 @@ print_normal_hunk (struct change *hunk)
 
   /* Print the lines that the first file has.  */
   if (changes & OLD)
-    for (i = first0; i <= last0; i++)
-      print_1_line ("<", &files[0].linbuf[i]);
+    {
+      for (i = first0; i <= last0; i++)
+        {
+          set_color_context (DELETE);
+          print_1_line ("<", &files[0].linbuf[i]);
+          set_color_context (RESET);
+        }
+    }
 
   if (changes == CHANGED)
     fputs ("---\n", outfile);
 
   /* Print the lines that the second file has.  */
   if (changes & NEW)
-    for (i = first1; i <= last1; i++)
-      print_1_line (">", &files[1].linbuf[i]);
+    {
+      for (i = first1; i <= last1; i++)
+        {
+          set_color_context (ADD);
+          print_1_line (">", &files[1].linbuf[i]);
+          set_color_context (RESET);
+        }
+    }
 }
diff --git a/src/side.c b/src/side.c
index 155512c..e52454e 100644
--- a/src/side.c
+++ b/src/side.c
@@ -206,6 +206,18 @@ print_1sdiff_line (char const *const *left, char sep,
   size_t c2o = sdiff_column2_offset;
   size_t col = 0;
   bool put_newline = false;
+  bool color_to_reset = false;
+
+  if (sep == '<')
+    {
+      set_color_context (DELETE);
+      color_to_reset = true;
+    }
+  else if (sep == '>')
+    {
+      set_color_context (ADD);
+      color_to_reset = true;
+    }
 
   if (left)
     {
@@ -233,6 +245,9 @@ print_1sdiff_line (char const *const *left, char sep,
 
   if (put_newline)
     putc ('\n', out);
+
+  if (color_to_reset)
+    set_color_context (RESET);
 }
 
 /* Print lines common to both files in side-by-side format.  */
diff --git a/src/util.c b/src/util.c
index 2d6d3fc..e7161d2 100644
--- a/src/util.c
+++ b/src/util.c
@@ -24,6 +24,8 @@
 #include <system-quote.h>
 #include <xalloc.h>
 #include "xvasprintf.h"
+#include <signal.h>
+#include <mbiter.h>
 
 char const pr_program[] = PR_PROGRAM;
 
@@ -153,6 +155,21 @@ print_message_queue (void)
 static char const *current_name0;
 static char const *current_name1;
 static bool currently_recursive;
+static bool colors_enabled;
+static bool output_is_tty;
+
+static void
+check_color_output (bool is_pipe)
+{
+  if (! outfile)
+    return;
+
+  output_is_tty = (colors_style != NEVER && !is_pipe
+                   && isatty (fileno (outfile)));
+
+  colors_enabled = (colors_style == ALWAYS
+                    || (colors_style == AUTO && output_is_tty));
+}
 
 void
 setup_output (char const *name0, char const *name1, bool recursive)
@@ -313,6 +330,7 @@ begin_output (void)
            outfile = fdopen (pipes[1], "w");
            if (!outfile)
              pfatal_with_name ("fdopen");
+            check_color_output (true);
          }
 #else
        char *command = system_quote_argv (SCI_SYSTEM, (char **) argv);
@@ -320,6 +338,7 @@ begin_output (void)
        outfile = popen (command, "w");
        if (!outfile)
          pfatal_with_name (command);
+        check_color_output (true);
        free (command);
 #endif
       }
@@ -330,6 +349,7 @@ begin_output (void)
       /* If -l was not specified, output the diff straight to 'stdout'.  */
 
       outfile = stdout;
+      check_color_output (false);
 
       /* If handling multiple files (because scanning a directory),
         print which files the following output is about.  */
@@ -672,8 +692,32 @@ void
 output_1_line (char const *base, char const *limit, char const *flag_format,
               char const *line_flag)
 {
+  /* Try to avoid to block for too long and write more than MAX_CHUNK bytes 
before
+     checking for pending signals.  */
+  const size_t max_chunk = 4096;
   if (!expand_tabs)
-    fwrite (base, sizeof (char), limit - base, outfile);
+    {
+      if (!colors_enabled || !output_is_tty)
+        {
+          fwrite (base, sizeof (char), limit - base, outfile);
+        }
+      else
+        {
+          size_t written = 0;
+          mbi_iterator_t iter;
+          for (mbi_init (iter, base, limit - base); mbi_avail (iter); 
mbi_advance (iter))
+            {
+              size_t mbi_len = mb_len (mbi_cur (iter));
+              fwrite (mbi_cur_ptr (iter), mbi_len, 1, outfile);
+              written += mbi_len;
+              if (written >= max_chunk)
+                {
+                  set_color_context (SAME);
+                  written = 0;
+                }
+            }
+        }
+    }
   else
     {
       register FILE *out = outfile;
@@ -681,6 +725,7 @@ output_1_line (char const *base, char const *limit, char 
const *flag_format,
       register char const *t = base;
       register size_t column = 0;
       size_t tab_size = tabsize;
+      size_t written = 0;
 
       while (t < limit)
        switch ((c = *t++))
@@ -690,7 +735,9 @@ output_1_line (char const *base, char const *limit, char 
const *flag_format,
              size_t spaces = tab_size - column % tab_size;
              column += spaces;
              do
-               putc (' ', out);
+                {
+                  written += putc (' ', out);
+                }
              while (--spaces);
            }
            break;
@@ -698,7 +745,7 @@ output_1_line (char const *base, char const *limit, char 
const *flag_format,
          case '\r':
            putc (c, out);
            if (flag_format && t < limit && *t != '\n')
-             fprintf (out, flag_format, line_flag);
+             written += fprintf (out, flag_format, line_flag);
            column = 0;
            break;
 
@@ -706,17 +753,96 @@ output_1_line (char const *base, char const *limit, char 
const *flag_format,
            if (column == 0)
              continue;
            column--;
-           putc (c, out);
+           written += putc (c, out);
            break;
 
          default:
            column += isprint (c) != 0;
-           putc (c, out);
+           written += putc (c, out);
+            if (written >= max_chunk)
+              {
+                set_color_context (SAME);
+                written = 0;
+              }
            break;
          }
     }
 }
 
+static sigset_t old_sigproc_set;
+void
+set_color_context (enum colors con)
+{
+  int j;
+  static sigset_t set;
+  static bool set_initialized;
+  static enum colors last_color_context = RESET;
+  static int const sig[] =
+    {
+      SIGTSTP,
+      SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+#ifdef SIGPOLL
+      SIGPOLL,
+#endif
+#ifdef SIGPROF
+      SIGPROF,
+#endif
+#ifdef SIGVTALRM
+      SIGVTALRM,
+#endif
+#ifdef SIGXCPU
+      SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+      SIGXFSZ,
+#endif
+    };
+
+  if (!colors_enabled || (con == SAME && !output_is_tty))
+    return;
+
+  if (output_is_tty && !set_initialized)
+    {
+      sigemptyset (&set);
+      for (j = 0; j < sizeof (sig) / sizeof (*sig); j++)
+        sigaddset (&set, sig[j]);
+      set_initialized = true;
+    }
+
+repeat:
+  switch (con)
+    {
+    case DELETE:
+      if (output_is_tty)
+        sigprocmask (SIG_BLOCK, &set, &old_sigproc_set);
+      fprintf (outfile, "\x1B[31m");
+      break;
+
+    case ADD:
+      if (output_is_tty)
+        sigprocmask (SIG_BLOCK, &set, &old_sigproc_set);
+      fprintf (outfile, "\x1B[32m");
+      break;
+
+    case SAME:
+    case RESET:
+      fprintf (outfile, "\x1b[0m");
+      if (output_is_tty)
+        {
+          fflush (outfile);
+          sigprocmask (SIG_SETMASK, &old_sigproc_set, NULL);
+        }
+      if (con == SAME)
+        {
+          con = last_color_context;
+          goto repeat;
+        }
+      break;
+    }
+
+  last_color_context = con;
+}
+
 char const change_letter[] = { 0, 'd', 'a', 'c' };
 
 /* Translate an internal line number (an index into diff's table of lines)
-- 
2.1.0


reply via email to

[Prev in Thread] Current Thread [Next in Thread]