2 "$Header: patch.c,v 2.0.2.0 90/05/01 22:17:50 davison Locked $";
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 2.0.2.0 90/05/01 22:17:50 davison
13 * patch12u: unidiff support added
15 * Revision 2.0.1.6 88/06/22 20:46:39 lwall
16 * patch12: rindex() wasn't declared
18 * Revision 2.0.1.5 88/06/03 15:09:37 lwall
19 * patch10: exit code improved.
20 * patch10: better support for non-flexfilenames.
22 * Revision 2.0.1.4 87/02/16 14:00:04 lwall
23 * Short replacement caused spurious "Out of sync" message.
25 * Revision 2.0.1.3 87/01/30 22:45:50 lwall
26 * Improved diagnostic on sync error.
27 * Moved do_ed_script() to pch.c.
29 * Revision 2.0.1.2 86/11/21 09:39:15 lwall
30 * Fuzz factor caused offset of installed lines.
32 * Revision 2.0.1.1 86/10/29 13:10:22 lwall
33 * Backwards search could terminate prematurely.
35 * Revision 2.0 86/09/17 15:37:32 lwall
36 * Baseline for netwide release.
38 * Revision 1.5 86/08/01 20:53:24 lwall
39 * Changed some %d's to %ld's.
42 * Revision 1.4 86/08/01 19:17:29 lwall
43 * Fixes for machines that can't vararg.
48 * 85/08/15 van%ucbmonet@berkeley
49 * Changes for 4.3bsd diff -c.
51 * Revision 1.3 85/03/26 15:07:43 lwall
54 * Revision 1.2.1.9 85/03/12 17:03:35 lwall
55 * Changed pfp->_file to fileno(pfp).
57 * Revision 1.2.1.8 85/03/12 16:30:43 lwall
58 * Check i_ptr and i_womp to make sure they aren't null before freeing.
59 * Also allow ed output to be suppressed.
61 * Revision 1.2.1.7 85/03/12 15:56:13 lwall
62 * Added -p option from jromine@uci-750a.
64 * Revision 1.2.1.6 85/03/12 12:12:51 lwall
65 * Now checks for normalness of file to patch.
67 * Revision 1.2.1.5 85/03/12 11:52:12 lwall
68 * Added -D (#ifdef) option from joe@fluke.
70 * Revision 1.2.1.4 84/12/06 11:14:15 lwall
71 * Made smarter about SCCS subdirectories.
73 * Revision 1.2.1.3 84/12/05 11:18:43 lwall
74 * Added -l switch to do loose string comparison.
76 * Revision 1.2.1.2 84/12/04 09:47:13 lwall
77 * Failed hunk count not reset on multiple patch file.
79 * Revision 1.2.1.1 84/12/04 09:42:37 lwall
80 * Branch for sdcrdcf changes.
82 * Revision 1.2 84/11/29 13:29:51 lwall
83 * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed
84 * multiple calls to mktemp(). Will now work on machines that can only
85 * read 32767 chars. Added -R option for diffs with new and old swapped.
86 * Various cosmetic changes.
88 * Revision 1.1 84/11/09 17:03:58 lwall
100 #include "backupfile.h"
104 void reinitialize_almost_everything();
105 void get_some_switches();
106 LINENUM
locate_hunk();
119 /* TRUE if -E was specified on command line. */
120 static int remove_empty_files
= FALSE
;
122 /* TRUE if -R was specified on command line. */
123 static int reverse_flag_specified
= FALSE
;
125 /* Apply a set of diffs as appropriate. */
141 setbuf(stderr
, serrbuf
);
142 for (i
= 0; i
<MAXFILEC
; i
++)
147 /* Cons up the names of the temporary files. */
149 /* Directory for temporary files. */
153 tmpdir
= getenv ("TMPDIR");
154 if (tmpdir
== NULL
) {
157 tmpname_len
= strlen (tmpdir
) + 20;
159 TMPOUTNAME
= (char *) malloc (tmpname_len
);
160 strcpy (TMPOUTNAME
, tmpdir
);
161 strcat (TMPOUTNAME
, "/patchoXXXXXX");
164 TMPINNAME
= (char *) malloc (tmpname_len
);
165 strcpy (TMPINNAME
, tmpdir
);
166 strcat (TMPINNAME
, "/patchiXXXXXX");
169 TMPREJNAME
= (char *) malloc (tmpname_len
);
170 strcpy (TMPREJNAME
, tmpdir
);
171 strcat (TMPREJNAME
, "/patchrXXXXXX");
174 TMPPATNAME
= (char *) malloc (tmpname_len
);
175 strcpy (TMPPATNAME
, tmpdir
);
176 strcat (TMPPATNAME
, "/patchpXXXXXX");
183 v
= getenv ("SIMPLE_BACKUP_SUFFIX");
185 simple_backup_suffix
= v
;
187 simple_backup_suffix
= ORIGEXT
;
189 v
= getenv ("VERSION_CONTROL");
190 backup_type
= get_version (v
); /* OK to pass NULL. */
199 /* make sure we clean up /tmp in case of disaster */
203 open_patch_file(filearg
[1]);
204 there_is_another_patch();
205 reinitialize_almost_everything()
206 ) { /* for each patch in patch file */
208 if (outname
== Nullch
)
209 outname
= savestr(filearg
[0]);
211 /* for ed script just up and do it and exit */
212 if (diff_type
== ED_DIFF
) {
217 /* initialize the patched file */
218 if (!skip_rest_of_patch
)
219 init_output(TMPOUTNAME
);
221 /* initialize reject file */
222 init_reject(TMPREJNAME
);
224 /* find out where all the lines are */
225 if (!skip_rest_of_patch
)
226 scan_input(filearg
[0]);
228 /* from here on, open no standard i/o files, because malloc */
229 /* might misfire and we can't catch it easily */
231 /* apply each hunk of patch */
235 while (another_hunk()) {
238 mymaxfuzz
= pch_context();
239 if (maxfuzz
< mymaxfuzz
)
241 if (!skip_rest_of_patch
) {
243 where
= locate_hunk(fuzz
);
244 if (hunk
== 1 && where
== Nulline
&& !force
) {
245 /* dwim for reversed patch? */
249 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
253 where
= locate_hunk(fuzz
); /* try again */
254 if (where
== Nulline
) { /* didn't find it swapped */
255 if (!pch_swap()) /* put it back to normal */
256 fatal1("lost hunk on alloc error!\n");
259 else if (noreverse
) {
260 if (!pch_swap()) /* put it back to normal */
261 fatal1("lost hunk on alloc error!\n");
264 "Ignoring previously applied (or reversed) patch.\n");
265 skip_rest_of_patch
= TRUE
;
270 "%seversed (or previously applied) patch detected! %s -R.",
271 reverse
? "R" : "Unr",
272 reverse
? "Assuming" : "Ignoring");
276 "%seversed (or previously applied) patch detected! %s -R? [y] ",
277 reverse
? "R" : "Unr",
278 reverse
? "Assume" : "Ignore");
280 ask1("Apply anyway? [n] ");
282 skip_rest_of_patch
= TRUE
;
285 if (!pch_swap()) /* put it back to normal */
286 fatal1("lost hunk on alloc error!\n");
290 } while (!skip_rest_of_patch
&& where
== Nulline
&&
291 ++fuzz
<= mymaxfuzz
);
293 if (skip_rest_of_patch
) { /* just got decided */
299 newwhere
= pch_newfirst() + last_offset
;
300 if (skip_rest_of_patch
) {
304 say3("Hunk #%d ignored at %ld.\n", hunk
, newwhere
);
306 else if (where
== Nulline
) {
310 say3("Hunk #%d failed at %ld.\n", hunk
, newwhere
);
315 say3("Hunk #%d succeeded at %ld", hunk
, newwhere
);
317 say2(" with fuzz %ld", fuzz
);
319 say3(" (offset %ld line%s)",
320 last_offset
, last_offset
==1L?"":"s");
326 if (out_of_mem
&& using_plan_a
) {
329 say1("\n\nRan out of memory using Plan A--trying again...\n\n");
341 /* finish spewing out the new file */
342 if (!skip_rest_of_patch
)
345 /* and put the output where desired */
347 if (!skip_rest_of_patch
) {
349 char *realout
= outname
;
351 if (move_file(TMPOUTNAME
, outname
) < 0) {
353 realout
= TMPOUTNAME
;
354 chmod(TMPOUTNAME
, filemode
);
357 chmod(outname
, filemode
);
359 if (remove_empty_files
&& stat(realout
, &statbuf
) == 0
360 && statbuf
.st_size
== 0) {
362 say2("Removing %s (empty after patching).\n", realout
);
363 while (unlink(realout
) >= 0) ; /* while is for Eunice. */
371 Strcpy(rejname
, outname
);
372 #ifndef FLEXFILENAMES
374 char *s
= rindex(rejname
,'/');
379 if (s
[12] == '.') /* try to preserve difference */
380 s
[12] = s
[13]; /* between .h, .c, .y, etc. */
384 Strcat(rejname
, REJEXT
);
386 if (skip_rest_of_patch
) {
387 say4("%d out of %d hunks ignored--saving rejects to %s\n",
388 failed
, hunk
, rejname
);
391 say4("%d out of %d hunks failed--saving rejects to %s\n",
392 failed
, hunk
, rejname
);
394 if (move_file(TMPREJNAME
, rejname
) < 0)
402 /* Prepare to find the next patch to do in the patch file. */
405 reinitialize_almost_everything()
411 last_frozen_line
= 0;
414 if (filearg
[0] != Nullch
&& !out_of_mem
) {
419 if (outname
!= Nullch
) {
428 if (revision
!= Nullch
) {
433 reverse
= reverse_flag_specified
;
434 skip_rest_of_patch
= FALSE
;
439 fatal1("you may not change to a different patch file\n");
446 fatal2("missing argument after `%s'\n", *Argv
);
450 /* Process switches and filenames up to next '+' or end of list. */
462 for (Argc
--,Argv
++; Argc
; Argc
--,Argv
++) {
465 return; /* + will be skipped by for loop */
467 if (*s
!= '-' || !s
[1]) {
468 if (filec
== MAXFILEC
)
469 fatal1("too many file arguments\n");
470 filearg
[filec
++] = savestr(s
);
475 simple_backup_suffix
= savestr(nextarg());
478 origprae
= savestr(nextarg());
481 diff_type
= CONTEXT_DIFF
;
487 pfatal2("can't cd to %s", s
);
493 if (!isalpha(*s
) && '_' != *s
)
494 fatal1("argument to -D is not an identifier\n");
495 Sprintf(if_defined
, "#ifdef %s\n", s
);
496 Sprintf(not_defined
, "#ifndef %s\n", s
);
497 Sprintf(end_defined
, "#endif /* %s */\n", s
);
503 remove_empty_files
= TRUE
;
517 diff_type
= NORMAL_DIFF
;
523 outname
= savestr(nextarg());
531 Strcpy(rejname
, nextarg());
535 reverse_flag_specified
= TRUE
;
541 skip_rest_of_patch
= TRUE
;
547 diff_type
= UNI_DIFF
;
554 backup_type
= get_version (nextarg ());
563 fprintf(stderr
, "patch: unrecognized option `%s'\n", Argv
[0]);
565 Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
567 [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
568 [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
569 [-r rej-name] [-V {numbered,existing,simple}]\n");
576 /* Attempt to find the right place to apply this hunk of patch. */
582 Reg1 LINENUM first_guess
= pch_first() + last_offset
;
584 LINENUM pat_lines
= pch_ptrn_lines();
585 Reg3 LINENUM max_pos_offset
= input_lines
- first_guess
587 Reg4 LINENUM max_neg_offset
= first_guess
- last_frozen_line
- 1
590 if (!pat_lines
) /* null range matches always */
592 if (max_neg_offset
>= first_guess
) /* do not try lines < 0 */
593 max_neg_offset
= first_guess
- 1;
594 if (first_guess
<= input_lines
&& patch_match(first_guess
, Nulline
, fuzz
))
596 for (offset
= 1; ; offset
++) {
597 Reg5
bool check_after
= (offset
<= max_pos_offset
);
598 Reg6
bool check_before
= (offset
<= max_neg_offset
);
600 if (check_after
&& patch_match(first_guess
, offset
, fuzz
)) {
603 say3("Offset changing from %ld to %ld\n", last_offset
, offset
);
605 last_offset
= offset
;
606 return first_guess
+offset
;
608 else if (check_before
&& patch_match(first_guess
, -offset
, fuzz
)) {
611 say3("Offset changing from %ld to %ld\n", last_offset
, -offset
);
613 last_offset
= -offset
;
614 return first_guess
-offset
;
616 else if (!check_before
&& !check_after
)
621 /* We did not find the pattern, dump out the hunk so they can handle it. */
627 Reg2 LINENUM pat_end
= pch_end();
628 /* add in last_offset to guess the same as the previous successful hunk */
629 LINENUM oldfirst
= pch_first() + last_offset
;
630 LINENUM newfirst
= pch_newfirst() + last_offset
;
631 LINENUM oldlast
= oldfirst
+ pch_ptrn_lines() - 1;
632 LINENUM newlast
= newfirst
+ pch_repl_lines() - 1;
633 char *stars
= (diff_type
>= NEW_CONTEXT_DIFF
? " ****" : "");
634 char *minuses
= (diff_type
>= NEW_CONTEXT_DIFF
? " ----" : " -----");
636 fprintf(rejfp
, "***************\n");
637 for (i
=0; i
<=pat_end
; i
++) {
638 switch (pch_char(i
)) {
640 if (oldlast
< oldfirst
)
641 fprintf(rejfp
, "*** 0%s\n", stars
);
642 else if (oldlast
== oldfirst
)
643 fprintf(rejfp
, "*** %ld%s\n", oldfirst
, stars
);
645 fprintf(rejfp
, "*** %ld,%ld%s\n", oldfirst
, oldlast
, stars
);
648 if (newlast
< newfirst
)
649 fprintf(rejfp
, "--- 0%s\n", minuses
);
650 else if (newlast
== newfirst
)
651 fprintf(rejfp
, "--- %ld%s\n", newfirst
, minuses
);
653 fprintf(rejfp
, "--- %ld,%ld%s\n", newfirst
, newlast
, minuses
);
656 fprintf(rejfp
, "%s", pfetch(i
));
658 case ' ': case '-': case '+': case '!':
659 fprintf(rejfp
, "%c %s", pch_char(i
), pfetch(i
));
662 fatal1("fatal internal error in abort_hunk\n");
667 /* We found where to apply it (we hope), so do it. */
673 Reg1 LINENUM old
= 1;
674 Reg2 LINENUM lastline
= pch_ptrn_lines();
675 Reg3 LINENUM
new = lastline
+1;
680 Reg4
int def_state
= OUTSIDE
;
681 Reg5
bool R_do_defines
= do_defines
;
682 Reg6 LINENUM pat_end
= pch_end();
685 while (pch_char(new) == '=' || pch_char(new) == '\n')
688 while (old
<= lastline
) {
689 if (pch_char(old
) == '-') {
690 copy_till(where
+ old
- 1);
692 if (def_state
== OUTSIDE
) {
693 fputs(not_defined
, ofp
);
694 def_state
= IN_IFNDEF
;
696 else if (def_state
== IN_IFDEF
) {
697 fputs(else_defined
, ofp
);
700 fputs(pfetch(old
), ofp
);
705 else if (new > pat_end
) {
708 else if (pch_char(new) == '+') {
709 copy_till(where
+ old
- 1);
711 if (def_state
== IN_IFNDEF
) {
712 fputs(else_defined
, ofp
);
715 else if (def_state
== OUTSIDE
) {
716 fputs(if_defined
, ofp
);
717 def_state
= IN_IFDEF
;
720 fputs(pfetch(new), ofp
);
723 else if (pch_char(new) != pch_char(old
)) {
724 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
725 pch_hunk_beg() + old
,
726 pch_hunk_beg() + new);
728 say3("oldchar = '%c', newchar = '%c'\n",
729 pch_char(old
), pch_char(new));
733 else if (pch_char(new) == '!') {
734 copy_till(where
+ old
- 1);
736 fputs(not_defined
, ofp
);
737 def_state
= IN_IFNDEF
;
739 while (pch_char(old
) == '!') {
741 fputs(pfetch(old
), ofp
);
747 fputs(else_defined
, ofp
);
750 while (pch_char(new) == '!') {
751 fputs(pfetch(new), ofp
);
756 assert(pch_char(new) == ' ');
759 if (R_do_defines
&& def_state
!= OUTSIDE
) {
760 fputs(end_defined
, ofp
);
765 if (new <= pat_end
&& pch_char(new) == '+') {
766 copy_till(where
+ old
- 1);
768 if (def_state
== OUTSIDE
) {
769 fputs(if_defined
, ofp
);
770 def_state
= IN_IFDEF
;
772 else if (def_state
== IN_IFNDEF
) {
773 fputs(else_defined
, ofp
);
777 while (new <= pat_end
&& pch_char(new) == '+') {
778 fputs(pfetch(new), ofp
);
782 if (R_do_defines
&& def_state
!= OUTSIDE
) {
783 fputs(end_defined
, ofp
);
787 /* Open the new file. */
793 ofp
= fopen(name
, "w");
795 pfatal2("can't create %s", name
);
798 /* Open a file to put hunks we can't locate. */
804 rejfp
= fopen(name
, "w");
806 pfatal2("can't create %s", name
);
809 /* Copy input file to output, up to wherever hunk is to be applied. */
813 Reg1 LINENUM lastline
;
815 Reg2 LINENUM R_last_frozen_line
= last_frozen_line
;
817 if (R_last_frozen_line
> lastline
)
818 fatal1("misordered hunks! output would be garbled\n");
819 while (R_last_frozen_line
< lastline
) {
820 dump_line(++R_last_frozen_line
);
822 last_frozen_line
= R_last_frozen_line
;
825 /* Finish copying the input file to the output file. */
832 say3("il=%ld lfl=%ld\n",input_lines
,last_frozen_line
);
835 copy_till(input_lines
); /* dump remainder of file */
840 /* Copy one line from input to output. */
847 Reg2
char R_newline
= '\n';
849 /* Note: string is not null terminated. */
850 for (s
=ifetch(line
, 0); putc(*s
, ofp
) != R_newline
; s
++) ;
853 /* Does the patch pattern match at line base+offset? */
856 patch_match(base
, offset
, fuzz
)
861 Reg1 LINENUM pline
= 1 + fuzz
;
863 Reg3 LINENUM pat_lines
= pch_ptrn_lines() - fuzz
;
865 for (iline
=base
+offset
+fuzz
; pline
<= pat_lines
; pline
++,iline
++) {
867 if (!similar(ifetch(iline
, (offset
>= 0)),
869 pch_line_len(pline
) ))
872 else if (strnNE(ifetch(iline
, (offset
>= 0)),
874 pch_line_len(pline
) ))
880 /* Do two lines match with canonicalized white space? */
889 if (isspace(*b
)) { /* whitespace (or \n) to match? */
890 if (!isspace(*a
)) /* no corresponding whitespace? */
892 while (len
&& isspace(*b
) && *b
!= '\n')
893 b
++,len
--; /* skip pattern whitespace */
894 while (isspace(*a
) && *a
!= '\n')
895 a
++; /* skip target whitespace */
896 if (*a
== '\n' || *b
== '\n')
897 return (*a
== *b
); /* should end in sync */
899 else if (*a
++ != *b
++) /* match non-whitespace chars */
902 len
--; /* probably not necessary */
904 return TRUE
; /* actually, this is not reached */
905 /* since there is always a \n */
908 /* Exit with cleanup. */