Add unified reject format and the --reject-format=FORMAT option

This commit is contained in:
Andreas Gruenbacher 2009-03-21 00:09:59 +01:00
parent 4399677be9
commit 0a22c10196
9 changed files with 351 additions and 22 deletions

View File

@ -1,3 +1,15 @@
2009-03-20 Andreas Gruenbacher <agruen@suse.de>
* patch.c: New option --reject-format=FORMAT.
(abort_hunk_context): Rename from abort_hunk().
(abort_hunk_unified, abort_hunk, mangled_patch,
print_unidiff_range): New functions.
* patch.man: Document this.
* pch.c, pch.h (pch_normalize): New function.
* tests/reject-format: New test case.
* Makefile.in (TESTS): Add test case.
* tests/preserve-c-function-names: Update.
2009-03-19 Andreas Gruenbacher <agruen@suse.de>
* Makefile.in (DISTFILES): Add the files from DISTFILES_M4,

View File

@ -88,9 +88,9 @@ HDRS = argmatch.h backupfile.h common.h dirname.h \
MISC = AUTHORS COPYING ChangeLog INSTALL Makefile.in NEWS README VERSION \
aclocal.m4 config.hin configure configure.ac install-sh \
mkinstalldirs patch.man stdbool_.h timespec.h
TESTS = tests/corrupt-reject-files tests/no-newline-triggers-assert \
tests/preserve-c-function-names \
tests/remember-backup-files tests/crlf-handling
TESTS = tests/corrupt-reject-files tests/crlf-handling \
tests/no-newline-triggers-assert tests/preserve-c-function-names \
tests/reject-format tests/remember-backup-files
DISTFILES = $(MISC) $(SRCS) $(HDRS) $(M4FILES) $(TESTS) \
pc/chdirsaf.c pc/djgpp/config.sed pc/djgpp/configure.bat \
pc/djgpp/configure.sed pc/djgpp/README

8
NEWS
View File

@ -1,3 +1,11 @@
Changes in version 2.6:
* If the input patch is in unified format, any .rej output is now
in unified format as well. Formerly it was in context format.
This can be overridden with --reject-format=FORMAT.
* Changed lines in reject files in context format will now be indicated
with '!' as per the definition of the format instead of '+' and '-'.
Changes in versions 2.5.8 and 2.5.9: bug fixes only.
Changes in version 2.5.7:

138
patch.c
View File

