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