Generate unified diff style reject files. --- patch.c | 104 +++++++++++++++++++++++++++++++++++++-- patch.man | 3 + tests/unified-reject-files.shrun | 104 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 208 insertions(+), 3 deletions(-) Index: b/patch.man =================================================================== --- a/patch.man +++ b/patch.man @@ -517,6 +517,9 @@ instead of the default .B \&.rej file. .TP +\fB\*=unified\-reject\-files\fP +Produce unified reject files. The default is to produce context type reject files. +.TP \fB\-R\fP or \fB\*=reverse\fP Assume that this patch was created with the old and new files swapped. (Yes, I'm afraid that does happen occasionally, human nature being what it Index: b/patch.c =================================================================== --- a/patch.c +++ b/patch.c @@ -66,8 +66,9 @@ static bool patch_match (LINENUM, LINENU static bool similar (char const *, size_t, char const *, size_t); static bool spew_output (struct outstate *); static char const *make_temp (char); +static void abort_hunk_context (void); +static void abort_hunk_unified (void); static int numeric_string (char const *, bool, char const *); -static void abort_hunk (void); static void cleanup (void); static void get_some_switches (void); static void init_output (char const *, int, struct outstate *); @@ -76,6 +77,8 @@ static void reinitialize_almost_everythi static void remove_if_needed (char const *, int volatile *); static void usage (FILE *, int) __attribute__((noreturn)); +static void (*abort_hunk) (void) = abort_hunk_context; + static bool make_backups; static bool backup_if_mismatch; static char const *version_control; @@ -522,6 +525,7 @@ static struct option const longopts[] = {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, {"posix", no_argument, NULL, CHAR_MAX + 7}, {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, + {"unified-reject-files", no_argument, NULL, CHAR_MAX + 9}, {NULL, no_argument, NULL, 0} }; @@ -580,6 +584,7 @@ static char const *const option_help[] = " --verbose Output extra information about the work being done.", " --dry-run Do not actually change any files; just print what would happen.", " --posix Conform to the POSIX standard.", +" --unified-reject-files Create unified reject files.", "", " -d DIR --directory=DIR Change the working directory to DIR first.", #if HAVE_SETMODE_DOS @@ -779,6 +784,9 @@ get_some_switches (void) (enum quoting_style) i); } break; + case CHAR_MAX + 9: + abort_hunk = abort_hunk_unified; + break; default: usage (stderr, 2); } @@ -930,7 +938,7 @@ locate_hunk (LINENUM fuzz) /* We did not find the pattern, dump out the hunk so they can handle it. */ static void -abort_hunk (void) +abort_hunk_context (void) { register LINENUM i; register LINENUM pat_end = pch_end (); @@ -979,13 +987,103 @@ abort_hunk (void) pch_write_line (i, rejfp); break; default: - fatal ("fatal internal error in abort_hunk"); + fatal ("fatal internal error in abort_hunk_context"); } if (ferror (rejfp)) write_fatal (); } } +/* Produce unified reject files */ + +static void +print_unified(FILE *fp) +{ + register LINENUM old = 1; + register LINENUM lastline = pch_ptrn_lines (); + register LINENUM new = lastline + 1; + register LINENUM pat_end = pch_end (); + + while (pch_char (new) == '=' || pch_char (new) == '\n') + new++; + + while (old <= lastline) + { + if (pch_char (old) == '-') + { + fputc ('-', fp); + pch_write_line (old++, fp); + } + else if (new > pat_end) + break; + else if (pch_char (new) == '+') + { + fputc ('+', fp); + pch_write_line (new++, fp); + } + else if (pch_char (new) != pch_char (old)) + { + char numbuf0[LINENUM_LENGTH_BOUND + 1]; + char numbuf1[LINENUM_LENGTH_BOUND + 1]; + if (debug & 1) + say ("oldchar = '%c', newchar = '%c'\n", + pch_char (old), pch_char (new)); + fatal ("Out-of-sync patch, lines %s,%s -- mangled text or " + "line numbers, maybe?", + format_linenum (numbuf0, pch_hunk_beg () + old), + format_linenum (numbuf1, pch_hunk_beg () + new)); + } + else if (pch_char (new) == '!') + { + do + { + fputc ('-', fp); + pch_write_line (old++, fp); + } + while (pch_char (old) == '!'); + do + { + fputc ('+', fp); + pch_write_line (new++, fp); + } + while (pch_char (new) == '!'); + } + else + { + assert (pch_char (new) == ' '); + fputc (' ', fp); + pch_write_line (new, fp); + old++; + new++; + } + } + while (pch_char (new) == '+' || pch_char (new) == ' ') + { + fputc ('+', fp); + pch_write_line (new++, fp); + } + assert (new > pat_end); +} + +static void +abort_hunk_unified (void) +{ + char rangebuf0[LINERANGE_LENGTH_BOUND + 1]; + char rangebuf1[LINERANGE_LENGTH_BOUND + 1]; + LINENUM oldfirst = pch_first () + last_offset; + LINENUM newfirst = pch_newfirst () + last_offset; + char const *c_function = pch_c_function (); + + fprintf (rejfp, "@@ -%s +%s @@%s\n", + format_startcount (rangebuf0, oldfirst, pch_ptrn_lines ()), + format_startcount (rangebuf1, newfirst, pch_repl_lines ()), + c_function ? c_function : ""); + print_unified (rejfp); + + if (ferror (rejfp)) + write_fatal (); +} + /* We found where to apply it (we hope), so do it. */ static bool Index: b/tests/unified-reject-files.shrun =================================================================== --- /dev/null +++ b/tests/unified-reject-files.shrun @@ -0,0 +1,104 @@ +$ PATCH=$(pwd)/patch +$ tmpdir=$(mktemp -d) +$ trap "cd /; rm -rf $tmpdir" EXIT +$ cd $tmpdir +$ cat > f.orig +< a() { +< 2 +< 3 +< +< 5 +< 6 +< } + +$ sed -e 's/5/5a/' f.orig > f +$ diff -U0 -p \ ++ -L "f.orig 2009-02-07 14:49:02.000000000 +0100" \ ++ -L "f 2009-02-07 14:49:03.000000000 +0100" \ ++ f.orig f > f.diff + +$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +Note: patch cannot deal with nanosecond timestamps :-( + +$ cat f.rej +> *************** a() { +> *** 5 **** +> - 5 +> --- 5 ---- +> + 5a + +$ diff -U0 -p -L f.orig -L f f.orig f > f.diff +$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +$ cat f.rej +> @@ -5 +5 @@ a() { +> -5 +> +5a + +$ $PATCH -f -F0 -s --no-backup-if-mismatch f < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +$ cat f.rej +> *************** a() { +> *** 5 **** +> - 5 +> --- 5 ---- +> + 5a + +$ $PATCH -f -F0 -s --no-backup-if-mismatch --unified-reject-files -R f.orig < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.orig.rej + +$ cat f.orig.rej +> @@ -5 +5 @@ a() { +> -5a +> +5 + +$ diff -U2 -p -L f.orig -L f f.orig f > f.diff +$ sed -e 's/5/5a/' -e 's/6/6x/' f.orig > f +$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +$ cat f.rej +> @@ -3,5 +3,5 @@ a() { +> 3 +> +> -5 +> +5a +> 6 +> } + +$ $PATCH -F0 -s --no-backup-if-mismatch f < f.diff +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +$ cat f.rej +> *************** a() { +> *** 3,7 **** +> 3 +> +> - 5 +> 6 +> } +> --- 3,7 ---- +> 3 +> +> + 5a +> 6 +> } + +$ diff -Nu -p -L /dev/null -L f.orig /dev/null f.orig > f2.diff +$ $PATCH -F0 -s --no-backup-if-mismatch --unified-reject-files f --set-utc < f2.diff +> Patch attempted to create file f, which already exists. +> 1 out of 1 hunk FAILED -- saving rejects to file f.rej + +$ cat f.rej +> @@ -0,0 +1,7 @@ +> +a() { +> +2 +> +3 +> + +> +5 +> +6 +> +}