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