@ -68,7 +68,6 @@ static bool similar (char const *, size_t, char const *, size_t);
static bool spew_output (struct outstate *, struct stat *);
static char const *make_temp (char);
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 *);
@ -77,6 +76,11 @@ static void reinitialize_almost_everything (void);
static void remove_if_needed (char const *, int volatile *);
static void usage (FILE *, int) __attribute__((noreturn));
static void abort_hunk (void);
static void abort_hunk_context (void);
static void abort_hunk_unified (void);
static enum diff reject_format = NO_DIFF; /* automatic */
static bool make_backups;
static bool backup_if_mismatch;
static char const *version_control;
@ -527,6 +531,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},
{"reject-format", required_argument, NULL, CHAR_MAX + 9},
{NULL, no_argument, NULL, 0}
};
@ -587,6 +592,7 @@ static char const *const option_help[] =
" --posix Conform to the POSIX standard.",
"",
" -d DIR --directory=DIR Change the working directory to DIR first.",
" --reject-format=FORMAT Create 'context' or 'unified' rejects.",
#if HAVE_SETMODE_DOS
" --binary Read and write data in binary mode.",
#else
@ -784,6 +790,14 @@ get_some_switches (void)
(enum quoting_style) i);
}
break;
case CHAR_MAX + 9:
if (strcmp (optarg, "context") == 0)
reject_format = NEW_CONTEXT_DIFF;
else if (strcmp (optarg, "unified") == 0)
reject_format = UNI_DIFF;
else
usage (stderr, 2);
break;
default:
usage (stderr, 2);
}
@ -932,10 +946,99 @@ locate_hunk (LINENUM fuzz)
return 0;
}
/* We did not find the pattern, dump out the hunk so they can handle it. */
static void
mangled_patch (LINENUM old, LINENUM new)
{
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));
}
/* Output a line number range in unified format. */
static void
abort_hunk (void)
print_unidiff_range (FILE *fp, LINENUM start, LINENUM count)
{
char numbuf0[LINENUM_LENGTH_BOUND + 1];
char numbuf1[LINENUM_LENGTH_BOUND + 1];
switch (count)
{
case 0:
fprintf (fp, "%s,0", format_linenum (numbuf0, start - 1));
break;
case 1:
fprintf (fp, "%s", format_linenum (numbuf0, start));
break;
default:
fprintf (fp, "%s,%s",
format_linenum (numbuf0, start),
format_linenum (numbuf1, count));
break;
}
}
/* Produce unified reject files */
static void
abort_hunk_unified (void)
{
FILE *fp = rejfp;
register LINENUM old = 1;
register LINENUM lastline = pch_ptrn_lines ();
register LINENUM new = lastline + 1;
/* Add last_offset to guess the same as the previous successful hunk. */
fprintf (fp, "@@ -");
print_unidiff_range (fp, pch_first () + last_offset, lastline);
fprintf (fp, " +");
print_unidiff_range (fp, pch_newfirst () + last_offset, pch_repl_lines ());
fprintf (fp, " @@\n");
while (pch_char (new) == '=' || pch_char (new) == '\n')
new++;
if (diff_type != UNI_DIFF)
pch_normalize (UNI_DIFF);
for (; ; old++, new++)
{
for (; pch_char (old) == '-'; old++)
{
fputc ('-', fp);
pch_write_line (old, fp);
}
for (; pch_char (new) == '+'; new++)
{
fputc ('+', fp);
pch_write_line (new, fp);
}
if (old > lastline)
break;
if (pch_char (new) != pch_char (old))
mangled_patch (old, new);
fputc (' ', fp);
pch_write_line (old, fp);
}
if (pch_char (new) != '^')
mangled_patch (old, new);
}
/* Output the rejected patch in context format. */
static void
abort_hunk_context (void)
{
register LINENUM i;
register LINENUM pat_end = pch_end ();
@ -950,6 +1053,9 @@ abort_hunk (void)
(int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----";
char const *c_function = pch_c_function();
if (diff_type == UNI_DIFF)
pch_normalize (NEW_CONTEXT_DIFF);
fprintf(rejfp, "***************%s\n", c_function ? c_function : "");
for (i=0; i<=pat_end; i++) {
char numbuf0[LINENUM_LENGTH_BOUND + 1];
@ -984,13 +1090,25 @@ 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 ();
}
}
/* Output the rejected hunk. */
static void
abort_hunk (void)
{
if (reject_format == UNI_DIFF
|| (reject_format == NO_DIFF && diff_type == UNI_DIFF))
abort_hunk_unified ();
else
abort_hunk_context ();
}
/* We found where to apply it (we hope), so do it. */
static bool
@ -1054,16 +1172,8 @@ apply_hunk (struct outstate *outstate, LINENUM where)
outstate->zero_output = false;
new++;
}
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) != pch_char(old))
mangled_patch (old, new);
else if (pch_char(new) == '!') {
assert (outstate->after_newline);
if (! copy_till (outstate, where + old - 1))

View File

@ -111,8 +111,7 @@ would generate a file name that is too long
makes the file name too long, then
.B #
replaces the file name's last character).
(The rejected hunk comes out in ordinary context diff form regardless of
the input patch's form.
(The rejected hunk comes out in unified or context diff format.
If the input was a normal diff, many of the contexts are simply null.)
The line numbers on the hunks in the reject file may be different than
in the patch file: they reflect the approximate location patch thinks the
@ -546,6 +545,12 @@ Luckily, most patches add or change lines rather than delete them, so most
reversed normal diffs begin with a delete, which fails, triggering
the heuristic.)
.TP
\fB\*=reject\-format=\fP\fIformat\fP
Produce reject files in the specified \fIformat\fP (either \fBcontext\fP or
\fBunified\fP). Without this option, rejected hunks come out in unified diff
format if the input patch was of that format, otherwise in ordinary context
diff form.
.TP
\fB\-s\fP or \fB\*=silent\fP or \fB\*=quiet\fP
Work silently, unless an error occurs.
.TP

67
pch.c
View File

@ -2054,3 +2054,70 @@ do_ed_script (FILE *ofp)
read_fatal ();
}
}
void
pch_normalize (enum diff format)
{
LINENUM old = 1;
LINENUM new = p_ptrn_lines + 1;
while (p_Char[new] == '=' || p_Char[new] == '\n')
new++;
if (format == UNI_DIFF)
{
/* Convert '!' markers into '-' and '+' as defined by the Unified
Format. */
for (; old <= p_ptrn_lines; old++)
if (p_Char[old] == '!')
p_Char[old] = '-';
for (; new <= p_end; new++)
if (p_Char[new] == '!')
p_Char[new] = '+';
}
else
{
/* Convert '-' and '+' markers which are part of a group into '!' as
defined by the Context Format. */
while (old <= p_ptrn_lines)
{
if (p_Char[old] == '-')
{
if (new <= p_end && p_Char[new] == '+')
{
do
{
p_Char[old] = '!';
old++;
}
while (old <= p_ptrn_lines && p_Char[old] == '-');
do
{
p_Char[new] = '!';
new++;
}
while (new <= p_end && p_Char[new] == '+');
}
else
{
do
old++;
while (old <= p_ptrn_lines && p_Char[old] == '-');
}
}
else if (new <= p_end && p_Char[new] == '+')
{
do
new++;
while (new <= p_end && p_Char[new] == '+');
}
else
{
old++;
new++;
}
}
}
}

