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