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