static char sccsid
[] = "@(#)patch.c 5.9 (Berkeley) 2/18/88";
"$Header: patch.c,v 2.0.1.4 87/02/16 14:00:04 lwall Exp $";
/* patch - a program to apply diffs to original files
* Copyright 1986, Larry Wall
* This program may be copied as long as you don't try to make any
* money off of it, or pretend that you wrote it.
* Revision 2.0.1.4 87/02/16 14:00:04 lwall
* Short replacement caused spurious "Out of sync" message.
* Revision 2.0.1.3 87/01/30 22:45:50 lwall
* Improved diagnostic on sync error.
* Moved do_ed_script() to pch.c.
* Revision 2.0.1.2 86/11/21 09:39:15 lwall
* Fuzz factor caused offset of installed lines.
* Revision 2.0.1.1 86/10/29 13:10:22 lwall
* Backwards search could terminate prematurely.
* Revision 2.0 86/09/17 15:37:32 lwall
* Baseline for netwide release.
* Revision 1.5 86/08/01 20:53:24 lwall
* Changed some %d's to %ld's.
* Revision 1.4 86/08/01 19:17:29 lwall
* Fixes for machines that can't vararg.
* 85/08/15 van%ucbmonet@berkeley
* Changes for 4.3bsd diff -c.
* Revision 1.3 85/03/26 15:07:43 lwall
* Revision 1.2.1.9 85/03/12 17:03:35 lwall
* Changed pfp->_file to fileno(pfp).
* Revision 1.2.1.8 85/03/12 16:30:43 lwall
* Check i_ptr and i_womp to make sure they aren't null before freeing.
* Also allow ed output to be suppressed.
* Revision 1.2.1.7 85/03/12 15:56:13 lwall
* Added -p option from jromine@uci-750a.
* Revision 1.2.1.6 85/03/12 12:12:51 lwall
* Now checks for normalness of file to patch.
* Revision 1.2.1.5 85/03/12 11:52:12 lwall
* Added -D (#ifdef) option from joe@fluke.
* Revision 1.2.1.4 84/12/06 11:14:15 lwall
* Made smarter about SCCS subdirectories.
* Revision 1.2.1.3 84/12/05 11:18:43 lwall
* Added -l switch to do loose string comparison.
* Revision 1.2.1.2 84/12/04 09:47:13 lwall
* Failed hunk count not reset on multiple patch file.
* Revision 1.2.1.1 84/12/04 09:42:37 lwall
* Branch for sdcrdcf changes.
* Revision 1.2 84/11/29 13:29:51 lwall
* Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
* multiple calls to mktemp(). Will now work on machines that can only
* read 32767 chars. Added -R option for diffs with new and old swapped.
* Various cosmetic changes.
* Revision 1.1 84/11/09 17:03:58 lwall
void reinitialize_almost_everything();
void get_some_switches();
/* Apply a set of diffs as appropriate. */
for (i
= 0; i
<MAXFILEC
; i
++)
/* make sure we clean up /tmp in case of disaster */
open_patch_file(filearg
[1]);
there_is_another_patch();
reinitialize_almost_everything()
) { /* for each patch in patch file */
outname
= savestr(filearg
[0]);
/* initialize the patched file */
/* for ed script just up and do it and exit */
if (diff_type
== ED_DIFF
) {
/* initialize reject file */
/* find out where all the lines are */
/* from here on, open no standard i/o files, because malloc */
/* might misfire and we can't catch it easily */
/* apply each hunk of patch */
mymaxfuzz
= pch_context();
if (!skip_rest_of_patch
) {
where
= locate_hunk(fuzz
);
if (hunk
== 1 && where
== Nulline
&& !force
) {
/* dwim for reversed patch? */
Not enough memory to try swapped hunk! Assuming unswapped.\n");
where
= locate_hunk(fuzz
); /* try again */
if (where
== Nulline
) { /* didn't find it swapped */
if (!pch_swap()) /* put it back to normal */
fatal1("Lost hunk on alloc error!\n");
if (!pch_swap()) /* put it back to normal */
fatal1("Lost hunk on alloc error!\n");
Ignoring previously applied (or reversed) patch.\n");
skip_rest_of_patch
= TRUE
;
%seversed (or previously applied) patch detected! %s -R? [y] ",
reverse
? "Assume" : "Ignore");
ask1("Apply anyway? [n] ");
skip_rest_of_patch
= TRUE
;
if (!pch_swap()) /* put it back to normal */
fatal1("Lost hunk on alloc error!\n");
} while (!skip_rest_of_patch
&& where
== Nulline
&&
if (skip_rest_of_patch
) { /* just got decided */
newwhere
= pch_newfirst() + last_offset
;
if (skip_rest_of_patch
) {
say3("Hunk #%d ignored at %ld.\n", hunk
, newwhere
);
else if (where
== Nulline
) {
say3("Hunk #%d failed at %ld.\n", hunk
, newwhere
);
say3("Hunk #%d succeeded at %ld", hunk
, newwhere
);
say2(" with fuzz %ld", fuzz
);
say3(" (offset %ld line%s)",
last_offset
, last_offset
==1L?"":"s");
if (out_of_mem
&& using_plan_a
) {
say1("\n\nRan out of memory using Plan A--trying again...\n\n");
/* finish spewing out the new file */
/* and put the output where desired */
if (!skip_rest_of_patch
) {
if (move_file(TMPOUTNAME
, outname
) < 0) {
chmod(TMPOUTNAME
, filemode
);
chmod(outname
, filemode
);
Strcpy(rejname
, outname
);
if (skip_rest_of_patch
) {
say4("%d out of %d hunks ignored--saving rejects to %s\n",
say4("%d out of %d hunks failed--saving rejects to %s\n",
if (move_file(TMPREJNAME
, rejname
) < 0)
/* Prepare to find the next patch to do in the patch file. */
reinitialize_almost_everything()
if (filearg
[0] != Nullch
&& !out_of_mem
) {
if (revision
!= Nullch
) {
skip_rest_of_patch
= FALSE
;
fatal1("You may not change to a different patch file.\n");
/* Process switches and filenames up to next '+' or end of list. */
for (Argc
--,Argv
++; Argc
; Argc
--,Argv
++) {
return; /* + will be skipped by for loop */
if (*s
!= '-' || !s
[1]) {
fatal1("Too many file arguments.\n");
filearg
[filec
++] = savestr(s
);
origext
= savestr(Argv
[1]);
diff_type
= CONTEXT_DIFF
;
fatal2("Can't cd to %s.\n", s
);
Sprintf(if_defined
, "#ifdef %s\n", s
);
Sprintf(not_defined
, "#ifndef %s\n", s
);
Sprintf(end_defined
, "#endif /* %s */\n", s
);
outname
= savestr(Argv
[1]);
Strcpy(rejname
, Argv
[1]);
skip_rest_of_patch
= TRUE
;
fatal2("Unrecognized switch: %s\n", Argv
[0]);
/* Attempt to find the right place to apply this hunk of patch. */
Reg1 LINENUM first_guess
= pch_first() + last_offset
;
LINENUM pat_lines
= pch_ptrn_lines();
Reg3 LINENUM max_pos_offset
= input_lines
- first_guess
Reg4 LINENUM max_neg_offset
= first_guess
- last_frozen_line
- 1
if (!pat_lines
) /* null range matches always */
if (max_neg_offset
>= first_guess
) /* do not try lines < 0 */
max_neg_offset
= first_guess
- 1;
if (first_guess
<= input_lines
&& patch_match(first_guess
, Nulline
, fuzz
))
for (offset
= 1; ; offset
++) {
Reg5
bool check_after
= (offset
<= max_pos_offset
);
Reg6
bool check_before
= (offset
<= max_neg_offset
);
if (check_after
&& patch_match(first_guess
, offset
, fuzz
)) {
say3("Offset changing from %ld to %ld\n", last_offset
, offset
);
return first_guess
+offset
;
else if (check_before
&& patch_match(first_guess
, -offset
, fuzz
)) {
say3("Offset changing from %ld to %ld\n", last_offset
, -offset
);
return first_guess
-offset
;
else if (!check_before
&& !check_after
)
/* We did not find the pattern, dump out the hunk so they can handle it. */
Reg2 LINENUM pat_end
= pch_end();
/* add in last_offset to guess the same as the previous successful hunk */
LINENUM oldfirst
= pch_first() + last_offset
;
LINENUM newfirst
= pch_newfirst() + last_offset
;
LINENUM oldlast
= oldfirst
+ pch_ptrn_lines() - 1;
LINENUM newlast
= newfirst
+ pch_repl_lines() - 1;
char *stars
= (diff_type
== NEW_CONTEXT_DIFF
? " ****" : "");
char *minuses
= (diff_type
== NEW_CONTEXT_DIFF
? " ----" : " -----");
fprintf(rejfp
, "***************\n");
for (i
=0; i
<=pat_end
; i
++) {
fprintf(rejfp
, "*** 0%s\n", stars
);
else if (oldlast
== oldfirst
)
fprintf(rejfp
, "*** %ld%s\n", oldfirst
, stars
);
fprintf(rejfp
, "*** %ld,%ld%s\n", oldfirst
, oldlast
, stars
);
fprintf(rejfp
, "--- 0%s\n", minuses
);
else if (newlast
== newfirst
)
fprintf(rejfp
, "--- %ld%s\n", newfirst
, minuses
);
fprintf(rejfp
, "--- %ld,%ld%s\n", newfirst
, newlast
, minuses
);
fprintf(rejfp
, "%s", pfetch(i
));
case ' ': case '-': case '+': case '!':
fprintf(rejfp
, "%c %s", pch_char(i
), pfetch(i
));
say1("Fatal internal error in abort_hunk().\n");
/* We found where to apply it (we hope), so do it. */
Reg2 LINENUM lastline
= pch_ptrn_lines();
Reg3 LINENUM
new = lastline
+1;
Reg4
int def_state
= OUTSIDE
;
Reg5
bool R_do_defines
= do_defines
;
Reg6 LINENUM pat_end
= pch_end();
while (pch_char(new) == '=' || pch_char(new) == '\n')
while (old
<= lastline
) {
if (pch_char(old
) == '-') {
copy_till(where
+ old
- 1);
if (def_state
== OUTSIDE
) {
else if (def_state
== IN_IFDEF
) {
fputs(else_defined
, ofp
);
else if (pch_char(new) == '+') {
copy_till(where
+ old
- 1);
if (def_state
== IN_IFNDEF
) {
fputs(else_defined
, ofp
);
else if (def_state
== OUTSIDE
) {
if (pch_char(new) != pch_char(old
)) {
say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
say3("oldchar = '%c', newchar = '%c'\n",
pch_char(old
), pch_char(new));
if (pch_char(new) == '!') {
copy_till(where
+ old
- 1);
while (pch_char(old
) == '!') {
fputs(else_defined
, ofp
);
while (pch_char(new) == '!') {
assert(pch_char(new) == ' ');
if (new <= pat_end
&& pch_char(new) == '+') {
copy_till(where
+ old
- 1);
if (def_state
== OUTSIDE
) {
else if (def_state
== IN_IFNDEF
) {
fputs(else_defined
, ofp
);
while (new <= pat_end
&& pch_char(new) == '+') {
if (R_do_defines
&& def_state
!= OUTSIDE
) {
fatal2("patch: can't create %s.\n", name
);
/* Open a file to put hunks we can't locate. */
rejfp
= fopen(name
, "w");
fatal2("patch: can't create %s.\n", name
);
/* Copy input file to output, up to wherever hunk is to be applied. */
Reg2 LINENUM R_last_frozen_line
= last_frozen_line
;
if (R_last_frozen_line
> lastline
)
say1("patch: misordered hunks! output will be garbled.\n");
while (R_last_frozen_line
< lastline
) {
dump_line(++R_last_frozen_line
);
last_frozen_line
= R_last_frozen_line
;
/* Finish copying the input file to the output file. */
say3("il=%ld lfl=%ld\n",input_lines
,last_frozen_line
);
copy_till(input_lines
); /* dump remainder of file */
/* Copy one line from input to output. */
Reg2
char R_newline
= '\n';
/* Note: string is not null terminated. */
for (s
=ifetch(line
, 0); putc(*s
, ofp
) != R_newline
; s
++) ;
/* Does the patch pattern match at line base+offset? */
patch_match(base
, offset
, fuzz
)
Reg1 LINENUM pline
= 1 + fuzz
;
Reg3 LINENUM pat_lines
= pch_ptrn_lines() - fuzz
;
for (iline
=base
+offset
+fuzz
; pline
<= pat_lines
; pline
++,iline
++) {
if (!similar(ifetch(iline
, (offset
>= 0)),
else if (strnNE(ifetch(iline
, (offset
>= 0)),
/* Do two lines match with canonicalized white space? */
if (isspace(*b
)) { /* whitespace (or \n) to match? */
if (!isspace(*a
)) /* no corresponding whitespace? */
while (len
&& isspace(*b
) && *b
!= '\n')
b
++,len
--; /* skip pattern whitespace */
while (isspace(*a
) && *a
!= '\n')
a
++; /* skip target whitespace */
if (*a
== '\n' || *b
== '\n')
return (*a
== *b
); /* should end in sync */
else if (*a
++ != *b
++) /* match non-whitespace chars */
len
--; /* probably not necessary */
return TRUE
; /* actually, this is not reached */
/* since there is always a \n */