1246074Sgabor/*- 2246074Sgabor * Copyright 1986, Larry Wall 3246074Sgabor * 4246074Sgabor * Redistribution and use in source and binary forms, with or without 5246074Sgabor * modification, are permitted provided that the following condition is met: 6246074Sgabor * 1. Redistributions of source code must retain the above copyright notice, 7246074Sgabor * this condition and the following disclaimer. 8246074Sgabor * 9246074Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 10246074Sgabor * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 11246074Sgabor * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 12246074Sgabor * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 13246074Sgabor * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 14246074Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 15246074Sgabor * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 16246074Sgabor * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 17246074Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 18246074Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 19246074Sgabor * SUCH DAMAGE. 20246074Sgabor * 21246074Sgabor * patch - a program to apply diffs to original files 22246074Sgabor * 23246074Sgabor * -C option added in 1998, original code by Marc Espie, based on FreeBSD 24246074Sgabor * behaviour 25246074Sgabor * 26275582Spfg * $OpenBSD: pch.c,v 1.43 2014/11/18 17:03:35 tobias Exp $ 27246091Sdelphij * $FreeBSD: stable/11/usr.bin/patch/pch.c 355351 2019-12-03 18:55:09Z kevans $ 28246074Sgabor */ 29246074Sgabor 30246074Sgabor#include <sys/types.h> 31246074Sgabor#include <sys/stat.h> 32246074Sgabor 33246074Sgabor#include <ctype.h> 34246074Sgabor#include <libgen.h> 35246074Sgabor#include <limits.h> 36281800Spfg#include <stdint.h> 37246074Sgabor#include <stdio.h> 38246074Sgabor#include <stdlib.h> 39246074Sgabor#include <string.h> 40246074Sgabor#include <unistd.h> 41246074Sgabor 42246074Sgabor#include "common.h" 43246074Sgabor#include "util.h" 44246074Sgabor#include "pch.h" 45246074Sgabor#include "pathnames.h" 46246074Sgabor 47246074Sgabor/* Patch (diff listing) abstract type. */ 48246074Sgabor 49275553Spfgstatic off_t p_filesize; /* size of the patch file */ 50246074Sgaborstatic LINENUM p_first; /* 1st line number */ 51246074Sgaborstatic LINENUM p_newfirst; /* 1st line number of replacement */ 52246074Sgaborstatic LINENUM p_ptrn_lines; /* # lines in pattern */ 53246074Sgaborstatic LINENUM p_repl_lines; /* # lines in replacement text */ 54246074Sgaborstatic LINENUM p_end = -1; /* last line in hunk */ 55246074Sgaborstatic LINENUM p_max; /* max allowed value of p_end */ 56246074Sgaborstatic LINENUM p_context = 3; /* # of context lines */ 57246074Sgaborstatic LINENUM p_input_line = 0; /* current line # from patch file */ 58246074Sgaborstatic char **p_line = NULL;/* the text of the hunk */ 59267490Spfgstatic unsigned short *p_len = NULL; /* length of each line */ 60246074Sgaborstatic char *p_char = NULL; /* +, -, and ! */ 61246074Sgaborstatic int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ 62246074Sgaborstatic int p_indent; /* indent to patch */ 63275553Spfgstatic off_t p_base; /* where to intuit this time */ 64246074Sgaborstatic LINENUM p_bline; /* line # of p_base */ 65275553Spfgstatic off_t p_start; /* where intuit found a patch */ 66246074Sgaborstatic LINENUM p_sline; /* and the line number for it */ 67246074Sgaborstatic LINENUM p_hunk_beg; /* line number of current hunk */ 68246074Sgaborstatic LINENUM p_efake = -1; /* end of faked up lines--don't free */ 69246074Sgaborstatic LINENUM p_bfake = -1; /* beg of faked up lines */ 70246074Sgaborstatic FILE *pfp = NULL; /* patch file pointer */ 71246074Sgaborstatic char *bestguess = NULL; /* guess at correct filename */ 72246074Sgabor 73355351Skevanschar *source_file; 74355351Skevans 75246074Sgaborstatic void grow_hunkmax(void); 76246074Sgaborstatic int intuit_diff_type(void); 77275553Spfgstatic void next_intuit_at(off_t, LINENUM); 78275553Spfgstatic void skip_to(off_t, LINENUM); 79246074Sgaborstatic size_t pgets(bool _do_indent); 80246074Sgaborstatic char *best_name(const struct file_name *, bool); 81246074Sgaborstatic char *posix_name(const struct file_name *, bool); 82246074Sgaborstatic size_t num_components(const char *); 83275612Spfgstatic LINENUM strtolinenum(char *, char **); 84246074Sgabor 85246074Sgabor/* 86246074Sgabor * Prepare to look for the next patch in the patch file. 87246074Sgabor */ 88246074Sgaborvoid 89246074Sgaborre_patch(void) 90246074Sgabor{ 91246074Sgabor p_first = 0; 92246074Sgabor p_newfirst = 0; 93246074Sgabor p_ptrn_lines = 0; 94246074Sgabor p_repl_lines = 0; 95246074Sgabor p_end = (LINENUM) - 1; 96246074Sgabor p_max = 0; 97246074Sgabor p_indent = 0; 98246074Sgabor} 99246074Sgabor 100246074Sgabor/* 101246074Sgabor * Open the patch file at the beginning of time. 102246074Sgabor */ 103246074Sgaborvoid 104246074Sgaboropen_patch_file(const char *filename) 105246074Sgabor{ 106246074Sgabor struct stat filestat; 107252636Sobrien int nr, nw; 108246074Sgabor 109246074Sgabor if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) { 110246074Sgabor pfp = fopen(TMPPATNAME, "w"); 111246074Sgabor if (pfp == NULL) 112246074Sgabor pfatal("can't create %s", TMPPATNAME); 113252636Sobrien while ((nr = fread(buf, 1, buf_size, stdin)) > 0) { 114252636Sobrien nw = fwrite(buf, 1, nr, pfp); 115252636Sobrien if (nr != nw) 116252636Sobrien pfatal("write error to %s", TMPPATNAME); 117252636Sobrien } 118246074Sgabor if (ferror(pfp) || fclose(pfp)) 119246074Sgabor pfatal("can't write %s", TMPPATNAME); 120246074Sgabor filename = TMPPATNAME; 121246074Sgabor } 122246074Sgabor pfp = fopen(filename, "r"); 123246074Sgabor if (pfp == NULL) 124246074Sgabor pfatal("patch file %s not found", filename); 125275553Spfg if (fstat(fileno(pfp), &filestat)) 126275553Spfg pfatal("can't stat %s", filename); 127246074Sgabor p_filesize = filestat.st_size; 128275553Spfg next_intuit_at(0, 1L); /* start at the beginning */ 129246074Sgabor set_hunkmax(); 130246074Sgabor} 131246074Sgabor 132246074Sgabor/* 133246074Sgabor * Make sure our dynamically realloced tables are malloced to begin with. 134246074Sgabor */ 135246074Sgaborvoid 136246074Sgaborset_hunkmax(void) 137246074Sgabor{ 138246074Sgabor if (p_line == NULL) 139267464Spfg p_line = malloc(hunkmax * sizeof(char *)); 140246074Sgabor if (p_len == NULL) 141267490Spfg p_len = malloc(hunkmax * sizeof(unsigned short)); 142246074Sgabor if (p_char == NULL) 143267464Spfg p_char = malloc(hunkmax * sizeof(char)); 144246074Sgabor} 145246074Sgabor 146246074Sgabor/* 147246074Sgabor * Enlarge the arrays containing the current hunk of patch. 148246074Sgabor */ 149246074Sgaborstatic void 150246074Sgaborgrow_hunkmax(void) 151246074Sgabor{ 152267464Spfg int new_hunkmax = hunkmax * 2; 153246074Sgabor 154246074Sgabor if (p_line == NULL || p_len == NULL || p_char == NULL) 155246074Sgabor fatal("Internal memory allocation error\n"); 156246074Sgabor 157267464Spfg p_line = reallocf(p_line, new_hunkmax * sizeof(char *)); 158267490Spfg p_len = reallocf(p_len, new_hunkmax * sizeof(unsigned short)); 159267464Spfg p_char = reallocf(p_char, new_hunkmax * sizeof(char)); 160246074Sgabor 161246074Sgabor if (p_line != NULL && p_len != NULL && p_char != NULL) { 162246074Sgabor hunkmax = new_hunkmax; 163246074Sgabor return; 164246074Sgabor } 165246074Sgabor 166246074Sgabor if (!using_plan_a) 167246074Sgabor fatal("out of memory\n"); 168246074Sgabor out_of_mem = true; /* whatever is null will be allocated again */ 169246074Sgabor /* from within plan_a(), of all places */ 170246074Sgabor} 171246074Sgabor 172246074Sgabor/* True if the remainder of the patch file contains a diff of some sort. */ 173246074Sgabor 174246074Sgaborbool 175246074Sgaborthere_is_another_patch(void) 176246074Sgabor{ 177246074Sgabor bool exists = false; 178246074Sgabor 179275553Spfg if (p_base != 0 && p_base >= p_filesize) { 180246074Sgabor if (verbose) 181246074Sgabor say("done\n"); 182246074Sgabor return false; 183246074Sgabor } 184345878Skevans if (p_filesize == 0) 185345878Skevans return false; 186345878Skevans nonempty_patchf_seen = true; 187246074Sgabor if (verbose) 188246074Sgabor say("Hmm..."); 189246074Sgabor diff_type = intuit_diff_type(); 190246074Sgabor if (!diff_type) { 191275553Spfg if (p_base != 0) { 192246074Sgabor if (verbose) 193246074Sgabor say(" Ignoring the trailing garbage.\ndone\n"); 194246074Sgabor } else 195246074Sgabor say(" I can't seem to find a patch in there anywhere.\n"); 196246074Sgabor return false; 197246074Sgabor } 198246074Sgabor if (verbose) 199246074Sgabor say(" %sooks like %s to me...\n", 200275553Spfg (p_base == 0 ? "L" : "The next patch l"), 201246074Sgabor diff_type == UNI_DIFF ? "a unified diff" : 202246074Sgabor diff_type == CONTEXT_DIFF ? "a context diff" : 203246074Sgabor diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : 204246074Sgabor diff_type == NORMAL_DIFF ? "a normal diff" : 205246074Sgabor "an ed script"); 206246074Sgabor if (p_indent && verbose) 207246074Sgabor say("(Patch is indented %d space%s.)\n", p_indent, 208246074Sgabor p_indent == 1 ? "" : "s"); 209246074Sgabor skip_to(p_start, p_sline); 210246074Sgabor while (filearg[0] == NULL) { 211246074Sgabor if (force || batch) { 212246074Sgabor say("No file to patch. Skipping...\n"); 213276218Spfg filearg[0] = xstrdup(bestguess); 214246074Sgabor skip_rest_of_patch = true; 215246074Sgabor return true; 216246074Sgabor } 217246074Sgabor ask("File to patch: "); 218246074Sgabor if (*buf != '\n') { 219246074Sgabor free(bestguess); 220276218Spfg bestguess = xstrdup(buf); 221246074Sgabor filearg[0] = fetchname(buf, &exists, 0); 222246074Sgabor } 223355351Skevans /* 224355351Skevans * fetchname can now return buf = NULL, exists = true, to 225355351Skevans * indicate to the caller that /dev/null was specified. Retain 226355351Skevans * previous behavior for now until this can be better evaluted. 227355351Skevans */ 228355351Skevans if (filearg[0] == NULL || !exists) { 229320084Spfg int def_skip = *bestguess == '\0'; 230320084Spfg ask("No file found--skip this patch? [%c] ", 231320084Spfg def_skip ? 'y' : 'n'); 232320084Spfg if (*buf == 'n' || (!def_skip && *buf != 'y')) 233246074Sgabor continue; 234246074Sgabor if (verbose) 235246074Sgabor say("Skipping patch...\n"); 236246074Sgabor free(filearg[0]); 237246074Sgabor filearg[0] = fetchname(bestguess, &exists, 0); 238246074Sgabor skip_rest_of_patch = true; 239246074Sgabor return true; 240246074Sgabor } 241246074Sgabor } 242246074Sgabor return true; 243246074Sgabor} 244246074Sgabor 245246074Sgaborstatic void 246246074Sgaborp4_fetchname(struct file_name *name, char *str) 247246074Sgabor{ 248246074Sgabor char *t, *h; 249246074Sgabor 250246074Sgabor /* Skip leading whitespace. */ 251246074Sgabor while (isspace((unsigned char)*str)) 252246074Sgabor str++; 253246074Sgabor 254246074Sgabor /* Remove the file revision number. */ 255246074Sgabor for (t = str, h = NULL; *t != '\0' && !isspace((unsigned char)*t); t++) 256246074Sgabor if (*t == '#') 257246074Sgabor h = t; 258246074Sgabor if (h != NULL) 259246074Sgabor *h = '\0'; 260246074Sgabor 261246074Sgabor name->path = fetchname(str, &name->exists, strippath); 262246074Sgabor} 263246074Sgabor 264246074Sgabor/* Determine what kind of diff is in the remaining part of the patch file. */ 265246074Sgabor 266246074Sgaborstatic int 267246074Sgaborintuit_diff_type(void) 268246074Sgabor{ 269275553Spfg off_t this_line = 0, previous_line; 270275553Spfg off_t first_command_line = -1; 271246074Sgabor LINENUM fcl_line = -1; 272246074Sgabor bool last_line_was_command = false, this_is_a_command = false; 273246074Sgabor bool stars_last_line = false, stars_this_line = false; 274246074Sgabor char *s, *t; 275246074Sgabor int indent, retval; 276246074Sgabor struct file_name names[MAX_FILE]; 277331047Seadler int piece_of_git = 0; 278246074Sgabor 279246074Sgabor memset(names, 0, sizeof(names)); 280246074Sgabor ok_to_create_file = false; 281275553Spfg fseeko(pfp, p_base, SEEK_SET); 282246074Sgabor p_input_line = p_bline - 1; 283246074Sgabor for (;;) { 284246074Sgabor previous_line = this_line; 285246074Sgabor last_line_was_command = this_is_a_command; 286246074Sgabor stars_last_line = stars_this_line; 287275553Spfg this_line = ftello(pfp); 288246074Sgabor indent = 0; 289246074Sgabor p_input_line++; 290246074Sgabor if (pgets(false) == 0) { 291275553Spfg if (first_command_line >= 0) { 292246074Sgabor /* nothing but deletes!? */ 293246074Sgabor p_start = first_command_line; 294246074Sgabor p_sline = fcl_line; 295246074Sgabor retval = ED_DIFF; 296246074Sgabor goto scan_exit; 297246074Sgabor } else { 298246074Sgabor p_start = this_line; 299246074Sgabor p_sline = p_input_line; 300246074Sgabor retval = 0; 301246074Sgabor goto scan_exit; 302246074Sgabor } 303246074Sgabor } 304246074Sgabor for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { 305246074Sgabor if (*s == '\t') 306246074Sgabor indent += 8 - (indent % 8); 307246074Sgabor else 308246074Sgabor indent++; 309246074Sgabor } 310246074Sgabor for (t = s; isdigit((unsigned char)*t) || *t == ','; t++) 311246074Sgabor ; 312246074Sgabor this_is_a_command = (isdigit((unsigned char)*s) && 313246074Sgabor (*t == 'd' || *t == 'c' || *t == 'a')); 314275553Spfg if (first_command_line < 0 && this_is_a_command) { 315246074Sgabor first_command_line = this_line; 316246074Sgabor fcl_line = p_input_line; 317246074Sgabor p_indent = indent; /* assume this for now */ 318246074Sgabor } 319246074Sgabor if (!stars_last_line && strnEQ(s, "*** ", 4)) 320246074Sgabor names[OLD_FILE].path = fetchname(s + 4, 321246074Sgabor &names[OLD_FILE].exists, strippath); 322331047Seadler else if (strnEQ(s, "--- ", 4)) { 323331047Seadler size_t off = 4; 324331047Seadler if (piece_of_git && strippath == 957 && 325331047Seadler strnEQ(s, "--- a/", 6)) 326331047Seadler off = 6; 327331047Seadler names[NEW_FILE].path = fetchname(s + off, 328246074Sgabor &names[NEW_FILE].exists, strippath); 329331047Seadler } else if (strnEQ(s, "+++ ", 4)) { 330246074Sgabor /* pretend it is the old name */ 331331047Seadler size_t off = 4; 332331047Seadler if (piece_of_git && strippath == 957 && 333331047Seadler strnEQ(s, "+++ b/", 6)) 334331047Seadler off = 6; 335331047Seadler names[OLD_FILE].path = fetchname(s + off, 336246074Sgabor &names[OLD_FILE].exists, strippath); 337331047Seadler } else if (strnEQ(s, "Index:", 6)) 338246074Sgabor names[INDEX_FILE].path = fetchname(s + 6, 339246074Sgabor &names[INDEX_FILE].exists, strippath); 340246074Sgabor else if (strnEQ(s, "Prereq:", 7)) { 341246074Sgabor for (t = s + 7; isspace((unsigned char)*t); t++) 342246074Sgabor ; 343276218Spfg revision = xstrdup(t); 344275582Spfg for (t = revision; 345275582Spfg *t && !isspace((unsigned char)*t); t++) 346246074Sgabor ; 347246074Sgabor *t = '\0'; 348246074Sgabor if (*revision == '\0') { 349246074Sgabor free(revision); 350246074Sgabor revision = NULL; 351246074Sgabor } 352331047Seadler } else if (strnEQ(s, "diff --git a/", 13)) { 353331047Seadler /* Git-style diffs. */ 354331047Seadler piece_of_git = 1; 355246074Sgabor } else if (strnEQ(s, "==== ", 5)) { 356246074Sgabor /* Perforce-style diffs. */ 357246074Sgabor if ((t = strstr(s + 5, " - ")) != NULL) 358246074Sgabor p4_fetchname(&names[NEW_FILE], t + 3); 359246074Sgabor p4_fetchname(&names[OLD_FILE], s + 5); 360246074Sgabor } 361246074Sgabor if ((!diff_type || diff_type == ED_DIFF) && 362275553Spfg first_command_line >= 0 && 363246074Sgabor strEQ(s, ".\n")) { 364246074Sgabor p_indent = indent; 365246074Sgabor p_start = first_command_line; 366246074Sgabor p_sline = fcl_line; 367246074Sgabor retval = ED_DIFF; 368246074Sgabor goto scan_exit; 369246074Sgabor } 370246074Sgabor if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { 371246074Sgabor if (strnEQ(s + 4, "0,0", 3)) 372246074Sgabor ok_to_create_file = true; 373246074Sgabor p_indent = indent; 374246074Sgabor p_start = this_line; 375246074Sgabor p_sline = p_input_line; 376246074Sgabor retval = UNI_DIFF; 377246074Sgabor goto scan_exit; 378246074Sgabor } 379246074Sgabor stars_this_line = strnEQ(s, "********", 8); 380246074Sgabor if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && 381246074Sgabor strnEQ(s, "*** ", 4)) { 382275612Spfg if (strtolinenum(s + 4, &s) == 0) 383246074Sgabor ok_to_create_file = true; 384246074Sgabor /* 385246074Sgabor * If this is a new context diff the character just 386275553Spfg * at the end of the line is a '*'. 387246074Sgabor */ 388275553Spfg while (*s && *s != '\n') 389246074Sgabor s++; 390246074Sgabor p_indent = indent; 391246074Sgabor p_start = previous_line; 392246074Sgabor p_sline = p_input_line - 1; 393246074Sgabor retval = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); 394246074Sgabor goto scan_exit; 395246074Sgabor } 396246074Sgabor if ((!diff_type || diff_type == NORMAL_DIFF) && 397246074Sgabor last_line_was_command && 398246074Sgabor (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) { 399246074Sgabor p_start = previous_line; 400246074Sgabor p_sline = p_input_line - 1; 401246074Sgabor p_indent = indent; 402246074Sgabor retval = NORMAL_DIFF; 403246074Sgabor goto scan_exit; 404246074Sgabor } 405246074Sgabor } 406246074Sgaborscan_exit: 407246074Sgabor if (retval == UNI_DIFF) { 408246074Sgabor /* unswap old and new */ 409246074Sgabor struct file_name tmp = names[OLD_FILE]; 410246074Sgabor names[OLD_FILE] = names[NEW_FILE]; 411246074Sgabor names[NEW_FILE] = tmp; 412246074Sgabor } 413355351Skevans 414355351Skevans /* Invalidated */ 415355351Skevans free(source_file); 416355351Skevans source_file = NULL; 417355351Skevans 418355351Skevans if (retval != 0) { 419355351Skevans /* 420355351Skevans * If we've successfully determined a diff type, stored in 421355351Skevans * retval, path == NULL means _PATH_DEVNULL if exists is set. 422355351Skevans * Explicitly specify it here to make it easier to detect later 423355351Skevans * on that we're actually creating a file and not that we've 424355351Skevans * just goofed something up. 425355351Skevans */ 426355351Skevans if (names[OLD_FILE].path != NULL) 427355351Skevans source_file = xstrdup(names[OLD_FILE].path); 428355351Skevans else if (names[OLD_FILE].exists) 429355351Skevans source_file = xstrdup(_PATH_DEVNULL); 430355351Skevans } 431246074Sgabor if (filearg[0] == NULL) { 432246074Sgabor if (posix) 433246074Sgabor filearg[0] = posix_name(names, ok_to_create_file); 434246074Sgabor else { 435246074Sgabor /* Ignore the Index: name for context diffs, like GNU */ 436246074Sgabor if (names[OLD_FILE].path != NULL || 437246074Sgabor names[NEW_FILE].path != NULL) { 438246074Sgabor free(names[INDEX_FILE].path); 439246074Sgabor names[INDEX_FILE].path = NULL; 440246074Sgabor } 441246074Sgabor filearg[0] = best_name(names, ok_to_create_file); 442246074Sgabor } 443246074Sgabor } 444246074Sgabor 445246074Sgabor free(bestguess); 446246074Sgabor bestguess = NULL; 447246074Sgabor if (filearg[0] != NULL) 448276218Spfg bestguess = xstrdup(filearg[0]); 449246074Sgabor else if (!ok_to_create_file) { 450246074Sgabor /* 451246074Sgabor * We don't want to create a new file but we need a 452246074Sgabor * filename to set bestguess. Avoid setting filearg[0] 453246074Sgabor * so the file is not created automatically. 454246074Sgabor */ 455246074Sgabor if (posix) 456246074Sgabor bestguess = posix_name(names, true); 457246074Sgabor else 458246074Sgabor bestguess = best_name(names, true); 459246074Sgabor } 460246074Sgabor free(names[OLD_FILE].path); 461246074Sgabor free(names[NEW_FILE].path); 462246074Sgabor free(names[INDEX_FILE].path); 463246074Sgabor return retval; 464246074Sgabor} 465246074Sgabor 466246074Sgabor/* 467246074Sgabor * Remember where this patch ends so we know where to start up again. 468246074Sgabor */ 469246074Sgaborstatic void 470275553Spfgnext_intuit_at(off_t file_pos, LINENUM file_line) 471246074Sgabor{ 472246074Sgabor p_base = file_pos; 473246074Sgabor p_bline = file_line; 474246074Sgabor} 475246074Sgabor 476246074Sgabor/* 477275553Spfg * Basically a verbose fseeko() to the actual diff listing. 478246074Sgabor */ 479246074Sgaborstatic void 480275553Spfgskip_to(off_t file_pos, LINENUM file_line) 481246074Sgabor{ 482246074Sgabor size_t len; 483246074Sgabor 484246074Sgabor if (p_base > file_pos) 485275553Spfg fatal("Internal error: seek %lld>%lld\n", 486275553Spfg (long long)p_base, (long long)file_pos); 487246074Sgabor if (verbose && p_base < file_pos) { 488275553Spfg fseeko(pfp, p_base, SEEK_SET); 489246074Sgabor say("The text leading up to this was:\n--------------------------\n"); 490275553Spfg while (ftello(pfp) < file_pos) { 491246074Sgabor len = pgets(false); 492246074Sgabor if (len == 0) 493246074Sgabor fatal("Unexpected end of file\n"); 494246074Sgabor say("|%s", buf); 495246074Sgabor } 496246074Sgabor say("--------------------------\n"); 497246074Sgabor } else 498275553Spfg fseeko(pfp, file_pos, SEEK_SET); 499246074Sgabor p_input_line = file_line - 1; 500246074Sgabor} 501246074Sgabor 502246074Sgabor/* Make this a function for better debugging. */ 503246074Sgaborstatic void 504246074Sgabormalformed(void) 505246074Sgabor{ 506246074Sgabor fatal("malformed patch at line %ld: %s", p_input_line, buf); 507246074Sgabor /* about as informative as "Syntax error" in C */ 508246074Sgabor} 509246074Sgabor 510246074Sgabor/* 511246074Sgabor * True if the line has been discarded (i.e. it is a line saying 512246074Sgabor * "\ No newline at end of file".) 513246074Sgabor */ 514246074Sgaborstatic bool 515246074Sgaborremove_special_line(void) 516246074Sgabor{ 517246074Sgabor int c; 518246074Sgabor 519246074Sgabor c = fgetc(pfp); 520246074Sgabor if (c == '\\') { 521246074Sgabor do { 522246074Sgabor c = fgetc(pfp); 523246074Sgabor } while (c != EOF && c != '\n'); 524246074Sgabor 525246074Sgabor return true; 526246074Sgabor } 527246074Sgabor if (c != EOF) 528275553Spfg fseeko(pfp, -1, SEEK_CUR); 529246074Sgabor 530246074Sgabor return false; 531246074Sgabor} 532246074Sgabor 533246074Sgabor/* 534246074Sgabor * True if there is more of the current diff listing to process. 535246074Sgabor */ 536246074Sgaborbool 537246074Sgaboranother_hunk(void) 538246074Sgabor{ 539275553Spfg off_t line_beginning; /* file pos of the current line */ 540246074Sgabor LINENUM repl_beginning; /* index of --- line */ 541246074Sgabor LINENUM fillcnt; /* #lines of missing ptrn or repl */ 542246074Sgabor LINENUM fillsrc; /* index of first line to copy */ 543246074Sgabor LINENUM filldst; /* index of first missing line */ 544289677Seadler bool ptrn_spaces_eaten; /* ptrn was slightly malformed */ 545246074Sgabor bool repl_could_be_missing; /* no + or ! lines in this hunk */ 546246074Sgabor bool repl_missing; /* we are now backtracking */ 547275553Spfg off_t repl_backtrack_position; /* file pos of first repl line */ 548246074Sgabor LINENUM repl_patch_line; /* input line number for same */ 549246074Sgabor LINENUM ptrn_copiable; /* # of copiable lines in ptrn */ 550246074Sgabor char *s; 551246074Sgabor size_t len; 552246074Sgabor int context = 0; 553246074Sgabor 554246074Sgabor while (p_end >= 0) { 555246074Sgabor if (p_end == p_efake) 556246074Sgabor p_end = p_bfake; /* don't free twice */ 557246074Sgabor else 558246074Sgabor free(p_line[p_end]); 559246074Sgabor p_end--; 560246074Sgabor } 561246074Sgabor p_efake = -1; 562246074Sgabor 563246074Sgabor p_max = hunkmax; /* gets reduced when --- found */ 564246074Sgabor if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { 565275553Spfg line_beginning = ftello(pfp); 566246074Sgabor repl_beginning = 0; 567246074Sgabor fillcnt = 0; 568246074Sgabor fillsrc = 0; 569246074Sgabor filldst = 0; 570246074Sgabor ptrn_spaces_eaten = false; 571246074Sgabor repl_could_be_missing = true; 572246074Sgabor repl_missing = false; 573246074Sgabor repl_backtrack_position = 0; 574246074Sgabor repl_patch_line = 0; 575246074Sgabor ptrn_copiable = 0; 576246074Sgabor 577246074Sgabor len = pgets(true); 578246074Sgabor p_input_line++; 579246074Sgabor if (len == 0 || strnNE(buf, "********", 8)) { 580246074Sgabor next_intuit_at(line_beginning, p_input_line); 581246074Sgabor return false; 582246074Sgabor } 583246074Sgabor p_context = 100; 584246074Sgabor p_hunk_beg = p_input_line + 1; 585246074Sgabor while (p_end < p_max) { 586275553Spfg line_beginning = ftello(pfp); 587246074Sgabor len = pgets(true); 588246074Sgabor p_input_line++; 589246074Sgabor if (len == 0) { 590246074Sgabor if (p_max - p_end < 4) { 591246074Sgabor /* assume blank lines got chopped */ 592246074Sgabor strlcpy(buf, " \n", buf_size); 593246074Sgabor } else { 594246074Sgabor if (repl_beginning && repl_could_be_missing) { 595246074Sgabor repl_missing = true; 596246074Sgabor goto hunk_done; 597246074Sgabor } 598246074Sgabor fatal("unexpected end of file in patch\n"); 599246074Sgabor } 600246074Sgabor } 601246074Sgabor p_end++; 602246074Sgabor if (p_end >= hunkmax) 603246074Sgabor fatal("Internal error: hunk larger than hunk " 604246074Sgabor "buffer size"); 605246074Sgabor p_char[p_end] = *buf; 606246074Sgabor p_line[p_end] = NULL; 607246074Sgabor switch (*buf) { 608246074Sgabor case '*': 609246074Sgabor if (strnEQ(buf, "********", 8)) { 610246074Sgabor if (repl_beginning && repl_could_be_missing) { 611246074Sgabor repl_missing = true; 612246074Sgabor goto hunk_done; 613246074Sgabor } else 614246074Sgabor fatal("unexpected end of hunk " 615246074Sgabor "at line %ld\n", 616246074Sgabor p_input_line); 617246074Sgabor } 618246074Sgabor if (p_end != 0) { 619246074Sgabor if (repl_beginning && repl_could_be_missing) { 620246074Sgabor repl_missing = true; 621246074Sgabor goto hunk_done; 622246074Sgabor } 623246074Sgabor fatal("unexpected *** at line %ld: %s", 624246074Sgabor p_input_line, buf); 625246074Sgabor } 626246074Sgabor context = 0; 627246074Sgabor p_line[p_end] = savestr(buf); 628246074Sgabor if (out_of_mem) { 629246074Sgabor p_end--; 630246074Sgabor return false; 631246074Sgabor } 632275582Spfg for (s = buf; 633275582Spfg *s && !isdigit((unsigned char)*s); s++) 634246074Sgabor ; 635246074Sgabor if (!*s) 636246074Sgabor malformed(); 637246074Sgabor if (strnEQ(s, "0,0", 3)) 638246074Sgabor memmove(s, s + 2, strlen(s + 2) + 1); 639275612Spfg p_first = strtolinenum(s, &s); 640246074Sgabor if (*s == ',') { 641275582Spfg for (; 642275582Spfg *s && !isdigit((unsigned char)*s); s++) 643246074Sgabor ; 644246074Sgabor if (!*s) 645246074Sgabor malformed(); 646275612Spfg p_ptrn_lines = strtolinenum(s, &s) - p_first + 1; 647275612Spfg if (p_ptrn_lines < 0) 648275612Spfg malformed(); 649246074Sgabor } else if (p_first) 650246074Sgabor p_ptrn_lines = 1; 651246074Sgabor else { 652246074Sgabor p_ptrn_lines = 0; 653246074Sgabor p_first = 1; 654246074Sgabor } 655275612Spfg if (p_first >= LINENUM_MAX - p_ptrn_lines || 656275612Spfg p_ptrn_lines >= LINENUM_MAX - 6) 657275612Spfg malformed(); 658246074Sgabor 659246074Sgabor /* we need this much at least */ 660246074Sgabor p_max = p_ptrn_lines + 6; 661246074Sgabor while (p_max >= hunkmax) 662246074Sgabor grow_hunkmax(); 663246074Sgabor p_max = hunkmax; 664246074Sgabor break; 665246074Sgabor case '-': 666246074Sgabor if (buf[1] == '-') { 667246074Sgabor if (repl_beginning || 668246074Sgabor (p_end != p_ptrn_lines + 1 + 669246074Sgabor (p_char[p_end - 1] == '\n'))) { 670246074Sgabor if (p_end == 1) { 671246074Sgabor /* 672246074Sgabor * `old' lines were omitted; 673246074Sgabor * set up to fill them in 674246074Sgabor * from 'new' context lines. 675246074Sgabor */ 676246074Sgabor p_end = p_ptrn_lines + 1; 677246074Sgabor fillsrc = p_end + 1; 678246074Sgabor filldst = 1; 679246074Sgabor fillcnt = p_ptrn_lines; 680246074Sgabor } else { 681246074Sgabor if (repl_beginning) { 682246074Sgabor if (repl_could_be_missing) { 683246074Sgabor repl_missing = true; 684246074Sgabor goto hunk_done; 685246074Sgabor } 686246074Sgabor fatal("duplicate \"---\" at line %ld--check line numbers at line %ld\n", 687246074Sgabor p_input_line, p_hunk_beg + repl_beginning); 688246074Sgabor } else { 689246074Sgabor fatal("%s \"---\" at line %ld--check line numbers at line %ld\n", 690246074Sgabor (p_end <= p_ptrn_lines 691246074Sgabor ? "Premature" 692246074Sgabor : "Overdue"), 693246074Sgabor p_input_line, p_hunk_beg); 694246074Sgabor } 695246074Sgabor } 696246074Sgabor } 697246074Sgabor repl_beginning = p_end; 698275553Spfg repl_backtrack_position = ftello(pfp); 699246074Sgabor repl_patch_line = p_input_line; 700246074Sgabor p_line[p_end] = savestr(buf); 701246074Sgabor if (out_of_mem) { 702246074Sgabor p_end--; 703246074Sgabor return false; 704246074Sgabor } 705246074Sgabor p_char[p_end] = '='; 706246074Sgabor for (s = buf; *s && !isdigit((unsigned char)*s); s++) 707246074Sgabor ; 708246074Sgabor if (!*s) 709246074Sgabor malformed(); 710275612Spfg p_newfirst = strtolinenum(s, &s); 711246074Sgabor if (*s == ',') { 712246074Sgabor for (; *s && !isdigit((unsigned char)*s); s++) 713246074Sgabor ; 714246074Sgabor if (!*s) 715246074Sgabor malformed(); 716275612Spfg p_repl_lines = strtolinenum(s, &s) - 717246074Sgabor p_newfirst + 1; 718275612Spfg if (p_repl_lines < 0) 719275612Spfg malformed(); 720246074Sgabor } else if (p_newfirst) 721246074Sgabor p_repl_lines = 1; 722246074Sgabor else { 723246074Sgabor p_repl_lines = 0; 724246074Sgabor p_newfirst = 1; 725246074Sgabor } 726275612Spfg if (p_newfirst >= LINENUM_MAX - p_repl_lines || 727275612Spfg p_repl_lines >= LINENUM_MAX - p_end) 728275612Spfg malformed(); 729246074Sgabor p_max = p_repl_lines + p_end; 730246074Sgabor if (p_max > MAXHUNKSIZE) 731246074Sgabor fatal("hunk too large (%ld lines) at line %ld: %s", 732246074Sgabor p_max, p_input_line, buf); 733246074Sgabor while (p_max >= hunkmax) 734246074Sgabor grow_hunkmax(); 735246074Sgabor if (p_repl_lines != ptrn_copiable && 736246074Sgabor (p_context != 0 || p_repl_lines != 1)) 737246074Sgabor repl_could_be_missing = false; 738246074Sgabor break; 739246074Sgabor } 740246074Sgabor goto change_line; 741246074Sgabor case '+': 742246074Sgabor case '!': 743246074Sgabor repl_could_be_missing = false; 744246074Sgabor change_line: 745246074Sgabor if (buf[1] == '\n' && canonicalize) 746246074Sgabor strlcpy(buf + 1, " \n", buf_size - 1); 747275582Spfg if (!isspace((unsigned char)buf[1]) && 748275582Spfg buf[1] != '>' && buf[1] != '<' && 749246074Sgabor repl_beginning && repl_could_be_missing) { 750246074Sgabor repl_missing = true; 751246074Sgabor goto hunk_done; 752246074Sgabor } 753246074Sgabor if (context >= 0) { 754246074Sgabor if (context < p_context) 755246074Sgabor p_context = context; 756246074Sgabor context = -1000; 757246074Sgabor } 758246074Sgabor p_line[p_end] = savestr(buf + 2); 759246074Sgabor if (out_of_mem) { 760246074Sgabor p_end--; 761246074Sgabor return false; 762246074Sgabor } 763246074Sgabor if (p_end == p_ptrn_lines) { 764246074Sgabor if (remove_special_line()) { 765246074Sgabor int l; 766246074Sgabor 767246074Sgabor l = strlen(p_line[p_end]) - 1; 768246074Sgabor (p_line[p_end])[l] = 0; 769246074Sgabor } 770246074Sgabor } 771246074Sgabor break; 772246074Sgabor case '\t': 773246074Sgabor case '\n': /* assume the 2 spaces got eaten */ 774246074Sgabor if (repl_beginning && repl_could_be_missing && 775246074Sgabor (!ptrn_spaces_eaten || 776246074Sgabor diff_type == NEW_CONTEXT_DIFF)) { 777246074Sgabor repl_missing = true; 778246074Sgabor goto hunk_done; 779246074Sgabor } 780246074Sgabor p_line[p_end] = savestr(buf); 781246074Sgabor if (out_of_mem) { 782246074Sgabor p_end--; 783246074Sgabor return false; 784246074Sgabor } 785246074Sgabor if (p_end != p_ptrn_lines + 1) { 786246074Sgabor ptrn_spaces_eaten |= (repl_beginning != 0); 787246074Sgabor context++; 788246074Sgabor if (!repl_beginning) 789246074Sgabor ptrn_copiable++; 790246074Sgabor p_char[p_end] = ' '; 791246074Sgabor } 792246074Sgabor break; 793246074Sgabor case ' ': 794246074Sgabor if (!isspace((unsigned char)buf[1]) && 795246074Sgabor repl_beginning && repl_could_be_missing) { 796246074Sgabor repl_missing = true; 797246074Sgabor goto hunk_done; 798246074Sgabor } 799246074Sgabor context++; 800246074Sgabor if (!repl_beginning) 801246074Sgabor ptrn_copiable++; 802246074Sgabor p_line[p_end] = savestr(buf + 2); 803246074Sgabor if (out_of_mem) { 804246074Sgabor p_end--; 805246074Sgabor return false; 806246074Sgabor } 807246074Sgabor break; 808246074Sgabor default: 809246074Sgabor if (repl_beginning && repl_could_be_missing) { 810246074Sgabor repl_missing = true; 811246074Sgabor goto hunk_done; 812246074Sgabor } 813246074Sgabor malformed(); 814246074Sgabor } 815246074Sgabor /* set up p_len for strncmp() so we don't have to */ 816246074Sgabor /* assume null termination */ 817246074Sgabor if (p_line[p_end]) 818246074Sgabor p_len[p_end] = strlen(p_line[p_end]); 819246074Sgabor else 820246074Sgabor p_len[p_end] = 0; 821246074Sgabor } 822246074Sgabor 823246074Sgaborhunk_done: 824246074Sgabor if (p_end >= 0 && !repl_beginning) 825246074Sgabor fatal("no --- found in patch at line %ld\n", pch_hunk_beg()); 826246074Sgabor 827246074Sgabor if (repl_missing) { 828246074Sgabor 829246074Sgabor /* reset state back to just after --- */ 830246074Sgabor p_input_line = repl_patch_line; 831246074Sgabor for (p_end--; p_end > repl_beginning; p_end--) 832246074Sgabor free(p_line[p_end]); 833275553Spfg fseeko(pfp, repl_backtrack_position, SEEK_SET); 834246074Sgabor 835246074Sgabor /* redundant 'new' context lines were omitted - set */ 836246074Sgabor /* up to fill them in from the old file context */ 837246074Sgabor if (!p_context && p_repl_lines == 1) { 838246074Sgabor p_repl_lines = 0; 839246074Sgabor p_max--; 840246074Sgabor } 841246074Sgabor fillsrc = 1; 842246074Sgabor filldst = repl_beginning + 1; 843246074Sgabor fillcnt = p_repl_lines; 844246074Sgabor p_end = p_max; 845246074Sgabor } else if (!p_context && fillcnt == 1) { 846246074Sgabor /* the first hunk was a null hunk with no context */ 847246074Sgabor /* and we were expecting one line -- fix it up. */ 848246074Sgabor while (filldst < p_end) { 849246074Sgabor p_line[filldst] = p_line[filldst + 1]; 850246074Sgabor p_char[filldst] = p_char[filldst + 1]; 851246074Sgabor p_len[filldst] = p_len[filldst + 1]; 852246074Sgabor filldst++; 853246074Sgabor } 854246074Sgabor#if 0 855246074Sgabor repl_beginning--; /* this doesn't need to be fixed */ 856246074Sgabor#endif 857246074Sgabor p_end--; 858246074Sgabor p_first++; /* do append rather than insert */ 859246074Sgabor fillcnt = 0; 860246074Sgabor p_ptrn_lines = 0; 861246074Sgabor } 862246074Sgabor if (diff_type == CONTEXT_DIFF && 863246074Sgabor (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) { 864246074Sgabor if (verbose) 865246074Sgabor say("%s\n%s\n%s\n", 866246074Sgabor "(Fascinating--this is really a new-style context diff but without", 867246074Sgabor "the telltale extra asterisks on the *** line that usually indicate", 868246074Sgabor "the new style...)"); 869246074Sgabor diff_type = NEW_CONTEXT_DIFF; 870246074Sgabor } 871246074Sgabor /* if there were omitted context lines, fill them in now */ 872246074Sgabor if (fillcnt) { 873246074Sgabor p_bfake = filldst; /* remember where not to free() */ 874246074Sgabor p_efake = filldst + fillcnt - 1; 875246074Sgabor while (fillcnt-- > 0) { 876246074Sgabor while (fillsrc <= p_end && p_char[fillsrc] != ' ') 877246074Sgabor fillsrc++; 878246074Sgabor if (fillsrc > p_end) 879246074Sgabor fatal("replacement text or line numbers mangled in hunk at line %ld\n", 880246074Sgabor p_hunk_beg); 881246074Sgabor p_line[filldst] = p_line[fillsrc]; 882246074Sgabor p_char[filldst] = p_char[fillsrc]; 883246074Sgabor p_len[filldst] = p_len[fillsrc]; 884246074Sgabor fillsrc++; 885246074Sgabor filldst++; 886246074Sgabor } 887246074Sgabor while (fillsrc <= p_end && fillsrc != repl_beginning && 888246074Sgabor p_char[fillsrc] != ' ') 889246074Sgabor fillsrc++; 890246074Sgabor#ifdef DEBUGGING 891246074Sgabor if (debug & 64) 892246074Sgabor printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", 893246074Sgabor fillsrc, filldst, repl_beginning, p_end + 1); 894246074Sgabor#endif 895246074Sgabor if (fillsrc != p_end + 1 && fillsrc != repl_beginning) 896246074Sgabor malformed(); 897246074Sgabor if (filldst != p_end + 1 && filldst != repl_beginning) 898246074Sgabor malformed(); 899246074Sgabor } 900246074Sgabor if (p_line[p_end] != NULL) { 901246074Sgabor if (remove_special_line()) { 902246074Sgabor p_len[p_end] -= 1; 903246074Sgabor (p_line[p_end])[p_len[p_end]] = 0; 904246074Sgabor } 905246074Sgabor } 906246074Sgabor } else if (diff_type == UNI_DIFF) { 907246074Sgabor LINENUM fillold; /* index of old lines */ 908246074Sgabor LINENUM fillnew; /* index of new lines */ 909246074Sgabor char ch; 910246074Sgabor 911275553Spfg line_beginning = ftello(pfp); /* file pos of the current line */ 912246074Sgabor len = pgets(true); 913246074Sgabor p_input_line++; 914246074Sgabor if (len == 0 || strnNE(buf, "@@ -", 4)) { 915246074Sgabor next_intuit_at(line_beginning, p_input_line); 916246074Sgabor return false; 917246074Sgabor } 918246074Sgabor s = buf + 4; 919246074Sgabor if (!*s) 920246074Sgabor malformed(); 921275612Spfg p_first = strtolinenum(s, &s); 922246074Sgabor if (*s == ',') { 923275612Spfg p_ptrn_lines = strtolinenum(s + 1, &s); 924246074Sgabor } else 925246074Sgabor p_ptrn_lines = 1; 926246074Sgabor if (*s == ' ') 927246074Sgabor s++; 928246074Sgabor if (*s != '+' || !*++s) 929246074Sgabor malformed(); 930275612Spfg p_newfirst = strtolinenum(s, &s); 931246074Sgabor if (*s == ',') { 932275612Spfg p_repl_lines = strtolinenum(s + 1, &s); 933246074Sgabor } else 934246074Sgabor p_repl_lines = 1; 935246074Sgabor if (*s == ' ') 936246074Sgabor s++; 937246074Sgabor if (*s != '@') 938246074Sgabor malformed(); 939275612Spfg if (p_first >= LINENUM_MAX - p_ptrn_lines || 940275612Spfg p_newfirst > LINENUM_MAX - p_repl_lines || 941275612Spfg p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) 942275612Spfg malformed(); 943246074Sgabor if (!p_ptrn_lines) 944246074Sgabor p_first++; /* do append rather than insert */ 945246074Sgabor p_max = p_ptrn_lines + p_repl_lines + 1; 946246074Sgabor while (p_max >= hunkmax) 947246074Sgabor grow_hunkmax(); 948246074Sgabor fillold = 1; 949246074Sgabor fillnew = fillold + p_ptrn_lines; 950246074Sgabor p_end = fillnew + p_repl_lines; 951246074Sgabor snprintf(buf, buf_size, "*** %ld,%ld ****\n", p_first, 952246074Sgabor p_first + p_ptrn_lines - 1); 953246074Sgabor p_line[0] = savestr(buf); 954246074Sgabor if (out_of_mem) { 955246074Sgabor p_end = -1; 956246074Sgabor return false; 957246074Sgabor } 958246074Sgabor p_char[0] = '*'; 959246074Sgabor snprintf(buf, buf_size, "--- %ld,%ld ----\n", p_newfirst, 960246074Sgabor p_newfirst + p_repl_lines - 1); 961246074Sgabor p_line[fillnew] = savestr(buf); 962246074Sgabor if (out_of_mem) { 963246074Sgabor p_end = 0; 964246074Sgabor return false; 965246074Sgabor } 966246074Sgabor p_char[fillnew++] = '='; 967246074Sgabor p_context = 100; 968246074Sgabor context = 0; 969246074Sgabor p_hunk_beg = p_input_line + 1; 970246074Sgabor while (fillold <= p_ptrn_lines || fillnew <= p_end) { 971275553Spfg line_beginning = ftello(pfp); 972246074Sgabor len = pgets(true); 973246074Sgabor p_input_line++; 974246074Sgabor if (len == 0) { 975246074Sgabor if (p_max - fillnew < 3) { 976246074Sgabor /* assume blank lines got chopped */ 977246074Sgabor strlcpy(buf, " \n", buf_size); 978246074Sgabor } else { 979246074Sgabor fatal("unexpected end of file in patch\n"); 980246074Sgabor } 981246074Sgabor } 982246074Sgabor if (*buf == '\t' || *buf == '\n') { 983246074Sgabor ch = ' '; /* assume the space got eaten */ 984246074Sgabor s = savestr(buf); 985246074Sgabor } else { 986246074Sgabor ch = *buf; 987246074Sgabor s = savestr(buf + 1); 988246074Sgabor } 989246074Sgabor if (out_of_mem) { 990246074Sgabor while (--fillnew > p_ptrn_lines) 991246074Sgabor free(p_line[fillnew]); 992246074Sgabor p_end = fillold - 1; 993246074Sgabor return false; 994246074Sgabor } 995246074Sgabor switch (ch) { 996246074Sgabor case '-': 997246074Sgabor if (fillold > p_ptrn_lines) { 998246074Sgabor free(s); 999246074Sgabor p_end = fillnew - 1; 1000246074Sgabor malformed(); 1001246074Sgabor } 1002246074Sgabor p_char[fillold] = ch; 1003246074Sgabor p_line[fillold] = s; 1004246074Sgabor p_len[fillold++] = strlen(s); 1005246074Sgabor if (fillold > p_ptrn_lines) { 1006246074Sgabor if (remove_special_line()) { 1007246074Sgabor p_len[fillold - 1] -= 1; 1008246074Sgabor s[p_len[fillold - 1]] = 0; 1009246074Sgabor } 1010246074Sgabor } 1011246074Sgabor break; 1012246074Sgabor case '=': 1013246074Sgabor ch = ' '; 1014246074Sgabor /* FALL THROUGH */ 1015246074Sgabor case ' ': 1016246074Sgabor if (fillold > p_ptrn_lines) { 1017246074Sgabor free(s); 1018246074Sgabor while (--fillnew > p_ptrn_lines) 1019246074Sgabor free(p_line[fillnew]); 1020246074Sgabor p_end = fillold - 1; 1021246074Sgabor malformed(); 1022246074Sgabor } 1023246074Sgabor context++; 1024246074Sgabor p_char[fillold] = ch; 1025246074Sgabor p_line[fillold] = s; 1026246074Sgabor p_len[fillold++] = strlen(s); 1027246074Sgabor s = savestr(s); 1028246074Sgabor if (out_of_mem) { 1029246074Sgabor while (--fillnew > p_ptrn_lines) 1030246074Sgabor free(p_line[fillnew]); 1031246074Sgabor p_end = fillold - 1; 1032246074Sgabor return false; 1033246074Sgabor } 1034246074Sgabor if (fillold > p_ptrn_lines) { 1035246074Sgabor if (remove_special_line()) { 1036246074Sgabor p_len[fillold - 1] -= 1; 1037246074Sgabor s[p_len[fillold - 1]] = 0; 1038246074Sgabor } 1039246074Sgabor } 1040246074Sgabor /* FALL THROUGH */ 1041246074Sgabor case '+': 1042246074Sgabor if (fillnew > p_end) { 1043246074Sgabor free(s); 1044246074Sgabor while (--fillnew > p_ptrn_lines) 1045246074Sgabor free(p_line[fillnew]); 1046246074Sgabor p_end = fillold - 1; 1047246074Sgabor malformed(); 1048246074Sgabor } 1049246074Sgabor p_char[fillnew] = ch; 1050246074Sgabor p_line[fillnew] = s; 1051246074Sgabor p_len[fillnew++] = strlen(s); 1052246074Sgabor if (fillold > p_ptrn_lines) { 1053246074Sgabor if (remove_special_line()) { 1054246074Sgabor p_len[fillnew - 1] -= 1; 1055246074Sgabor s[p_len[fillnew - 1]] = 0; 1056246074Sgabor } 1057246074Sgabor } 1058246074Sgabor break; 1059246074Sgabor default: 1060246074Sgabor p_end = fillnew; 1061246074Sgabor malformed(); 1062246074Sgabor } 1063246074Sgabor if (ch != ' ' && context > 0) { 1064246074Sgabor if (context < p_context) 1065246074Sgabor p_context = context; 1066246074Sgabor context = -1000; 1067246074Sgabor } 1068246074Sgabor } /* while */ 1069246074Sgabor } else { /* normal diff--fake it up */ 1070246074Sgabor char hunk_type; 1071246074Sgabor int i; 1072246074Sgabor LINENUM min, max; 1073246074Sgabor 1074275553Spfg line_beginning = ftello(pfp); 1075246074Sgabor p_context = 0; 1076246074Sgabor len = pgets(true); 1077246074Sgabor p_input_line++; 1078246074Sgabor if (len == 0 || !isdigit((unsigned char)*buf)) { 1079246074Sgabor next_intuit_at(line_beginning, p_input_line); 1080246074Sgabor return false; 1081246074Sgabor } 1082275612Spfg p_first = strtolinenum(buf, &s); 1083246074Sgabor if (*s == ',') { 1084275612Spfg p_ptrn_lines = strtolinenum(s + 1, &s) - p_first + 1; 1085275612Spfg if (p_ptrn_lines < 0) 1086275612Spfg malformed(); 1087246074Sgabor } else 1088246074Sgabor p_ptrn_lines = (*s != 'a'); 1089246074Sgabor hunk_type = *s; 1090246074Sgabor if (hunk_type == 'a') 1091246074Sgabor p_first++; /* do append rather than insert */ 1092275612Spfg min = strtolinenum(s + 1, &s); 1093246074Sgabor if (*s == ',') 1094275612Spfg max = strtolinenum(s + 1, &s); 1095246074Sgabor else 1096246074Sgabor max = min; 1097275612Spfg if (min < 0 || min > max || max - min == LINENUM_MAX) 1098275612Spfg malformed(); 1099246074Sgabor if (hunk_type == 'd') 1100246074Sgabor min++; 1101275612Spfg p_newfirst = min; 1102275612Spfg p_repl_lines = max - min + 1; 1103275612Spfg if (p_newfirst > LINENUM_MAX - p_repl_lines || 1104275612Spfg p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1) 1105275612Spfg malformed(); 1106275612Spfg p_end = p_ptrn_lines + p_repl_lines + 1; 1107246074Sgabor if (p_end > MAXHUNKSIZE) 1108246074Sgabor fatal("hunk too large (%ld lines) at line %ld: %s", 1109246074Sgabor p_end, p_input_line, buf); 1110246074Sgabor while (p_end >= hunkmax) 1111246074Sgabor grow_hunkmax(); 1112246074Sgabor snprintf(buf, buf_size, "*** %ld,%ld\n", p_first, 1113246074Sgabor p_first + p_ptrn_lines - 1); 1114246074Sgabor p_line[0] = savestr(buf); 1115246074Sgabor if (out_of_mem) { 1116246074Sgabor p_end = -1; 1117246074Sgabor return false; 1118246074Sgabor } 1119246074Sgabor p_char[0] = '*'; 1120246074Sgabor for (i = 1; i <= p_ptrn_lines; i++) { 1121246074Sgabor len = pgets(true); 1122246074Sgabor p_input_line++; 1123246074Sgabor if (len == 0) 1124246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1125246074Sgabor p_input_line); 1126246074Sgabor if (*buf != '<') 1127246074Sgabor fatal("< expected at line %ld of patch\n", 1128246074Sgabor p_input_line); 1129246074Sgabor p_line[i] = savestr(buf + 2); 1130246074Sgabor if (out_of_mem) { 1131246074Sgabor p_end = i - 1; 1132246074Sgabor return false; 1133246074Sgabor } 1134246074Sgabor p_len[i] = strlen(p_line[i]); 1135246074Sgabor p_char[i] = '-'; 1136246074Sgabor } 1137246074Sgabor 1138246074Sgabor if (remove_special_line()) { 1139246074Sgabor p_len[i - 1] -= 1; 1140246074Sgabor (p_line[i - 1])[p_len[i - 1]] = 0; 1141246074Sgabor } 1142246074Sgabor if (hunk_type == 'c') { 1143246074Sgabor len = pgets(true); 1144246074Sgabor p_input_line++; 1145246074Sgabor if (len == 0) 1146246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1147246074Sgabor p_input_line); 1148246074Sgabor if (*buf != '-') 1149246074Sgabor fatal("--- expected at line %ld of patch\n", 1150246074Sgabor p_input_line); 1151246074Sgabor } 1152246074Sgabor snprintf(buf, buf_size, "--- %ld,%ld\n", min, max); 1153246074Sgabor p_line[i] = savestr(buf); 1154246074Sgabor if (out_of_mem) { 1155246074Sgabor p_end = i - 1; 1156246074Sgabor return false; 1157246074Sgabor } 1158246074Sgabor p_char[i] = '='; 1159246074Sgabor for (i++; i <= p_end; i++) { 1160246074Sgabor len = pgets(true); 1161246074Sgabor p_input_line++; 1162246074Sgabor if (len == 0) 1163246074Sgabor fatal("unexpected end of file in patch at line %ld\n", 1164246074Sgabor p_input_line); 1165246074Sgabor if (*buf != '>') 1166246074Sgabor fatal("> expected at line %ld of patch\n", 1167246074Sgabor p_input_line); 1168328147Skevans /* Don't overrun if we don't have enough line */ 1169328147Skevans if (len > 2) 1170328147Skevans p_line[i] = savestr(buf + 2); 1171328147Skevans else 1172328147Skevans p_line[i] = savestr(""); 1173328147Skevans 1174246074Sgabor if (out_of_mem) { 1175246074Sgabor p_end = i - 1; 1176246074Sgabor return false; 1177246074Sgabor } 1178246074Sgabor p_len[i] = strlen(p_line[i]); 1179246074Sgabor p_char[i] = '+'; 1180246074Sgabor } 1181246074Sgabor 1182246074Sgabor if (remove_special_line()) { 1183246074Sgabor p_len[i - 1] -= 1; 1184246074Sgabor (p_line[i - 1])[p_len[i - 1]] = 0; 1185246074Sgabor } 1186246074Sgabor } 1187246074Sgabor if (reverse) /* backwards patch? */ 1188246074Sgabor if (!pch_swap()) 1189246074Sgabor say("Not enough memory to swap next hunk!\n"); 1190246074Sgabor#ifdef DEBUGGING 1191246074Sgabor if (debug & 2) { 1192298530Spfg LINENUM i; 1193246074Sgabor char special; 1194246074Sgabor 1195246074Sgabor for (i = 0; i <= p_end; i++) { 1196246074Sgabor if (i == p_ptrn_lines) 1197246074Sgabor special = '^'; 1198246074Sgabor else 1199246074Sgabor special = ' '; 1200298530Spfg fprintf(stderr, "%3ld %c %c %s", i, p_char[i], 1201246074Sgabor special, p_line[i]); 1202246074Sgabor fflush(stderr); 1203246074Sgabor } 1204246074Sgabor } 1205246074Sgabor#endif 1206246074Sgabor if (p_end + 1 < hunkmax)/* paranoia reigns supreme... */ 1207246074Sgabor p_char[p_end + 1] = '^'; /* add a stopper for apply_hunk */ 1208246074Sgabor return true; 1209246074Sgabor} 1210246074Sgabor 1211246074Sgabor/* 1212246074Sgabor * Input a line from the patch file. 1213246074Sgabor * Worry about indentation if do_indent is true. 1214246074Sgabor * The line is read directly into the buf global variable which 1215246074Sgabor * is resized if necessary in order to hold the complete line. 1216246074Sgabor * Returns the number of characters read including the terminating 1217246074Sgabor * '\n', if any. 1218246074Sgabor */ 1219246074Sgaborsize_t 1220246074Sgaborpgets(bool do_indent) 1221246074Sgabor{ 1222246074Sgabor char *line; 1223246074Sgabor size_t len; 1224246074Sgabor int indent = 0, skipped = 0; 1225246074Sgabor 1226246074Sgabor line = fgetln(pfp, &len); 1227246074Sgabor if (line != NULL) { 1228246074Sgabor if (len + 1 > buf_size) { 1229246074Sgabor while (len + 1 > buf_size) 1230246074Sgabor buf_size *= 2; 1231246074Sgabor free(buf); 1232246074Sgabor buf = malloc(buf_size); 1233246074Sgabor if (buf == NULL) 1234246074Sgabor fatal("out of memory\n"); 1235246074Sgabor } 1236246074Sgabor if (do_indent == 1 && p_indent) { 1237246074Sgabor for (; 1238246074Sgabor indent < p_indent && (*line == ' ' || *line == '\t' || *line == 'X'); 1239246074Sgabor line++, skipped++) { 1240246074Sgabor if (*line == '\t') 1241246074Sgabor indent += 8 - (indent %7); 1242246074Sgabor else 1243246074Sgabor indent++; 1244246074Sgabor } 1245246074Sgabor } 1246252637Sobrien memcpy(buf, line, len - skipped); 1247246074Sgabor buf[len - skipped] = '\0'; 1248246074Sgabor } 1249246074Sgabor return len; 1250246074Sgabor} 1251246074Sgabor 1252246074Sgabor 1253246074Sgabor/* 1254246074Sgabor * Reverse the old and new portions of the current hunk. 1255246074Sgabor */ 1256246074Sgaborbool 1257246074Sgaborpch_swap(void) 1258246074Sgabor{ 1259246074Sgabor char **tp_line; /* the text of the hunk */ 1260267490Spfg unsigned short *tp_len;/* length of each line */ 1261246074Sgabor char *tp_char; /* +, -, and ! */ 1262246074Sgabor LINENUM i; 1263246074Sgabor LINENUM n; 1264246074Sgabor bool blankline = false; 1265246074Sgabor char *s; 1266246074Sgabor 1267246074Sgabor i = p_first; 1268246074Sgabor p_first = p_newfirst; 1269246074Sgabor p_newfirst = i; 1270246074Sgabor 1271246074Sgabor /* make a scratch copy */ 1272246074Sgabor 1273246074Sgabor tp_line = p_line; 1274246074Sgabor tp_len = p_len; 1275246074Sgabor tp_char = p_char; 1276246074Sgabor p_line = NULL; /* force set_hunkmax to allocate again */ 1277246074Sgabor p_len = NULL; 1278246074Sgabor p_char = NULL; 1279246074Sgabor set_hunkmax(); 1280246074Sgabor if (p_line == NULL || p_len == NULL || p_char == NULL) { 1281246074Sgabor 1282246074Sgabor free(p_line); 1283246074Sgabor p_line = tp_line; 1284246074Sgabor free(p_len); 1285246074Sgabor p_len = tp_len; 1286246074Sgabor free(p_char); 1287246074Sgabor p_char = tp_char; 1288246074Sgabor return false; /* not enough memory to swap hunk! */ 1289246074Sgabor } 1290246074Sgabor /* now turn the new into the old */ 1291246074Sgabor 1292246074Sgabor i = p_ptrn_lines + 1; 1293246074Sgabor if (tp_char[i] == '\n') { /* account for possible blank line */ 1294246074Sgabor blankline = true; 1295246074Sgabor i++; 1296246074Sgabor } 1297246074Sgabor if (p_efake >= 0) { /* fix non-freeable ptr range */ 1298246074Sgabor if (p_efake <= i) 1299246074Sgabor n = p_end - i + 1; 1300246074Sgabor else 1301246074Sgabor n = -i; 1302246074Sgabor p_efake += n; 1303246074Sgabor p_bfake += n; 1304246074Sgabor } 1305246074Sgabor for (n = 0; i <= p_end; i++, n++) { 1306246074Sgabor p_line[n] = tp_line[i]; 1307246074Sgabor p_char[n] = tp_char[i]; 1308246074Sgabor if (p_char[n] == '+') 1309246074Sgabor p_char[n] = '-'; 1310246074Sgabor p_len[n] = tp_len[i]; 1311246074Sgabor } 1312246074Sgabor if (blankline) { 1313246074Sgabor i = p_ptrn_lines + 1; 1314246074Sgabor p_line[n] = tp_line[i]; 1315246074Sgabor p_char[n] = tp_char[i]; 1316246074Sgabor p_len[n] = tp_len[i]; 1317246074Sgabor n++; 1318246074Sgabor } 1319246074Sgabor if (p_char[0] != '=') 1320246074Sgabor fatal("Malformed patch at line %ld: expected '=' found '%c'\n", 1321246074Sgabor p_input_line, p_char[0]); 1322246074Sgabor p_char[0] = '*'; 1323246074Sgabor for (s = p_line[0]; *s; s++) 1324246074Sgabor if (*s == '-') 1325246074Sgabor *s = '*'; 1326246074Sgabor 1327246074Sgabor /* now turn the old into the new */ 1328246074Sgabor 1329246074Sgabor if (p_char[0] != '*') 1330246074Sgabor fatal("Malformed patch at line %ld: expected '*' found '%c'\n", 1331246074Sgabor p_input_line, p_char[0]); 1332246074Sgabor tp_char[0] = '='; 1333246074Sgabor for (s = tp_line[0]; *s; s++) 1334246074Sgabor if (*s == '*') 1335246074Sgabor *s = '-'; 1336246074Sgabor for (i = 0; n <= p_end; i++, n++) { 1337246074Sgabor p_line[n] = tp_line[i]; 1338246074Sgabor p_char[n] = tp_char[i]; 1339246074Sgabor if (p_char[n] == '-') 1340246074Sgabor p_char[n] = '+'; 1341246074Sgabor p_len[n] = tp_len[i]; 1342246074Sgabor } 1343246074Sgabor 1344246074Sgabor if (i != p_ptrn_lines + 1) 1345246074Sgabor fatal("Malformed patch at line %ld: expected %ld lines, " 1346246074Sgabor "got %ld\n", 1347246074Sgabor p_input_line, p_ptrn_lines + 1, i); 1348246074Sgabor 1349246074Sgabor i = p_ptrn_lines; 1350246074Sgabor p_ptrn_lines = p_repl_lines; 1351246074Sgabor p_repl_lines = i; 1352246074Sgabor 1353246074Sgabor free(tp_line); 1354246074Sgabor free(tp_len); 1355246074Sgabor free(tp_char); 1356246074Sgabor 1357246074Sgabor return true; 1358246074Sgabor} 1359246074Sgabor 1360246074Sgabor/* 1361246074Sgabor * Return the specified line position in the old file of the old context. 1362246074Sgabor */ 1363246074SgaborLINENUM 1364246074Sgaborpch_first(void) 1365246074Sgabor{ 1366246074Sgabor return p_first; 1367246074Sgabor} 1368246074Sgabor 1369246074Sgabor/* 1370246074Sgabor * Return the number of lines of old context. 1371246074Sgabor */ 1372246074SgaborLINENUM 1373246074Sgaborpch_ptrn_lines(void) 1374246074Sgabor{ 1375246074Sgabor return p_ptrn_lines; 1376246074Sgabor} 1377246074Sgabor 1378246074Sgabor/* 1379246074Sgabor * Return the probable line position in the new file of the first line. 1380246074Sgabor */ 1381246074SgaborLINENUM 1382246074Sgaborpch_newfirst(void) 1383246074Sgabor{ 1384246074Sgabor return p_newfirst; 1385246074Sgabor} 1386246074Sgabor 1387246074Sgabor/* 1388246074Sgabor * Return the number of lines in the replacement text including context. 1389246074Sgabor */ 1390246074SgaborLINENUM 1391246074Sgaborpch_repl_lines(void) 1392246074Sgabor{ 1393246074Sgabor return p_repl_lines; 1394246074Sgabor} 1395246074Sgabor 1396246074Sgabor/* 1397246074Sgabor * Return the number of lines in the whole hunk. 1398246074Sgabor */ 1399246074SgaborLINENUM 1400246074Sgaborpch_end(void) 1401246074Sgabor{ 1402246074Sgabor return p_end; 1403246074Sgabor} 1404246074Sgabor 1405246074Sgabor/* 1406246074Sgabor * Return the number of context lines before the first changed line. 1407246074Sgabor */ 1408246074SgaborLINENUM 1409246074Sgaborpch_context(void) 1410246074Sgabor{ 1411246074Sgabor return p_context; 1412246074Sgabor} 1413246074Sgabor 1414246074Sgabor/* 1415246074Sgabor * Return the length of a particular patch line. 1416246074Sgabor */ 1417267490Spfgunsigned short 1418246074Sgaborpch_line_len(LINENUM line) 1419246074Sgabor{ 1420246074Sgabor return p_len[line]; 1421246074Sgabor} 1422246074Sgabor 1423246074Sgabor/* 1424246074Sgabor * Return the control character (+, -, *, !, etc) for a patch line. 1425246074Sgabor */ 1426246074Sgaborchar 1427246074Sgaborpch_char(LINENUM line) 1428246074Sgabor{ 1429246074Sgabor return p_char[line]; 1430246074Sgabor} 1431246074Sgabor 1432246074Sgabor/* 1433246074Sgabor * Return a pointer to a particular patch line. 1434246074Sgabor */ 1435246074Sgaborchar * 1436246074Sgaborpfetch(LINENUM line) 1437246074Sgabor{ 1438246074Sgabor return p_line[line]; 1439246074Sgabor} 1440246074Sgabor 1441246074Sgabor/* 1442246074Sgabor * Return where in the patch file this hunk began, for error messages. 1443246074Sgabor */ 1444246074SgaborLINENUM 1445246074Sgaborpch_hunk_beg(void) 1446246074Sgabor{ 1447246074Sgabor return p_hunk_beg; 1448246074Sgabor} 1449246074Sgabor 1450246074Sgabor/* 1451246074Sgabor * Apply an ed script by feeding ed itself. 1452246074Sgabor */ 1453246074Sgaborvoid 1454246074Sgabordo_ed_script(void) 1455246074Sgabor{ 1456246074Sgabor char *t; 1457275553Spfg off_t beginning_of_this_line; 1458246074Sgabor FILE *pipefp = NULL; 1459286346Sdelphij int continuation; 1460246074Sgabor 1461246074Sgabor if (!skip_rest_of_patch) { 1462246074Sgabor if (copy_file(filearg[0], TMPOUTNAME) < 0) { 1463246074Sgabor unlink(TMPOUTNAME); 1464246074Sgabor fatal("can't create temp file %s", TMPOUTNAME); 1465246074Sgabor } 1466286346Sdelphij snprintf(buf, buf_size, "%s%s%s", _PATH_RED, 1467246074Sgabor verbose ? " " : " -s ", TMPOUTNAME); 1468246074Sgabor pipefp = popen(buf, "w"); 1469246074Sgabor } 1470246074Sgabor for (;;) { 1471275553Spfg beginning_of_this_line = ftello(pfp); 1472246074Sgabor if (pgets(true) == 0) { 1473246074Sgabor next_intuit_at(beginning_of_this_line, p_input_line); 1474246074Sgabor break; 1475246074Sgabor } 1476246074Sgabor p_input_line++; 1477246074Sgabor for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++) 1478246074Sgabor ; 1479246074Sgabor /* POSIX defines allowed commands as {a,c,d,i,s} */ 1480275582Spfg if (isdigit((unsigned char)*buf) && 1481275582Spfg (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) { 1482246074Sgabor if (pipefp != NULL) 1483246074Sgabor fputs(buf, pipefp); 1484286346Sdelphij if (*t == 's') { 1485286346Sdelphij for (;;) { 1486286346Sdelphij continuation = 0; 1487286346Sdelphij t = strchr(buf, '\0') - 1; 1488286346Sdelphij while (--t >= buf && *t == '\\') 1489286346Sdelphij continuation = !continuation; 1490286346Sdelphij if (!continuation || 1491286346Sdelphij pgets(true) == 0) 1492286346Sdelphij break; 1493286346Sdelphij if (pipefp != NULL) 1494286346Sdelphij fputs(buf, pipefp); 1495286346Sdelphij } 1496286346Sdelphij } else if (*t != 'd') { 1497246074Sgabor while (pgets(true)) { 1498246074Sgabor p_input_line++; 1499246074Sgabor if (pipefp != NULL) 1500246074Sgabor fputs(buf, pipefp); 1501246074Sgabor if (strEQ(buf, ".\n")) 1502246074Sgabor break; 1503246074Sgabor } 1504246074Sgabor } 1505246074Sgabor } else { 1506246074Sgabor next_intuit_at(beginning_of_this_line, p_input_line); 1507246074Sgabor break; 1508246074Sgabor } 1509246074Sgabor } 1510246074Sgabor if (pipefp == NULL) 1511246074Sgabor return; 1512246074Sgabor fprintf(pipefp, "w\n"); 1513246074Sgabor fprintf(pipefp, "q\n"); 1514246074Sgabor fflush(pipefp); 1515246074Sgabor pclose(pipefp); 1516246074Sgabor ignore_signals(); 1517246074Sgabor if (!check_only) { 1518246074Sgabor if (move_file(TMPOUTNAME, outname) < 0) { 1519246074Sgabor toutkeep = true; 1520246074Sgabor chmod(TMPOUTNAME, filemode); 1521246074Sgabor } else 1522246074Sgabor chmod(outname, filemode); 1523246074Sgabor } 1524246074Sgabor set_signals(1); 1525246074Sgabor} 1526246074Sgabor 1527246074Sgabor/* 1528246074Sgabor * Choose the name of the file to be patched based on POSIX rules. 1529246074Sgabor * NOTE: the POSIX rules are amazingly stupid and we only follow them 1530246074Sgabor * if the user specified --posix or set POSIXLY_CORRECT. 1531246074Sgabor */ 1532246074Sgaborstatic char * 1533246074Sgaborposix_name(const struct file_name *names, bool assume_exists) 1534246074Sgabor{ 1535246074Sgabor char *path = NULL; 1536246074Sgabor int i; 1537246074Sgabor 1538246074Sgabor /* 1539246074Sgabor * POSIX states that the filename will be chosen from one 1540246074Sgabor * of the old, new and index names (in that order) if 1541246074Sgabor * the file exists relative to CWD after -p stripping. 1542246074Sgabor */ 1543246074Sgabor for (i = 0; i < MAX_FILE; i++) { 1544246074Sgabor if (names[i].path != NULL && names[i].exists) { 1545246074Sgabor path = names[i].path; 1546246074Sgabor break; 1547246074Sgabor } 1548246074Sgabor } 1549246074Sgabor if (path == NULL && !assume_exists) { 1550246074Sgabor /* 1551286795Sdelphij * No files found, check to see if the diff could be 1552286795Sdelphij * creating a new file. 1553246074Sgabor */ 1554246074Sgabor if (path == NULL && ok_to_create_file && 1555246074Sgabor names[NEW_FILE].path != NULL) 1556246074Sgabor path = names[NEW_FILE].path; 1557246074Sgabor } 1558246074Sgabor 1559276218Spfg return path ? xstrdup(path) : NULL; 1560246074Sgabor} 1561246074Sgabor 1562246074Sgaborstatic char * 1563286795Sdelphijcompare_names(const struct file_name *names, bool assume_exists) 1564246074Sgabor{ 1565246074Sgabor size_t min_components, min_baselen, min_len, tmp; 1566246074Sgabor char *best = NULL; 1567255232Sse char *path; 1568246074Sgabor int i; 1569246074Sgabor 1570246074Sgabor /* 1571246074Sgabor * The "best" name is the one with the fewest number of path 1572246074Sgabor * components, the shortest basename length, and the shortest 1573246074Sgabor * overall length (in that order). We only use the Index: file 1574246074Sgabor * if neither of the old or new files could be intuited from 1575246074Sgabor * the diff header. 1576246074Sgabor */ 1577246074Sgabor min_components = min_baselen = min_len = SIZE_MAX; 1578246074Sgabor for (i = INDEX_FILE; i >= OLD_FILE; i--) { 1579255232Sse path = names[i].path; 1580286795Sdelphij if (path == NULL || (!names[i].exists && !assume_exists)) 1581246074Sgabor continue; 1582255232Sse if ((tmp = num_components(path)) > min_components) 1583246074Sgabor continue; 1584250943Sse if (tmp < min_components) { 1585250943Sse min_components = tmp; 1586255232Sse best = path; 1587250943Sse } 1588255232Sse if ((tmp = strlen(basename(path))) > min_baselen) 1589246074Sgabor continue; 1590250943Sse if (tmp < min_baselen) { 1591250943Sse min_baselen = tmp; 1592255232Sse best = path; 1593250943Sse } 1594255232Sse if ((tmp = strlen(path)) > min_len) 1595246074Sgabor continue; 1596246074Sgabor min_len = tmp; 1597255232Sse best = path; 1598246074Sgabor } 1599255232Sse return best; 1600255232Sse} 1601255232Sse 1602255232Sse/* 1603255232Sse * Choose the name of the file to be patched based the "best" one 1604255232Sse * available. 1605255232Sse */ 1606255232Ssestatic char * 1607255232Ssebest_name(const struct file_name *names, bool assume_exists) 1608255232Sse{ 1609255232Sse char *best; 1610255232Sse 1611286795Sdelphij best = compare_names(names, assume_exists); 1612246074Sgabor 1613286795Sdelphij /* No match? Check to see if the diff could be creating a new file. */ 1614286795Sdelphij if (best == NULL && ok_to_create_file) 1615286795Sdelphij best = names[NEW_FILE].path; 1616286795Sdelphij 1617276218Spfg return best ? xstrdup(best) : NULL; 1618246074Sgabor} 1619246074Sgabor 1620246074Sgaborstatic size_t 1621246074Sgabornum_components(const char *path) 1622246074Sgabor{ 1623246074Sgabor size_t n; 1624246074Sgabor const char *cp; 1625246074Sgabor 1626246074Sgabor for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) { 1627246074Sgabor while (*cp == '/') 1628246074Sgabor cp++; /* skip consecutive slashes */ 1629246074Sgabor } 1630246074Sgabor return n; 1631246074Sgabor} 1632275612Spfg 1633275612Spfg/* 1634275612Spfg * Convert number at NPTR into LINENUM and save address of first 1635275612Spfg * character that is not a digit in ENDPTR. If conversion is not 1636275612Spfg * possible, call fatal. 1637275612Spfg */ 1638275612Spfgstatic LINENUM 1639275612Spfgstrtolinenum(char *nptr, char **endptr) 1640275612Spfg{ 1641275612Spfg LINENUM rv; 1642275612Spfg char c; 1643275612Spfg char *p; 1644275612Spfg const char *errstr; 1645275612Spfg 1646275612Spfg for (p = nptr; isdigit((unsigned char)*p); p++) 1647275612Spfg ; 1648275612Spfg 1649275612Spfg if (p == nptr) 1650275612Spfg malformed(); 1651275612Spfg 1652275612Spfg c = *p; 1653275612Spfg *p = '\0'; 1654275612Spfg 1655275612Spfg rv = strtonum(nptr, 0, LINENUM_MAX, &errstr); 1656275612Spfg if (errstr != NULL) 1657275612Spfg fatal("invalid line number at line %ld: `%s' is %s\n", 1658275612Spfg p_input_line, nptr, errstr); 1659276218Spfg 1660275612Spfg *p = c; 1661275612Spfg *endptr = p; 1662275612Spfg 1663275612Spfg return rv; 1664275612Spfg} 1665