mirror of
https://https.git.savannah.gnu.org/git/diffutils.git
synced 2026-01-27 09:54:25 +00:00
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 (print_1_line_nl): New function. (print_1_line): Make it a wrapper of 'print_1_line_nl'. (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.
This commit is contained in:
parent
b4efca9de4
commit
c0fa19fe92
@ -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}.
|
||||
|
||||
@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 specified.
|
||||
@item auto
|
||||
@vindex auto @r{color option}
|
||||
@cindex terminal, using color iff
|
||||
Use color only 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
|
||||
|
||||
@ -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,14 +207,21 @@ pr_context_hunk (struct change *hunk)
|
||||
if (function)
|
||||
print_context_function (out, function);
|
||||
|
||||
fputs ("\n*** ", out);
|
||||
putc ('\n', out);
|
||||
set_color_context (LINE_NUMBER_CONTEXT);
|
||||
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++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 0)
|
||||
@ -225,23 +234,34 @@ 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 ? "!" : "-");
|
||||
|
||||
print_1_line (prefix, &files[0].linbuf[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 ? "!" : "-");
|
||||
}
|
||||
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++)
|
||||
{
|
||||
/* Skip past changes that apply (in file 1)
|
||||
@ -254,12 +274,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 ? "!" : "+");
|
||||
|
||||
print_1_line (prefix, &files[1].linbuf[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 ? "!" : "+");
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -330,11 +355,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);
|
||||
@ -363,25 +390,43 @@ pr_unidiff_hunk (struct change *hunk)
|
||||
/* For each difference, first output the deleted part. */
|
||||
|
||||
k = next->deleted;
|
||||
if (k)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
|
||||
while (k--)
|
||||
{
|
||||
char const * const *line = &files[0].linbuf[i++];
|
||||
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;
|
||||
while (k--)
|
||||
if (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! */
|
||||
|
||||
27
src/diff.c
27
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. */
|
||||
|
||||
|
||||
28
src/diff.h
28
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;
|
||||
|
||||
@ -383,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 *),
|
||||
@ -390,3 +407,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);
|
||||
|
||||
30
src/normal.c
30
src/normal.c
@ -49,21 +49,43 @@ 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);
|
||||
set_color_context (RESET_CONTEXT);
|
||||
fputc ('\n', outfile);
|
||||
|
||||
/* Print the lines that the first file has. */
|
||||
if (changes & OLD)
|
||||
for (i = first0; i <= last0; i++)
|
||||
print_1_line ("<", &files[0].linbuf[i]);
|
||||
{
|
||||
if (first0 <= last0)
|
||||
set_color_context (DELETE_CONTEXT);
|
||||
for (i = first0; i <= last0; i++)
|
||||
{
|
||||
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)
|
||||
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]);
|
||||
{
|
||||
if (first1 <= last1)
|
||||
set_color_context (ADD_CONTEXT);
|
||||
for (i = first1; i <= last1; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
src/side.c
15
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. */
|
||||
|
||||
327
src/util.c
327
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. */
|
||||
@ -629,6 +812,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. */
|
||||
@ -657,10 +852,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.
|
||||
@ -672,8 +870,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 +892,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;
|
||||
}
|
||||
|
||||
case '\r':
|
||||
putc (c, out);
|
||||
if (flag_format && t < limit && *t != '\n')
|
||||
fprintf (out, flag_format, line_flag);
|
||||
column = 0;
|
||||
break;
|
||||
switch ((c = *t++))
|
||||
{
|
||||
case '\t':
|
||||
{
|
||||
size_t spaces = tab_size - column % tab_size;
|
||||
column += spaces;
|
||||
do
|
||||
putc (' ', out);
|
||||
while (--spaces);
|
||||
}
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (column == 0)
|
||||
continue;
|
||||
column--;
|
||||
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;
|
||||
|
||||
default:
|
||||
column += isprint (c) != 0;
|
||||
putc (c, out);
|
||||
break;
|
||||
}
|
||||
case '\b':
|
||||
if (column == 0)
|
||||
continue;
|
||||
column--;
|
||||
putc (c, out);
|
||||
break;
|
||||
|
||||
default:
|
||||
column += isprint (c) != 0;
|
||||
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;
|
||||
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user