diff --git a/doc/patchutils.xml b/doc/patchutils.xml
index 67282967..42b1ff70 100644
--- a/doc/patchutils.xml
+++ b/doc/patchutils.xml
@@ -82,6 +82,7 @@
-w
--ignore-all-space
+ --color=WHEN
--interpolate
--combine
@@ -238,6 +239,14 @@
+
+
+
+ colorize the output; WHEN
+ can be 'never', 'always', or 'auto' (default: auto, use 'never' to disable)
+
+
+
,
@@ -391,6 +400,7 @@
-w
--ignore-all-space
+ --color=WHEN
--interpolate
--combine
@@ -530,6 +540,14 @@
+
+
+
+ colorize the output; WHEN
+ can be 'never', 'always', or 'auto' (default: auto, use 'never' to disable)
+
+
+
,
@@ -2857,6 +2875,7 @@ will pipe patch of file #2 to vim - -R
-w
--ignore-all-space
+ --color=WHEN
--in-place
diff1
diff2
@@ -2978,6 +2997,14 @@ will pipe patch of file #2 to vim - -R
+
+
+
+ colorize the output; WHEN
+ can be 'never', 'always', or 'auto' (default: auto, use 'never' to disable)
+
+
+
,
diff --git a/src/interdiff.c b/src/interdiff.c
index 8506630a..fc240843 100644
--- a/src/interdiff.c
+++ b/src/interdiff.c
@@ -90,7 +90,8 @@ struct lines_info {
};
static int human_readable = 1;
-static char diff_opts[4];
+static char *diff_opts[100];
+static int num_diff_opts = 0;
static unsigned int max_context_real = 3, max_context = 3;
static int context_specified = 0;
static int ignore_components = 0;
@@ -708,7 +709,7 @@ output_patch1_only (FILE *p1, FILE *out, int not_reverted)
int diff_is_empty = 1;
unsigned int use_context = max_context;
- if (diff_opts[0] == '\0' && !context_specified)
+ if (!num_diff_opts && !context_specified)
return do_output_patch1_only (p1, out, not_reverted);
/* We want to redo the diff using the supplied options. */
@@ -757,9 +758,9 @@ output_patch1_only (FILE *p1, FILE *out, int not_reverted)
use_context = file_orig.min_context;
if (use_context == 3)
- sprintf(options, "-%su", diff_opts);
+ strcpy (options, "-u");
else
- sprintf (options, "-%sU%d", diff_opts, use_context);
+ sprintf (options, "-U%d", use_context);
/* Write it out. */
write_file (&file_orig, tmpp1fd);
@@ -770,7 +771,11 @@ output_patch1_only (FILE *p1, FILE *out, int not_reverted)
clear_lines_info (&file_new);
fflush (NULL);
- in = xpipe (DIFF, &child, "r", DIFF, options, tmpp1, tmpp2, NULL);
+ char *argv[2 + num_diff_opts + 2 + 1];
+ memcpy (argv, (const char *[]) { DIFF, options }, 2 * sizeof (char *));
+ memcpy (argv + 2, diff_opts, num_diff_opts * sizeof (char *));
+ memcpy (argv + 2 + num_diff_opts, (char *[]) { tmpp1, tmpp2, NULL }, (2 + 1) * sizeof (char *));
+ in = xpipe (DIFF, &child, "r", argv);
/* Eat the first line */
for (;;) {
@@ -836,8 +841,8 @@ apply_patch (FILE *patch, const char *file, int reverted)
else
basename = file;
- w = xpipe(PATCH, &child, "w", PATCH,
- reverted ? "-Rsp0" : "-sp0", file, NULL);
+ w = xpipe(PATCH, &child, "w", (char **) (const char *[]) { PATCH,
+ reverted ? "-Rsp0" : "-sp0", file, NULL });
fprintf (w, "--- %s\n+++ %s\n", basename, basename);
line = NULL;
@@ -1043,9 +1048,9 @@ output_delta (FILE *p1, FILE *p2, FILE *out)
memcpy (tmpp2 + tmplen, tail2, sizeof (tail2));
if (max_context == 3)
- sprintf(options, "-%su", diff_opts);
+ strcpy (options, "-u");
else
- sprintf (options, "-%sU%d", diff_opts, max_context);
+ sprintf (options, "-U%d", max_context);
tmpp1fd = xmkstemp (tmpp1);
tmpp2fd = xmkstemp (tmpp2);
@@ -1102,7 +1107,11 @@ output_delta (FILE *p1, FILE *p2, FILE *out)
fflush (NULL);
- in = xpipe(DIFF, &child, "r", DIFF, options, tmpp1, tmpp2, NULL);
+ char *argv[2 + num_diff_opts + 2 + 1];
+ memcpy (argv, (const char *[]) { DIFF, options }, 2 * sizeof (char *));
+ memcpy (argv + 2, diff_opts, num_diff_opts * sizeof (char *));
+ memcpy (argv + 2 + num_diff_opts, (char *[]) { tmpp1, tmpp2, NULL }, (2 + 1) * sizeof (char *));
+ in = xpipe (DIFF, &child, "r", argv);
/* Eat the first line */
for (;;) {
@@ -1500,14 +1509,23 @@ take_diff (const char *f1, const char *f2, char *headers[2],
FILE *in;
if (max_context == 3)
- sprintf (options, "-%su", diff_opts);
+ strcpy (options, "-u");
else
- sprintf (options, "-%sU%d", diff_opts, max_context);
-
- if (debug)
- printf ("+ " DIFF " %s %s %s\n", options, f1, f2);
+ sprintf (options, "-U%d", max_context);
+
+ char *argv[2 + num_diff_opts + 2 + 1];
+ memcpy (argv, (const char *[]) { DIFF, options }, 2 * sizeof (char *));
+ memcpy (argv + 2, diff_opts, num_diff_opts * sizeof (char *));
+ memcpy (argv + 2 + num_diff_opts, (const char *[]) { f1, f2, NULL }, (2 + 1) * sizeof (char *));
+ if (debug) {
+ fputs ("+", stdout);
+ for (int i = 0; argv[i]; i++) {
+ printf (" %s", argv[i]);
+ }
+ puts ("");
+ }
- in = xpipe (DIFF, &child, "r", DIFF, options, f1, f2, NULL);
+ in = xpipe (DIFF, &child, "r", argv);
/* Eat the first line */
for (;;) {
@@ -2000,6 +2018,9 @@ syntax (int err)
" ignore changes in the amount of whitespace\n"
" -B, --ignore-blank-lines\n"
" ignore changes whose lines are all blank\n"
+" --color[=WHEN]\n"
+" colorize the output; WHEN can be 'never', 'always',\n"
+" or 'auto' (default: auto, use 'never' to disable)\n"
" -p N, --strip-match=N\n"
" pathname components to ignore\n"
" -q, --quiet\n"
@@ -2065,11 +2086,9 @@ int
main (int argc, char *argv[])
{
FILE *p1, *p2;
- int num_diff_opts = 0;
int ret;
get_mode_from_name (argv[0]);
- diff_opts[0] = '\0';
for (;;) {
static struct option long_options[] = {
{"help", 0, 0, 1000 + 'H'},
@@ -2087,6 +2106,7 @@ main (int argc, char *argv[])
{"ignore-space-change", 0, 0, 'b'},
{"ignore-case", 0, 0, 'i'},
{"ignore-all-space", 0, 0, 'w'},
+ {"color", 2, 0, 1000 + 'c'},
{"decompress", 0, 0, 'z'},
{"quiet", 0, 0, 'q'},
{0, 0, 0, 0}
@@ -2131,11 +2151,22 @@ main (int argc, char *argv[])
case 'b':
case 'i':
case 'w':
- if (!memchr (diff_opts, c, num_diff_opts)) {
- diff_opts[num_diff_opts++] = c;
- diff_opts[num_diff_opts] = '\0';
+ if (asprintf (diff_opts + num_diff_opts++, "-%c", c) < 0)
+ error (EXIT_FAILURE, errno, "Memory allocation failed");
+ break;
+ case 1000 + 'c': {
+ /* Determine the color mode: default to "auto" if no argument given */
+ const char *color_mode = optarg ? optarg : "auto";
+
+ /* Handle auto mode: check if stdout is a terminal */
+ if (strcmp(color_mode, "auto") == 0) {
+ color_mode = isatty(STDOUT_FILENO) ? "always" : "never";
}
+
+ if (asprintf (diff_opts + num_diff_opts++, "--color=%s", color_mode) < 0)
+ error (EXIT_FAILURE, errno, "Memory allocation failed");
break;
+ }
case 1000 + 'I':
set_interdiff ();
break;
@@ -2165,6 +2196,19 @@ main (int argc, char *argv[])
error (EXIT_FAILURE, 0,
"-z and --in-place are mutually exclusive.");
+ /* Add default color=always if no color option was specified and we're in a terminal */
+ int has_color_option = 0;
+ for (int i = 0; i < num_diff_opts; i++) {
+ if (strncmp(diff_opts[i], "--color", 7) == 0) {
+ has_color_option = 1;
+ break;
+ }
+ }
+ if (!has_color_option && isatty(STDOUT_FILENO)) {
+ if (asprintf (diff_opts + num_diff_opts++, "--color=always") < 0)
+ error (EXIT_FAILURE, errno, "Memory allocation failed");
+ }
+
if (optind + 2 != argc)
syntax (1);
diff --git a/src/rediff.c b/src/rediff.c
index 7ee59ab1..d0b7126b 100644
--- a/src/rediff.c
+++ b/src/rediff.c
@@ -906,8 +906,8 @@ static int rediff (const char *original, const char *edited, FILE *out)
last->num_lines = linenum - last->line_in_diff + 1;
/* Run diff between original and edited. */
- diff_pipe = xpipe (DIFF, &child, "r", DIFF, "-U0",
- original, edited, NULL);
+ diff_pipe = xpipe (DIFF, &child, "r", (char **) (const char*[]) {DIFF, "-U0",
+ original, edited, NULL });
m = xtmpfile ();
if (m) {
size_t buffer_size = 10000;
diff --git a/src/util.c b/src/util.c
index 93682629..9db66608 100644
--- a/src/util.c
+++ b/src/util.c
@@ -246,7 +246,7 @@ FILE *xopen_unzip (const char *name, const char *mode)
buffer = xmalloc (buflen);
fo = xtmpfile();
- fi = xpipe(zprog, &pid, "r", zprog, name, NULL);
+ fi = xpipe(zprog, &pid, "r", (char **) (const char *[]) { zprog, name, NULL });
while (!feof (fi)) {
size_t count = fread (buffer, 1, buflen, fi);
@@ -280,27 +280,15 @@ FILE *xopen_unzip (const char *name, const char *mode)
}
/* safe pipe/popen. mode is either "r" or "w" */
-FILE * xpipe(const char * cmd, pid_t *pid, const char *mode, ...)
+FILE * xpipe(const char * cmd, pid_t *pid, const char *mode, char *const argv[])
{
- va_list ap;
int fildes[2];
int child;
- int nargs = 0;
- char *argv[128], *arg;
FILE *res;
if (!mode || (*mode != 'r' && *mode != 'w'))
error (EXIT_FAILURE, 0, "xpipe: bad mode: %s", mode);
- va_start(ap, mode);
- do {
- arg = va_arg(ap, char *);
- argv[nargs++] = arg;
- if (nargs >= 128)
- error (EXIT_FAILURE, 0, "xpipe: too many args");
- } while (arg != NULL);
- va_end(ap);
-
fflush (NULL);
if (pipe (fildes) == -1)
error (EXIT_FAILURE, errno, "pipe failed");
diff --git a/src/util.h b/src/util.h
index 64a74810..7382dea2 100644
--- a/src/util.h
+++ b/src/util.h
@@ -46,7 +46,7 @@ FILE *xtmpfile (void);
FILE *xopen(const char *file, const char *mode);
FILE *xopen_seekable(const char *file, const char *mode);
FILE *xopen_unzip(const char *file, const char *mode);
-FILE *xpipe(const char *cmd, pid_t *pid, const char *mode, ...);
+FILE *xpipe(const char *cmd, pid_t *pid, const char *mode, char *const argv[]);
struct patlist;