a047ce691daf471c3ab4f3c0e46146d63996c129
[dragonfly.git] / usr.bin / patch / pch.c
1 /*
2  * $OpenBSD: pch.c,v 1.35 2004/08/05 21:47:24 deraadt Exp $
3  * $DragonFly: src/usr.bin/patch/pch.c,v 1.4 2006/04/18 22:11:35 joerg Exp $
4  */
5
6 /*
7  * patch - a program to apply diffs to original files
8  * 
9  * Copyright 1986, Larry Wall
10  * 
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following condition is met:
13  * 1. Redistributions of source code must retain the above copyright notice,
14  * this condition and the following disclaimer.
15  * 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  * 
28  * -C option added in 1998, original code by Marc Espie, based on FreeBSD
29  * behaviour
30  */
31
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 #include <ctype.h>
36 #include <libgen.h>
37 #include <limits.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include "common.h"
44 #include "util.h"
45 #include "pch.h"
46 #include "pathnames.h"
47
48 /* Patch (diff listing) abstract type. */
49
50 static long     p_filesize;     /* size of the patch file */
51 static LINENUM  p_first;        /* 1st line number */
52 static LINENUM  p_newfirst;     /* 1st line number of replacement */
53 static LINENUM  p_ptrn_lines;   /* # lines in pattern */
54 static LINENUM  p_repl_lines;   /* # lines in replacement text */
55 static LINENUM  p_end = -1;     /* last line in hunk */
56 static LINENUM  p_max;          /* max allowed value of p_end */
57 static LINENUM  p_context = 3;  /* # of context lines */
58 static LINENUM  p_input_line = 0;       /* current line # from patch file */
59 static char     **p_line = NULL;/* the text of the hunk */
60 static short    *p_len = NULL;  /* length of each line */
61 static char     *p_char = NULL; /* +, -, and ! */
62 static int      hunkmax = INITHUNKMAX;  /* size of above arrays to begin with */
63 static int      p_indent;       /* indent to patch */
64 static LINENUM  p_base;         /* where to intuit this time */
65 static LINENUM  p_bline;        /* line # of p_base */
66 static LINENUM  p_start;        /* where intuit found a patch */
67 static LINENUM  p_sline;        /* and the line number for it */
68 static LINENUM  p_hunk_beg;     /* line number of current hunk */
69 static LINENUM  p_efake = -1;   /* end of faked up lines--don't free */
70 static LINENUM  p_bfake = -1;   /* beg of faked up lines */
71 static FILE     *pfp = NULL;    /* patch file pointer */
72 static char     *bestguess = NULL;      /* guess at correct filename */
73
74 static void     grow_hunkmax(void);
75 static int      intuit_diff_type(void);
76 static void     next_intuit_at(LINENUM, LINENUM);
77 static void     skip_to(LINENUM, LINENUM);
78 static char     *pgets(char *, int, FILE *);
79 static char     *best_name(const struct file_name *, bool);
80 static char     *posix_name(const struct file_name *, bool);
81 static size_t   num_components(const char *);
82
83 /*
84  * Prepare to look for the next patch in the patch file.
85  */
86 void
87 re_patch(void)
88 {
89         p_first = 0;
90         p_newfirst = 0;
91         p_ptrn_lines = 0;
92         p_repl_lines = 0;
93         p_end = (LINENUM) - 1;
94         p_max = 0;
95         p_indent = 0;
96 }
97
98 /*
99  * Open the patch file at the beginning of time.
100  */
101 void
102 open_patch_file(const char *filename)
103 {
104         struct stat filestat;
105
106         if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
107                 pfp = fopen(TMPPATNAME, "w");
108                 if (pfp == NULL)
109                         pfatal("can't create %s", TMPPATNAME);
110                 while (fgets(buf, buf_len, stdin) != NULL)
111                         fputs(buf, pfp);
112                 fclose(pfp);
113                 filename = TMPPATNAME;
114         }
115         pfp = fopen(filename, "r");
116         if (pfp == NULL)
117                 pfatal("patch file %s not found", filename);
118         fstat(fileno(pfp), &filestat);
119         p_filesize = filestat.st_size;
120         next_intuit_at(0L, 1L); /* start at the beginning */
121         set_hunkmax();
122 }
123
124 /*
125  * Make sure our dynamically realloced tables are malloced to begin with.
126  */
127 void
128 set_hunkmax(void)
129 {
130         if (p_line == NULL)
131                 p_line = malloc((size_t) hunkmax * sizeof(char *));
132         if (p_len == NULL)
133                 p_len = malloc((size_t) hunkmax * sizeof(short));
134         if (p_char == NULL)
135                 p_char = malloc((size_t) hunkmax * sizeof(char));
136 }
137
138 /*
139  * Enlarge the arrays containing the current hunk of patch.
140  */
141 static void
142 grow_hunkmax(void)
143 {
144         int             new_hunkmax;
145         char            **new_p_line;
146         short           *new_p_len;
147         char            *new_p_char;
148
149         new_hunkmax = hunkmax * 2;
150
151         if (p_line == NULL || p_len == NULL || p_char == NULL)
152                 fatal("Internal memory allocation error\n");
153
154         new_p_line = realloc(p_line, new_hunkmax * sizeof(char *));
155         if (new_p_line == NULL)
156                 free(p_line);
157
158         new_p_len = realloc(p_len, new_hunkmax * sizeof(short));
159         if (new_p_len == NULL)
160                 free(p_len);
161
162         new_p_char = realloc(p_char, new_hunkmax * sizeof(char));
163         if (new_p_char == NULL)
164                 free(p_char);
165
166         p_char = new_p_char;
167         p_len = new_p_len;
168         p_line = new_p_line;
169
170         if (p_line != NULL && p_len != NULL && p_char != NULL) {
171                 hunkmax = new_hunkmax;
172                 return;
173         }
174
175         if (!using_plan_a)
176                 fatal("out of memory\n");
177         out_of_mem = true;      /* whatever is null will be allocated again */
178                                 /* from within plan_a(), of all places */
179 }
180
181 /* True if the remainder of the patch file contains a diff of some sort. */
182
183 bool
184 there_is_another_patch(void)
185 {
186         bool exists = false;
187
188         if (p_base != 0L && p_base >= p_filesize) {
189                 if (verbose)
190                         say("done\n");
191                 return false;
192         }
193         if (verbose)
194                 say("Hmm...");
195         diff_type = intuit_diff_type();
196         if (!diff_type) {
197                 if (p_base != 0L) {
198                         if (verbose)
199                                 say("  Ignoring the trailing garbage.\ndone\n");
200                 } else
201                         say("  I can't seem to find a patch in there anywhere.\n");
202                 return false;
203         }
204         if (verbose)
205                 say("  %sooks like %s to me...\n",
206                     (p_base == 0L ? "L" : "The next patch l"),
207                     diff_type == UNI_DIFF ? "a unified diff" :
208                     diff_type == CONTEXT_DIFF ? "a context diff" :
209                 diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
210                     diff_type == NORMAL_DIFF ? "a normal diff" :
211                     "an ed script");
212         if (p_indent && verbose)
213                 say("(Patch is indented %d space%s.)\n", p_indent,
214                     p_indent == 1 ? "" : "s");
215         skip_to(p_start, p_sline);
216         while (filearg[0] == NULL) {
217                 if (force || batch) {
218                         say("No file to patch.  Skipping...\n");
219                         filearg[0] = savestr(bestguess);
220                         skip_rest_of_patch = true;
221                         return true;
222                 }
223                 ask("File to patch: ");
224                 if (*buf != '\n') {
225                         free(bestguess);
226                         bestguess = savestr(buf);
227                         filearg[0] = fetchname(buf, &exists, 0);
228                 }
229                 if (!exists) {
230                         ask("No file found--skip this patch? [n] ");
231                         if (*buf != 'y')
232                                 continue;
233                         if (verbose)
234                                 say("Skipping patch...\n");
235                         free(filearg[0]);
236                         filearg[0] = fetchname(bestguess, &exists, 0);
237                         skip_rest_of_patch = true;
238                         return true;
239                 }
240         }
241         return true;
242 }
243
244 /* Determine what kind of diff is in the remaining part of the patch file. */
245
246 static int
247 intuit_diff_type(void)
248 {
249         long    this_line = 0, previous_line;
250         long    first_command_line = -1;
251         LINENUM fcl_line = -1;
252         bool    last_line_was_command = false, this_is_a_command = false;
253         bool    stars_last_line = false, stars_this_line = false;
254         char    *s, *t;
255         int     indent, retval;
256         struct file_name names[MAX_FILE];
257
258         memset(names, 0, sizeof(names));
259         ok_to_create_file = false;
260         fseek(pfp, p_base, SEEK_SET);
261         p_input_line = p_bline - 1;
262         for (;;) {
263                 previous_line = this_line;
264                 last_line_was_command = this_is_a_command;
265                 stars_last_line = stars_this_line;
266                 this_line = ftell(pfp);
267                 indent = 0;
268                 p_input_line++;
269                 if (fgets(buf, buf_len, pfp) == NULL) {
270                         if (first_command_line >= 0L) {
271                                 /* nothing but deletes!? */
272                                 p_start = first_command_line;
273                                 p_sline = fcl_line;
274                                 retval = ED_DIFF;
275                                 goto scan_exit;
276                         } else {
277                                 p_start = this_line;
278                                 p_sline = p_input_line;
279                                 retval = 0;
280                                 goto scan_exit;
281                         }
282                 }
283                 for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
284                         if (*s == '\t')
285                                 indent += 8 - (indent % 8);
286                         else
287                                 indent++;
288                 }
289                 for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
290                         ;
291                 this_is_a_command = (isdigit((unsigned char)*s) &&
292                     (*t == 'd' || *t == 'c' || *t == 'a'));
293                 if (first_command_line < 0L && this_is_a_command) {
294                         first_command_line = this_line;
295                         fcl_line = p_input_line;
296                         p_indent = indent;      /* assume this for now */
297                 }
298                 if (!stars_last_line && strnEQ(s, "*** ", 4))
299                         names[OLD_FILE].path = fetchname(s + 4,
300                             &names[OLD_FILE].exists, strippath);
301                 else if (strnEQ(s, "--- ", 4))
302                         names[NEW_FILE].path = fetchname(s + 4,
303                             &names[NEW_FILE].exists, strippath);
304                 else if (strnEQ(s, "+++ ", 4))
305                         /* pretend it is the old name */
306                         names[OLD_FILE].path = fetchname(s + 4,
307                             &names[OLD_FILE].exists, strippath);
308                 else if (strnEQ(s, "Index:", 6))
309                         names[INDEX_FILE].path = fetchname(s + 6,
310                             &names[INDEX_FILE].exists, strippath);
311                 else if (strnEQ(s, "Prereq:", 7)) {
312                         for (t = s + 7; isspace((unsigned char)*t); t++)
313                                 ;
314                         revision = savestr(t);
315                         for (t = revision; *t && !isspace((unsigned char)*t); t++)
316                                 ;
317                         *t = '\0';
318                         if (*revision == '\0') {
319                                 free(revision);
320                                 revision = NULL;
321                         }
322                 }
323                 if ((!diff_type || diff_type == ED_DIFF) &&
324                     first_command_line >= 0L &&
325                     strEQ(s, ".\n")) {
326                         p_indent = indent;
327                         p_start = first_command_line;
328                         p_sline = fcl_line;
329                         retval = ED_DIFF;
330                         goto scan_exit;
331                 }
332                 if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
333                         if (strnEQ(s + 4, "0,0", 3))
334                                 ok_to_create_file = true;
335                         p_indent = indent;
336                         p_start = this_line;
337                         p_sline = p_input_line;
338                         retval = UNI_DIFF;
339                         goto scan_exit;
340                 }
341                 stars_this_line = strnEQ(s, "********", 8);
342                 if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
343                     strnEQ(s, "*** ", 4)) {
344                         if (atol(s + 4) == 0)
345                                 ok_to_create_file = true;
346                         /*
347                          * If this is a new context diff the character just
348                          * before the newline is a '*'.
349                          */
350                         while (*s != '\n')
351                                 s++;
352                         p_indent = indent;
353                         p_start = previous_line;
354                         p_sline = p_input_line - 1;
355                         retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
356                         goto scan_exit;
357                 }
358                 if ((!diff_type || diff_type == NORMAL_DIFF) &&
359                     last_line_was_command &&
360                     (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
361                         p_start = previous_line;
362                         p_sline = p_input_line - 1;
363                         p_indent = indent;
364                         retval = NORMAL_DIFF;
365                         goto scan_exit;
366                 }
367         }
368 scan_exit:
369         if (retval == UNI_DIFF) {
370                 /* unswap old and new */
371                 struct file_name tmp = names[OLD_FILE];
372                 names[OLD_FILE] = names[NEW_FILE];
373                 names[NEW_FILE] = tmp;
374         }
375         if (filearg[0] == NULL) {
376                 if (posix)
377                         filearg[0] = posix_name(names, ok_to_create_file);
378                 else {
379                         /* Ignore the Index: name for context diffs, like GNU */
380                         if (names[OLD_FILE].path != NULL ||
381                             names[NEW_FILE].path != NULL) {
382                                 free(names[INDEX_FILE].path);
383                                 names[INDEX_FILE].path = NULL;
384                         }
385                         filearg[0] = best_name(names, ok_to_create_file);
386                 }
387         }
388
389         free(bestguess);
390         bestguess = NULL;
391         if (filearg[0] != NULL)
392                 bestguess = savestr(filearg[0]);
393         else if (!ok_to_create_file) {
394                 /*
395                  * We don't want to create a new file but we need a
396                  * filename to set bestguess.  Avoid setting filearg[0]
397                  * so the file is not created automatically.
398                  */
399                 if (posix)
400                         bestguess = posix_name(names, true);
401                 else
402                         bestguess = best_name(names, true);
403         }
404         free(names[OLD_FILE].path);
405         free(names[NEW_FILE].path);
406         free(names[INDEX_FILE].path);
407         return retval;
408 }
409
410 /*
411  * Remember where this patch ends so we know where to start up again.
412  */
413 static void
414 next_intuit_at(LINENUM file_pos, LINENUM file_line)
415 {
416         p_base = file_pos;
417         p_bline = file_line;
418 }
419
420 /*
421  * Basically a verbose fseek() to the actual diff listing.
422  */
423 static void
424 skip_to(LINENUM file_pos, LINENUM file_line)
425 {
426         char    *ret;
427
428         if (p_base > file_pos)
429                 fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
430         if (verbose && p_base < file_pos) {
431                 fseek(pfp, p_base, SEEK_SET);
432                 say("The text leading up to this was:\n--------------------------\n");
433                 while (ftell(pfp) < file_pos) {
434                         ret = fgets(buf, buf_len, pfp);
435                         if (ret == NULL)
436                                 fatal("Unexpected end of file\n");
437                         say("|%s", buf);
438                 }
439                 say("--------------------------\n");
440         } else
441                 fseek(pfp, file_pos, SEEK_SET);
442         p_input_line = file_line - 1;
443 }
444
445 /* Make this a function for better debugging.  */
446 static void
447 malformed(void)
448 {
449         fatal("malformed patch at line %ld: %s", p_input_line, buf);
450         /* about as informative as "Syntax error" in C */
451 }
452
453 /*
454  * True if the line has been discarded (i.e. it is a line saying
455  *  "\ No newline at end of file".)
456  */
457 static bool
458 remove_special_line(void)
459 {
460         int     c;
461
462         c = fgetc(pfp);
463         if (c == '\\') {
464                 do {
465                         c = fgetc(pfp);
466                 } while (c != EOF && c != '\n');
467
468                 return true;
469         }
470         if (c != EOF)
471                 fseek(pfp, -1L, SEEK_CUR);
472
473         return false;
474 }
475
476 /*
477  * True if there is more of the current diff listing to process.
478  */
479 bool
480 another_hunk(void)
481 {
482         long    line_beginning;                 /* file pos of the current line */
483         LINENUM repl_beginning;                 /* index of --- line */
484         LINENUM fillcnt;                        /* #lines of missing ptrn or repl */
485         LINENUM fillsrc;                        /* index of first line to copy */
486         LINENUM filldst;                        /* index of first missing line */
487         bool    ptrn_spaces_eaten;              /* ptrn was slightly misformed */
488         bool    repl_could_be_missing;          /* no + or ! lines in this hunk */
489         bool    repl_missing;                   /* we are now backtracking */
490         long    repl_backtrack_position;        /* file pos of first repl line */
491         LINENUM repl_patch_line;                /* input line number for same */
492         LINENUM ptrn_copiable;                  /* # of copiable lines in ptrn */
493         char    *s, *ret;
494         int     context = 0;
495
496         while (p_end >= 0) {
497                 if (p_end == p_efake)
498                         p_end = p_bfake;        /* don't free twice */
499                 else
500                         free(p_line[p_end]);
501                 p_end--;
502         }
503         p_efake = -1;
504
505         p_max = hunkmax;        /* gets reduced when --- found */
506         if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
507                 line_beginning = ftell(pfp);
508                 repl_beginning = 0;
509                 fillcnt = 0;
510                 fillsrc = 0;
511                 filldst = 0;
512                 ptrn_spaces_eaten = false;
513                 repl_could_be_missing = true;
514                 repl_missing = false;
515                 repl_backtrack_position = 0;
516                 repl_patch_line = 0;
517                 ptrn_copiable = 0;
518
519                 ret = pgets(buf, buf_len, pfp);
520                 p_input_line++;
521                 if (ret == NULL || strnNE(buf, "********", 8)) {
522                         next_intuit_at(line_beginning, p_input_line);
523                         return false;
524                 }
525                 p_context = 100;
526                 p_hunk_beg = p_input_line + 1;
527                 while (p_end < p_max) {
528                         line_beginning = ftell(pfp);
529                         ret = pgets(buf, buf_len, pfp);
530                         p_input_line++;
531                         if (ret == NULL) {
532                                 if (p_max - p_end < 4) {
533                                         /* assume blank lines got chopped */
534                                         strlcpy(buf, "  \n", buf_len);
535                                 } else {
536                                         if (repl_beginning && repl_could_be_missing) {
537                                                 repl_missing = true;
538                                                 goto hunk_done;
539                                         }
540                                         fatal("unexpected end of file in patch\n");
541                                 }
542                         }
543                         p_end++;
544                         if (p_end >= hunkmax)
545                                 fatal("Internal error: hunk larger than hunk "
546                                     "buffer size");
547                         p_char[p_end] = *buf;
548                         p_line[p_end] = NULL;
549                         switch (*buf) {
550                         case '*':
551                                 if (strnEQ(buf, "********", 8)) {
552                                         if (repl_beginning && repl_could_be_missing) {
553                                                 repl_missing = true;
554                                                 goto hunk_done;
555                                         } else
556                                                 fatal("unexpected end of hunk "
557                                                     "at line %ld\n",
558                                                     p_input_line);
559                                 }
560                                 if (p_end != 0) {
561                                         if (repl_beginning && repl_could_be_missing) {
562                                                 repl_missing = true;
563                                                 goto hunk_done;
564                                         }
565                                         fatal("unexpected *** at line %ld: %s",
566                                             p_input_line, buf);
567                                 }
568                                 context = 0;
569                                 p_line[p_end] = savestr(buf);
570                                 if (out_of_mem) {
571                                         p_end--;
572                                         return false;
573                                 }
574                                 for (s = buf; *s && !isdigit((unsigned char)*s); s++)
575                                         ;
576                                 if (!*s)
577                                         malformed();
578                                 if (strnEQ(s, "0,0", 3))
579                                         memmove(s, s + 2, strlen(s + 2) + 1);
580                                 p_first = (LINENUM) atol(s);
581                                 while (isdigit((unsigned char)*s))
582                                         s++;
583                                 if (*s == ',') {
584                                         for (; *s && !isdigit((unsigned char)*s); s++)
585                                                 ;
586                                         if (!*s)
587                                                 malformed();
588                                         p_ptrn_lines = ((LINENUM) atol(s)) - p_first + 1;
589                                 } else if (p_first)
590                                         p_ptrn_lines = 1;
591                                 else {
592                                         p_ptrn_lines = 0;
593                                         p_first = 1;
594                                 }
595
596                                 /* we need this much at least */
597                                 p_max = p_ptrn_lines + 6;
598                                 while (p_max >= hunkmax)
599                                         grow_hunkmax();
600                                 p_max = hunkmax;
601                                 break;
602                         case '-':
603                                 if (buf[1] == '-') {
604                                         if (repl_beginning ||
605                                             (p_end != p_ptrn_lines + 1 +
606                                             (p_char[p_end - 1] == '\n'))) {
607                                                 if (p_end == 1) {
608                                                         /*
609                                                          * `old' lines were omitted;
610                                                          * set up to fill them in
611                                                          * from 'new' context lines.
612                                                          */
613                                                         p_end = p_ptrn_lines + 1;
614                                                         fillsrc = p_end + 1;
615                                                         filldst = 1;
616                                                         fillcnt = p_ptrn_lines;
617                                                 } else {
618                                                         if (repl_beginning) {
619                                                                 if (repl_could_be_missing) {
620                                                                         repl_missing = true;
621                                                                         goto hunk_done;
622                                                                 }
623                                                                 fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n",
624                                                                     p_input_line, p_hunk_beg + repl_beginning);
625                                                         } else {
626                                                                 fatal("%s \"---\" at line %ld--check line numbers at line %ld\n",
627                                                                     (p_end <= p_ptrn_lines
628                                                                     ? "Premature"
629                                                                     : "Overdue"),
630                                                                     p_input_line, p_hunk_beg);
631                                                         }
632                                                 }
633                                         }
634                                         repl_beginning = p_end;
635                                         repl_backtrack_position = ftell(pfp);
636                                         repl_patch_line = p_input_line;
637                                         p_line[p_end] = savestr(buf);
638                                         if (out_of_mem) {
639                                                 p_end--;
640                                                 return false;
641                                         }
642                                         p_char[p_end] = '=';
643                                         for (s = buf; *s && !isdigit((unsigned char)*s); s++)
644                                                 ;
645                                         if (!*s)
646                                                 malformed();
647                                         p_newfirst = (LINENUM) atol(s);
648                                         while (isdigit((unsigned char)*s))
649                                                 s++;
650                                         if (*s == ',') {
651                                                 for (; *s && !isdigit((unsigned char)*s); s++)
652                                                         ;
653                                                 if (!*s)
654                                                         malformed();
655                                                 p_repl_lines = ((LINENUM) atol(s)) -
656                                                     p_newfirst + 1;
657                                         } else if (p_newfirst)
658                                                 p_repl_lines = 1;
659                                         else {
660                                                 p_repl_lines = 0;
661                                                 p_newfirst = 1;
662                                         }
663                                         p_max = p_repl_lines + p_end;
664                                         if (p_max > MAXHUNKSIZE)
665                                                 fatal("hunk too large (%ld lines) at line %ld: %s",
666                                                     p_max, p_input_line, buf);
667                                         while (p_max >= hunkmax)
668                                                 grow_hunkmax();
669                                         if (p_repl_lines != ptrn_copiable &&
670                                             (p_context != 0 || p_repl_lines != 1))
671                                                 repl_could_be_missing = false;
672                                         break;
673                                 }
674                                 goto change_line;
675                         case '+':
676                         case '!':
677                                 repl_could_be_missing = false;
678                 change_line:
679                                 if (buf[1] == '\n' && canonicalize)
680                                         strlcpy(buf + 1, " \n", buf_len - 1);
681                                 if (!isspace((unsigned char)buf[1]) && buf[1] != '>' &&
682                                     buf[1] != '<' &&
683                                     repl_beginning && repl_could_be_missing) {
684                                         repl_missing = true;
685                                         goto hunk_done;
686                                 }
687                                 if (context >= 0) {
688                                         if (context < p_context)
689                                                 p_context = context;
690                                         context = -1000;
691                                 }
692                                 p_line[p_end] = savestr(buf + 2);
693                                 if (out_of_mem) {
694                                         p_end--;
695                                         return false;
696                                 }
697                                 if (p_end == p_ptrn_lines) {
698                                         if (remove_special_line()) {
699                                                 int     len;
700
701                                                 len = strlen(p_line[p_end]) - 1;
702                                                 (p_line[p_end])[len] = 0;
703                                         }
704                                 }
705                                 break;
706                         case '\t':
707                         case '\n':      /* assume the 2 spaces got eaten */
708                                 if (repl_beginning && repl_could_be_missing &&
709                                     (!ptrn_spaces_eaten ||
710                                     diff_type == NEW_CONTEXT_DIFF)) {
711                                         repl_missing = true;
712                                         goto hunk_done;
713                                 }
714                                 p_line[p_end] = savestr(buf);
715                                 if (out_of_mem) {
716                                         p_end--;
717                                         return false;
718                                 }
719                                 if (p_end != p_ptrn_lines + 1) {
720                                         ptrn_spaces_eaten |= (repl_beginning != 0);
721                                         context++;
722                                         if (!repl_beginning)
723                                                 ptrn_copiable++;
724                                         p_char[p_end] = ' ';
725                                 }
726                                 break;
727                         case ' ':
728                                 if (!isspace((unsigned char)buf[1]) &&
729                                     repl_beginning && repl_could_be_missing) {
730                                         repl_missing = true;
731                                         goto hunk_done;
732                                 }
733                                 context++;
734                                 if (!repl_beginning)
735                                         ptrn_copiable++;
736                                 p_line[p_end] = savestr(buf + 2);
737                                 if (out_of_mem) {
738                                         p_end--;
739                                         return false;
740                                 }
741                                 break;
742                         default:
743                                 if (repl_beginning && repl_could_be_missing) {
744                                         repl_missing = true;
745                                         goto hunk_done;
746                                 }
747                                 malformed();
748                         }
749                         /* set up p_len for strncmp() so we don't have to */
750                         /* assume null termination */
751                         if (p_line[p_end])
752                                 p_len[p_end] = strlen(p_line[p_end]);
753                         else
754                                 p_len[p_end] = 0;
755                 }
756
757 hunk_done:
758                 if (p_end >= 0 && !repl_beginning)
759                         fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
760
761                 if (repl_missing) {
762
763                         /* reset state back to just after --- */
764                         p_input_line = repl_patch_line;
765                         for (p_end--; p_end > repl_beginning; p_end--)
766                                 free(p_line[p_end]);
767                         fseek(pfp, repl_backtrack_position, SEEK_SET);
768
769                         /* redundant 'new' context lines were omitted - set */
770                         /* up to fill them in from the old file context */
771                         if (!p_context && p_repl_lines == 1) {
772                                 p_repl_lines = 0;
773                                 p_max--;
774                         }
775                         fillsrc = 1;
776                         filldst = repl_beginning + 1;
777                         fillcnt = p_repl_lines;
778                         p_end = p_max;
779                 } else if (!p_context && fillcnt == 1) {
780                         /* the first hunk was a null hunk with no context */
781                         /* and we were expecting one line -- fix it up. */
782                         while (filldst < p_end) {
783                                 p_line[filldst] = p_line[filldst + 1];
784                                 p_char[filldst] = p_char[filldst + 1];
785                                 p_len[filldst] = p_len[filldst + 1];
786                                 filldst++;
787                         }
788 #if 0
789                         repl_beginning--;       /* this doesn't need to be fixed */
790 #endif
791                         p_end--;
792                         p_first++;      /* do append rather than insert */
793                         fillcnt = 0;
794                         p_ptrn_lines = 0;
795                 }
796                 if (diff_type == CONTEXT_DIFF &&
797                     (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
798                         if (verbose)
799                                 say("%s\n%s\n%s\n",
800                                     "(Fascinating--this is really a new-style context diff but without",
801                                     "the telltale extra asterisks on the *** line that usually indicate",
802                                     "the new style...)");
803                         diff_type = NEW_CONTEXT_DIFF;
804                 }
805                 /* if there were omitted context lines, fill them in now */
806                 if (fillcnt) {
807                         p_bfake = filldst;      /* remember where not to free() */
808                         p_efake = filldst + fillcnt - 1;
809                         while (fillcnt-- > 0) {
810                                 while (fillsrc <= p_end && p_char[fillsrc] != ' ')
811                                         fillsrc++;
812                                 if (fillsrc > p_end)
813                                         fatal("replacement text or line numbers mangled in hunk at line %ld\n",
814                                             p_hunk_beg);
815                                 p_line[filldst] = p_line[fillsrc];
816                                 p_char[filldst] = p_char[fillsrc];
817                                 p_len[filldst] = p_len[fillsrc];
818                                 fillsrc++;
819                                 filldst++;
820                         }
821                         while (fillsrc <= p_end && fillsrc != repl_beginning &&
822                             p_char[fillsrc] != ' ')
823                                 fillsrc++;
824 #ifdef DEBUGGING
825                         if (debug & 64)
826                                 printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
827                                 fillsrc, filldst, repl_beginning, p_end + 1);
828 #endif
829                         if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
830                                 malformed();
831                         if (filldst != p_end + 1 && filldst != repl_beginning)
832                                 malformed();
833                 }
834                 if (p_line[p_end] != NULL) {
835                         if (remove_special_line()) {
836                                 p_len[p_end] -= 1;
837                                 (p_line[p_end])[p_len[p_end]] = 0;
838                         }
839                 }
840         } else if (diff_type == UNI_DIFF) {
841                 LINENUM fillold;        /* index of old lines */
842                 LINENUM fillnew;        /* index of new lines */
843                 char    ch;
844
845                 line_beginning = ftell(pfp); /* file pos of the current line */
846                 ret = pgets(buf, buf_len, pfp);
847                 p_input_line++;
848                 if (ret == NULL || strnNE(buf, "@@ -", 4)) {
849                         next_intuit_at(line_beginning, p_input_line);
850                         return false;
851                 }
852                 s = buf + 4;
853                 if (!*s)
854                         malformed();
855                 p_first = (LINENUM) atol(s);
856                 while (isdigit((unsigned char)*s))
857                         s++;
858                 if (*s == ',') {
859                         p_ptrn_lines = (LINENUM) atol(++s);
860                         while (isdigit((unsigned char)*s))
861                                 s++;
862                 } else
863                         p_ptrn_lines = 1;
864                 if (*s == ' ')
865                         s++;
866                 if (*s != '+' || !*++s)
867                         malformed();
868                 p_newfirst = (LINENUM) atol(s);
869                 while (isdigit((unsigned char)*s))
870                         s++;
871                 if (*s == ',') {
872                         p_repl_lines = (LINENUM) atol(++s);
873                         while (isdigit((unsigned char)*s))
874                                 s++;
875                 } else
876                         p_repl_lines = 1;
877                 if (*s == ' ')
878                         s++;
879                 if (*s != '@')
880                         malformed();
881                 if (!p_ptrn_lines)
882                         p_first++;      /* do append rather than insert */
883                 p_max = p_ptrn_lines + p_repl_lines + 1;
884                 while (p_max >= hunkmax)
885                         grow_hunkmax();
886                 fillold = 1;
887                 fillnew = fillold + p_ptrn_lines;
888                 p_end = fillnew + p_repl_lines;
889                 snprintf(buf, buf_len, "*** %ld,%ld ****\n", p_first,
890                     p_first + p_ptrn_lines - 1);
891                 p_line[0] = savestr(buf);
892                 if (out_of_mem) {
893                         p_end = -1;
894                         return false;
895                 }
896                 p_char[0] = '*';
897                 snprintf(buf, buf_len, "--- %ld,%ld ----\n", p_newfirst,
898                     p_newfirst + p_repl_lines - 1);
899                 p_line[fillnew] = savestr(buf);
900                 if (out_of_mem) {
901                         p_end = 0;
902                         return false;
903                 }
904                 p_char[fillnew++] = '=';
905                 p_context = 100;
906                 context = 0;
907                 p_hunk_beg = p_input_line + 1;
908                 while (fillold <= p_ptrn_lines || fillnew <= p_end) {
909                         line_beginning = ftell(pfp);
910                         ret = pgets(buf, buf_len, pfp);
911                         p_input_line++;
912                         if (ret == NULL) {
913                                 if (p_max - fillnew < 3) {
914                                         /* assume blank lines got chopped */
915                                         strlcpy(buf, " \n", buf_len);
916                                 } else {
917                                         fatal("unexpected end of file in patch\n");
918                                 }
919                         }
920                         if (*buf == '\t' || *buf == '\n') {
921                                 ch = ' ';       /* assume the space got eaten */
922                                 s = savestr(buf);
923                         } else {
924                                 ch = *buf;
925                                 s = savestr(buf + 1);
926                         }
927                         if (out_of_mem) {
928                                 while (--fillnew > p_ptrn_lines)
929                                         free(p_line[fillnew]);
930                                 p_end = fillold - 1;
931                                 return false;
932                         }
933                         switch (ch) {
934                         case '-':
935                                 if (fillold > p_ptrn_lines) {
936                                         free(s);
937                                         p_end = fillnew - 1;
938                                         malformed();
939                                 }
940                                 p_char[fillold] = ch;
941                                 p_line[fillold] = s;
942                                 p_len[fillold++] = strlen(s);
943                                 if (fillold > p_ptrn_lines) {
944                                         if (remove_special_line()) {
945                                                 p_len[fillold - 1] -= 1;
946                                                 s[p_len[fillold - 1]] = 0;
947                                         }
948                                 }
949                                 break;
950                         case '=':
951                                 ch = ' ';
952                                 /* FALL THROUGH */
953                         case ' ':
954                                 if (fillold > p_ptrn_lines) {
955                                         free(s);
956                                         while (--fillnew > p_ptrn_lines)
957                                                 free(p_line[fillnew]);
958                                         p_end = fillold - 1;
959                                         malformed();
960                                 }
961                                 context++;
962                                 p_char[fillold] = ch;
963                                 p_line[fillold] = s;
964                                 p_len[fillold++] = strlen(s);
965                                 s = savestr(s);
966                                 if (out_of_mem) {
967                                         while (--fillnew > p_ptrn_lines)
968                                                 free(p_line[fillnew]);
969                                         p_end = fillold - 1;
970                                         return false;
971                                 }
972                                 if (fillold > p_ptrn_lines) {
973                                         if (remove_special_line()) {
974                                                 p_len[fillold - 1] -= 1;
975                                                 s[p_len[fillold - 1]] = 0;
976                                         }
977                                 }
978                                 /* FALL THROUGH */
979                         case '+':
980                                 if (fillnew > p_end) {
981                                         free(s);
982                                         while (--fillnew > p_ptrn_lines)
983                                                 free(p_line[fillnew]);
984                                         p_end = fillold - 1;
985                                         malformed();
986                                 }
987                                 p_char[fillnew] = ch;
988                                 p_line[fillnew] = s;
989                                 p_len[fillnew++] = strlen(s);
990                                 if (fillold > p_ptrn_lines) {
991                                         if (remove_special_line()) {
992                                                 p_len[fillnew - 1] -= 1;
993                                                 s[p_len[fillnew - 1]] = 0;
994                                         }
995                                 }
996                                 break;
997                         default:
998                                 p_end = fillnew;
999                                 malformed();
1000                         }
1001                         if (ch != ' ' && context > 0) {
1002                                 if (context < p_context)
1003                                         p_context = context;
1004                                 context = -1000;
1005                         }
1006                 }               /* while */
1007         } else {                /* normal diff--fake it up */
1008                 char    hunk_type;
1009                 int     i;
1010                 LINENUM min, max;
1011
1012                 line_beginning = ftell(pfp);
1013                 p_context = 0;
1014                 ret = pgets(buf, buf_len, pfp);
1015                 p_input_line++;
1016                 if (ret == NULL || !isdigit((unsigned char)*buf)) {
1017                         next_intuit_at(line_beginning, p_input_line);
1018                         return false;
1019                 }
1020                 p_first = (LINENUM) atol(buf);
1021                 for (s = buf; isdigit((unsigned char)*s); s++)
1022                         ;
1023                 if (*s == ',') {
1024                         p_ptrn_lines = (LINENUM) atol(++s) - p_first + 1;
1025                         while (isdigit((unsigned char)*s))
1026                                 s++;
1027                 } else
1028                         p_ptrn_lines = (*s != 'a');
1029                 hunk_type = *s;
1030                 if (hunk_type == 'a')
1031                         p_first++;      /* do append rather than insert */
1032                 min = (LINENUM) atol(++s);
1033                 for (; isdigit((unsigned char)*s); s++)
1034                         ;
1035                 if (*s == ',')
1036                         max = (LINENUM) atol(++s);
1037                 else
1038                         max = min;
1039                 if (hunk_type == 'd')
1040                         min++;
1041                 p_end = p_ptrn_lines + 1 + max - min + 1;
1042                 if (p_end > MAXHUNKSIZE)
1043                         fatal("hunk too large (%ld lines) at line %ld: %s",
1044                             p_end, p_input_line, buf);
1045                 while (p_end >= hunkmax)
1046                         grow_hunkmax();
1047                 p_newfirst = min;
1048                 p_repl_lines = max - min + 1;
1049                 snprintf(buf, buf_len, "*** %ld,%ld\n", p_first,
1050                     p_first + p_ptrn_lines - 1);
1051                 p_line[0] = savestr(buf);
1052                 if (out_of_mem) {
1053                         p_end = -1;
1054                         return false;
1055                 }
1056                 p_char[0] = '*';
1057                 for (i = 1; i <= p_ptrn_lines; i++) {
1058                         ret = pgets(buf, buf_len, pfp);
1059                         p_input_line++;
1060                         if (ret == NULL)
1061                                 fatal("unexpected end of file in patch at line %ld\n",
1062                                     p_input_line);
1063                         if (*buf != '<')
1064                                 fatal("< expected at line %ld of patch\n",
1065                                     p_input_line);
1066                         p_line[i] = savestr(buf + 2);
1067                         if (out_of_mem) {
1068                                 p_end = i - 1;
1069                                 return false;
1070                         }
1071                         p_len[i] = strlen(p_line[i]);
1072                         p_char[i] = '-';
1073                 }
1074
1075                 if (remove_special_line()) {
1076                         p_len[i - 1] -= 1;
1077                         (p_line[i - 1])[p_len[i - 1]] = 0;
1078                 }
1079                 if (hunk_type == 'c') {
1080                         ret = pgets(buf, buf_len, pfp);
1081                         p_input_line++;
1082                         if (ret == NULL)
1083                                 fatal("unexpected end of file in patch at line %ld\n",
1084                                     p_input_line);
1085                         if (*buf != '-')
1086                                 fatal("--- expected at line %ld of patch\n",
1087                                     p_input_line);
1088                 }
1089                 snprintf(buf, buf_len, "--- %ld,%ld\n", min, max);
1090                 p_line[i] = savestr(buf);
1091                 if (out_of_mem) {
1092                         p_end = i - 1;
1093                         return false;
1094                 }
1095                 p_char[i] = '=';
1096                 for (i++; i <= p_end; i++) {
1097                         ret = pgets(buf, buf_len, pfp);
1098                         p_input_line++;
1099                         if (ret == NULL)
1100                                 fatal("unexpected end of file in patch at line %ld\n",
1101                                     p_input_line);
1102                         if (*buf != '>')
1103                                 fatal("> expected at line %ld of patch\n",
1104                                     p_input_line);
1105                         p_line[i] = savestr(buf + 2);
1106                         if (out_of_mem) {
1107                                 p_end = i - 1;
1108                                 return false;
1109                         }
1110                         p_len[i] = strlen(p_line[i]);
1111                         p_char[i] = '+';
1112                 }
1113
1114                 if (remove_special_line()) {
1115                         p_len[i - 1] -= 1;
1116                         (p_line[i - 1])[p_len[i - 1]] = 0;
1117                 }
1118         }
1119         if (reverse)            /* backwards patch? */
1120                 if (!pch_swap())
1121                         say("Not enough memory to swap next hunk!\n");
1122 #ifdef DEBUGGING
1123         if (debug & 2) {
1124                 int     i;
1125                 char    special;
1126
1127                 for (i = 0; i <= p_end; i++) {
1128                         if (i == p_ptrn_lines)
1129                                 special = '^';
1130                         else
1131                                 special = ' ';
1132                         fprintf(stderr, "%3d %c %c %s", i, p_char[i],
1133                             special, p_line[i]);
1134                         fflush(stderr);
1135                 }
1136         }
1137 #endif
1138         if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */
1139                 p_char[p_end + 1] = '^';        /* add a stopper for apply_hunk */
1140         return true;
1141 }
1142
1143 /*
1144  * Input a line from the patch file, worrying about indentation.
1145  */
1146 static char *
1147 pgets(char *bf, int sz, FILE *fp)
1148 {
1149         char    *s, *ret = fgets(bf, sz, fp);
1150         int     indent = 0;
1151
1152         if (p_indent && ret != NULL) {
1153                 for (s = buf;
1154                     indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X');
1155                     s++) {
1156                         if (*s == '\t')
1157                                 indent += 8 - (indent % 7);
1158                         else
1159                                 indent++;
1160                 }
1161                 if (buf != s && strlcpy(buf, s, buf_len) >= buf_len)
1162                         fatal("buffer too small in pgets()\n");
1163         }
1164         return ret;
1165 }
1166
1167 /*
1168  * Reverse the old and new portions of the current hunk.
1169  */
1170 bool
1171 pch_swap(void)
1172 {
1173         char    **tp_line;      /* the text of the hunk */
1174         short   *tp_len;        /* length of each line */
1175         char    *tp_char;       /* +, -, and ! */
1176         LINENUM i;
1177         LINENUM n;
1178         bool    blankline = false;
1179         char    *s;
1180
1181         i = p_first;
1182         p_first = p_newfirst;
1183         p_newfirst = i;
1184
1185         /* make a scratch copy */
1186
1187         tp_line = p_line;
1188         tp_len = p_len;
1189         tp_char = p_char;
1190         p_line = NULL;  /* force set_hunkmax to allocate again */
1191         p_len = NULL;
1192         p_char = NULL;
1193         set_hunkmax();
1194         if (p_line == NULL || p_len == NULL || p_char == NULL) {
1195
1196                 free(p_line);
1197                 p_line = tp_line;
1198                 free(p_len);
1199                 p_len = tp_len;
1200                 free(p_char);
1201                 p_char = tp_char;
1202                 return false;   /* not enough memory to swap hunk! */
1203         }
1204         /* now turn the new into the old */
1205
1206         i = p_ptrn_lines + 1;
1207         if (tp_char[i] == '\n') {       /* account for possible blank line */
1208                 blankline = true;
1209                 i++;
1210         }
1211         if (p_efake >= 0) {     /* fix non-freeable ptr range */
1212                 if (p_efake <= i)
1213                         n = p_end - i + 1;
1214                 else
1215                         n = -i;
1216                 p_efake += n;
1217                 p_bfake += n;
1218         }
1219         for (n = 0; i <= p_end; i++, n++) {
1220                 p_line[n] = tp_line[i];
1221                 p_char[n] = tp_char[i];
1222                 if (p_char[n] == '+')
1223                         p_char[n] = '-';
1224                 p_len[n] = tp_len[i];
1225         }
1226         if (blankline) {
1227                 i = p_ptrn_lines + 1;
1228                 p_line[n] = tp_line[i];
1229                 p_char[n] = tp_char[i];
1230                 p_len[n] = tp_len[i];
1231                 n++;
1232         }
1233         if (p_char[0] != '=')
1234                 fatal("Malformed patch at line %ld: expected '=' found '%c'\n",
1235                     p_input_line, p_char[0]);
1236         p_char[0] = '*';
1237         for (s = p_line[0]; *s; s++)
1238                 if (*s == '-')
1239                         *s = '*';
1240
1241         /* now turn the old into the new */
1242
1243         if (p_char[0] != '*')
1244                 fatal("Malformed patch at line %ld: expected '*' found '%c'\n",
1245                     p_input_line, p_char[0]);
1246         tp_char[0] = '=';
1247         for (s = tp_line[0]; *s; s++)
1248                 if (*s == '*')
1249                         *s = '-';
1250         for (i = 0; n <= p_end; i++, n++) {
1251                 p_line[n] = tp_line[i];
1252                 p_char[n] = tp_char[i];
1253                 if (p_char[n] == '-')
1254                         p_char[n] = '+';
1255                 p_len[n] = tp_len[i];
1256         }
1257
1258         if (i != p_ptrn_lines + 1)
1259                 fatal("Malformed patch at line %ld: expected %ld lines, "
1260                     "got %ld\n",
1261                     p_input_line, p_ptrn_lines + 1, i);
1262
1263         i = p_ptrn_lines;
1264         p_ptrn_lines = p_repl_lines;
1265         p_repl_lines = i;
1266
1267         free(tp_line);
1268         free(tp_len);
1269         free(tp_char);
1270
1271         return true;
1272 }
1273
1274 /*
1275  * Return the specified line position in the old file of the old context.
1276  */
1277 LINENUM
1278 pch_first(void)
1279 {
1280         return p_first;
1281 }
1282
1283 /*
1284  * Return the number of lines of old context.
1285  */
1286 LINENUM
1287 pch_ptrn_lines(void)
1288 {
1289         return p_ptrn_lines;
1290 }
1291
1292 /*
1293  * Return the probable line position in the new file of the first line.
1294  */
1295 LINENUM
1296 pch_newfirst(void)
1297 {
1298         return p_newfirst;
1299 }
1300
1301 /*
1302  * Return the number of lines in the replacement text including context.
1303  */
1304 LINENUM
1305 pch_repl_lines(void)
1306 {
1307         return p_repl_lines;
1308 }
1309
1310 /*
1311  * Return the number of lines in the whole hunk.
1312  */
1313 LINENUM
1314 pch_end(void)
1315 {
1316         return p_end;
1317 }
1318
1319 /*
1320  * Return the number of context lines before the first changed line.
1321  */
1322 LINENUM
1323 pch_context(void)
1324 {
1325         return p_context;
1326 }
1327
1328 /*
1329  * Return the length of a particular patch line.
1330  */
1331 short
1332 pch_line_len(LINENUM line)
1333 {
1334         return p_len[line];
1335 }
1336
1337 /*
1338  * Return the control character (+, -, *, !, etc) for a patch line.
1339  */
1340 char
1341 pch_char(LINENUM line)
1342 {
1343         return p_char[line];
1344 }
1345
1346 /*
1347  * Return a pointer to a particular patch line.
1348  */
1349 char *
1350 pfetch(LINENUM line)
1351 {
1352         return p_line[line];
1353 }
1354
1355 /*
1356  * Return where in the patch file this hunk began, for error messages.
1357  */
1358 LINENUM
1359 pch_hunk_beg(void)
1360 {
1361         return p_hunk_beg;
1362 }
1363
1364 /*
1365  * Apply an ed script by feeding ed itself.
1366  */
1367 void
1368 do_ed_script(void)
1369 {
1370         char    *t;
1371         long    beginning_of_this_line;
1372         FILE    *pipefp;
1373
1374         pipefp = NULL;
1375         if (!skip_rest_of_patch) {
1376                 if (copy_file(filearg[0], TMPOUTNAME) < 0) {
1377                         unlink(TMPOUTNAME);
1378                         fatal("can't create temp file %s", TMPOUTNAME);
1379                 }
1380                 snprintf(buf, buf_len, "%s%s%s", _PATH_ED,
1381                     verbose ? " " : " -s ", TMPOUTNAME);
1382                 pipefp = popen(buf, "w");
1383         }
1384         for (;;) {
1385                 beginning_of_this_line = ftell(pfp);
1386                 if (pgets(buf, buf_len, pfp) == NULL) {
1387                         next_intuit_at(beginning_of_this_line, p_input_line);
1388                         break;
1389                 }
1390                 p_input_line++;
1391                 for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
1392                         ;
1393                 /* POSIX defines allowed commands as {a,c,d,i,s} */
1394                 if (isdigit((unsigned char)*buf) && (*t == 'a' || *t == 'c' ||
1395                     *t == 'd' || *t == 'i' || *t == 's')) {
1396                         if (pipefp != NULL)
1397                                 fputs(buf, pipefp);
1398                         if (*t != 'd') {
1399                                 while (pgets(buf, buf_len, pfp) != NULL) {
1400                                         p_input_line++;
1401                                         if (pipefp != NULL)
1402                                                 fputs(buf, pipefp);
1403                                         if (strEQ(buf, ".\n"))
1404                                                 break;
1405                                 }
1406                         }
1407                 } else {
1408                         next_intuit_at(beginning_of_this_line, p_input_line);
1409                         break;
1410                 }
1411         }
1412         if (pipefp == NULL)
1413                 return;
1414         fprintf(pipefp, "w\n");
1415         fprintf(pipefp, "q\n");
1416         fflush(pipefp);
1417         pclose(pipefp);
1418         ignore_signals();
1419         if (!check_only) {
1420                 if (move_file(TMPOUTNAME, outname) < 0) {
1421                         toutkeep = true;
1422                         chmod(TMPOUTNAME, filemode);
1423                 } else
1424                         chmod(outname, filemode);
1425         }
1426         set_signals(1);
1427 }
1428
1429 /*
1430  * Choose the name of the file to be patched based on POSIX rules.
1431  * NOTE: the POSIX rules are amazingly stupid and we only follow them
1432  *       if the user specified --posix or set POSIXLY_CORRECT.
1433  */
1434 static char *
1435 posix_name(const struct file_name *names, bool assume_exists)
1436 {
1437         char *path = NULL;
1438         int i;
1439
1440         /*
1441          * POSIX states that the filename will be chosen from one
1442          * of the old, new and index names (in that order) if
1443          * the file exists relative to CWD after -p stripping.
1444          */
1445         for (i = 0; i < MAX_FILE; i++) {
1446                 if (names[i].path != NULL && names[i].exists) {
1447                         path = names[i].path;
1448                         break;
1449                 }
1450         }
1451         if (path == NULL && !assume_exists) {
1452                 /*
1453                  * No files found, look for something we can checkout from
1454                  * RCS/SCCS dirs.  Same order as above.
1455                  */
1456                 for (i = 0; i < MAX_FILE; i++) {
1457                         if (names[i].path != NULL &&
1458                             (path = checked_in(names[i].path)) != NULL)
1459                                 break;
1460                 }
1461                 /*
1462                  * Still no match?  Check to see if the diff could be creating
1463                  * a new file.
1464                  */
1465                 if (path == NULL && ok_to_create_file &&
1466                     names[NEW_FILE].path != NULL)
1467                         path = names[NEW_FILE].path;
1468         }
1469
1470         return path ? savestr(path) : NULL;
1471 }
1472
1473 /*
1474  * Choose the name of the file to be patched based the "best" one
1475  * available.
1476  */
1477 static char *
1478 best_name(const struct file_name *names, bool assume_exists)
1479 {
1480         size_t min_components, min_baselen, min_len, tmp;
1481         char *best = NULL;
1482         int i;
1483
1484         /*
1485          * The "best" name is the one with the fewest number of path
1486          * components, the shortest basename length, and the shortest
1487          * overall length (in that order).  We only use the Index: file
1488          * if neither of the old or new files could be intuited from
1489          * the diff header.
1490          */
1491         min_components = min_baselen = min_len = SIZE_MAX;
1492         for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1493                 if (names[i].path == NULL ||
1494                     (!names[i].exists && !assume_exists))
1495                         continue;
1496                 if ((tmp = num_components(names[i].path)) > min_components)
1497                         continue;
1498                 min_components = tmp;
1499                 if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1500                         continue;
1501                 min_baselen = tmp;
1502                 if ((tmp = strlen(names[i].path)) > min_len)
1503                         continue;
1504                 min_len = tmp;
1505                 best = names[i].path;
1506         }
1507         if (best == NULL) {
1508                 /*
1509                  * No files found, look for something we can checkout from
1510                  * RCS/SCCS dirs.  Logic is identical to that above...
1511                  */
1512                 min_components = min_baselen = min_len = SIZE_MAX;
1513                 for (i = INDEX_FILE; i >= OLD_FILE; i--) {
1514                         if (names[i].path == NULL ||
1515                             checked_in(names[i].path) == NULL)
1516                                 continue;
1517                         if ((tmp = num_components(names[i].path)) > min_components)
1518                                 continue;
1519                         min_components = tmp;
1520                         if ((tmp = strlen(basename(names[i].path))) > min_baselen)
1521                                 continue;
1522                         min_baselen = tmp;
1523                         if ((tmp = strlen(names[i].path)) > min_len)
1524                                 continue;
1525                         min_len = tmp;
1526                         best = names[i].path;
1527                 }
1528                 /*
1529                  * Still no match?  Check to see if the diff could be creating
1530                  * a new file.
1531                  */
1532                 if (best == NULL && ok_to_create_file &&
1533                     names[NEW_FILE].path != NULL)
1534                         best = names[NEW_FILE].path;
1535         }
1536
1537         return best ? savestr(best) : NULL;
1538 }
1539
1540 static size_t
1541 num_components(const char *path)
1542 {
1543         size_t n;
1544         const char *cp;
1545
1546         for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
1547                 while (*cp == '/')
1548                         cp++;           /* skip consecutive slashes */
1549         }
1550         return n;
1551 }