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