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