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++)
158 buf_size = INITLINELEN;
159 buf = malloc((MEM)(buf_size));
161 fatal1("out of memory\n");
165 index_first = getenv ("PATCH_INDEX_FIRST") != 0;
167 /* Cons up the names of the temporary files. */
169 /* Directory for temporary files. */
173 tmpdir = getenv ("TMPDIR");
174 if (tmpdir == NULL) {
177 tmpname_len = strlen (tmpdir) + 20;
179 TMPOUTNAME = (char *) malloc (tmpname_len);
180 strcpy (TMPOUTNAME, tmpdir);
181 strcat (TMPOUTNAME, "/patchoXXXXXX");
184 TMPINNAME = (char *) malloc (tmpname_len);
185 strcpy (TMPINNAME, tmpdir);
186 strcat (TMPINNAME, "/patchiXXXXXX");
189 TMPREJNAME = (char *) malloc (tmpname_len);
190 strcpy (TMPREJNAME, tmpdir);
191 strcat (TMPREJNAME, "/patchrXXXXXX");
194 TMPPATNAME = (char *) malloc (tmpname_len);
195 strcpy (TMPPATNAME, tmpdir);
196 strcat (TMPPATNAME, "/patchpXXXXXX");
203 v = getenv ("SIMPLE_BACKUP_SUFFIX");
205 simple_backup_suffix = v;
207 simple_backup_suffix = ".orig";
209 v = getenv ("VERSION_CONTROL");
210 backup_type = get_version (v); /* OK to pass NULL. */
219 /* make sure we clean up /tmp in case of disaster */
223 open_patch_file(filearg[1]);
224 there_is_another_patch();
225 reinitialize_almost_everything()
226 ) { /* for each patch in patch file */
228 if (outname == Nullch)
229 outname = savestr(filearg[0]);
231 /* for ed script just up and do it and exit */
232 if (diff_type == ED_DIFF) {
237 /* initialize the patched file */
238 if (!skip_rest_of_patch)
239 init_output(TMPOUTNAME);
241 /* initialize reject file */
242 init_reject(TMPREJNAME);
244 /* find out where all the lines are */
245 if (!skip_rest_of_patch)
246 scan_input(filearg[0]);
248 /* from here on, open no standard i/o files, because malloc */
249 /* might misfire and we can't catch it easily */
251 /* apply each hunk of patch */
256 while (another_hunk()) {
259 mymaxfuzz = pch_context();
260 if (maxfuzz < mymaxfuzz)
262 if (!skip_rest_of_patch) {
264 where = locate_hunk(fuzz);
265 if (hunk == 1 && where == Nulline && !(force|rev_okayed)) {
266 /* dwim for reversed patch? */
270 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
274 where = locate_hunk(fuzz); /* try again */
275 if (where == Nulline) { /* didn't find it swapped */
276 if (!pch_swap()) /* put it back to normal */
277 fatal1("lost hunk on alloc error!\n");
280 else if (noreverse) {
281 if (!pch_swap()) /* put it back to normal */
282 fatal1("lost hunk on alloc error!\n");
285 "Ignoring previously applied (or reversed) patch.\n");
286 skip_rest_of_patch = TRUE;
291 "%seversed (or previously applied) patch detected! %s -R.",
292 reverse ? "R" : "Unr",
293 reverse ? "Assuming" : "Ignoring");
297 "%seversed (or previously applied) patch detected! %s -R? [y] ",
298 reverse ? "R" : "Unr",
299 reverse ? "Assume" : "Ignore");
301 (void) ask1("Apply anyway? [n] ");
305 skip_rest_of_patch = TRUE;
308 if (!pch_swap()) /* put it back to normal */
309 fatal1("lost hunk on alloc error!\n");
313 } while (!skip_rest_of_patch && where == Nulline &&
314 ++fuzz <= mymaxfuzz);
316 if (skip_rest_of_patch) { /* just got decided */
322 newwhere = pch_newfirst() + last_offset;
323 if (skip_rest_of_patch) {
325 if (! skip_flag_specified)
328 say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
330 else if (where == Nulline) {
334 say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
339 say3("Hunk #%d succeeded at %ld", hunk, newwhere);
341 say2(" with fuzz %ld", fuzz);
343 say3(" (offset %ld line%s)",
344 last_offset, last_offset==1L?"":"s");
350 if (out_of_mem && using_plan_a) {
351 optind = optind_last;
352 say1("\n\nRan out of memory using Plan A--trying again...\n\n");
364 /* finish spewing out the new file */
365 if (!skip_rest_of_patch)
368 /* and put the output where desired */
370 if (!skip_rest_of_patch) {
372 char *realout = outname;
376 } else if (move_file(TMPOUTNAME, outname) < 0) {
378 realout = TMPOUTNAME;
379 chmod(TMPOUTNAME, filemode);
382 chmod(outname, filemode);
384 if (remove_empty_files && stat(realout, &statbuf) == 0
385 && statbuf.st_size == 0) {
387 say2("Removing %s (empty after patching).\n", realout);
388 while (unlink(realout) >= 0) ; /* while is for Eunice. */
396 Strlcpy(rejname, outname, sizeof(rejname));
397 addext(rejname, ".rej", '#');
399 if (skip_rest_of_patch) {
400 say4("%d out of %d hunks ignored--saving rejects to %s\n",
401 failed, hunk, rejname);
404 say4("%d out of %d hunks failed--saving rejects to %s\n",
405 failed, hunk, rejname);
409 } else if (move_file(TMPREJNAME, rejname) < 0)
418 /* Prepare to find the next patch to do in the patch file. */
421 reinitialize_almost_everything(void)
427 last_frozen_line = 0;
430 if (filearg[0] != Nullch && !out_of_mem) {
435 if (outname != Nullch) {
444 if (revision != Nullch) {
449 reverse = reverse_flag_specified;
450 skip_rest_of_patch = FALSE;
451 skip_flag_specified = FALSE;
456 fatal1("you may not change to a different patch file\n");
459 static char *shortopts = "-b:B:cCd:D:eEfF:i:IlnNo:p::r:RsStuvV:x:";
460 static struct option longopts[] =
462 {"suffix", 1, NULL, 'b'},
463 {"prefix", 1, NULL, 'B'},
464 {"check", 0, NULL, 'C'},
465 {"context", 0, NULL, 'c'},
466 {"directory", 1, NULL, 'd'},
467 {"ifdef", 1, NULL, 'D'},
468 {"ed", 0, NULL, 'e'},
469 {"remove-empty-files", 0, NULL, 'E'},
470 {"force", 0, NULL, 'f'},
471 {"fuzz", 1, NULL, 'F'},
472 {"index-first", 0, NULL, 'I'},
473 {"ignore-whitespace", 0, NULL, 'l'},
474 {"normal", 0, NULL, 'n'},
475 {"forward", 0, NULL, 'N'},
476 {"output", 1, NULL, 'o'},
477 {"strip", 2, NULL, 'p'},
478 {"reject-file", 1, NULL, 'r'},
479 {"reverse", 0, NULL, 'R'},
480 {"quiet", 0, NULL, 's'},
481 {"silent", 0, NULL, 's'},
482 {"skip", 0, NULL, 'S'},
483 {"batch", 0, NULL, 't'},
484 {"unified", 0, NULL, 'u'},
485 {"version", 0, NULL, 'v'},
486 {"version-control", 1, NULL, 'V'},
487 {"debug", 1, NULL, 'x'},
491 /* Process switches and filenames up to next '+' or end of list. */
494 get_some_switches(void)
499 optind_last = optind;
502 while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
505 if (strEQ(optarg, "+"))
507 if (filec == MAXFILEC)
508 fatal1("too many file arguments\n");
509 filearg[filec++] = savestr(optarg);
514 simple_backup_suffix = savestr(optarg);
517 origprae = savestr(optarg);
520 diff_type = CONTEXT_DIFF;
526 if (chdir(optarg) < 0)
527 pfatal2("can't cd to %s", optarg);
531 if (!isalpha((unsigned char)*optarg) && '_' != *optarg)
532 fatal1("argument to -D is not an identifier\n");
533 Snprintf(if_defined, sizeof(if_defined), "#ifdef %s\n", optarg);
534 Snprintf(not_defined, sizeof(not_defined), "#ifndef %s\n", optarg);
535 Snprintf(end_defined, sizeof(end_defined), "#endif /* %s */\n", optarg);
541 remove_empty_files = TRUE;
547 maxfuzz = atoi(optarg);
550 filearg[1] = savestr(optarg);
559 diff_type = NORMAL_DIFF;
565 outname = savestr(optarg);
569 strippath = atoi(optarg);
574 Strlcpy(rejname, optarg, sizeof(rejname));
578 reverse_flag_specified = TRUE;
584 skip_rest_of_patch = TRUE;
585 skip_flag_specified = TRUE;
591 diff_type = UNI_DIFF;
598 backup_type = get_version (optarg);
603 debug = atoi(optarg);
608 Usage: %s [options] [origfile [patchfile]] [+ [options] [origfile]]...\n",
612 [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
613 [-D symbol] [-F max-fuzz] [-i patchfile] [-o out-file] [-p[strip-count]]\n\
614 [-r rej-name] [-V {numbered,existing,simple}] [--check] [--context]\n\
615 [--prefix=backup-prefix] [--suffix=backup-ext] [--ifdef=symbol]\n\
616 [--directory=directory] [--ed] [--fuzz=max-fuzz] [--force] [--batch]\n\
617 [--ignore-whitespace] [--forward] [--reverse] [--output=out-file]\n");
619 [--strip[=strip-count]] [--normal] [--reject-file=rej-name] [--skip]\n\
620 [--remove-empty-files] [--quiet] [--silent] [--unified] [--version]\n\
621 [--version-control={numbered,existing,simple}] [--index-first]\n");
627 /* Process any filename args given after "--". */
628 for (; optind < Argc; ++optind) {
629 if (filec == MAXFILEC)
630 fatal1("too many file arguments\n");
631 filearg[filec++] = savestr(Argv[optind]);
636 * Attempt to find the right place to apply this hunk of patch.
639 locate_hunk(LINENUM fuzz)
641 Reg1 LINENUM first_guess = pch_first() + last_offset;
643 LINENUM pat_lines = pch_ptrn_lines();
644 Reg3 LINENUM max_pos_offset = input_lines - first_guess
646 Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
649 if (!pat_lines) /* null range matches always */
651 if (max_neg_offset >= first_guess) /* do not try lines < 0 */
652 max_neg_offset = first_guess - 1;
653 if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
655 for (offset = 1; ; offset++) {
656 Reg5 bool check_after = (offset <= max_pos_offset);
657 Reg6 bool check_before = (offset <= max_neg_offset);
659 if (check_after && patch_match(first_guess, offset, fuzz)) {
662 say3("Offset changing from %ld to %ld\n", last_offset, offset);
664 last_offset = offset;
665 return first_guess+offset;
667 else if (check_before && patch_match(first_guess, -offset, fuzz)) {
670 say3("Offset changing from %ld to %ld\n", last_offset, -offset);
672 last_offset = -offset;
673 return first_guess-offset;
675 else if (!check_before && !check_after)
680 /* We did not find the pattern, dump out the hunk so they can handle it. */
686 Reg2 LINENUM pat_end = pch_end();
687 /* add in last_offset to guess the same as the previous successful hunk */
688 LINENUM oldfirst = pch_first() + last_offset;
689 LINENUM newfirst = pch_newfirst() + last_offset;
690 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
691 LINENUM newlast = newfirst + pch_repl_lines() - 1;
692 char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
693 char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
695 fprintf(rejfp, "***************\n");
696 for (i=0; i<=pat_end; i++) {
697 switch (pch_char(i)) {
699 if (oldlast < oldfirst)
700 fprintf(rejfp, "*** 0%s\n", stars);
701 else if (oldlast == oldfirst)
702 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
704 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
707 if (newlast < newfirst)
708 fprintf(rejfp, "--- 0%s\n", minuses);
709 else if (newlast == newfirst)
710 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
712 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
715 fprintf(rejfp, "%s", pfetch(i));
717 case ' ': case '-': case '+': case '!':
718 fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
721 fatal1("fatal internal error in abort_hunk\n");
727 * We found where to apply it (we hope), so do it.
730 apply_hunk(LINENUM where)
732 Reg1 LINENUM old = 1;
733 Reg2 LINENUM lastline = pch_ptrn_lines();
734 Reg3 LINENUM new = lastline+1;
739 Reg4 int def_state = OUTSIDE;
740 Reg5 bool R_do_defines = do_defines;
741 Reg6 LINENUM pat_end = pch_end();
744 while (pch_char(new) == '=' || pch_char(new) == '\n')
747 while (old <= lastline) {
748 if (pch_char(old) == '-') {
749 copy_till(where + old - 1);
751 if (def_state == OUTSIDE) {
752 fputs(not_defined, ofp);
753 def_state = IN_IFNDEF;
755 else if (def_state == IN_IFDEF) {
756 fputs(else_defined, ofp);
759 fputs(pfetch(old), ofp);
764 else if (new > pat_end) {
767 else if (pch_char(new) == '+') {
768 copy_till(where + old - 1);
770 if (def_state == IN_IFNDEF) {
771 fputs(else_defined, ofp);
774 else if (def_state == OUTSIDE) {
775 fputs(if_defined, ofp);
776 def_state = IN_IFDEF;
779 fputs(pfetch(new), ofp);
782 else if (pch_char(new) != pch_char(old)) {
783 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
784 pch_hunk_beg() + old,
785 pch_hunk_beg() + new);
787 say3("oldchar = '%c', newchar = '%c'\n",
788 pch_char(old), pch_char(new));
792 else if (pch_char(new) == '!') {
793 copy_till(where + old - 1);
795 fputs(not_defined, ofp);
796 def_state = IN_IFNDEF;
798 while (pch_char(old) == '!') {
800 fputs(pfetch(old), ofp);
806 fputs(else_defined, ofp);
809 while (pch_char(new) == '!') {
810 fputs(pfetch(new), ofp);
815 assert(pch_char(new) == ' ');
818 if (R_do_defines && def_state != OUTSIDE) {
819 fputs(end_defined, ofp);
824 if (new <= pat_end && pch_char(new) == '+') {
825 copy_till(where + old - 1);
827 if (def_state == OUTSIDE) {
828 fputs(if_defined, ofp);
829 def_state = IN_IFDEF;
831 else if (def_state == IN_IFNDEF) {
832 fputs(else_defined, ofp);
836 while (new <= pat_end && pch_char(new) == '+') {
837 fputs(pfetch(new), ofp);
841 if (R_do_defines && def_state != OUTSIDE) {
842 fputs(end_defined, ofp);
846 /* Open the new file. */
849 init_output(char *name)
851 ofp = fopen(name, "w");
853 pfatal2("can't create %s", name);
856 /* Open a file to put hunks we can't locate. */
859 init_reject(char *name)
861 rejfp = fopen(name, "w");
863 pfatal2("can't create %s", name);
866 /* Copy input file to output, up to wherever hunk is to be applied. */
869 copy_till(LINENUM lastline)
871 Reg2 LINENUM R_last_frozen_line = last_frozen_line;
873 if (R_last_frozen_line > lastline)
874 fatal1("misordered hunks! output would be garbled\n");
875 while (R_last_frozen_line < lastline) {
876 dump_line(++R_last_frozen_line);
878 last_frozen_line = R_last_frozen_line;
881 /* Finish copying the input file to the output file. */
888 say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
891 copy_till(input_lines); /* dump remainder of file */
896 /* Copy one line from input to output. */
899 dump_line(LINENUM line)
902 Reg2 char R_newline = '\n';
904 /* Note: string is not null terminated. */
905 for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
908 /* Does the patch pattern match at line base+offset? */
911 patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
913 Reg1 LINENUM pline = 1 + fuzz;
915 Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
917 for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
919 if (!similar(ifetch(iline, (offset >= 0)),
921 pch_line_len(pline) ))
924 else if (strnNE(ifetch(iline, (offset >= 0)),
926 pch_line_len(pline) ))
932 /* Do two lines match with canonicalized white space? */
935 similar(char *a, char *b, int len)
938 if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */
939 if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */
941 while (len && isspace((unsigned char)*b) && *b != '\n')
942 b++,len--; /* skip pattern whitespace */
943 while (isspace((unsigned char)*a) && *a != '\n')
944 a++; /* skip target whitespace */
945 if (*a == '\n' || *b == '\n')
946 return (*a == *b); /* should end in sync */
948 else if (*a++ != *b++) /* match non-whitespace chars */
951 len--; /* probably not necessary */
953 return TRUE; /* actually, this is not reached */
954 /* since there is always a \n */
957 /* Exit with cleanup. */