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