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