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;