1 /* @(#)pch.c	1.38 19/08/17 2011-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)pch.c	1.38 19/08/17 2011-2019 J. Schilling";
6 #endif
7 /*
8  *	Copyright (c) 1986-1988 Larry Wall
9  *	Copyright (c) 1990 Wayne Davison
10  *	Copyright (c) 2011-2019 J. Schilling
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following condition is met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  * this condition and the following disclaimer.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #define	EXT	extern
32 #include "common.h"
33 #include "util.h"
34 #undef	EXT
35 #define	EXT
36 #include "pch.h"
37 
38 /* Patch (diff listing) abstract type. */
39 
40 static FILE *pfp;			/* patch file pointer */
41 static off_t	p_filesize;		/* size of the patch file */
42 static LINENUM p_first;			/* 1st line number */
43 static LINENUM p_newfirst;		/* 1st line number of replacement */
44 static LINENUM p_ptrn_lines;		/* # lines in pattern */
45 EXT    LINENUM p_repl_lines;		/* # lines in replacement text */
46 static LINENUM p_end = -1;		/* last line in hunk */
47 static LINENUM p_max;			/* max allowed value of p_end */
48 static LINENUM p_context = 3;		/* # of context lines */
49 static LINENUM p_input_line = 0;	/* current line # from patch file */
50 static char **p_line = Null(char **);	/* the text of the hunk */
51 static size_t *p_len = Null(size_t *);	/* length of each line */
52 static char *p_char = Nullch;		/* +, -, and ! */
53 static LINENUM hunkmax = INITHUNKMAX;	/* size of above arrays to begin with */
54 static size_t p_indent;			/* indent to patch */
55 static LINENUM p_base;			/* where to intuit this time */
56 static LINENUM p_bline;			/* line # of p_base */
57 static LINENUM p_start;			/* where intuit found a patch */
58 static LINENUM p_sline;			/* and the line number for it */
59 static LINENUM p_hunk_beg;		/* line number of current hunk */
60 static LINENUM p_efake = -1;		/* end of faked up lines--don't free */
61 static LINENUM p_bfake = -1;		/* beg of faked up lines */
62 
63 static	void	grow_hunkmax __PR((void));
64 static	int	intuit_diff_type __PR((void));
65 static	void	next_intuit_at __PR((LINENUM file_pos, LINENUM file_line));
66 static	void	skip_to __PR((off_t file_pos, off_t file_line));
67 static	void	malformed __PR((void));
68 static	ssize_t	pgets __PR((char **bfp, size_t *szp, FILE *fp));
69 
70 /* Prepare to look for the next patch in the patch file. */
71 
72 void
re_patch()73 re_patch()
74 {
75 	p_first = Nulline;
76 	p_newfirst = Nulline;
77 	p_ptrn_lines = Nulline;
78 	p_repl_lines = Nulline;
79 	p_end = (LINENUM)-1;
80 	p_max = Nulline;
81 	p_indent = 0;
82 }
83 
84 /* Open the patch file at the beginning of time. */
85 
86 void
open_patch_file(filename)87 open_patch_file(filename)
88 	char *filename;
89 {
90 	if (filename == Nullch || !*filename || strEQ(filename, "-")) {
91 		pfp = fopen(TMPPATNAME, "w");
92 		if (pfp == Nullfp)
93 			pfatal(_("can't create %s.\n"), TMPPATNAME);
94 		while (fgetaline(stdin, &buf, &bufsize) > 0)
95 			fputs(buf, pfp);
96 		Fclose(pfp);
97 		filename = TMPPATNAME;
98 	}
99 	pfp = fopen(filename, "r");
100 	if (pfp == Nullfp)
101 		pfatal(_("patch file %s not found\n"), filename);
102 	Fstat(fileno(pfp), &file_stat);
103 	p_filesize = file_stat.st_size;
104 	next_intuit_at((LINENUM)0, (LINENUM)1);	/* start at the beginning */
105 	set_hunkmax();
106 }
107 
108 /* Make sure our dynamically realloced tables are malloced to begin with. */
109 
110 void
set_hunkmax()111 set_hunkmax()
112 {
113 	if (p_line == Null(char **))
114 		p_line = (char **) malloc(hunkmax * sizeof (char *));
115 	if (p_len == Null(size_t *))
116 		p_len  = (size_t *) malloc(hunkmax * sizeof (size_t));
117 	if (p_char == Nullch)
118 		p_char = (char *)  malloc(hunkmax * sizeof (char));
119 }
120 
121 /* Enlarge the arrays containing the current hunk of patch. */
122 
123 static void
grow_hunkmax()124 grow_hunkmax()
125 {
126 	char	**op_line = p_line;
127 	size_t	*op_len = p_len;
128 	char	*op_char = p_char;
129 
130 	hunkmax *= 2;
131 	/*
132 	 * Note that on most systems, only the p_line array ever gets fresh
133 	 * memory since p_len can move into p_line's old space, and p_char can
134 	 * move into p_len's old space.  Not on PDP-11's however.  But it
135 	 * doesn't matter.
136 	 */
137 	assert(p_line != Null(char **) &&
138 	    p_len != Null(size_t *) &&
139 	    p_char != Nullch);
140 	p_line = (char **) realloc((char *)p_line, hunkmax * sizeof (char *));
141 	p_len  = (size_t *) realloc((char *)p_len,  hunkmax * sizeof (size_t));
142 	p_char = (char *)  realloc((char *)p_char, hunkmax * sizeof (char));
143 	if (p_line != Null(char **) &&
144 	    p_len != Null(size_t *) &&
145 	    p_char != Nullch) {
146 		return;
147 	}
148 	if (p_line == Null(char **))
149 		p_line = op_line;
150 	if (p_len == Null(size_t *))
151 		p_len = op_len;
152 	if (p_char == Null(char *))
153 		p_char = op_char;
154 	hunkmax /= 2;
155 
156 	if (!using_plan_a)
157 		fatal(_("patch: out of memory (grow_hunkmax)\n"));
158 
159 	out_of_mem = TRUE;	/* whatever is null will be allocated again */
160 				/* from within plan_a(), of all places */
161 }
162 
163 /* True if the remainder of the patch file contains a diff of some sort. */
164 
165 bool
there_is_another_patch()166 there_is_another_patch()
167 {
168 	if (p_base != 0L && p_base >= p_filesize) {
169 		if (verbose)
170 			say(_("done\n"));
171 		return (FALSE);
172 	}
173 	if (verbose)
174 		say(_("Hmm..."));
175 	diff_type = intuit_diff_type();
176 	if (!diff_type) {
177 		if (p_base != 0L) {
178 			if (verbose)
179 				say(_("  Ignoring the trailing garbage.\ndone\n"));
180 		} else {
181 		    say(_("  I can't seem to find a patch in there anywhere.\n"));
182 		}
183 		return (FALSE);
184 	}
185 	if (verbose)
186 		say(p_base == 0L ?
187 		    _("  Looks like %s to me...\n") :
188 		    _("  The next patch looks like %s to me...\n"),
189 		    diff_type == UNI_DIFF ? "a unified diff" :
190 		    diff_type == CONTEXT_DIFF ? "a context diff" :
191 		    diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
192 		    diff_type == NORMAL_DIFF ? "a normal diff" :
193 		    "an ed script");
194 	if (p_indent && verbose) {
195 		if (p_indent == 1)
196 			say("(Patch is indented 1 space.)\n");
197 		else
198 			say("(Patch is indented %lld spaces.)\n",
199 			(Llong)p_indent);
200 	}
201 	skip_to(p_start, p_sline);
202 	while (filearg[0] == Nullch) {
203 		if (force) {
204 			say(_("No file to patch.  Skipping...\n"));
205 			filearg[0] = savestr(bestguess);
206 			return (TRUE);
207 		}
208 		ask(_("File to patch: "));
209 		if (*buf != '\n') {
210 			if (bestguess)
211 				free(bestguess);
212 			bestguess = savestr(buf);
213 			filearg[0] = fetchname(buf, 0, FALSE, NULL);
214 		}
215 		if (filearg[0] == Nullch) {
216 			ask(_("No file found--skip this patch? [n] "));
217 			if (*buf != 'y') {
218 				continue;
219 			}
220 			if (verbose)
221 				say(_("Skipping patch...\n"));
222 			filearg[0] = fetchname(bestguess, 0, TRUE, NULL);
223 			skip_rest_of_patch = TRUE;
224 			return (TRUE);
225 		}
226 	}
227 	return (TRUE);
228 }
229 
230 /* Determine what kind of diff is in the remaining part of the patch file. */
231 
232 static int
intuit_diff_type()233 intuit_diff_type()
234 {
235 	off_t this_line = 0;
236 	off_t previous_line;
237 	off_t first_command_line = (off_t)-1;
238 	LINENUM fcl_line = 0;
239 	bool last_line_was_command = FALSE;
240 	bool this_is_a_command = FALSE;
241 	bool stars_last_line = FALSE;
242 	bool stars_this_line = FALSE;
243 	size_t indent;
244 	char *s;
245 	char *t;
246 	char *indtmp = Nullch;
247 	char *oldtmp = Nullch;
248 	char *newtmp = Nullch;
249 	char *indname = Nullch;
250 	char *oldname = Nullch;
251 	char *newname = Nullch;
252 	int retval;
253 	bool no_filearg = (filearg[0] == Nullch);
254 
255 	is_null_time[0] = is_null_time[1] = FALSE;
256 	ok_to_create_file = FALSE;
257 	Fseek(pfp, p_base, 0);
258 	p_input_line = p_bline - 1;
259 	for (;;) {
260 		previous_line = this_line;
261 		last_line_was_command = this_is_a_command;
262 		stars_last_line = stars_this_line;
263 		this_line = ftell(pfp);
264 		indent = 0;
265 		p_input_line++;
266 		if (fgetaline(pfp, &buf, &bufsize) <= 0) {
267 			if (first_command_line >= 0L) {
268 				/* nothing but deletes!? */
269 				p_start = first_command_line;
270 				p_sline = fcl_line;
271 				retval = ED_DIFF;
272 				goto scan_exit;
273 			} else {
274 				p_start = this_line;
275 				p_sline = p_input_line;
276 				retval = 0;
277 				goto scan_exit;
278 			}
279 		}
280 		for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
281 			if (*s == '\t')
282 				indent += 8 - (indent % 8);
283 			else
284 				indent++;
285 		}
286 		for (t = s; isdigit(UCH *t) || *t == ','; t++) {
287 			;
288 			/* LINTED */
289 		}
290 		this_is_a_command = (isdigit(UCH *s) &&
291 		    (*t == 'd' || *t == 'c' || *t == 'a'));
292 		if (first_command_line < 0L && this_is_a_command) {
293 			first_command_line = this_line;
294 			fcl_line = p_input_line;
295 			p_indent = indent;	/* assume this for now */
296 
297 			if (t[1] == '\n') {
298 				p_start = first_command_line;
299 				p_sline = fcl_line;
300 				retval = ED_DIFF;
301 				goto scan_exit;
302 			}
303 		}
304 		if (first_command_line < 0L && isdigit(UCH *s) &&
305 		    (*t == 'i' || *t == 's')) {
306 			p_start = first_command_line;
307 			p_sline = fcl_line;
308 			retval = ED_DIFF;
309 			goto scan_exit;
310 		}
311 		if (!stars_last_line && strnEQ(s, "*** ", 4)) {
312 			oldtmp = savestr(s+4);
313 		} else if (strnEQ(s, "--- ", 4)) {
314 			newtmp = savestr(s+4);
315 		} else if (strnEQ(s, "+++ ", 4)) {
316 			oldtmp = savestr(s+4);	/* pretend it is the old name */
317 		} else if (strnEQ(s, "Index:", 6)) {
318 			indtmp = savestr(s+6);
319 		} else if (wall_plus &&
320 			    strnEQ(s, "Prereq:", 7)) {
321 			for (t = s+7; isspace(UCH *t); t++) {
322 				;
323 				/* LINTED */
324 			}
325 			revision = savestr(t);
326 			for (t = revision; *t && !isspace(UCH *t); t++) {
327 				;
328 				/* LINTED */
329 			}
330 			*t = '\0';
331 			if (!*revision) {
332 				free(revision);
333 				revision = Nullch;
334 			}
335 		}
336 		if ((!diff_type || diff_type == ED_DIFF) &&
337 		    first_command_line >= 0L &&
338 		    strEQ(s, ".\n")) {
339 			p_indent = indent;
340 			p_start = first_command_line;
341 			p_sline = fcl_line;
342 			retval = ED_DIFF;
343 			goto scan_exit;
344 		}
345 		if ((!diff_type || diff_type == UNI_DIFF) &&
346 		    strnEQ(s, "@@ -", 4)) {
347 			if (!atolnum(s+3))
348 				ok_to_create_file = TRUE;
349 			p_indent = indent;
350 			p_start = this_line;
351 			p_sline = p_input_line;
352 			retval = UNI_DIFF;
353 			/*
354 			 * "--- " was scanned as "newname" and
355 			 * "+++ " was scanned as "oldname".
356 			 * Swap names for correct POSIX name selcetion.
357 			 */
358 			t = newtmp;
359 			newtmp = oldtmp;
360 			oldtmp = t;
361 			goto scan_exit;
362 		}
363 		stars_this_line = strnEQ(s, "********", 8);
364 		if ((!diff_type || diff_type == CONTEXT_DIFF) &&
365 		    stars_last_line &&
366 		    strnEQ(s, "*** ", 4)) {
367 			if (!atolnum(s+4))
368 				ok_to_create_file = TRUE;
369 			/* if this is a new context diff the character just */
370 			/* before the newline is a '*'. */
371 			while (*s != '\n')
372 				s++;
373 			p_indent = indent;
374 			p_start = previous_line;
375 			p_sline = p_input_line - 1;
376 			retval = (*(s-1) == '*' ?
377 					NEW_CONTEXT_DIFF : CONTEXT_DIFF);
378 			goto scan_exit;
379 		}
380 		if ((!diff_type || diff_type == NORMAL_DIFF) &&
381 		    last_line_was_command &&
382 		    (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
383 			p_start = previous_line;
384 			p_sline = p_input_line - 1;
385 			p_indent = indent;
386 			retval = NORMAL_DIFF;
387 			goto scan_exit;
388 		}
389 	}
390 
391 	scan_exit:
392 
393 	if (no_filearg) {
394 		if (indtmp != Nullch) {
395 			indname = fetchname(indtmp, strippath,
396 						ok_to_create_file, NULL);
397 		}
398 		if (oldtmp != Nullch) {
399 			oldname = fetchname(oldtmp, strippath,
400 						ok_to_create_file,
401 						&file_times[0]);
402 			is_null_time[0] = file_times[0].dt_sec == 0;
403 		}
404 		if (newtmp != Nullch) {
405 			newname = fetchname(newtmp, strippath,
406 						ok_to_create_file,
407 						&file_times[1]);
408 			is_null_time[1] = file_times[1].dt_sec == 0;
409 		}
410 
411 		if (do_wall && oldname && newname) {
412 			/*
413 			 * Old Larry Wall algorithm from patch-2.0.
414 			 * This heuristic works only if the old name is
415 			 * something like "file.orig" and the new name is just
416 			 * "file". For "original" vs. "changed", it fails.
417 			 */
418 			if (strlen(oldname) < strlen(newname))
419 				filearg[0] = savestr(oldname);
420 			else
421 				filearg[0] = savestr(newname);
422 		} else if (oldname) {
423 			filearg[0] = savestr(oldname);
424 		} else if (newname) {
425 			filearg[0] = savestr(newname);
426 		} else if (indname) {
427 			filearg[0] = savestr(indname);
428 		}
429 	}
430 	if (bestguess) {
431 		free(bestguess);
432 		bestguess = Nullch;
433 	}
434 	if (filearg[0] != Nullch) {
435 		bestguess = savestr(filearg[0]);
436 	} else if (indtmp != Nullch) {
437 		bestguess = fetchname(indtmp, strippath, TRUE, NULL);
438 	} else {
439 		if (oldtmp != Nullch)
440 			oldname = fetchname(oldtmp, strippath, TRUE, NULL);
441 		if (newtmp != Nullch)
442 			newname = fetchname(newtmp, strippath, TRUE, NULL);
443 
444 		if (do_wall && oldname && newname) {
445 			/*
446 			 * Old Larry Wall algorithm from patch-2.0.
447 			 * This heuristic works only if the old name is
448 			 * something like "file.orig" and the new name is just
449 			 * "file". For "original" vs. "changed", it fails.
450 			 */
451 			if (strlen(oldname) < strlen(newname))
452 				bestguess = savestr(oldname);
453 			else
454 				bestguess = savestr(newname);
455 		} else if (oldname) {
456 			bestguess = savestr(oldname);
457 		} else if (newname) {
458 			bestguess = savestr(newname);
459 		}
460 	}
461 	if (indtmp != Nullch)
462 		free(indtmp);
463 	if (oldtmp != Nullch)
464 		free(oldtmp);
465 	if (newtmp != Nullch)
466 		free(newtmp);
467 	if (indname != Nullch)
468 		free(indname);
469 	if (oldname != Nullch)
470 		free(oldname);
471 	if (newname != Nullch)
472 		free(newname);
473 	return (retval);
474 }
475 
476 /* Remember where this patch ends so we know where to start up again. */
477 
478 static void
next_intuit_at(file_pos,file_line)479 next_intuit_at(file_pos, file_line)
480 	LINENUM	file_pos;
481 	LINENUM	file_line;
482 {
483 	p_base = file_pos;
484 	p_bline = file_line;
485 }
486 
487 /* Basically a verbose fseek() to the actual diff listing. */
488 
489 static void
skip_to(file_pos,file_line)490 skip_to(file_pos, file_line)
491 	off_t file_pos;
492 	off_t file_line;
493 {
494 	ssize_t	ret;
495 
496 	assert(p_base <= file_pos);
497 	if (verbose && p_base < file_pos) {
498 		Fseek(pfp, p_base, 0);
499 		say(
500 _("The text leading up to this was:\n--------------------------\n"));
501 		while (ftell(pfp) < file_pos) {
502 			ret = fgetaline(pfp, &buf, &bufsize);
503 			assert(ret > 0);
504 			say("|%s", buf);
505 		}
506 		say("--------------------------\n");
507 	} else {
508 		Fseek(pfp, file_pos, 0);
509 	}
510 	p_input_line = file_line - 1;
511 }
512 
513 /* Make this a function for better debugging.  */
514 static void
malformed()515 malformed()
516 {
517 	fatal(_("Malformed patch at line %lld: %s"), (Llong)p_input_line, buf);
518 		/* about as informative as "Syntax error" in C */
519 }
520 
521 /* True if there is more of the current diff listing to process. */
522 
523 bool
another_hunk()524 another_hunk()
525 {
526 	char	*s;
527 	ssize_t	ret;
528 	LINENUM	context = 0;
529 
530 	while (p_end >= 0) {
531 		if (p_end == p_efake)
532 			p_end = p_bfake;	/* don't free twice */
533 		else
534 			free(p_line[p_end]);
535 		p_end--;
536 	}
537 	assert(p_end == -1);
538 	p_efake = -1;
539 
540 	p_max = hunkmax;		/* gets reduced when --- found */
541 	if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
542 		off_t	line_beginning = ftell(pfp);
543 					/* file pos of the current line */
544 		LINENUM repl_beginning = 0; /* index of --- line */
545 		LINENUM fillcnt = 0;	/* #lines of missing ptrn or repl */
546 		LINENUM fillsrc;	/* index of first line to copy */
547 		LINENUM filldst;	/* index of first missing line */
548 		bool ptrn_spaces_eaten = FALSE;	/* ptrn w. slightly misformed */
549 		bool repl_could_be_missing = TRUE;
550 					/* no + or ! lines in this hunk */
551 		bool repl_missing = FALSE; /* we are now backtracking */
552 		off_t repl_backtrack_position = 0;
553 					/* file pos of first repl line */
554 		LINENUM repl_patch_line; /* input line number for same */
555 		LINENUM ptrn_copiable = 0;
556 					/* # of copiable lines in ptrn */
557 
558 		fillsrc = filldst = repl_patch_line = 0; /* Make gcc quiet */
559 
560 		ret = pgets(&buf, &bufsize, pfp);
561 		p_input_line++;
562 		if (ret <= 0 || strnNE(buf, "********", 8)) {
563 			next_intuit_at(line_beginning, p_input_line);
564 			return (FALSE);
565 		}
566 		p_context = 100;
567 		p_hunk_beg = p_input_line + 1;
568 		while (p_end < p_max) {
569 			line_beginning = ftell(pfp);
570 			ret = pgets(&buf, &bufsize, pfp);
571 			p_input_line++;
572 			if (ret <= 0) {
573 				if (p_max - p_end < 4)
574 					Strcpy(buf, "  \n");  /* assume blank lines got chopped */
575 				else {
576 					if (repl_beginning &&
577 					    repl_could_be_missing) {
578 						repl_missing = TRUE;
579 						goto hunk_done;
580 					}
581 					fatal(
582 _("Unexpected end of file in patch.\n"));
583 				}
584 			}
585 			p_end++;
586 			assert(p_end < hunkmax);
587 			p_char[p_end] = *buf;
588 #ifdef zilog
589 			p_line[(short)p_end] = Nullch;
590 #else
591 			p_line[p_end] = Nullch;
592 #endif
593 			switch (*buf) {
594 
595 			case '*':
596 				if (strnEQ(buf, "********", 8)) {
597 					if (repl_beginning &&
598 					    repl_could_be_missing) {
599 						repl_missing = TRUE;
600 						goto hunk_done;
601 					} else {
602 						fatal(
603 _("Unexpected end of hunk at line %lld.\n"),
604 						    (Llong)p_input_line);
605 					}
606 				}
607 				if (p_end != 0) {
608 					if (repl_beginning &&
609 					    repl_could_be_missing) {
610 						repl_missing = TRUE;
611 						goto hunk_done;
612 					}
613 					fatal(_("Unexpected *** at line %lld: %s"),
614 						(Llong)p_input_line, buf);
615 				}
616 				context = 0;
617 				p_line[p_end] = savestr(buf);
618 				if (out_of_mem) {
619 					p_end--;
620 					return (FALSE);
621 				}
622 				for (s = buf; *s && !isdigit(UCH *s); s++) {
623 					;
624 					/* LINTED */
625 				}
626 				if (!*s)
627 					malformed();
628 				if (strnEQ(s, "0,0", 3))
629 					strcpy(s, s+2);
630 				p_first = atolnum(s);
631 				while (isdigit(UCH *s))
632 					s++;
633 				if (*s == ',') {
634 					for (; *s && !isdigit(UCH *s); s++) {
635 						;
636 						/* LINTED */
637 					}
638 					if (!*s)
639 						malformed();
640 					p_ptrn_lines = atolnum(s) -
641 								p_first + 1;
642 				} else if (p_first) {
643 					p_ptrn_lines = 1;
644 				} else {
645 					p_ptrn_lines = 0;
646 					p_first = 1;
647 				}
648 				p_max = p_ptrn_lines + 6;	/* we need this much at least */
649 				while (p_max >= hunkmax && !out_of_mem)
650 					grow_hunkmax();
651 				p_max = hunkmax;
652 				if (out_of_mem) {
653 					p_end = -1;
654 					return (FALSE);
655 				}
656 				break;
657 			case '-':
658 				if (buf[1] == '-') {
659 					if (repl_beginning ||
660 					    (p_end != p_ptrn_lines + 1 +
661 					    (p_char[p_end-1] == '\n'))) {
662 						if (p_end == 1) {
663 							/* `old' lines were omitted - set up to fill */
664 							/* them in from 'new' context lines. */
665 							p_end = p_ptrn_lines + 1;
666 							fillsrc = p_end + 1;
667 							filldst = 1;
668 							fillcnt = p_ptrn_lines;
669 						} else {
670 							if (repl_beginning) {
671 								if (repl_could_be_missing) {
672 									repl_missing = TRUE;
673 									goto hunk_done;
674 								}
675 								fatal(
676 _("Duplicate \"---\" at line %lld--check line numbers at line %lld.\n"),
677 								    (Llong)p_input_line,
678 								    (Llong)(p_hunk_beg + repl_beginning));
679 							} else {
680 								fatal(
681 _("%s \"---\" at line %lld--check line numbers at line %lld.\n"),
682 								    (p_end <= p_ptrn_lines
683 								    ? _("Premature")
684 								    : _("Overdue")),
685 								    (Llong)p_input_line,
686 								    (Llong)p_hunk_beg);
687 							}
688 						}
689 					}
690 					repl_beginning = p_end;
691 					repl_backtrack_position = ftell(pfp);
692 					repl_patch_line = p_input_line;
693 					p_line[p_end] = savestr(buf);
694 					if (out_of_mem) {
695 						p_end--;
696 						return (FALSE);
697 					}
698 					p_char[p_end] = '=';
699 					for (s = buf;
700 					    *s && !isdigit(UCH *s); s++) {
701 						;
702 						/* LINTED */
703 					}
704 					if (!*s)
705 						malformed();
706 					p_newfirst = atolnum(s);
707 					while (isdigit(UCH *s))
708 						s++;
709 					if (*s == ',') {
710 						for (;
711 						    *s && !isdigit(UCH *s);
712 						    s++) {
713 							;
714 							/* LINTED */
715 						}
716 						if (!*s)
717 							malformed();
718 						p_repl_lines = atolnum(s) - p_newfirst + 1;
719 					} else if (p_newfirst) {
720 						p_repl_lines = 1;
721 					} else {
722 						p_repl_lines = 0;
723 						p_newfirst = 1;
724 					}
725 					p_max = p_repl_lines + p_end;
726 					while (p_max >= hunkmax && !out_of_mem)
727 						grow_hunkmax();
728 					if (out_of_mem) {
729 						p_end = -1;
730 						return (FALSE);
731 					}
732 					if (p_repl_lines != ptrn_copiable &&
733 					    (p_context != 0 || p_repl_lines != 1))
734 						repl_could_be_missing = FALSE;
735 					break;
736 				}
737 				goto change_line;
738 			case '+':
739 			case '!':
740 				repl_could_be_missing = FALSE;
741 			change_line:
742 				if (buf[1] == '\n' && canonicalize)
743 					strcpy(buf+1, " \n");
744 				if (!isspace(UCH buf[1]) &&
745 				    buf[1] != '>' && buf[1] != '<' &&
746 				    repl_beginning && repl_could_be_missing) {
747 					repl_missing = TRUE;
748 					goto hunk_done;
749 				}
750 				if (context >= 0) {
751 					if (context < p_context)
752 						p_context = context;
753 					context = -1000;
754 				}
755 				p_line[p_end] = savestr(buf+2);
756 				if (out_of_mem) {
757 					p_end--;
758 					return (FALSE);
759 				}
760 				break;
761 			case '\t':
762 			case '\n':	/* assume the 2 spaces got eaten */
763 				if (repl_beginning && repl_could_be_missing &&
764 				    (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) {
765 					repl_missing = TRUE;
766 					goto hunk_done;
767 				}
768 				p_line[p_end] = savestr(buf);
769 				if (out_of_mem) {
770 					p_end--;
771 					return (FALSE);
772 				}
773 				if (p_end != p_ptrn_lines + 1) {
774 					ptrn_spaces_eaten |= (repl_beginning != 0);
775 					context++;
776 					if (!repl_beginning)
777 						ptrn_copiable++;
778 					p_char[p_end] = ' ';
779 				}
780 				break;
781 			case ' ':
782 				if (!isspace(UCH buf[1]) &&
783 				    repl_beginning && repl_could_be_missing) {
784 					repl_missing = TRUE;
785 					goto hunk_done;
786 				}
787 				context++;
788 				if (!repl_beginning)
789 					ptrn_copiable++;
790 				p_line[p_end] = savestr(buf+2);
791 				if (out_of_mem) {
792 					p_end--;
793 					return (FALSE);
794 				}
795 				break;
796 			default:
797 				if (repl_beginning && repl_could_be_missing) {
798 					repl_missing = TRUE;
799 					goto hunk_done;
800 				}
801 				malformed();
802 			}
803 			/* set up p_len for strncmp() so we don't have to */
804 			/* assume null termination */
805 			if (p_line[p_end])
806 				p_len[p_end] = strlen(p_line[p_end]);
807 			else
808 				p_len[p_end] = 0;
809 		}
810 
811 	hunk_done:
812 		if (p_end >= 0 && !repl_beginning)
813 			fatal(_("No --- found in patch at line %lld\n"),
814 				(Llong)pch_hunk_beg());
815 
816 		if (repl_missing) {
817 			/* reset state back to just after --- */
818 			p_input_line = repl_patch_line;
819 			for (p_end--; p_end > repl_beginning; p_end--)
820 				free(p_line[p_end]);
821 			Fseek(pfp, repl_backtrack_position, 0);
822 
823 			/* redundant 'new' context lines were omitted - set */
824 			/* up to fill them in from the old file context */
825 			if (!p_context && p_repl_lines == 1) {
826 				p_repl_lines = 0;
827 				p_max--;
828 			}
829 			fillsrc = 1;
830 			filldst = repl_beginning+1;
831 			fillcnt = p_repl_lines;
832 			p_end = p_max;
833 		} else if (!p_context && fillcnt == 1) {
834 			/* the first hunk was a null hunk with no context */
835 			/* and we were expecting one line -- fix it up. */
836 			while (filldst < p_end) {
837 				p_line[filldst] = p_line[filldst+1];
838 				p_char[filldst] = p_char[filldst+1];
839 				p_len[filldst] = p_len[filldst+1];
840 				filldst++;
841 			}
842 #if 0
843 			repl_beginning--; /* this doesn't need to be fixed */
844 #endif
845 			p_end--;
846 			p_first++;	/* do append rather than insert */
847 			fillcnt = 0;
848 			p_ptrn_lines = 0;
849 		}
850 
851 		if (diff_type == CONTEXT_DIFF &&
852 		    (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context))) {
853 			if (verbose)
854 				say("%s\n%s\n%s\n",
855 _("(Fascinating--this is really a new-style context diff but without"),
856 _("the telltale extra asterisks on the *** line that usually indicate"),
857 _("the new style...)"));
858 			diff_type = NEW_CONTEXT_DIFF;
859 		}
860 
861 		/* if there were omitted context lines, fill them in now */
862 		if (fillcnt) {
863 			p_bfake = filldst;		/* remember where not to free() */
864 			p_efake = filldst + fillcnt - 1;
865 			while (fillcnt-- > 0) {
866 				while (fillsrc <= p_end && p_char[fillsrc] != ' ')
867 					fillsrc++;
868 				if (fillsrc > p_end)
869 					fatal(
870 _("Replacement text or line numbers mangled in hunk at line %lld\n"),
871 						(Llong)p_hunk_beg);
872 				p_line[filldst] = p_line[fillsrc];
873 				p_char[filldst] = p_char[fillsrc];
874 				p_len[filldst] = p_len[fillsrc];
875 				fillsrc++; filldst++;
876 			}
877 			while (fillsrc <= p_end && fillsrc != repl_beginning &&
878 			    p_char[fillsrc] != ' ')
879 				fillsrc++;
880 #ifdef DEBUGGING
881 			if (debug & 64)
882 				printf("fillsrc %lld, filldst %lld, rb %lld, e+1 %lld\n",
883 				    (Llong)fillsrc, (Llong)filldst,
884 				    (Llong)repl_beginning, (Llong)p_end+1);
885 #endif
886 			assert(fillsrc == p_end+1 || fillsrc == repl_beginning);
887 			assert(filldst == p_end+1 || filldst == repl_beginning);
888 		}
889 	} else if (diff_type == UNI_DIFF) {
890 		off_t	line_beginning = ftell(pfp);
891 						/* file pos of the current line */
892 		LINENUM fillsrc;		/* index of old lines */
893 		LINENUM filldst;		/* index of new lines */
894 		char ch;
895 
896 		ret = pgets(&buf, &bufsize, pfp);
897 		p_input_line++;
898 		if (ret <= 0 || strnNE(buf, "@@ -", 4)) {
899 			next_intuit_at(line_beginning, p_input_line);
900 			return (FALSE);
901 		}
902 		s = buf+4;
903 		if (!*s)
904 			malformed();
905 		p_first = atolnum(s);
906 		while (isdigit(UCH *s))
907 			s++;
908 		if (*s == ',') {
909 			p_ptrn_lines = atolnum(++s);
910 			while (isdigit(UCH *s))
911 				s++;
912 		} else {
913 			p_ptrn_lines = 1;
914 		}
915 		if (*s == ' ')
916 			s++;
917 		if (*s != '+' || !*++s)
918 			malformed();
919 		p_newfirst = atolnum(s);
920 		while (isdigit(UCH *s))
921 			s++;
922 		if (*s == ',') {
923 			p_repl_lines = atolnum(++s);
924 			while (isdigit(UCH *s)) s++;
925 		} else {
926 			p_repl_lines = 1;
927 		}
928 		if (*s == ' ')
929 			s++;
930 		if (*s != '@')
931 			malformed();
932 		if (!p_ptrn_lines)
933 			p_first++;	/* do append rather than insert */
934 		p_max = p_ptrn_lines + p_repl_lines + 1;
935 		while (p_max >= hunkmax && !out_of_mem)
936 			grow_hunkmax();
937 		if (out_of_mem) {
938 			p_end = -1;
939 			return (FALSE);
940 		}
941 		fillsrc = 1;
942 		filldst = fillsrc + p_ptrn_lines;
943 		p_end = filldst + p_repl_lines;
944 		Snprintf(buf, bufsize, "*** %lld,%lld ****\n",
945 			(Llong)p_first, (Llong)(p_first + p_ptrn_lines - 1));
946 		p_line[0] = savestr(buf);
947 		if (out_of_mem) {
948 			p_end = -1;
949 			return (FALSE);
950 		}
951 		p_char[0] = '*';
952 		Snprintf(buf, bufsize, "--- %lld,%lld ----\n",
953 			(Llong)p_newfirst, (Llong)(p_newfirst+p_repl_lines-1));
954 		p_line[filldst] = savestr(buf);
955 		if (out_of_mem) {
956 			p_end = 0;
957 			return (FALSE);
958 		}
959 		p_char[filldst++] = '=';
960 		p_context = 100;
961 		context = 0;
962 		p_hunk_beg = p_input_line + 1;
963 		while (fillsrc <= p_ptrn_lines || filldst <= p_end) {
964 			line_beginning = ftell(pfp);
965 			ret = pgets(&buf, &bufsize, pfp);
966 			p_input_line++;
967 			if (ret <= 0) {
968 				if (p_max - filldst < 3) {
969 					Strcpy(buf, " \n");  /* assume blank lines got chopped */
970 				} else {
971 					fatal(
972 _("Unexpected end of file in patch.\n"));
973 				}
974 			}
975 			if (*buf == '\t' || *buf == '\n') {
976 				ch = ' ';	/* assume the space got eaten */
977 				s = savestr(buf);
978 			} else {
979 				ch = *buf;
980 				s = savestr(buf+1);
981 			}
982 			if (out_of_mem) {
983 				while (--filldst > p_ptrn_lines)
984 					free(p_line[filldst]);
985 				p_end = fillsrc-1;
986 				return (FALSE);
987 			}
988 			switch (ch) {
989 			case '-':
990 				if (fillsrc > p_ptrn_lines) {
991 					free(s);
992 					p_end = filldst-1;
993 					malformed();
994 				}
995 				p_char[fillsrc] = ch;
996 				p_line[fillsrc] = s;
997 				p_len[fillsrc++] = strlen(s);
998 				break;
999 			case '=':
1000 				ch = ' ';
1001 				/* FALLTHROUGH */
1002 			case ' ':
1003 				if (fillsrc > p_ptrn_lines) {
1004 					free(s);
1005 					while (--filldst > p_ptrn_lines)
1006 						free(p_line[filldst]);
1007 					p_end = fillsrc-1;
1008 					malformed();
1009 				}
1010 				context++;
1011 				p_char[fillsrc] = ch;
1012 				p_line[fillsrc] = s;
1013 				p_len[fillsrc++] = strlen(s);
1014 				s = savestr(s);
1015 				if (out_of_mem) {
1016 					while (--filldst > p_ptrn_lines)
1017 						free(p_line[filldst]);
1018 					p_end = fillsrc-1;
1019 					return (FALSE);
1020 				}
1021 				/* FALLTHROUGH */
1022 			case '+':
1023 				if (filldst > p_end) {
1024 					free(s);
1025 					while (--filldst > p_ptrn_lines)
1026 						free(p_line[filldst]);
1027 					p_end = fillsrc-1;
1028 					malformed();
1029 				}
1030 				p_char[filldst] = ch;
1031 				p_line[filldst] = s;
1032 				p_len[filldst++] = strlen(s);
1033 				break;
1034 			default:
1035 				p_end = filldst;
1036 				malformed();
1037 			}
1038 			if (ch != ' ' && context > 0) {
1039 				if (context < p_context)
1040 					p_context = context;
1041 				context = -1000;
1042 			}
1043 		} /* while */
1044 	} else {			/* normal diff--fake it up */
1045 		char hunk_type;
1046 		int i;
1047 #undef	min
1048 #undef	max
1049 		LINENUM	min, max;
1050 		off_t	line_beginning = ftell(pfp);
1051 
1052 		p_context = 0;
1053 		ret = pgets(&buf, &bufsize, pfp);
1054 		p_input_line++;
1055 		if (ret <= 0 || !isdigit(UCH *buf)) {
1056 			next_intuit_at(line_beginning, p_input_line);
1057 			return (FALSE);
1058 		}
1059 		p_first = atolnum(buf);
1060 		for (s = buf; isdigit(UCH *s); s++) {
1061 			;
1062 			/* LINTED */
1063 		}
1064 		if (*s == ',') {
1065 			p_ptrn_lines = atolnum(++s) - p_first + 1;
1066 			while (isdigit(UCH *s))
1067 				s++;
1068 		} else {
1069 			p_ptrn_lines = (*s != 'a');
1070 		}
1071 		hunk_type = *s;
1072 		if (hunk_type == 'a')
1073 			p_first++;	/* do append rather than insert */
1074 		min = atolnum(++s);
1075 		for (; isdigit(UCH *s); s++) {
1076 			;
1077 			/* LINTED */
1078 		}
1079 		if (*s == ',')
1080 			max = atolnum(++s);
1081 		else
1082 			max = min;
1083 		if (hunk_type == 'd')
1084 			min++;
1085 		p_end = p_ptrn_lines + 1 + max - min + 1;
1086 		while (p_end >= hunkmax && !out_of_mem)
1087 			grow_hunkmax();
1088 		if (out_of_mem) {
1089 			p_end = -1;
1090 			return (FALSE);
1091 		}
1092 		p_newfirst = min;
1093 		p_repl_lines = max - min + 1;
1094 		Snprintf(buf, bufsize, "*** %lld,%lld\n",
1095 			(Llong)p_first, (Llong)(p_first + p_ptrn_lines - 1));
1096 		p_line[0] = savestr(buf);
1097 		if (out_of_mem) {
1098 			p_end = -1;
1099 			return (FALSE);
1100 		}
1101 		p_char[0] = '*';
1102 		for (i = 1; i <= p_ptrn_lines; i++) {
1103 			ret = pgets(&buf, &bufsize, pfp);
1104 			p_input_line++;
1105 			if (ret <= 0)
1106 				fatal(
1107 _("Unexpected end of file in patch at line %lld.\n"),
1108 				    (Llong)p_input_line);
1109 			if (*buf != '<')
1110 				fatal(_("< expected at line %lld of patch.\n"),
1111 					(Llong)p_input_line);
1112 			p_line[i] = savestr(buf+2);
1113 			if (out_of_mem) {
1114 				p_end = i-1;
1115 				return (FALSE);
1116 			}
1117 			p_len[i] = strlen(p_line[i]);
1118 			p_char[i] = '-';
1119 		}
1120 		if (hunk_type == 'c') {
1121 			ret = pgets(&buf, &bufsize, pfp);
1122 			p_input_line++;
1123 			if (ret <= 0)
1124 				fatal(
1125 _("Unexpected end of file in patch at line %lld.\n"),
1126 				    (Llong)p_input_line);
1127 			if (*buf != '-')
1128 				fatal(_("--- expected at line %lld of patch.\n"),
1129 				(Llong)p_input_line);
1130 		}
1131 		Snprintf(buf, bufsize, "--- %lld,%lld\n", (Llong)min, (Llong)max);
1132 		p_line[i] = savestr(buf);
1133 		if (out_of_mem) {
1134 			p_end = i-1;
1135 			return (FALSE);
1136 		}
1137 		p_char[i] = '=';
1138 		for (i++; i <= p_end; i++) {
1139 			ret = pgets(&buf, &bufsize, pfp);
1140 			p_input_line++;
1141 			if (ret <= 0)
1142 				fatal(
1143 _("Unexpected end of file in patch at line %lld.\n"),
1144 				    (Llong)p_input_line);
1145 			if (*buf != '>')
1146 				fatal(_("> expected at line %lld of patch.\n"),
1147 					(Llong)p_input_line);
1148 			p_line[i] = savestr(buf+2);
1149 			if (out_of_mem) {
1150 				p_end = i-1;
1151 				return (FALSE);
1152 			}
1153 			p_len[i] = strlen(p_line[i]);
1154 			p_char[i] = '+';
1155 		}
1156 	}
1157 	if (reverse)			/* backwards patch? */
1158 		if (!pch_swap())
1159 		    say(_("Not enough memory to swap next hunk!\n"));
1160 #ifdef DEBUGGING
1161 	if (debug & 2) {
1162 		int i;
1163 		char special;
1164 
1165 		for (i = 0; i <= p_end; i++) {
1166 			if (i == p_ptrn_lines)
1167 				special = '^';
1168 			else
1169 				special = ' ';
1170 			fprintf(stderr, "%3d %c %c %s",
1171 					i, p_char[i], special, p_line[i]);
1172 			Fflush(stderr);
1173 		}
1174 	}
1175 #endif
1176 	if (p_end+1 < hunkmax)		/* paranoia reigns supreme... */
1177 		p_char[p_end+1] = '^';	/* add a stopper for apply_hunk */
1178 	return (TRUE);
1179 }
1180 
1181 /* Input a line from the patch file, worrying about indentation. */
1182 
1183 static ssize_t
pgets(bfp,szp,fp)1184 pgets(bfp, szp, fp)
1185 	char	**bfp;
1186 	size_t	*szp;
1187 	FILE	*fp;
1188 {
1189 	ssize_t	ret = fgetaline(fp, bfp, szp);
1190 	char *s;
1191 	size_t indent = 0;
1192 
1193 	if (p_indent && ret > 0) {
1194 		for (s = *bfp;
1195 		    indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1196 		    s++) {
1197 			if (*s == '\t')
1198 				indent += 8 - (indent % 7);
1199 			else
1200 				indent++;
1201 		}
1202 		if (*bfp != s)
1203 			ovstrcpy(*bfp, s);
1204 	}
1205 	return (ret);
1206 }
1207 
1208 /* Reverse the old and new portions of the current hunk. */
1209 
1210 bool
pch_swap()1211 pch_swap()
1212 {
1213 	char **tp_line;		/* the text of the hunk */
1214 	size_t *tp_len;		/* length of each line */
1215 	char *tp_char;		/* +, -, and ! */
1216 	LINENUM i;
1217 	LINENUM n;
1218 	bool blankline = FALSE;
1219 	char *s;
1220 
1221 	i = p_first;
1222 	p_first = p_newfirst;
1223 	p_newfirst = i;
1224 
1225 	/* make a scratch copy */
1226 
1227 	tp_line = p_line;
1228 	tp_len = p_len;
1229 	tp_char = p_char;
1230 	p_line = Null(char **);	/* force set_hunkmax to allocate again */
1231 	p_len = Null(size_t *);
1232 	p_char = Nullch;
1233 	set_hunkmax();
1234 	if (p_line == Null(char **) ||
1235 	    p_len == Null(size_t *) ||
1236 	    p_char == Nullch) {
1237 		if (p_line != Null(char **))
1238 			free((char *)p_line);
1239 		p_line = tp_line;
1240 		if (p_len != Null(size_t *))
1241 			free((char *)p_len);
1242 		p_len = tp_len;
1243 		if (p_char != Nullch)
1244 			free((char *)p_char);
1245 		p_char = tp_char;
1246 		return (FALSE);		/* not enough memory to swap hunk! */
1247 	}
1248 
1249 	/* now turn the new into the old */
1250 
1251 	i = p_ptrn_lines + 1;
1252 	if (tp_char[i] == '\n') {	/* account for possible blank line */
1253 		blankline = TRUE;
1254 		i++;
1255 	}
1256 	if (p_efake >= 0) {		/* fix non-freeable ptr range */
1257 		if (p_efake <= i)
1258 			n = p_end - i + 1;
1259 		else
1260 			n = -i;
1261 		p_efake += n;
1262 		p_bfake += n;
1263 	}
1264 	for (n = 0; i <= p_end; i++, n++) {
1265 		p_line[n] = tp_line[i];
1266 		p_char[n] = tp_char[i];
1267 		if (p_char[n] == '+')
1268 			p_char[n] = '-';
1269 		p_len[n] = tp_len[i];
1270 	}
1271 	if (blankline) {
1272 		i = p_ptrn_lines + 1;
1273 		p_line[n] = tp_line[i];
1274 		p_char[n] = tp_char[i];
1275 		p_len[n] = tp_len[i];
1276 		n++;
1277 	}
1278 	assert(p_char[0] == '=');
1279 	p_char[0] = '*';
1280 	for (s = p_line[0]; *s; s++)
1281 		if (*s == '-')
1282 			*s = '*';
1283 
1284 	/* now turn the old into the new */
1285 
1286 	assert(tp_char[0] == '*');
1287 	tp_char[0] = '=';
1288 	for (s = tp_line[0]; *s; s++)
1289 		if (*s == '*')
1290 			*s = '-';
1291 	for (i = 0; n <= p_end; i++, n++) {
1292 		p_line[n] = tp_line[i];
1293 		p_char[n] = tp_char[i];
1294 		if (p_char[n] == '-')
1295 			p_char[n] = '+';
1296 		p_len[n] = tp_len[i];
1297 	}
1298 	assert(i == p_ptrn_lines + 1);
1299 	i = p_ptrn_lines;
1300 	p_ptrn_lines = p_repl_lines;
1301 	p_repl_lines = i;
1302 	if (tp_line != Null(char **))
1303 		free((char *)tp_line);
1304 	if (tp_len != Null(size_t *))
1305 		free((char *)tp_len);
1306 	if (tp_char != Nullch)
1307 		free((char *)tp_char);
1308 	return (TRUE);
1309 }
1310 
1311 /* Return the specified line position in the old file of the old context. */
1312 
1313 LINENUM
pch_first()1314 pch_first()
1315 {
1316 	return (p_first);
1317 }
1318 
1319 /* Return the number of lines of old context. */
1320 
1321 LINENUM
pch_ptrn_lines()1322 pch_ptrn_lines()
1323 {
1324 	return (p_ptrn_lines);
1325 }
1326 
1327 /* Return the probable line position in the new file of the first line. */
1328 
1329 LINENUM
pch_newfirst()1330 pch_newfirst()
1331 {
1332 	return (p_newfirst);
1333 }
1334 
1335 /* Return the number of lines in the replacement text including context. */
1336 
1337 LINENUM
pch_repl_lines()1338 pch_repl_lines()
1339 {
1340 	return (p_repl_lines);
1341 }
1342 
1343 /* Return the number of lines in the whole hunk. */
1344 
1345 LINENUM
pch_end()1346 pch_end()
1347 {
1348 	return (p_end);
1349 }
1350 
1351 /* Return the number of context lines before the first changed line. */
1352 
1353 LINENUM
pch_context()1354 pch_context()
1355 {
1356 	return (p_context);
1357 }
1358 
1359 /* Return the length of a particular patch line. */
1360 
1361 size_t
pch_line_len(line)1362 pch_line_len(line)
1363 LINENUM line;
1364 {
1365 	return (p_len[line]);
1366 }
1367 
1368 /* Return the control character (+, -, *, !, etc) for a patch line. */
1369 
1370 char
pch_char(line)1371 pch_char(line)
1372 LINENUM line;
1373 {
1374 	return (p_char[line]);
1375 }
1376 
1377 /* Return a pointer to a particular patch line. */
1378 
1379 char *
pfetch(line)1380 pfetch(line)
1381 LINENUM line;
1382 {
1383 	return (p_line[line]);
1384 }
1385 
1386 /* Return where in the patch file this hunk began, for error messages. */
1387 
1388 LINENUM
pch_hunk_beg()1389 pch_hunk_beg()
1390 {
1391 	return (p_hunk_beg);
1392 }
1393 
1394 /* Apply an ed script by feeding ed itself. */
1395 
1396 void
do_ed_script()1397 do_ed_script()
1398 {
1399 	char	*t;
1400 	off_t	beginning_of_this_line;
1401 	bool	this_line_is_command = FALSE;
1402 	FILE	*pipefp = 0;
1403 
1404 	if (!skip_rest_of_patch) {
1405 		Unlink(TMPOUTNAME);
1406 		copy_file(filearg[0], TMPOUTNAME);
1407 		/*
1408 		 * Warning: "ed" stays in command mode in case there is e.g.
1409 		 * a wrong line number before an "append" command.
1410 		 *
1411 		 * Using this "feature" could allow to hide a shell command
1412 		 * from the command filter. However in correctly implemented
1413 		 * "ed" versions, an error while reading from a file must
1414 		 * result in e non-zero exit. This was not true for older
1415 		 * GNU ed versions and resulted in the "beep" attack.
1416 		 *
1417 		 * To avoid related other probably unknown problems, we
1418 		 * use the "red" command instead and start "red" from /tmp
1419 		 * as it does not accept filenames with a '/' inside.
1420 		 */
1421 		if (verbose) {
1422 			Snprintf(buf, bufsize, "cd %s && %s %s",
1423 						TMPDIR,
1424 						CNF_PATH_RED,
1425 						TMPOUTNAME + TMPDLEN);
1426 		} else {
1427 			Snprintf(buf, bufsize, "cd %s && %s - %s",
1428 						TMPDIR,
1429 						CNF_PATH_RED,
1430 						TMPOUTNAME + TMPDLEN);
1431 		}
1432 		pipefp = popen(buf, "w");
1433 #ifdef	__unsafe__
1434 		if (pipefp == NULL) {
1435 			if (verbose)
1436 				Snprintf(buf, bufsize, "/bin/ed %s", TMPOUTNAME);
1437 			else
1438 				Snprintf(buf, bufsize, "/bin/ed - %s", TMPOUTNAME);
1439 			pipefp = popen(buf, "w");
1440 		}
1441 #endif
1442 	}
1443 	for (;;) {
1444 		beginning_of_this_line = ftell(pfp);
1445 		if (pgets(&buf, &bufsize, pfp) <= 0) {
1446 			next_intuit_at(beginning_of_this_line, p_input_line);
1447 			break;
1448 		}
1449 		p_input_line++;
1450 		for (t = buf; isdigit(UCH *t) || *t == ','; t++) {
1451 			;
1452 			/* LINTED */
1453 		}
1454 		this_line_is_command = (isdigit(UCH *buf) &&
1455 		    (*t == 'd' || *t == 'c' || *t == 'a'));
1456 		if (!this_line_is_command) {
1457 			/*
1458 			 * Check for the diff workaround for "." in a line
1459 			 * that inserts ".." and then substitites the result.
1460 			 * Most diff programs emit "s/.//\na\a", but diff from
1461 			 * OpenBSD emits the substitute program with address.
1462 			 */
1463 			if (strEQ(buf, "a\n") || strEQ(t, "s/.//\n"))
1464 				this_line_is_command = 1;
1465 		}
1466 		if (this_line_is_command) {
1467 			if (!skip_rest_of_patch)
1468 				fputs(buf, pipefp);
1469 			if (*t != 'd' && *t != 's') {
1470 				while (pgets(&buf, &bufsize, pfp) > 0) {
1471 					p_input_line++;
1472 					if (!skip_rest_of_patch)
1473 						fputs(buf, pipefp);
1474 					if (strEQ(buf, ".\n"))
1475 						break;
1476 				}
1477 			}
1478 		} else {
1479 			next_intuit_at(beginning_of_this_line, p_input_line);
1480 			break;
1481 		}
1482 	}
1483 	if (skip_rest_of_patch)
1484 		return;
1485 	fprintf(pipefp, "w\n");
1486 	fprintf(pipefp, "q\n");
1487 	Fflush(pipefp);
1488 	Pclose(pipefp);
1489 	ignore_signals();
1490 	if (move_file(TMPOUTNAME, outname) < 0) {
1491 		toutkeep = TRUE;
1492 		chmod(TMPOUTNAME, filemode);
1493 	} else {
1494 		chmod(outname, filemode);
1495 	}
1496 	set_signals(1);
1497 }
1498 
1499 LINENUM
atolnum(s)1500 atolnum(s)
1501 	char	*s;
1502 {
1503 	char	*os;
1504 	LINENUM	l = 0;
1505 	LINENUM	multmax;
1506 	LINENUM	lmax;
1507 	int	c;
1508 	int	neg = 0;
1509 
1510 	lmax = TYPE_MAXVAL(LINENUM);
1511 	multmax = TYPE_MAXVAL(LINENUM) / 10;
1512 
1513 	if (*s == '-') {
1514 		neg++;
1515 		s++;
1516 	} else if (*s == '+')
1517 		s++;
1518 
1519 	os = s;
1520 	while ((c = *s++) != '\0') {
1521 		if (c < '0' || c > '9')
1522 			break;
1523 		if (l > multmax) {
1524 			if (p_input_line)
1525 				malformed();
1526 			else
1527 				fatal(_("Number '%s' too large.\n"), os);
1528 		}
1529 		l *= 10;
1530 		c -= '0';
1531 		if (c > (lmax - l)) {
1532 			if (p_input_line)
1533 				malformed();
1534 			else
1535 				fatal(_("Number '%s' too large.\n"), os);
1536 		}
1537 		l += c;
1538 	}
1539 	if (s == ++os) {
1540 		if (p_input_line)
1541 			malformed();
1542 		else
1543 			fatal(_("Not a number '%s'.\n"), --os);
1544 	}
1545 	return (neg? -l:l);
1546 }
1547 
1548 int
atoinum(s)1549 atoinum(s)
1550 	char	*s;
1551 {
1552 	LINENUM	l = atolnum(s);
1553 	int	ret;
1554 
1555 	ret = l;
1556 	if (ret != l)
1557 		fatal(_("Number '%lld' too large.\n"), (Llong)l);
1558 
1559 	return (ret);
1560 }
1561