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
101 #include "backupfile.h"
106 void reinitialize_almost_everything(void);
107 void get_some_switches(void);
108 LINENUM locate_hunk(LINENUM _fuzz);
109 void abort_hunk(void);
110 void apply_hunk(LINENUM _where);
111 void init_output(char *_name);
112 void init_reject(char *_name);
113 void copy_till(LINENUM _lastline);
114 void spew_output(void);
115 void dump_line(LINENUM _line);
116 bool patch_match(LINENUM _base, LINENUM _offset, LINENUM _fuzz);
117 bool similar(char *_a, char *_b, int _len);
118 void my_exit(int _status);
121 /* TRUE if -E was specified on command line. */
122 static int remove_empty_files = FALSE;
124 /* TRUE if -R was specified on command line. */
125 static int reverse_flag_specified = FALSE;
127 /* TRUE if -C was specified on command line. */
128 int check_patch = FALSE;
130 /* TRUE if -I was specified on command line */
131 /* or PATCH_INDEX_FIRST env. variable is set */
134 /* TRUE if -S was specified on command line. */
135 int skip_flag_specified = FALSE;
137 /* Apply a set of diffs as appropriate. */
154 setbuf(stderr, serrbuf);
155 for (i = 0; i<MAXFILEC; i++)
160 index_first = getenv ("PATCH_INDEX_FIRST") != 0;
162 /* Cons up the names of the temporary files. */
164 /* Directory for temporary files. */
168 tmpdir = getenv ("TMPDIR");
169 if (tmpdir == NULL) {
172 tmpname_len = strlen (tmpdir) + 20;
174 TMPOUTNAME = (char *) malloc (tmpname_len);
175 strcpy (TMPOUTNAME, tmpdir);
176 strcat (TMPOUTNAME, "/patchoXXXXXX");
179 TMPINNAME = (char *) malloc (tmpname_len);
180 strcpy (TMPINNAME, tmpdir);
181 strcat (TMPINNAME, "/patchiXXXXXX");
184 TMPREJNAME = (char *) malloc (tmpname_len);
185 strcpy (TMPREJNAME, tmpdir);
186 strcat (TMPREJNAME, "/patchrXXXXXX");
189 TMPPATNAME = (char *) malloc (tmpname_len);
190 strcpy (TMPPATNAME, tmpdir);
191 strcat (TMPPATNAME, "/patchpXXXXXX");
198 v = getenv ("SIMPLE_BACKUP_SUFFIX");
200 simple_backup_suffix = v;
202 simple_backup_suffix = ".orig";
204 v = getenv ("VERSION_CONTROL");
205 backup_type = get_version (v); /* OK to pass NULL. */
214 /* make sure we clean up /tmp in case of disaster */
218 open_patch_file(filearg[1]);
219 there_is_another_patch();
220 reinitialize_almost_everything()
221 ) { /* for each patch in patch file */
223 if (outname == Nullch)
224 outname = savestr(filearg[0]);
226 /* for ed script just up and do it and exit */
227 if (diff_type == ED_DIFF) {
232 /* initialize the patched file */
233 if (!skip_rest_of_patch)
234 init_output(TMPOUTNAME);
236 /* initialize reject file */
237 init_reject(TMPREJNAME);
239 /* find out where all the lines are */
240 if (!skip_rest_of_patch)
241 scan_input(filearg[0]);
243 /* from here on, open no standard i/o files, because malloc */
244 /* might misfire and we can't catch it easily */
246 /* apply each hunk of patch */
251 while (another_hunk()) {
254 mymaxfuzz = pch_context();
255 if (maxfuzz < mymaxfuzz)
257 if (!skip_rest_of_patch) {
259 where = locate_hunk(fuzz);
260 if (hunk == 1 && where == Nulline && !(force|rev_okayed)) {
261 /* dwim for reversed patch? */
265 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
269 where = locate_hunk(fuzz); /* try again */
270 if (where == Nulline) { /* didn't find it swapped */
271 if (!pch_swap()) /* put it back to normal */
272 fatal1("lost hunk on alloc error!\n");
275 else if (noreverse) {
276 if (!pch_swap()) /* put it back to normal */
277 fatal1("lost hunk on alloc error!\n");
280 "Ignoring previously applied (or reversed) patch.\n");
281 skip_rest_of_patch = TRUE;
286 "%seversed (or previously applied) patch detected! %s -R.",
287 reverse ? "R" : "Unr",
288 reverse ? "Assuming" : "Ignoring");
292 "%seversed (or previously applied) patch detected! %s -R? [y] ",
293 reverse ? "R" : "Unr",
294 reverse ? "Assume" : "Ignore");
296 (void) ask1("Apply anyway? [n] ");
300 skip_rest_of_patch = TRUE;
303 if (!pch_swap()) /* put it back to normal */
304 fatal1("lost hunk on alloc error!\n");
308 } while (!skip_rest_of_patch && where == Nulline &&
309 ++fuzz <= mymaxfuzz);
311 if (skip_rest_of_patch) { /* just got decided */
317 newwhere = pch_newfirst() + last_offset;
318 if (skip_rest_of_patch) {
320 if (! skip_flag_specified)
323 say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
325 else if (where == Nulline) {
329 say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
334 say3("Hunk #%d succeeded at %ld", hunk, newwhere);
336 say2(" with fuzz %ld", fuzz);
338 say3(" (offset %ld line%s)",
339 last_offset, last_offset==1L?"":"s");
345 if (out_of_mem && using_plan_a) {
346 optind = optind_last;
347 say1("\n\nRan out of memory using Plan A--trying again...\n\n");
359 /* finish spewing out the new file */
360 if (!skip_rest_of_patch)
363 /* and put the output where desired */
365 if (!skip_rest_of_patch) {
367 char *realout = outname;
371 } else if (move_file(TMPOUTNAME, outname) < 0) {
373 realout = TMPOUTNAME;
374 chmod(TMPOUTNAME, filemode);
377 chmod(outname, filemode);
379 if (remove_empty_files && stat(realout, &statbuf) == 0
380 && statbuf.st_size == 0) {
382 say2("Removing %s (empty after patching).\n", realout);
383 while (unlink(realout) >= 0) ; /* while is for Eunice. */
391 Strlcpy(rejname, outname, sizeof(rejname));
392 addext(rejname, ".rej", '#');
394 if (skip_rest_of_patch) {
395 say4("%d out of %d hunks ignored--saving rejects to %s\n",
396 failed, hunk, rejname);
399 say4("%d out of %d hunks failed--saving rejects to %s\n",
400 failed, hunk, rejname);
404 } else if (move_file(TMPREJNAME, rejname) < 0)
413 /* Prepare to find the next patch to do in the patch file. */
416 reinitialize_almost_everything(void)
422 last_frozen_line = 0;
425 if (filearg[0] != Nullch && !out_of_mem) {
430 if (outname != Nullch) {
439 if (revision != Nullch) {
444 reverse = reverse_flag_specified;
445 skip_rest_of_patch = FALSE;
446 skip_flag_specified = FALSE;
451 fatal1("you may not change to a different patch file\n");
454 static char *shortopts = "-b:B:cCd:D:eEfF:i:IlnNo:p::r:RsStuvV:x:";
455 static struct option longopts[] =
457 {"suffix", 1, NULL, 'b'},
458 {"prefix", 1, NULL, 'B'},
459 {"check", 0, NULL, 'C'},
460 {"context", 0, NULL, 'c'},
461 {"directory", 1, NULL, 'd'},
462 {"ifdef", 1, NULL, 'D'},
463 {"ed", 0, NULL, 'e'},
464 {"remove-empty-files", 0, NULL, 'E'},
465 {"force", 0, NULL, 'f'},
466 {"fuzz", 1, NULL, 'F'},
467 {"index-first", 0, NULL, 'I'},
468 {"ignore-whitespace", 0, NULL, 'l'},
469 {"normal", 0, NULL, 'n'},
470 {"forward", 0, NULL, 'N'},
471 {"output", 1, NULL, 'o'},
472 {"strip", 2, NULL, 'p'},
473 {"reject-file", 1, NULL, 'r'},
474 {"reverse", 0, NULL, 'R'},
475 {"quiet", 0, NULL, 's'},
476 {"silent", 0, NULL, 's'},
477 {"skip", 0, NULL, 'S'},
478 {"batch", 0, NULL, 't'},
479 {"unified", 0, NULL, 'u'},
480 {"version", 0, NULL, 'v'},
481 {"version-control", 1, NULL, 'V'},
482 {"debug", 1, NULL, 'x'},
486 /* Process switches and filenames up to next '+' or end of list. */
489 get_some_switches(void)
494 optind_last = optind;
497 while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
500 if (strEQ(optarg, "+"))
502 if (filec == MAXFILEC)
503 fatal1("too many file arguments\n");
504 filearg[filec++] = savestr(optarg);
509 simple_backup_suffix = savestr(optarg);
512 origprae = savestr(optarg);
515 diff_type = CONTEXT_DIFF;
521 if (chdir(optarg) < 0)
522 pfatal2("can't cd to %s", optarg);
526 if (!isalpha((unsigned char)*optarg) && '_' != *optarg)
527 fatal1("argument to -D is not an identifier\n");
528 Snprintf(if_defined, sizeof(if_defined), "#ifdef %s\n", optarg);
529 Snprintf(not_defined, sizeof(not_defined), "#ifndef %s\n", optarg);
530 Snprintf(end_defined, sizeof(end_defined), "#endif /* %s */\n", optarg);
536 remove_empty_files = TRUE;
542 maxfuzz = atoi(optarg);
545 filearg[1] = savestr(optarg);
554 diff_type = NORMAL_DIFF;
560 outname = savestr(optarg);
564 strippath = atoi(optarg);
569 Strlcpy(rejname, optarg, sizeof(rejname));
573 reverse_flag_specified = TRUE;
579 skip_rest_of_patch = TRUE;
580 skip_flag_specified = TRUE;
586 diff_type = UNI_DIFF;
593 backup_type = get_version (optarg);
598 debug = atoi(optarg);
603 Usage: %s [options] [origfile [patchfile]] [+ [options] [origfile]]...\n",
607 [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
608 [-D symbol] [-F max-fuzz] [-i patchfile] [-o out-file] [-p[strip-count]]\n\
609 [-r rej-name] [-V {numbered,existing,simple}] [--check] [--context]\n\
610 [--prefix=backup-prefix] [--suffix=backup-ext] [--ifdef=symbol]\n\
611 [--directory=directory] [--ed] [--fuzz=max-fuzz] [--force] [--batch]\n\
612 [--ignore-whitespace] [--forward] [--reverse] [--output=out-file]\n");
614 [--strip[=strip-count]] [--normal] [--reject-file=rej-name] [--skip]\n\
615 [--remove-empty-files] [--quiet] [--silent] [--unified] [--version]\n\
616 [--version-control={numbered,existing,simple}] [--index-first]\n");
622 /* Process any filename args given after "--". */
623 for (; optind < Argc; ++optind) {
624 if (filec == MAXFILEC)
625 fatal1("too many file arguments\n");
626 filearg[filec++] = savestr(Argv[optind]);
631 * Attempt to find the right place to apply this hunk of patch.
634 locate_hunk(LINENUM fuzz)
636 Reg1 LINENUM first_guess = pch_first() + last_offset;
638 LINENUM pat_lines = pch_ptrn_lines();
639 Reg3 LINENUM max_pos_offset = input_lines - first_guess
641 Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
644 if (!pat_lines) /* null range matches always */
646 if (max_neg_offset >= first_guess) /* do not try lines < 0 */
647 max_neg_offset = first_guess - 1;
648 if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
650 for (offset = 1; ; offset++) {
651 Reg5 bool check_after = (offset <= max_pos_offset);
652 Reg6 bool check_before = (offset <= max_neg_offset);
654 if (check_after && patch_match(first_guess, offset, fuzz)) {
657 say3("Offset changing from %ld to %ld\n", last_offset, offset);
659 last_offset = offset;
660 return first_guess+offset;
662 else if (check_before && patch_match(first_guess, -offset, fuzz)) {
665 say3("Offset changing from %ld to %ld\n", last_offset, -offset);
667 last_offset = -offset;
668 return first_guess-offset;
670 else if (!check_before && !check_after)
675 /* We did not find the pattern, dump out the hunk so they can handle it. */
681 Reg2 LINENUM pat_end = pch_end();
682 /* add in last_offset to guess the same as the previous successful hunk */
683 LINENUM oldfirst = pch_first() + last_offset;
684 LINENUM newfirst = pch_newfirst() + last_offset;
685 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
686 LINENUM newlast = newfirst + pch_repl_lines() - 1;
687 char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
688 char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
690 fprintf(rejfp, "***************\n");
691 for (i=0; i<=pat_end; i++) {
692 switch (pch_char(i)) {
694 if (oldlast < oldfirst)
695 fprintf(rejfp, "*** 0%s\n", stars);
696 else if (oldlast == oldfirst)
697 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
699 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
702 if (newlast < newfirst)
703 fprintf(rejfp, "--- 0%s\n", minuses);
704 else if (newlast == newfirst)
705 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
707 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
710 fprintf(rejfp, "%s", pfetch(i));
712 case ' ': case '-': case '+': case '!':
713 fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
716 fatal1("fatal internal error in abort_hunk\n");
722 * We found where to apply it (we hope), so do it.
725 apply_hunk(LINENUM where)
727 Reg1 LINENUM old = 1;
728 Reg2 LINENUM lastline = pch_ptrn_lines();
729 Reg3 LINENUM new = lastline+1;
734 Reg4 int def_state = OUTSIDE;
735 Reg5 bool R_do_defines = do_defines;
736 Reg6 LINENUM pat_end = pch_end();
739 while (pch_char(new) == '=' || pch_char(new) == '\n')
742 while (old <= lastline) {
743 if (pch_char(old) == '-') {
744 copy_till(where + old - 1);
746 if (def_state == OUTSIDE) {
747 fputs(not_defined, ofp);
748 def_state = IN_IFNDEF;
750 else if (def_state == IN_IFDEF) {
751 fputs(else_defined, ofp);
754 fputs(pfetch(old), ofp);
759 else if (new > pat_end) {
762 else if (pch_char(new) == '+') {
763 copy_till(where + old - 1);
765 if (def_state == IN_IFNDEF) {
766 fputs(else_defined, ofp);
769 else if (def_state == OUTSIDE) {
770 fputs(if_defined, ofp);
771 def_state = IN_IFDEF;
774 fputs(pfetch(new), ofp);
777 else if (pch_char(new) != pch_char(old)) {
778 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
779 pch_hunk_beg() + old,
780 pch_hunk_beg() + new);
782 say3("oldchar = '%c', newchar = '%c'\n",
783 pch_char(old), pch_char(new));
787 else if (pch_char(new) == '!') {
788 copy_till(where + old - 1);
790 fputs(not_defined, ofp);
791 def_state = IN_IFNDEF;
793 while (pch_char(old) == '!') {
795 fputs(pfetch(old), ofp);
801 fputs(else_defined, ofp);
804 while (pch_char(new) == '!') {
805 fputs(pfetch(new), ofp);
810 assert(pch_char(new) == ' ');
813 if (R_do_defines && def_state != OUTSIDE) {
814 fputs(end_defined, ofp);
819 if (new <= pat_end && pch_char(new) == '+') {
820 copy_till(where + old - 1);
822 if (def_state == OUTSIDE) {
823 fputs(if_defined, ofp);
824 def_state = IN_IFDEF;
826 else if (def_state == IN_IFNDEF) {
827 fputs(else_defined, ofp);
831 while (new <= pat_end && pch_char(new) == '+') {
832 fputs(pfetch(new), ofp);
836 if (R_do_defines && def_state != OUTSIDE) {
837 fputs(end_defined, ofp);
841 /* Open the new file. */
844 init_output(char *name)
846 ofp = fopen(name, "w");
848 pfatal2("can't create %s", name);
851 /* Open a file to put hunks we can't locate. */
854 init_reject(char *name)
856 rejfp = fopen(name, "w");
858 pfatal2("can't create %s", name);
861 /* Copy input file to output, up to wherever hunk is to be applied. */
864 copy_till(LINENUM lastline)
866 Reg2 LINENUM R_last_frozen_line = last_frozen_line;
868 if (R_last_frozen_line > lastline)
869 fatal1("misordered hunks! output would be garbled\n");
870 while (R_last_frozen_line < lastline) {
871 dump_line(++R_last_frozen_line);
873 last_frozen_line = R_last_frozen_line;
876 /* Finish copying the input file to the output file. */
883 say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
886 copy_till(input_lines); /* dump remainder of file */
891 /* Copy one line from input to output. */
894 dump_line(LINENUM line)
897 Reg2 char R_newline = '\n';
899 /* Note: string is not null terminated. */
900 for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
903 /* Does the patch pattern match at line base+offset? */
906 patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
908 Reg1 LINENUM pline = 1 + fuzz;
910 Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
912 for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
914 if (!similar(ifetch(iline, (offset >= 0)),
916 pch_line_len(pline) ))
919 else if (strnNE(ifetch(iline, (offset >= 0)),
921 pch_line_len(pline) ))
927 /* Do two lines match with canonicalized white space? */
930 similar(char *a, char *b, int len)
933 if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */
934 if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */
936 while (len && isspace((unsigned char)*b) && *b != '\n')
937 b++,len--; /* skip pattern whitespace */
938 while (isspace((unsigned char)*a) && *a != '\n')
939 a++; /* skip target whitespace */
940 if (*a == '\n' || *b == '\n')
941 return (*a == *b); /* should end in sync */
943 else if (*a++ != *b++) /* match non-whitespace chars */
946 len--; /* probably not necessary */
948 return TRUE; /* actually, this is not reached */
949 /* since there is always a \n */
952 /* Exit with cleanup. */