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