4 /* patch - a program to apply diffs to original files
6 * Copyright 1986, Larry Wall
8 * This program may be copied as long as you don't try to make any
9 * money off of it, or pretend that you wrote it.
12 * Revision 1.1 2005/04/21 14:55:10 beng
15 * Revision 1.1.1.1 2005/04/20 13:33:19 beng
16 * Initial import of minix 2.0.4
18 * Revision 2.0.1.6 88/06/22 20:46:39 lwall
19 * patch12: rindex() wasn't declared
21 * Revision 2.0.1.5 88/06/03 15:09:37 lwall
22 * patch10: exit code improved.
23 * patch10: better support for non-flexfilenames.
25 * Revision 2.0.1.4 87/02/16 14:00:04 lwall
26 * Short replacement caused spurious "Out of sync" message.
28 * Revision 2.0.1.3 87/01/30 22:45:50 lwall
29 * Improved diagnostic on sync error.
30 * Moved do_ed_script() to pch.c.
32 * Revision 2.0.1.2 86/11/21 09:39:15 lwall
33 * Fuzz factor caused offset of installed lines.
35 * Revision 2.0.1.1 86/10/29 13:10:22 lwall
36 * Backwards search could terminate prematurely.
38 * Revision 2.0 86/09/17 15:37:32 lwall
39 * Baseline for netwide release.
41 * Revision 1.5 86/08/01 20:53:24 lwall
42 * Changed some %d's to %ld's.
45 * Revision 1.4 86/08/01 19:17:29 lwall
46 * Fixes for machines that can't vararg.
51 * 85/08/15 van%ucbmonet@berkeley
52 * Changes for 4.3bsd diff -c.
54 * Revision 1.3 85/03/26 15:07:43 lwall
57 * Revision 1.2.1.9 85/03/12 17:03:35 lwall
58 * Changed pfp->_file to fileno(pfp).
60 * Revision 1.2.1.8 85/03/12 16:30:43 lwall
61 * Check i_ptr and i_womp to make sure they aren't null before freeing.
62 * Also allow ed output to be suppressed.
64 * Revision 1.2.1.7 85/03/12 15:56:13 lwall
65 * Added -p option from jromine@uci-750a.
67 * Revision 1.2.1.6 85/03/12 12:12:51 lwall
68 * Now checks for normalness of file to patch.
70 * Revision 1.2.1.5 85/03/12 11:52:12 lwall
71 * Added -D (#ifdef) option from joe@fluke.
73 * Revision 1.2.1.4 84/12/06 11:14:15 lwall
74 * Made smarter about SCCS subdirectories.
76 * Revision 1.2.1.3 84/12/05 11:18:43 lwall
77 * Added -l switch to do loose string comparison.
79 * Revision 1.2.1.2 84/12/04 09:47:13 lwall
80 * Failed hunk count not reset on multiple patch file.
82 * Revision 1.2.1.1 84/12/04 09:42:37 lwall
83 * Branch for sdcrdcf changes.
85 * Revision 1.2 84/11/29 13:29:51 lwall
86 * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
87 * multiple calls to mktemp(). Will now work on machines that can only
88 * read 32767 chars. Added -R option for diffs with new and old swapped.
89 * Various cosmetic changes.
91 * Revision 1.1 84/11/09 17:03:58 lwall
97 * Modified by Saeko & Kouichi Hirabayashi to fit small memory (64K+64K)
98 * system by adding "#if[n]def SMALL" parts.
111 _PROTOTYPE(int main
, (int argc
, char **argv
));
112 _PROTOTYPE(void reinitialize_almost_everything
, (void));
113 _PROTOTYPE(void get_some_switches
, (void));
114 _PROTOTYPE(LINENUM locate_hunk
, (LINENUM fuzz
));
115 _PROTOTYPE(void abort_hunk
, (void));
116 _PROTOTYPE(void apply_hunk
, (LINENUM where
));
117 _PROTOTYPE(void init_output
, (char *name
));
118 _PROTOTYPE(void init_reject
, (char *name
));
119 _PROTOTYPE(void copy_till
, (Reg1 LINENUM lastline
));
120 _PROTOTYPE(void spew_output
, (void));
121 _PROTOTYPE(void dump_line
, (LINENUM line
));
122 _PROTOTYPE(bool patch_match
, (LINENUM base
, LINENUM offset
, LINENUM fuzz
));
123 _PROTOTYPE(bool similar
, (Reg1
char *a
, Reg2
char *b
, Reg3
int len
));
125 /* Apply a set of diffs as appropriate. */
140 setbuf(stderr
, serrbuf
);
141 for (i
= 0; i
<MAXFILEC
; i
++)
156 /* make sure we clean up /tmp in case of disaster */
160 open_patch_file(filearg
[1]);
161 there_is_another_patch();
162 reinitialize_almost_everything()
163 ) { /* for each patch in patch file */
165 if (outname
== Nullch
)
166 outname
= savestr(filearg
[0]);
168 /* initialize the patched file */
169 if (!skip_rest_of_patch
)
170 init_output(TMPOUTNAME
);
172 /* for ed script just up and do it and exit */
173 if (diff_type
== ED_DIFF
) {
178 /* initialize reject file */
179 init_reject(TMPREJNAME
);
181 /* find out where all the lines are */
182 if (!skip_rest_of_patch
)
183 scan_input(filearg
[0]);
185 /* from here on, open no standard i/o files, because malloc */
186 /* might misfire and we can't catch it easily */
188 /* apply each hunk of patch */
192 while (another_hunk()) {
195 mymaxfuzz
= pch_context();
196 if (maxfuzz
< mymaxfuzz
)
198 if (!skip_rest_of_patch
) {
200 where
= locate_hunk(fuzz
);
201 if (hunk
== 1 && where
== Nulline
&& !force
) {
202 /* dwim for reversed patch? */
206 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
210 where
= locate_hunk(fuzz
); /* try again */
211 if (where
== Nulline
) { /* didn't find it swapped */
212 if (!pch_swap()) /* put it back to normal */
213 fatal1("Lost hunk on alloc error!\n");
216 else if (noreverse
) {
217 if (!pch_swap()) /* put it back to normal */
218 fatal1("Lost hunk on alloc error!\n");
221 "Ignoring previously applied (or reversed) patch.\n");
222 skip_rest_of_patch
= TRUE
;
226 "%seversed (or previously applied) patch detected! %s -R? [y] ",
227 reverse
? "R" : "Unr",
228 reverse
? "Assume" : "Ignore");
230 ask1("Apply anyway? [n] ");
232 skip_rest_of_patch
= TRUE
;
235 if (!pch_swap()) /* put it back to normal */
236 fatal1("Lost hunk on alloc error!\n");
240 } while (!skip_rest_of_patch
&& where
== Nulline
&&
241 ++fuzz
<= mymaxfuzz
);
243 if (skip_rest_of_patch
) { /* just got decided */
249 newwhere
= pch_newfirst() + last_offset
;
250 if (skip_rest_of_patch
) {
254 say3("Hunk #%d ignored at %ld.\n", hunk
, newwhere
);
256 else if (where
== Nulline
) {
260 say3("Hunk #%d failed at %ld.\n", hunk
, newwhere
);
265 say3("Hunk #%d succeeded at %ld", hunk
, newwhere
);
267 say2(" with fuzz %ld", fuzz
);
269 say3(" (offset %ld line%s)",
270 last_offset
, last_offset
==1L?"":"s");
276 if (out_of_mem
&& using_plan_a
) {
279 say1("\n\nRan out of memory using Plan A--trying again...\n\n");
285 /* finish spewing out the new file */
286 if (!skip_rest_of_patch
)
289 /* and put the output where desired */
291 if (!skip_rest_of_patch
) {
292 if (move_file(TMPOUTNAME
, outname
) < 0) {
294 chmod(TMPOUTNAME
, filemode
);
297 chmod(outname
, filemode
);
304 Strcpy(rejname
, outname
);
305 #ifndef FLEXFILENAMES
307 char *s
= rindex(rejname
,'/');
312 if (s
[12] == '.') /* try to preserve difference */
313 s
[12] = s
[13]; /* between .h, .c, .y, etc. */
317 Strcat(rejname
, REJEXT
);
319 if (skip_rest_of_patch
) {
320 say4("%d out of %d hunks ignored--saving rejects to %s\n",
321 failed
, hunk
, rejname
);
324 say4("%d out of %d hunks failed--saving rejects to %s\n",
325 failed
, hunk
, rejname
);
327 if (move_file(TMPREJNAME
, rejname
) < 0)
339 /* Prepare to find the next patch to do in the patch file. */
342 reinitialize_almost_everything()
348 last_frozen_line
= 0;
351 if (filearg
[0] != Nullch
&& !out_of_mem
) {
356 if (outname
!= Nullch
) {
365 if (revision
!= Nullch
) {
371 skip_rest_of_patch
= FALSE
;
376 fatal1("You may not change to a different patch file.\n");
379 /* Process switches and filenames up to next '+' or end of list. */
391 for (Argc
--,Argv
++; Argc
; Argc
--,Argv
++) {
394 return; /* + will be skipped by for loop */
396 if (*s
!= '-' || !s
[1]) {
397 if (filec
== MAXFILEC
)
398 fatal1("Too many file arguments.\n");
399 filearg
[filec
++] = savestr(s
);
404 origext
= savestr(Argv
[1]);
408 origprae
= savestr(Argv
[1]);
412 diff_type
= CONTEXT_DIFF
;
420 fatal2("Can't cd to %s.\n", s
);
429 fatal1("Argument to -D not an identifier.\n");
430 Sprintf(if_defined
, "#ifdef %s\n", s
);
431 Sprintf(not_defined
, "#ifndef %s\n", s
);
432 Sprintf(end_defined
, "#endif /* %s */\n", s
);
449 diff_type
= NORMAL_DIFF
;
455 outname
= savestr(Argv
[1]);
464 Strcpy(rejname
, Argv
[1]);
474 skip_rest_of_patch
= TRUE
;
485 fatal2("Unrecognized switch: %s\n", Argv
[0]);
491 /* Attempt to find the right place to apply this hunk of patch. */
497 Reg1 LINENUM first_guess
= pch_first() + last_offset
;
499 LINENUM pat_lines
= pch_ptrn_lines();
500 Reg3 LINENUM max_pos_offset
= input_lines
- first_guess
502 Reg4 LINENUM max_neg_offset
= first_guess
- last_frozen_line
- 1
505 if (!pat_lines
) /* null range matches always */
507 if (max_neg_offset
>= first_guess
) /* do not try lines < 0 */
508 max_neg_offset
= first_guess
- 1;
509 if (first_guess
<= input_lines
&& patch_match(first_guess
, Nulline
, fuzz
))
511 for (offset
= 1; ; offset
++) {
512 Reg5
bool check_after
= (offset
<= max_pos_offset
);
513 Reg6
bool check_before
= (offset
<= max_neg_offset
);
515 if (check_after
&& patch_match(first_guess
, offset
, fuzz
)) {
518 say3("Offset changing from %ld to %ld\n", last_offset
, offset
);
520 last_offset
= offset
;
521 return first_guess
+offset
;
523 else if (check_before
&& patch_match(first_guess
, -offset
, fuzz
)) {
526 say3("Offset changing from %ld to %ld\n", last_offset
, -offset
);
528 last_offset
= -offset
;
529 return first_guess
-offset
;
531 else if (!check_before
&& !check_after
)
536 /* We did not find the pattern, dump out the hunk so they can handle it. */
542 Reg2 LINENUM pat_end
= pch_end();
543 /* add in last_offset to guess the same as the previous successful hunk */
544 LINENUM oldfirst
= pch_first() + last_offset
;
545 LINENUM newfirst
= pch_newfirst() + last_offset
;
546 LINENUM oldlast
= oldfirst
+ pch_ptrn_lines() - 1;
547 LINENUM newlast
= newfirst
+ pch_repl_lines() - 1;
548 char *stars
= (diff_type
== NEW_CONTEXT_DIFF
? " ****" : "");
549 char *minuses
= (diff_type
== NEW_CONTEXT_DIFF
? " ----" : " -----");
551 fprintf(rejfp
, "***************\n");
552 for (i
=0; i
<=pat_end
; i
++) {
553 switch (pch_char(i
)) {
555 if (oldlast
< oldfirst
)
556 fprintf(rejfp
, "*** 0%s\n", stars
);
557 else if (oldlast
== oldfirst
)
558 fprintf(rejfp
, "*** %ld%s\n", oldfirst
, stars
);
560 fprintf(rejfp
, "*** %ld,%ld%s\n", oldfirst
, oldlast
, stars
);
563 if (newlast
< newfirst
)
564 fprintf(rejfp
, "--- 0%s\n", minuses
);
565 else if (newlast
== newfirst
)
566 fprintf(rejfp
, "--- %ld%s\n", newfirst
, minuses
);
568 fprintf(rejfp
, "--- %ld,%ld%s\n", newfirst
, newlast
, minuses
);
571 fprintf(rejfp
, "%s", pfetch(i
));
573 case ' ': case '-': case '+': case '!':
574 fprintf(rejfp
, "%c %s", pch_char(i
), pfetch(i
));
577 say1("Fatal internal error in abort_hunk().\n");
583 /* We found where to apply it (we hope), so do it. */
589 Reg1 LINENUM old
= 1;
590 Reg2 LINENUM lastline
= pch_ptrn_lines();
591 Reg3 LINENUM
new = lastline
+1;
596 Reg4
int def_state
= OUTSIDE
;
597 Reg5
bool R_do_defines
= do_defines
;
598 Reg6 LINENUM pat_end
= pch_end();
601 while (pch_char(new) == '=' || pch_char(new) == '\n')
604 while (old
<= lastline
) {
605 if (pch_char(old
) == '-') {
606 copy_till(where
+ old
- 1);
608 if (def_state
== OUTSIDE
) {
609 fputs(not_defined
, ofp
);
610 def_state
= IN_IFNDEF
;
612 else if (def_state
== IN_IFDEF
) {
613 fputs(else_defined
, ofp
);
616 fputs(pfetch(old
), ofp
);
621 else if (new > pat_end
)
623 else if (pch_char(new) == '+') {
624 copy_till(where
+ old
- 1);
626 if (def_state
== IN_IFNDEF
) {
627 fputs(else_defined
, ofp
);
630 else if (def_state
== OUTSIDE
) {
631 fputs(if_defined
, ofp
);
632 def_state
= IN_IFDEF
;
635 fputs(pfetch(new), ofp
);
639 if (pch_char(new) != pch_char(old
)) {
640 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
641 pch_hunk_beg() + old
,
642 pch_hunk_beg() + new);
644 say3("oldchar = '%c', newchar = '%c'\n",
645 pch_char(old
), pch_char(new));
649 if (pch_char(new) == '!') {
650 copy_till(where
+ old
- 1);
652 fputs(not_defined
, ofp
);
653 def_state
= IN_IFNDEF
;
655 while (pch_char(old
) == '!') {
657 fputs(pfetch(old
), ofp
);
663 fputs(else_defined
, ofp
);
666 while (pch_char(new) == '!') {
667 fputs(pfetch(new), ofp
);
671 fputs(end_defined
, ofp
);
676 assert(pch_char(new) == ' ');
682 if (new <= pat_end
&& pch_char(new) == '+') {
683 copy_till(where
+ old
- 1);
685 if (def_state
== OUTSIDE
) {
686 fputs(if_defined
, ofp
);
687 def_state
= IN_IFDEF
;
689 else if (def_state
== IN_IFNDEF
) {
690 fputs(else_defined
, ofp
);
694 while (new <= pat_end
&& pch_char(new) == '+') {
695 fputs(pfetch(new), ofp
);
699 if (R_do_defines
&& def_state
!= OUTSIDE
) {
700 fputs(end_defined
, ofp
);
704 /* Open the new file. */
710 ofp
= fopen(name
, "w");
712 fatal2("patch: can't create %s.\n", name
);
715 /* Open a file to put hunks we can't locate. */
721 rejfp
= fopen(name
, "w");
723 fatal2("patch: can't create %s.\n", name
);
726 /* Copy input file to output, up to wherever hunk is to be applied. */
730 Reg1 LINENUM lastline
;
732 Reg2 LINENUM R_last_frozen_line
= last_frozen_line
;
734 if (R_last_frozen_line
> lastline
)
735 say1("patch: misordered hunks! output will be garbled.\n");
736 while (R_last_frozen_line
< lastline
) {
737 dump_line(++R_last_frozen_line
);
739 last_frozen_line
= R_last_frozen_line
;
742 /* Finish copying the input file to the output file. */
749 say3("il=%ld lfl=%ld\n",input_lines
,last_frozen_line
);
752 copy_till(input_lines
); /* dump remainder of file */
757 /* Copy one line from input to output. */
764 Reg2
char R_newline
= '\n';
766 /* Note: string is not null terminated. */
767 for (s
=ifetch(line
, 0); putc(*s
, ofp
) != R_newline
; s
++) ;
770 /* Does the patch pattern match at line base+offset? */
773 patch_match(base
, offset
, fuzz
)
778 Reg1 LINENUM pline
= 1 + fuzz
;
780 Reg3 LINENUM pat_lines
= pch_ptrn_lines() - fuzz
;
782 for (iline
=base
+offset
+fuzz
; pline
<= pat_lines
; pline
++,iline
++) {
784 if (!similar(ifetch(iline
, (offset
>= 0)),
786 pch_line_len(pline
) ))
789 else if (strnNE(ifetch(iline
, (offset
>= 0)),
791 pch_line_len(pline
) ))
797 /* Do two lines match with canonicalized white space? */
806 if (isspace(*b
)) { /* whitespace (or \n) to match? */
807 if (!isspace(*a
)) /* no corresponding whitespace? */
809 while (len
&& isspace(*b
) && *b
!= '\n')
810 b
++,len
--; /* skip pattern whitespace */
811 while (isspace(*a
) && *a
!= '\n')
812 a
++; /* skip target whitespace */
813 if (*a
== '\n' || *b
== '\n')
814 return (*a
== *b
); /* should end in sync */
816 else if (*a
++ != *b
++) /* match non-whitespace chars */
819 len
--; /* probably not necessary */
821 return TRUE
; /* actually, this is not reached */
822 /* since there is always a \n */
825 /* Exit with cleanup. */