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