patch.c revision 1.10 1 /* $NetBSD: patch.c,v 1.10 2002/03/08 21:57:33 kristerw Exp $ */
2
3 /* patch - a program to apply diffs to original files
4 *
5 * Copyright 1986, Larry Wall
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following condition
9 * is met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this condition and the following disclaimer.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/cdefs.h>
27 #ifndef lint
28 __RCSID("$NetBSD: patch.c,v 1.10 2002/03/08 21:57:33 kristerw Exp $");
29 #endif /* not lint */
30
31 #include "INTERN.h"
32 #include "common.h"
33 #include "EXTERN.h"
34 #include "version.h"
35 #include "util.h"
36 #include "pch.h"
37 #include "inp.h"
38 #include "backupfile.h"
39
40 #include <stdlib.h>
41 #include <unistd.h>
42
43 /* procedures */
44 static void reinitialize_almost_everything(void);
45 static char *nextarg(void);
46 static int optcmp(const void *, const void *);
47 static char decode_long_option(char *);
48 static void get_some_switches(void);
49 static LINENUM locate_hunk(LINENUM);
50 static void abort_hunk(void);
51 static void apply_hunk(LINENUM);
52 static void init_output(char *);
53 static void init_reject(char *);
54 static void copy_till(LINENUM);
55 static void spew_output(void);
56 static void dump_line(LINENUM);
57 static bool patch_match(LINENUM, LINENUM, LINENUM);
58 static bool similar(char *, char *, int);
59 int main(int, char *[]);
60
61 /* TRUE if -E was specified on command line. */
62 static int remove_empty_files = FALSE;
63
64 /* TRUE if -R was specified on command line. */
65 static int reverse_flag_specified = FALSE;
66
67 /* Apply a set of diffs as appropriate. */
68
69 int
70 main(int argc, char *argv[])
71 {
72 LINENUM where = 0;
73 LINENUM newwhere;
74 LINENUM fuzz;
75 LINENUM mymaxfuzz;
76 int hunk = 0;
77 int failed = 0;
78 int failtotal = 0;
79 int i;
80
81 setbuf(stderr, serrbuf);
82 for (i = 0; i<MAXFILEC; i++)
83 filearg[i] = Nullch;
84
85 myuid = getuid();
86
87 /* Cons up the names of the temporary files. */
88 {
89 /* Directory for temporary files. */
90 char *tmpdir;
91 int tmpname_len;
92
93 tmpdir = getenv ("TMPDIR");
94 if (tmpdir == NULL) {
95 tmpdir = "/tmp";
96 }
97 tmpname_len = strlen (tmpdir) + 20;
98
99 TMPOUTNAME = (char *) malloc (tmpname_len);
100 strcpy (TMPOUTNAME, tmpdir);
101 strcat (TMPOUTNAME, "/patchoXXXXXX");
102 if ((i = mkstemp(TMPOUTNAME)) < 0)
103 pfatal2("can't create %s", TMPOUTNAME);
104 Close(i);
105
106 TMPINNAME = (char *) malloc (tmpname_len);
107 strcpy (TMPINNAME, tmpdir);
108 strcat (TMPINNAME, "/patchiXXXXXX");
109 if ((i = mkstemp(TMPINNAME)) < 0)
110 pfatal2("can't create %s", TMPINNAME);
111 Close(i);
112
113 TMPREJNAME = (char *) malloc (tmpname_len);
114 strcpy (TMPREJNAME, tmpdir);
115 strcat (TMPREJNAME, "/patchrXXXXXX");
116 if ((i = mkstemp(TMPREJNAME)) < 0)
117 pfatal2("can't create %s", TMPREJNAME);
118 Close(i);
119
120 TMPPATNAME = (char *) malloc (tmpname_len);
121 strcpy (TMPPATNAME, tmpdir);
122 strcat (TMPPATNAME, "/patchpXXXXXX");
123 if ((i = mkstemp(TMPPATNAME)) < 0)
124 pfatal2("can't create %s", TMPPATNAME);
125 Close(i);
126 }
127
128 {
129 char *v;
130
131 v = getenv ("SIMPLE_BACKUP_SUFFIX");
132 if (v)
133 simple_backup_suffix = v;
134 else
135 simple_backup_suffix = ORIGEXT;
136 #ifndef NODIR
137 v = getenv ("VERSION_CONTROL");
138 backup_type = get_version (v); /* OK to pass NULL. */
139 #endif
140 }
141
142 /* parse switches */
143 Argc = argc;
144 Argv = argv;
145 get_some_switches();
146
147 /* make sure we clean up /tmp in case of disaster */
148 set_signals(0);
149
150 for (
151 open_patch_file(filearg[1]);
152 there_is_another_patch();
153 reinitialize_almost_everything()
154 ) { /* for each patch in patch file */
155
156 if (outname == Nullch)
157 outname = savestr(filearg[0]);
158
159 /* for ed script just up and do it and exit */
160 if (diff_type == ED_DIFF) {
161 do_ed_script();
162 continue;
163 }
164
165 /* initialize the patched file */
166 if (!skip_rest_of_patch)
167 init_output(TMPOUTNAME);
168
169 /* initialize reject file */
170 init_reject(TMPREJNAME);
171
172 /* find out where all the lines are */
173 if (!skip_rest_of_patch)
174 scan_input(filearg[0]);
175
176 /* from here on, open no standard i/o files, because malloc */
177 /* might misfire and we can't catch it easily */
178
179 /* apply each hunk of patch */
180 hunk = 0;
181 failed = 0;
182 out_of_mem = FALSE;
183 while (another_hunk()) {
184 hunk++;
185 fuzz = Nulline;
186 mymaxfuzz = pch_context();
187 if (maxfuzz < mymaxfuzz)
188 mymaxfuzz = maxfuzz;
189 if (!skip_rest_of_patch) {
190 do {
191 where = locate_hunk(fuzz);
192 if (hunk == 1 && where == Nulline && !force) {
193 /* dwim for reversed patch? */
194 if (!pch_swap()) {
195 if (fuzz == Nulline)
196 say1(
197 "Not enough memory to try swapped hunk! Assuming unswapped.\n");
198 continue;
199 }
200 reverse = !reverse;
201 where = locate_hunk(fuzz); /* try again */
202 if (where == Nulline) { /* didn't find it swapped */
203 if (!pch_swap()) /* put it back to normal */
204 fatal1("lost hunk on alloc error!\n");
205 reverse = !reverse;
206 }
207 else if (noreverse) {
208 if (!pch_swap()) /* put it back to normal */
209 fatal1("lost hunk on alloc error!\n");
210 reverse = !reverse;
211 say1(
212 "Ignoring previously applied (or reversed) patch.\n");
213 skip_rest_of_patch = TRUE;
214 }
215 else if (batch) {
216 if (verbose)
217 say3(
218 "%seversed (or previously applied) patch detected! %s -R.",
219 reverse ? "R" : "Unr",
220 reverse ? "Assuming" : "Ignoring");
221 }
222 else {
223 ask3(
224 "%seversed (or previously applied) patch detected! %s -R? [y] ",
225 reverse ? "R" : "Unr",
226 reverse ? "Assume" : "Ignore");
227 if (*buf == 'n') {
228 ask1("Apply anyway? [n] ");
229 if (*buf != 'y')
230 skip_rest_of_patch = TRUE;
231 where = Nulline;
232 reverse = !reverse;
233 if (!pch_swap()) /* put it back to normal */
234 fatal1("lost hunk on alloc error!\n");
235 }
236 }
237 }
238 } while (!skip_rest_of_patch && where == Nulline &&
239 ++fuzz <= mymaxfuzz);
240
241 if (skip_rest_of_patch) { /* just got decided */
242 Fclose(ofp);
243 ofp = Nullfp;
244 }
245 }
246
247 newwhere = pch_newfirst() + last_offset;
248 if (skip_rest_of_patch) {
249 abort_hunk();
250 failed++;
251 if (verbose)
252 say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
253 }
254 else if (where == Nulline) {
255 abort_hunk();
256 failed++;
257 if (verbose)
258 say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
259 }
260 else {
261 apply_hunk(where);
262 if (verbose) {
263 say3("Hunk #%d succeeded at %ld", hunk, newwhere);
264 if (fuzz)
265 say2(" with fuzz %ld", fuzz);
266 if (last_offset)
267 say3(" (offset %ld line%s)",
268 last_offset, last_offset==1L?"":"s");
269 say1(".\n");
270 }
271 }
272 }
273
274 if (out_of_mem && using_plan_a) {
275 Argc = Argc_last;
276 Argv = Argv_last;
277 say1("\n\nRan out of memory using Plan A--trying again...\n\n");
278 if (ofp)
279 Fclose(ofp);
280 ofp = Nullfp;
281 if (rejfp)
282 Fclose(rejfp);
283 rejfp = Nullfp;
284 continue;
285 }
286
287 assert(hunk);
288
289 /* finish spewing out the new file */
290 if (!skip_rest_of_patch)
291 spew_output();
292
293 /* and put the output where desired */
294 ignore_signals();
295 if (!skip_rest_of_patch) {
296 struct stat statbuf;
297 char *realout = outname;
298
299 if (move_file(TMPOUTNAME, outname) < 0) {
300 toutkeep = TRUE;
301 realout = TMPOUTNAME;
302 chmod(TMPOUTNAME, filemode);
303 }
304 else
305 chmod(outname, filemode);
306
307 if (remove_empty_files && stat(realout, &statbuf) == 0
308 && statbuf.st_size == 0) {
309 if (verbose)
310 say2("Removing %s (empty after patching).\n", realout);
311 while (unlink(realout) >= 0) ; /* while is for Eunice. */
312 }
313 }
314 Fclose(rejfp);
315 rejfp = Nullfp;
316 if (failed) {
317 failtotal += failed;
318 if (!*rejname) {
319 Strcpy(rejname, outname);
320 #ifndef FLEXFILENAMES
321 {
322 char *s = strrchr(rejname,'/');
323
324 if (!s)
325 s = rejname;
326 if (strlen(s) > 13)
327 if (s[12] == '.') /* try to preserve difference */
328 s[12] = s[13]; /* between .h, .c, .y, etc. */
329 s[13] = '\0';
330 }
331 #endif
332 Strcat(rejname, REJEXT);
333 }
334 if (skip_rest_of_patch) {
335 say4("%d out of %d hunks ignored--saving rejects to %s\n",
336 failed, hunk, rejname);
337 }
338 else {
339 say4("%d out of %d hunks failed--saving rejects to %s\n",
340 failed, hunk, rejname);
341 }
342 if (move_file(TMPREJNAME, rejname) < 0)
343 trejkeep = TRUE;
344 }
345 set_signals(1);
346 }
347 my_exit(failtotal);
348 }
349
350 /* Prepare to find the next patch to do in the patch file. */
351
352 static void
353 reinitialize_almost_everything(void)
354 {
355 re_patch();
356 re_input();
357
358 input_lines = 0;
359 last_frozen_line = 0;
360
361 filec = 0;
362 if (filearg[0] != Nullch && !out_of_mem) {
363 free(filearg[0]);
364 filearg[0] = Nullch;
365 }
366
367 if (outname != Nullch) {
368 free(outname);
369 outname = Nullch;
370 }
371
372 last_offset = 0;
373
374 diff_type = 0;
375
376 if (revision != Nullch) {
377 free(revision);
378 revision = Nullch;
379 }
380
381 reverse = reverse_flag_specified;
382 skip_rest_of_patch = FALSE;
383
384 get_some_switches();
385
386 if (filec >= 2)
387 fatal1("you may not change to a different patch file\n");
388 }
389
390 static char *
391 nextarg(void)
392 {
393 if (!--Argc)
394 fatal2("missing argument after `%s'\n", *Argv);
395 return *++Argv;
396 }
397
398 /* Module for handling of long options. */
399
400 struct option {
401 char *long_opt;
402 char short_opt;
403 };
404
405 static int
406 optcmp(const void *va, const void *vb)
407 {
408 const struct option *a = va, *b = vb;
409 return strcmp (a->long_opt, b->long_opt);
410 }
411
412 /* Decode Long options beginning with "--" to their short equivalents. */
413
414 static char
415 decode_long_option(char *opt)
416 {
417 /*
418 * This table must be sorted on the first field. We also decode
419 * unimplemented options as those will probably be dealt with
420 * later, anyhow.
421 */
422 static struct option options[] = {
423 { "batch", 't' },
424 { "check", 'C' },
425 { "context", 'c' },
426 { "debug", 'x' },
427 { "directory", 'd' },
428 { "ed", 'e' },
429 { "force", 'f' },
430 { "forward", 'N' },
431 { "fuzz", 'F' },
432 { "ifdef", 'D' },
433 { "ignore-whitespace", 'l' },
434 { "normal", 'n' },
435 { "output", 'o' },
436 { "patchfile", 'i' },
437 { "prefix", 'B' },
438 { "quiet", 's' },
439 { "reject-file", 'r' },
440 { "remove-empty-files", 'E' },
441 { "reverse", 'R' },
442 { "silent", 's' },
443 { "skip", 'S' },
444 { "strip", 'p' },
445 { "suffix", 'b' },
446 { "unified", 'u' },
447 { "version", 'v' },
448 { "version-control", 'V' },
449 };
450 struct option key, *found;
451
452 key.long_opt = opt;
453 found = (struct option *)bsearch(&key, options,
454 sizeof(options) / sizeof(options[0]), sizeof(options[0]), optcmp);
455
456 return found ? found->short_opt : '\0';
457 }
458
459 /* Process switches and filenames up to next '+' or end of list. */
460
461 static void
462 get_some_switches(void)
463 {
464 char *s;
465
466 rejname[0] = '\0';
467 Argc_last = Argc;
468 Argv_last = Argv;
469 if (!Argc)
470 return;
471 for (Argc--,Argv++; Argc; Argc--,Argv++) {
472 s = Argv[0];
473 if (strEQ(s, "+")) {
474 return; /* + will be skipped by for loop */
475 }
476 if (*s != '-' || !s[1]) {
477 if (filec == MAXFILEC)
478 fatal1("too many file arguments\n");
479 if (filec == 1 && filearg[filec] != Nullch)
480 fatal1("-i option and patchfile argument are mutually\
481 exclusive\n");
482 filearg[filec++] = savestr(s);
483 }
484 else {
485 char opt;
486
487 if (*(s + 1) == '-') {
488 opt = decode_long_option(s + 2);
489 s += strlen(s) - 1;
490 }
491 else
492 opt = *++s;
493
494 switch (opt) {
495 case 'b':
496 simple_backup_suffix = savestr(nextarg());
497 break;
498 case 'B':
499 origprae = savestr(nextarg());
500 break;
501 case 'c':
502 diff_type = CONTEXT_DIFF;
503 break;
504 case 'd':
505 if (!*++s)
506 s = nextarg();
507 if (chdir(s) < 0)
508 pfatal2("can't cd to %s", s);
509 break;
510 case 'D':
511 do_defines = TRUE;
512 if (!*++s)
513 s = nextarg();
514 if (!isalpha((unsigned char)*s) && '_' != *s)
515 fatal1("argument to -D is not an identifier\n");
516 Sprintf(if_defined, "#ifdef %s\n", s);
517 Sprintf(not_defined, "#ifndef %s\n", s);
518 Sprintf(end_defined, "#endif /* %s */\n", s);
519 break;
520 case 'e':
521 diff_type = ED_DIFF;
522 break;
523 case 'E':
524 remove_empty_files = TRUE;
525 break;
526 case 'f':
527 force = TRUE;
528 break;
529 case 'F':
530 if (*++s == '=')
531 s++;
532 maxfuzz = atoi(s);
533 break;
534 case 'i':
535 if (filearg[1] != Nullch)
536 free(filearg[1]);
537 filearg[1] = savestr(nextarg());
538 break;
539 case 'l':
540 canonicalize = TRUE;
541 break;
542 case 'n':
543 diff_type = NORMAL_DIFF;
544 break;
545 case 'N':
546 noreverse = TRUE;
547 break;
548 case 'o':
549 outname = savestr(nextarg());
550 break;
551 case 'p':
552 if (*++s == '=')
553 s++;
554 strippath = atoi(s);
555 break;
556 case 'r':
557 Strcpy(rejname, nextarg());
558 break;
559 case 'R':
560 reverse = TRUE;
561 reverse_flag_specified = TRUE;
562 break;
563 case 's':
564 verbose = FALSE;
565 break;
566 case 'S':
567 skip_rest_of_patch = TRUE;
568 break;
569 case 't':
570 batch = TRUE;
571 break;
572 case 'u':
573 diff_type = UNI_DIFF;
574 break;
575 case 'v':
576 version();
577 break;
578 case 'V':
579 #ifndef NODIR
580 backup_type = get_version (nextarg ());
581 #endif
582 break;
583 #ifdef DEBUGGING
584 case 'x':
585 debug = atoi(s+1);
586 break;
587 #endif
588 default:
589 fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]);
590 fprintf(stderr, "\
591 Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
592 Options:\n\
593 [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
594 [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
595 [-r rej-name] [-V {numbered,existing,simple}]\n");
596 my_exit(1);
597 }
598 }
599 }
600 }
601
602 /* Attempt to find the right place to apply this hunk of patch. */
603
604 static LINENUM
605 locate_hunk(LINENUM fuzz)
606 {
607 LINENUM first_guess = pch_first() + last_offset;
608 LINENUM offset;
609 LINENUM pat_lines = pch_ptrn_lines();
610 LINENUM max_pos_offset = input_lines - first_guess
611 - pat_lines + 1;
612 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
613 + pch_context();
614
615 if (!pat_lines) /* null range matches always */
616 return first_guess;
617 if (max_neg_offset >= first_guess) /* do not try lines < 0 */
618 max_neg_offset = first_guess - 1;
619 if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
620 return first_guess;
621 for (offset = 1; ; offset++) {
622 bool check_after = (offset <= max_pos_offset);
623 bool check_before = (offset <= max_neg_offset);
624
625 if (check_after && patch_match(first_guess, offset, fuzz)) {
626 #ifdef DEBUGGING
627 if (debug & 1)
628 say3("Offset changing from %ld to %ld\n", last_offset, offset);
629 #endif
630 last_offset = offset;
631 return first_guess+offset;
632 }
633 else if (check_before && patch_match(first_guess, -offset, fuzz)) {
634 #ifdef DEBUGGING
635 if (debug & 1)
636 say3("Offset changing from %ld to %ld\n", last_offset, -offset);
637 #endif
638 last_offset = -offset;
639 return first_guess-offset;
640 }
641 else if (!check_before && !check_after)
642 return Nulline;
643 }
644 }
645
646 /* We did not find the pattern, dump out the hunk so they can handle it. */
647
648 static void
649 abort_hunk(void)
650 {
651 LINENUM i;
652 LINENUM pat_end = pch_end();
653 /* add in last_offset to guess the same as the previous successful hunk */
654 LINENUM oldfirst = pch_first() + last_offset;
655 LINENUM newfirst = pch_newfirst() + last_offset;
656 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
657 LINENUM newlast = newfirst + pch_repl_lines() - 1;
658 char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
659 char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
660
661 fprintf(rejfp, "***************\n");
662 for (i=0; i<=pat_end; i++) {
663 switch (pch_char(i)) {
664 case '*':
665 if (oldlast < oldfirst)
666 fprintf(rejfp, "*** 0%s\n", stars);
667 else if (oldlast == oldfirst)
668 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
669 else
670 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
671 break;
672 case '=':
673 if (newlast < newfirst)
674 fprintf(rejfp, "--- 0%s\n", minuses);
675 else if (newlast == newfirst)
676 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
677 else
678 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
679 break;
680 case '\n':
681 fprintf(rejfp, "%s", pfetch(i));
682 break;
683 case ' ': case '-': case '+': case '!':
684 fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
685 break;
686 default:
687 fatal1("fatal internal error in abort_hunk\n");
688 }
689 }
690 }
691
692 /* We found where to apply it (we hope), so do it. */
693
694 static void
695 apply_hunk(LINENUM where)
696 {
697 LINENUM old = 1;
698 LINENUM lastline = pch_ptrn_lines();
699 LINENUM new = lastline+1;
700 #define OUTSIDE 0
701 #define IN_IFNDEF 1
702 #define IN_IFDEF 2
703 #define IN_ELSE 3
704 int def_state = OUTSIDE;
705 bool R_do_defines = do_defines;
706 LINENUM pat_end = pch_end();
707
708 where--;
709 while (pch_char(new) == '=' || pch_char(new) == '\n')
710 new++;
711
712 while (old <= lastline) {
713 if (pch_char(old) == '-') {
714 copy_till(where + old - 1);
715 if (R_do_defines) {
716 if (def_state == OUTSIDE) {
717 fputs(not_defined, ofp);
718 def_state = IN_IFNDEF;
719 }
720 else if (def_state == IN_IFDEF) {
721 fputs(else_defined, ofp);
722 def_state = IN_ELSE;
723 }
724 fputs(pfetch(old), ofp);
725 }
726 last_frozen_line++;
727 old++;
728 }
729 else if (new > pat_end) {
730 break;
731 }
732 else if (pch_char(new) == '+') {
733 copy_till(where + old - 1);
734 if (R_do_defines) {
735 if (def_state == IN_IFNDEF) {
736 fputs(else_defined, ofp);
737 def_state = IN_ELSE;
738 }
739 else if (def_state == OUTSIDE) {
740 fputs(if_defined, ofp);
741 def_state = IN_IFDEF;
742 }
743 }
744 fputs(pfetch(new), ofp);
745 new++;
746 }
747 else if (pch_char(new) != pch_char(old)) {
748 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
749 pch_hunk_beg() + old,
750 pch_hunk_beg() + new);
751 #ifdef DEBUGGING
752 say3("oldchar = '%c', newchar = '%c'\n",
753 pch_char(old), pch_char(new));
754 #endif
755 my_exit(1);
756 }
757 else if (pch_char(new) == '!') {
758 copy_till(where + old - 1);
759 if (R_do_defines) {
760 fputs(not_defined, ofp);
761 def_state = IN_IFNDEF;
762 }
763 while (pch_char(old) == '!') {
764 if (R_do_defines) {
765 fputs(pfetch(old), ofp);
766 }
767 last_frozen_line++;
768 old++;
769 }
770 if (R_do_defines) {
771 fputs(else_defined, ofp);
772 def_state = IN_ELSE;
773 }
774 while (pch_char(new) == '!') {
775 fputs(pfetch(new), ofp);
776 new++;
777 }
778 }
779 else {
780 assert(pch_char(new) == ' ');
781 old++;
782 new++;
783 if (R_do_defines && def_state != OUTSIDE) {
784 fputs(end_defined, ofp);
785 def_state = OUTSIDE;
786 }
787 }
788 }
789 if (new <= pat_end && pch_char(new) == '+') {
790 copy_till(where + old - 1);
791 if (R_do_defines) {
792 if (def_state == OUTSIDE) {
793 fputs(if_defined, ofp);
794 def_state = IN_IFDEF;
795 }
796 else if (def_state == IN_IFNDEF) {
797 fputs(else_defined, ofp);
798 def_state = IN_ELSE;
799 }
800 }
801 while (new <= pat_end && pch_char(new) == '+') {
802 fputs(pfetch(new), ofp);
803 new++;
804 }
805 }
806 if (R_do_defines && def_state != OUTSIDE) {
807 fputs(end_defined, ofp);
808 }
809 }
810
811 /* Open the new file. */
812
813 static void
814 init_output(char *name)
815 {
816 ofp = fopen(name, "w");
817 if (ofp == Nullfp)
818 pfatal2("can't create %s", name);
819 }
820
821 /* Open a file to put hunks we can't locate. */
822
823 static void
824 init_reject(char *name)
825 {
826 rejfp = fopen(name, "w");
827 if (rejfp == Nullfp)
828 pfatal2("can't create %s", name);
829 }
830
831 /* Copy input file to output, up to wherever hunk is to be applied. */
832
833 static void
834 copy_till(LINENUM lastline)
835 {
836 LINENUM R_last_frozen_line = last_frozen_line;
837
838 if (R_last_frozen_line > lastline)
839 fatal1("misordered hunks! output would be garbled\n");
840 while (R_last_frozen_line < lastline) {
841 dump_line(++R_last_frozen_line);
842 }
843 last_frozen_line = R_last_frozen_line;
844 }
845
846 /* Finish copying the input file to the output file. */
847
848 static void
849 spew_output(void)
850 {
851 #ifdef DEBUGGING
852 if (debug & 256)
853 say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
854 #endif
855 if (input_lines)
856 copy_till(input_lines); /* dump remainder of file */
857 Fclose(ofp);
858 ofp = Nullfp;
859 }
860
861 /* Copy one line from input to output. */
862
863 static void
864 dump_line(LINENUM line)
865 {
866 char *s;
867 char R_newline = '\n';
868
869 /* Note: string is not null terminated. */
870 for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
871 }
872
873 /* Does the patch pattern match at line base+offset? */
874
875 static bool
876 patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
877 {
878 LINENUM pline = 1 + fuzz;
879 LINENUM iline;
880 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
881
882 for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
883 if (canonicalize) {
884 if (!similar(ifetch(iline, (offset >= 0)),
885 pfetch(pline),
886 pch_line_len(pline) ))
887 return FALSE;
888 }
889 else if (strnNE(ifetch(iline, (offset >= 0)),
890 pfetch(pline),
891 pch_line_len(pline) ))
892 return FALSE;
893 }
894 return TRUE;
895 }
896
897 /* Do two lines match with canonicalized white space? */
898
899 static bool
900 similar(char *a, char *b, int len)
901 {
902 while (len) {
903 if (isspace((unsigned char)*b)) {/* whitespace (or \n) to match? */
904 if (!isspace((unsigned char)*a))/* no corresponding whitespace? */
905 return FALSE;
906 while (len && isspace((unsigned char)*b) && *b != '\n')
907 b++,len--; /* skip pattern whitespace */
908 while (isspace((unsigned char)*a) && *a != '\n')
909 a++; /* skip target whitespace */
910 if (*a == '\n' || *b == '\n')
911 return (*a == *b); /* should end in sync */
912 }
913 else if (*a++ != *b++) /* match non-whitespace chars */
914 return FALSE;
915 else
916 len--; /* probably not necessary */
917 }
918 return TRUE; /* actually, this is not reached */
919 /* since there is always a \n */
920 }
921
922 /* Exit with cleanup. */
923
924 void
925 my_exit(int status)
926 {
927 Unlink(TMPINNAME);
928 if (!toutkeep) {
929 Unlink(TMPOUTNAME);
930 }
931 if (!trejkeep) {
932 Unlink(TMPREJNAME);
933 }
934 Unlink(TMPPATNAME);
935 exit(status);
936 }
937