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