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