1
pch.h
View File

@ -44,3 +44,4 @@ void do_ed_script (FILE *);
void open_patch_file (char const *);
void re_patch (void);
void set_hunkmax (void);
void pch_normalize (enum diff);

View File

@ -33,7 +33,7 @@ int foo()
}
EOF
diff -p -u a b > ab.diff
diff -p -c a b > ab.diff
printf "" > c
check 'patch c < ab.diff || cat c.rej' <<EOF
patching file c
@ -44,12 +44,12 @@ Hunk #1 FAILED at 2.
{
/* waste a line */
- return 1;
! return 1;
}
--- 2,6 ----
{
/* waste a line */
+ return 2;
! return 2;
}
EOF

126
tests/reject-format Executable file
View File

@ -0,0 +1,126 @@
#! /bin/bash
# Copyright (C) 2009 Free Software Foundation, Inc.
#
# Copying and distribution of this file, with or without modification,
# in any medium, are permitted without royalty provided the copyright
# notice and this notice are preserved.
# Test the --reject-format=FORMAT option
. $srcdir/test-lib.sh
require_cat
require_diff
use_local_patch
use_tmpdir
# ==============================================================
cat > f.orig <<EOF
a() {
2
3
5
6
}
EOF
sed -e 's/5/5a/' f.orig > f
diff -p -c f.orig f > f.diff
check 'patch -f -F0 --no-backup-if-mismatch f < f.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
Status: 1
EOF
check 'cat f.rej' <<EOF
*************** a() {
*** 2,7 ****
2
3
! 5
6
}
--- 2,7 ----
2
3
! 5a
6
}
EOF
# --------------------------------------------------------------
diff -p -u f.orig f > f.diff
check 'patch -f -F0 --no-backup-if-mismatch f < f.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
Status: 1
EOF
check 'cat f.rej' <<EOF
@@ -2,6 +2,6 @@
2
3
-5
+5a
6
}
EOF
# --------------------------------------------------------------
diff -p -c f.orig f > f.diff
check 'patch -f -F0 --no-backup-if-mismatch --reject-format=unified f < f.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
Status: 1
EOF
check 'cat f.rej' <<EOF
@@ -2,6 +2,6 @@
2
3
-5
+5a
6
}
EOF
# --------------------------------------------------------------
diff -p -u f.orig f > f.diff
check 'patch -f -F0 --no-backup-if-mismatch --reject-format=context f < f.diff || echo "Status: $?"' <<EOF
patching file f
Hunk #1 FAILED at 2.
1 out of 1 hunk FAILED -- saving rejects to file f.rej
Status: 1
EOF
check 'cat f.rej' <<EOF
*************** a() {
*** 2,7 ****
2
3
! 5
6
}
--- 2,7 ----
2
3
! 5a
6
}
EOF