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