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 * 26246091Sdelphij * $OpenBSD: patch.c,v 1.50 2012/05/15 19:32:02 millert 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 153246091Sdelphij setlinebuf(stdout); 154246091Sdelphij setlinebuf(stderr); 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 */ 218246091Sdelphij 219246091Sdelphij patch_seen = true; 220246074Sgabor 221246074Sgabor warn_on_invalid_line = true; 222246074Sgabor 223246074Sgabor if (outname == NULL) 224246074Sgabor outname = savestr(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 } 419246091Sdelphij 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'}, 472246074Sgabor {"ed", no_argument, 0, 'e'}, 473246074Sgabor {"force", no_argument, 0, 'f'}, 474246074Sgabor {"forward", no_argument, 0, 'N'}, 475246074Sgabor {"fuzz", required_argument, 0, 'F'}, 476246074Sgabor {"ifdef", required_argument, 0, 'D'}, 477246074Sgabor {"input", required_argument, 0, 'i'}, 478246074Sgabor {"ignore-whitespace", no_argument, 0, 'l'}, 479246074Sgabor {"normal", no_argument, 0, 'n'}, 480246074Sgabor {"output", required_argument, 0, 'o'}, 481246074Sgabor {"prefix", required_argument, 0, 'B'}, 482246074Sgabor {"quiet", no_argument, 0, 's'}, 483246074Sgabor {"reject-file", required_argument, 0, 'r'}, 484246074Sgabor {"remove-empty-files", no_argument, 0, 'E'}, 485246074Sgabor {"reverse", no_argument, 0, 'R'}, 486246074Sgabor {"silent", no_argument, 0, 's'}, 487246074Sgabor {"strip", required_argument, 0, 'p'}, 488246074Sgabor {"suffix", required_argument, 0, 'z'}, 489246074Sgabor {"unified", no_argument, 0, 'u'}, 490246074Sgabor {"version", no_argument, 0, 'v'}, 491246074Sgabor {"version-control", required_argument, 0, 'V'}, 492246074Sgabor {"posix", no_argument, &posix, 1}, 493246074Sgabor {NULL, 0, 0, 0} 494246074Sgabor }; 495246074Sgabor int ch; 496246074Sgabor 497246074Sgabor rejname[0] = '\0'; 498246074Sgabor Argc_last = Argc; 499246074Sgabor Argv_last = Argv; 500246074Sgabor if (!Argc) 501246074Sgabor return; 502246074Sgabor optreset = optind = 1; 503246074Sgabor while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) { 504246074Sgabor switch (ch) { 505246074Sgabor case 'b': 506246074Sgabor if (backup_type == none) 507246074Sgabor backup_type = numbered_existing; 508246074Sgabor if (optarg == NULL) 509246074Sgabor break; 510246074Sgabor if (verbose) 511246074Sgabor say("Warning, the ``-b suffix'' option has been" 512246074Sgabor " obsoleted by the -z option.\n"); 513246074Sgabor /* FALLTHROUGH */ 514246074Sgabor case 'z': 515246074Sgabor /* must directly follow 'b' case for backwards compat */ 516246074Sgabor simple_backup_suffix = savestr(optarg); 517246074Sgabor break; 518246074Sgabor case 'B': 519246074Sgabor origprae = savestr(optarg); 520246074Sgabor break; 521246074Sgabor case 'c': 522246074Sgabor diff_type = CONTEXT_DIFF; 523246074Sgabor break; 524246074Sgabor case 'C': 525246074Sgabor check_only = true; 526246074Sgabor break; 527246074Sgabor case 'd': 528246074Sgabor if (chdir(optarg) < 0) 529246074Sgabor pfatal("can't cd to %s", optarg); 530246074Sgabor break; 531246074Sgabor case 'D': 532246074Sgabor do_defines = true; 533246074Sgabor if (!isalpha((unsigned char)*optarg) && *optarg != '_') 534246074Sgabor fatal("argument to -D is not an identifier\n"); 535246074Sgabor snprintf(if_defined, sizeof if_defined, 536246074Sgabor "#ifdef %s\n", optarg); 537246074Sgabor snprintf(not_defined, sizeof not_defined, 538246074Sgabor "#ifndef %s\n", optarg); 539246074Sgabor snprintf(end_defined, sizeof end_defined, 540246074Sgabor "#endif /* %s */\n", optarg); 541246074Sgabor break; 542246074Sgabor case 'e': 543246074Sgabor diff_type = ED_DIFF; 544246074Sgabor break; 545246074Sgabor case 'E': 546246074Sgabor remove_empty_files = true; 547246074Sgabor break; 548246074Sgabor case 'f': 549246074Sgabor force = true; 550246074Sgabor break; 551246074Sgabor case 'F': 552246074Sgabor maxfuzz = atoi(optarg); 553246074Sgabor break; 554246074Sgabor case 'i': 555246074Sgabor if (++filec == MAXFILEC) 556246074Sgabor fatal("too many file arguments\n"); 557246074Sgabor filearg[filec] = savestr(optarg); 558246074Sgabor break; 559246074Sgabor case 'l': 560246074Sgabor canonicalize = true; 561246074Sgabor break; 562246074Sgabor case 'n': 563246074Sgabor diff_type = NORMAL_DIFF; 564246074Sgabor break; 565246074Sgabor case 'N': 566246074Sgabor noreverse = true; 567246074Sgabor break; 568246074Sgabor case 'o': 569246074Sgabor outname = savestr(optarg); 570246074Sgabor break; 571246074Sgabor case 'p': 572246074Sgabor strippath = atoi(optarg); 573246074Sgabor break; 574246074Sgabor case 'r': 575246074Sgabor if (strlcpy(rejname, optarg, 576246074Sgabor sizeof(rejname)) >= sizeof(rejname)) 577246074Sgabor fatal("argument for -r is too long\n"); 578246074Sgabor break; 579246074Sgabor case 'R': 580246074Sgabor reverse = true; 581246074Sgabor reverse_flag_specified = true; 582246074Sgabor break; 583246074Sgabor case 's': 584246074Sgabor verbose = false; 585246074Sgabor break; 586246074Sgabor case 't': 587246074Sgabor batch = true; 588246074Sgabor break; 589246074Sgabor case 'u': 590246074Sgabor diff_type = UNI_DIFF; 591246074Sgabor break; 592246074Sgabor case 'v': 593246074Sgabor version(); 594246074Sgabor break; 595246074Sgabor case 'V': 596246074Sgabor backup_type = get_version(optarg); 597246074Sgabor break; 598246074Sgabor#ifdef DEBUGGING 599246074Sgabor case 'x': 600246074Sgabor debug = atoi(optarg); 601246074Sgabor break; 602246074Sgabor#endif 603246074Sgabor default: 604246074Sgabor if (ch != '\0') 605246074Sgabor usage(); 606246074Sgabor break; 607246074Sgabor } 608246074Sgabor } 609246074Sgabor Argc -= optind; 610246074Sgabor Argv += optind; 611246074Sgabor 612246074Sgabor if (Argc > 0) { 613246074Sgabor filearg[0] = savestr(*Argv++); 614246074Sgabor Argc--; 615246074Sgabor while (Argc > 0) { 616246074Sgabor if (++filec == MAXFILEC) 617246074Sgabor fatal("too many file arguments\n"); 618246074Sgabor filearg[filec] = savestr(*Argv++); 619246074Sgabor Argc--; 620246074Sgabor } 621246074Sgabor } 622246074Sgabor 623246074Sgabor if (getenv("POSIXLY_CORRECT") != NULL) 624246074Sgabor posix = 1; 625246074Sgabor} 626246074Sgabor 627246074Sgaborstatic void 628246074Sgaborusage(void) 629246074Sgabor{ 630246074Sgabor fprintf(stderr, 631246074Sgabor"usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] [-d directory]\n" 632246074Sgabor" [-F max-fuzz] [-i patchfile] [-o out-file] [-p strip-count]\n" 633246074Sgabor" [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n" 634246074Sgabor" [--posix] [origfile [patchfile]]\n" 635246074Sgabor" patch <patchfile\n"); 636246074Sgabor my_exit(EXIT_SUCCESS); 637246074Sgabor} 638246074Sgabor 639246074Sgabor/* 640246074Sgabor * Attempt to find the right place to apply this hunk of patch. 641246074Sgabor */ 642246074Sgaborstatic LINENUM 643246074Sgaborlocate_hunk(LINENUM fuzz) 644246074Sgabor{ 645246074Sgabor LINENUM first_guess = pch_first() + last_offset; 646246074Sgabor LINENUM offset; 647246074Sgabor LINENUM pat_lines = pch_ptrn_lines(); 648246074Sgabor LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1; 649246074Sgabor LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context(); 650246074Sgabor 651246074Sgabor if (pat_lines == 0) { /* null range matches always */ 652246074Sgabor if (verbose && fuzz == 0 && (diff_type == CONTEXT_DIFF 653246074Sgabor || diff_type == NEW_CONTEXT_DIFF 654246074Sgabor || diff_type == UNI_DIFF)) { 655246074Sgabor say("Empty context always matches.\n"); 656246074Sgabor } 657246074Sgabor return (first_guess); 658246074Sgabor } 659246074Sgabor if (max_neg_offset >= first_guess) /* do not try lines < 0 */ 660246074Sgabor max_neg_offset = first_guess - 1; 661246074Sgabor if (first_guess <= input_lines && patch_match(first_guess, 0, fuzz)) 662246074Sgabor return first_guess; 663246074Sgabor for (offset = 1; ; offset++) { 664246074Sgabor bool check_after = (offset <= max_pos_offset); 665246074Sgabor bool check_before = (offset <= max_neg_offset); 666246074Sgabor 667246074Sgabor if (check_after && patch_match(first_guess, offset, fuzz)) { 668246074Sgabor#ifdef DEBUGGING 669246074Sgabor if (debug & 1) 670246074Sgabor say("Offset changing from %ld to %ld\n", 671246074Sgabor last_offset, offset); 672246074Sgabor#endif 673246074Sgabor last_offset = offset; 674246074Sgabor return first_guess + offset; 675246074Sgabor } else if (check_before && patch_match(first_guess, -offset, fuzz)) { 676246074Sgabor#ifdef DEBUGGING 677246074Sgabor if (debug & 1) 678246074Sgabor say("Offset changing from %ld to %ld\n", 679246074Sgabor last_offset, -offset); 680246074Sgabor#endif 681246074Sgabor last_offset = -offset; 682246074Sgabor return first_guess - offset; 683246074Sgabor } else if (!check_before && !check_after) 684246074Sgabor return 0; 685246074Sgabor } 686246074Sgabor} 687246074Sgabor 688246074Sgabor/* We did not find the pattern, dump out the hunk so they can handle it. */ 689246074Sgabor 690246074Sgaborstatic void 691246074Sgaborabort_context_hunk(void) 692246074Sgabor{ 693246074Sgabor LINENUM i; 694246074Sgabor const LINENUM pat_end = pch_end(); 695246074Sgabor /* 696246074Sgabor * add in last_offset to guess the same as the previous successful 697246074Sgabor * hunk 698246074Sgabor */ 699246074Sgabor const LINENUM oldfirst = pch_first() + last_offset; 700246074Sgabor const LINENUM newfirst = pch_newfirst() + last_offset; 701246074Sgabor const LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; 702246074Sgabor const LINENUM newlast = newfirst + pch_repl_lines() - 1; 703246074Sgabor const char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : ""); 704246074Sgabor const char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----"); 705246074Sgabor 706246074Sgabor fprintf(rejfp, "***************\n"); 707246074Sgabor for (i = 0; i <= pat_end; i++) { 708246074Sgabor switch (pch_char(i)) { 709246074Sgabor case '*': 710246074Sgabor if (oldlast < oldfirst) 711246074Sgabor fprintf(rejfp, "*** 0%s\n", stars); 712246074Sgabor else if (oldlast == oldfirst) 713246074Sgabor fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); 714246074Sgabor else 715246074Sgabor fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, 716246074Sgabor oldlast, stars); 717246074Sgabor break; 718246074Sgabor case '=': 719246074Sgabor if (newlast < newfirst) 720246074Sgabor fprintf(rejfp, "--- 0%s\n", minuses); 721246074Sgabor else if (newlast == newfirst) 722246074Sgabor fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); 723246074Sgabor else 724246074Sgabor fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, 725246074Sgabor newlast, minuses); 726246074Sgabor break; 727246074Sgabor case '\n': 728246074Sgabor fprintf(rejfp, "%s", pfetch(i)); 729246074Sgabor break; 730246074Sgabor case ' ': 731246074Sgabor case '-': 732246074Sgabor case '+': 733246074Sgabor case '!': 734246074Sgabor fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); 735246074Sgabor break; 736246074Sgabor default: 737246074Sgabor fatal("fatal internal error in abort_context_hunk\n"); 738246074Sgabor } 739246074Sgabor } 740246074Sgabor} 741246074Sgabor 742246074Sgaborstatic void 743246074Sgaborrej_line(int ch, LINENUM i) 744246074Sgabor{ 745246074Sgabor size_t len; 746246074Sgabor const char *line = pfetch(i); 747246074Sgabor 748246074Sgabor len = strlen(line); 749246074Sgabor 750246074Sgabor fprintf(rejfp, "%c%s", ch, line); 751246074Sgabor if (len == 0 || line[len-1] != '\n') 752246074Sgabor fprintf(rejfp, "\n\\ No newline at end of file\n"); 753246074Sgabor} 754246074Sgabor 755246074Sgaborstatic void 756246074Sgaborabort_hunk(void) 757246074Sgabor{ 758246074Sgabor LINENUM i, j, split; 759246074Sgabor int ch1, ch2; 760246074Sgabor const LINENUM pat_end = pch_end(); 761246074Sgabor const LINENUM oldfirst = pch_first() + last_offset; 762246074Sgabor const LINENUM newfirst = pch_newfirst() + last_offset; 763246074Sgabor 764246074Sgabor if (diff_type != UNI_DIFF) { 765246074Sgabor abort_context_hunk(); 766246074Sgabor return; 767246074Sgabor } 768246074Sgabor split = -1; 769246074Sgabor for (i = 0; i <= pat_end; i++) { 770246074Sgabor if (pch_char(i) == '=') { 771246074Sgabor split = i; 772246074Sgabor break; 773246074Sgabor } 774246074Sgabor } 775246074Sgabor if (split == -1) { 776246074Sgabor fprintf(rejfp, "malformed hunk: no split found\n"); 777246074Sgabor return; 778246074Sgabor } 779246074Sgabor i = 0; 780246074Sgabor j = split + 1; 781246074Sgabor fprintf(rejfp, "@@ -%ld,%ld +%ld,%ld @@\n", 782246074Sgabor pch_ptrn_lines() ? oldfirst : 0, 783246074Sgabor pch_ptrn_lines(), newfirst, pch_repl_lines()); 784246074Sgabor while (i < split || j <= pat_end) { 785246074Sgabor ch1 = i < split ? pch_char(i) : -1; 786246074Sgabor ch2 = j <= pat_end ? pch_char(j) : -1; 787246074Sgabor if (ch1 == '-') { 788246074Sgabor rej_line('-', i); 789246074Sgabor i++; 790246074Sgabor } else if (ch1 == ' ' && ch2 == ' ') { 791246074Sgabor rej_line(' ', i); 792246074Sgabor i++; 793246074Sgabor j++; 794246074Sgabor } else if (ch1 == '!' && ch2 == '!') { 795246074Sgabor while (i < split && ch1 == '!') { 796246074Sgabor rej_line('-', i); 797246074Sgabor i++; 798246074Sgabor ch1 = i < split ? pch_char(i) : -1; 799246074Sgabor } 800246074Sgabor while (j <= pat_end && ch2 == '!') { 801246074Sgabor rej_line('+', j); 802246074Sgabor j++; 803246074Sgabor ch2 = j <= pat_end ? pch_char(j) : -1; 804246074Sgabor } 805246074Sgabor } else if (ch1 == '*') { 806246074Sgabor i++; 807246074Sgabor } else if (ch2 == '+' || ch2 == ' ') { 808246074Sgabor rej_line(ch2, j); 809246074Sgabor j++; 810246074Sgabor } else { 811246074Sgabor fprintf(rejfp, "internal error on (%ld %ld %ld)\n", 812246074Sgabor i, split, j); 813246074Sgabor rej_line(ch1, i); 814246074Sgabor rej_line(ch2, j); 815246074Sgabor return; 816246074Sgabor } 817246074Sgabor } 818246074Sgabor} 819246074Sgabor 820246074Sgabor/* We found where to apply it (we hope), so do it. */ 821246074Sgabor 822246074Sgaborstatic void 823246074Sgaborapply_hunk(LINENUM where) 824246074Sgabor{ 825246074Sgabor LINENUM old = 1; 826246074Sgabor const LINENUM lastline = pch_ptrn_lines(); 827246074Sgabor LINENUM new = lastline + 1; 828246074Sgabor#define OUTSIDE 0 829246074Sgabor#define IN_IFNDEF 1 830246074Sgabor#define IN_IFDEF 2 831246074Sgabor#define IN_ELSE 3 832246074Sgabor int def_state = OUTSIDE; 833246074Sgabor const LINENUM pat_end = pch_end(); 834246074Sgabor 835246074Sgabor where--; 836246074Sgabor while (pch_char(new) == '=' || pch_char(new) == '\n') 837246074Sgabor new++; 838246074Sgabor 839246074Sgabor while (old <= lastline) { 840246074Sgabor if (pch_char(old) == '-') { 841246074Sgabor copy_till(where + old - 1, false); 842246074Sgabor if (do_defines) { 843246074Sgabor if (def_state == OUTSIDE) { 844246074Sgabor fputs(not_defined, ofp); 845246074Sgabor def_state = IN_IFNDEF; 846246074Sgabor } else if (def_state == IN_IFDEF) { 847246074Sgabor fputs(else_defined, ofp); 848246074Sgabor def_state = IN_ELSE; 849246074Sgabor } 850246074Sgabor fputs(pfetch(old), ofp); 851246074Sgabor } 852246074Sgabor last_frozen_line++; 853246074Sgabor old++; 854246074Sgabor } else if (new > pat_end) { 855246074Sgabor break; 856246074Sgabor } else if (pch_char(new) == '+') { 857246074Sgabor copy_till(where + old - 1, false); 858246074Sgabor if (do_defines) { 859246074Sgabor if (def_state == IN_IFNDEF) { 860246074Sgabor fputs(else_defined, ofp); 861246074Sgabor def_state = IN_ELSE; 862246074Sgabor } else if (def_state == OUTSIDE) { 863246074Sgabor fputs(if_defined, ofp); 864246074Sgabor def_state = IN_IFDEF; 865246074Sgabor } 866246074Sgabor } 867246074Sgabor fputs(pfetch(new), ofp); 868246074Sgabor new++; 869246074Sgabor } else if (pch_char(new) != pch_char(old)) { 870246074Sgabor say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", 871246074Sgabor pch_hunk_beg() + old, 872246074Sgabor pch_hunk_beg() + new); 873246074Sgabor#ifdef DEBUGGING 874246074Sgabor say("oldchar = '%c', newchar = '%c'\n", 875246074Sgabor pch_char(old), pch_char(new)); 876246074Sgabor#endif 877246074Sgabor my_exit(2); 878246074Sgabor } else if (pch_char(new) == '!') { 879246074Sgabor copy_till(where + old - 1, false); 880246074Sgabor if (do_defines) { 881246074Sgabor fputs(not_defined, ofp); 882246074Sgabor def_state = IN_IFNDEF; 883246074Sgabor } 884246074Sgabor while (pch_char(old) == '!') { 885246074Sgabor if (do_defines) { 886246074Sgabor fputs(pfetch(old), ofp); 887246074Sgabor } 888246074Sgabor last_frozen_line++; 889246074Sgabor old++; 890246074Sgabor } 891246074Sgabor if (do_defines) { 892246074Sgabor fputs(else_defined, ofp); 893246074Sgabor def_state = IN_ELSE; 894246074Sgabor } 895246074Sgabor while (pch_char(new) == '!') { 896246074Sgabor fputs(pfetch(new), ofp); 897246074Sgabor new++; 898246074Sgabor } 899246074Sgabor } else { 900246074Sgabor if (pch_char(new) != ' ') 901246074Sgabor fatal("Internal error: expected ' '\n"); 902246074Sgabor old++; 903246074Sgabor new++; 904246074Sgabor if (do_defines && def_state != OUTSIDE) { 905246074Sgabor fputs(end_defined, ofp); 906246074Sgabor def_state = OUTSIDE; 907246074Sgabor } 908246074Sgabor } 909246074Sgabor } 910246074Sgabor if (new <= pat_end && pch_char(new) == '+') { 911246074Sgabor copy_till(where + old - 1, false); 912246074Sgabor if (do_defines) { 913246074Sgabor if (def_state == OUTSIDE) { 914246074Sgabor fputs(if_defined, ofp); 915246074Sgabor def_state = IN_IFDEF; 916246074Sgabor } else if (def_state == IN_IFNDEF) { 917246074Sgabor fputs(else_defined, ofp); 918246074Sgabor def_state = IN_ELSE; 919246074Sgabor } 920246074Sgabor } 921246074Sgabor while (new <= pat_end && pch_char(new) == '+') { 922246074Sgabor fputs(pfetch(new), ofp); 923246074Sgabor new++; 924246074Sgabor } 925246074Sgabor } 926246074Sgabor if (do_defines && def_state != OUTSIDE) { 927246074Sgabor fputs(end_defined, ofp); 928246074Sgabor } 929246074Sgabor} 930246074Sgabor 931246074Sgabor/* 932246074Sgabor * Open the new file. 933246074Sgabor */ 934246074Sgaborstatic void 935246074Sgaborinit_output(const char *name) 936246074Sgabor{ 937246074Sgabor ofp = fopen(name, "w"); 938246074Sgabor if (ofp == NULL) 939246074Sgabor pfatal("can't create %s", name); 940246074Sgabor} 941246074Sgabor 942246074Sgabor/* 943246074Sgabor * Open a file to put hunks we can't locate. 944246074Sgabor */ 945246074Sgaborstatic void 946246074Sgaborinit_reject(const char *name) 947246074Sgabor{ 948246074Sgabor rejfp = fopen(name, "w"); 949246074Sgabor if (rejfp == NULL) 950246074Sgabor pfatal("can't create %s", name); 951246074Sgabor} 952246074Sgabor 953246074Sgabor/* 954246074Sgabor * Copy input file to output, up to wherever hunk is to be applied. 955246074Sgabor * If endoffile is true, treat the last line specially since it may 956246074Sgabor * lack a newline. 957246074Sgabor */ 958246074Sgaborstatic void 959246074Sgaborcopy_till(LINENUM lastline, bool endoffile) 960246074Sgabor{ 961246074Sgabor if (last_frozen_line > lastline) 962246074Sgabor fatal("misordered hunks! output would be garbled\n"); 963246074Sgabor while (last_frozen_line < lastline) { 964246074Sgabor if (++last_frozen_line == lastline && endoffile) 965246074Sgabor dump_line(last_frozen_line, !last_line_missing_eol); 966246074Sgabor else 967246074Sgabor dump_line(last_frozen_line, true); 968246074Sgabor } 969246074Sgabor} 970246074Sgabor 971246074Sgabor/* 972246074Sgabor * Finish copying the input file to the output file. 973246074Sgabor */ 974246074Sgaborstatic bool 975246074Sgaborspew_output(void) 976246074Sgabor{ 977246074Sgabor int rv; 978246074Sgabor 979246074Sgabor#ifdef DEBUGGING 980246074Sgabor if (debug & 256) 981246074Sgabor say("il=%ld lfl=%ld\n", input_lines, last_frozen_line); 982246074Sgabor#endif 983246074Sgabor if (input_lines) 984246074Sgabor copy_till(input_lines, true); /* dump remainder of file */ 985250975Sgjb rv = ferror(ofp) == 0 && fclose(ofp) == 0; 986246074Sgabor ofp = NULL; 987246074Sgabor return rv; 988246074Sgabor} 989246074Sgabor 990246074Sgabor/* 991246074Sgabor * Copy one line from input to output. 992246074Sgabor */ 993246074Sgaborstatic void 994246074Sgabordump_line(LINENUM line, bool write_newline) 995246074Sgabor{ 996246074Sgabor char *s; 997246074Sgabor 998246074Sgabor s = ifetch(line, 0); 999246074Sgabor if (s == NULL) 1000246074Sgabor return; 1001246074Sgabor /* Note: string is not NUL terminated. */ 1002246074Sgabor for (; *s != '\n'; s++) 1003246074Sgabor putc(*s, ofp); 1004246074Sgabor if (write_newline) 1005246074Sgabor putc('\n', ofp); 1006246074Sgabor} 1007246074Sgabor 1008246074Sgabor/* 1009246074Sgabor * Does the patch pattern match at line base+offset? 1010246074Sgabor */ 1011246074Sgaborstatic bool 1012246074Sgaborpatch_match(LINENUM base, LINENUM offset, LINENUM fuzz) 1013246074Sgabor{ 1014246074Sgabor LINENUM pline = 1 + fuzz; 1015246074Sgabor LINENUM iline; 1016246074Sgabor LINENUM pat_lines = pch_ptrn_lines() - fuzz; 1017246074Sgabor const char *ilineptr; 1018246074Sgabor const char *plineptr; 1019246074Sgabor short plinelen; 1020246074Sgabor 1021246074Sgabor for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) { 1022246074Sgabor ilineptr = ifetch(iline, offset >= 0); 1023246074Sgabor if (ilineptr == NULL) 1024246074Sgabor return false; 1025246074Sgabor plineptr = pfetch(pline); 1026246074Sgabor plinelen = pch_line_len(pline); 1027246074Sgabor if (canonicalize) { 1028246074Sgabor if (!similar(ilineptr, plineptr, plinelen)) 1029246074Sgabor return false; 1030246074Sgabor } else if (strnNE(ilineptr, plineptr, plinelen)) 1031246074Sgabor return false; 1032246074Sgabor if (iline == input_lines) { 1033246074Sgabor /* 1034246074Sgabor * We are looking at the last line of the file. 1035246074Sgabor * If the file has no eol, the patch line should 1036246074Sgabor * not have one either and vice-versa. Note that 1037246074Sgabor * plinelen > 0. 1038246074Sgabor */ 1039246074Sgabor if (last_line_missing_eol) { 1040246074Sgabor if (plineptr[plinelen - 1] == '\n') 1041246074Sgabor return false; 1042246074Sgabor } else { 1043246074Sgabor if (plineptr[plinelen - 1] != '\n') 1044246074Sgabor return false; 1045246074Sgabor } 1046246074Sgabor } 1047246074Sgabor } 1048246074Sgabor return true; 1049246074Sgabor} 1050246074Sgabor 1051246074Sgabor/* 1052246074Sgabor * Do two lines match with canonicalized white space? 1053246074Sgabor */ 1054246074Sgaborstatic bool 1055246074Sgaborsimilar(const char *a, const char *b, int len) 1056246074Sgabor{ 1057246074Sgabor while (len) { 1058246074Sgabor if (isspace((unsigned char)*b)) { /* whitespace (or \n) to match? */ 1059246074Sgabor if (!isspace((unsigned char)*a)) /* no corresponding whitespace? */ 1060246074Sgabor return false; 1061246074Sgabor while (len && isspace((unsigned char)*b) && *b != '\n') 1062246074Sgabor b++, len--; /* skip pattern whitespace */ 1063246074Sgabor while (isspace((unsigned char)*a) && *a != '\n') 1064246074Sgabor a++; /* skip target whitespace */ 1065246074Sgabor if (*a == '\n' || *b == '\n') 1066246074Sgabor return (*a == *b); /* should end in sync */ 1067246074Sgabor } else if (*a++ != *b++) /* match non-whitespace chars */ 1068246074Sgabor return false; 1069246074Sgabor else 1070246074Sgabor len--; /* probably not necessary */ 1071246074Sgabor } 1072246074Sgabor return true; /* actually, this is not reached */ 1073246074Sgabor /* since there is always a \n */ 1074246074Sgabor} 1075