1 /* patch - a program to apply diffs to original files */
3 /* $Id: patch.c,v 1.23 1997/07/05 10:32:23 eggert Exp $ */
6 Copyright 1984, 1985, 1986, 1987, 1988 Larry Wall
7 Copyright 1989, 1990, 1991, 1992, 1993, 1997 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING.
21 If not, write to the Free Software Foundation,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #include <backupfile.h>
40 /* Some nonstandard hosts don't declare this structure even in <utime.h>. */
41 #if ! HAVE_STRUCT_UTIMBUF
49 /* Output stream state. */
59 static FILE *create_output_file PARAMS ((char const *));
60 static LINENUM locate_hunk PARAMS ((LINENUM));
61 static bool apply_hunk PARAMS ((struct outstate *, LINENUM));
62 static bool copy_till PARAMS ((struct outstate *, LINENUM));
63 static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM));
64 static bool similar PARAMS ((char const *, size_t, char const *, size_t));
65 static bool spew_output PARAMS ((struct outstate *));
66 static char const *make_temp PARAMS ((int));
67 static int numeric_string PARAMS ((char const *, int, char const *));
68 static void abort_hunk PARAMS ((void));
69 static void cleanup PARAMS ((void));
70 static void get_some_switches PARAMS ((void));
71 static void init_output PARAMS ((char const *, struct outstate *));
72 static void init_reject PARAMS ((char const *));
73 static void reinitialize_almost_everything PARAMS ((void));
74 static void usage PARAMS ((FILE *, int)) __attribute__((noreturn));
76 static int make_backups;
77 static int backup_if_mismatch;
78 static char const *version_control;
79 static int remove_empty_files;
81 /* TRUE if -R was specified on command line. */
82 static int reverse_flag_specified;
84 /* how many input lines have been irretractably output */
85 static LINENUM last_frozen_line;
87 static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */
88 static char const if_defined[] = "\n#ifdef %s\n";
89 static char const not_defined[] = "#ifndef %s\n";
90 static char const else_defined[] = "\n#else\n";
91 static char const end_defined[] = "\n#endif /* %s */\n";
94 static char * const *Argv;
96 static FILE *rejfp; /* reject file pointer */
98 static char const *patchname;
100 static char const * volatile TMPREJNAME;
102 static LINENUM last_offset;
103 static LINENUM maxfuzz = 2;
105 static char serrbuf[BUFSIZ];
107 char const program_name[] = "patch";
109 /* Apply a set of diffs as appropriate. */
111 int main PARAMS ((int, char **));
119 bool somefailed = FALSE;
120 struct outstate outstate;
124 setbuf(stderr, serrbuf);
127 buf = xmalloc (bufsize);
131 posixly_correct = getenv ("POSIXLY_CORRECT") != 0;
132 backup_if_mismatch = ! posixly_correct;
133 patch_get = ((val = getenv ("PATCH_GET"))
134 ? numeric_string (val, 1, "PATCH_GET value")
135 : posixly_correct - 1);
138 char const *v = getenv ("SIMPLE_BACKUP_SUFFIX");
140 simple_backup_suffix = v;
143 version_control = getenv ("PATCH_VERSION_CONTROL");
144 if (! version_control)
145 version_control = getenv ("VERSION_CONTROL");
147 /* Cons up the names of the global temporary files.
148 Do this before `cleanup' can possibly be called (e.g. by `pfatal'). */
149 TMPOUTNAME = make_temp ('o');
150 TMPINNAME = make_temp ('i');
151 TMPREJNAME = make_temp ('r');
152 TMPPATNAME = make_temp ('p');
159 if (make_backups | backup_if_mismatch)
160 backup_type = get_version (version_control);
162 init_output (outfile, &outstate);
164 /* Make sure we clean up in case of disaster. */
168 open_patch_file (patchname);
169 there_is_another_patch();
170 reinitialize_almost_everything()
171 ) { /* for each patch in patch file */
175 char *outname = outfile ? outfile : inname;
177 if (!skip_rest_of_patch)
178 get_input_file (inname, outname);
180 if (diff_type == ED_DIFF) {
181 outstate.zero_output = 0;
184 do_ed_script (outstate.ofp);
188 if (stat (TMPOUTNAME, &statbuf) != 0)
189 pfatal ("%s", TMPOUTNAME);
190 outstate.zero_output = statbuf.st_size == 0;
195 int apply_anyway = 0;
197 /* initialize the patched file */
198 if (! skip_rest_of_patch && ! outfile)
199 init_output (TMPOUTNAME, &outstate);
201 /* initialize reject file */
202 init_reject(TMPREJNAME);
204 /* find out where all the lines are */
205 if (!skip_rest_of_patch)
208 /* from here on, open no standard i/o files, because malloc */
209 /* might misfire and we can't catch it easily */
211 /* apply each hunk of patch */
212 while (0 < (got_hunk = another_hunk (diff_type, reverse))) {
213 LINENUM where = 0; /* Pacify `gcc -Wall'. */
216 LINENUM prefix_context = pch_prefix_context ();
217 LINENUM suffix_context = pch_suffix_context ();
218 LINENUM context = (prefix_context < suffix_context
219 ? suffix_context : prefix_context);
220 LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context);
222 if (!skip_rest_of_patch) {
224 where = locate_hunk(fuzz);
225 if (! where || fuzz || last_offset)
227 if (hunk == 1 && ! where && ! (force | apply_anyway)
228 && reverse == reverse_flag_specified) {
229 /* dwim for reversed patch? */
232 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
236 where = locate_hunk (fuzz);
239 ("%s patch detected!",
242 : "Reversed (or previously applied)"))))
246 /* Put it back to normal. */
248 fatal ("lost hunk on alloc error!");
252 fuzz--; /* Undo `++fuzz' below. */
257 } while (!skip_rest_of_patch && !where
258 && ++fuzz <= mymaxfuzz);
260 if (skip_rest_of_patch) { /* just got decided */
261 if (outstate.ofp && ! outfile)
263 fclose (outstate.ofp);
269 newwhere = pch_newfirst() + last_offset;
270 if (skip_rest_of_patch) {
273 if (verbosity == VERBOSE)
274 say ("Hunk #%d ignored at %ld.\n", hunk, newwhere);
277 || (where == 1 && pch_says_nonexistent (reverse)
278 && instat.st_size)) {
280 say ("Patch attempted to create file `%s', which already exists.\n", inname);
283 if (verbosity != SILENT)
284 say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
286 else if (! apply_hunk (&outstate, where)) {
289 if (verbosity != SILENT)
290 say ("Hunk #%d FAILED at %ld.\n", hunk, newwhere);
292 if (verbosity == VERBOSE
293 || (verbosity != SILENT && (fuzz || last_offset))) {
294 say ("Hunk #%d succeeded at %ld", hunk, newwhere);
296 say (" with fuzz %ld", fuzz);
298 say (" (offset %ld line%s)",
299 last_offset, last_offset==1?"":"s");
305 if (got_hunk < 0 && using_plan_a) {
307 fatal ("out of memory using Plan A");
308 say ("\n\nRan out of memory using Plan A -- trying again...\n\n");
311 fclose (outstate.ofp);
318 /* finish spewing out the new file */
319 if (!skip_rest_of_patch)
322 if (! spew_output (&outstate))
324 say ("Skipping patch.\n");
325 skip_rest_of_patch = TRUE;
330 /* and put the output where desired */
332 if (! skip_rest_of_patch && ! outfile) {
333 if (outstate.zero_output
334 && (remove_empty_files
335 || (pch_says_nonexistent (reverse ^ 1) == 2
336 && ! posixly_correct)))
338 if (verbosity == VERBOSE)
339 say ("Removing file `%s'%s.\n", outname,
340 dry_run ? " and any empty ancestor directories" : "");
343 move_file ((char *) 0, outname, (mode_t) 0,
345 || (backup_if_mismatch && (mismatch | failed))));
346 removedirs (outname);
351 if (! outstate.zero_output
352 && pch_says_nonexistent (reverse ^ 1))
355 if (verbosity != SILENT)
356 say ("File `%s' is not empty after patch, as expected.\n",
364 move_file (TMPOUTNAME, outname, instat.st_mode,
366 || (backup_if_mismatch && (mismatch | failed))));
368 if ((set_time | set_utc)
369 && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1)
371 struct utimbuf utimbuf;
372 utimbuf.actime = utimbuf.modtime = t;
374 if (! force && ! inerrno
375 && ! pch_says_nonexistent (reverse)
376 && (t = pch_timestamp (reverse)) != (time_t) -1
377 && t != instat.st_mtime)
378 say ("not setting time of file `%s' (time mismatch)\n",
380 else if (! force && (mismatch | failed))
381 say ("not setting time of file `%s' (contents mismatch)\n",
383 else if (utime (outname, &utimbuf) != 0)
384 pfatal ("can't set timestamp on file `%s'", outname);
387 if (! inerrno && chmod (outname, instat.st_mode) != 0)
388 pfatal ("can't set permissions on file `%s'", outname);
392 if (diff_type != ED_DIFF) {
393 if (fclose (rejfp) != 0)
397 say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1),
398 skip_rest_of_patch ? "ignored" : "FAILED");
402 rej = xmalloc (strlen (outname) + 5);
403 strcpy (rej, outname);
404 addext (rej, ".rej", '#');
406 say (" -- saving rejects to %s", rej);
409 move_file (TMPREJNAME, rej, instat.st_mode, FALSE);
411 && (chmod (rej, (instat.st_mode
412 & ~(S_IXUSR|S_IXGRP|S_IXOTH)))
414 pfatal ("can't set permissions on file `%s'", rej);
424 if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
432 /* Prepare to find the next patch to do in the patch file. */
435 reinitialize_almost_everything()
441 last_frozen_line = 0;
457 reverse = reverse_flag_specified;
458 skip_rest_of_patch = FALSE;
461 static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z";
462 static struct option const longopts[] =
464 {"backup", no_argument, NULL, 'b'},
465 {"prefix", required_argument, NULL, 'B'},
466 {"context", no_argument, NULL, 'c'},
467 {"directory", required_argument, NULL, 'd'},
468 {"ifdef", required_argument, NULL, 'D'},
469 {"ed", no_argument, NULL, 'e'},
470 {"remove-empty-files", no_argument, NULL, 'E'},
471 {"force", no_argument, NULL, 'f'},
472 {"fuzz", required_argument, NULL, 'F'},
473 {"get", no_argument, NULL, 'g'},
474 {"input", required_argument, NULL, 'i'},
475 {"ignore-whitespace", no_argument, NULL, 'l'},
476 {"normal", no_argument, NULL, 'n'},
477 {"forward", no_argument, NULL, 'N'},
478 {"output", required_argument, NULL, 'o'},
479 {"strip", required_argument, NULL, 'p'},
480 {"reject-file", required_argument, NULL, 'r'},
481 {"reverse", no_argument, NULL, 'R'},
482 {"quiet", no_argument, NULL, 's'},
483 {"silent", no_argument, NULL, 's'},
484 {"batch", no_argument, NULL, 't'},
485 {"set-time", no_argument, NULL, 'T'},
486 {"unified", no_argument, NULL, 'u'},
487 {"version", no_argument, NULL, 'v'},
488 {"version-control", required_argument, NULL, 'V'},
489 {"debug", required_argument, NULL, 'x'},
490 {"basename-prefix", required_argument, NULL, 'Y'},
491 {"suffix", required_argument, NULL, 'z'},
492 {"set-utc", no_argument, NULL, 'Z'},
493 {"dry-run", no_argument, NULL, 129},
494 {"verbose", no_argument, NULL, 130},
495 {"binary", no_argument, NULL, 131},
496 {"help", no_argument, NULL, 132},
497 {"backup-if-mismatch", no_argument, NULL, 133},
498 {"no-backup-if-mismatch", no_argument, NULL, 134},
499 {NULL, no_argument, NULL, 0}
502 static char const *const option_help[] =
506 " -p NUM --strip=NUM Strip NUM leading components from file names.",
507 " -F LINES --fuzz LINES Set the fuzz factor to LINES for inexact matching.",
508 " -l --ignore-whitespace Ignore white space changes between patch and input.",
510 " -c --context Interpret the patch as a context difference.",
511 " -e --ed Interpret the patch as an ed script.",
512 " -n --normal Interpret the patch as a normal difference.",
513 " -u --unified Interpret the patch as a unified difference.",
515 " -N --forward Ignore patches that appear to be reversed or already applied.",
516 " -R --reverse Assume patches were created with old and new files swapped.",
518 " -i PATCHFILE --input=PATCHFILE Read patch from PATCHFILE instead of stdin.",
522 " -o FILE --output=FILE Output patched files to FILE.",
523 " -r FILE --reject-file=FILE Output rejects to FILE.",
525 " -D NAME --ifdef=NAME Make merged if-then-else output using NAME.",
526 " -E --remove-empty-files Remove output files that are empty after patching.",
528 " -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).",
529 " -T --set-time Likewise, assuming local time.",
531 "Backup and version control options:",
533 " -b --backup Back up the original contents of each file.",
534 " --backup-if-mismatch Back up if the patch does not match exactly.",
535 " --no-backup-if-mismatch Back up mismatches only if otherwise requested.",
537 " -V STYLE --version-control=STYLE Use STYLE version control.",
538 " STYLE is either 'simple', 'numbered', or 'existing'.",
539 " -B PREFIX --prefix=PREFIX Prepend PREFIX to backup file names.",
540 " -Y PREFIX --basename-prefix=PREFIX Prepend PREFIX to backup file basenames.",
541 " -z SUFFIX --suffix=SUFFIX Append SUFFIX to backup file names.",
543 " -g NUM --get=NUM Get files from RCS or SCCS if positive; ask if negative.",
545 "Miscellaneous options:",
547 " -t --batch Ask no questions; skip bad-Prereq patches; assume reversed.",
548 " -f --force Like -t, but ignore bad-Prereq patches, and assume unreversed.",
549 " -s --quiet --silent Work silently unless an error occurs.",
550 " --verbose Output extra information about the work being done.",
551 " --dry-run Do not actually change any files; just print what would happen.",
553 " -d DIR --directory=DIR Change the working directory to DIR first.",
555 " --binary Read and write data in binary mode.",
557 " --binary Read and write data in binary mode (no effect on this platform).",
560 " -v --version Output version info.",
561 " --help Output this help.",
563 "Report bugs to <bug-gnu-utils@prep.ai.mit.edu>.",
568 usage (stream, status)
572 char const * const *p;
576 fprintf (stream, "%s: Try `%s --help' for more information.\n",
577 program_name, Argv[0]);
581 fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n",
583 for (p = option_help; *p; p++)
584 fprintf (stream, "%s\n", *p);
590 /* Process switches and filenames. */
602 while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
607 /* Special hack for backward compatibility with CVS 1.9.
608 If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE',
609 treat `-b' as if it were `-b -z'. */
610 if (Argc - optind == 3
611 && strcmp (Argv[optind - 1], "-b") == 0
612 && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1])
613 && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1])
614 && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1]))
616 optarg = Argv[optind++];
617 if (verbosity != SILENT)
618 say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n",
625 fatal ("backup prefix is empty");
626 origprae = savestr (optarg);
629 diff_type = CONTEXT_DIFF;
632 if (chdir(optarg) < 0)
633 pfatal ("can't change directory to `%s'", optarg);
636 do_defines = savestr (optarg);
642 remove_empty_files = TRUE;
648 maxfuzz = numeric_string (optarg, 0, "fuzz factor");
651 patch_get = numeric_string (optarg, 1, "get option value");
654 patchname = savestr (optarg);
660 diff_type = NORMAL_DIFF;
666 if (strcmp (optarg, "-") == 0)
667 fatal ("can't output patches to standard output");
668 outfile = savestr (optarg);
671 strippath = numeric_string (optarg, 0, "strip count");
674 rejname = savestr (optarg);
678 reverse_flag_specified = 1;
690 diff_type = UNI_DIFF;
697 version_control = optarg;
701 debug = numeric_string (optarg, 1, "debugging option");
706 fatal ("backup basename prefix is empty");
707 origbase = savestr (optarg);
712 fatal ("backup suffix is empty");
713 simple_backup_suffix = savestr (optarg);
726 binary_transput = O_BINARY;
732 backup_if_mismatch = 1;
735 backup_if_mismatch = 0;
742 /* Process any filename args. */
745 inname = savestr (Argv[optind++]);
749 patchname = savestr (Argv[optind++]);
752 fprintf (stderr, "%s: extra operand `%s'\n",
753 program_name, Argv[optind]);
760 /* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero)
761 of type ARGTYPE_MSGID by converting it to an integer,
762 returning the result. */
764 numeric_string (string, negative_allowed, argtype_msgid)
766 int negative_allowed;
767 char const *argtype_msgid;
770 char const *p = string;
771 int sign = *p == '-' ? -1 : 1;
773 p += *p == '-' || *p == '+';
777 int v10 = value * 10;
778 int digit = *p - '0';
779 int signed_digit = sign * digit;
780 int next_value = v10 + signed_digit;
782 if (9 < (unsigned) digit)
783 fatal ("%s `%s' is not a number", argtype_msgid, string);
785 if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0))
786 fatal ("%s `%s' is too large", argtype_msgid, string);
792 if (value < 0 && ! negative_allowed)
793 fatal ("%s `%s' is negative", argtype_msgid, string);
798 /* Attempt to find the right place to apply this hunk of patch. */
804 register LINENUM first_guess = pch_first () + last_offset;
805 register LINENUM offset;
806 LINENUM pat_lines = pch_ptrn_lines();
807 LINENUM prefix_context = pch_prefix_context ();
808 LINENUM suffix_context = pch_suffix_context ();
809 LINENUM context = (prefix_context < suffix_context
810 ? suffix_context : prefix_context);
811 LINENUM prefix_fuzz = fuzz + prefix_context - context;
812 LINENUM suffix_fuzz = fuzz + suffix_context - context;
813 LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1;
814 LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz);
815 LINENUM max_pos_offset = max_where - first_guess;
816 LINENUM max_neg_offset = first_guess - min_where;
817 LINENUM max_offset = (max_pos_offset < max_neg_offset
818 ? max_neg_offset : max_pos_offset);
820 if (!pat_lines) /* null range matches always */
823 /* Do not try lines <= 0. */
824 if (first_guess <= max_neg_offset)
825 max_neg_offset = first_guess - 1;
829 /* Can only match start of file. */
832 /* Can only match entire file. */
833 if (pat_lines != input_lines || prefix_context < last_frozen_line)
836 offset = 1 - first_guess;
837 if (last_frozen_line <= prefix_context
838 && offset <= max_pos_offset
839 && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz))
841 last_offset = offset;
842 return first_guess + offset;
850 /* Can only match end of file. */
851 offset = first_guess - (input_lines - pat_lines + 1);
852 if (offset <= max_neg_offset
853 && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0))
855 last_offset = - offset;
856 return first_guess - offset;
862 for (offset = 0; offset <= max_offset; offset++) {
863 if (offset <= max_pos_offset
864 && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) {
866 say ("Offset changing from %ld to %ld\n", last_offset, offset);
867 last_offset = offset;
868 return first_guess+offset;
870 if (0 < offset && offset <= max_neg_offset
871 && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) {
873 say ("Offset changing from %ld to %ld\n", last_offset, -offset);
874 last_offset = -offset;
875 return first_guess-offset;
881 /* We did not find the pattern, dump out the hunk so they can handle it. */
887 register LINENUM pat_end = pch_end ();
888 /* add in last_offset to guess the same as the previous successful hunk */
889 LINENUM oldfirst = pch_first() + last_offset;
890 LINENUM newfirst = pch_newfirst() + last_offset;
891 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
892 LINENUM newlast = newfirst + pch_repl_lines() - 1;
894 (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : "";
895 char const *minuses =
896 (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----";
898 fprintf(rejfp, "***************\n");
899 for (i=0; i<=pat_end; i++) {
900 switch (pch_char(i)) {
902 if (oldlast < oldfirst)
903 fprintf(rejfp, "*** 0%s\n", stars);
904 else if (oldlast == oldfirst)
905 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
907 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
910 if (newlast < newfirst)
911 fprintf(rejfp, "--- 0%s\n", minuses);
912 else if (newlast == newfirst)
913 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
915 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
917 case ' ': case '-': case '+': case '!':
918 fprintf (rejfp, "%c ", pch_char (i));
921 pch_write_line (i, rejfp);
924 fatal ("fatal internal error in abort_hunk");
931 /* We found where to apply it (we hope), so do it. */
934 apply_hunk (outstate, where)
935 struct outstate *outstate;
938 register LINENUM old = 1;
939 register LINENUM lastline = pch_ptrn_lines ();
940 register LINENUM new = lastline+1;
941 register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE;
942 register char const *R_do_defines = do_defines;
943 register LINENUM pat_end = pch_end ();
944 register FILE *fp = outstate->ofp;
947 while (pch_char(new) == '=' || pch_char(new) == '\n')
950 while (old <= lastline) {
951 if (pch_char(old) == '-') {
952 assert (outstate->after_newline);
953 if (! copy_till (outstate, where + old - 1))
956 if (def_state == OUTSIDE) {
957 fprintf (fp, outstate->after_newline + if_defined,
959 def_state = IN_IFNDEF;
961 else if (def_state == IN_IFDEF) {
962 fprintf (fp, outstate->after_newline + else_defined);
967 outstate->after_newline = pch_write_line (old, fp);
968 outstate->zero_output = 0;
973 else if (new > pat_end) {
976 else if (pch_char(new) == '+') {
977 if (! copy_till (outstate, where + old - 1))
980 if (def_state == IN_IFNDEF) {
981 fprintf (fp, outstate->after_newline + else_defined);
984 else if (def_state == OUTSIDE) {
985 fprintf (fp, outstate->after_newline + if_defined,
987 def_state = IN_IFDEF;
992 outstate->after_newline = pch_write_line (new, fp);
993 outstate->zero_output = 0;
996 else if (pch_char(new) != pch_char(old)) {
998 say ("oldchar = '%c', newchar = '%c'\n",
999 pch_char (old), pch_char (new));
1000 fatal ("Out-of-sync patch, lines %ld,%ld -- mangled text or line numbers, maybe?",
1001 pch_hunk_beg() + old,
1002 pch_hunk_beg() + new);
1004 else if (pch_char(new) == '!') {
1005 assert (outstate->after_newline);
1006 if (! copy_till (outstate, where + old - 1))
1008 assert (outstate->after_newline);
1010 fprintf (fp, not_defined, R_do_defines);
1013 def_state = IN_IFNDEF;
1019 outstate->after_newline = pch_write_line (old, fp);
1024 while (pch_char (old) == '!');
1027 fprintf (fp, outstate->after_newline + else_defined);
1030 def_state = IN_ELSE;
1035 outstate->after_newline = pch_write_line (new, fp);
1038 while (pch_char (new) == '!');
1039 outstate->zero_output = 0;
1042 assert(pch_char(new) == ' ');
1045 if (R_do_defines && def_state != OUTSIDE) {
1046 fprintf (fp, outstate->after_newline + end_defined,
1050 outstate->after_newline = 1;
1051 def_state = OUTSIDE;
1055 if (new <= pat_end && pch_char(new) == '+') {
1056 if (! copy_till (outstate, where + old - 1))
1059 if (def_state == OUTSIDE) {
1060 fprintf (fp, outstate->after_newline + if_defined,
1062 def_state = IN_IFDEF;
1064 else if (def_state == IN_IFNDEF) {
1065 fprintf (fp, outstate->after_newline + else_defined);
1066 def_state = IN_ELSE;
1070 outstate->zero_output = 0;
1075 if (! outstate->after_newline && putc ('\n', fp) == EOF)
1077 outstate->after_newline = pch_write_line (new, fp);
1078 outstate->zero_output = 0;
1081 while (new <= pat_end && pch_char (new) == '+');
1083 if (R_do_defines && def_state != OUTSIDE) {
1084 fprintf (fp, outstate->after_newline + end_defined, R_do_defines);
1087 outstate->after_newline = 1;
1092 /* Create an output file. */
1095 create_output_file (name)
1098 int fd = create_file (name, O_WRONLY | binary_transput, instat.st_mode);
1099 FILE *f = fdopen (fd, binary_transput ? "wb" : "w");
1101 pfatal ("can't create `%s'", name);
1105 /* Open the new file. */
1108 init_output (name, outstate)
1110 struct outstate *outstate;
1112 outstate->ofp = name ? create_output_file (name) : (FILE *) 0;
1113 outstate->after_newline = 1;
1114 outstate->zero_output = 1;
1117 /* Open a file to put hunks we can't locate. */
1123 rejfp = create_output_file (name);
1126 /* Copy input file to output, up to wherever hunk is to be applied. */
1129 copy_till (outstate, lastline)
1130 register struct outstate *outstate;
1131 register LINENUM lastline;
1133 register LINENUM R_last_frozen_line = last_frozen_line;
1134 register FILE *fp = outstate->ofp;
1135 register char const *s;
1138 if (R_last_frozen_line > lastline)
1140 say ("misordered hunks! output would be garbled\n");
1143 while (R_last_frozen_line < lastline)
1145 s = ifetch (++R_last_frozen_line, 0, &size);
1148 if ((! outstate->after_newline && putc ('\n', fp) == EOF)
1149 || ! fwrite (s, sizeof *s, size, fp))
1151 outstate->after_newline = s[size - 1] == '\n';
1152 outstate->zero_output = 0;
1155 last_frozen_line = R_last_frozen_line;
1159 /* Finish copying the input file to the output file. */
1162 spew_output (outstate)
1163 struct outstate *outstate;
1166 say ("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
1168 if (last_frozen_line < input_lines)
1169 if (! copy_till (outstate, input_lines))
1172 if (outstate->ofp && ! outfile)
1174 if (fclose (outstate->ofp) != 0)
1182 /* Does the patch pattern match at line base+offset? */
1185 patch_match (base, offset, prefix_fuzz, suffix_fuzz)
1188 LINENUM prefix_fuzz;
1189 LINENUM suffix_fuzz;
1191 register LINENUM pline = 1 + prefix_fuzz;
1192 register LINENUM iline;
1193 register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz;
1195 register char const *p;
1197 for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) {
1198 p = ifetch (iline, offset >= 0, &size);
1200 if (!similar(p, size,
1202 pch_line_len(pline) ))
1205 else if (size != pch_line_len (pline)
1206 || memcmp (p, pfetch (pline), size) != 0)
1212 /* Do two lines match with canonicalized white space? */
1215 similar (a, alen, b, blen)
1216 register char const *a;
1217 register size_t alen;
1218 register char const *b;
1219 register size_t blen;
1221 /* Ignore presence or absence of trailing newlines. */
1222 alen -= alen && a[alen - 1] == '\n';
1223 blen -= blen && b[blen - 1] == '\n';
1227 if (!blen || (*b == ' ' || *b == '\t'))
1229 while (blen && (*b == ' ' || *b == '\t'))
1233 if (!(*a == ' ' || *a == '\t'))
1236 while (alen && (*a == ' ' || *a == '\t'));
1239 return alen == blen;
1241 else if (!alen || *a++ != *b++)
1248 /* Make a temporary file. */
1251 char *mktemp PARAMS ((char *));
1255 #define TMPDIR "/tmp"
1264 char const *tmpdir = getenv ("TMPDIR"); /* Unix tradition */
1265 if (!tmpdir) tmpdir = getenv ("TMP"); /* DOS tradition */
1266 if (!tmpdir) tmpdir = getenv ("TEMP"); /* another DOS tradition */
1267 if (!tmpdir) tmpdir = TMPDIR;
1268 r = xmalloc (strlen (tmpdir) + 10);
1269 sprintf (r, "%s/p%cXXXXXX", tmpdir, letter);
1274 r = xmalloc (L_tmpnam);
1275 if (! (tmpnam (r) == r && *r))
1281 /* Fatal exit with cleanup. */
1290 exit_with_signal (sig);
1299 unlink (TMPOUTNAME);
1300 unlink (TMPPATNAME);
1301 unlink (TMPREJNAME);