[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --co
From: |
Giuseppe Scrivano |
Subject: |
[bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color |
Date: |
Sat, 28 Nov 2015 21:36:44 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux) |
Jim Meyering <address@hidden> writes:
> diff --git a/bootstrap.conf b/bootstrap.conf
> index 9b2de22..3ab2c8b 100644
> --- a/bootstrap.conf
> +++ b/bootstrap.conf
> @@ -19,6 +19,7 @@
> # gnulib modules used by this package.
> gnulib_modules='
> announce-gen
> +argmatch
> binary-io
> c-stack
> config-h
> diff --git a/src/util.c b/src/util.c
> index 65b06e9..d27d202 100644
> --- a/src/util.c
> +++ b/src/util.c
> @@ -19,6 +19,7 @@
> along with this program. If not, see <http://www.gnu.org/licenses/>. */
>
> #include "diff.h"
> +#include "argmatch.h"
> #include <dirname.h>
> #include <error.h>
> #include <system-quote.h>
> @@ -561,6 +562,7 @@ static const char *const indicator_name[]=
> {
> "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", NULL
> };
> +ARGMATCH_VERIFY (indicator_name, color_indicator);
>
> static const char *color_palette;
thanks for the review and the patch. I had to add this chunk as
reported by "make syntax-check":
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 74fb756..af39427 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+lib/argmatch.c
lib/c-stack.c
lib/error.c
lib/file-type.c
As it is hopefully getting closer to be accepted, I am attaching the
full series with the amended changes. I've verified that each patch
passes "make check" and "make syntax-check".
Regards,
Giuseppe
>From 900ab32b3cd9d06a9c002c53eb5c9738047f203f Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Sun, 8 Mar 2015 22:45:11 +0100
Subject: [PATCH 1/5] 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_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): 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.
(output_1_line): Periodically call `process_signals'.
(caught_signals): New sigset_t.
(colors_enabled): New boolean variable.
(interrupt_signal): New sig_atomic_t.
(stop_signal_count): New sig_atomic_t.
(check_color_output): New function.
(install_signal_handlers): Likewise. Copied from coreutils ls.
(process_signals): Likewise. Copied from coreutils ls.
(set_color_context): New function.
(sighandler): Likewise. Copied from coreutils ls.
(stophandler): Likewise. Copied from coreutils ls.
---
doc/diffutils.texi | 21 ++++
src/context.c | 54 ++++++++--
src/diff.c | 27 ++++-
src/diff.h | 27 +++++
src/normal.c | 18 +++-
src/side.c | 15 +++
src/util.c | 308 +++++++++++++++++++++++++++++++++++++++++++++++------
7 files changed, 423 insertions(+), 47 deletions(-)
diff --git a/doc/diffutils.texi b/doc/diffutils.texi
index 091257f..b2c39da 100644
--- a/doc/diffutils.texi
+++ b/doc/diffutils.texi
@@ -3742,6 +3742,27 @@ 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 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:
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 specified.
address@hidden auto
address@hidden auto @r{color option}
address@hidden terminal, using color iff
+Use color only 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..104c90b 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_color_context (HEADER_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]);
}
+ set_color_context (RESET_CONTEXT);
}
/* Print an edit script in context format. */
@@ -205,9 +207,11 @@ pr_context_hunk (struct change *hunk)
if (function)
print_context_function (out, function);
+ set_color_context (LINE_NUMBER_CONTEXT);
fputs ("\n*** ", out);
print_context_number_range (&files[0], first0, last0);
fputs (" ****\n", out);
+ set_color_context (RESET_CONTEXT);
if (changes & OLD)
{
@@ -215,6 +219,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,18 +230,26 @@ 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_color_context (DELETE_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)
+ set_color_context (RESET_CONTEXT);
}
}
+ set_color_context (LINE_NUMBER_CONTEXT);
fputs ("--- ", out);
print_context_number_range (&files[1], first1, last1);
fputs (" ----\n", out);
+ set_color_context (RESET_CONTEXT);
if (changes & NEW)
{
@@ -244,6 +257,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 +268,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_color_context (ADD_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)
+ set_color_context (RESET_CONTEXT);
}
}
}
@@ -330,11 +349,13 @@ pr_unidiff_hunk (struct change *hunk)
begin_output ();
out = outfile;
+ set_color_context (LINE_NUMBER_CONTEXT);
fputs ("@@ -", out);
print_unidiff_number_range (&files[0], first0, last0);
fputs (" +", out);
print_unidiff_number_range (&files[1], first1, last1);
fputs (" @@", out);
+ set_color_context (RESET_CONTEXT);
if (function)
print_context_function (out, function);
@@ -360,9 +381,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_color_context (DELETE_CONTEXT);
+ }
+
while (k--)
{
char const * const *line = &files[0].linbuf[i++];
@@ -375,6 +404,11 @@ pr_unidiff_hunk (struct change *hunk)
/* Then output the inserted part. */
k = next->inserted;
+ if (k)
+ {
+ reset_context = true;
+ set_color_context (ADD_CONTEXT);
+ }
while (k--)
{
char const * const *line = &files[1].linbuf[j++];
@@ -386,6 +420,8 @@ pr_unidiff_hunk (struct change *hunk)
/* We're done with this hunk, so on to the next! */
+ if (reset_context)
+ set_color_context (RESET_CONTEXT);
next = next->link;
}
}
diff --git a/src/diff.c b/src/diff.c
index efd7e47..536f545 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..3c46042 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
};
+
+/* When colors should be used in the output. */
+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,14 @@ 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 *);
+
+enum color_context
+{
+ HEADER_CONTEXT,
+ ADD_CONTEXT,
+ DELETE_CONTEXT,
+ RESET_CONTEXT,
+ LINE_NUMBER_CONTEXT,
+};
+
+extern void set_color_context (enum color_context color_context);
diff --git a/src/normal.c b/src/normal.c
index 721fd1a..82aee77 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_color_context (LINE_NUMBER_CONTEXT);
print_number_range (',', &files[0], first0, last0);
fputc (change_letter[changes], outfile);
print_number_range (',', &files[1], first1, last1);
fputc ('\n', outfile);
+ set_color_context (RESET_CONTEXT);
/* 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_color_context (DELETE_CONTEXT);
+ for (i = first0; i <= last0; i++)
+ print_1_line ("<", &files[0].linbuf[i]);
+ set_color_context (RESET_CONTEXT);
+ }
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_color_context (ADD_CONTEXT);
+ for (i = first1; i <= last1; i++)
+ print_1_line (">", &files[1].linbuf[i]);
+ set_color_context (RESET_CONTEXT);
+ }
}
diff --git a/src/side.c b/src/side.c
index 155512c..a0213c4 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_CONTEXT);
+ color_to_reset = true;
+ }
+ else if (sep == '>')
+ {
+ set_color_context (ADD_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)
+ set_color_context (RESET_CONTEXT);
}
/* Print lines common to both files in side-by-side format. */
diff --git a/src/util.c b/src/util.c
index 2d6d3fc..694f4d9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -24,6 +24,22 @@
#include <system-quote.h>
#include <xalloc.h>
#include "xvasprintf.h"
+#include <signal.h>
+
+/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
+ present. */
+#ifndef SA_NOCLDSTOP
+# define SA_NOCLDSTOP 0
+# define sigprocmask(How, Set, Oset) /* empty */
+# define sigset_t int
+# if ! HAVE_SIGINTERRUPT
+# define siginterrupt(sig, flag) /* empty */
+# endif
+#endif
+
+#ifndef SA_RESTART
+# define SA_RESTART 0
+#endif
char const pr_program[] = PR_PROGRAM;
@@ -143,6 +159,174 @@ print_message_queue (void)
}
}
+/* The set of signals that are caught. */
+
+static sigset_t caught_signals;
+
+/* If nonzero, the value of the pending fatal signal. */
+
+static sig_atomic_t volatile interrupt_signal;
+
+/* A count of the number of pending stop signals that have been received. */
+
+static sig_atomic_t volatile stop_signal_count;
+
+/* An ordinary signal was received; arrange for the program to exit. */
+
+static void
+sighandler (int sig)
+{
+ if (! SA_NOCLDSTOP)
+ signal (sig, SIG_IGN);
+ if (! interrupt_signal)
+ interrupt_signal = sig;
+}
+
+/* A SIGTSTP was received; arrange for the program to suspend itself. */
+
+static void
+stophandler (int sig)
+{
+ if (! SA_NOCLDSTOP)
+ signal (sig, stophandler);
+ if (! interrupt_signal)
+ stop_signal_count++;
+}
+/* Process any pending signals. If signals are caught, this function
+ should be called periodically. Ideally there should never be an
+ unbounded amount of time when signals are not being processed.
+ Signal handling can restore the default colors, so callers must
+ immediately change colors after invoking this function. */
+
+static void
+process_signals (void)
+{
+ while (interrupt_signal || stop_signal_count)
+ {
+ int sig;
+ int stops;
+ sigset_t oldset;
+
+ set_color_context (RESET_CONTEXT);
+ fflush (stdout);
+
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+
+ /* Reload interrupt_signal and stop_signal_count, in case a new
+ signal was handled before sigprocmask took effect. */
+ sig = interrupt_signal;
+ stops = stop_signal_count;
+
+ /* SIGTSTP is special, since the application can receive that signal
+ more than once. In this case, don't set the signal handler to the
+ default. Instead, just raise the uncatchable SIGSTOP. */
+ if (stops)
+ {
+ stop_signal_count = stops - 1;
+ sig = SIGSTOP;
+ }
+ else
+ signal (sig, SIG_DFL);
+
+ /* Exit or suspend the program. */
+ raise (sig);
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ /* If execution reaches here, then the program has been
+ continued (after being suspended). */
+ }
+}
+
+static void
+install_signal_handlers (void)
+{
+ /* The signals that are trapped, and the number of such signals. */
+ static int const sig[] =
+ {
+ /* This one is handled specially. */
+ SIGTSTP,
+
+ /* The usual suspects. */
+ 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
+ };
+ enum { nsigs = sizeof (sig) / sizeof *(sig) };
+
+#if ! SA_NOCLDSTOP
+ bool caught_sig[nsigs];
+#endif
+ {
+ int j;
+#if SA_NOCLDSTOP
+ struct sigaction act;
+
+ sigemptyset (&caught_signals);
+ for (j = 0; j < nsigs; j++)
+ {
+ sigaction (sig[j], NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, sig[j]);
+ }
+
+ act.sa_mask = caught_signals;
+ act.sa_flags = SA_RESTART;
+
+ for (j = 0; j < nsigs; j++)
+ if (sigismember (&caught_signals, sig[j]))
+ {
+ act.sa_handler = sig[j] == SIGTSTP ? stophandler : sighandler;
+ sigaction (sig[j], &act, NULL);
+ }
+#else
+ for (j = 0; j < nsigs; j++)
+ {
+ caught_sig[j] = (signal (sig[j], SIG_IGN) != SIG_IGN);
+ if (caught_sig[j])
+ {
+ signal (sig[j], sig[j] == SIGTSTP ? stophandler : sighandler);
+ siginterrupt (sig[j], 0);
+ }
+ }
+#endif
+ }
+}
+
+static char const *current_name0;
+static char const *current_name1;
+static bool currently_recursive;
+static bool colors_enabled;
+
+static void
+check_color_output (bool is_pipe)
+{
+ bool output_is_tty;
+
+ if (! outfile || colors_style == NEVER)
+ return;
+
+ output_is_tty = !is_pipe && isatty (fileno (outfile));
+
+ colors_enabled = (colors_style == ALWAYS
+ || (colors_style == AUTO && output_is_tty));
+
+ if (output_is_tty)
+ install_signal_handlers ();
+}
+
/* Call before outputting the results of comparing files NAME0 and NAME1
to set up OUTFILE, the stdio stream for the output to go to.
@@ -150,10 +334,6 @@ print_message_queue (void)
we fork off a 'pr' and make OUTFILE a pipe to it.
'pr' then outputs to our stdout. */
-static char const *current_name0;
-static char const *current_name1;
-static bool currently_recursive;
-
void
setup_output (char const *name0, char const *name1, bool recursive)
{
@@ -313,6 +493,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 +501,7 @@ begin_output (void)
outfile = popen (command, "w");
if (!outfile)
pfatal_with_name (command);
+ check_color_output (true);
free (command);
#endif
}
@@ -330,6 +512,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 +855,21 @@ void
output_1_line (char const *base, char const *limit, char const *flag_format,
char const *line_flag)
{
+ const size_t MAX_CHUNK = 1024;
if (!expand_tabs)
- fwrite (base, sizeof (char), limit - base, outfile);
+ {
+ size_t left = limit - base;
+ while (left)
+ {
+ size_t to_write = MIN (left, MAX_CHUNK);
+ size_t written = fwrite (base, sizeof (char), to_write, outfile);
+ if (written < to_write)
+ return;
+ base += written;
+ left -= written;
+ process_signals ();
+ }
+ }
else
{
register FILE *out = outfile;
@@ -681,39 +877,85 @@ 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 counter_proc_signals = 0;
while (t < limit)
- switch ((c = *t++))
- {
- case '\t':
- {
- size_t spaces = tab_size - column % tab_size;
- column += spaces;
- do
- putc (' ', out);
- while (--spaces);
- }
- break;
+ {
+ counter_proc_signals++;
+ if (counter_proc_signals == MAX_CHUNK)
+ {
+ process_signals ();
+ counter_proc_signals = 0;
+ }
+
+ switch ((c = *t++))
+ {
+ case '\t':
+ {
+ size_t spaces = tab_size - column % tab_size;
+ column += spaces;
+ do
+ putc (' ', out);
+ while (--spaces);
+ }
+ break;
+
+ case '\r':
+ putc (c, out);
+ if (flag_format && t < limit && *t != '\n')
+ fprintf (out, flag_format, line_flag);
+ column = 0;
+ break;
+
+ case '\b':
+ if (column == 0)
+ continue;
+ column--;
+ putc (c, out);
+ break;
+
+ default:
+ column += isprint (c) != 0;
+ putc (c, out);
+ break;
+ }
+ }
+ }
+}
- case '\r':
- putc (c, out);
- if (flag_format && t < limit && *t != '\n')
- fprintf (out, flag_format, line_flag);
- column = 0;
- break;
- case '\b':
- if (column == 0)
- continue;
- column--;
- putc (c, out);
- break;
+void
+set_color_context (enum color_context color_context)
+{
+ process_signals ();
+ if (colors_enabled)
+ {
+ switch (color_context)
+ {
+ case HEADER_CONTEXT:
+ fputs ("\x1B[1m", outfile);
+ break;
- default:
- column += isprint (c) != 0;
- putc (c, out);
- break;
- }
+ case LINE_NUMBER_CONTEXT:
+ fputs ("\x1B[36m", outfile);
+
+ break;
+
+ case ADD_CONTEXT:
+ fputs ("\x1B[32m", outfile);
+ break;
+
+ case DELETE_CONTEXT:
+ fputs ("\x1B[31m", outfile);
+ break;
+
+ case RESET_CONTEXT:
+ fputs ("\x1b[0m", outfile);
+ break;
+
+ default:
+ abort ();
+ }
}
}
--
2.5.0
>From f860a90da16f6d21939611718696347c3506b075 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Mon, 19 Oct 2015 10:29:41 +0200
Subject: [PATCH 2/5] diff: add --palette
* bootstrap (gnulib_modules): Add 'argmatch'.
* doc/diffutils.texi: Add documentation for --palette
* src/diff.h (set_color_palette): New prototype.
* src/diff.c (set_color_palette): New function.
(color_palette): New variable.
* src/utils.c: Include "argmatch.h".
(struct bin_str): New struct.
(struct color_ext_type): New struct.
(color_indicator): New array.
(indicator_name): New array.
(indicator_no): New enum.
(parse_state): New enum.
(put_indicator): New function.
(get_funky_string): New function. Copied from coreutils ls.
(parse_diff_color): New function. Copied from coreutils ls
"parse_ls_color" function.
(set_color_context): Use put_indicator instead of directly
outputting the sequence.
* po/POTFILES.in: Add 'lib/argmatch.c'
---
bootstrap.conf | 1 +
doc/diffutils.texi | 34 +++++
po/POTFILES.in | 1 +
src/diff.c | 8 +
src/diff.h | 1 +
src/util.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
6 files changed, 462 insertions(+), 7 deletions(-)
diff --git a/bootstrap.conf b/bootstrap.conf
index 9b2de22..3ab2c8b 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -19,6 +19,7 @@
# gnulib modules used by this package.
gnulib_modules='
announce-gen
+argmatch
binary-io
c-stack
config-h
diff --git a/doc/diffutils.texi b/doc/diffutils.texi
index b2c39da..4ec9a0b 100644
--- a/doc/diffutils.texi
+++ b/doc/diffutils.texi
@@ -3890,6 +3890,40 @@ if-then-else format. @xref{Line Formats}.
@itemx --show-c-function
Show which C function each change is in. @xref{C Function Headings}.
address@hidden address@hidden
+Specify what color palette to use when colored output is enabled. It
+defaults to @samp{rs=0:hd=1:ad=32:de=31:ln=36} for red deleted lines,
+green added lines, cyan line numbers, bold header.
+
+Supported capabilities are as follows.
+
address@hidden @code
address@hidden ad=32
address@hidden ad @r{capability}
+
+SGR substring for added lines.
+The default is green foreground.
+
address@hidden de=31
address@hidden de @r{capability}
+
+SGR substring for deleted lines.
+The default is red foreground.
+
address@hidden hd=1
address@hidden hd @r{capability}
+
+SGR substring for chunk header.
+The default is bold foreground.
+
address@hidden ln=36
address@hidden ln @r{capability}
+
+SGR substring for line numbers.
+The default is cyan foreground.
address@hidden table
+
+
@item -q
@itemx --brief
Report only whether the files differ, not the details of the
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 74fb756..af39427 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+lib/argmatch.c
lib/c-stack.c
lib/error.c
lib/file-type.c
diff --git a/src/diff.c b/src/diff.c
index 536f545..46ac99d 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -140,6 +140,7 @@ enum
CHANGED_GROUP_FORMAT_OPTION,
COLOR_OPTION,
+ COLOR_PALETTE_OPTION,
};
static char const group_format_option[][sizeof "--unchanged-group-format"] =
@@ -196,6 +197,7 @@ static struct option const longopts[] =
{"old-group-format", 1, 0, OLD_GROUP_FORMAT_OPTION},
{"old-line-format", 1, 0, OLD_LINE_FORMAT_OPTION},
{"paginate", 0, 0, 'l'},
+ {"palette", 1, 0, COLOR_PALETTE_OPTION},
{"rcs", 0, 0, 'n'},
{"recursive", 0, 0, 'r'},
{"report-identical-files", 0, 0, 's'},
@@ -635,6 +637,10 @@ main (int argc, char **argv)
specify_colors_style (optarg);
break;
+ case COLOR_PALETTE_OPTION:
+ set_color_palette (optarg);
+ break;
+
default:
try_help (NULL, NULL);
}
@@ -950,6 +956,8 @@ static char const * const option_help_msgid[] = {
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_(" --palette=PALETTE specify the colors to use when --color is
active"),
+ N_(" PALETTE is a colon-separated list terminfo
capabilities"),
"",
N_(" --help display this help and exit"),
N_("-v, --version output version information and exit"),
diff --git a/src/diff.h b/src/diff.h
index 3c46042..c5fe4f9 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -417,3 +417,4 @@ enum color_context
};
extern void set_color_context (enum color_context color_context);
+extern void set_color_palette (char const *palette);
diff --git a/src/util.c b/src/util.c
index 694f4d9..3bd828f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -19,6 +19,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "diff.h"
+#include "argmatch.h"
#include <dirname.h>
#include <error.h>
#include <system-quote.h>
@@ -310,6 +311,397 @@ static char const *current_name1;
static bool currently_recursive;
static bool colors_enabled;
+static struct color_ext_type *color_ext_list = NULL;
+
+struct bin_str
+ {
+ size_t len; /* Number of bytes */
+ const char *string; /* Pointer to the same */
+ };
+
+struct color_ext_type
+ {
+ struct bin_str ext; /* The extension we're looking for */
+ struct bin_str seq; /* The sequence to output when we do */
+ struct color_ext_type *next; /* Next in list */
+ };
+
+/* Parse a string as part of the --palette argument; this may involve
+ decoding all kinds of escape characters. If equals_end is set an
+ unescaped equal sign ends the string, otherwise only a : or \0
+ does. Set *OUTPUT_COUNT to the number of bytes output. Return
+ true if successful.
+
+ The resulting string is *not* null-terminated, but may contain
+ embedded nulls.
+
+ Note that both dest and src are char **; on return they point to
+ the first free byte after the array and the character that ended
+ the input string, respectively. */
+
+static bool
+get_funky_string (char **dest, const char **src, bool equals_end,
+ size_t *output_count)
+{
+ char num; /* For numerical codes */
+ size_t count; /* Something to count with */
+ enum {
+ ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
+ } state;
+ const char *p;
+ char *q;
+
+ p = *src; /* We don't want to double-indirect */
+ q = *dest; /* the whole darn time. */
+
+ count = 0; /* No characters counted in yet. */
+ num = 0;
+
+ state = ST_GND; /* Start in ground state. */
+ while (state < ST_END)
+ {
+ switch (state)
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash scape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = '\a';
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
+ num = 127;
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ 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:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if (*p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ *dest = q;
+ *src = p;
+ *output_count = count;
+
+ return state != ST_ERROR;
+}
+
+enum parse_state
+ {
+ PS_START = 1,
+ PS_2,
+ PS_3,
+ PS_4,
+ PS_DONE,
+ PS_FAIL
+ };
+
+#define LEN_STR_PAIR(s) sizeof (s) - 1, s
+
+static struct bin_str color_indicator[] =
+ {
+ { LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
+ { LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
+ { 0, NULL }, /* ec: End color (replaces lc+rs+rc) */
+ { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
+ { LEN_STR_PAIR ("1") }, /* hd: Header */
+ { LEN_STR_PAIR ("32") }, /* ad: Add line */
+ { LEN_STR_PAIR ("31") }, /* de: Delete line */
+ { LEN_STR_PAIR ("36") }, /* ln: Line number */
+ };
+
+static const char *const indicator_name[] =
+ {
+ "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", NULL
+ };
+ARGMATCH_VERIFY (indicator_name, color_indicator);
+
+static char const *color_palette;
+
+void
+set_color_palette (char const *palette)
+{
+ color_palette = palette;
+}
+
+static void
+parse_diff_color (void)
+{
+ char *color_buf;
+ const char *p; /* Pointer to character being parsed */
+ char *buf; /* color_buf buffer pointer */
+ int ind_no; /* Indicator number */
+ char label[3]; /* Indicator label */
+ struct color_ext_type *ext; /* Extension we are working on */
+
+ if ((p = color_palette) == NULL || *p == '\0')
+ return;
+
+ ext = NULL;
+ strcpy (label, "??");
+
+ /* This is an overly conservative estimate, but any possible
+ --palette string will *not* generate a color_buf longer than
+ itself, so it is a safe way of allocating a buffer in
+ advance. */
+ buf = color_buf = xstrdup (p);
+
+ enum parse_state state = PS_START;
+ while (true)
+ {
+ switch (state)
+ {
+ case PS_START: /* First label character */
+ switch (*p)
+ {
+ case ':':
+ ++p;
+ break;
+
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
+
+ ext = xmalloc (sizeof *ext);
+ ext->next = color_ext_list;
+ color_ext_list = ext;
+
+ ++p;
+ ext->ext.string = buf;
+
+ state = (get_funky_string (&buf, &p, true, &ext->ext.len)
+ ? PS_4 : PS_FAIL);
+ break;
+
+ case '\0':
+ state = PS_DONE; /* Done! */
+ goto done;
+
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = PS_2;
+ break;
+ }
+ break;
+
+ case PS_2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = PS_3;
+ }
+ else
+ state = PS_FAIL; /* Error */
+ break;
+
+ case PS_3: /* Equal sign after indicator label */
+ state = PS_FAIL; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
+ {
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+ state = (get_funky_string (&buf, &p, false,
+ &color_indicator[ind_no].len)
+ ? PS_START : PS_FAIL);
+ break;
+ }
+ }
+ if (state == PS_FAIL)
+ error (0, 0, _("unrecognized prefix: %s"), label);
+ }
+ break;
+
+ case PS_4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (get_funky_string (&buf, &p, false, &ext->seq.len)
+ ? PS_START : PS_FAIL);
+ }
+ else
+ state = PS_FAIL;
+ break;
+
+ case PS_FAIL:
+ goto done;
+
+ default:
+ abort ();
+ }
+ }
+ done:
+
+ if (state == PS_FAIL)
+ {
+ struct color_ext_type *e;
+ struct color_ext_type *e2;
+
+ error (0, 0,
+ _("unparsable value for --palette"));
+ free (color_buf);
+ for (e = color_ext_list; e != NULL; /* empty */)
+ {
+ e2 = e;
+ e = e->next;
+ free (e2);
+ }
+ colors_enabled = false;
+ }
+}
+
static void
check_color_output (bool is_pipe)
{
@@ -323,6 +715,9 @@ check_color_output (bool is_pipe)
colors_enabled = (colors_style == ALWAYS
|| (colors_style == AUTO && output_is_tty));
+ if (colors_enabled)
+ parse_diff_color ();
+
if (output_is_tty)
install_signal_handlers ();
}
@@ -923,42 +1318,57 @@ output_1_line (char const *base, char const *limit, char
const *flag_format,
}
}
+enum indicator_no
+ {
+ C_LEFT, C_RIGHT, C_END, C_RESET, C_HEADER, C_ADD, C_DELETE, C_LINE
+ };
+
+static void
+put_indicator (const struct bin_str *ind)
+{
+ fwrite (ind->string, ind->len, 1, outfile);
+}
+
+static enum color_context last_context = RESET_CONTEXT;
void
set_color_context (enum color_context color_context)
{
process_signals ();
- if (colors_enabled)
+ if (colors_enabled && last_context != color_context)
{
+ put_indicator (&color_indicator[C_LEFT]);
switch (color_context)
{
case HEADER_CONTEXT:
- fputs ("\x1B[1m", outfile);
+ put_indicator (&color_indicator[C_HEADER]);
break;
case LINE_NUMBER_CONTEXT:
- fputs ("\x1B[36m", outfile);
-
+ put_indicator (&color_indicator[C_LINE]);
break;
case ADD_CONTEXT:
- fputs ("\x1B[32m", outfile);
+ put_indicator (&color_indicator[C_ADD]);
break;
case DELETE_CONTEXT:
- fputs ("\x1B[31m", outfile);
+ put_indicator (&color_indicator[C_DELETE]);
break;
case RESET_CONTEXT:
- fputs ("\x1b[0m", outfile);
+ put_indicator (&color_indicator[C_RESET]);
break;
default:
abort ();
}
+ put_indicator (&color_indicator[C_RIGHT]);
+ last_context = color_context;
}
}
+
char const change_letter[] = { 0, 'd', 'a', 'c' };
/* Translate an internal line number (an index into diff's table of lines)
--
2.5.0
>From bebef1281fdb6d53f725b0eb1d18703e8845d768 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Mon, 2 Nov 2015 19:03:32 +0000
Subject: [PATCH 3/5] doc: mention --color and --palette in NEWS
---
NEWS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/NEWS b/NEWS
index 7cdfedd..088f13b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@ GNU diffutils NEWS -*-
outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** New features
+
+ diff accepts two new options --color and --palette to generate
+ and configure colored output. --color takes an optional argument
+ specifying when to colorize a line: --color=always, --color=auto,
+ --color=never. --palette is used to configure which colors are used.
+
** Bug fixes
When binary files differ, diff now exits with status 1 as POSIX requires.
--
2.5.0
>From e58c36a9062168bf56a8447d1c4287879ef54e9f Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Mon, 2 Nov 2015 19:05:10 +0000
Subject: [PATCH 4/5] tests: Add tests for --color and --palette
* tests/colors: New file.
* tests/Makefile.am (TESTS): Add colors.
---
tests/Makefile.am | 3 +-
tests/colors | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+), 1 deletion(-)
create mode 100755 tests/colors
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 438fbdf..805ccc2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -15,7 +15,8 @@ TESTS = \
no-newline-at-eof \
stdin \
strcoll-0-names \
- filename-quoting
+ filename-quoting \
+ colors
EXTRA_DIST = \
$(TESTS) init.sh t-local.sh
diff --git a/tests/colors b/tests/colors
new file mode 100755
index 0000000..92c0452
--- /dev/null
+++ b/tests/colors
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+
+TZ=UTC0
+export TZ
+
+fail=0
+
+echo a > a
+echo b > b
+
+epoch='1970-01-01 00:00:00'
+touch --date="$epoch" a b
+
+gen_exp_u()
+{
+ local tab=$(printf '\t')
+ local epoch_plus="$epoch.000000000 +0000"
+ local rs=$(printf "\e[${rs}m")
+ local hd=$(printf "\e[${hd}m")
+ local ad=$(printf "\e[${ad}m")
+ local de=$(printf "\e[${de}m")
+ local ln=$(printf "\e[${ln}m")
+ printf '%s' \
+"$hd--- a$tab$epoch_plus
++++ b$tab$epoch_plus
+$rs${ln}@@ -1 +1 @@$rs
+$de-a
+$ad+b
+$rs"
+}
+
+gen_exp_c()
+{
+ local tab=$(printf '\t')
+ local epoch_posix_1003_1_2001="Thu Jan 1 00:00:00 1970"
+ local rs=$(printf "\e[${rs}m")
+ local hd=$(printf "\e[${hd}m")
+ local ad=$(printf "\e[${ad}m")
+ local de=$(printf "\e[${de}m")
+ local ln=$(printf "\e[${ln}m")
+ printf '%s' \
+"$hd*** a$tab$epoch_posix_1003_1_2001
+--- b$tab$epoch_posix_1003_1_2001
+$rs***************$ln
+*** 1 ****
+$rs$de! a
+$rs$ln--- 1 ----
+$rs$ad! b
+$rs"
+}
+
+gen_exp_default()
+{
+ printf '%s' \
+"1c1
+< a
+---
+> b
+"
+}
+
+gen_exp_default_colors()
+{
+ local rs=$(printf "\e[${rs}m")
+ local hd=$(printf "\e[${hd}m")
+ local ad=$(printf "\e[${ad}m")
+ local de=$(printf "\e[${de}m")
+ local ln=$(printf "\e[${ln}m")
+ printf '%s' \
+"${ln}1c1
+$rs$de< a
+$rs---
+$ad> b
+$rs"
+}
+
+# Compare with some known outputs
+
+rs=0 hd=1 ad=32 de=31 ln=36
+
+diff --color=auto a b > out
+test $? = 1 || fail=1
+gen_exp_default > exp || framework_failure_
+compare exp out || fail=1
+
+diff --color=never a b > out
+test $? = 1 || fail=1
+gen_exp_default > exp || framework_failure_
+compare exp out || fail=1
+
+diff a b > out
+test $? = 1 || fail=1
+gen_exp_default > exp || framework_failure_
+compare exp out || fail=1
+
+diff --color=always a b > out
+test $? = 1 || fail=1
+gen_exp_default_colors > exp || framework_failure_
+compare exp out || fail=1
+
+diff -u --color=always a b > out
+test $? = 1 || fail=1
+gen_exp_u > exp || framework_failure_
+compare exp out || fail=1
+
+diff -c --color=always a b > out
+test $? = 1 || fail=1
+gen_exp_c > exp || framework_failure_
+compare exp out || fail=1
+
+rs=0 hd=33 ad=34 de=35 ln=36
+diff -u --color=always --palette="rs=0:hd=33:ad=34:de=35:ln=36" a b > out
+test $? = 1 || fail=1
+gen_exp_u > exp || framework_failure_
+compare exp out || fail=1
+
+Exit $fail
--
2.5.0
>From a61b7dc46b43459656cde44824d23948cc28d5f9 Mon Sep 17 00:00:00 2001
From: Giuseppe Scrivano <address@hidden>
Date: Fri, 27 Nov 2015 13:56:05 +0100
Subject: [PATCH 5/5] Generate terminal sequences on the line they belong
* src/diff.h: New function 'print_1_line_nl'.
* src/context.c (pr_context_hunk): Use 'print_1_line_nl'.
Generate terminal sequences on the line they belong.
(pr_unidiff_hunk): Likewise.
* src/normal.c (print_normal_hunk): Likewise.
* src/util.c (print_1_line_nl): New function.
(print_1_line): Become a wrapper of 'print_1_line_nl'.
* tests/colors: Adjust tests.
---
src/context.c | 67 +++++++++++++++++++++++++++++++++--------------------------
src/diff.h | 1 +
src/normal.c | 26 ++++++++++++++++-------
src/util.c | 19 +++++++++++++++--
tests/colors | 28 ++++++++++++-------------
5 files changed, 89 insertions(+), 52 deletions(-)
diff --git a/src/context.c b/src/context.c
index 104c90b..46b5b1f 100644
--- a/src/context.c
+++ b/src/context.c
@@ -207,19 +207,23 @@ pr_context_hunk (struct change *hunk)
if (function)
print_context_function (out, function);
+ putc ('\n', out);
set_color_context (LINE_NUMBER_CONTEXT);
- fputs ("\n*** ", out);
+ fputs ("*** ", out);
print_context_number_range (&files[0], first0, last0);
- fputs (" ****\n", out);
+ fputs (" ****", out);
set_color_context (RESET_CONTEXT);
+ putc ('\n', out);
if (changes & OLD)
{
struct change *next = hunk;
+ if (first0 <= last0)
+ set_color_context (DELETE_CONTEXT);
+
for (i = first0; i <= last0; i++)
{
- bool reset_context = false;
/* Skip past changes that apply (in file 0)
only to lines before line I. */
@@ -231,33 +235,35 @@ pr_context_hunk (struct change *hunk)
prefix = " ";
if (next && next->line0 <= i)
{
- reset_context = true;
- set_color_context (DELETE_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)
+ print_1_line_nl (prefix, &files[0].linbuf[i], true);
+ if (i == last0)
set_color_context (RESET_CONTEXT);
+ if (files[0].linbuf[i + 1][-1] == '\n')
+ putc ('\n', out);
}
}
set_color_context (LINE_NUMBER_CONTEXT);
fputs ("--- ", out);
print_context_number_range (&files[1], first1, last1);
- fputs (" ----\n", out);
+ fputs (" ----", out);
set_color_context (RESET_CONTEXT);
+ putc ('\n', out);
if (changes & NEW)
{
struct change *next = hunk;
+ if (first1 <= last1)
+ set_color_context (ADD_CONTEXT);
+
for (i = first1; i <= last1; i++)
{
- bool reset_context = false;
/* Skip past changes that apply (in file 1)
only to lines before line I. */
@@ -269,16 +275,16 @@ pr_context_hunk (struct change *hunk)
prefix = " ";
if (next && next->line1 <= i)
{
- reset_context = true;
- set_color_context (ADD_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)
+ print_1_line_nl (prefix, &files[1].linbuf[i], true);
+ if (i == last1)
set_color_context (RESET_CONTEXT);
+ if (files[1].linbuf[i + 1][-1] == '\n')
+ putc ('\n', out);
}
}
}
@@ -381,16 +387,11 @@ 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_color_context (DELETE_CONTEXT);
- }
+ set_color_context (DELETE_CONTEXT);
while (k--)
{
@@ -398,30 +399,38 @@ pr_unidiff_hunk (struct change *hunk)
putc ('-', out);
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
putc ('\t', out);
- print_1_line (NULL, line);
+ print_1_line_nl (NULL, line, true);
+
+ if (!k)
+ set_color_context (RESET_CONTEXT);
+
+ if (line[1][-1] == '\n')
+ putc ('\n', out);
}
/* Then output the inserted part. */
k = next->inserted;
if (k)
- {
- reset_context = true;
- set_color_context (ADD_CONTEXT);
- }
- while (k--)
+ set_color_context (ADD_CONTEXT);
+
+ while (k--)
{
char const * const *line = &files[1].linbuf[j++];
putc ('+', out);
if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
putc ('\t', out);
- print_1_line (NULL, line);
+ print_1_line_nl (NULL, line, true);
+
+ if (!k)
+ set_color_context (RESET_CONTEXT);
+
+ if (line[1][-1] == '\n')
+ putc ('\n', out);
}
/* We're done with this hunk, so on to the next! */
- if (reset_context)
- set_color_context (RESET_CONTEXT);
next = next->link;
}
}
diff --git a/src/diff.h b/src/diff.h
index c5fe4f9..6f1bb34 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -399,6 +399,7 @@ extern void output_1_line (char const *, char const *, char
const *,
extern void perror_with_name (char const *);
extern void pfatal_with_name (char const *) __attribute__((noreturn));
extern void print_1_line (char const *, char const * const *);
+extern void print_1_line_nl (char const *, char const * const *, bool);
extern void print_message_queue (void);
extern void print_number_range (char, struct file_data *, lin, lin);
extern void print_script (struct change *, struct change * (*) (struct change
*),
diff --git a/src/normal.c b/src/normal.c
index 82aee77..e78e8ba 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -53,16 +53,22 @@ print_normal_hunk (struct change *hunk)
print_number_range (',', &files[0], first0, last0);
fputc (change_letter[changes], outfile);
print_number_range (',', &files[1], first1, last1);
- fputc ('\n', outfile);
set_color_context (RESET_CONTEXT);
+ fputc ('\n', outfile);
/* Print the lines that the first file has. */
if (changes & OLD)
{
- set_color_context (DELETE_CONTEXT);
+ if (first0 <= last0)
+ set_color_context (DELETE_CONTEXT);
for (i = first0; i <= last0; i++)
- print_1_line ("<", &files[0].linbuf[i]);
- set_color_context (RESET_CONTEXT);
+ {
+ print_1_line_nl ("<", &files[0].linbuf[i], true);
+ if (i == last0)
+ set_color_context (RESET_CONTEXT);
+ if (files[0].linbuf[i + 1][-1] == '\n')
+ putc ('\n', outfile);
+ }
}
if (changes == CHANGED)
@@ -71,9 +77,15 @@ print_normal_hunk (struct change *hunk)
/* Print the lines that the second file has. */
if (changes & NEW)
{
- set_color_context (ADD_CONTEXT);
+ if (first1 <= last1)
+ set_color_context (ADD_CONTEXT);
for (i = first1; i <= last1; i++)
- print_1_line (">", &files[1].linbuf[i]);
- set_color_context (RESET_CONTEXT);
+ {
+ print_1_line_nl (">", &files[1].linbuf[i], true);
+ if (i == last1)
+ set_color_context (RESET_CONTEXT);
+ if (files[1].linbuf[i + 1][-1] == '\n')
+ putc ('\n', outfile);
+ }
}
}
diff --git a/src/util.c b/src/util.c
index 3bd828f..1fa61fa 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1208,6 +1208,18 @@ print_script (struct change *script,
void
print_1_line (char const *line_flag, char const *const *line)
{
+ print_1_line_nl (line_flag, line, false);
+}
+
+/* Print the text of a single line LINE,
+ flagging it with the characters in LINE_FLAG (which say whether
+ the line is inserted, deleted, changed, etc.). LINE_FLAG must not
+ end in a blank, unless it is a single blank. If SKIP_NL is set, then
+ the final '\n' is not printed. */
+
+void
+print_1_line_nl (char const *line_flag, char const *const *line, bool skip_nl)
+{
char const *base = line[0], *limit = line[1]; /* Help the compiler. */
FILE *out = outfile; /* Help the compiler some more. */
char const *flag_format = 0;
@@ -1235,10 +1247,13 @@ print_1_line (char const *line_flag, char const *const
*line)
fprintf (out, flag_format_1, line_flag_1);
}
- output_1_line (base, limit, flag_format, line_flag);
+ output_1_line (base, limit - (skip_nl && limit[-1] == '\n'), flag_format,
line_flag);
if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
- fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
+ {
+ set_color_context (RESET_CONTEXT);
+ fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
+ }
}
/* Output a line from BASE up to LIMIT.
diff --git a/tests/colors b/tests/colors
index 92c0452..facfd8d 100755
--- a/tests/colors
+++ b/tests/colors
@@ -26,9 +26,9 @@ gen_exp_u()
"$hd--- a$tab$epoch_plus
+++ b$tab$epoch_plus
$rs${ln}@@ -1 +1 @@$rs
-$de-a
-$ad+b
-$rs"
+$de-a$rs
+$ad+b$rs
+"
}
gen_exp_c()
@@ -43,12 +43,12 @@ gen_exp_c()
printf '%s' \
"$hd*** a$tab$epoch_posix_1003_1_2001
--- b$tab$epoch_posix_1003_1_2001
-$rs***************$ln
-*** 1 ****
-$rs$de! a
-$rs$ln--- 1 ----
-$rs$ad! b
-$rs"
+$rs***************
+$ln*** 1 ****$rs
+$de! a$rs
+$ln--- 1 ----$rs
+$ad! b$rs
+"
}
gen_exp_default()
@@ -69,11 +69,11 @@ gen_exp_default_colors()
local de=$(printf "\e[${de}m")
local ln=$(printf "\e[${ln}m")
printf '%s' \
-"${ln}1c1
-$rs$de< a
-$rs---
-$ad> b
-$rs"
+"${ln}1c1$rs
+$de< a$rs
+---
+$ad> b$rs
+"
}
# Compare with some known outputs
--
2.5.0
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, (continued)
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Giuseppe Scrivano, 2015/11/03
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Eric Blake, 2015/11/03
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Jim Meyering, 2015/11/03
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Giuseppe Scrivano, 2015/11/16
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Jim Meyering, 2015/11/26
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Giuseppe Scrivano, 2015/11/27
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Jim Meyering, 2015/11/27
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color,
Giuseppe Scrivano <=
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Jim Meyering, 2015/11/28
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Giuseppe Scrivano, 2015/11/29
- [bug-diffutils] bug#20062: bug#20062: [PATCH] diff: add support for --color, Jim Meyering, 2015/11/29
- [bug-diffutils] bug#20062: bug#20062: bug#20062: [PATCH] diff: add support for --color, Eric Blake, 2015/11/03