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