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