xref: /csrg-svn/usr.bin/patch/patch.c (revision 24294)
1 #ifndef lint
2 static char sccsid[] = "@(#)patch.c	5.1 (Berkeley) 08/16/85";
3 #endif not lint
4 
5 /* patch - a program to apply diffs to original files
6  *
7  * $Header: patch.c,v 1.3 85/03/26 15:07:43 lwall Exp $
8  *
9  * Copyright 1984, Larry Wall
10  *
11  * This program may be copied as long as you don't try to make any
12  * money off of it, or pretend that you wrote it.
13  *
14  * $Log:	patch.c,v $
15  * Revision 1.3  85/03/26  15:07:43  lwall
16  * Frozen.
17  *
18  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
19  * Changed pfp->_file to fileno(pfp).
20  *
21  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
22  * Check i_ptr and i_womp to make sure they aren't null before freeing.
23  * Also allow ed output to be suppressed.
24  *
25  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
26  * Added -p option from jromine@uci-750a.
27  *
28  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
29  * Now checks for normalness of file to patch.
30  *
31  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
32  * Added -D (#ifdef) option from joe@fluke.
33  *
34  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
35  * Made smarter about SCCS subdirectories.
36  *
37  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
38  * Added -l switch to do loose string comparison.
39  *
40  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
41  * Failed hunk count not reset on multiple patch file.
42  *
43  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
44  * Branch for sdcrdcf changes.
45  *
46  * Revision 1.2  84/11/29  13:29:51  lwall
47  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
48  * multiple calls to mktemp().  Will now work on machines that can only
49  * read 32767 chars.  Added -R option for diffs with new and old swapped.
50  * Various cosmetic changes.
51  *
52  * Revision 1.1  84/11/09  17:03:58  lwall
53  * Initial revision
54  *
55  */
56 
57 #define DEBUGGING
58 
59 /* shut lint up about the following when return value ignored */
60 
61 #define Signal (void)signal
62 #define Unlink (void)unlink
63 #define Lseek (void)lseek
64 #define Fseek (void)fseek
65 #define Fstat (void)fstat
66 #define Pclose (void)pclose
67 #define Close (void)close
68 #define Fclose (void)fclose
69 #define Fflush (void)fflush
70 #define Sprintf (void)sprintf
71 #define Mktemp (void)mktemp
72 #define Strcpy (void)strcpy
73 #define Strcat (void)strcat
74 
75 #include <stdio.h>
76 #include <assert.h>
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include <ctype.h>
80 #include <signal.h>
81 
82 /* constants */
83 
84 #define TRUE (1)
85 #define FALSE (0)
86 
87 #define MAXHUNKSIZE 500
88 #define MAXLINELEN 1024
89 #define BUFFERSIZE 1024
90 #define ORIGEXT ".orig"
91 #define SCCSPREFIX "s."
92 #define GET "get -e %s"
93 #define RCSSUFFIX ",v"
94 #define CHECKOUT "co -l %s"
95 
96 /* handy definitions */
97 
98 #define Null(t) ((t)0)
99 #define Nullch Null(char *)
100 #define Nullfp Null(FILE *)
101 
102 #define Ctl(ch) (ch & 037)
103 
104 #define strNE(s1,s2) (strcmp(s1,s2))
105 #define strEQ(s1,s2) (!strcmp(s1,s2))
106 #define strnNE(s1,s2,l) (strncmp(s1,s2,l))
107 #define strnEQ(s1,s2,l) (!strncmp(s1,s2,l))
108 
109 /* typedefs */
110 
111 typedef char bool;
112 typedef long LINENUM;			/* must be signed */
113 typedef unsigned MEM;			/* what to feed malloc */
114 
115 /* globals */
116 
117 int Argc;				/* guess */
118 char **Argv;
119 
120 struct stat filestat;			/* file statistics area */
121 
122 char serrbuf[BUFSIZ];			/* buffer for stderr */
123 char buf[MAXLINELEN];			/* general purpose buffer */
124 FILE *pfp = Nullfp;			/* patch file pointer */
125 FILE *ofp = Nullfp;			/* output file pointer */
126 FILE *rejfp = Nullfp;			/* reject file pointer */
127 
128 LINENUM input_lines = 0;		/* how long is input file in lines */
129 LINENUM last_frozen_line = 0;		/* how many input lines have been */
130 					/* irretractibly output */
131 
132 #define MAXFILEC 2
133 int filec = 0;				/* how many file arguments? */
134 char *filearg[MAXFILEC];
135 
136 char *outname = Nullch;
137 char rejname[128];
138 
139 char *origext = Nullch;
140 
141 char TMPOUTNAME[] = "/tmp/patchoXXXXXX";
142 char TMPINNAME[] = "/tmp/patchiXXXXXX";	/* you might want /usr/tmp here */
143 char TMPREJNAME[] = "/tmp/patchrXXXXXX";
144 char TMPPATNAME[] = "/tmp/patchpXXXXXX";
145 
146 LINENUM last_offset = 0;
147 #ifdef DEBUGGING
148 int debug = 0;
149 #endif
150 bool verbose = TRUE;
151 bool reverse = FALSE;
152 bool usepath = FALSE;
153 bool canonicalize = FALSE;
154 
155 #define CONTEXT_DIFF 1
156 #define NORMAL_DIFF 2
157 #define ED_DIFF 3
158 int diff_type = 0;
159 
160 int do_defines = 0;			/* patch using ifdef, ifndef, etc. */
161 char if_defined[128];			/* #ifdef xyzzy */
162 char not_defined[128];			/* #ifndef xyzzy */
163 char else_defined[] = "#else\n";	/* #else */
164 char end_defined[128];			/* #endif xyzzy */
165 
166 char *revision = Nullch;		/* prerequisite revision, if any */
167 
168 /* procedures */
169 
170 LINENUM locate_hunk();
171 bool patch_match();
172 bool similar();
173 char *malloc();
174 char *savestr();
175 char *strcpy();
176 char *strcat();
177 char *sprintf();		/* usually */
178 int my_exit();
179 bool rev_in_string();
180 char *fetchname();
181 long atol();
182 long lseek();
183 char *mktemp();
184 
185 /* patch type */
186 
187 bool there_is_another_patch();
188 bool another_hunk();
189 char *pfetch();
190 int pch_line_len();
191 LINENUM pch_first();
192 LINENUM pch_ptrn_lines();
193 LINENUM pch_newfirst();
194 LINENUM pch_repl_lines();
195 LINENUM pch_end();
196 LINENUM pch_context();
197 LINENUM pch_hunk_beg();
198 char pch_char();
199 char *pfetch();
200 char *pgets();
201 
202 /* input file type */
203 
204 char *ifetch();
205 
206 /* apply a context patch to a named file */
207 
208 main(argc,argv)
209 int argc;
210 char **argv;
211 {
212     LINENUM where;
213     int hunk = 0;
214     int failed = 0;
215     int i;
216 
217     setbuf(stderr,serrbuf);
218     for (i = 0; i<MAXFILEC; i++)
219 	filearg[i] = Nullch;
220     Mktemp(TMPOUTNAME);
221     Mktemp(TMPINNAME);
222     Mktemp(TMPREJNAME);
223     Mktemp(TMPPATNAME);
224 
225     /* parse switches */
226     Argc = argc;
227     Argv = argv;
228     get_some_switches();
229 
230     /* make sure we clean up /tmp in case of disaster */
231     set_signals();
232 
233     for (
234 	open_patch_file(filearg[1]);
235 	there_is_another_patch();
236 	reinitialize_almost_everything()
237     ) {					/* for each patch in patch file */
238 
239 	if (outname == Nullch)
240 	    outname = savestr(filearg[0]);
241 
242 	/* initialize the patched file */
243 	init_output(TMPOUTNAME);
244 
245 	/* for ed script just up and do it and exit */
246 	if (diff_type == ED_DIFF) {
247 	    do_ed_script();
248 	    continue;
249 	}
250 
251 	/* initialize reject file */
252 	init_reject(TMPREJNAME);
253 
254 	/* find out where all the lines are */
255 	scan_input(filearg[0]);
256 
257 	/* from here on, open no standard i/o files, because malloc */
258 	/* might misfire */
259 
260 	/* apply each hunk of patch */
261 	hunk = 0;
262 	failed = 0;
263 	while (another_hunk()) {
264 	    hunk++;
265 	    where = locate_hunk();
266 	    if (hunk == 1 && where == Null(LINENUM)) {
267 					/* dwim for reversed patch? */
268 		pch_swap();
269 		reverse = !reverse;
270 		where = locate_hunk();	/* try again */
271 		if (where == Null(LINENUM)) {
272 		    pch_swap();		/* no, put it back to normal */
273 		    reverse = !reverse;
274 		}
275 		else {
276 		    say("%seversed (or previously applied) patch detected!  %s -R.\n",
277 			reverse ? "R" : "Unr",
278 			reverse ? "Assuming" : "Ignoring");
279 		}
280 	    }
281 	    if (where == Null(LINENUM)) {
282 		abort_hunk();
283 		failed++;
284 		if (verbose)
285 		    say("Hunk #%d failed.\n",hunk);
286 	    }
287 	    else {
288 		apply_hunk(where);
289 		if (verbose)
290 		    if (last_offset)
291 			say("Hunk #%d succeeded (offset %d line%s).\n",
292 			  hunk,last_offset,last_offset==1?"":"s");
293 		    else
294 			say("Hunk #%d succeeded.\n", hunk);
295 	    }
296 	}
297 
298 	assert(hunk);
299 
300 	/* finish spewing out the new file */
301 	spew_output();
302 
303 	/* and put the output where desired */
304 	ignore_signals();
305 	move_file(TMPOUTNAME,outname);
306 	Fclose(rejfp);
307 	rejfp = Nullfp;
308 	if (failed) {
309 	    if (!*rejname) {
310 		Strcpy(rejname, outname);
311 		Strcat(rejname, ".rej");
312 	    }
313 	    say("%d out of %d hunks failed--saving rejects to %s\n",
314 		failed, hunk, rejname);
315 	    move_file(TMPREJNAME,rejname);
316 	}
317 	set_signals();
318     }
319     my_exit(0);
320 }
321 
322 reinitialize_almost_everything()
323 {
324     re_patch();
325     re_input();
326 
327     input_lines = 0;
328     last_frozen_line = 0;
329 
330     filec = 0;
331     if (filearg[0] != Nullch) {
332 	free(filearg[0]);
333 	filearg[0] = Nullch;
334     }
335 
336     if (outname != Nullch) {
337 	free(outname);
338 	outname = Nullch;
339     }
340 
341     last_offset = 0;
342 
343     diff_type = 0;
344 
345     if (revision != Nullch) {
346 	free(revision);
347 	revision = Nullch;
348     }
349 
350     reverse = FALSE;
351 
352     get_some_switches();
353 
354     if (filec >= 2)
355 	fatal("You may not change to a different patch file.\n");
356 }
357 
358 get_some_switches()
359 {
360     register char *s;
361 
362     rejname[0] = '\0';
363     if (!Argc)
364 	return;
365     for (Argc--,Argv++; Argc; Argc--,Argv++) {
366 	s = Argv[0];
367 	if (strEQ(s,"+")) {
368 	    return;			/* + will be skipped by for loop */
369 	}
370 	if (*s != '-' || !s[1]) {
371 	    if (filec == MAXFILEC)
372 		fatal("Too many file arguments.\n");
373 	    filearg[filec++] = savestr(s);
374 	}
375 	else {
376 	    switch (*++s) {
377 	    case 'b':
378 		origext = savestr(Argv[1]);
379 		Argc--,Argv++;
380 		break;
381 	    case 'c':
382 		diff_type = CONTEXT_DIFF;
383 		break;
384 	    case 'd':
385 		if (chdir(Argv[1]) < 0)
386 		    fatal("Can't cd to %s.\n",Argv[1]);
387 		Argc--,Argv++;
388 		break;
389 	    case 'D':
390 	    	do_defines++;
391 		Sprintf(if_defined, "#ifdef %s\n", Argv[1]);
392 		Sprintf(not_defined, "#ifndef %s\n", Argv[1]);
393 		Sprintf(end_defined, "#endif %s\n", Argv[1]);
394 		Argc--,Argv++;
395 		break;
396 	    case 'e':
397 		diff_type = ED_DIFF;
398 		break;
399 	    case 'l':
400 		canonicalize = TRUE;
401 		break;
402 	    case 'n':
403 		diff_type = NORMAL_DIFF;
404 		break;
405 	    case 'o':
406 		outname = savestr(Argv[1]);
407 		Argc--,Argv++;
408 		break;
409 	    case 'p':
410 		usepath = TRUE;	/* do not strip path names */
411 		break;
412 	    case 'r':
413 		Strcpy(rejname,Argv[1]);
414 		Argc--,Argv++;
415 		break;
416 	    case 'R':
417 		reverse = TRUE;
418 		break;
419 	    case 's':
420 		verbose = FALSE;
421 		break;
422 #ifdef DEBUGGING
423 	    case 'x':
424 		debug = atoi(s+1);
425 		break;
426 #endif
427 	    default:
428 		fatal("Unrecognized switch: %s\n",Argv[0]);
429 	    }
430 	}
431     }
432 }
433 
434 LINENUM
435 locate_hunk()
436 {
437     register LINENUM first_guess = pch_first() + last_offset;
438     register LINENUM offset;
439     LINENUM pat_lines = pch_ptrn_lines();
440     register LINENUM max_pos_offset = input_lines - first_guess
441 				- pat_lines + 1;
442     register LINENUM max_neg_offset = first_guess - last_frozen_line - 1
443 				- pch_context();
444 
445     if (!pat_lines)			/* null range matches always */
446 	return first_guess;
447     if (max_neg_offset >= first_guess)	/* do not try lines < 0 */
448 	max_neg_offset = first_guess - 1;
449     if (first_guess <= input_lines && patch_match(first_guess,(LINENUM)0))
450 	return first_guess;
451     for (offset = 1; ; offset++) {
452 	bool check_after = (offset <= max_pos_offset);
453 	bool check_before = (offset <= max_pos_offset);
454 
455 	if (check_after && patch_match(first_guess,offset)) {
456 #ifdef DEBUGGING
457 	    if (debug & 1)
458 		printf("Offset changing from %d to %d\n",last_offset,offset);
459 #endif
460 	    last_offset = offset;
461 	    return first_guess+offset;
462 	}
463 	else if (check_before && patch_match(first_guess,-offset)) {
464 #ifdef DEBUGGING
465 	    if (debug & 1)
466 		printf("Offset changing from %d to %d\n",last_offset,-offset);
467 #endif
468 	    last_offset = -offset;
469 	    return first_guess-offset;
470 	}
471 	else if (!check_before && !check_after)
472 	    return Null(LINENUM);
473     }
474 }
475 
476 /* we did not find the pattern, dump out the hunk so they can handle it */
477 
478 abort_hunk()
479 {
480     register LINENUM i;
481     register LINENUM pat_end = pch_end();
482     /* add in last_offset to guess the same as the previous successful hunk */
483     int oldfirst = pch_first() + last_offset;
484     int newfirst = pch_newfirst() + last_offset;
485     int oldlast = oldfirst + pch_ptrn_lines() - 1;
486     int newlast = newfirst + pch_repl_lines() - 1;
487 
488     fprintf(rejfp,"***************\n");
489     for (i=0; i<=pat_end; i++) {
490 	switch (pch_char(i)) {
491 	case '*':
492 	    fprintf(rejfp,"*** %d,%d\n", oldfirst, oldlast);
493 	    break;
494 	case '=':
495 	    fprintf(rejfp,"--- %d,%d -----\n", newfirst, newlast);
496 	    break;
497 	case '\n':
498 	    fprintf(rejfp,"%s", pfetch(i));
499 	    break;
500 	case ' ': case '-': case '+': case '!':
501 	    fprintf(rejfp,"%c %s", pch_char(i), pfetch(i));
502 	    break;
503 	default:
504 	    say("Fatal internal error in abort_hunk().\n");
505 	    abort();
506 	}
507     }
508 }
509 
510 /* we found where to apply it (we hope), so do it */
511 
512 apply_hunk(where)
513 LINENUM where;
514 {
515     register LINENUM old = 1;
516     register LINENUM lastline = pch_ptrn_lines();
517     register LINENUM new = lastline+1;
518     register int def_state = 0;	/* -1 = ifndef, 1 = ifdef */
519 
520     where--;
521     while (pch_char(new) == '=' || pch_char(new) == '\n')
522 	new++;
523 
524     while (old <= lastline) {
525 	if (pch_char(old) == '-') {
526 	    copy_till(where + old - 1);
527 	    if (do_defines) {
528 		if (def_state == 0) {
529 		    fputs(not_defined, ofp);
530 		    def_state = -1;
531 		} else
532 		if (def_state == 1) {
533 		    fputs(else_defined, ofp);
534 		    def_state = 2;
535 		}
536 		fputs(pfetch(old), ofp);
537 	    }
538 	    last_frozen_line++;
539 	    old++;
540 	}
541 	else if (pch_char(new) == '+') {
542 	    copy_till(where + old - 1);
543 	    if (do_defines) {
544 		if (def_state == -1) {
545 		    fputs(else_defined, ofp);
546 		    def_state = 2;
547 		} else
548 		if (def_state == 0) {
549 		    fputs(if_defined, ofp);
550 		    def_state = 1;
551 		}
552 	    }
553 	    fputs(pfetch(new),ofp);
554 	    new++;
555 	}
556 	else {
557 	    if (pch_char(new) != pch_char(old)) {
558 		say("Out-of-sync patch, lines %d,%d\n",
559 		    pch_hunk_beg() + old - 1,
560 		    pch_hunk_beg() + new - 1);
561 #ifdef DEBUGGING
562 		printf("oldchar = '%c', newchar = '%c'\n",
563 		    pch_char(old), pch_char(new));
564 #endif
565 		my_exit(1);
566 	    }
567 	    if (pch_char(new) == '!') {
568 		copy_till(where + old - 1);
569 		if (do_defines) {
570 		   fputs(not_defined,ofp);
571 		   def_state = -1;
572 		}
573 		while (pch_char(old) == '!') {
574 		    if (do_defines) {
575 			fputs(pfetch(old),ofp);
576 		    }
577 		    last_frozen_line++;
578 		    old++;
579 		}
580 		if (do_defines) {
581 		    fputs(else_defined, ofp);
582 		    def_state = 2;
583 		}
584 		while (pch_char(new) == '!') {
585 		    fputs(pfetch(new),ofp);
586 		    new++;
587 		}
588 		if (do_defines) {
589 		    fputs(end_defined, ofp);
590 		    def_state = 0;
591 		}
592 	    }
593 	    else {
594 		assert(pch_char(new) == ' ');
595 		old++;
596 		new++;
597 	    }
598 	}
599     }
600     if (new <= pch_end() && pch_char(new) == '+') {
601 	copy_till(where + old - 1);
602 	if (do_defines) {
603 	    if (def_state == 0) {
604 	    	fputs(if_defined, ofp);
605 		def_state = 1;
606 	    } else
607 	    if (def_state == -1) {
608 		fputs(else_defined, ofp);
609 		def_state = 2;
610 	    }
611 	}
612 	while (new <= pch_end() && pch_char(new) == '+') {
613 	    fputs(pfetch(new),ofp);
614 	    new++;
615 	}
616     }
617     if (do_defines && def_state) {
618 	fputs(end_defined, ofp);
619     }
620 }
621 
622 do_ed_script()
623 {
624     FILE *pipefp, *popen();
625     bool this_line_is_command = FALSE;
626     register char *t;
627     long beginning_of_this_line;
628 
629     Unlink(TMPOUTNAME);
630     copy_file(filearg[0],TMPOUTNAME);
631     if (verbose)
632 	Sprintf(buf,"/bin/ed %s",TMPOUTNAME);
633     else
634 	Sprintf(buf,"/bin/ed - %s",TMPOUTNAME);
635     pipefp = popen(buf,"w");
636     for (;;) {
637 	beginning_of_this_line = ftell(pfp);
638 	if (pgets(buf,sizeof buf,pfp) == Nullch) {
639 	    next_intuit_at(beginning_of_this_line);
640 	    break;
641 	}
642 	for (t=buf; isdigit(*t) || *t == ','; t++) ;
643 	this_line_is_command = (isdigit(*buf) &&
644 	  (*t == 'd' || *t == 'c' || *t == 'a') );
645 	if (this_line_is_command) {
646 	    fputs(buf,pipefp);
647 	    if (*t != 'd') {
648 		while (pgets(buf,sizeof buf,pfp) != Nullch) {
649 		    fputs(buf,pipefp);
650 		    if (strEQ(buf,".\n"))
651 			break;
652 		}
653 	    }
654 	}
655 	else {
656 	    next_intuit_at(beginning_of_this_line);
657 	    break;
658 	}
659     }
660     fprintf(pipefp,"w\n");
661     fprintf(pipefp,"q\n");
662     Fflush(pipefp);
663     Pclose(pipefp);
664     ignore_signals();
665     move_file(TMPOUTNAME,outname);
666     set_signals();
667 }
668 
669 init_output(name)
670 char *name;
671 {
672     ofp = fopen(name,"w");
673     if (ofp == Nullfp)
674 	fatal("patch: can't create %s.\n",name);
675 }
676 
677 init_reject(name)
678 char *name;
679 {
680     rejfp = fopen(name,"w");
681     if (rejfp == Nullfp)
682 	fatal("patch: can't create %s.\n",name);
683 }
684 
685 move_file(from,to)
686 char *from, *to;
687 {
688     char bakname[512];
689     register char *s;
690     int fromfd;
691     register int i;
692 
693     /* to stdout? */
694 
695     if (strEQ(to,"-")) {
696 #ifdef DEBUGGING
697 	if (debug & 4)
698 	    say("Moving %s to stdout.\n",from);
699 #endif
700 	fromfd = open(from,0);
701 	if (fromfd < 0)
702 	    fatal("patch: internal error, can't reopen %s\n",from);
703 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
704 	    if (write(1,buf,i) != 1)
705 		fatal("patch: write failed\n");
706 	Close(fromfd);
707 	return;
708     }
709 
710     Strcpy(bakname,to);
711     Strcat(bakname,origext?origext:ORIGEXT);
712     if (stat(to,&filestat) >= 0) {	/* output file exists */
713 	dev_t to_device = filestat.st_dev;
714 	ino_t to_inode  = filestat.st_ino;
715 	char *simplename = bakname;
716 
717 	for (s=bakname; *s; s++) {
718 	    if (*s == '/')
719 		simplename = s+1;
720 	}
721 	/* find a backup name that is not the same file */
722 	while (stat(bakname,&filestat) >= 0 &&
723 		to_device == filestat.st_dev && to_inode == filestat.st_ino) {
724 	    for (s=simplename; *s && !islower(*s); s++) ;
725 	    if (*s)
726 		*s = toupper(*s);
727 	    else
728 		Strcpy(simplename, simplename+1);
729 	}
730 	while (unlink(bakname) >= 0) ;	/* while() is for benefit of Eunice */
731 #ifdef DEBUGGING
732 	if (debug & 4)
733 	    say("Moving %s to %s.\n",to,bakname);
734 #endif
735 	if (link(to,bakname) < 0) {
736 	    say("patch: can't backup %s, output is in %s\n",
737 		to,from);
738 	    return;
739 	}
740 	while (unlink(to) >= 0) ;
741     }
742 #ifdef DEBUGGING
743     if (debug & 4)
744 	say("Moving %s to %s.\n",from,to);
745 #endif
746     if (link(from,to) < 0) {		/* different file system? */
747 	int tofd;
748 
749 	tofd = creat(to,0666);
750 	if (tofd < 0) {
751 	    say("patch: can't create %s, output is in %s.\n",
752 	      to, from);
753 	    return;
754 	}
755 	fromfd = open(from,0);
756 	if (fromfd < 0)
757 	    fatal("patch: internal error, can't reopen %s\n",from);
758 	while ((i=read(fromfd,buf,sizeof buf)) > 0)
759 	    if (write(tofd,buf,i) != i)
760 		fatal("patch: write failed\n");
761 	Close(fromfd);
762 	Close(tofd);
763     }
764     Unlink(from);
765 }
766 
767 copy_file(from,to)
768 char *from, *to;
769 {
770     int tofd;
771     int fromfd;
772     register int i;
773 
774     tofd = creat(to,0666);
775     if (tofd < 0)
776 	fatal("patch: can't create %s.\n", to);
777     fromfd = open(from,0);
778     if (fromfd < 0)
779 	fatal("patch: internal error, can't reopen %s\n",from);
780     while ((i=read(fromfd,buf,sizeof buf)) > 0)
781 	if (write(tofd,buf,i) != i)
782 	    fatal("patch: write (%s) failed\n", to);
783     Close(fromfd);
784     Close(tofd);
785 }
786 
787 copy_till(lastline)
788 register LINENUM lastline;
789 {
790     if (last_frozen_line > lastline)
791 	say("patch: misordered hunks! output will be garbled.\n");
792     while (last_frozen_line < lastline) {
793 	dump_line(++last_frozen_line);
794     }
795 }
796 
797 spew_output()
798 {
799     copy_till(input_lines);		/* dump remainder of file */
800     Fclose(ofp);
801     ofp = Nullfp;
802 }
803 
804 dump_line(line)
805 LINENUM line;
806 {
807     register char *s;
808 
809     for (s=ifetch(line,0); putc(*s,ofp) != '\n'; s++) ;
810 }
811 
812 /* does the patch pattern match at line base+offset? */
813 
814 bool
815 patch_match(base,offset)
816 LINENUM base;
817 LINENUM offset;
818 {
819     register LINENUM pline;
820     register LINENUM iline;
821     register LINENUM pat_lines = pch_ptrn_lines();
822 
823     for (pline = 1, iline=base+offset; pline <= pat_lines; pline++,iline++) {
824 	if (canonicalize) {
825 	    if (!similar(ifetch(iline,(offset >= 0)),
826 			 pfetch(pline),
827 			 pch_line_len(pline) ))
828 		return FALSE;
829 	}
830 	else if (strnNE(ifetch(iline,(offset >= 0)),
831 		   pfetch(pline),
832 		   pch_line_len(pline) ))
833 	    return FALSE;
834     }
835     return TRUE;
836 }
837 
838 /* match two lines with canonicalized white space */
839 
840 bool
841 similar(a,b,len)
842 register char *a, *b;
843 register int len;
844 {
845     while (len) {
846 	if (isspace(*b)) {		/* whitespace (or \n) to match? */
847 	    if (!isspace(*a))		/* no corresponding whitespace? */
848 		return FALSE;
849 	    while (len && isspace(*b) && *b != '\n')
850 		b++,len--;		/* skip pattern whitespace */
851 	    while (isspace(*a) && *a != '\n')
852 		a++;			/* skip target whitespace */
853 	    if (*a == '\n' || *b == '\n')
854 		return (*a == *b);	/* should end in sync */
855 	}
856 	else if (*a++ != *b++)		/* match non-whitespace chars */
857 	    return FALSE;
858 	else
859 	    len--;			/* probably not necessary */
860     }
861     return TRUE;			/* actually, this is not reached */
862 					/* since there is always a \n */
863 }
864 
865 /* input file with indexable lines abstract type */
866 
867 bool using_plan_a = TRUE;
868 static long i_size;			/* size of the input file */
869 static char *i_womp;			/* plan a buffer for entire file */
870 static char **i_ptr;			/* pointers to lines in i_womp */
871 
872 static int tifd = -1;			/* plan b virtual string array */
873 static char *tibuf[2];			/* plan b buffers */
874 static LINENUM tiline[2] = {-1,-1};	/* 1st line in each buffer */
875 static LINENUM lines_per_buf;		/* how many lines per buffer */
876 static int tireclen;			/* length of records in tmp file */
877 
878 re_input()
879 {
880     if (using_plan_a) {
881 	i_size = 0;
882 	/*NOSTRICT*/
883 	if (i_ptr != Null(char**))
884 	    free((char *)i_ptr);
885 	if (i_womp != Nullch)
886 	    free(i_womp);
887 	i_womp = Nullch;
888 	i_ptr = Null(char **);
889     }
890     else {
891 	using_plan_a = TRUE;		/* maybe the next one is smaller */
892 	Close(tifd);
893 	tifd = -1;
894 	free(tibuf[0]);
895 	free(tibuf[1]);
896 	tibuf[0] = tibuf[1] = Nullch;
897 	tiline[0] = tiline[1] = -1;
898 	tireclen = 0;
899     }
900 }
901 
902 scan_input(filename)
903 char *filename;
904 {
905     bool plan_a();
906 
907     if (!plan_a(filename))
908 	plan_b(filename);
909 }
910 
911 /* try keeping everything in memory */
912 
913 bool
914 plan_a(filename)
915 char *filename;
916 {
917     int ifd;
918     register char *s;
919     register LINENUM iline;
920 
921     if (stat(filename,&filestat) < 0) {
922 	Sprintf(buf,"RCS/%s%s",filename,RCSSUFFIX);
923 	if (stat(buf,&filestat) >= 0 || stat(buf+4,&filestat) >= 0) {
924 	    Sprintf(buf,CHECKOUT,filename);
925 	    if (verbose)
926 		say("Can't find %s--attempting to check it out from RCS.\n",
927 		    filename);
928 	    if (system(buf) || stat(filename,&filestat))
929 		fatal("Can't check out %s.\n",filename);
930 	}
931 	else {
932 	    Sprintf(buf,"SCCS/%s%s",SCCSPREFIX,filename);
933 	    if (stat(buf,&filestat) >= 0 || stat(buf+5,&filestat) >= 0) {
934 		Sprintf(buf,GET,filename);
935 		if (verbose)
936 		    say("Can't find %s--attempting to get it from SCCS.\n",
937 			filename);
938 		if (system(buf) || stat(filename,&filestat))
939 		    fatal("Can't get %s.\n",filename);
940 	    }
941 	    else
942 		fatal("Can't find %s.\n",filename);
943 	}
944     }
945     if ((filestat.st_mode & S_IFMT) & ~S_IFREG)
946 	fatal("%s is not a normal file--can't patch.\n",filename);
947     i_size = filestat.st_size;
948     /*NOSTRICT*/
949     i_womp = malloc((MEM)(i_size+2));
950     if (i_womp == Nullch)
951 	return FALSE;
952     if ((ifd = open(filename,0)) < 0)
953 	fatal("Can't open file %s\n",filename);
954     /*NOSTRICT*/
955     if (read(ifd,i_womp,(int)i_size) != i_size) {
956 	Close(ifd);
957 	free(i_womp);
958 	return FALSE;
959     }
960     Close(ifd);
961     if (i_womp[i_size-1] != '\n')
962 	i_womp[i_size++] = '\n';
963     i_womp[i_size] = '\0';
964 
965     /* count the lines in the buffer so we know how many pointers we need */
966 
967     iline = 0;
968     for (s=i_womp; *s; s++) {
969 	if (*s == '\n')
970 	    iline++;
971     }
972     /*NOSTRICT*/
973     i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *)));
974     if (i_ptr == Null(char **)) {	/* shucks, it was a near thing */
975 	free((char *)i_womp);
976 	return FALSE;
977     }
978 
979     /* now scan the buffer and build pointer array */
980 
981     iline = 1;
982     i_ptr[iline] = i_womp;
983     for (s=i_womp; *s; s++) {
984 	if (*s == '\n')
985 	    i_ptr[++iline] = s+1;	/* these are NOT null terminated */
986     }
987     input_lines = iline - 1;
988 
989     /* now check for revision, if any */
990 
991     if (revision != Nullch) {
992 	if (!rev_in_string(i_womp)) {
993 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
994 		revision);
995 	    if (*buf != 'y')
996 		fatal("Aborted.\n");
997 	}
998 	else if (verbose)
999 	    say("Good.  This file appears to be the %s version.\n",
1000 		revision);
1001     }
1002     return TRUE;			/* plan a will work */
1003 }
1004 
1005 /* keep (virtually) nothing in memory */
1006 
1007 plan_b(filename)
1008 char *filename;
1009 {
1010     FILE *ifp;
1011     register int i = 0;
1012     register int maxlen = 1;
1013     bool found_revision = (revision == Nullch);
1014 
1015     using_plan_a = FALSE;
1016     if ((ifp = fopen(filename,"r")) == Nullfp)
1017 	fatal("Can't open file %s\n",filename);
1018     if ((tifd = creat(TMPINNAME,0666)) < 0)
1019 	fatal("Can't open file %s\n",TMPINNAME);
1020     while (fgets(buf,sizeof buf, ifp) != Nullch) {
1021 	if (revision != Nullch && !found_revision && rev_in_string(buf))
1022 	    found_revision = TRUE;
1023 	if ((i = strlen(buf)) > maxlen)
1024 	    maxlen = i;			/* find longest line */
1025     }
1026     if (revision != Nullch) {
1027 	if (!found_revision) {
1028 	    ask("This file doesn't appear to be the %s version--patch anyway? [n] ",
1029 		revision);
1030 	    if (*buf != 'y')
1031 		fatal("Aborted.\n");
1032 	}
1033 	else if (verbose)
1034 	    say("Good.  This file appears to be the %s version.\n",
1035 		revision);
1036     }
1037     Fseek(ifp,0L,0);		/* rewind file */
1038     lines_per_buf = BUFFERSIZE / maxlen;
1039     tireclen = maxlen;
1040     tibuf[0] = malloc((MEM)(BUFFERSIZE + 1));
1041     tibuf[1] = malloc((MEM)(BUFFERSIZE + 1));
1042     if (tibuf[1] == Nullch)
1043 	fatal("Can't seem to get enough memory.\n");
1044     for (i=1; ; i++) {
1045 	if (! (i % lines_per_buf))	/* new block */
1046 	    if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
1047 		fatal("patch: can't write temp file.\n");
1048 	if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp)
1049 	  == Nullch) {
1050 	    input_lines = i - 1;
1051 	    if (i % lines_per_buf)
1052 		if (write(tifd,tibuf[0],BUFFERSIZE) < BUFFERSIZE)
1053 		    fatal("patch: can't write temp file.\n");
1054 	    break;
1055 	}
1056     }
1057     Fclose(ifp);
1058     Close(tifd);
1059     if ((tifd = open(TMPINNAME,0)) < 0) {
1060 	fatal("Can't reopen file %s\n",TMPINNAME);
1061     }
1062 }
1063 
1064 /* fetch a line from the input file, \n terminated, not necessarily \0 */
1065 char *
1066 ifetch(line,whichbuf)
1067 register LINENUM line;
1068 int whichbuf;				/* ignored when file in memory */
1069 {
1070     if (line < 1 || line > input_lines)
1071 	return "";
1072     if (using_plan_a)
1073 	return i_ptr[line];
1074     else {
1075 	LINENUM offline = line % lines_per_buf;
1076 	LINENUM baseline = line - offline;
1077 
1078 	if (tiline[0] == baseline)
1079 	    whichbuf = 0;
1080 	else if (tiline[1] == baseline)
1081 	    whichbuf = 1;
1082 	else {
1083 	    tiline[whichbuf] = baseline;
1084 	    Lseek(tifd,(long)baseline / lines_per_buf * BUFFERSIZE,0);
1085 	    if (read(tifd,tibuf[whichbuf],BUFFERSIZE) < 0)
1086 		fatal("Error reading tmp file %s.\n",TMPINNAME);
1087 	}
1088 	return tibuf[whichbuf] + (tireclen*offline);
1089     }
1090 }
1091 
1092 /* patch abstract type */
1093 
1094 static long p_filesize;			/* size of the patch file */
1095 static LINENUM p_first;			/* 1st line number */
1096 static LINENUM p_newfirst;		/* 1st line number of replacement */
1097 static LINENUM p_ptrn_lines;		/* # lines in pattern */
1098 static LINENUM p_repl_lines;		/* # lines in replacement text */
1099 static LINENUM p_end = -1;		/* last line in hunk */
1100 static LINENUM p_max;			/* max allowed value of p_end */
1101 static LINENUM p_context = 3;		/* # of context lines */
1102 static LINENUM p_input_line = 0;	/* current line # from patch file */
1103 static char *p_line[MAXHUNKSIZE];	/* the text of the hunk */
1104 static char p_char[MAXHUNKSIZE];	/* +, -, and ! */
1105 static int p_len[MAXHUNKSIZE];		/* length of each line */
1106 static int p_indent;			/* indent to patch */
1107 static long p_base;			/* where to intuit this time */
1108 static long p_start;			/* where intuit found a patch */
1109 
1110 re_patch()
1111 {
1112     p_first = (LINENUM)0;
1113     p_newfirst = (LINENUM)0;
1114     p_ptrn_lines = (LINENUM)0;
1115     p_repl_lines = (LINENUM)0;
1116     p_end = (LINENUM)-1;
1117     p_max = (LINENUM)0;
1118     p_indent = 0;
1119 }
1120 
1121 open_patch_file(filename)
1122 char *filename;
1123 {
1124     if (filename == Nullch || !*filename || strEQ(filename,"-")) {
1125 	pfp = fopen(TMPPATNAME,"w");
1126 	if (pfp == Nullfp)
1127 	    fatal("patch: can't create %s.\n",TMPPATNAME);
1128 	while (fgets(buf,sizeof buf,stdin) != NULL)
1129 	    fputs(buf,pfp);
1130 	Fclose(pfp);
1131 	filename = TMPPATNAME;
1132     }
1133     pfp = fopen(filename,"r");
1134     if (pfp == Nullfp)
1135 	fatal("patch file %s not found\n",filename);
1136     Fstat(fileno(pfp), &filestat);
1137     p_filesize = filestat.st_size;
1138     next_intuit_at(0L);			/* start at the beginning */
1139 }
1140 
1141 bool
1142 there_is_another_patch()
1143 {
1144     bool no_input_file = (filearg[0] == Nullch);
1145 
1146     if (p_base != 0L && p_base >= p_filesize) {
1147 	if (verbose)
1148 	    say("done\n");
1149 	return FALSE;
1150     }
1151     if (verbose)
1152 	say("Hmm...");
1153     diff_type = intuit_diff_type();
1154     if (!diff_type) {
1155 	if (p_base != 0L) {
1156 	    if (verbose)
1157 		say("  Ignoring the trailing garbage.\ndone\n");
1158 	}
1159 	else
1160 	    say("  I can't seem to find a patch in there anywhere.\n");
1161 	return FALSE;
1162     }
1163     if (verbose)
1164 	say("  %sooks like %s to me...\n",
1165 	    (p_base == 0L ? "L" : "The next patch l"),
1166 	    diff_type == CONTEXT_DIFF ? "a context diff" :
1167 	    diff_type == NORMAL_DIFF ? "a normal diff" :
1168 	    "an ed script" );
1169     if (p_indent && verbose)
1170 	say("(Patch is indented %d space%s.)\n",p_indent,p_indent==1?"":"s");
1171     skip_to(p_start);
1172     if (no_input_file) {
1173 	if (filearg[0] == Nullch) {
1174 	    ask("File to patch: ");
1175 	    filearg[0] = fetchname(buf);
1176 	}
1177 	else if (verbose) {
1178 	    say("Patching file %s...\n",filearg[0]);
1179 	}
1180     }
1181     return TRUE;
1182 }
1183 
1184 intuit_diff_type()
1185 {
1186     long this_line = 0;
1187     long previous_line;
1188     long first_command_line = -1;
1189     bool last_line_was_command = FALSE;
1190     bool this_line_is_command = FALSE;
1191     register int indent;
1192     register char *s, *t;
1193     char *oldname = Nullch;
1194     char *newname = Nullch;
1195     bool no_filearg = (filearg[0] == Nullch);
1196 
1197     Fseek(pfp,p_base,0);
1198     for (;;) {
1199 	previous_line = this_line;
1200 	last_line_was_command = this_line_is_command;
1201 	this_line = ftell(pfp);
1202 	indent = 0;
1203 	if (fgets(buf,sizeof buf,pfp) == Nullch) {
1204 	    if (first_command_line >= 0L) {
1205 					/* nothing but deletes!? */
1206 		p_start = first_command_line;
1207 		return ED_DIFF;
1208 	    }
1209 	    else {
1210 		p_start = this_line;
1211 		return 0;
1212 	    }
1213 	}
1214 	for (s = buf; *s == ' ' || *s == '\t'; s++) {
1215 	    if (*s == '\t')
1216 		indent += 8 - (indent % 8);
1217 	    else
1218 		indent++;
1219 	}
1220 	for (t=s; isdigit(*t) || *t == ','; t++) ;
1221 	this_line_is_command = (isdigit(*s) &&
1222 	  (*t == 'd' || *t == 'c' || *t == 'a') );
1223 	if (first_command_line < 0L && this_line_is_command) {
1224 	    first_command_line = this_line;
1225 	    p_indent = indent;		/* assume this for now */
1226 	}
1227 	if (strnEQ(s,"*** ",4))
1228 	    oldname = fetchname(s+4);
1229 	else if (strnEQ(s,"--- ",4)) {
1230 	    newname = fetchname(s+4);
1231 	    if (no_filearg) {
1232 		if (oldname && newname) {
1233 		    if (strlen(oldname) < strlen(newname))
1234 			filearg[0] = oldname;
1235 		    else
1236 			filearg[0] = newname;
1237 		}
1238 		else if (oldname)
1239 		    filearg[0] = oldname;
1240 		else if (newname)
1241 		    filearg[0] = newname;
1242 	    }
1243 	}
1244 	else if (strnEQ(s,"Index:",6)) {
1245 	    if (no_filearg)
1246 		filearg[0] = fetchname(s+6);
1247 					/* this filearg might get limboed */
1248 	}
1249 	else if (strnEQ(s,"Prereq:",7)) {
1250 	    for (t=s+7; isspace(*t); t++) ;
1251 	    revision = savestr(t);
1252 	    for (t=revision; *t && !isspace(*t); t++) ;
1253 	    *t = '\0';
1254 	    if (!*revision) {
1255 		free(revision);
1256 		revision = Nullch;
1257 	    }
1258 	}
1259 	if ((!diff_type || diff_type == ED_DIFF) &&
1260 	  first_command_line >= 0L &&
1261 	  strEQ(s,".\n") ) {
1262 	    p_indent = indent;
1263 	    p_start = first_command_line;
1264 	    return ED_DIFF;
1265 	}
1266 	if ((!diff_type || diff_type == CONTEXT_DIFF) &&
1267 		 strnEQ(s,"********",8)) {
1268 	    p_indent = indent;
1269 	    p_start = this_line;
1270 	    return CONTEXT_DIFF;
1271 	}
1272 	if ((!diff_type || diff_type == NORMAL_DIFF) &&
1273 	  last_line_was_command &&
1274 	  (strnEQ(s,"< ",2) || strnEQ(s,"> ",2)) ) {
1275 	    p_start = previous_line;
1276 	    p_indent = indent;
1277 	    return NORMAL_DIFF;
1278 	}
1279     }
1280 }
1281 
1282 char *
1283 fetchname(at)
1284 char *at;
1285 {
1286     char *s = savestr(at);
1287     char *name;
1288     register char *t;
1289     char tmpbuf[200];
1290 
1291     for (t=s; isspace(*t); t++) ;
1292     name = t;
1293     for (; *t && !isspace(*t); t++)
1294 	if (!usepath)
1295 	    if (*t == '/')
1296 		name = t+1;
1297     *t = '\0';
1298     name = savestr(name);
1299     Sprintf(tmpbuf,"RCS/%s",name);
1300     free(s);
1301     if (stat(name,&filestat) < 0) {
1302 	Strcat(tmpbuf,RCSSUFFIX);
1303 	if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+4,&filestat) < 0) {
1304 	    Sprintf(tmpbuf,"SCCS/%s%s",SCCSPREFIX,name);
1305 	    if (stat(tmpbuf,&filestat) < 0 && stat(tmpbuf+5,&filestat) < 0) {
1306 		free(name);
1307 		name = Nullch;
1308 	    }
1309 	}
1310     }
1311     return name;
1312 }
1313 
1314 next_intuit_at(file_pos)
1315 long file_pos;
1316 {
1317     p_base = file_pos;
1318 }
1319 
1320 skip_to(file_pos)
1321 long file_pos;
1322 {
1323     char *ret;
1324 
1325     assert(p_base <= file_pos);
1326     if (verbose && p_base < file_pos) {
1327 	Fseek(pfp,p_base,0);
1328 	say("The text leading up to this was:\n--------------------------\n");
1329 	while (ftell(pfp) < file_pos) {
1330 	    ret = fgets(buf,sizeof buf,pfp);
1331 	    assert(ret != Nullch);
1332 	    say("|%s",buf);
1333 	}
1334 	say("--------------------------\n");
1335     }
1336     else
1337 	Fseek(pfp,file_pos,0);
1338 }
1339 
1340 bool
1341 another_hunk()
1342 {
1343     register char *s;
1344     char *ret;
1345     int context = 0;
1346 
1347     while (p_end >= 0) {
1348 	free(p_line[p_end--]);
1349     }
1350     assert(p_end == -1);
1351 
1352     p_max = MAXHUNKSIZE;		/* gets reduced when --- found */
1353     if (diff_type == CONTEXT_DIFF) {
1354 	long line_beginning = ftell(pfp);
1355 	LINENUM repl_beginning = 0;
1356 
1357 	ret = pgets(buf,sizeof buf, pfp);
1358 	if (ret == Nullch || strnNE(buf,"********",8)) {
1359 	    next_intuit_at(line_beginning);
1360 	    return FALSE;
1361 	}
1362 	p_context = 100;
1363 	while (p_end < p_max) {
1364 	    ret = pgets(buf,sizeof buf, pfp);
1365 	    if (ret == Nullch) {
1366 		if (p_max - p_end < 4)
1367 		    Strcpy(buf,"  \n");	/* assume blank lines got chopped */
1368 		else
1369 		    fatal("Unexpected end of file in patch.\n");
1370 	    }
1371 	    p_input_line++;
1372 	    if (strnEQ(buf,"********",8))
1373 		fatal("Unexpected end of hunk at line %d.\n",
1374 		    p_input_line);
1375 	    p_char[++p_end] = *buf;
1376 	    switch (*buf) {
1377 	    case '*':
1378 		if (p_end != 0)
1379 		    fatal("Unexpected *** at line %d: %s", p_input_line, buf);
1380 		context = 0;
1381 		p_line[p_end] = savestr(buf);
1382 		for (s=buf; *s && !isdigit(*s); s++) ;
1383 		p_first = (LINENUM) atol(s);
1384 		while (isdigit(*s)) s++;
1385 		for (; *s && !isdigit(*s); s++) ;
1386 		p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
1387 		break;
1388 	    case '-':
1389 		if (buf[1] == '-') {
1390 		    if (p_end != p_ptrn_lines + 1 &&
1391 			p_end != p_ptrn_lines + 2)
1392 			fatal("Unexpected --- at line %d: %s",
1393 			    p_input_line,buf);
1394 		    repl_beginning = p_end;
1395 		    context = 0;
1396 		    p_line[p_end] = savestr(buf);
1397 		    p_char[p_end] = '=';
1398 		    for (s=buf; *s && !isdigit(*s); s++) ;
1399 		    p_newfirst = (LINENUM) atol(s);
1400 		    while (isdigit(*s)) s++;
1401 		    for (; *s && !isdigit(*s); s++) ;
1402 		    p_max = ((LINENUM)atol(s)) - p_newfirst + 1 + p_end;
1403 		    break;
1404 		}
1405 		/* FALL THROUGH */
1406 	    case '+': case '!':
1407 		if (context > 0) {
1408 		    if (context < p_context)
1409 			p_context = context;
1410 		    context = -100;
1411 		}
1412 		p_line[p_end] = savestr(buf+2);
1413 		break;
1414 	    case '\t': case '\n':	/* assume the 2 spaces got eaten */
1415 		p_line[p_end] = savestr(buf);
1416 		if (p_end != p_ptrn_lines + 1) {
1417 		    context++;
1418 		    p_char[p_end] = ' ';
1419 		}
1420 		break;
1421 	    case ' ':
1422 		context++;
1423 		p_line[p_end] = savestr(buf+2);
1424 		break;
1425 	    default:
1426 		fatal("Malformed patch at line %d: %s",p_input_line,buf);
1427 	    }
1428 	    p_len[p_end] = strlen(p_line[p_end]);
1429 					/* for strncmp() so we do not have */
1430 					/* to assume null termination */
1431 	}
1432 	if (p_end >=0 && !p_ptrn_lines)
1433 	    fatal("No --- found in patch at line %d\n", pch_hunk_beg());
1434 	p_repl_lines = p_end - repl_beginning;
1435     }
1436     else {				/* normal diff--fake it up */
1437 	char hunk_type;
1438 	register int i;
1439 	LINENUM min, max;
1440 	long line_beginning = ftell(pfp);
1441 
1442 	p_context = 0;
1443 	ret = pgets(buf,sizeof buf, pfp);
1444 	p_input_line++;
1445 	if (ret == Nullch || !isdigit(*buf)) {
1446 	    next_intuit_at(line_beginning);
1447 	    return FALSE;
1448 	}
1449 	p_first = (LINENUM)atol(buf);
1450 	for (s=buf; isdigit(*s); s++) ;
1451 	if (*s == ',') {
1452 	    p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
1453 	    while (isdigit(*s)) s++;
1454 	}
1455 	else
1456 	    p_ptrn_lines = (*s != 'a');
1457 	hunk_type = *s;
1458 	if (hunk_type == 'a')
1459 	    p_first++;			/* do append rather than insert */
1460 	min = (LINENUM)atol(++s);
1461 	for (; isdigit(*s); s++) ;
1462 	if (*s == ',')
1463 	    max = (LINENUM)atol(++s);
1464 	else
1465 	    max = min;
1466 	if (hunk_type == 'd')
1467 	    min++;
1468 	p_end = p_ptrn_lines + 1 + max - min + 1;
1469 	p_newfirst = min;
1470 	p_repl_lines = max - min + 1;
1471 	Sprintf(buf,"*** %d,%d\n", p_first, p_first + p_ptrn_lines - 1);
1472 	p_line[0] = savestr(buf);
1473 	p_char[0] = '*';
1474 	for (i=1; i<=p_ptrn_lines; i++) {
1475 	    ret = pgets(buf,sizeof buf, pfp);
1476 	    p_input_line++;
1477 	    if (ret == Nullch)
1478 		fatal("Unexpected end of file in patch at line %d.\n",
1479 		  p_input_line);
1480 	    if (*buf != '<')
1481 		fatal("< expected at line %d of patch.\n", p_input_line);
1482 	    p_line[i] = savestr(buf+2);
1483 	    p_len[i] = strlen(p_line[i]);
1484 	    p_char[i] = '-';
1485 	}
1486 	if (hunk_type == 'c') {
1487 	    ret = pgets(buf,sizeof buf, pfp);
1488 	    p_input_line++;
1489 	    if (ret == Nullch)
1490 		fatal("Unexpected end of file in patch at line %d.\n",
1491 		    p_input_line);
1492 	    if (*buf != '-')
1493 		fatal("--- expected at line %d of patch.\n", p_input_line);
1494 	}
1495 	Sprintf(buf,"--- %d,%d\n",min,max);
1496 	p_line[i] = savestr(buf);
1497 	p_char[i] = '=';
1498 	for (i++; i<=p_end; i++) {
1499 	    ret = pgets(buf,sizeof buf, pfp);
1500 	    p_input_line++;
1501 	    if (ret == Nullch)
1502 		fatal("Unexpected end of file in patch at line %d.\n",
1503 		    p_input_line);
1504 	    if (*buf != '>')
1505 		fatal("> expected at line %d of patch.\n", p_input_line);
1506 	    p_line[i] = savestr(buf+2);
1507 	    p_len[i] = strlen(p_line[i]);
1508 	    p_char[i] = '+';
1509 	}
1510     }
1511     if (reverse)			/* backwards patch? */
1512 	pch_swap();
1513 #ifdef DEBUGGING
1514     if (debug & 2) {
1515 	int i;
1516 	char special;
1517 
1518 	for (i=0; i <= p_end; i++) {
1519 	    if (i == p_ptrn_lines)
1520 		special = '^';
1521 	    else
1522 		special = ' ';
1523 	    printf("%3d %c %c %s",i,p_char[i],special,p_line[i]);
1524 	}
1525     }
1526 #endif
1527     return TRUE;
1528 }
1529 
1530 char *
1531 pgets(bf,sz,fp)
1532 char *bf;
1533 int sz;
1534 FILE *fp;
1535 {
1536     char *ret = fgets(bf,sz,fp);
1537     register char *s;
1538     register int indent = 0;
1539 
1540     if (p_indent && ret != Nullch) {
1541 	for (s=buf; indent < p_indent && (*s == ' ' || *s == '\t'); s++) {
1542 	    if (*s == '\t')
1543 		indent += 8 - (indent % 7);
1544 	    else
1545 		indent++;
1546 	}
1547 	if (buf != s)
1548 	    Strcpy(buf,s);
1549     }
1550     return ret;
1551 }
1552 
1553 pch_swap()
1554 {
1555     char *tp_line[MAXHUNKSIZE];		/* the text of the hunk */
1556     char tp_char[MAXHUNKSIZE];		/* +, -, and ! */
1557     int tp_len[MAXHUNKSIZE];		/* length of each line */
1558     register LINENUM i, n;
1559     bool blankline = FALSE;
1560     register char *s;
1561 
1562     i = p_first;
1563     p_first = p_newfirst;
1564     p_newfirst = i;
1565 
1566     /* make a scratch copy */
1567 
1568     for (i=0; i<=p_end; i++) {
1569 	tp_line[i] = p_line[i];
1570 	tp_char[i] = p_char[i];
1571 	tp_len[i] = p_len[i];
1572     }
1573 
1574     /* now turn the new into the old */
1575 
1576     i = p_ptrn_lines + 1;
1577     if (tp_char[i] == '\n') {		/* account for possible blank line */
1578 	blankline = TRUE;
1579 	i++;
1580     }
1581     for (n=0; i <= p_end; i++,n++) {
1582 	p_line[n] = tp_line[i];
1583 	p_char[n] = tp_char[i];
1584 	if (p_char[n] == '+')
1585 	    p_char[n] = '-';
1586 	p_len[n] = tp_len[i];
1587     }
1588     if (blankline) {
1589 	i = p_ptrn_lines + 1;
1590 	p_line[n] = tp_line[i];
1591 	p_char[n] = tp_char[i];
1592 	p_len[n] = tp_len[i];
1593 	n++;
1594     }
1595     assert(p_char[0] == '=');
1596     p_char[0] = '*';
1597     for (s=p_line[0]; *s; s++)
1598 	if (*s == '-')
1599 	    *s = '*';
1600 
1601     /* now turn the old into the new */
1602 
1603     assert(tp_char[0] == '*');
1604     tp_char[0] = '=';
1605     for (s=tp_line[0]; *s; s++)
1606 	if (*s == '*')
1607 	    *s = '-';
1608     for (i=0; n <= p_end; i++,n++) {
1609 	p_line[n] = tp_line[i];
1610 	p_char[n] = tp_char[i];
1611 	if (p_char[n] == '-')
1612 	    p_char[n] = '+';
1613 	p_len[n] = tp_len[i];
1614     }
1615     assert(i == p_ptrn_lines + 1);
1616     i = p_ptrn_lines;
1617     p_ptrn_lines = p_repl_lines;
1618     p_repl_lines = i;
1619 }
1620 
1621 LINENUM
1622 pch_first()
1623 {
1624     return p_first;
1625 }
1626 
1627 LINENUM
1628 pch_ptrn_lines()
1629 {
1630     return p_ptrn_lines;
1631 }
1632 
1633 LINENUM
1634 pch_newfirst()
1635 {
1636     return p_newfirst;
1637 }
1638 
1639 LINENUM
1640 pch_repl_lines()
1641 {
1642     return p_repl_lines;
1643 }
1644 
1645 LINENUM
1646 pch_end()
1647 {
1648     return p_end;
1649 }
1650 
1651 LINENUM
1652 pch_context()
1653 {
1654     return p_context;
1655 }
1656 
1657 pch_line_len(line)
1658 LINENUM line;
1659 {
1660     return p_len[line];
1661 }
1662 
1663 char
1664 pch_char(line)
1665 LINENUM line;
1666 {
1667     return p_char[line];
1668 }
1669 
1670 char *
1671 pfetch(line)
1672 LINENUM line;
1673 {
1674     return p_line[line];
1675 }
1676 
1677 LINENUM
1678 pch_hunk_beg()
1679 {
1680     return p_input_line - p_end - 1;
1681 }
1682 
1683 char *
1684 savestr(s)
1685 register char *s;
1686 {
1687     register char  *rv,
1688                    *t;
1689 
1690     t = s;
1691     while (*t++);
1692     rv = malloc((MEM) (t - s));
1693     if (rv == NULL)
1694 	fatal ("patch: out of memory (savestr)\n");
1695     t = rv;
1696     while (*t++ = *s++);
1697     return rv;
1698 }
1699 
1700 my_exit(status)
1701 int status;
1702 {
1703     Unlink(TMPINNAME);
1704     Unlink(TMPOUTNAME);
1705     Unlink(TMPREJNAME);
1706     Unlink(TMPPATNAME);
1707     exit(status);
1708 }
1709 
1710 #ifdef lint
1711 
1712 /*VARARGS ARGSUSED*/
1713 say(pat) char *pat; { ; }
1714 /*VARARGS ARGSUSED*/
1715 fatal(pat) char *pat; { ; }
1716 /*VARARGS ARGSUSED*/
1717 ask(pat) char *pat; { ; }
1718 
1719 #else lint
1720 
1721 say(pat,arg1,arg2,arg3)
1722 char *pat;
1723 int arg1,arg2,arg3;
1724 {
1725     fprintf(stderr,pat,arg1,arg2,arg3);
1726     Fflush(stderr);
1727 }
1728 
1729 fatal(pat,arg1,arg2,arg3)
1730 char *pat;
1731 int arg1,arg2,arg3;
1732 {
1733     say(pat,arg1,arg2,arg3);
1734     my_exit(1);
1735 }
1736 
1737 ask(pat,arg1,arg2,arg3)
1738 char *pat;
1739 int arg1,arg2,arg3;
1740 {
1741     int ttyfd = open("/dev/tty",2);
1742     int r;
1743 
1744     say(pat,arg1,arg2,arg3);
1745     if (ttyfd >= 0) {
1746 	r = read(ttyfd, buf, sizeof buf);
1747 	Close(ttyfd);
1748     }
1749     else
1750 	r = read(2, buf, sizeof buf);
1751     if (r <= 0)
1752 	buf[0] = 0;
1753 }
1754 #endif lint
1755 
1756 bool
1757 rev_in_string(string)
1758 char *string;
1759 {
1760     register char *s;
1761     register int patlen;
1762 
1763     if (revision == Nullch)
1764 	return TRUE;
1765     patlen = strlen(revision);
1766     for (s = string; *s; s++) {
1767 	if (isspace(*s) && strnEQ(s+1,revision,patlen) &&
1768 		isspace(s[patlen+1] )) {
1769 	    return TRUE;
1770 	}
1771     }
1772     return FALSE;
1773 }
1774 
1775 set_signals()
1776 {
1777     /*NOSTRICT*/
1778     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
1779 	Signal(SIGHUP, my_exit);
1780     /*NOSTRICT*/
1781     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
1782 	Signal(SIGINT, my_exit);
1783 }
1784 
1785 ignore_signals()
1786 {
1787     /*NOSTRICT*/
1788     Signal(SIGHUP, SIG_IGN);
1789     /*NOSTRICT*/
1790     Signal(SIGINT, SIG_IGN);
1791 }
1792