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