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