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