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