Hi Paul, I've attached the last version of the patch. Is there anything left that should be done?
Thanks, Giuseppe
>From 8b39f7297c0137af62336ad79044271803b66299 Mon Sep 17 00:00:00 2001 From: Giuseppe Scrivano <gscri...@redhat.com> Date: Sun, 8 Mar 2015 22:45:11 +0100 Subject: [PATCH] diff: add support for --color * doc/diffutils.texi (diff Options): Add documentation for --color. Copied from coreutils ls --color. * src/context.c (pr_unidiff_hunk): Set the color context. (print_context_header): Likewise. (pr_context_hunk): Likewise. * src/diff.h (enum colors_style): New enum to record when to use colors. (colors_style): New variable to memorize the argument value. (set_add_color_context): Add function definition. (set_delete_color_context): Likewise. (set_header_color_context): Likewise. (set_line_numbers_color_context): Likewise. (reset_color_context): Likewise. * 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): Set the color context. * src/side.c (print_1sdiff_line): Likewise. * src/util.c (colors_enabled): New boolean variable. (begin_output): Call check_color_output once the output file is configured. (colors_enabled): New boolean variable. (check_color_output): New function. (install_signal_handlers): Likewise. (signal_handler): Likewise. (set_add_color_context): Likewise. (set_delete_color_context): Likewise. (reset_color_context): Likewise. (set_header_color_context): Likewise. (set_line_numbers_color_context): Likewise. --- doc/diffutils.texi | 21 +++++++++ src/context.c | 51 ++++++++++++++++++---- src/diff.c | 27 +++++++++++- src/diff.h | 21 +++++++++ src/normal.c | 18 ++++++-- src/side.c | 15 +++++++ src/util.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 265 insertions(+), 14 deletions(-) diff --git a/doc/diffutils.texi b/doc/diffutils.texi index 3e25807..dd5a1c6 100644 --- a/doc/diffutils.texi +++ b/doc/diffutils.texi @@ -3745,6 +3745,27 @@ Read and write data in binary mode. @xref{Binary}. Use the context output format, showing three lines of context. @xref{Context Format}. +@item --color [=@var{when}] +@cindex color, distinguishing different context +Specify whether to use color for distinguishing different contexts, +like header, added or removed lines. @var{when} may be omitted, or +one of: +@itemize @bullet +@item none +@vindex none @r{color option} +Do not use color at all. This is the default when no --color option +is present. +@item auto +@vindex auto @r{color option} +@cindex terminal, using color iff +Only use color if standard output is a terminal. +@item always +@vindex always @r{color option} +Always use color. +@end itemize +Specifying @option{--color} and no @var{when} is equivalent to +@option{--color=auto}. + @item -C @var{lines} @itemx --context@r{[}=@var{lines}@r{]} Use the context output format, showing @var{lines} (an integer) lines of diff --git a/src/context.c b/src/context.c index e0f21c4..9b23d36 100644 --- a/src/context.c +++ b/src/context.c @@ -80,6 +80,7 @@ print_context_label (char const *mark, void print_context_header (struct file_data inf[], char const *const *names, bool unidiff) { + set_header_color_context (); if (unidiff) { print_context_label ("---", &inf[0], names[0], file_label[0]); @@ -90,6 +91,7 @@ print_context_header (struct file_data inf[], char const *const *names, bool uni print_context_label ("***", &inf[0], names[0], file_label[0]); print_context_label ("---", &inf[1], names[1], file_label[1]); } + reset_color_context (false); } /* Print an edit script in context format. */ @@ -215,6 +217,7 @@ pr_context_hunk (struct change *hunk) for (i = first0; i <= last0; i++) { + bool reset_context = false; /* Skip past changes that apply (in file 0) only to lines before line I. */ @@ -225,12 +228,18 @@ pr_context_hunk (struct change *hunk) prefix = " "; if (next && next->line0 <= i) - /* The change NEXT covers this line. - If lines were inserted here in file 1, this is "changed". - Otherwise it is "deleted". */ - prefix = (next->inserted > 0 ? "!" : "-"); + { + reset_context = true; + set_delete_color_context (); + /* The change NEXT covers this line. + If lines were inserted here in file 1, this is "changed". + Otherwise it is "deleted". */ + prefix = (next->inserted > 0 ? "!" : "-"); + } print_1_line (prefix, &files[0].linbuf[i]); + if (reset_context) + reset_color_context (false); } } @@ -244,6 +253,7 @@ pr_context_hunk (struct change *hunk) for (i = first1; i <= last1; i++) { + bool reset_context = false; /* Skip past changes that apply (in file 1) only to lines before line I. */ @@ -254,12 +264,17 @@ pr_context_hunk (struct change *hunk) prefix = " "; if (next && next->line1 <= i) - /* The change NEXT covers this line. - If lines were deleted here in file 0, this is "changed". - Otherwise it is "inserted". */ - prefix = (next->deleted > 0 ? "!" : "+"); - + { + reset_context = true; + set_add_color_context (); + /* The change NEXT covers this line. + If lines were deleted here in file 0, this is "changed". + Otherwise it is "inserted". */ + prefix = (next->deleted > 0 ? "!" : "+"); + } print_1_line (prefix, &files[1].linbuf[i]); + if (reset_context) + reset_color_context (false); } } } @@ -330,11 +345,13 @@ pr_unidiff_hunk (struct change *hunk) begin_output (); out = outfile; + set_line_numbers_color_context (); fputs ("@@ -", out); print_unidiff_number_range (&files[0], first0, last0); fputs (" +", out); print_unidiff_number_range (&files[1], first1, last1); fputs (" @@", out); + reset_color_context (false); if (function) print_context_function (out, function); @@ -360,9 +377,17 @@ pr_unidiff_hunk (struct change *hunk) } else { + bool reset_context = false; + /* For each difference, first output the deleted part. */ k = next->deleted; + if (k) + { + reset_context = true; + set_delete_color_context (); + } + while (k--) { char const * const *line = &files[0].linbuf[i++]; @@ -375,9 +400,15 @@ pr_unidiff_hunk (struct change *hunk) /* Then output the inserted part. */ k = next->inserted; + if (k) + { + reset_context = true; + set_add_color_context (); + } while (k--) { char const * const *line = &files[1].linbuf[j++]; + set_add_color_context (); putc ('+', out); if (initial_tab && ! (suppress_blank_empty && **line == '\n')) putc ('\t', out); @@ -386,6 +417,8 @@ pr_unidiff_hunk (struct change *hunk) /* We're done with this hunk, so on to the next! */ + if (reset_context) + reset_color_context (false); next = next->link; } } diff --git a/src/diff.c b/src/diff.c index ff28377..be6e6e3 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); } @@ -940,6 +948,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 +1018,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..553bad1 100644 --- a/src/diff.h +++ b/src/diff.h @@ -38,6 +38,19 @@ enum changes /* Both deletes and inserts: a hunk containing both old and new lines. */ CHANGED }; + +/* 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 +96,9 @@ enum output_style XTERN enum output_style output_style; +/* Define the current color context used to print a line. */ +XTERN enum colors_style colors_style; + /* Nonzero if output cannot be generated for identical files. */ XTERN bool no_diff_means_no_output; @@ -390,3 +406,8 @@ 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_header_color_context (void); +extern void set_add_color_context (void); +extern void set_delete_color_context (void); +extern void reset_color_context (bool from_signal); +extern void set_line_numbers_color_context (void); diff --git a/src/normal.c b/src/normal.c index 721fd1a..0ce01ee 100644 --- a/src/normal.c +++ b/src/normal.c @@ -49,21 +49,31 @@ print_normal_hunk (struct change *hunk) begin_output (); /* Print out the line number header for this hunk */ + set_line_numbers_color_context (); print_number_range (',', &files[0], first0, last0); fputc (change_letter[changes], outfile); print_number_range (',', &files[1], first1, last1); fputc ('\n', outfile); + reset_color_context (false); /* Print the lines that the first file has. */ if (changes & OLD) - for (i = first0; i <= last0; i++) - print_1_line ("<", &files[0].linbuf[i]); + { + set_delete_color_context (); + for (i = first0; i <= last0; i++) + print_1_line ("<", &files[0].linbuf[i]); + reset_color_context (false); + } 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]); + { + set_add_color_context (); + for (i = first1; i <= last1; i++) + print_1_line (">", &files[1].linbuf[i]); + reset_color_context (false); + } } diff --git a/src/side.c b/src/side.c index 155512c..8327d11 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_delete_color_context (); + color_to_reset = true; + } + else if (sep == '>') + { + set_add_color_context (); + 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) + reset_color_context (false); } /* Print lines common to both files in side-by-side format. */ diff --git a/src/util.c b/src/util.c index 2d6d3fc..0f8f7a8 100644 --- a/src/util.c +++ b/src/util.c @@ -24,6 +24,7 @@ #include <system-quote.h> #include <xalloc.h> #include "xvasprintf.h" +#include <signal.h> char const pr_program[] = PR_PROGRAM; @@ -143,6 +144,59 @@ print_message_queue (void) } } +static void +signal_handler (int sig) +{ + reset_color_context (true); + + /* Restore the default handler, and report the signal again. */ + signal (sig, SIG_DFL); + raise (sig); +} + +static void +install_signal_handlers (void) +{ + int j; +#if HAVE_SIGACTION + struct sigaction act; +#endif + 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 + }; + + for (j = 0; j < sizeof (sig) / sizeof (*sig); j++) + { +#if HAVE_SIGACTION + sigaction (sig[j], NULL, &act); + if (act.sa_handler != SIG_IGN) + { + act.sa_handler = signal_handler; + sigaction (sig[j], &act, NULL); + } +#else + signal (sig[j], signal_handler); +#endif + } +} + /* Call before outputting the results of comparing files NAME0 and NAME1 to set up OUTFILE, the stdio stream for the output to go to. @@ -153,6 +207,24 @@ 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)); + + if (output_is_tty) + install_signal_handlers (); +} void setup_output (char const *name0, char const *name1, bool recursive) @@ -313,6 +385,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 +393,7 @@ begin_output (void) outfile = popen (command, "w"); if (!outfile) pfatal_with_name (command); + check_color_output (true); free (command); #endif } @@ -330,6 +404,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. */ @@ -717,6 +792,57 @@ output_1_line (char const *base, char const *limit, char const *flag_format, } } +void +set_header_color_context (void) +{ + if (colors_enabled) + fprintf (outfile, "\x1B[1;39m"); +} + +void +set_line_numbers_color_context (void) +{ + if (colors_enabled) + fprintf (outfile, "\x1B[36m"); +} + +void +set_add_color_context (void) +{ + if (colors_enabled) + fprintf (outfile, "\x1B[32m"); +} + +void +set_delete_color_context (void) +{ + if (colors_enabled) + fprintf (outfile, "\x1B[31m"); +} + +void +reset_color_context (bool from_signal) +{ + static char const reset_sequence[] = "\x1b[0m"; + if (! colors_enabled) + return; + + if (! from_signal) + fputs (reset_sequence, outfile); + else + { + size_t written = 0; + while (written < sizeof reset_sequence - 1) + { + int ret = write (STDOUT_FILENO, reset_sequence + written, + sizeof reset_sequence - 1 - written); + if (ret < 0) + return; + written += ret; + } + } +} + char const change_letter[] = { 0, 'd', 'a', 'c' }; /* Translate an internal line number (an index into diff's table of lines) -- 2.1.0