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