]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - gnu/usr.bin/patch/pch.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / gnu / usr.bin / patch / pch.c
1 /* $FreeBSD$
2  *
3  * $Log: pch.c,v $
4  * Revision 2.0.2.0  90/05/01  22:17:51  davison
5  * patch12u: unidiff support added
6  *
7  * Revision 2.0.1.7  88/06/03  15:13:28  lwall
8  * patch10: Can now find patches in shar scripts.
9  * patch10: Hunks that swapped and then swapped back could core dump.
10  *
11  * Revision 2.0.1.6  87/06/04  16:18:13  lwall
12  * pch_swap didn't swap p_bfake and p_efake.
13  *
14  * Revision 2.0.1.5  87/01/30  22:47:42  lwall
15  * Improved responses to mangled patches.
16  *
17  * Revision 2.0.1.4  87/01/05  16:59:53  lwall
18  * New-style context diffs caused double call to free().
19  *
20  * Revision 2.0.1.3  86/11/14  10:08:33  lwall
21  * Fixed problem where a long pattern wouldn't grow the hunk.
22  * Also restored p_input_line when backtracking so error messages are right.
23  *
24  * Revision 2.0.1.2  86/11/03  17:49:52  lwall
25  * New-style delete triggers spurious assertion error.
26  *
27  * Revision 2.0.1.1  86/10/29  15:52:08  lwall
28  * Could falsely report new-style context diff.
29  *
30  * Revision 2.0  86/09/17  15:39:37  lwall
31  * Baseline for netwide release.
32  *
33  */
34
35 #include "EXTERN.h"
36 #include "common.h"
37 #include "util.h"
38 #include "INTERN.h"
39 #include "pch.h"
40
41 /* Patch (diff listing) abstract type. */
42
43 static long p_filesize;                 /* size of the patch file */
44 static LINENUM p_first;                 /* 1st line number */
45 static LINENUM p_newfirst;              /* 1st line number of replacement */
46 static LINENUM p_ptrn_lines;            /* # lines in pattern */
47 static LINENUM p_repl_lines;            /* # lines in replacement text */
48 static LINENUM p_end = -1;              /* last line in hunk */
49 static LINENUM p_max;                   /* max allowed value of p_end */
50 static LINENUM p_context = 3;           /* # of context lines */
51 static LINENUM p_input_line = 0;        /* current line # from patch file */
52 static char **p_line = Null(char**);    /* the text of the hunk */
53 static short *p_len = Null(short*);     /* length of each line */
54 static char *p_Char = Nullch;           /* +, -, and ! */
55 static int hunkmax = INITHUNKMAX;       /* size of above arrays to begin with */
56 static int p_indent;                    /* indent to patch */
57 static LINENUM p_base;                  /* where to intuit this time */
58 static LINENUM p_bline;                 /* line # of p_base */
59 static LINENUM p_start;                 /* where intuit found a patch */
60 static LINENUM p_sline;                 /* and the line number for it */
61 static LINENUM p_hunk_beg;              /* line number of current hunk */
62 static LINENUM p_efake = -1;            /* end of faked up lines--don't free */
63 static LINENUM p_bfake = -1;            /* beg of faked up lines */
64
65 /*
66  * Prepare to look for the next patch in the patch file.
67  */
68 void
69 re_patch(void)
70 {
71         p_first = Nulline;
72         p_newfirst = Nulline;
73         p_ptrn_lines = Nulline;
74         p_repl_lines = Nulline;
75         p_end = (LINENUM)-1;
76         p_max = Nulline;
77         p_indent = 0;
78 }
79
80 /*
81  * Open the patch file at the beginning of time.
82  */
83 void
84 open_patch_file(char *filename)
85 {
86         if (filename == Nullch || !*filename || strEQ(filename, "-")) {
87                 pfp = fopen(TMPPATNAME, "w");
88                 if (pfp == Nullfp)
89                         pfatal2("can't create %s", TMPPATNAME);
90                 while (fgets(buf, buf_size, stdin) != Nullch)
91                         fputs(buf, pfp);
92                 Fclose(pfp);
93                 filename = TMPPATNAME;
94         }
95         pfp = fopen(filename, "r");
96         if (pfp == Nullfp)
97             pfatal2("patch file %s not found", filename);
98         Fstat(fileno(pfp), &filestat);
99         p_filesize = filestat.st_size;
100         next_intuit_at(0L,1L);                  /* start at the beginning */
101         set_hunkmax();
102 }
103
104 /*
105  * Make sure our dynamically realloced tables are malloced to begin with.
106  */
107 void
108 set_hunkmax(void)
109 {
110 #ifndef lint
111         if (p_line == Null(char**))
112                 p_line = (char**) malloc((MEM)hunkmax * sizeof(char *));
113         if (p_len == Null(short*))
114                 p_len  = (short*) malloc((MEM)hunkmax * sizeof(short));
115 #endif
116         if (p_Char == Nullch)
117                 p_Char = (char*)  malloc((MEM)hunkmax * sizeof(char));
118 }
119
120 /*
121  * Enlarge the arrays containing the current hunk of patch.
122  */
123 void
124 grow_hunkmax(void)
125 {
126         hunkmax *= 2;
127         /*
128          * Note that on most systems, only the p_line array ever gets
129          * fresh memory since p_len can move into p_line's old space,
130          * and p_Char can move into p_len's old space.  Not on PDP-11's
131          * however.  But it doesn't matter.
132          */
133         assert(p_line != Null(char**) && p_len != Null(short*) &&
134             p_Char != Nullch);
135 #ifndef lint
136         p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *));
137         p_len  = (short*) realloc((char*)p_len,  (MEM)hunkmax * sizeof(short));
138         p_Char = (char*)  realloc((char*)p_Char, (MEM)hunkmax * sizeof(char));
139 #endif
140         if (p_line != Null(char**) && p_len != Null(short*) && p_Char != Nullch)
141                 return;
142         if (!using_plan_a)
143                 fatal1("out of memory\n");
144         out_of_mem = TRUE;      /* whatever is null will be allocated again */
145                                 /* from within plan_a(), of all places */
146 }
147
148 /*
149  * True if the remainder of the patch file contains a diff of some sort.
150  */
151 bool
152 there_is_another_patch(void)
153 {
154         if (p_base != 0L && p_base >= p_filesize) {
155                 if (verbose)
156                         say1("done\n");
157                 return FALSE;
158         }
159         if (verbose)
160                 say1("Hmm...");
161         diff_type = intuit_diff_type();
162         if (!diff_type) {
163                 if (p_base != 0L) {
164                         if (verbose)
165                                 say1("  Ignoring the trailing garbage.\ndone\n");
166                 }
167                 else
168                         say1("  I can't seem to find a patch in there anywhere.\n");
169                 return FALSE;
170         }
171         if (verbose)
172                 say3("  %sooks like %s to me...\n",
173                     (p_base == 0L ? "L" : "The next patch l"),
174                     diff_type == UNI_DIFF ? "a unified diff" :
175                     diff_type == CONTEXT_DIFF ? "a context diff" :
176                     diff_type == NEW_CONTEXT_DIFF ?
177                     "a new-style context diff" :
178                     diff_type == NORMAL_DIFF ? "a normal diff" :
179                     "an ed script" );
180         if (p_indent && verbose)
181                 say3("(Patch is indented %d space%s.)\n",
182                     p_indent, p_indent==1?"":"s");
183         skip_to(p_start,p_sline);
184         while (filearg[0] == Nullch) {
185                 if (force || batch || skip_rest_of_patch) {
186                         say1("No file to patch.  Skipping...\n");
187                         filearg[0] = savestr(bestguess);
188                         skip_rest_of_patch = TRUE;
189                         return TRUE;
190                 }
191                 (void) ask1("File to patch: ");
192                 if (*buf != '\n') {
193                         if (bestguess)
194                                 free(bestguess);
195                         bestguess = savestr(buf);
196                         filearg[0] = fetchname(buf, 0, FALSE);
197                 }
198                 if (filearg[0] == Nullch) {
199                         if (ask1("No file found--skip this patch? [n] ")) {
200                                 if (*buf != 'y') {
201                                         continue;
202                                 }
203                         }
204                         if (verbose)
205                                 say1("Skipping patch...\n");
206                         filearg[0] = fetchname(bestguess, 0, TRUE);
207                         skip_rest_of_patch = TRUE;
208                         return TRUE;
209                 }
210         }
211         return TRUE;
212 }
213
214 static char *
215 p4_savestr(char *str)
216 {
217         char *t, *h;
218
219         /* Leading whitespace. */
220         while (isspace((unsigned char)*str))
221                 str++;
222
223         /* Remove the file revision number. */
224         for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++)
225                 if (*t == '#')
226                         h = t;
227         if (h != NULL)
228                 *h = '\0';
229
230         return savestr(str);
231 }
232
233 /*
234  * Determine what kind of diff is in the remaining part of the patch file.
235  */
236 int
237 intuit_diff_type(void)
238 {
239         Reg4 long this_line = 0;
240         Reg5 long previous_line;
241         Reg6 long first_command_line = -1;
242         long fcl_line;
243         Reg7 bool last_line_was_command = FALSE;
244         Reg8 bool this_is_a_command = FALSE;
245         Reg9 bool stars_last_line = FALSE;
246         Reg10 bool stars_this_line = FALSE;
247         Reg3 int indent;
248         Reg1 char *s;
249         Reg2 char *t;
250         char *indtmp = Nullch;
251         char *oldtmp = Nullch;
252         char *newtmp = Nullch;
253         char *indname = Nullch;
254         char *oldname = Nullch;
255         char *newname = Nullch;
256         Reg11 int retval;
257         bool no_filearg = (filearg[0] == Nullch);
258         extern int index_first;
259
260         ok_to_create_file = FALSE;
261         Fseek(pfp, p_base, 0);
262         p_input_line = p_bline - 1;
263         for (;;) {
264                 previous_line = this_line;
265                 last_line_was_command = this_is_a_command;
266                 stars_last_line = stars_this_line;
267                 this_line = ftell(pfp);
268                 indent = 0;
269                 p_input_line++;
270                 if (pgets(FALSE) == 0) {
271                         if (first_command_line >= 0L) {
272                                 /* nothing but deletes!? */
273                                 p_start = first_command_line;
274                                 p_sline = fcl_line;
275                                 retval = ED_DIFF;
276                                 goto scan_exit;
277                         }
278                         else {
279                                 p_start = this_line;
280                                 p_sline = p_input_line;
281                                 retval = 0;
282                                 goto scan_exit;
283                         }
284                 }
285                 for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
286                         if (*s == '\t')
287                                 indent += 8 - (indent % 8);
288                         else
289                                 indent++;
290                 }
291                 for (t=s; isdigit((unsigned char)*t) || *t == ','; t++)
292                         ;
293                 this_is_a_command = (isdigit((unsigned char)*s) &&
294                     (*t == 'd' || *t == 'c' || *t == 'a') );
295                 if (first_command_line < 0L && this_is_a_command) {
296                         first_command_line = this_line;
297                         fcl_line = p_input_line;
298                         p_indent = indent;      /* assume this for now */
299                 }
300                 if (!stars_last_line && strnEQ(s, "*** ", 4))
301                         oldtmp = savestr(s+4);
302                 else if (strnEQ(s, "--- ", 4))
303                         newtmp = savestr(s+4);
304                 else if (strnEQ(s, "+++ ", 4))
305                         oldtmp = savestr(s+4);  /* pretend it is the old name */
306                 else if (strnEQ(s, "Index:", 6))
307                         indtmp = savestr(s+6);
308                 else if (strnEQ(s, "Prereq:", 7)) {
309                         for (t = s + 7; isspace((unsigned char)*t); t++)
310                                 ;
311                         revision = savestr(t);
312                         for (t = revision; *t && !isspace((unsigned char)*t);
313                             t++)
314                                 ;
315                         *t = '\0';
316                         if (!*revision) {
317                                 free(revision);
318                                 revision = Nullch;
319                         }
320                 } else if (strnEQ(s, "==== ", 5)) {
321                         /* Perforce-style diffs. */
322                         if ((t = strstr(s + 5, " - ")) != NULL)
323                                 newtmp = p4_savestr(t + 3);
324                         oldtmp = p4_savestr(s + 5);
325                 }
326                 if ((!diff_type || diff_type == ED_DIFF) &&
327                     first_command_line >= 0L &&
328                     strEQ(s, ".\n") ) {
329                         p_indent = indent;
330                         p_start = first_command_line;
331                         p_sline = fcl_line;
332                         retval = ED_DIFF;
333                         goto scan_exit;
334                 }
335                 if ((!diff_type || diff_type == UNI_DIFF) &&
336                     strnEQ(s, "@@ -", 4)) {
337                         if (!atol(s+3))
338                                 ok_to_create_file = TRUE;
339                         p_indent = indent;
340                         p_start = this_line;
341                         p_sline = p_input_line;
342                         retval = UNI_DIFF;
343                         goto scan_exit;
344                 }
345                 stars_this_line = strnEQ(s, "********", 8);
346                 if ((!diff_type || diff_type == CONTEXT_DIFF) &&
347                     stars_last_line &&
348                     strnEQ(s, "*** ", 4)) {
349                         if (!atol(s+4))
350                                 ok_to_create_file = TRUE;
351                         /*
352                          * If this is a new context diff the character just
353                          * before the newline is a '*'.
354                          */
355                         while (*s != '\n')
356                                 s++;
357                         p_indent = indent;
358                         p_start = previous_line;
359                         p_sline = p_input_line - 1;
360                         retval = (*(s-1) == '*' ?
361                             NEW_CONTEXT_DIFF : CONTEXT_DIFF);
362                         goto scan_exit;
363                 }
364                 if ((!diff_type || diff_type == NORMAL_DIFF) &&
365                     last_line_was_command &&
366                     (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) {
367                         p_start = previous_line;
368                         p_sline = p_input_line - 1;
369                         p_indent = indent;
370                         retval = NORMAL_DIFF;
371                         goto scan_exit;
372                 }
373         }
374  scan_exit:
375         if (no_filearg) {
376                 if (indtmp != Nullch)
377                         indname = fetchname(indtmp, strippath,
378                             ok_to_create_file);
379                 if (oldtmp != Nullch)
380                         oldname = fetchname(oldtmp, strippath,
381                             ok_to_create_file);
382                 if (newtmp != Nullch)
383                         newname = fetchname(newtmp, strippath,
384                             ok_to_create_file);
385                 if (index_first && indname)
386                         filearg[0] = savestr(indname);
387                 else if (oldname && newname) {
388                         if (strlen(oldname) < strlen(newname))
389                                 filearg[0] = savestr(oldname);
390                         else
391                                 filearg[0] = savestr(newname);
392                 } else if (indname)
393                         filearg[0] = savestr(indname);
394                 else if (oldname)
395                         filearg[0] = savestr(oldname);
396                 else if (newname)
397                         filearg[0] = savestr(newname);
398         }
399         if (bestguess) {
400                 free(bestguess);
401                 bestguess = Nullch;
402         }
403         if (filearg[0] != Nullch)
404                 bestguess = savestr(filearg[0]);
405         else if (indtmp != Nullch)
406                 bestguess = fetchname(indtmp, strippath, TRUE);
407         else {
408                 if (oldtmp != Nullch)
409                         oldname = fetchname(oldtmp, strippath, TRUE);
410                 if (newtmp != Nullch)
411                         newname = fetchname(newtmp, strippath, TRUE);
412                 if (oldname && newname) {
413                         if (strlen(oldname) < strlen(newname))
414                                 bestguess = savestr(oldname);
415                         else
416                                 bestguess = savestr(newname);
417                 }
418                 else if (oldname)
419                         bestguess = savestr(oldname);
420                 else if (newname)
421                         bestguess = savestr(newname);
422         }
423         if (indtmp != Nullch)
424                 free(indtmp);
425         if (oldtmp != Nullch)
426                 free(oldtmp);
427         if (newtmp != Nullch)
428                 free(newtmp);
429         if (indname != Nullch)
430                 free(indname);
431         if (oldname != Nullch)
432                 free(oldname);
433         if (newname != Nullch)
434                 free(newname);
435         return retval;
436 }
437
438 /*
439  * Remember where this patch ends so we know where to start up again.
440  */
441 void
442 next_intuit_at(long file_pos, long file_line)
443 {
444         p_base = file_pos;
445         p_bline = file_line;
446 }
447
448 /*
449  * Basically a verbose fseek() to the actual diff listing.
450  */
451 void
452 skip_to(long file_pos, long file_line)
453 {
454         size_t len;
455
456         assert(p_base <= file_pos);
457         if (verbose && p_base < file_pos) {
458                 Fseek(pfp, p_base, 0);
459                 say1("The text leading up to this was:\n--------------------------\n");
460                 while (ftell(pfp) < file_pos) {
461                         len = pgets(FALSE);
462                         assert(len != 0);
463                         say2("|%s", buf);
464                 }
465                 say1("--------------------------\n");
466         }
467         else
468                 Fseek(pfp, file_pos, 0);
469         p_input_line = file_line - 1;
470 }
471
472 /*
473  * Make this a function for better debugging.
474  */
475 static void
476 malformed(void)
477 {
478         fatal3("malformed patch at line %ld: %s", p_input_line, buf);
479                 /* about as informative as "Syntax error" in C */
480 }
481
482 /*
483  * True if the line has been discarded (i.e. it is a line saying
484  *  "\ No newline at end of file".)
485  */
486 static bool
487 remove_special_line(void)
488 {
489         int c;
490
491         c = fgetc(pfp);
492         if (c == '\\') {
493                 do {
494                         c = fgetc(pfp);
495                 } while (c != EOF && c != '\n');
496
497                 return TRUE;
498         }
499
500         if (c != EOF)
501                 fseek(pfp, -1, SEEK_CUR);
502
503         return FALSE;
504 }
505
506 /*
507  * True if there is more of the current diff listing to process.
508  */
509 bool
510 another_hunk(void)
511 {
512     Reg1 char *s;
513     size_t len;
514     Reg2 int context = 0;
515
516     while (p_end >= 0) {
517         if (p_end == p_efake)
518             p_end = p_bfake;            /* don't free twice */
519         else
520             free(p_line[p_end]);
521         p_end--;
522     }
523     assert(p_end == -1);
524     p_efake = -1;
525
526     p_max = hunkmax;                    /* gets reduced when --- found */
527     if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
528         long line_beginning = ftell(pfp);
529                                         /* file pos of the current line */
530         LINENUM repl_beginning = 0;     /* index of --- line */
531         Reg4 LINENUM fillcnt = 0;       /* #lines of missing ptrn or repl */
532         Reg5 LINENUM fillsrc;           /* index of first line to copy */
533         Reg6 LINENUM filldst;           /* index of first missing line */
534         bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */
535         Reg9 bool repl_could_be_missing = TRUE;
536                                         /* no + or ! lines in this hunk */
537         bool repl_missing = FALSE;      /* we are now backtracking */
538         long repl_backtrack_position = 0;
539                                         /* file pos of first repl line */
540         LINENUM repl_patch_line;        /* input line number for same */
541         Reg7 LINENUM ptrn_copiable = 0;
542                                         /* # of copiable lines in ptrn */
543
544         len = pgets(TRUE);
545         p_input_line++;
546         if (len == 0 || strnNE(buf, "********", 8)) {
547             next_intuit_at(line_beginning,p_input_line);
548             return FALSE;
549         }
550         p_context = 100;
551         p_hunk_beg = p_input_line + 1;
552         while (p_end < p_max) {
553             line_beginning = ftell(pfp);
554             len = pgets(TRUE);
555             p_input_line++;
556             if (len == 0) {
557                 if (p_max - p_end < 4)
558                     Strcpy(buf, "  \n");  /* assume blank lines got chopped */
559                 else {
560                     if (repl_beginning && repl_could_be_missing) {
561                         repl_missing = TRUE;
562                         goto hunk_done;
563                     }
564                     fatal1("unexpected end of file in patch\n");
565                 }
566             }
567             p_end++;
568             assert(p_end < hunkmax);
569             p_Char[p_end] = *buf;
570 #ifdef zilog
571             p_line[(short)p_end] = Nullch;
572 #else
573             p_line[p_end] = Nullch;
574 #endif
575             switch (*buf) {
576             case '*':
577                 if (strnEQ(buf, "********", 8)) {
578                     if (repl_beginning && repl_could_be_missing) {
579                         repl_missing = TRUE;
580                         goto hunk_done;
581                     }
582                     else
583                         fatal2("unexpected end of hunk at line %ld\n",
584                             p_input_line);
585                 }
586                 if (p_end != 0) {
587                     if (repl_beginning && repl_could_be_missing) {
588                         repl_missing = TRUE;
589                         goto hunk_done;
590                     }
591                     fatal3("unexpected *** at line %ld: %s", p_input_line, buf);
592                 }
593                 context = 0;
594                 p_line[p_end] = savestr(buf);
595                 if (out_of_mem) {
596                     p_end--;
597                     return FALSE;
598                 }
599                 for (s=buf; *s && !isdigit((unsigned char)*s); s++) ;
600                 if (!*s)
601                     malformed ();
602                 if (strnEQ(s,"0,0",3))
603                     strcpy(s,s+2);
604                 p_first = (LINENUM) atol(s);
605                 while (isdigit((unsigned char)*s)) s++;
606                 if (*s == ',') {
607                     for (; *s && !isdigit((unsigned char)*s); s++) ;
608                     if (!*s)
609                         malformed ();
610                     p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
611                 }
612                 else if (p_first)
613                     p_ptrn_lines = 1;
614                 else {
615                     p_ptrn_lines = 0;
616                     p_first = 1;
617                 }
618                 p_max = p_ptrn_lines + 6;       /* we need this much at least */
619                 while (p_max >= hunkmax)
620                     grow_hunkmax();
621                 p_max = hunkmax;
622                 break;
623             case '-':
624                 if (buf[1] == '-') {
625                     if (repl_beginning ||
626                         (p_end != p_ptrn_lines + 1 + (p_Char[p_end-1] == '\n')))
627                     {
628                         if (p_end == 1) {
629                             /* `old' lines were omitted - set up to fill */
630                             /* them in from 'new' context lines. */
631                             p_end = p_ptrn_lines + 1;
632                             fillsrc = p_end + 1;
633                             filldst = 1;
634                             fillcnt = p_ptrn_lines;
635                         }
636                         else {
637                             if (repl_beginning) {
638                                 if (repl_could_be_missing){
639                                     repl_missing = TRUE;
640                                     goto hunk_done;
641                                 }
642                                 fatal3(
643 "duplicate \"---\" at line %ld--check line numbers at line %ld\n",
644                                     p_input_line, p_hunk_beg + repl_beginning);
645                             }
646                             else {
647                                 fatal4(
648 "%s \"---\" at line %ld--check line numbers at line %ld\n",
649                                     (p_end <= p_ptrn_lines
650                                         ? "Premature"
651                                         : "Overdue" ),
652                                     p_input_line, p_hunk_beg);
653                             }
654                         }
655                     }
656                     repl_beginning = p_end;
657                     repl_backtrack_position = ftell(pfp);
658                     repl_patch_line = p_input_line;
659                     p_line[p_end] = savestr(buf);
660                     if (out_of_mem) {
661                         p_end--;
662                         return FALSE;
663                     }
664                     p_Char[p_end] = '=';
665                     for (s=buf; *s && !isdigit((unsigned char)*s); s++) ;
666                     if (!*s)
667                         malformed ();
668                     p_newfirst = (LINENUM) atol(s);
669                     while (isdigit((unsigned char)*s)) s++;
670                     if (*s == ',') {
671                         for (; *s && !isdigit((unsigned char)*s); s++) ;
672                         if (!*s)
673                             malformed ();
674                         p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1;
675                     }
676                     else if (p_newfirst)
677                         p_repl_lines = 1;
678                     else {
679                         p_repl_lines = 0;
680                         p_newfirst = 1;
681                     }
682                     p_max = p_repl_lines + p_end;
683                     if (p_max > MAXHUNKSIZE)
684                         fatal4("hunk too large (%ld lines) at line %ld: %s",
685                               p_max, p_input_line, buf);
686                     while (p_max >= hunkmax)
687                         grow_hunkmax();
688                     if (p_repl_lines != ptrn_copiable
689                      && (p_context != 0 || p_repl_lines != 1))
690                         repl_could_be_missing = FALSE;
691                     break;
692                 }
693                 goto change_line;
694             case '+':  case '!':
695                 repl_could_be_missing = FALSE;
696               change_line:
697                 if (buf[1] == '\n' && canonicalize)
698                     strcpy(buf+1," \n");
699                 if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' &&
700                   repl_beginning && repl_could_be_missing) {
701                     repl_missing = TRUE;
702                     goto hunk_done;
703                 }
704                 if (context >= 0) {
705                     if (context < p_context)
706                         p_context = context;
707                     context = -1000;
708                 }
709                 p_line[p_end] = savestr(buf+2);
710                 if (out_of_mem) {
711                     p_end--;
712                     return FALSE;
713                 }
714                 if (p_end == p_ptrn_lines)
715                 {
716                         if (remove_special_line()) {
717                                 int len;
718
719                                 len = strlen(p_line[p_end]) - 1;
720                                 (p_line[p_end])[len] = 0;
721                         }
722                 }
723                 break;
724             case '\t': case '\n':       /* assume the 2 spaces got eaten */
725                 if (repl_beginning && repl_could_be_missing &&
726                   (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) {
727                     repl_missing = TRUE;
728                     goto hunk_done;
729                 }
730                 p_line[p_end] = savestr(buf);
731                 if (out_of_mem) {
732                     p_end--;
733                     return FALSE;
734                 }
735                 if (p_end != p_ptrn_lines + 1) {
736                     ptrn_spaces_eaten |= (repl_beginning != 0);
737                     context++;
738                     if (!repl_beginning)
739                         ptrn_copiable++;
740                     p_Char[p_end] = ' ';
741                 }
742                 break;
743             case ' ':
744                 if (!isspace((unsigned char)buf[1]) &&
745                   repl_beginning && repl_could_be_missing) {
746                     repl_missing = TRUE;
747                     goto hunk_done;
748                 }
749                 context++;
750                 if (!repl_beginning)
751                     ptrn_copiable++;
752                 p_line[p_end] = savestr(buf+2);
753                 if (out_of_mem) {
754                     p_end--;
755                     return FALSE;
756                 }
757                 break;
758             default:
759                 if (repl_beginning && repl_could_be_missing) {
760                     repl_missing = TRUE;
761                     goto hunk_done;
762                 }
763                 malformed ();
764             }
765             /* set up p_len for strncmp() so we don't have to */
766             /* assume null termination */
767             if (p_line[p_end])
768                 p_len[p_end] = strlen(p_line[p_end]);
769             else
770                 p_len[p_end] = 0;
771         }
772
773     hunk_done:
774         if (p_end >=0 && !repl_beginning)
775             fatal2("no --- found in patch at line %ld\n", pch_hunk_beg());
776
777         if (repl_missing) {
778
779             /* reset state back to just after --- */
780             p_input_line = repl_patch_line;
781             for (p_end--; p_end > repl_beginning; p_end--)
782                 free(p_line[p_end]);
783             Fseek(pfp, repl_backtrack_position, 0);
784
785             /* redundant 'new' context lines were omitted - set */
786             /* up to fill them in from the old file context */
787             if (!p_context && p_repl_lines == 1) {
788                 p_repl_lines = 0;
789                 p_max--;
790             }
791             fillsrc = 1;
792             filldst = repl_beginning+1;
793             fillcnt = p_repl_lines;
794             p_end = p_max;
795         }
796         else if (!p_context && fillcnt == 1) {
797             /* the first hunk was a null hunk with no context */
798             /* and we were expecting one line -- fix it up. */
799             while (filldst < p_end) {
800                 p_line[filldst] = p_line[filldst+1];
801                 p_Char[filldst] = p_Char[filldst+1];
802                 p_len[filldst] = p_len[filldst+1];
803                 filldst++;
804             }
805 #if 0
806             repl_beginning--;           /* this doesn't need to be fixed */
807 #endif
808             p_end--;
809             p_first++;                  /* do append rather than insert */
810             fillcnt = 0;
811             p_ptrn_lines = 0;
812         }
813
814         if (diff_type == CONTEXT_DIFF &&
815           (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) {
816             if (verbose)
817                 say4("%s\n%s\n%s\n",
818 "(Fascinating--this is really a new-style context diff but without",
819 "the telltale extra asterisks on the *** line that usually indicate",
820 "the new style...)");
821             diff_type = NEW_CONTEXT_DIFF;
822         }
823
824         /* if there were omitted context lines, fill them in now */
825         if (fillcnt) {
826             p_bfake = filldst;          /* remember where not to free() */
827             p_efake = filldst + fillcnt - 1;
828             while (fillcnt-- > 0) {
829                 while (fillsrc <= p_end && p_Char[fillsrc] != ' ')
830                     fillsrc++;
831                 if (fillsrc > p_end)
832                     fatal2("replacement text or line numbers mangled in hunk at line %ld\n",
833                         p_hunk_beg);
834                 p_line[filldst] = p_line[fillsrc];
835                 p_Char[filldst] = p_Char[fillsrc];
836                 p_len[filldst] = p_len[fillsrc];
837                 fillsrc++; filldst++;
838             }
839             while (fillsrc <= p_end && fillsrc != repl_beginning &&
840               p_Char[fillsrc] != ' ')
841                 fillsrc++;
842 #ifdef DEBUGGING
843             if (debug & 64)
844                 printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
845                     fillsrc,filldst,repl_beginning,p_end+1);
846 #endif
847             assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
848             assert(filldst==p_end+1 || filldst==repl_beginning);
849         }
850
851         if (p_line[p_end] != NULL)
852         {
853                 if (remove_special_line()) {
854                         p_len[p_end] -= 1;
855                         (p_line[p_end])[p_len[p_end]] = 0;
856                 }
857         }
858     }
859     else if (diff_type == UNI_DIFF) {
860         long line_beginning = ftell(pfp);
861                                         /* file pos of the current line */
862         Reg4 LINENUM fillsrc;           /* index of old lines */
863         Reg5 LINENUM filldst;           /* index of new lines */
864         char ch;
865
866         len = pgets(TRUE);
867         p_input_line++;
868         if (len == 0 || strnNE(buf, "@@ -", 4)) {
869             next_intuit_at(line_beginning,p_input_line);
870             return FALSE;
871         }
872         s = buf+4;
873         if (!*s)
874             malformed ();
875         p_first = (LINENUM) atol(s);
876         while (isdigit((unsigned char)*s)) s++;
877         if (*s == ',') {
878             p_ptrn_lines = (LINENUM) atol(++s);
879             while (isdigit((unsigned char)*s)) s++;
880         } else
881             p_ptrn_lines = 1;
882         if (*s == ' ') s++;
883         if (*s != '+' || !*++s)
884             malformed ();
885         p_newfirst = (LINENUM) atol(s);
886         while (isdigit((unsigned char)*s)) s++;
887         if (*s == ',') {
888             p_repl_lines = (LINENUM) atol(++s);
889             while (isdigit((unsigned char)*s)) s++;
890         } else
891             p_repl_lines = 1;
892         if (*s == ' ') s++;
893         if (*s != '@')
894             malformed ();
895         if (!p_ptrn_lines)
896             p_first++;                  /* do append rather than insert */
897         p_max = p_ptrn_lines + p_repl_lines + 1;
898         while (p_max >= hunkmax)
899             grow_hunkmax();
900         fillsrc = 1;
901         filldst = fillsrc + p_ptrn_lines;
902         p_end = filldst + p_repl_lines;
903         Sprintf(buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1);
904         p_line[0] = savestr(buf);
905         if (out_of_mem) {
906             p_end = -1;
907             return FALSE;
908         }
909         p_Char[0] = '*';
910         Sprintf(buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1);
911         p_line[filldst] = savestr(buf);
912         if (out_of_mem) {
913             p_end = 0;
914             return FALSE;
915         }
916         p_Char[filldst++] = '=';
917         p_context = 100;
918         context = 0;
919         p_hunk_beg = p_input_line + 1;
920         while (fillsrc <= p_ptrn_lines || filldst <= p_end) {
921             line_beginning = ftell(pfp);
922             len = pgets(TRUE);
923             p_input_line++;
924             if (len == 0) {
925                 if (p_max - filldst < 3)
926                     Strcpy(buf, " \n");  /* assume blank lines got chopped */
927                 else {
928                     fatal1("unexpected end of file in patch\n");
929                 }
930             }
931             if (*buf == '\t' || *buf == '\n') {
932                 ch = ' ';               /* assume the space got eaten */
933                 s = savestr(buf);
934             }
935             else {
936                 ch = *buf;
937                 s = savestr(buf+1);
938             }
939             if (out_of_mem) {
940                 while (--filldst > p_ptrn_lines)
941                     free(p_line[filldst]);
942                 p_end = fillsrc-1;
943                 return FALSE;
944             }
945             switch (ch) {
946             case '-':
947                 if (fillsrc > p_ptrn_lines) {
948                     free(s);
949                     p_end = filldst-1;
950                     malformed ();
951                 }
952                 p_Char[fillsrc] = ch;
953                 p_line[fillsrc] = s;
954                 p_len[fillsrc++] = strlen(s);
955                 if (fillsrc > p_ptrn_lines) {
956                         if (remove_special_line()) {
957                                 p_len[fillsrc - 1] -= 1;
958                                 s[p_len[fillsrc - 1]] = 0;
959                         }
960                 }
961                 break;
962             case '=':
963                 ch = ' ';
964                 /* FALLTHROUGH */
965             case ' ':
966                 if (fillsrc > p_ptrn_lines) {
967                     free(s);
968                     while (--filldst > p_ptrn_lines)
969                         free(p_line[filldst]);
970                     p_end = fillsrc-1;
971                     malformed ();
972                 }
973                 context++;
974                 p_Char[fillsrc] = ch;
975                 p_line[fillsrc] = s;
976                 p_len[fillsrc++] = strlen(s);
977                 s = savestr(s);
978                 if (out_of_mem) {
979                     while (--filldst > p_ptrn_lines)
980                         free(p_line[filldst]);
981                     p_end = fillsrc-1;
982                     return FALSE;
983                 }
984                 /* FALLTHROUGH */
985             case '+':
986                 if (filldst > p_end) {
987                     free(s);
988                     while (--filldst > p_ptrn_lines)
989                         free(p_line[filldst]);
990                     p_end = fillsrc-1;
991                     malformed ();
992                 }
993                 p_Char[filldst] = ch;
994                 p_line[filldst] = s;
995                 p_len[filldst++] = strlen(s);
996                 if (fillsrc > p_ptrn_lines) {
997                         if (remove_special_line()) {
998                                 p_len[filldst - 1] -= 1;
999                                 s[p_len[filldst - 1]] = 0;
1000                         }
1001                 }
1002                 break;
1003             default:
1004                 p_end = filldst;
1005                 malformed ();
1006             }
1007             if (ch != ' ' && context > 0) {
1008                 if (context < p_context)
1009                     p_context = context;
1010                 context = -1000;
1011             }
1012         }/* while */
1013     }
1014     else {                              /* normal diff--fake it up */
1015         char hunk_type;
1016         Reg3 int i;
1017         LINENUM min, max;
1018         long line_beginning = ftell(pfp);
1019
1020         p_context = 0;
1021         len = pgets(TRUE);
1022         p_input_line++;
1023         if (len == 0 || !isdigit((unsigned char)*buf)) {
1024             next_intuit_at(line_beginning,p_input_line);
1025             return FALSE;
1026         }
1027         p_first = (LINENUM)atol(buf);
1028         for (s=buf; isdigit((unsigned char)*s); s++) ;
1029         if (*s == ',') {
1030             p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
1031             while (isdigit((unsigned char)*s)) s++;
1032         }
1033         else
1034             p_ptrn_lines = (*s != 'a');
1035         hunk_type = *s;
1036         if (hunk_type == 'a')
1037             p_first++;                  /* do append rather than insert */
1038         min = (LINENUM)atol(++s);
1039         for (; isdigit((unsigned char)*s); s++) ;
1040         if (*s == ',')
1041             max = (LINENUM)atol(++s);
1042         else
1043             max = min;
1044         if (hunk_type == 'd')
1045             min++;
1046         p_end = p_ptrn_lines + 1 + max - min + 1;
1047         if (p_end > MAXHUNKSIZE)
1048             fatal4("hunk too large (%ld lines) at line %ld: %s",
1049                   p_end, p_input_line, buf);
1050         while (p_end >= hunkmax)
1051             grow_hunkmax();
1052         p_newfirst = min;
1053         p_repl_lines = max - min + 1;
1054         Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
1055         p_line[0] = savestr(buf);
1056         if (out_of_mem) {
1057             p_end = -1;
1058             return FALSE;
1059         }
1060         p_Char[0] = '*';
1061         for (i=1; i<=p_ptrn_lines; i++) {
1062             len = pgets(TRUE);
1063             p_input_line++;
1064             if (len == 0)
1065                 fatal2("unexpected end of file in patch at line %ld\n",
1066                   p_input_line);
1067             if (*buf != '<')
1068                 fatal2("< expected at line %ld of patch\n", p_input_line);
1069             p_line[i] = savestr(buf+2);
1070             if (out_of_mem) {
1071                 p_end = i-1;
1072                 return FALSE;
1073             }
1074             p_len[i] = strlen(p_line[i]);
1075             p_Char[i] = '-';
1076         }
1077
1078         if (remove_special_line()) {
1079                 p_len[i-1] -= 1;
1080                 (p_line[i-1])[p_len[i-1]] = 0;
1081         }
1082
1083         if (hunk_type == 'c') {
1084             len = pgets(TRUE);
1085             p_input_line++;
1086             if (len == 0)
1087                 fatal2("unexpected end of file in patch at line %ld\n",
1088                     p_input_line);
1089             if (*buf != '-')
1090                 fatal2("--- expected at line %ld of patch\n", p_input_line);
1091         }
1092         Sprintf(buf, "--- %ld,%ld\n", min, max);
1093         p_line[i] = savestr(buf);
1094         if (out_of_mem) {
1095             p_end = i-1;
1096             return FALSE;
1097         }
1098         p_Char[i] = '=';
1099         for (i++; i<=p_end; i++) {
1100             len = pgets(TRUE);
1101             p_input_line++;
1102             if (len == 0)
1103                 fatal2("unexpected end of file in patch at line %ld\n",
1104                     p_input_line);
1105             if (*buf != '>')
1106                 fatal2("> expected at line %ld of patch\n", p_input_line);
1107             p_line[i] = savestr(buf+2);
1108             if (out_of_mem) {
1109                 p_end = i-1;
1110                 return FALSE;
1111             }
1112             p_len[i] = strlen(p_line[i]);
1113             p_Char[i] = '+';
1114         }
1115
1116         if (remove_special_line()) {
1117                 p_len[i-1] -= 1;
1118                 (p_line[i-1])[p_len[i-1]] = 0;
1119         }
1120     }
1121     if (reverse)                        /* backwards patch? */
1122         if (!pch_swap())
1123             say1("Not enough memory to swap next hunk!\n");
1124 #ifdef DEBUGGING
1125     if (debug & 2) {
1126         int i;
1127         char special;
1128
1129         for (i=0; i <= p_end; i++) {
1130             if (i == p_ptrn_lines)
1131                 special = '^';
1132             else
1133                 special = ' ';
1134             fprintf(stderr, "%3d %c %c %s", i, p_Char[i], special, p_line[i]);
1135             Fflush(stderr);
1136         }
1137     }
1138 #endif
1139     if (p_end+1 < hunkmax)      /* paranoia reigns supreme... */
1140         p_Char[p_end+1] = '^';  /* add a stopper for apply_hunk */
1141     return TRUE;
1142 }
1143
1144 /*
1145  * Input a line from the patch file.
1146  * Worry about indentation if do_indent is true.
1147  * The line is read directly into the buf global variable which
1148  * is resized if necessary in order to hold the complete line.
1149  * Returns the number of characters read including the terminating
1150  * '\n', if any.
1151  */
1152 size_t
1153 pgets(bool do_indent)
1154 {
1155         char *line;
1156         size_t len;
1157         int indent = 0, skipped = 0;
1158
1159         line = fgetln(pfp, &len);
1160         if (line != Nullch) {
1161                 if (len + 1 > buf_size) {
1162                         while (len + 1 > buf_size)
1163                                 buf_size *= 2;
1164                         free(buf);
1165                         buf = malloc(buf_size);
1166                         if (buf == Nullch)
1167                                 fatal1("out of memory\n");
1168                 }
1169                 if (do_indent == TRUE && p_indent) {
1170                         for (;
1171                             indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X');
1172                             line++, skipped++) {
1173                                 if (*line == '\t')
1174                                         indent += 8 - (indent %7);
1175                                 else
1176                                         indent++;
1177                         }
1178                 }
1179                 Strncpy(buf, line, len - skipped);
1180                 buf[len - skipped] = '\0';
1181         }
1182         return len;
1183 }
1184
1185 /*
1186  * Reverse the old and new portions of the current hunk.
1187  */
1188 bool
1189 pch_swap(void)
1190 {
1191         char **tp_line;         /* the text of the hunk */
1192         short *tp_len;          /* length of each line */
1193         char *tp_char;          /* +, -, and ! */
1194         Reg1 LINENUM i;
1195         Reg2 LINENUM n;
1196         bool blankline = FALSE;
1197         Reg3 char *s;
1198
1199         i = p_first;
1200         p_first = p_newfirst;
1201         p_newfirst = i;
1202
1203         /* make a scratch copy */
1204
1205         tp_line = p_line;
1206         tp_len = p_len;
1207         tp_char = p_Char;
1208         p_line = Null(char**);  /* force set_hunkmax to allocate again */
1209         p_len = Null(short*);
1210         p_Char = Nullch;
1211         set_hunkmax();
1212         if (p_line == Null(char**) || p_len == Null(short*) || p_Char == Nullch) {
1213 #ifndef lint
1214                 if (p_line == Null(char**))
1215                         free((char*)p_line);
1216                 p_line = tp_line;
1217                 if (p_len == Null(short*))
1218                         free((char*)p_len);
1219                 p_len = tp_len;
1220 #endif
1221                 if (p_Char == Nullch)
1222                         free((char*)p_Char);
1223                 p_Char = tp_char;
1224                 return FALSE;           /* not enough memory to swap hunk! */
1225         }
1226
1227         /* now turn the new into the old */
1228
1229         i = p_ptrn_lines + 1;
1230         if (tp_char[i] == '\n') {       /* account for possible blank line */
1231                 blankline = TRUE;
1232                 i++;
1233         }
1234         if (p_efake >= 0) {             /* fix non-freeable ptr range */
1235                 if (p_efake <= i)
1236                         n = p_end - i + 1;
1237                 else
1238                         n = -i;
1239                 p_efake += n;
1240                 p_bfake += n;
1241         }
1242         for (n=0; i <= p_end; i++,n++) {
1243                 p_line[n] = tp_line[i];
1244                 p_Char[n] = tp_char[i];
1245                 if (p_Char[n] == '+')
1246                         p_Char[n] = '-';
1247                 p_len[n] = tp_len[i];
1248         }
1249         if (blankline) {
1250                 i = p_ptrn_lines + 1;
1251                 p_line[n] = tp_line[i];
1252                 p_Char[n] = tp_char[i];
1253                 p_len[n] = tp_len[i];
1254                 n++;
1255         }
1256         assert(p_Char[0] == '=');
1257         p_Char[0] = '*';
1258         for (s=p_line[0]; *s; s++)
1259                 if (*s == '-')
1260                         *s = '*';
1261
1262         /* now turn the old into the new */
1263
1264         assert(tp_char[0] == '*');
1265         tp_char[0] = '=';
1266         for (s=tp_line[0]; *s; s++)
1267                 if (*s == '*')
1268                         *s = '-';
1269         for (i=0; n <= p_end; i++,n++) {
1270                 p_line[n] = tp_line[i];
1271                 p_Char[n] = tp_char[i];
1272                 if (p_Char[n] == '-')
1273                         p_Char[n] = '+';
1274                 p_len[n] = tp_len[i];
1275         }
1276         assert(i == p_ptrn_lines + 1);
1277         i = p_ptrn_lines;
1278         p_ptrn_lines = p_repl_lines;
1279         p_repl_lines = i;
1280 #ifndef lint
1281         if (tp_line == Null(char**))
1282                 free((char*)tp_line);
1283         if (tp_len == Null(short*))
1284                 free((char*)tp_len);
1285 #endif
1286         if (tp_char == Nullch)
1287                 free((char*)tp_char);
1288         return TRUE;
1289 }
1290
1291 /*
1292  * Return the specified line position in the old file of the old context.
1293  */
1294 LINENUM
1295 pch_first(void)
1296 {
1297         return p_first;
1298 }
1299
1300 /*
1301  * Return the number of lines of old context.
1302  */
1303 LINENUM
1304 pch_ptrn_lines(void)
1305 {
1306         return p_ptrn_lines;
1307 }
1308
1309 /*
1310  * Return the probable line position in the new file of the first line.
1311  */
1312 LINENUM
1313 pch_newfirst(void)
1314 {
1315         return p_newfirst;
1316 }
1317
1318 /*
1319  * Return the number of lines in the replacement text including context.
1320  */
1321 LINENUM
1322 pch_repl_lines(void)
1323 {
1324         return p_repl_lines;
1325 }
1326
1327 /*
1328  * Return the number of lines in the whole hunk.
1329  */
1330 LINENUM
1331 pch_end(void)
1332 {
1333         return p_end;
1334 }
1335
1336 /*
1337  * Return the number of context lines before the first changed line.
1338  */
1339 LINENUM
1340 pch_context(void)
1341 {
1342         return p_context;
1343 }
1344
1345 /*
1346  * Return the length of a particular patch line.
1347  */
1348 short
1349 pch_line_len(LINENUM line)
1350 {
1351         return p_len[line];
1352 }
1353
1354 /*
1355  * Return the control character (+, -, *, !, etc) for a patch line.
1356  */
1357 char
1358 pch_char(LINENUM line)
1359 {
1360         return p_Char[line];
1361 }
1362
1363 /*
1364  * Return a pointer to a particular patch line.
1365  */
1366 char *
1367 pfetch(LINENUM line)
1368 {
1369         return p_line[line];
1370 }
1371
1372 /*
1373  * Return where in the patch file this hunk began, for error messages.
1374  */
1375 LINENUM
1376 pch_hunk_beg(void)
1377 {
1378         return p_hunk_beg;
1379 }
1380
1381 /*
1382  * Apply an ed script by feeding ed itself.
1383  */
1384 void
1385 do_ed_script(void)
1386 {
1387         Reg1 char *t;
1388         Reg2 long beginning_of_this_line;
1389         Reg3 bool this_line_is_command = FALSE;
1390         Reg4 FILE *pipefp;
1391
1392         if (!skip_rest_of_patch) {
1393                 Unlink(TMPOUTNAME);
1394                 copy_file(filearg[0], TMPOUTNAME);
1395                 if (verbose)
1396                         Sprintf(buf, "/bin/ed %s", TMPOUTNAME);
1397                 else
1398                         Sprintf(buf, "/bin/ed - %s", TMPOUTNAME);
1399                 pipefp = popen(buf, "w");
1400         }
1401         for (;;) {
1402                 beginning_of_this_line = ftell(pfp);
1403                 if (pgets(TRUE) == 0) {
1404                         next_intuit_at(beginning_of_this_line, p_input_line);
1405                         break;
1406                 }
1407                 p_input_line++;
1408                 for (t=buf; isdigit((unsigned char)*t) || *t == ','; t++)
1409                         ;
1410                 this_line_is_command = (isdigit((unsigned char)*buf) &&
1411                     (*t == 'd' || *t == 'c' || *t == 'a') );
1412                 if (this_line_is_command) {
1413                         if (!skip_rest_of_patch)
1414                                 fputs(buf, pipefp);
1415                         if (*t != 'd') {
1416                                 while (pgets(TRUE) != 0) {
1417                                         p_input_line++;
1418                                         if (!skip_rest_of_patch)
1419                                                 fputs(buf, pipefp);
1420                                         if (strEQ(buf, ".\n"))
1421                                                 break;
1422                                 }
1423                         }
1424                 }
1425                 else {
1426                         next_intuit_at(beginning_of_this_line,p_input_line);
1427                         break;
1428                 }
1429         }
1430         if (skip_rest_of_patch)
1431                 return;
1432         fprintf(pipefp, "w\n");
1433         fprintf(pipefp, "q\n");
1434         Fflush(pipefp);
1435         Pclose(pipefp);
1436         ignore_signals();
1437         if (move_file(TMPOUTNAME, outname) < 0) {
1438                 toutkeep = TRUE;
1439                 chmod(TMPOUTNAME, filemode);
1440         }
1441         else
1442                 chmod(outname, filemode);
1443         set_signals(1);
1444 }