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