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 * 26276807Spfg * $OpenBSD: patch.c,v 1.54 2014/12/13 10:31:07 tobias Exp $ 27246091Sdelphij * $FreeBSD$ 28246074Sgabor * 29246074Sgabor */ 30246074Sgabor 31246074Sgabor#include <sys/types.h> 32246074Sgabor#include <sys/stat.h> 33246074Sgabor 34246074Sgabor#include <ctype.h> 35246074Sgabor#include <getopt.h> 36246074Sgabor#include <limits.h> 37246074Sgabor#include <stdio.h> 38246074Sgabor#include <string.h> 39246074Sgabor#include <stdlib.h> 40246074Sgabor#include <unistd.h> 41246074Sgabor 42246074Sgabor#include "common.h" 43246074Sgabor#include "util.h" 44246074Sgabor#include "pch.h" 45246074Sgabor#include "inp.h" 46246074Sgabor#include "backupfile.h" 47246074Sgabor#include "pathnames.h" 48246074Sgabor 49246074Sgabormode_t filemode = 0644; 50246074Sgabor 51246074Sgaborchar *buf; /* general purpose buffer */ 52246074Sgaborsize_t buf_size; /* size of the general purpose buffer */ 53246074Sgabor 54246074Sgaborbool using_plan_a = true; /* try to keep everything in memory */ 55246074Sgaborbool out_of_mem = false; /* ran out of memory in plan a */ 56246074Sgabor 57246074Sgabor#define MAXFILEC 2 58246074Sgabor 59246074Sgaborchar *filearg[MAXFILEC]; 60246074Sgaborbool ok_to_create_file = false; 61246074Sgaborchar *outname = NULL; 62246074Sgaborchar *origprae = NULL; 63246074Sgaborchar *TMPOUTNAME; 64246074Sgaborchar *TMPINNAME; 65246074Sgaborchar *TMPREJNAME; 66246074Sgaborchar *TMPPATNAME; 67246074Sgaborbool toutkeep = false; 68246074Sgaborbool trejkeep = false; 69246074Sgaborbool warn_on_invalid_line; 70246074Sgaborbool last_line_missing_eol; 71246074Sgabor 72246074Sgabor#ifdef DEBUGGING 73246074Sgaborint debug = 0; 74246074Sgabor#endif 75246074Sgabor 76246074Sgaborbool force = false; 77246074Sgaborbool batch = false; 78246074Sgaborbool verbose = true; 79246074Sgaborbool reverse = false; 80246074Sgaborbool noreverse = false; 81246074Sgaborbool skip_rest_of_patch = false; 82246074Sgaborint strippath = 957; 83246074Sgaborbool canonicalize = false; 84246074Sgaborbool check_only = false; 85246074Sgaborint diff_type = 0; 86246074Sgaborchar *revision = NULL; /* prerequisite revision, if any */ 87246074SgaborLINENUM input_lines = 0; /* how long is input file in lines */ 88246074Sgaborint posix = 0; /* strict POSIX mode? */ 89246074Sgabor 90246074Sgaborstatic void reinitialize_almost_everything(void); 91246074Sgaborstatic void get_some_switches(void); 92246074Sgaborstatic LINENUM locate_hunk(LINENUM); 93246074Sgaborstatic void abort_context_hunk(void); 94246074Sgaborstatic void rej_line(int, LINENUM); 95246074Sgaborstatic void abort_hunk(void); 96246074Sgaborstatic void apply_hunk(LINENUM); 97246074Sgaborstatic void init_output(const char *); 98246074Sgaborstatic void init_reject(const char *); 99246074Sgaborstatic void copy_till(LINENUM, bool); 100246074Sgaborstatic bool spew_output(void); 101246074Sgaborstatic void dump_line(LINENUM, bool); 102246074Sgaborstatic bool patch_match(LINENUM, LINENUM, LINENUM); 103246074Sgaborstatic bool similar(const char *, const char *, int); 104246074Sgaborstatic void usage(void); 105246074Sgabor 106246074Sgabor/* true if -E was specified on command line. */ 107246074Sgaborstatic bool remove_empty_files = false; 108246074Sgabor 109246074Sgabor/* true if -R was specified on command line. */ 110246074Sgaborstatic bool reverse_flag_specified = false; 111246074Sgabor 112246074Sgabor/* buffer holding the name of the rejected patch file. */ 113246074Sgaborstatic char rejname[NAME_MAX + 1]; 114246074Sgabor 115246074Sgabor/* how many input lines have been irretractibly output */ 116246074Sgaborstatic LINENUM last_frozen_line = 0; 117246074Sgabor 118246074Sgaborstatic int Argc; /* guess */ 119246074Sgaborstatic char **Argv; 120246074Sgaborstatic int Argc_last; /* for restarting plan_b */ 121246074Sgaborstatic char **Argv_last; 122246074Sgabor 123246074Sgaborstatic FILE *ofp = NULL; /* output file pointer */ 124246074Sgaborstatic FILE *rejfp = NULL; /* reject file pointer */ 125246074Sgabor 126246074Sgaborstatic int filec = 0; /* how many file arguments? */ 127246074Sgaborstatic LINENUM last_offset = 0; 128246074Sgaborstatic LINENUM maxfuzz = 2; 129246074Sgabor 130246074Sgabor/* patch using ifdef, ifndef, etc. */ 131246074Sgaborstatic bool do_defines = false; 132246074Sgabor/* #ifdef xyzzy */ 133246074Sgaborstatic char if_defined[128]; 134246074Sgabor/* #ifndef xyzzy */ 135246074Sgaborstatic char not_defined[128]; 136246074Sgabor/* #else */ 137246074Sgaborstatic const char else_defined[] = "#else\n"; 138246074Sgabor/* #endif xyzzy */ 139246074Sgaborstatic char end_defined[128]; 140246074Sgabor 141246074Sgabor 142246074Sgabor/* Apply a set of diffs as appropriate. */ 143246074Sgabor 144246074Sgaborint 145246074Sgabormain(int argc, char *argv[]) 146246074Sgabor{ 147246074Sgabor int error = 0, hunk, failed, i, fd; 148255894Sdelphij bool patch_seen, reverse_seen; 149246074Sgabor LINENUM where = 0, newwhere, fuzz, mymaxfuzz; 150246074Sgabor const char *tmpdir; 151246074Sgabor char *v; 152246074Sgabor 153275840Spfg setvbuf(stdout, NULL, _IOLBF, 0); 154275840Spfg setvbuf(stderr, NULL, _IOLBF, 0); 155246074Sgabor for (i = 0; i < MAXFILEC; i++) 156246074Sgabor filearg[i] = NULL; 157246074Sgabor 158246074Sgabor buf_size = INITLINELEN; 159246074Sgabor buf = malloc((unsigned)(buf_size)); 160246074Sgabor if (buf == NULL) 161246074Sgabor fatal("out of memory\n"); 162246074Sgabor 163246074Sgabor /* Cons up the names of the temporary files. */ 164246074Sgabor if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') 165246074Sgabor tmpdir = _PATH_TMP; 166246074Sgabor for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--) 167246074Sgabor ; 168246074Sgabor i++; 169246074Sgabor if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1) 170246074Sgabor fatal("cannot allocate memory"); 171246074Sgabor if ((fd = mkstemp(TMPOUTNAME)) < 0) 172246074Sgabor pfatal("can't create %s", TMPOUTNAME); 173246074Sgabor close(fd); 174246074Sgabor 175246074Sgabor if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1) 176246074Sgabor fatal("cannot allocate memory"); 177246074Sgabor if ((fd = mkstemp(TMPINNAME)) < 0) 178246074Sgabor pfatal("can't create %s", TMPINNAME); 179246074Sgabor close(fd); 180246074Sgabor 181246074Sgabor if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1) 182246074Sgabor fatal("cannot allocate memory"); 183246074Sgabor if ((fd = mkstemp(TMPREJNAME)) < 0) 184246074Sgabor pfatal("can't create %s", TMPREJNAME); 185246074Sgabor close(fd); 186246074Sgabor 187246074Sgabor if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1) 188246074Sgabor fatal("cannot allocate memory"); 189246074Sgabor if ((fd = mkstemp(TMPPATNAME)) < 0) 190246074Sgabor pfatal("can't create %s", TMPPATNAME); 191246074Sgabor close(fd); 192246074Sgabor 193246074Sgabor v = getenv("SIMPLE_BACKUP_SUFFIX"); 194246074Sgabor if (v) 195246074Sgabor simple_backup_suffix = v; 196246074Sgabor else 197246074Sgabor simple_backup_suffix = ORIGEXT; 198246074Sgabor 199246074Sgabor /* parse switches */ 200246074Sgabor Argc = argc; 201246074Sgabor Argv = argv; 202246074Sgabor get_some_switches(); 203246074Sgabor 204246074Sgabor if (backup_type == none) { 205246074Sgabor if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL) 206246074Sgabor v = getenv("VERSION_CONTROL"); 207246074Sgabor if (v != NULL || !posix) 208246074Sgabor backup_type = get_version(v); /* OK to pass NULL. */ 209246074Sgabor } 210246074Sgabor 211246074Sgabor /* make sure we clean up /tmp in case of disaster */ 212246074Sgabor set_signals(0); 213246074Sgabor 214246091Sdelphij patch_seen = false; 215246074Sgabor for (open_patch_file(filearg[1]); there_is_another_patch(); 216246074Sgabor reinitialize_almost_everything()) { 217246074Sgabor /* for each patch in patch file */ 218276807Spfg 219246091Sdelphij patch_seen = true; 220246074Sgabor 221246074Sgabor warn_on_invalid_line = true; 222246074Sgabor 223246074Sgabor if (outname == NULL) 224276807Spfg outname = xstrdup(filearg[0]); 225246074Sgabor 226246074Sgabor /* for ed script just up and do it and exit */ 227246074Sgabor if (diff_type == ED_DIFF) { 228246074Sgabor do_ed_script(); 229246074Sgabor continue; 230246074Sgabor } 231246074Sgabor /* initialize the patched file */ 232246074Sgabor if (!skip_rest_of_patch) 233246074Sgabor init_output(TMPOUTNAME); 234246074Sgabor 235246074Sgabor /* initialize reject file */ 236246074Sgabor init_reject(TMPREJNAME); 237246074Sgabor 238246074Sgabor /* find out where all the lines are */ 239246074Sgabor if (!skip_rest_of_patch) 240246074Sgabor scan_input(filearg[0]); 241246074Sgabor 242253614Spfg /* 243253614Spfg * from here on, open no standard i/o files, because 244253614Spfg * malloc might misfire and we can't catch it easily 245253614Spfg */ 246246074Sgabor 247246074Sgabor /* apply each hunk of patch */ 248246074Sgabor hunk = 0; 249246074Sgabor failed = 0; 250255894Sdelphij reverse_seen = false; 251246074Sgabor out_of_mem = false; 252246074Sgabor while (another_hunk()) { 253246074Sgabor hunk++; 254246074Sgabor fuzz = 0; 255246074Sgabor mymaxfuzz = pch_context(); 256246074Sgabor if (maxfuzz < mymaxfuzz) 257246074Sgabor mymaxfuzz = maxfuzz; 258246074Sgabor if (!skip_rest_of_patch) { 259246074Sgabor do { 260246074Sgabor where = locate_hunk(fuzz); 261255894Sdelphij if (hunk == 1 && where == 0 && !force && !reverse_seen) { 262246074Sgabor /* dwim for reversed patch? */ 263246074Sgabor if (!pch_swap()) { 264246074Sgabor if (fuzz == 0) 265246074Sgabor say("Not enough memory to try swapped hunk! Assuming unswapped.\n"); 266246074Sgabor continue; 267246074Sgabor } 268246074Sgabor reverse = !reverse; 269246074Sgabor /* try again */ 270246074Sgabor where = locate_hunk(fuzz); 271246074Sgabor if (where == 0) { 272246074Sgabor /* didn't find it swapped */ 273246074Sgabor if (!pch_swap()) 274246074Sgabor /* put it back to normal */ 275246074Sgabor fatal("lost hunk on alloc error!\n"); 276246074Sgabor reverse = !reverse; 277246074Sgabor } else if (noreverse) { 278246074Sgabor if (!pch_swap()) 279246074Sgabor /* put it back to normal */ 280246074Sgabor fatal("lost hunk on alloc error!\n"); 281246074Sgabor reverse = !reverse; 282246074Sgabor say("Ignoring previously applied (or reversed) patch.\n"); 283246074Sgabor skip_rest_of_patch = true; 284246074Sgabor } else if (batch) { 285246074Sgabor if (verbose) 286246074Sgabor say("%seversed (or previously applied) patch detected! %s -R.", 287246074Sgabor reverse ? "R" : "Unr", 288246074Sgabor reverse ? "Assuming" : "Ignoring"); 289246074Sgabor } else { 290246074Sgabor ask("%seversed (or previously applied) patch detected! %s -R? [y] ", 291246074Sgabor reverse ? "R" : "Unr", 292246074Sgabor reverse ? "Assume" : "Ignore"); 293246074Sgabor if (*buf == 'n') { 294246074Sgabor ask("Apply anyway? [n] "); 295246074Sgabor if (*buf != 'y') 296246074Sgabor skip_rest_of_patch = true; 297255894Sdelphij else 298255894Sdelphij reverse_seen = true; 299246074Sgabor where = 0; 300246074Sgabor reverse = !reverse; 301246074Sgabor if (!pch_swap()) 302246074Sgabor /* put it back to normal */ 303246074Sgabor fatal("lost hunk on alloc error!\n"); 304246074Sgabor } 305246074Sgabor } 306246074Sgabor } 307246074Sgabor } while (!skip_rest_of_patch && where == 0 && 308246074Sgabor ++fuzz <= mymaxfuzz); 309246074Sgabor 310246074Sgabor if (skip_rest_of_patch) { /* just got decided */ 311250975Sgjb if (ferror(ofp) || fclose(ofp)) { 312246074Sgabor say("Error writing %s\n", 313246074Sgabor TMPOUTNAME); 314246074Sgabor error = 1; 315246074Sgabor } 316246074Sgabor ofp = NULL; 317246074Sgabor } 318246074Sgabor } 319246074Sgabor newwhere = pch_newfirst() + last_offset; 320246074Sgabor if (skip_rest_of_patch) { 321246074Sgabor abort_hunk(); 322246074Sgabor failed++; 323246074Sgabor if (verbose) 324246074Sgabor say("Hunk #%d ignored at %ld.\n", 325246074Sgabor hunk, newwhere); 326246074Sgabor } else if (where == 0) { 327246074Sgabor abort_hunk(); 328246074Sgabor failed++; 329246074Sgabor if (verbose) 330246074Sgabor say("Hunk #%d failed at %ld.\n", 331246074Sgabor hunk, newwhere); 332246074Sgabor } else { 333246074Sgabor apply_hunk(where); 334246074Sgabor if (verbose) { 335246074Sgabor say("Hunk #%d succeeded at %ld", 336246074Sgabor hunk, newwhere); 337246074Sgabor if (fuzz != 0) 338246074Sgabor say(" with fuzz %ld", fuzz); 339246074Sgabor if (last_offset) 340246074Sgabor say(" (offset %ld line%s)", 341246074Sgabor last_offset, 342246074Sgabor last_offset == 1L ? "" : "s"); 343246074Sgabor say(".\n"); 344246074Sgabor } 345246074Sgabor } 346246074Sgabor } 347246074Sgabor 348246074Sgabor if (out_of_mem && using_plan_a) { 349246074Sgabor Argc = Argc_last; 350246074Sgabor Argv = Argv_last; 351246074Sgabor say("\n\nRan out of memory using Plan A--trying again...\n\n"); 352246074Sgabor if (ofp) 353246074Sgabor fclose(ofp); 354246074Sgabor ofp = NULL; 355246074Sgabor if (rejfp) 356246074Sgabor fclose(rejfp); 357246074Sgabor rejfp = NULL; 358246074Sgabor continue; 359246074Sgabor } 360246074Sgabor if (hunk == 0) 361246074Sgabor fatal("Internal error: hunk should not be 0\n"); 362246074Sgabor 363246074Sgabor /* finish spewing out the new file */ 364246074Sgabor if (!skip_rest_of_patch && !spew_output()) { 365246074Sgabor say("Can't write %s\n", TMPOUTNAME); 366246074Sgabor error = 1; 367246074Sgabor } 368246074Sgabor 369246074Sgabor /* and put the output where desired */ 370246074Sgabor ignore_signals(); 371246074Sgabor if (!skip_rest_of_patch) { 372246074Sgabor struct stat statbuf; 373246074Sgabor char *realout = outname; 374246074Sgabor 375246074Sgabor if (!check_only) { 376246074Sgabor if (move_file(TMPOUTNAME, outname) < 0) { 377246074Sgabor toutkeep = true; 378246074Sgabor realout = TMPOUTNAME; 379246074Sgabor chmod(TMPOUTNAME, filemode); 380246074Sgabor } else 381246074Sgabor chmod(outname, filemode); 382246074Sgabor 383246074Sgabor if (remove_empty_files && 384246074Sgabor stat(realout, &statbuf) == 0 && 385246074Sgabor statbuf.st_size == 0) { 386246074Sgabor if (verbose) 387246074Sgabor say("Removing %s (empty after patching).\n", 388246074Sgabor realout); 389246074Sgabor unlink(realout); 390246074Sgabor } 391246074Sgabor } 392246074Sgabor } 393250975Sgjb if (ferror(rejfp) || fclose(rejfp)) { 394246074Sgabor say("Error writing %s\n", rejname); 395246074Sgabor error = 1; 396246074Sgabor } 397246074Sgabor rejfp = NULL; 398246074Sgabor if (failed) { 399246074Sgabor error = 1; 400246074Sgabor if (*rejname == '\0') { 401246074Sgabor if (strlcpy(rejname, outname, 402246074Sgabor sizeof(rejname)) >= sizeof(rejname)) 403246074Sgabor fatal("filename %s is too long\n", outname); 404246074Sgabor if (strlcat(rejname, REJEXT, 405246074Sgabor sizeof(rejname)) >= sizeof(rejname)) 406246074Sgabor fatal("filename %s is too long\n", outname); 407246074Sgabor } 408246091Sdelphij if (!check_only) 409246091Sdelphij say("%d out of %d hunks %s--saving rejects to %s\n", 410246091Sdelphij failed, hunk, skip_rest_of_patch ? "ignored" : "failed", rejname); 411246091Sdelphij else 412255894Sdelphij say("%d out of %d hunks %s while patching %s\n", 413255894Sdelphij failed, hunk, skip_rest_of_patch ? "ignored" : "failed", filearg[0]); 414246074Sgabor if (!check_only && move_file(TMPREJNAME, rejname) < 0) 415246074Sgabor trejkeep = true; 416246074Sgabor } 417246074Sgabor set_signals(1); 418246074Sgabor } 419276807Spfg 420246091Sdelphij if (!patch_seen) 421246091Sdelphij error = 2; 422246091Sdelphij 423246074Sgabor my_exit(error); 424246074Sgabor /* NOTREACHED */ 425246074Sgabor} 426246074Sgabor 427246074Sgabor/* Prepare to find the next patch to do in the patch file. */ 428246074Sgabor 429246074Sgaborstatic void 430246074Sgaborreinitialize_almost_everything(void) 431246074Sgabor{ 432246074Sgabor re_patch(); 433246074Sgabor re_input(); 434246074Sgabor 435246074Sgabor input_lines = 0; 436246074Sgabor last_frozen_line = 0; 437246074Sgabor 438246074Sgabor filec = 0; 439246074Sgabor if (!out_of_mem) { 440246074Sgabor free(filearg[0]); 441246074Sgabor filearg[0] = NULL; 442246074Sgabor } 443246074Sgabor 444246074Sgabor free(outname); 445246074Sgabor outname = NULL; 446246074Sgabor 447246074Sgabor last_offset = 0; 448246074Sgabor diff_type = 0; 449246074Sgabor 450246074Sgabor free(revision); 451246074Sgabor revision = NULL; 452246074Sgabor 453246074Sgabor reverse = reverse_flag_specified; 454246074Sgabor skip_rest_of_patch = false; 455246074Sgabor 456246074Sgabor get_some_switches(); 457246074Sgabor} 458246074Sgabor 459246074Sgabor/* Process switches and filenames. */ 460246074Sgabor 461246074Sgaborstatic void 462246074Sgaborget_some_switches(void) 463246074Sgabor{ 464246074Sgabor const char *options = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:"; 465246074Sgabor static struct option longopts[] = { 466246074Sgabor {"backup", no_argument, 0, 'b'}, 467246074Sgabor {"batch", no_argument, 0, 't'}, 468246074Sgabor {"check", no_argument, 0, 'C'}, 469246074Sgabor {"context", no_argument, 0, 'c'}, 470246074Sgabor {"debug", required_argument, 0, 'x'}, 471246074Sgabor {"directory", required_argument, 0, 'd'}, 472267747Spfg {"dry-run", no_argument, 0, 'C'}, 473246074Sgabor {"ed", no_argument, 0, 'e'}, 474246074Sgabor {"force", no_argument, 0, 'f'}, 475246074Sgabor {"forward", no_argument, 0, 'N'}, 476246074Sgabor {"fuzz", required_argument, 0, 'F'}, 477246074Sgabor {"ifdef", required_argument, 0, 'D'}, 478246074Sgabor {"input", required_argument, 0, 'i'}, 479246074Sgabor {"ignore-whitespace", no_argument, 0, 'l'}, 480246074Sgabor {"normal", no_argument, 0, 'n'}, 481246074Sgabor {"output", required_argument, 0, 'o'}, 482246074Sgabor {"prefix", required_argument, 0, 'B'}, 483246074Sgabor {"quiet", no_argument, 0, 's'}, 484246074Sgabor {"reject-file", required_argument, 0, 'r'}, 485246074Sgabor {"remove-empty-files", no_argument, 0, 'E'}, 486246074Sgabor {"reverse", no_argument, 0, 'R'}, 487246074Sgabor {"silent", no_argument, 0, 's'}, 488246074Sgabor {"strip", required_argument, 0, 'p'}, 489246074Sgabor {"suffix", required_argument, 0, 'z'}, 490246074Sgabor {"unified", no_argument, 0, 'u'}, 491246074Sgabor {"version", no_argument, 0, 'v'}, 492246074Sgabor {"version-control", required_argument, 0, 'V'}, 493246074Sgabor {"posix", no_argument, &posix, 1}, 494246074Sgabor {NULL, 0, 0, 0} 495246074Sgabor }; 496246074Sgabor int ch; 497246074Sgabor 498246074Sgabor rejname[0] = '\0'; 499246074Sgabor Argc_last = Argc; 500246074Sgabor Argv_last = Argv; 501246074Sgabor if (!Argc) 502246074Sgabor return; 503246074Sgabor optreset = optind = 1; 504246074Sgabor while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) { 505246074Sgabor switch (ch) { 506246074Sgabor case 'b': 507246074Sgabor if (backup_type == none) 508246074Sgabor backup_type = numbered_existing; 509246074Sgabor if (optarg == NULL) 510246074Sgabor break; 511246074Sgabor if (verbose) 512246074Sgabor say("Warning, the ``-b suffix'' option has been" 513246074Sgabor " obsoleted by the -z option.\n"); 514246074Sgabor /* FALLTHROUGH */ 515246074Sgabor case 'z': 516246074Sgabor /* must directly follow 'b' case for backwards compat */ 517276807Spfg simple_backup_suffix = xstrdup(optarg); 518246074Sgabor break; 519246074Sgabor case 'B': 520276807Spfg origprae = xstrdup(optarg); 521246074Sgabor break; 522246074Sgabor case 'c': 523246074Sgabor diff_type = CONTEXT_DIFF; 524246074Sgabor break; 525246074Sgabor case 'C': 526246074Sgabor check_only = true; 527246074Sgabor break; 528246074Sgabor case 'd': 529246074Sgabor if (chdir(optarg) < 0) 530246074Sgabor pfatal("can't cd to %s", optarg); 531246074Sgabor break; 532246074Sgabor case 'D': 533246074Sgabor do_defines = true; 534246074Sgabor if (!isalpha((unsigned char)*optarg) && *optarg != '_') 535246074Sgabor fatal("argument to -D is not an identifier\n"); 536246074Sgabor snprintf(if_defined, sizeof if_defined, 537246074Sgabor "#ifdef %s\n", optarg); 538246074Sgabor snprintf(not_defined, sizeof not_defined, 539246074Sgabor "#ifndef %s\n", optarg); 540246074Sgabor snprintf(end_defined, sizeof end_defined, 541246074Sgabor "#endif /* %s */\n", optarg); 542246074Sgabor break; 543246074Sgabor case 'e': 544246074Sgabor diff_type = ED_DIFF; 545246074Sgabor break; 546246074Sgabor case 'E': 547246074Sgabor remove_empty_files = true; 548246074Sgabor break; 549246074Sgabor case 'f': 550246074Sgabor force = true; 551246074Sgabor break; 552246074Sgabor case 'F': 553246074Sgabor maxfuzz = atoi(optarg); 554246074Sgabor break; 555246074Sgabor case 'i': 556246074Sgabor if (++filec == MAXFILEC) 557246074Sgabor fatal("too many file arguments\n"); 558276807Spfg filearg[filec] = xstrdup(optarg); 559246074Sgabor break; 560246074Sgabor case 'l': 561246074Sgabor canonicalize = true; 562246074Sgabor break; 563246074Sgabor case 'n': 564246074Sgabor diff_type = NORMAL_DIFF; 565246074Sgabor break; 566246074Sgabor case 'N': 567246074Sgabor noreverse = true; 568246074Sgabor break; 569246074Sgabor case 'o': 570276807Spfg outname = xstrdup(optarg); 571246074Sgabor break; 572246074Sgabor case 'p': 573246074Sgabor strippath = atoi(optarg); 574246074Sgabor break; 575246074Sgabor case 'r': 576246074Sgabor if (strlcpy(rejname, optarg, 577246074Sgabor sizeof(rejname)) >= sizeof(rejname)) 578246074Sgabor fatal("argument for -r is too long\n"); 579246074Sgabor break; 580246074Sgabor case 'R': 581246074Sgabor reverse = true; 582246074Sgabor reverse_flag_specified = true; 583246074Sgabor break; 584246074Sgabor case 's': 585246074Sgabor verbose = false; 586246074Sgabor break; 587246074Sgabor case 't': 588246074Sgabor batch = true; 589246074Sgabor break; 590246074Sgabor case 'u': 591246074Sgabor diff_type = UNI_DIFF; 592246074Sgabor break; 593246074Sgabor case 'v': 594246074Sgabor version(); 595246074Sgabor break; 596246074Sgabor case 'V': 597246074Sgabor backup_type = get_version(optarg); 598246074Sgabor break; 599246074Sgabor#ifdef DEBUGGING 600246074Sgabor case 'x': 601246074Sgabor debug = atoi(optarg); 602246074Sgabor break; 603246074Sgabor#endif 604246074Sgabor default: 605246074Sgabor if (ch != '\0') 606246074Sgabor usage(); 607246074Sgabor break; 608246074Sgabor } 609246074Sgabor } 610246074Sgabor Argc -= optind; 611246074Sgabor Argv += optind; 612246074Sgabor 613246074Sgabor if (Argc > 0) { 614276807Spfg filearg[0] = xstrdup(*Argv++); 615246074Sgabor Argc--; 616246074Sgabor while (Argc > 0) { 617246074Sgabor if (++filec == MAXFILEC) 618246074Sgabor fatal("too many file arguments\n"); 619276807Spfg filearg[filec] = xstrdup(*Argv++); 620246074Sgabor Argc--; 621246074Sgabor } 622246074Sgabor } 623246074Sgabor 624246074Sgabor if (getenv("POSIXLY_CORRECT") != NULL) 625246074Sgabor posix = 1; 626246074Sgabor} 627246074Sgabor 628246074Sgaborstatic void 629246074Sgaborusage(void) 630246074Sgabor{ 631246074Sgabor fprintf(stderr, 632246074Sgabor"usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n" 633246074Sgabor" [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n" 634246074Sgabor" [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n" 635246074Sgabor" [--posix] [origfile [patchfile]]\n" 636246074Sgabor" patch <patchfile\n"); 637276706Spfg my_exit(EXIT_FAILURE); 638246074Sgabor} 639246074Sgabor 640246074Sgabor/* 641246074Sgabor * Attempt to find the right place to apply this hunk of patch. 642246074Sgabor */ 643246074Sgaborstatic LINENUM 644246074Sgaborlocate_hunk(LINENUM fuzz) 645246074Sgabor{ 646246074Sgabor LINENUM first_guess = pch_first() + last_offset; 647246074Sgabor LINENUM offset; 648246074Sgabor LINENUM pat_lines = pch_ptrn_lines(); 649246074Sgabor LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1; 650246074Sgabor LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context(); 651246074Sgabor 652246074Sgabor if (pat_lines == 0) { /* null range matches always */ 653246074Sgabor if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF 654246074Sgabor || diff_type == NEW_CONTEXT_DIFF 655246074Sgabor || diff_type == UNI_DIFF)) { 656246074Sgabor say("Empty context always matches.\n"); 657246074Sgabor } 658246074Sgabor return (first_guess); 659246074Sgabor } 660246074Sgabor if (max_neg_offset >= first_guess) /* do not try lines < 0 */ 661246074Sgabor max_neg_offset = first_guess - 1; 662246074Sgabor if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz)) 663246074Sgabor return first_guess; 664246074Sgabor for (offset = 1; ; offset++) { 665246074Sgabor bool check_after = (offset <= max_pos_offset); 666246074Sgabor bool check_before = (offset <= max_neg_offset); 667246074Sgabor 668246074Sgabor if (check_after && patch_match(first_guess, offset, fuzz)) { 669246074Sgabor#ifdef DEBUGGING 670246074Sgabor if (debug & 1) 671246074Sgabor say("Offset changing from %ld to %ld\n", 672246074Sgabor last_offset, offset); 673246074Sgabor#endif 674246074Sgabor last_offset = offset; 675246074Sgabor return first_guess + offset; 676246074Sgabor } else if (check_before && patch_match(first_guess, -offset, fuzz)) { 677246074Sgabor#ifdef DEBUGGING 678246074Sgabor if (debug & 1) 679246074Sgabor say("Offset changing from %ld to %ld\n", 680246074Sgabor last_offset, -offset); 681246074Sgabor#endif 682246074Sgabor last_offset = -offset; 683246074Sgabor return first_guess - offset; 684246074Sgabor } else if (!check_before && !check_after) 685246074Sgabor return 0; 686246074Sgabor } 687246074Sgabor} 688246074Sgabor 689246074Sgabor/* We did not find the pattern, dump out the hunk so they can handle it. */ 690246074Sgabor 691246074Sgaborstatic void 692246074Sgaborabort_context_hunk(void) 693246074Sgabor{ 694246074Sgabor LINENUM i; 695246074Sgabor const LINENUM pat_end = pch_end(); 696246074Sgabor /* 697246074Sgabor * add in last_offset to guess the same as the previous successful 698246074Sgabor * hunk 699246074Sgabor */ 700246074Sgabor const LINENUM oldfirst = pch_first() + last_offset; 701246074Sgabor const LINENUM newfirst = pch_newfirst() + last_offset; 702246074Sgabor const LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; 703246074Sgabor const LINENUM newlast = newfirst + pch_repl_lines() - 1; 704246074Sgabor const char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : ""); 705246074Sgabor const char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----"); 706246074Sgabor 707246074Sgabor fprintf(rejfp, "***************\n"); 708246074Sgabor for (i = 0; i <= pat_end; i++) { 709246074Sgabor switch (pch_char(i)) { 710246074Sgabor case '*': 711246074Sgabor if (oldlast < oldfirst) 712246074Sgabor fprintf(rejfp, "*** 0%s\n", stars); 713246074Sgabor else if (oldlast == oldfirst) 714246074Sgabor fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); 715246074Sgabor else 716246074Sgabor fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, 717246074Sgabor oldlast, stars); 718246074Sgabor break; 719246074Sgabor case '=': 720246074Sgabor if (newlast < newfirst) 721246074Sgabor fprintf(rejfp, "--- 0%s\n", minuses); 722246074Sgabor else if (newlast == newfirst) 723246074Sgabor fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); 724246074Sgabor else 725246074Sgabor fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, 726246074Sgabor newlast, minuses); 727246074Sgabor break; 728246074Sgabor case '\n': 729246074Sgabor fprintf(rejfp, "%s", pfetch(i)); 730246074Sgabor break; 731246074Sgabor case ' ': 732246074Sgabor case '-': 733246074Sgabor case '+': 734246074Sgabor case '!': 735246074Sgabor fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); 736246074Sgabor break; 737246074Sgabor default: 738246074Sgabor fatal("fatal internal error in abort_context_hunk\n"); 739246074Sgabor } 740246074Sgabor } 741246074Sgabor} 742246074Sgabor 743246074Sgaborstatic void 744246074Sgaborrej_line(int ch, LINENUM i) 745246074Sgabor{ 746267746Spfg unsigned short len; 747246074Sgabor const char *line = pfetch(i); 748246074Sgabor 749267746Spfg len = strnlen(line, USHRT_MAX); 750246074Sgabor 751246074Sgabor fprintf(rejfp, "%c%s", ch, line); 752267746Spfg if (len == 0 || line[len-1] != '\n') { 753267746Spfg if (len >= USHRT_MAX) 754267746Spfg fprintf(rejfp, "\n\\ Line too long\n"); 755267746Spfg else 756267746Spfg fprintf(rejfp, "\n\\ No newline at end of line\n"); 757267746Spfg } 758246074Sgabor} 759246074Sgabor 760246074Sgaborstatic void 761246074Sgaborabort_hunk(void) 762246074Sgabor{ 763246074Sgabor LINENUM i, j, split; 764246074Sgabor int ch1, ch2; 765246074Sgabor const LINENUM pat_end = pch_end(); 766246074Sgabor const LINENUM oldfirst = pch_first() + last_offset; 767246074Sgabor const LINENUM newfirst = pch_newfirst() + last_offset; 768246074Sgabor 769246074Sgabor if (diff_type != UNI_DIFF) { 770246074Sgabor abort_context_hunk(); 771246074Sgabor return; 772246074Sgabor } 773246074Sgabor split = -1; 774246074Sgabor for (i = 0; i <= pat_end; i++) { 775246074Sgabor if (pch_char(i) == '=') { 776246074Sgabor split = i; 777246074Sgabor break; 778246074Sgabor } 779246074Sgabor } 780246074Sgabor if (split == -1) { 781246074Sgabor fprintf(rejfp, "malformed hunk: no split found\n"); 782246074Sgabor return; 783246074Sgabor } 784246074Sgabor i = 0; 785246074Sgabor j = split + 1; 786246074Sgabor fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n", 787246074Sgabor pch_ptrn_lines() ? oldfirst : 0, 788246074Sgabor pch_ptrn_lines(), newfirst, pch_repl_lines()); 789246074Sgabor while (i < split || j <= pat_end) { 790246074Sgabor ch1 = i < split ? pch_char(i) : -1; 791246074Sgabor ch2 = j <= pat_end ? pch_char(j) : -1; 792246074Sgabor if (ch1 == '-') { 793246074Sgabor rej_line('-', i); 794246074Sgabor i++; 795246074Sgabor } else if (ch1 == ' ' && ch2 == ' ') { 796246074Sgabor rej_line(' ', i); 797246074Sgabor i++; 798246074Sgabor j++; 799246074Sgabor } else if (ch1 == '!' && ch2 == '!') { 800246074Sgabor while (i < split && ch1 == '!') { 801246074Sgabor rej_line('-', i); 802246074Sgabor i++; 803246074Sgabor ch1 = i < split ? pch_char(i) : -1; 804246074Sgabor } 805246074Sgabor while (j <= pat_end && ch2 == '!') { 806246074Sgabor rej_line('+', j); 807246074Sgabor j++; 808246074Sgabor ch2 = j <= pat_end ? pch_char(j) : -1; 809246074Sgabor } 810246074Sgabor } else if (ch1 == '*') { 811246074Sgabor i++; 812246074Sgabor } else if (ch2 == '+' || ch2 == ' ') { 813246074Sgabor rej_line(ch2, j); 814246074Sgabor j++; 815246074Sgabor } else { 816246074Sgabor fprintf(rejfp, "internal error on (%ld %ld %ld)\n", 817246074Sgabor i, split, j); 818246074Sgabor rej_line(ch1, i); 819246074Sgabor rej_line(ch2, j); 820246074Sgabor return; 821246074Sgabor } 822246074Sgabor } 823246074Sgabor} 824246074Sgabor 825246074Sgabor/* We found where to apply it (we hope), so do it. */ 826246074Sgabor 827246074Sgaborstatic void 828246074Sgaborapply_hunk(LINENUM where) 829246074Sgabor{ 830246074Sgabor LINENUM old = 1; 831246074Sgabor const LINENUM lastline = pch_ptrn_lines(); 832246074Sgabor LINENUM new = lastline + 1; 833246074Sgabor#define OUTSIDE 0 834246074Sgabor#define IN_IFNDEF 1 835246074Sgabor#define IN_IFDEF 2 836246074Sgabor#define IN_ELSE 3 837246074Sgabor int def_state = OUTSIDE; 838246074Sgabor const LINENUM pat_end = pch_end(); 839246074Sgabor 840246074Sgabor where--; 841246074Sgabor while (pch_char(new) == '=' || pch_char(new) == '\n') 842246074Sgabor new++; 843246074Sgabor 844246074Sgabor while (old <= lastline) { 845246074Sgabor if (pch_char(old) == '-') { 846246074Sgabor copy_till(where + old - 1, false); 847246074Sgabor if (do_defines) { 848246074Sgabor if (def_state == OUTSIDE) { 849246074Sgabor fputs(not_defined, ofp); 850246074Sgabor def_state = IN_IFNDEF; 851246074Sgabor } else if (def_state == IN_IFDEF) { 852246074Sgabor fputs(else_defined, ofp); 853246074Sgabor def_state = IN_ELSE; 854246074Sgabor } 855246074Sgabor fputs(pfetch(old), ofp); 856246074Sgabor } 857246074Sgabor last_frozen_line++; 858246074Sgabor old++; 859246074Sgabor } else if (new > pat_end) { 860246074Sgabor break; 861246074Sgabor } else if (pch_char(new) == '+') { 862246074Sgabor copy_till(where + old - 1, false); 863246074Sgabor if (do_defines) { 864246074Sgabor if (def_state == IN_IFNDEF) { 865246074Sgabor fputs(else_defined, ofp); 866246074Sgabor def_state = IN_ELSE; 867246074Sgabor } else if (def_state == OUTSIDE) { 868246074Sgabor fputs(if_defined, ofp); 869246074Sgabor def_state = IN_IFDEF; 870246074Sgabor } 871246074Sgabor } 872246074Sgabor fputs(pfetch(new), ofp); 873246074Sgabor new++; 874246074Sgabor } else if (pch_char(new) != pch_char(old)) { 875246074Sgabor say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", 876246074Sgabor pch_hunk_beg() + old, 877246074Sgabor pch_hunk_beg() + new); 878246074Sgabor#ifdef DEBUGGING 879246074Sgabor say("oldchar = '%c', newchar = '%c'\n", 880246074Sgabor pch_char(old), pch_char(new)); 881246074Sgabor#endif 882246074Sgabor my_exit(2); 883246074Sgabor } else if (pch_char(new) == '!') { 884246074Sgabor copy_till(where + old - 1, false); 885246074Sgabor if (do_defines) { 886246074Sgabor fputs(not_defined, ofp); 887246074Sgabor def_state = IN_IFNDEF; 888246074Sgabor } 889246074Sgabor while (pch_char(old) == '!') { 890246074Sgabor if (do_defines) { 891246074Sgabor fputs(pfetch(old), ofp); 892246074Sgabor } 893246074Sgabor last_frozen_line++; 894246074Sgabor old++; 895246074Sgabor } 896246074Sgabor if (do_defines) { 897246074Sgabor fputs(else_defined, ofp); 898246074Sgabor def_state = IN_ELSE; 899246074Sgabor } 900246074Sgabor while (pch_char(new) == '!') { 901246074Sgabor fputs(pfetch(new), ofp); 902246074Sgabor new++; 903246074Sgabor } 904246074Sgabor } else { 905246074Sgabor if (pch_char(new) != ' ') 906246074Sgabor fatal("Internal error: expected ' '\n"); 907246074Sgabor old++; 908246074Sgabor new++; 909246074Sgabor if (do_defines && def_state != OUTSIDE) { 910246074Sgabor fputs(end_defined, ofp); 911246074Sgabor def_state = OUTSIDE; 912246074Sgabor } 913246074Sgabor } 914246074Sgabor } 915246074Sgabor if (new <= pat_end && pch_char(new) == '+') { 916246074Sgabor copy_till(where + old - 1, false); 917246074Sgabor if (do_defines) { 918246074Sgabor if (def_state == OUTSIDE) { 919246074Sgabor fputs(if_defined, ofp); 920246074Sgabor def_state = IN_IFDEF; 921246074Sgabor } else if (def_state == IN_IFNDEF) { 922246074Sgabor fputs(else_defined, ofp); 923246074Sgabor def_state = IN_ELSE; 924246074Sgabor } 925246074Sgabor } 926246074Sgabor while (new <= pat_end && pch_char(new) == '+') { 927246074Sgabor fputs(pfetch(new), ofp); 928246074Sgabor new++; 929246074Sgabor } 930246074Sgabor } 931246074Sgabor if (do_defines && def_state != OUTSIDE) { 932246074Sgabor fputs(end_defined, ofp); 933246074Sgabor } 934246074Sgabor} 935246074Sgabor 936246074Sgabor/* 937246074Sgabor * Open the new file. 938246074Sgabor */ 939246074Sgaborstatic void 940246074Sgaborinit_output(const char *name) 941246074Sgabor{ 942246074Sgabor ofp = fopen(name, "w"); 943246074Sgabor if (ofp == NULL) 944246074Sgabor pfatal("can't create %s", name); 945246074Sgabor} 946246074Sgabor 947246074Sgabor/* 948246074Sgabor * Open a file to put hunks we can't locate. 949246074Sgabor */ 950246074Sgaborstatic void 951246074Sgaborinit_reject(const char *name) 952246074Sgabor{ 953246074Sgabor rejfp = fopen(name, "w"); 954246074Sgabor if (rejfp == NULL) 955246074Sgabor pfatal("can't create %s", name); 956246074Sgabor} 957246074Sgabor 958246074Sgabor/* 959246074Sgabor * Copy input file to output, up to wherever hunk is to be applied. 960246074Sgabor * If endoffile is true, treat the last line specially since it may 961246074Sgabor * lack a newline. 962246074Sgabor */ 963246074Sgaborstatic void 964246074Sgaborcopy_till(LINENUM lastline, bool endoffile) 965246074Sgabor{ 966246074Sgabor if (last_frozen_line > lastline) 967246074Sgabor fatal("misordered hunks! output would be garbled\n"); 968246074Sgabor while (last_frozen_line < lastline) { 969246074Sgabor if (++last_frozen_line == lastline && endoffile) 970246074Sgabor dump_line(last_frozen_line, !last_line_missing_eol); 971246074Sgabor else 972246074Sgabor dump_line(last_frozen_line, true); 973246074Sgabor } 974246074Sgabor} 975246074Sgabor 976246074Sgabor/* 977246074Sgabor * Finish copying the input file to the output file. 978246074Sgabor */ 979246074Sgaborstatic bool 980246074Sgaborspew_output(void) 981246074Sgabor{ 982246074Sgabor int rv; 983246074Sgabor 984246074Sgabor#ifdef DEBUGGING 985246074Sgabor if (debug & 256) 986246074Sgabor say("il=%ld lfl=%ld\n", input_lines, last_frozen_line); 987246074Sgabor#endif 988246074Sgabor if (input_lines) 989246074Sgabor copy_till(input_lines, true); /* dump remainder of file */ 990250975Sgjb rv = ferror(ofp) == 0 && fclose(ofp) == 0; 991246074Sgabor ofp = NULL; 992246074Sgabor return rv; 993246074Sgabor} 994246074Sgabor 995246074Sgabor/* 996246074Sgabor * Copy one line from input to output. 997246074Sgabor */ 998246074Sgaborstatic void 999246074Sgabordump_line(LINENUM line, bool write_newline) 1000246074Sgabor{ 1001246074Sgabor char *s; 1002246074Sgabor 1003246074Sgabor s = ifetch(line, 0); 1004246074Sgabor if (s == NULL) 1005246074Sgabor return; 1006246074Sgabor /* Note: string is not NUL terminated. */ 1007246074Sgabor for (; *s != '\n'; s++) 1008246074Sgabor putc(*s, ofp); 1009246074Sgabor if (write_newline) 1010246074Sgabor putc('\n', ofp); 1011246074Sgabor} 1012246074Sgabor 1013246074Sgabor/* 1014246074Sgabor * Does the patch pattern match at line base+offset? 1015246074Sgabor */ 1016246074Sgaborstatic bool 1017246074Sgaborpatch_match(LINENUM base, LINENUM offset, LINENUM fuzz) 1018246074Sgabor{ 1019246074Sgabor LINENUM pline = 1 + fuzz; 1020246074Sgabor LINENUM iline; 1021246074Sgabor LINENUM pat_lines = pch_ptrn_lines() - fuzz; 1022246074Sgabor const char *ilineptr; 1023246074Sgabor const char *plineptr; 1024267746Spfg unsigned short plinelen; 1025246074Sgabor 1026246074Sgabor for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) { 1027246074Sgabor ilineptr = ifetch(iline, offset >= 0); 1028246074Sgabor if (ilineptr == NULL) 1029246074Sgabor return false; 1030246074Sgabor plineptr = pfetch(pline); 1031246074Sgabor plinelen = pch_line_len(pline); 1032246074Sgabor if (canonicalize) { 1033246074Sgabor if (!similar(ilineptr, plineptr, plinelen)) 1034246074Sgabor return false; 1035246074Sgabor } else if (strnNE(ilineptr, plineptr, plinelen)) 1036246074Sgabor return false; 1037246074Sgabor if (iline == input_lines) { 1038246074Sgabor /* 1039246074Sgabor * We are looking at the last line of the file. 1040246074Sgabor * If the file has no eol, the patch line should 1041246074Sgabor * not have one either and vice-versa. Note that 1042246074Sgabor * plinelen > 0. 1043246074Sgabor */ 1044246074Sgabor if (last_line_missing_eol) { 1045246074Sgabor if (plineptr[plinelen - 1] == '\n') 1046246074Sgabor return false; 1047246074Sgabor } else { 1048246074Sgabor if (plineptr[plinelen - 1] != '\n') 1049246074Sgabor return false; 1050246074Sgabor } 1051246074Sgabor } 1052246074Sgabor } 1053246074Sgabor return true; 1054246074Sgabor} 1055246074Sgabor 1056246074Sgabor/* 1057246074Sgabor * Do two lines match with canonicalized white space? 1058246074Sgabor */ 1059246074Sgaborstatic bool 1060246074Sgaborsimilar(const char *a, const char *b, int len) 1061246074Sgabor{ 1062246074Sgabor while (len) { 1063246074Sgabor if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */ 1064246074Sgabor if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */ 1065246074Sgabor return false; 1066246074Sgabor while (len && isspace((unsigned char)*b) && *b != '\n') 1067246074Sgabor b++, len--; /* skip pattern whitespace */ 1068246074Sgabor while (isspace((unsigned char)*a) && *a != '\n') 1069246074Sgabor a++; /* skip target whitespace */ 1070246074Sgabor if (*a == '\n' || *b == '\n') 1071246074Sgabor return (*a == *b); /* should end in sync */ 1072246074Sgabor } else if (*a++ != *b++) /* match non-whitespace chars */ 1073246074Sgabor return false; 1074246074Sgabor else 1075246074Sgabor len--; /* probably not necessary */ 1076246074Sgabor } 1077246074Sgabor return true; /* actually, this is not reached */ 1078246074Sgabor /* since there is always a \n */ 1079246074Sgabor} 1080