pch.c revision 276807
1139749Simp
2113584Ssimokawa/*-
3103285Sikob * Copyright 1986, Larry Wall
4103285Sikob *
5103285Sikob * Redistribution and use in source and binary forms, with or without
6103285Sikob * modification, are permitted provided that the following condition is met:
7103285Sikob * 1. Redistributions of source code must retain the above copyright notice,
8103285Sikob * this condition and the following disclaimer.
9103285Sikob *
10103285Sikob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
11103285Sikob * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
12103285Sikob * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
13103285Sikob * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
14103285Sikob * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16103285Sikob * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17106802Ssimokawa * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
18103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
19103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
20103285Sikob * SUCH DAMAGE.
21103285Sikob *
22103285Sikob * patch - a program to apply diffs to original files
23103285Sikob *
24103285Sikob * -C option added in 1998, original code by Marc Espie, based on FreeBSD
25103285Sikob * behaviour
26103285Sikob *
27103285Sikob * $OpenBSD: pch.c,v 1.43 2014/11/18 17:03:35 tobias Exp $
28103285Sikob * $FreeBSD: stable/10/usr.bin/patch/pch.c 276807 2015-01-08 03:44:54Z pfg $
29103285Sikob */
30103285Sikob
31103285Sikob#include <sys/types.h>
32103285Sikob#include <sys/stat.h>
33103285Sikob
34103285Sikob#include <ctype.h>
35103285Sikob#include <libgen.h>
36103285Sikob#include <limits.h>
37106802Ssimokawa#include <stdio.h>
38103285Sikob#include <stdlib.h>
39103285Sikob#include <string.h>
40103285Sikob#include <unistd.h>
41103285Sikob
42103285Sikob#include "common.h"
43103285Sikob#include "util.h"
44103285Sikob#include "pch.h"
45103285Sikob#include "pathnames.h"
46103285Sikob
47103285Sikob/* Patch (diff listing) abstract type. */
48103285Sikob
49103285Sikobstatic off_t	p_filesize;	/* size of the patch file */
50169123Ssimokawastatic LINENUM	p_first;	/* 1st line number */
51103285Sikobstatic LINENUM	p_newfirst;	/* 1st line number of replacement */
52103285Sikobstatic LINENUM	p_ptrn_lines;	/* # lines in pattern */
53103285Sikobstatic LINENUM	p_repl_lines;	/* # lines in replacement text */
54113584Ssimokawastatic LINENUM	p_end = -1;	/* last line in hunk */
55170374Ssimokawastatic LINENUM	p_max;		/* max allowed value of p_end */
56103285Sikobstatic LINENUM	p_context = 3;	/* # of context lines */
57103285Sikobstatic LINENUM	p_input_line = 0;	/* current line # from patch file */
58103285Sikobstatic char	**p_line = NULL;/* the text of the hunk */
59127468Ssimokawastatic unsigned short	*p_len = NULL; /* length of each line */
60117067Ssimokawastatic char	*p_char = NULL;	/* +, -, and ! */
61117067Ssimokawastatic int	hunkmax = INITHUNKMAX;	/* size of above arrays to begin with */
62117067Ssimokawastatic int	p_indent;	/* indent to patch */
63127468Ssimokawastatic off_t	p_base;		/* where to intuit this time */
64127468Ssimokawastatic LINENUM	p_bline;	/* line # of p_base */
65127468Ssimokawastatic off_t	p_start;	/* where intuit found a patch */
66127468Ssimokawastatic LINENUM	p_sline;	/* and the line number for it */
67127468Ssimokawastatic LINENUM	p_hunk_beg;	/* line number of current hunk */
68127468Ssimokawastatic LINENUM	p_efake = -1;	/* end of faked up lines--don't free */
69127468Ssimokawastatic LINENUM	p_bfake = -1;	/* beg of faked up lines */
70127468Ssimokawastatic FILE	*pfp = NULL;	/* patch file pointer */
71103285Sikobstatic char	*bestguess = NULL;	/* guess at correct filename */
72103285Sikob
73113584Ssimokawastatic void	grow_hunkmax(void);
74103285Sikobstatic int	intuit_diff_type(void);
75103285Sikobstatic void	next_intuit_at(off_t, LINENUM);
76103285Sikobstatic void	skip_to(off_t, LINENUM);
77127468Ssimokawastatic size_t	pgets(bool _do_indent);
78103285Sikobstatic char	*best_name(const struct file_name *, bool);
79103285Sikobstatic char	*posix_name(const struct file_name *, bool);
80106802Ssimokawastatic size_t	num_components(const char *);
81169123Ssimokawastatic LINENUM	strtolinenum(char *, char **);
82170400Ssimokawa
83169123Ssimokawa/*
84169123Ssimokawa * Prepare to look for the next patch in the patch file.
85169123Ssimokawa */
86170400Ssimokawavoid
87170400Ssimokaware_patch(void)
88170400Ssimokawa{
89169123Ssimokawa	p_first = 0;
90103285Sikob	p_newfirst = 0;
91103285Sikob	p_ptrn_lines = 0;
92113584Ssimokawa	p_repl_lines = 0;
93103285Sikob	p_end = (LINENUM) - 1;
94103285Sikob	p_max = 0;
95113584Ssimokawa	p_indent = 0;
96103285Sikob}
97103285Sikob
98170374Ssimokawa/*
99103285Sikob * Open the patch file at the beginning of time.
100103285Sikob */
101103285Sikobvoid
102103285Sikobopen_patch_file(const char *filename)
103103285Sikob{
104103285Sikob	struct stat filestat;
105113584Ssimokawa	int nr, nw;
106116376Ssimokawa
107124378Ssimokawa	if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
108129585Sdfr		pfp = fopen(TMPPATNAME, "w");
109103285Sikob		if (pfp == NULL)
110103285Sikob			pfatal("can't create %s", TMPPATNAME);
111170374Ssimokawa		while ((nr = fread(buf, 1, buf_size, stdin)) > 0) {
112170374Ssimokawa			nw = fwrite(buf, 1, nr, pfp);
113170374Ssimokawa			if (nr != nw)
114170374Ssimokawa				pfatal("write error to %s", TMPPATNAME);
115170374Ssimokawa		}
116170374Ssimokawa		if (ferror(pfp) || fclose(pfp))
117170374Ssimokawa			pfatal("can't write %s", TMPPATNAME);
118170374Ssimokawa		filename = TMPPATNAME;
119170374Ssimokawa	}
120170374Ssimokawa	pfp = fopen(filename, "r");
121170374Ssimokawa	if (pfp == NULL)
122170374Ssimokawa		pfatal("patch file %s not found", filename);
123170374Ssimokawa	if (fstat(fileno(pfp), &filestat))
124170374Ssimokawa		pfatal("can't stat %s", filename);
125170374Ssimokawa	p_filesize = filestat.st_size;
126170374Ssimokawa	next_intuit_at(0, 1L);	/* start at the beginning */
127170374Ssimokawa	set_hunkmax();
128103285Sikob}
129103285Sikob
130103285Sikob/*
131103285Sikob * Make sure our dynamically realloced tables are malloced to begin with.
132103285Sikob */
133103285Sikobvoid
134103285Sikobset_hunkmax(void)
135103285Sikob{
136124169Ssimokawa	if (p_line == NULL)
137124169Ssimokawa		p_line = malloc(hunkmax * sizeof(char *));
138124169Ssimokawa	if (p_len == NULL)
139124169Ssimokawa		p_len = malloc(hunkmax * sizeof(unsigned short));
140124169Ssimokawa	if (p_char == NULL)
141124169Ssimokawa		p_char = malloc(hunkmax * sizeof(char));
142124169Ssimokawa}
143124169Ssimokawa
144129585Sdfr/*
145129585Sdfr * Enlarge the arrays containing the current hunk of patch.
146124169Ssimokawa */
147124169Ssimokawastatic void
148124169Ssimokawagrow_hunkmax(void)
149124169Ssimokawa{
150113584Ssimokawa	int new_hunkmax = hunkmax * 2;
151129585Sdfr
152113584Ssimokawa	if (p_line == NULL || p_len == NULL || p_char == NULL)
153124169Ssimokawa		fatal("Internal memory allocation error\n");
154124169Ssimokawa
155124169Ssimokawa	p_line = reallocf(p_line, new_hunkmax * sizeof(char *));
156124169Ssimokawa	p_len = reallocf(p_len, new_hunkmax * sizeof(unsigned short));
157113584Ssimokawa	p_char = reallocf(p_char, new_hunkmax * sizeof(char));
158124169Ssimokawa
159124169Ssimokawa	if (p_line != NULL && p_len != NULL && p_char != NULL) {
160129585Sdfr		hunkmax = new_hunkmax;
161129585Sdfr		return;
162129585Sdfr	}
163129585Sdfr
164124169Ssimokawa	if (!using_plan_a)
165124169Ssimokawa		fatal("out of memory\n");
166124169Ssimokawa	out_of_mem = true;	/* whatever is null will be allocated again */
167170374Ssimokawa				/* from within plan_a(), of all places */
168170374Ssimokawa}
169170374Ssimokawa
170103285Sikob/* True if the remainder of the patch file contains a diff of some sort. */
171103285Sikob
172103285Sikobbool
173103285Sikobthere_is_another_patch(void)
174103285Sikob{
175103285Sikob	bool exists = false;
176103285Sikob
177103285Sikob	if (p_base != 0 && p_base >= p_filesize) {
178103285Sikob		if (verbose)
179112523Ssimokawa			say("done\n");
180103285Sikob		return false;
181103285Sikob	}
182103285Sikob	if (verbose)
183103285Sikob		say("Hmm...");
184103285Sikob	diff_type = intuit_diff_type();
185103285Sikob	if (!diff_type) {
186103285Sikob		if (p_base != 0) {
187103285Sikob			if (verbose)
188103285Sikob				say("  Ignoring the trailing garbage.\ndone\n");
189103285Sikob		} else
190103285Sikob			say("  I can't seem to find a patch in there anywhere.\n");
191103285Sikob		return false;
192103285Sikob	}
193103285Sikob	if (verbose)
194103285Sikob		say("  %sooks like %s to me...\n",
195103285Sikob		    (p_base == 0 ? "L" : "The next patch l"),
196103285Sikob		    diff_type == UNI_DIFF ? "a unified diff" :
197103285Sikob		    diff_type == CONTEXT_DIFF ? "a context diff" :
198103285Sikob		diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
199103285Sikob		    diff_type == NORMAL_DIFF ? "a normal diff" :
200103285Sikob		    "an ed script");
201103285Sikob	if (p_indent && verbose)
202103285Sikob		say("(Patch is indented %d space%s.)\n", p_indent,
203103285Sikob		    p_indent == 1 ? "" : "s");
204103285Sikob	skip_to(p_start, p_sline);
205103285Sikob	while (filearg[0] == NULL) {
206103285Sikob		if (force || batch) {
207103285Sikob			say("No file to patch.  Skipping...\n");
208113584Ssimokawa			filearg[0] = xstrdup(bestguess);
209103285Sikob			skip_rest_of_patch = true;
210103285Sikob			return true;
211103285Sikob		}
212103285Sikob		ask("File to patch: ");
213103285Sikob		if (*buf != '\n') {
214103285Sikob			free(bestguess);
215103285Sikob			bestguess = xstrdup(buf);
216103285Sikob			filearg[0] = fetchname(buf, &exists, 0);
217103285Sikob		}
218103285Sikob		if (!exists) {
219103285Sikob			ask("No file found--skip this patch? [n] ");
220103285Sikob			if (*buf != 'y')
221103285Sikob				continue;
222103285Sikob			if (verbose)
223103285Sikob				say("Skipping patch...\n");
224103285Sikob			free(filearg[0]);
225103285Sikob			filearg[0] = fetchname(bestguess, &exists, 0);
226103285Sikob			skip_rest_of_patch = true;
227103285Sikob			return true;
228103285Sikob		}
229103285Sikob	}
230103285Sikob	return true;
231103285Sikob}
232103285Sikob
233103285Sikobstatic void
234103285Sikobp4_fetchname(struct file_name *name, char *str)
235103285Sikob{
236103285Sikob	char *t, *h;
237103285Sikob
238103285Sikob	/* Skip leading whitespace. */
239103285Sikob	while (isspace((unsigned char)*str))
240103285Sikob		str++;
241103285Sikob
242103285Sikob	/* Remove the file revision number. */
243103285Sikob	for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++)
244103285Sikob		if (*t == '#')
245103285Sikob			h = t;
246103285Sikob	if (h != NULL)
247103285Sikob		*h = '\0';
248103285Sikob
249103285Sikob	name->path = fetchname(str, &name->exists, strippath);
250103285Sikob}
251103285Sikob
252103285Sikob/* Determine what kind of diff is in the remaining part of the patch file. */
253103285Sikob
254103285Sikobstatic int
255103285Sikobintuit_diff_type(void)
256103285Sikob{
257103285Sikob	off_t	this_line = 0, previous_line;
258103285Sikob	off_t	first_command_line = -1;
259103285Sikob	LINENUM	fcl_line = -1;
260103285Sikob	bool	last_line_was_command = false, this_is_a_command = false;
261103285Sikob	bool	stars_last_line = false, stars_this_line = false;
262103285Sikob	char	*s, *t;
263103285Sikob	int	indent, retval;
264103285Sikob	struct file_name names[MAX_FILE];
265103285Sikob
266103285Sikob	memset(names, 0, sizeof(names));
267103285Sikob	ok_to_create_file = false;
268103285Sikob	fseeko(pfp, p_base, SEEK_SET);
269103285Sikob	p_input_line = p_bline - 1;
270103285Sikob	for (;;) {
271103285Sikob		previous_line = this_line;
272170374Ssimokawa		last_line_was_command = this_is_a_command;
273129585Sdfr		stars_last_line = stars_this_line;
274129585Sdfr		this_line = ftello(pfp);
275103285Sikob		indent = 0;
276129585Sdfr		p_input_line++;
277103285Sikob		if (pgets(false) == 0) {
278103285Sikob			if (first_command_line >= 0) {
279103285Sikob				/* nothing but deletes!? */
280103285Sikob				p_start = first_command_line;
281103285Sikob				p_sline = fcl_line;
282103285Sikob				retval = ED_DIFF;
283103285Sikob				goto scan_exit;
284103285Sikob			} else {
285103285Sikob				p_start = this_line;
286103285Sikob				p_sline = p_input_line;
287103285Sikob				retval = 0;
288129585Sdfr				goto scan_exit;
289103285Sikob			}
290103285Sikob		}
291103285Sikob		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
292103285Sikob			if (*s == '\t')
293129585Sdfr				indent += 8 - (indent % 8);
294103285Sikob			else
295103285Sikob				indent++;
296103285Sikob		}
297103285Sikob		for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
298103285Sikob			;
299103285Sikob		this_is_a_command = (isdigit((unsigned char)*s) &&
300103285Sikob		    (*t == 'd' || *t == 'c' || *t == 'a'));
301103285Sikob		if (first_command_line < 0 && this_is_a_command) {
302103285Sikob			first_command_line = this_line;
303103285Sikob			fcl_line = p_input_line;
304109280Ssimokawa			p_indent = indent;	/* assume this for now */
305103285Sikob		}
306107653Ssimokawa		if (!stars_last_line && strnEQ(s, "*** ", 4))
307103285Sikob			names[OLD_FILE].path = fetchname(s + 4,
308132432Ssimokawa			    &names[OLD_FILE].exists, strippath);
309188509Ssbruno		else if (strnEQ(s, "--- ", 4))
310188509Ssbruno			names[NEW_FILE].path = fetchname(s + 4,
311103285Sikob			    &names[NEW_FILE].exists, strippath);
312103285Sikob		else if (strnEQ(s, "+++ ", 4))
313103285Sikob			/* pretend it is the old name */
314103285Sikob			names[OLD_FILE].path = fetchname(s + 4,
315129585Sdfr			    &names[OLD_FILE].exists, strippath);
316106790Ssimokawa		else if (strnEQ(s, "Index:", 6))
317103285Sikob			names[INDEX_FILE].path = fetchname(s + 6,
318129585Sdfr			    &names[INDEX_FILE].exists, strippath);
319108500Ssimokawa		else if (strnEQ(s, "Prereq:", 7)) {
320103285Sikob			for (t = s + 7; isspace((unsigned char)*t); t++)
321103285Sikob				;
322108500Ssimokawa			revision = xstrdup(t);
323108500Ssimokawa			for (t = revision;
324108500Ssimokawa			     *t && !isspace((unsigned char)*t); t++)
325103285Sikob				;
326103285Sikob			*t = '\0';
327108500Ssimokawa			if (*revision == '\0') {
328103285Sikob				free(revision);
329103285Sikob				revision = NULL;
330103285Sikob			}
331109280Ssimokawa		} else if (strnEQ(s, "==== ", 5)) {
332103285Sikob			/* Perforce-style diffs. */
333108500Ssimokawa			if ((t = strstr(s + 5, " - ")) != NULL)
334132432Ssimokawa				p4_fetchname(&names[NEW_FILE], t + 3);
335188509Ssbruno			p4_fetchname(&names[OLD_FILE], s + 5);
336108527Ssimokawa		}
337109280Ssimokawa		if ((!diff_type || diff_type == ED_DIFF) &&
338108527Ssimokawa		    first_command_line >= 0 &&
339108527Ssimokawa		    strEQ(s, ".\n")) {
340108500Ssimokawa			p_indent = indent;
341108500Ssimokawa			p_start = first_command_line;
342108500Ssimokawa			p_sline = fcl_line;
343108500Ssimokawa			retval = ED_DIFF;
344108500Ssimokawa			goto scan_exit;
345132432Ssimokawa		}
346188509Ssbruno		if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
347108500Ssimokawa			if (strnEQ(s + 4, "0,0", 3))
348109280Ssimokawa				ok_to_create_file = true;
349108500Ssimokawa			p_indent = indent;
350108500Ssimokawa			p_start = this_line;
351108500Ssimokawa			p_sline = p_input_line;
352188509Ssbruno			retval = UNI_DIFF;
353108500Ssimokawa			goto scan_exit;
354188509Ssbruno		}
355188509Ssbruno		stars_this_line = strnEQ(s, "********", 8);
356108500Ssimokawa		if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
357103285Sikob		    strnEQ(s, "*** ", 4)) {
358103285Sikob			if (strtolinenum(s + 4, &s) == 0)
359103285Sikob				ok_to_create_file = true;
360103285Sikob			/*
361130585Sphk			 * If this is a new context diff the character just
362103285Sikob			 * at the end of the line is a '*'.
363103285Sikob			 */
364103285Sikob			while (*s && *s != '\n')
365103285Sikob				s++;
366103285Sikob			p_indent = indent;
367103285Sikob			p_start = previous_line;
368129585Sdfr			p_sline = p_input_line - 1;
369103285Sikob			retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
370103285Sikob			goto scan_exit;
371103285Sikob		}
372103285Sikob		if ((!diff_type || diff_type == NORMAL_DIFF) &&
373103285Sikob		    last_line_was_command &&
374103285Sikob		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
375103285Sikob			p_start = previous_line;
376103285Sikob			p_sline = p_input_line - 1;
377103285Sikob			p_indent = indent;
378103285Sikob			retval = NORMAL_DIFF;
379103285Sikob			goto scan_exit;
380103285Sikob		}
381103285Sikob	}
382103285Sikobscan_exit:
383103285Sikob	if (retval == UNI_DIFF) {
384103285Sikob		/* unswap old and new */
385103285Sikob		struct file_name tmp = names[OLD_FILE];
386103285Sikob		names[OLD_FILE] = names[NEW_FILE];
387103285Sikob		names[NEW_FILE] = tmp;
388103285Sikob	}
389103285Sikob	if (filearg[0] == NULL) {
390103285Sikob		if (posix)
391103285Sikob			filearg[0] = posix_name(names, ok_to_create_file);
392103285Sikob		else {
393103285Sikob			/* Ignore the Index: name for context diffs, like GNU */
394103285Sikob			if (names[OLD_FILE].path != NULL ||
395103285Sikob			    names[NEW_FILE].path != NULL) {
396103285Sikob				free(names[INDEX_FILE].path);
397103285Sikob				names[INDEX_FILE].path = NULL;
398103285Sikob			}
399103285Sikob			filearg[0] = best_name(names, ok_to_create_file);
400103285Sikob		}
401103285Sikob	}
402103285Sikob
403103285Sikob	free(bestguess);
404103285Sikob	bestguess = NULL;
405119118Ssimokawa	if (filearg[0] != NULL)
406119118Ssimokawa		bestguess = xstrdup(filearg[0]);
407119118Ssimokawa	else if (!ok_to_create_file) {
408119118Ssimokawa		/*
409119118Ssimokawa		 * We don't want to create a new file but we need a
410119118Ssimokawa		 * filename to set bestguess.  Avoid setting filearg[0]
411119118Ssimokawa		 * so the file is not created automatically.
412119118Ssimokawa		 */
413119118Ssimokawa		if (posix)
414119118Ssimokawa			bestguess = posix_name(names, true);
415119118Ssimokawa		else
416119118Ssimokawa			bestguess = best_name(names, true);
417119118Ssimokawa	}
418119118Ssimokawa	free(names[OLD_FILE].path);
419103285Sikob	free(names[NEW_FILE].path);
420119118Ssimokawa	free(names[INDEX_FILE].path);
421103285Sikob	return retval;
422103285Sikob}
423103285Sikob
424103285Sikob/*
425106790Ssimokawa * Remember where this patch ends so we know where to start up again.
426108530Ssimokawa */
427108530Ssimokawastatic void
428103285Sikobnext_intuit_at(off_t file_pos, LINENUM file_line)
429129585Sdfr{
430108530Ssimokawa	p_base = file_pos;
431108530Ssimokawa	p_bline = file_line;
432108530Ssimokawa}
433108530Ssimokawa
434108530Ssimokawa/*
435108530Ssimokawa * Basically a verbose fseeko() to the actual diff listing.
436108530Ssimokawa */
437108530Ssimokawastatic void
438108530Ssimokawaskip_to(off_t file_pos, LINENUM file_line)
439167685Ssimokawa{
440167685Ssimokawa	size_t	len;
441108530Ssimokawa
442108530Ssimokawa	if (p_base > file_pos)
443108530Ssimokawa		fatal("Internal error: seek %lld>%lld\n",
444108530Ssimokawa		   (long long)p_base, (long long)file_pos);
445108530Ssimokawa	if (verbose && p_base < file_pos) {
446108530Ssimokawa		fseeko(pfp, p_base, SEEK_SET);
447108530Ssimokawa		say("The text leading up to this was:\n--------------------------\n");
448108530Ssimokawa		while (ftello(pfp) < file_pos) {
449108530Ssimokawa			len = pgets(false);
450108530Ssimokawa			if (len == 0)
451108530Ssimokawa				fatal("Unexpected end of file\n");
452108530Ssimokawa			say("|%s", buf);
453108701Ssimokawa		}
454108701Ssimokawa		say("--------------------------\n");
455108530Ssimokawa	} else
456108530Ssimokawa		fseeko(pfp, file_pos, SEEK_SET);
457108530Ssimokawa	p_input_line = file_line - 1;
458108530Ssimokawa}
459108530Ssimokawa
460108530Ssimokawa/* Make this a function for better debugging.  */
461108530Ssimokawastatic void
462108530Ssimokawamalformed(void)
463108530Ssimokawa{
464108530Ssimokawa	fatal("malformed patch at line %ld: %s", p_input_line, buf);
465108530Ssimokawa	/* about as informative as "Syntax error" in C */
466108701Ssimokawa}
467108701Ssimokawa
468108530Ssimokawa/*
469108530Ssimokawa * True if the line has been discarded (i.e. it is a line saying
470108530Ssimokawa *  "\ No newline at end of file".)
471108530Ssimokawa */
472108530Ssimokawastatic bool
473108530Ssimokawaremove_special_line(void)
474108530Ssimokawa{
475108530Ssimokawa	int	c;
476132432Ssimokawa
477108530Ssimokawa	c = fgetc(pfp);
478108530Ssimokawa	if (c == '\\') {
479108530Ssimokawa		do {
480108530Ssimokawa			c = fgetc(pfp);
481108530Ssimokawa		} while (c != EOF && c != '\n');
482108530Ssimokawa
483108530Ssimokawa		return true;
484108530Ssimokawa	}
485108530Ssimokawa	if (c != EOF)
486108530Ssimokawa		fseeko(pfp, -1, SEEK_CUR);
487108530Ssimokawa
488108530Ssimokawa	return false;
489108530Ssimokawa}
490108530Ssimokawa
491108530Ssimokawa/*
492108530Ssimokawa * True if there is more of the current diff listing to process.
493108530Ssimokawa */
494108530Ssimokawabool
495108530Ssimokawaanother_hunk(void)
496108530Ssimokawa{
497108530Ssimokawa	off_t	line_beginning;			/* file pos of the current line */
498108530Ssimokawa	LINENUM	repl_beginning;			/* index of --- line */
499108530Ssimokawa	LINENUM	fillcnt;			/* #lines of missing ptrn or repl */
500108530Ssimokawa	LINENUM	fillsrc;			/* index of first line to copy */
501108530Ssimokawa	LINENUM	filldst;			/* index of first missing line */
502108530Ssimokawa	bool	ptrn_spaces_eaten;		/* ptrn was slightly misformed */
503108530Ssimokawa	bool	repl_could_be_missing;		/* no + or ! lines in this hunk */
504108530Ssimokawa	bool	repl_missing;			/* we are now backtracking */
505108701Ssimokawa	off_t	repl_backtrack_position;	/* file pos of first repl line */
506129585Sdfr	LINENUM	repl_patch_line;		/* input line number for same */
507103285Sikob	LINENUM	ptrn_copiable;			/* # of copiable lines in ptrn */
508103285Sikob	char	*s;
509129541Sdfr	size_t	len;
510108530Ssimokawa	int	context = 0;
511108530Ssimokawa
512129541Sdfr	while (p_end >= 0) {
513108530Ssimokawa		if (p_end == p_efake)
514108530Ssimokawa			p_end = p_bfake;	/* don't free twice */
515108530Ssimokawa		else
516108530Ssimokawa			free(p_line[p_end]);
517108530Ssimokawa		p_end--;
518108530Ssimokawa	}
519108530Ssimokawa	p_efake = -1;
520108530Ssimokawa
521108530Ssimokawa	p_max = hunkmax;	/* gets reduced when --- found */
522108530Ssimokawa	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
523108530Ssimokawa		line_beginning = ftello(pfp);
524108701Ssimokawa		repl_beginning = 0;
525108530Ssimokawa		fillcnt = 0;
526132432Ssimokawa		fillsrc = 0;
527108530Ssimokawa		filldst = 0;
528108530Ssimokawa		ptrn_spaces_eaten = false;
529108530Ssimokawa		repl_could_be_missing = true;
530108530Ssimokawa		repl_missing = false;
531108530Ssimokawa		repl_backtrack_position = 0;
532108530Ssimokawa		repl_patch_line = 0;
533132432Ssimokawa		ptrn_copiable = 0;
534108530Ssimokawa
535108530Ssimokawa		len = pgets(true);
536108701Ssimokawa		p_input_line++;
537108701Ssimokawa		if (len == 0 || strnNE(buf, "********", 8)) {
538108701Ssimokawa			next_intuit_at(line_beginning, p_input_line);
539108701Ssimokawa			return false;
540108530Ssimokawa		}
541108530Ssimokawa		p_context = 100;
542108701Ssimokawa		p_hunk_beg = p_input_line + 1;
543108701Ssimokawa		while (p_end < p_max) {
544108701Ssimokawa			line_beginning = ftello(pfp);
545108701Ssimokawa			len = pgets(true);
546108701Ssimokawa			p_input_line++;
547108701Ssimokawa			if (len == 0) {
548108701Ssimokawa				if (p_max - p_end < 4) {
549108701Ssimokawa					/* assume blank lines got chopped */
550108701Ssimokawa					strlcpy(buf, "  \n", buf_size);
551108701Ssimokawa				} else {
552108701Ssimokawa					if (repl_beginning && repl_could_be_missing) {
553132432Ssimokawa						repl_missing = true;
554108530Ssimokawa						goto hunk_done;
555108530Ssimokawa					}
556108530Ssimokawa					fatal("unexpected end of file in patch\n");
557108701Ssimokawa				}
558108530Ssimokawa			}
559113584Ssimokawa			p_end++;
560108530Ssimokawa			if (p_end >= hunkmax)
561108530Ssimokawa				fatal("Internal error: hunk larger than hunk "
562113584Ssimokawa				    "buffer size");
563108530Ssimokawa			p_char[p_end] = *buf;
564108530Ssimokawa			p_line[p_end] = NULL;
565108701Ssimokawa			switch (*buf) {
566108530Ssimokawa			case '*':
567108642Ssimokawa				if (strnEQ(buf, "********", 8)) {
568108701Ssimokawa					if (repl_beginning && repl_could_be_missing) {
569108642Ssimokawa						repl_missing = true;
570108642Ssimokawa						goto hunk_done;
571108530Ssimokawa					} else
572108530Ssimokawa						fatal("unexpected end of hunk "
573108530Ssimokawa						    "at line %ld\n",
574108701Ssimokawa						    p_input_line);
575108701Ssimokawa				}
576108701Ssimokawa				if (p_end != 0) {
577116978Ssimokawa					if (repl_beginning && repl_could_be_missing) {
578108701Ssimokawa						repl_missing = true;
579108701Ssimokawa						goto hunk_done;
580108701Ssimokawa					}
581108701Ssimokawa					fatal("unexpected *** at line %ld: %s",
582116978Ssimokawa					    p_input_line, buf);
583116978Ssimokawa				}
584116978Ssimokawa				context = 0;
585116978Ssimokawa				p_line[p_end] = savestr(buf);
586116978Ssimokawa				if (out_of_mem) {
587116978Ssimokawa					p_end--;
588108530Ssimokawa					return false;
589108530Ssimokawa				}
590108530Ssimokawa				for (s = buf;
591108530Ssimokawa				     *s && !isdigit((unsigned char)*s); s++)
592108530Ssimokawa					;
593108530Ssimokawa				if (!*s)
594108530Ssimokawa					malformed();
595108530Ssimokawa				if (strnEQ(s, "0,0", 3))
596108530Ssimokawa					memmove(s, s + 2, strlen(s + 2) + 1);
597108701Ssimokawa				p_first = strtolinenum(s, &s);
598129541Sdfr				if (*s == ',') {
599170374Ssimokawa					for (;
600108530Ssimokawa					     *s && !isdigit((unsigned char)*s); s++)
601108530Ssimokawa						;
602108530Ssimokawa					if (!*s)
603170374Ssimokawa						malformed();
604170374Ssimokawa					p_ptrn_lines = strtolinenum(s, &s) - p_first + 1;
605170374Ssimokawa					if (p_ptrn_lines < 0)
606108530Ssimokawa						malformed();
607108530Ssimokawa				} else if (p_first)
608108530Ssimokawa					p_ptrn_lines = 1;
609108530Ssimokawa				else {
610108530Ssimokawa					p_ptrn_lines = 0;
611108530Ssimokawa					p_first = 1;
612108530Ssimokawa				}
613121781Ssimokawa				if (p_first >= LINENUM_MAX - p_ptrn_lines ||
614129585Sdfr				    p_ptrn_lines >= LINENUM_MAX - 6)
615129585Sdfr					malformed();
616108530Ssimokawa
617121781Ssimokawa				/* we need this much at least */
618103285Sikob				p_max = p_ptrn_lines + 6;
619121781Ssimokawa				while (p_max >= hunkmax)
620103285Sikob					grow_hunkmax();
621121781Ssimokawa				p_max = hunkmax;
622121781Ssimokawa				break;
623118416Ssimokawa			case '-':
624118416Ssimokawa				if (buf[1] == '-') {
625118416Ssimokawa					if (repl_beginning ||
626118416Ssimokawa					    (p_end != p_ptrn_lines + 1 +
627129541Sdfr					    (p_char[p_end - 1] == '\n'))) {
628110045Ssimokawa						if (p_end == 1) {
629110045Ssimokawa							/*
630110045Ssimokawa							 * `old' lines were omitted;
631110045Ssimokawa							 * set up to fill them in
632110045Ssimokawa							 * from 'new' context lines.
633110045Ssimokawa							 */
634110045Ssimokawa							p_end = p_ptrn_lines + 1;
635110045Ssimokawa							fillsrc = p_end + 1;
636103285Sikob							filldst = 1;
637129541Sdfr							fillcnt = p_ptrn_lines;
638118820Ssimokawa						} else {
639118820Ssimokawa							if (repl_beginning) {
640103285Sikob								if (repl_could_be_missing) {
641103285Sikob									repl_missing = true;
642103285Sikob									goto hunk_done;
643103285Sikob								}
644103285Sikob								fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
645103285Sikob								    p_input_line, p_hunk_beg + repl_beginning);
646113584Ssimokawa							} else {
647113584Ssimokawa								fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
648113584Ssimokawa								    (p_end <= p_ptrn_lines
649113584Ssimokawa								    ? "Premature"
650113584Ssimokawa								    : "Overdue"),
651103285Sikob								    p_input_line, p_hunk_beg);
652103285Sikob							}
653103285Sikob						}
654103285Sikob					}
655103285Sikob					repl_beginning = p_end;
656113584Ssimokawa					repl_backtrack_position = ftello(pfp);
657113584Ssimokawa					repl_patch_line = p_input_line;
658113584Ssimokawa					p_line[p_end] = savestr(buf);
659113584Ssimokawa					if (out_of_mem) {
660103285Sikob						p_end--;
661118293Ssimokawa						return false;
662118293Ssimokawa					}
663118293Ssimokawa					p_char[p_end] = '=';
664118293Ssimokawa					for (s = buf; *s && !isdigit((unsigned char)*s); s++)
665118293Ssimokawa						;
666103285Sikob					if (!*s)
667103285Sikob						malformed();
668110593Ssimokawa					p_newfirst = strtolinenum(s, &s);
669110593Ssimokawa					if (*s == ',') {
670103285Sikob						for (; *s && !isdigit((unsigned char)*s); s++)
671103285Sikob							;
672103285Sikob						if (!*s)
673103285Sikob							malformed();
674103285Sikob						p_repl_lines = strtolinenum(s, &s) -
675103285Sikob						    p_newfirst + 1;
676103285Sikob						if (p_repl_lines < 0)
677103285Sikob							malformed();
678103285Sikob					} else if (p_newfirst)
679118293Ssimokawa						p_repl_lines = 1;
680118293Ssimokawa					else {
681103285Sikob						p_repl_lines = 0;
682103285Sikob						p_newfirst = 1;
683103285Sikob					}
684103285Sikob					if (p_newfirst >= LINENUM_MAX - p_repl_lines ||
685103285Sikob					    p_repl_lines >= LINENUM_MAX - p_end)
686113584Ssimokawa						malformed();
687103285Sikob					p_max = p_repl_lines + p_end;
688113584Ssimokawa					if (p_max > MAXHUNKSIZE)
689113584Ssimokawa						fatal("hunk too large (%ld lines) at line %ld: %s",
690113584Ssimokawa						    p_max, p_input_line, buf);
691113584Ssimokawa					while (p_max >= hunkmax)
692103285Sikob						grow_hunkmax();
693103285Sikob					if (p_repl_lines != ptrn_copiable &&
694103285Sikob					    (p_context != 0 || p_repl_lines != 1))
695116376Ssimokawa						repl_could_be_missing = false;
696116376Ssimokawa					break;
697103285Sikob				}
698103285Sikob				goto change_line;
699103285Sikob			case '+':
700103285Sikob			case '!':
701103285Sikob				repl_could_be_missing = false;
702103285Sikob		change_line:
703103285Sikob				if (buf[1] == '\n' && canonicalize)
704103285Sikob					strlcpy(buf + 1, " \n", buf_size - 1);
705113584Ssimokawa				if (!isspace((unsigned char)buf[1]) &&
706103285Sikob				    buf[1] != '>' && buf[1] != '<' &&
707103285Sikob				    repl_beginning && repl_could_be_missing) {
708129541Sdfr					repl_missing = true;
709103285Sikob					goto hunk_done;
710113584Ssimokawa				}
711113584Ssimokawa				if (context >= 0) {
712113584Ssimokawa					if (context < p_context)
713113584Ssimokawa						p_context = context;
714108527Ssimokawa					context = -1000;
715108527Ssimokawa				}
716113584Ssimokawa				p_line[p_end] = savestr(buf + 2);
717129585Sdfr				if (out_of_mem) {
718113584Ssimokawa					p_end--;
719113584Ssimokawa					return false;
720113584Ssimokawa				}
721113584Ssimokawa				if (p_end == p_ptrn_lines) {
722109736Ssimokawa					if (remove_special_line()) {
723109736Ssimokawa						int	l;
724113584Ssimokawa
725113584Ssimokawa						l = strlen(p_line[p_end]) - 1;
726108527Ssimokawa						(p_line[p_end])[l] = 0;
727108527Ssimokawa					}
728108527Ssimokawa				}
729113584Ssimokawa				break;
730108527Ssimokawa			case '\t':
731108527Ssimokawa			case '\n':	/* assume the 2 spaces got eaten */
732103285Sikob				if (repl_beginning && repl_could_be_missing &&
733113584Ssimokawa				    (!ptrn_spaces_eaten ||
734108527Ssimokawa				    diff_type == NEW_CONTEXT_DIFF)) {
735108527Ssimokawa					repl_missing = true;
736108527Ssimokawa					goto hunk_done;
737113584Ssimokawa				}
738108527Ssimokawa				p_line[p_end] = savestr(buf);
739108527Ssimokawa				if (out_of_mem) {
740103285Sikob					p_end--;
741109814Ssimokawa					return false;
742109814Ssimokawa				}
743109814Ssimokawa				if (p_end != p_ptrn_lines + 1) {
744109814Ssimokawa					ptrn_spaces_eaten |= (repl_beginning != 0);
745103285Sikob					context++;
746109814Ssimokawa					if (!repl_beginning)
747109814Ssimokawa						ptrn_copiable++;
748103285Sikob					p_char[p_end] = ' ';
749103285Sikob				}
750103285Sikob				break;
751103285Sikob			case ' ':
752103285Sikob				if (!isspace((unsigned char)buf[1]) &&
753103285Sikob				    repl_beginning && repl_could_be_missing) {
754103285Sikob					repl_missing = true;
755103285Sikob					goto hunk_done;
756103285Sikob				}
757113584Ssimokawa				context++;
758103285Sikob				if (!repl_beginning)
759113584Ssimokawa					ptrn_copiable++;
760113584Ssimokawa				p_line[p_end] = savestr(buf + 2);
761113584Ssimokawa				if (out_of_mem) {
762103285Sikob					p_end--;
763103285Sikob					return false;
764103285Sikob				}
765103285Sikob				break;
766106790Ssimokawa			default:
767113584Ssimokawa				if (repl_beginning && repl_could_be_missing) {
768113584Ssimokawa					repl_missing = true;
769170374Ssimokawa					goto hunk_done;
770170374Ssimokawa				}
771170374Ssimokawa				malformed();
772170374Ssimokawa			}
773170374Ssimokawa			/* set up p_len for strncmp() so we don't have to */
774170374Ssimokawa			/* assume null termination */
775170374Ssimokawa			if (p_line[p_end])
776170374Ssimokawa				p_len[p_end] = strlen(p_line[p_end]);
777170374Ssimokawa			else
778108530Ssimokawa				p_len[p_end] = 0;
779108530Ssimokawa		}
780103285Sikob
781108530Ssimokawahunk_done:
782103285Sikob		if (p_end >= 0 && !repl_beginning)
783106790Ssimokawa			fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
784106790Ssimokawa
785106790Ssimokawa		if (repl_missing) {
786103285Sikob
787103285Sikob			/* reset state back to just after --- */
788103285Sikob			p_input_line = repl_patch_line;
789103285Sikob			for (p_end--; p_end > repl_beginning; p_end--)
790103285Sikob				free(p_line[p_end]);
791106790Ssimokawa			fseeko(pfp, repl_backtrack_position, SEEK_SET);
792129585Sdfr
793106790Ssimokawa			/* redundant 'new' context lines were omitted - set */
794103285Sikob			/* up to fill them in from the old file context */
795103285Sikob			if (!p_context && p_repl_lines == 1) {
796103285Sikob				p_repl_lines = 0;
797103285Sikob				p_max--;
798103285Sikob			}
799108527Ssimokawa			fillsrc = 1;
800108527Ssimokawa			filldst = repl_beginning + 1;
801108527Ssimokawa			fillcnt = p_repl_lines;
802108527Ssimokawa			p_end = p_max;
803108527Ssimokawa		} else if (!p_context && fillcnt == 1) {
804113584Ssimokawa			/* the first hunk was a null hunk with no context */
805113584Ssimokawa			/* and we were expecting one line -- fix it up. */
806113584Ssimokawa			while (filldst < p_end) {
807113584Ssimokawa				p_line[filldst] = p_line[filldst + 1];
808108527Ssimokawa				p_char[filldst] = p_char[filldst + 1];
809108527Ssimokawa				p_len[filldst] = p_len[filldst + 1];
810108527Ssimokawa				filldst++;
811108527Ssimokawa			}
812108527Ssimokawa#if 0
813108527Ssimokawa			repl_beginning--;	/* this doesn't need to be fixed */
814108527Ssimokawa#endif
815108527Ssimokawa			p_end--;
816108527Ssimokawa			p_first++;	/* do append rather than insert */
817108527Ssimokawa			fillcnt = 0;
818108527Ssimokawa			p_ptrn_lines = 0;
819170374Ssimokawa		}
820170374Ssimokawa		if (diff_type == CONTEXT_DIFF &&
821170374Ssimokawa		    (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
822170374Ssimokawa			if (verbose)
823170374Ssimokawa				say("%s\n%s\n%s\n",
824170374Ssimokawa				    "(Fascinating--this is really a new-style context diff but without",
825170374Ssimokawa				    "the telltale extra asterisks on the *** line that usually indicate",
826170374Ssimokawa				    "the new style...)");
827108527Ssimokawa			diff_type = NEW_CONTEXT_DIFF;
828108527Ssimokawa		}
829108527Ssimokawa		/* if there were omitted context lines, fill them in now */
830108527Ssimokawa		if (fillcnt) {
831108655Ssimokawa			p_bfake = filldst;	/* remember where not to free() */
832108655Ssimokawa			p_efake = filldst + fillcnt - 1;
833108655Ssimokawa			while (fillcnt-- > 0) {
834108655Ssimokawa				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
835108655Ssimokawa					fillsrc++;
836108655Ssimokawa				if (fillsrc > p_end)
837106790Ssimokawa					fatal("replacement text or line numbers mangled in hunk at line %ld\n",
838113584Ssimokawa					    p_hunk_beg);
839113584Ssimokawa				p_line[filldst] = p_line[fillsrc];
840113584Ssimokawa				p_char[filldst] = p_char[fillsrc];
841120660Ssimokawa				p_len[filldst] = p_len[fillsrc];
842113584Ssimokawa				fillsrc++;
843113584Ssimokawa				filldst++;
844113584Ssimokawa			}
845113584Ssimokawa			while (fillsrc <= p_end && fillsrc != repl_beginning &&
846113584Ssimokawa			    p_char[fillsrc] != ' ')
847113584Ssimokawa				fillsrc++;
848113584Ssimokawa#ifdef DEBUGGING
849113584Ssimokawa			if (debug & 64)
850113584Ssimokawa				printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
851113584Ssimokawa				fillsrc, filldst, repl_beginning, p_end + 1);
852113584Ssimokawa#endif
853113584Ssimokawa			if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
854113584Ssimokawa				malformed();
855113584Ssimokawa			if (filldst != p_end + 1 && filldst != repl_beginning)
856113584Ssimokawa				malformed();
857113584Ssimokawa		}
858113584Ssimokawa		if (p_line[p_end] != NULL) {
859113584Ssimokawa			if (remove_special_line()) {
860113584Ssimokawa				p_len[p_end] -= 1;
861113584Ssimokawa				(p_line[p_end])[p_len[p_end]] = 0;
862113584Ssimokawa			}
863113584Ssimokawa		}
864113584Ssimokawa	} else if (diff_type == UNI_DIFF) {
865113584Ssimokawa		LINENUM	fillold;	/* index of old lines */
866113584Ssimokawa		LINENUM	fillnew;	/* index of new lines */
867113584Ssimokawa		char	ch;
868113584Ssimokawa
869113584Ssimokawa		line_beginning = ftello(pfp); /* file pos of the current line */
870106790Ssimokawa		len = pgets(true);
871103285Sikob		p_input_line++;
872103285Sikob		if (len == 0 || strnNE(buf, "@@ -", 4)) {
873120660Ssimokawa			next_intuit_at(line_beginning, p_input_line);
874103285Sikob			return false;
875129585Sdfr		}
876103285Sikob		s = buf + 4;
877103285Sikob		if (!*s)
878120660Ssimokawa			malformed();
879103285Sikob		p_first = strtolinenum(s, &s);
880120660Ssimokawa		if (*s == ',') {
881129585Sdfr			p_ptrn_lines = strtolinenum(s + 1, &s);
882103285Sikob		} else
883108655Ssimokawa			p_ptrn_lines = 1;
884103285Sikob		if (*s == ' ')
885170374Ssimokawa			s++;
886170374Ssimokawa		if (*s != '+' || !*++s)
887103285Sikob			malformed();
888103285Sikob		p_newfirst = strtolinenum(s, &s);
889103285Sikob		if (*s == ',') {
890103285Sikob			p_repl_lines = strtolinenum(s + 1, &s);
891103285Sikob		} else
892103285Sikob			p_repl_lines = 1;
893103285Sikob		if (*s == ' ')
894103285Sikob			s++;
895103285Sikob		if (*s != '@')
896103285Sikob			malformed();
897103285Sikob		if (p_first >= LINENUM_MAX - p_ptrn_lines ||
898103285Sikob		    p_newfirst > LINENUM_MAX - p_repl_lines ||
899103285Sikob		    p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
900103285Sikob			malformed();
901103285Sikob		if (!p_ptrn_lines)
902103285Sikob			p_first++;	/* do append rather than insert */
903103285Sikob		p_max = p_ptrn_lines + p_repl_lines + 1;
904103285Sikob		while (p_max >= hunkmax)
905170374Ssimokawa			grow_hunkmax();
906103285Sikob		fillold = 1;
907103285Sikob		fillnew = fillold + p_ptrn_lines;
908103285Sikob		p_end = fillnew + p_repl_lines;
909170374Ssimokawa		snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first,
910103285Sikob		    p_first + p_ptrn_lines - 1);
911103285Sikob		p_line[0] = savestr(buf);
912170374Ssimokawa		if (out_of_mem) {
913103285Sikob			p_end = -1;
914120660Ssimokawa			return false;
915103285Sikob		}
916103285Sikob		p_char[0] = '*';
917120660Ssimokawa		snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst,
918103285Sikob		    p_newfirst + p_repl_lines - 1);
919113584Ssimokawa		p_line[fillnew] = savestr(buf);
920119155Ssimokawa		if (out_of_mem) {
921119155Ssimokawa			p_end = 0;
922119155Ssimokawa			return false;
923119155Ssimokawa		}
924119155Ssimokawa		p_char[fillnew++] = '=';
925119155Ssimokawa		p_context = 100;
926120660Ssimokawa		context = 0;
927103285Sikob		p_hunk_beg = p_input_line + 1;
928103285Sikob		while (fillold <= p_ptrn_lines || fillnew <= p_end) {
929113584Ssimokawa			line_beginning = ftello(pfp);
930103285Sikob			len = pgets(true);
931103285Sikob			p_input_line++;
932119155Ssimokawa			if (len == 0) {
933119155Ssimokawa				if (p_max - fillnew < 3) {
934103285Sikob					/* assume blank lines got chopped */
935103285Sikob					strlcpy(buf, " \n", buf_size);
936103285Sikob				} else {
937113584Ssimokawa					fatal("unexpected end of file in patch\n");
938103285Sikob				}
939103285Sikob			}
940103285Sikob			if (*buf == '\t' || *buf == '\n') {
941103285Sikob				ch = ' ';	/* assume the space got eaten */
942113584Ssimokawa				s = savestr(buf);
943113584Ssimokawa			} else {
944119155Ssimokawa				ch = *buf;
945113584Ssimokawa				s = savestr(buf + 1);
946103285Sikob			}
947103285Sikob			if (out_of_mem) {
948113584Ssimokawa				while (--fillnew > p_ptrn_lines)
949113584Ssimokawa					free(p_line[fillnew]);
950103285Sikob				p_end = fillold - 1;
951113584Ssimokawa				return false;
952113584Ssimokawa			}
953113584Ssimokawa			switch (ch) {
954113584Ssimokawa			case '-':
955119155Ssimokawa				if (fillold > p_ptrn_lines) {
956113584Ssimokawa					free(s);
957103285Sikob					p_end = fillnew - 1;
958111942Ssimokawa					malformed();
959103285Sikob				}
960103285Sikob				p_char[fillold] = ch;
961120660Ssimokawa				p_line[fillold] = s;
962113584Ssimokawa				p_len[fillold++] = strlen(s);
963113584Ssimokawa				if (fillold > p_ptrn_lines) {
964103285Sikob					if (remove_special_line()) {
965113584Ssimokawa						p_len[fillold - 1] -= 1;
966120660Ssimokawa						s[p_len[fillold - 1]] = 0;
967113584Ssimokawa					}
968113584Ssimokawa				}
969103285Sikob				break;
970111942Ssimokawa			case '=':
971113584Ssimokawa				ch = ' ';
972113584Ssimokawa				/* FALL THROUGH */
973113584Ssimokawa			case ' ':
974113584Ssimokawa				if (fillold > p_ptrn_lines) {
975113584Ssimokawa					free(s);
976113584Ssimokawa					while (--fillnew > p_ptrn_lines)
977113584Ssimokawa						free(p_line[fillnew]);
978113584Ssimokawa					p_end = fillold - 1;
979113584Ssimokawa					malformed();
980113584Ssimokawa				}
981113584Ssimokawa				context++;
982111942Ssimokawa				p_char[fillold] = ch;
983111942Ssimokawa				p_line[fillold] = s;
984113584Ssimokawa				p_len[fillold++] = strlen(s);
985113584Ssimokawa				s = savestr(s);
986111942Ssimokawa				if (out_of_mem) {
987111942Ssimokawa					while (--fillnew > p_ptrn_lines)
988113584Ssimokawa						free(p_line[fillnew]);
989111942Ssimokawa					p_end = fillold - 1;
990111942Ssimokawa					return false;
991111942Ssimokawa				}
992111942Ssimokawa				if (fillold > p_ptrn_lines) {
993103285Sikob					if (remove_special_line()) {
994113584Ssimokawa						p_len[fillold - 1] -= 1;
995113584Ssimokawa						s[p_len[fillold - 1]] = 0;
996113584Ssimokawa					}
997113584Ssimokawa				}
998113584Ssimokawa				/* FALL THROUGH */
999113584Ssimokawa			case '+':
1000113584Ssimokawa				if (fillnew > p_end) {
1001113584Ssimokawa					free(s);
1002113584Ssimokawa					while (--fillnew > p_ptrn_lines)
1003103285Sikob						free(p_line[fillnew]);
1004108655Ssimokawa					p_end = fillold - 1;
1005108655Ssimokawa					malformed();
1006132432Ssimokawa				}
1007187993Ssbruno				p_char[fillnew] = ch;
1008108655Ssimokawa				p_line[fillnew] = s;
1009103285Sikob				p_len[fillnew++] = strlen(s);
1010103285Sikob				if (fillold > p_ptrn_lines) {
1011113584Ssimokawa					if (remove_special_line()) {
1012113584Ssimokawa						p_len[fillnew - 1] -= 1;
1013113584Ssimokawa						s[p_len[fillnew - 1]] = 0;
1014113584Ssimokawa					}
1015103285Sikob				}
1016103285Sikob				break;
1017103285Sikob			default:
1018103285Sikob				p_end = fillnew;
1019103285Sikob				malformed();
1020113584Ssimokawa			}
1021103285Sikob			if (ch != ' ' && context > 0) {
1022170374Ssimokawa				if (context < p_context)
1023103285Sikob					p_context = context;
1024103285Sikob				context = -1000;
1025103285Sikob			}
1026103285Sikob		}		/* while */
1027103285Sikob	} else {		/* normal diff--fake it up */
1028107653Ssimokawa		char	hunk_type;
1029103285Sikob		int	i;
1030103285Sikob		LINENUM	min, max;
1031103285Sikob
1032103285Sikob		line_beginning = ftello(pfp);
1033113584Ssimokawa		p_context = 0;
1034113584Ssimokawa		len = pgets(true);
1035103285Sikob		p_input_line++;
1036103285Sikob		if (len == 0 || !isdigit((unsigned char)*buf)) {
1037103285Sikob			next_intuit_at(line_beginning, p_input_line);
1038103285Sikob			return false;
1039132432Ssimokawa		}
1040107653Ssimokawa		p_first = strtolinenum(buf, &s);
1041103285Sikob		if (*s == ',') {
1042113584Ssimokawa			p_ptrn_lines = strtolinenum(s + 1, &s) - p_first + 1;
1043103285Sikob			if (p_ptrn_lines < 0)
1044103285Sikob				malformed();
1045103285Sikob		} else
1046106790Ssimokawa			p_ptrn_lines = (*s != 'a');
1047103285Sikob		hunk_type = *s;
1048103285Sikob		if (hunk_type == 'a')
1049103285Sikob			p_first++;	/* do append rather than insert */
1050103285Sikob		min = strtolinenum(s + 1, &s);
1051106790Ssimokawa		if (*s == ',')
1052106790Ssimokawa			max = strtolinenum(s + 1, &s);
1053106790Ssimokawa		else
1054103285Sikob			max = min;
1055103285Sikob		if (min < 0 || min > max || max - min == LINENUM_MAX)
1056170374Ssimokawa			malformed();
1057103285Sikob		if (hunk_type == 'd')
1058170374Ssimokawa			min++;
1059103285Sikob		p_newfirst = min;
1060103285Sikob		p_repl_lines = max - min + 1;
1061106790Ssimokawa		if (p_newfirst > LINENUM_MAX - p_repl_lines ||
1062106790Ssimokawa		    p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
1063106790Ssimokawa			malformed();
1064103285Sikob		p_end = p_ptrn_lines + p_repl_lines + 1;
1065103285Sikob		if (p_end > MAXHUNKSIZE)
1066170374Ssimokawa			fatal("hunk too large (%ld lines) at line %ld: %s",
1067103285Sikob			    p_end, p_input_line, buf);
1068170374Ssimokawa		while (p_end >= hunkmax)
1069103285Sikob			grow_hunkmax();
1070103285Sikob		snprintf(buf, buf_size, "*** %ld,%ld\n", p_first,
1071106790Ssimokawa		    p_first + p_ptrn_lines - 1);
1072106790Ssimokawa		p_line[0] = savestr(buf);
1073106790Ssimokawa		if (out_of_mem) {
1074103285Sikob			p_end = -1;
1075113584Ssimokawa			return false;
1076103285Sikob		}
1077120660Ssimokawa		p_char[0] = '*';
1078103285Sikob		for (i = 1; i <= p_ptrn_lines; i++) {
1079129585Sdfr			len = pgets(true);
1080113584Ssimokawa			p_input_line++;
1081103285Sikob			if (len == 0)
1082103285Sikob				fatal("unexpected end of file in patch at line %ld\n",
1083113584Ssimokawa				    p_input_line);
1084103285Sikob			if (*buf != '<')
1085103285Sikob				fatal("< expected at line %ld of patch\n",
1086113584Ssimokawa				    p_input_line);
1087103285Sikob			p_line[i] = savestr(buf + 2);
1088103285Sikob			if (out_of_mem) {
1089113584Ssimokawa				p_end = i - 1;
1090103285Sikob				return false;
1091103285Sikob			}
1092103285Sikob			p_len[i] = strlen(p_line[i]);
1093103285Sikob			p_char[i] = '-';
1094103285Sikob		}
1095103285Sikob
1096113584Ssimokawa		if (remove_special_line()) {
1097113584Ssimokawa			p_len[i - 1] -= 1;
1098103285Sikob			(p_line[i - 1])[p_len[i - 1]] = 0;
1099103285Sikob		}
1100113584Ssimokawa		if (hunk_type == 'c') {
1101113584Ssimokawa			len = pgets(true);
1102170425Ssimokawa			p_input_line++;
1103103285Sikob			if (len == 0)
1104103285Sikob				fatal("unexpected end of file in patch at line %ld\n",
1105103285Sikob				    p_input_line);
1106113584Ssimokawa			if (*buf != '-')
1107113584Ssimokawa				fatal("--- expected at line %ld of patch\n",
1108113584Ssimokawa				    p_input_line);
1109119155Ssimokawa		}
1110167629Ssimokawa		snprintf(buf, buf_size, "--- %ld,%ld\n", min, max);
1111119155Ssimokawa		p_line[i] = savestr(buf);
1112103285Sikob		if (out_of_mem) {
1113113584Ssimokawa			p_end = i - 1;
1114113584Ssimokawa			return false;
1115103285Sikob		}
1116103285Sikob		p_char[i] = '=';
1117103285Sikob		for (i++; i <= p_end; i++) {
1118103285Sikob			len = pgets(true);
1119103285Sikob			p_input_line++;
1120103285Sikob			if (len == 0)
1121113584Ssimokawa				fatal("unexpected end of file in patch at line %ld\n",
1122103285Sikob				    p_input_line);
1123110577Ssimokawa			if (*buf != '>')
1124103285Sikob				fatal("> expected at line %ld of patch\n",
1125103285Sikob				    p_input_line);
1126103285Sikob			p_line[i] = savestr(buf + 2);
1127103285Sikob			if (out_of_mem) {
1128103285Sikob				p_end = i - 1;
1129110577Ssimokawa				return false;
1130103285Sikob			}
1131103285Sikob			p_len[i] = strlen(p_line[i]);
1132103285Sikob			p_char[i] = '+';
1133103285Sikob		}
1134103285Sikob
1135103285Sikob		if (remove_special_line()) {
1136103285Sikob			p_len[i - 1] -= 1;
1137103285Sikob			(p_line[i - 1])[p_len[i - 1]] = 0;
1138103285Sikob		}
1139103285Sikob	}
1140103285Sikob	if (reverse)		/* backwards patch? */
1141103285Sikob		if (!pch_swap())
1142103285Sikob			say("Not enough memory to swap next hunk!\n");
1143103285Sikob#ifdef DEBUGGING
1144103285Sikob	if (debug & 2) {
1145103285Sikob		int	i;
1146103285Sikob		char	special;
1147103285Sikob
1148103285Sikob		for (i = 0; i <= p_end; i++) {
1149103285Sikob			if (i == p_ptrn_lines)
1150103285Sikob				special = '^';
1151103285Sikob			else
1152103285Sikob				special = ' ';
1153103285Sikob			fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1154110577Ssimokawa			    special, p_line[i]);
1155103285Sikob			fflush(stderr);
1156170374Ssimokawa		}
1157119289Ssimokawa	}
1158113584Ssimokawa#endif
1159113584Ssimokawa	if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1160119289Ssimokawa		p_char[p_end + 1] = '^';	/* add a stopper for apply_hunk */
1161113584Ssimokawa	return true;
1162113584Ssimokawa}
1163170427Ssimokawa
1164170374Ssimokawa/*
1165170425Ssimokawa * Input a line from the patch file.
1166170374Ssimokawa * Worry about indentation if do_indent is true.
1167114218Ssimokawa * The line is read directly into the buf global variable which
1168167630Ssimokawa * is resized if necessary in order to hold the complete line.
1169167630Ssimokawa * Returns the number of characters read including the terminating
1170114218Ssimokawa * '\n', if any.
1171114218Ssimokawa */
1172170374Ssimokawasize_t
1173114218Ssimokawapgets(bool do_indent)
1174120660Ssimokawa{
1175113584Ssimokawa	char *line;
1176114218Ssimokawa	size_t len;
1177103285Sikob	int indent = 0, skipped = 0;
1178110577Ssimokawa
1179110577Ssimokawa	line = fgetln(pfp, &len);
1180110577Ssimokawa	if (line != NULL) {
1181110577Ssimokawa		if (len + 1 > buf_size) {
1182113584Ssimokawa			while (len + 1 > buf_size)
1183113584Ssimokawa				buf_size *= 2;
1184103285Sikob			free(buf);
1185170374Ssimokawa			buf = malloc(buf_size);
1186110269Ssimokawa			if (buf == NULL)
1187170374Ssimokawa				fatal("out of memory\n");
1188103285Sikob		}
1189103285Sikob		if (do_indent == 1 && p_indent) {
1190103285Sikob			for (;
1191103285Sikob			    indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X');
1192103285Sikob			    line++, skipped++) {
1193111956Ssimokawa				if (*line == '\t')
1194111956Ssimokawa					indent += 8 - (indent %7);
1195111956Ssimokawa				else
1196111956Ssimokawa					indent++;
1197111956Ssimokawa			}
1198111956Ssimokawa		}
1199103285Sikob		memcpy(buf, line, len - skipped);
1200103285Sikob		buf[len - skipped] = '\0';
1201103285Sikob	}
1202103285Sikob	return len;
1203103285Sikob}
1204170374Ssimokawa
1205103285Sikob
1206170374Ssimokawa/*
1207103285Sikob * Reverse the old and new portions of the current hunk.
1208103285Sikob */
1209103285Sikobbool
1210106790Ssimokawapch_swap(void)
1211106790Ssimokawa{
1212106790Ssimokawa	char	**tp_line;	/* the text of the hunk */
1213103285Sikob	unsigned short	*tp_len;/* length of each line */
1214103285Sikob	char	*tp_char;	/* +, -, and ! */
1215113584Ssimokawa	LINENUM	i;
1216103285Sikob	LINENUM	n;
1217108527Ssimokawa	bool	blankline = false;
1218108527Ssimokawa	char	*s;
1219108527Ssimokawa
1220113584Ssimokawa	i = p_first;
1221103285Sikob	p_first = p_newfirst;
1222113584Ssimokawa	p_newfirst = i;
1223113584Ssimokawa
1224113584Ssimokawa	/* make a scratch copy */
1225113584Ssimokawa
1226113584Ssimokawa	tp_line = p_line;
1227113584Ssimokawa	tp_len = p_len;
1228113584Ssimokawa	tp_char = p_char;
1229103285Sikob	p_line = NULL;	/* force set_hunkmax to allocate again */
1230103285Sikob	p_len = NULL;
1231103285Sikob	p_char = NULL;
1232113584Ssimokawa	set_hunkmax();
1233110195Ssimokawa	if (p_line == NULL || p_len == NULL || p_char == NULL) {
1234103285Sikob
1235108527Ssimokawa		free(p_line);
1236103285Sikob		p_line = tp_line;
1237106790Ssimokawa		free(p_len);
1238106790Ssimokawa		p_len = tp_len;
1239113584Ssimokawa		free(p_char);
1240103285Sikob		p_char = tp_char;
1241103285Sikob		return false;	/* not enough memory to swap hunk! */
1242103285Sikob	}
1243108642Ssimokawa	/* now turn the new into the old */
1244108642Ssimokawa
1245108642Ssimokawa	i = p_ptrn_lines + 1;
1246108642Ssimokawa	if (tp_char[i] == '\n') {	/* account for possible blank line */
1247113584Ssimokawa		blankline = true;
1248113584Ssimokawa		i++;
1249113584Ssimokawa	}
1250113584Ssimokawa	if (p_efake >= 0) {	/* fix non-freeable ptr range */
1251113584Ssimokawa		if (p_efake <= i)
1252113584Ssimokawa			n = p_end - i + 1;
1253113584Ssimokawa		else
1254113584Ssimokawa			n = -i;
1255113584Ssimokawa		p_efake += n;
1256113584Ssimokawa		p_bfake += n;
1257117126Sscottl	}
1258127468Ssimokawa	for (n = 0; i <= p_end; i++, n++) {
1259117126Sscottl		p_line[n] = tp_line[i];
1260170374Ssimokawa		p_char[n] = tp_char[i];
1261117228Ssimokawa		if (p_char[n] == '+')
1262117228Ssimokawa			p_char[n] = '-';
1263113584Ssimokawa		p_len[n] = tp_len[i];
1264113584Ssimokawa	}
1265103285Sikob	if (blankline) {
1266103285Sikob		i = p_ptrn_lines + 1;
1267103285Sikob		p_line[n] = tp_line[i];
1268103285Sikob		p_char[n] = tp_char[i];
1269103285Sikob		p_len[n] = tp_len[i];
1270113584Ssimokawa		n++;
1271103285Sikob	}
1272109379Ssimokawa	if (p_char[0] != '=')
1273103285Sikob		fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1274103285Sikob		    p_input_line, p_char[0]);
1275109379Ssimokawa	p_char[0] = '*';
1276113584Ssimokawa	for (s = p_line[0]; *s; s++)
1277113584Ssimokawa		if (*s == '-')
1278113584Ssimokawa			*s = '*';
1279113584Ssimokawa
1280113584Ssimokawa	/* now turn the old into the new */
1281124836Ssimokawa
1282103285Sikob	if (p_char[0] != '*')
1283103285Sikob		fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1284103285Sikob		    p_input_line, p_char[0]);
1285103285Sikob	tp_char[0] = '=';
1286103285Sikob	for (s = tp_line[0]; *s; s++)
1287113584Ssimokawa		if (*s == '*')
1288113584Ssimokawa			*s = '-';
1289113584Ssimokawa	for (i = 0; n <= p_end; i++, n++) {
1290113584Ssimokawa		p_line[n] = tp_line[i];
1291113584Ssimokawa		p_char[n] = tp_char[i];
1292113584Ssimokawa		if (p_char[n] == '-')
1293113584Ssimokawa			p_char[n] = '+';
1294113584Ssimokawa		p_len[n] = tp_len[i];
1295113584Ssimokawa	}
1296113584Ssimokawa
1297113584Ssimokawa	if (i != p_ptrn_lines + 1)
1298103285Sikob		fatal("Malformed patch at line %ld: expected %ld lines, "
1299113584Ssimokawa		    "got %ld\n",
1300108530Ssimokawa		    p_input_line, p_ptrn_lines + 1, i);
1301108530Ssimokawa
1302108530Ssimokawa	i = p_ptrn_lines;
1303108530Ssimokawa	p_ptrn_lines = p_repl_lines;
1304108530Ssimokawa	p_repl_lines = i;
1305108530Ssimokawa
1306103285Sikob	free(tp_line);
1307103285Sikob	free(tp_len);
1308103285Sikob	free(tp_char);
1309103285Sikob
1310103285Sikob	return true;
1311108642Ssimokawa}
1312108642Ssimokawa
1313108642Ssimokawa/*
1314103285Sikob * Return the specified line position in the old file of the old context.
1315103285Sikob */
1316108527SsimokawaLINENUM
1317103285Sikobpch_first(void)
1318106790Ssimokawa{
1319106790Ssimokawa	return p_first;
1320106790Ssimokawa}
1321103285Sikob
1322103285Sikob/*
1323109890Ssimokawa * Return the number of lines of old context.
1324113584Ssimokawa */
1325113584SsimokawaLINENUM
1326103285Sikobpch_ptrn_lines(void)
1327103285Sikob{
1328109890Ssimokawa	return p_ptrn_lines;
1329167086Sjhb}
1330103285Sikob
1331103285Sikob/*
1332103285Sikob * Return the probable line position in the new file of the first line.
1333103285Sikob */
1334106790SsimokawaLINENUM
1335106790Ssimokawapch_newfirst(void)
1336106790Ssimokawa{
1337103285Sikob	return p_newfirst;
1338103285Sikob}
1339103285Sikob
1340103285Sikob/*
1341103285Sikob * Return the number of lines in the replacement text including context.
1342103285Sikob */
1343109890SsimokawaLINENUM
1344167086Sjhbpch_repl_lines(void)
1345103285Sikob{
1346103285Sikob	return p_repl_lines;
1347103285Sikob}
1348103285Sikob
1349106790Ssimokawa/*
1350113584Ssimokawa * Return the number of lines in the whole hunk.
1351106790Ssimokawa */
1352129585SdfrLINENUM
1353103285Sikobpch_end(void)
1354113584Ssimokawa{
1355103285Sikob	return p_end;
1356103285Sikob}
1357103285Sikob
1358103285Sikob/*
1359106790Ssimokawa * Return the number of context lines before the first changed line.
1360106790Ssimokawa */
1361103285SikobLINENUM
1362103285Sikobpch_context(void)
1363113584Ssimokawa{
1364129585Sdfr	return p_context;
1365103285Sikob}
1366120660Ssimokawa
1367103285Sikob/*
1368103285Sikob * Return the length of a particular patch line.
1369103285Sikob */
1370103285Sikobunsigned short
1371103285Sikobpch_line_len(LINENUM line)
1372103285Sikob{
1373103285Sikob	return p_len[line];
1374103285Sikob}
1375103285Sikob
1376103285Sikob/*
1377103285Sikob * Return the control character (+, -, *, !, etc) for a patch line.
1378103285Sikob */
1379123740Speterchar
1380103285Sikobpch_char(LINENUM line)
1381103285Sikob{
1382103285Sikob	return p_char[line];
1383103285Sikob}
1384103285Sikob
1385103285Sikob/*
1386103285Sikob * Return a pointer to a particular patch line.
1387103285Sikob */
1388103285Sikobchar *
1389103285Sikobpfetch(LINENUM line)
1390113584Ssimokawa{
1391113584Ssimokawa	return p_line[line];
1392103285Sikob}
1393103285Sikob
1394103285Sikob/*
1395109892Ssimokawa * Return where in the patch file this hunk began, for error messages.
1396113584Ssimokawa */
1397113584SsimokawaLINENUM
1398113584Ssimokawapch_hunk_beg(void)
1399113584Ssimokawa{
1400103285Sikob	return p_hunk_beg;
1401103285Sikob}
1402113584Ssimokawa
1403113584Ssimokawa/*
1404113584Ssimokawa * Apply an ed script by feeding ed itself.
1405109280Ssimokawa */
1406113584Ssimokawavoid
1407113584Ssimokawado_ed_script(void)
1408113584Ssimokawa{
1409103285Sikob	char	*t;
1410103285Sikob	off_t	beginning_of_this_line;
1411103285Sikob	FILE	*pipefp = NULL;
1412103285Sikob
1413113584Ssimokawa	if (!skip_rest_of_patch) {
1414113584Ssimokawa		if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1415103285Sikob			unlink(TMPOUTNAME);
1416103285Sikob			fatal("can't create temp file %s", TMPOUTNAME);
1417106790Ssimokawa		}
1418106790Ssimokawa		snprintf(buf, buf_size, "%s%s%s", _PATH_ED,
1419106790Ssimokawa		    verbose ? " " : " -s ", TMPOUTNAME);
1420103285Sikob		pipefp = popen(buf, "w");
1421103285Sikob	}
1422109892Ssimokawa	for (;;) {
1423129585Sdfr		beginning_of_this_line = ftello(pfp);
1424103285Sikob		if (pgets(true) == 0) {
1425120660Ssimokawa			next_intuit_at(beginning_of_this_line, p_input_line);
1426103285Sikob			break;
1427103285Sikob		}
1428103285Sikob		p_input_line++;
1429103285Sikob		for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1430103285Sikob			;
1431103285Sikob		/* POSIX defines allowed commands as {a,c,d,i,s} */
1432103285Sikob		if (isdigit((unsigned char)*buf) &&
1433103285Sikob		    (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) {
1434103285Sikob			if (pipefp != NULL)
1435103285Sikob				fputs(buf, pipefp);
1436103285Sikob			if (*t != 'd') {
1437103285Sikob				while (pgets(true)) {
1438103285Sikob					p_input_line++;
1439103285Sikob					if (pipefp != NULL)
1440123740Speter						fputs(buf, pipefp);
1441103285Sikob					if (strEQ(buf, ".\n"))
1442103285Sikob						break;
1443103285Sikob				}
1444103285Sikob			}
1445103285Sikob		} else {
1446103285Sikob			next_intuit_at(beginning_of_this_line, p_input_line);
1447103285Sikob			break;
1448103285Sikob		}
1449103285Sikob	}
1450103285Sikob	if (pipefp == NULL)
1451103285Sikob		return;
1452103285Sikob	fprintf(pipefp, "w\n");
1453103285Sikob	fprintf(pipefp, "q\n");
1454108642Ssimokawa	fflush(pipefp);
1455103285Sikob	pclose(pipefp);
1456103285Sikob	ignore_signals();
1457103285Sikob	if (!check_only) {
1458103285Sikob		if (move_file(TMPOUTNAME, outname) < 0) {
1459113584Ssimokawa			toutkeep = true;
1460113584Ssimokawa			chmod(TMPOUTNAME, filemode);
1461113584Ssimokawa		} else
1462103285Sikob			chmod(outname, filemode);
1463109892Ssimokawa	}
1464109892Ssimokawa	set_signals(1);
1465113584Ssimokawa}
1466113584Ssimokawa
1467103285Sikob/*
1468103285Sikob * Choose the name of the file to be patched based on POSIX rules.
1469113584Ssimokawa * NOTE: the POSIX rules are amazingly stupid and we only follow them
1470113584Ssimokawa *       if the user specified --posix or set POSIXLY_CORRECT.
1471113584Ssimokawa */
1472113584Ssimokawastatic char *
1473113584Ssimokawaposix_name(const struct file_name *names, bool assume_exists)
1474113584Ssimokawa{
1475103285Sikob	char *path = NULL;
1476103285Sikob	int i;
1477103285Sikob
1478103285Sikob	/*
1479113584Ssimokawa	 * POSIX states that the filename will be chosen from one
1480113584Ssimokawa	 * of the old, new and index names (in that order) if
1481103285Sikob	 * the file exists relative to CWD after -p stripping.
1482113584Ssimokawa	 */
1483113584Ssimokawa	for (i = 0; i < MAX_FILE; i++) {
1484103285Sikob		if (names[i].path != NULL && names[i].exists) {
1485103285Sikob			path = names[i].path;
1486103285Sikob			break;
1487113584Ssimokawa		}
1488103285Sikob	}
1489103285Sikob	if (path == NULL && !assume_exists) {
1490103285Sikob		/*
1491103285Sikob		 * No files found, look for something we can checkout from
1492106790Ssimokawa		 * RCS/SCCS dirs.  Same order as above.
1493106790Ssimokawa		 */
1494113584Ssimokawa		for (i = 0; i < MAX_FILE; i++) {
1495109890Ssimokawa			if (names[i].path != NULL &&
1496109890Ssimokawa			    (path = checked_in(names[i].path)) != NULL)
1497109890Ssimokawa				break;
1498109890Ssimokawa		}
1499109890Ssimokawa		/*
1500109890Ssimokawa		 * Still no match?  Check to see if the diff could be creating
1501113584Ssimokawa		 * a new file.
1502109890Ssimokawa		 */
1503113584Ssimokawa		if (path == NULL && ok_to_create_file &&
1504113584Ssimokawa		    names[NEW_FILE].path != NULL)
1505113584Ssimokawa			path = names[NEW_FILE].path;
1506109890Ssimokawa	}
1507109890Ssimokawa
1508109890Ssimokawa	return path ? xstrdup(path) : NULL;
1509109890Ssimokawa}
1510109890Ssimokawa
1511113584Ssimokawastatic char *
1512109890Ssimokawacompare_names(const struct file_name *names, bool assume_exists, int phase)
1513109890Ssimokawa{
1514109890Ssimokawa	size_t min_components, min_baselen, min_len, tmp;
1515109890Ssimokawa	char *best = NULL;
1516109890Ssimokawa	char *path;
1517109890Ssimokawa	int i;
1518109890Ssimokawa
1519109890Ssimokawa	/*
1520109890Ssimokawa	 * The "best" name is the one with the fewest number of path
1521109890Ssimokawa	 * components, the shortest basename length, and the shortest
1522109890Ssimokawa	 * overall length (in that order).  We only use the Index: file
1523109890Ssimokawa	 * if neither of the old or new files could be intuited from
1524109890Ssimokawa	 * the diff header.
1525106790Ssimokawa	 */
1526103285Sikob	min_components = min_baselen = min_len = SIZE_MAX;
1527103285Sikob	for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1528103285Sikob		path = names[i].path;
1529103285Sikob		if (path == NULL ||
1530103285Sikob		    (phase == 1 && !names[i].exists && !assume_exists) ||
1531109890Ssimokawa		    (phase == 2 && checked_in(path) == NULL))
1532129585Sdfr			continue;
1533109890Ssimokawa		if ((tmp = num_components(path)) > min_components)
1534109890Ssimokawa			continue;
1535103285Sikob		if (tmp < min_components) {
1536103285Sikob			min_components = tmp;
1537109890Ssimokawa			best = path;
1538109890Ssimokawa		}
1539109890Ssimokawa		if ((tmp = strlen(basename(path))) > min_baselen)
1540109890Ssimokawa			continue;
1541109179Ssimokawa		if (tmp < min_baselen) {
1542109890Ssimokawa			min_baselen = tmp;
1543103285Sikob			best = path;
1544113584Ssimokawa		}
1545109179Ssimokawa		if ((tmp = strlen(path)) > min_len)
1546109179Ssimokawa			continue;
1547170374Ssimokawa		min_len = tmp;
1548103285Sikob		best = path;
1549103285Sikob	}
1550103285Sikob	return best;
1551103285Sikob}
1552109890Ssimokawa
1553109892Ssimokawa/*
1554109890Ssimokawa * Choose the name of the file to be patched based the "best" one
1555170374Ssimokawa * available.
1556109890Ssimokawa */
1557109890Ssimokawastatic char *
1558120660Ssimokawabest_name(const struct file_name *names, bool assume_exists)
1559109890Ssimokawa{
1560113584Ssimokawa	char *best;
1561113584Ssimokawa
1562109890Ssimokawa	best = compare_names(names, assume_exists, 1);
1563109890Ssimokawa	if (best == NULL) {
1564109890Ssimokawa		best = compare_names(names, assume_exists, 2);
1565113584Ssimokawa		/*
1566113584Ssimokawa		 * Still no match?  Check to see if the diff could be creating
1567113584Ssimokawa		 * a new file.
1568113584Ssimokawa		 */
1569109892Ssimokawa		if (best == NULL && ok_to_create_file &&
1570109890Ssimokawa		    names[NEW_FILE].path != NULL)
1571113584Ssimokawa			best = names[NEW_FILE].path;
1572113584Ssimokawa	}
1573109892Ssimokawa
1574113584Ssimokawa	return best ? xstrdup(best) : NULL;
1575113584Ssimokawa}
1576109892Ssimokawa
1577103285Sikobstatic size_t
1578109890Ssimokawanum_components(const char *path)
1579109890Ssimokawa{
1580109890Ssimokawa	size_t n;
1581109403Ssimokawa	const char *cp;
1582170374Ssimokawa
1583113584Ssimokawa	for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1584113584Ssimokawa		while (*cp == '/')
1585109890Ssimokawa			cp++;		/* skip consecutive slashes */
1586109890Ssimokawa	}
1587113584Ssimokawa	return n;
1588113584Ssimokawa}
1589113584Ssimokawa
1590109890Ssimokawa/*
1591109890Ssimokawa * Convert number at NPTR into LINENUM and save address of first
1592109890Ssimokawa * character that is not a digit in ENDPTR.  If conversion is not
1593113584Ssimokawa * possible, call fatal.
1594109890Ssimokawa */
1595113584Ssimokawastatic LINENUM
1596109403Ssimokawastrtolinenum(char *nptr, char **endptr)
1597109403Ssimokawa{
1598109403Ssimokawa	LINENUM rv;
1599113584Ssimokawa	char c;
1600109890Ssimokawa	char *p;
1601109890Ssimokawa	const char *errstr;
1602113584Ssimokawa
1603113584Ssimokawa	for (p = nptr; isdigit((unsigned char)*p); p++)
1604167629Ssimokawa		;
1605109890Ssimokawa
1606113584Ssimokawa	if (p == nptr)
1607113584Ssimokawa		malformed();
1608113584Ssimokawa
1609113584Ssimokawa	c = *p;
1610109403Ssimokawa	*p = '\0';
1611109890Ssimokawa
1612109890Ssimokawa	rv = strtonum(nptr, 0, LINENUM_MAX, &errstr);
1613109890Ssimokawa	if (errstr != NULL)
1614109890Ssimokawa		fatal("invalid line number at line %ld: `%s' is %s\n",
1615109890Ssimokawa		    p_input_line, nptr, errstr);
1616113584Ssimokawa
1617109890Ssimokawa	*p = c;
1618109890Ssimokawa	*endptr = p;
1619109890Ssimokawa
1620109356Ssimokawa	return rv;
1621109356Ssimokawa}
1622113584Ssimokawa