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