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. */