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