1/* patch - a program to apply diffs to original files */ 2 3/* $Id: patch.c 8008 2004-06-16 21:22:10Z korli $ */ 4 5/* Copyright 1984, 1985-1987, 1988 Larry Wall 6 Copyright 1989, 1990-1993, 1997-1998, 1999 Free Software Foundation, Inc. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 2, or (at your option) 11 any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; see the file COPYING. 20 If not, write to the Free Software Foundation, 21 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 22 23#define XTERN 24#include <common.h> 25#undef XTERN 26#define XTERN extern 27#include <argmatch.h> 28#include <backupfile.h> 29#include <getopt.h> 30#include <inp.h> 31#include <pch.h> 32#include <quotearg.h> 33#include <util.h> 34#include <version.h> 35#include <xalloc.h> 36 37#if HAVE_UTIME_H 38# include <utime.h> 39#endif 40/* Some nonstandard hosts don't declare this structure even in <utime.h>. */ 41#if ! HAVE_STRUCT_UTIMBUF 42struct utimbuf 43{ 44 time_t actime; 45 time_t modtime; 46}; 47#endif 48 49/* Output stream state. */ 50struct outstate 51{ 52 FILE *ofp; 53 int after_newline; 54 int zero_output; 55}; 56 57/* procedures */ 58 59static FILE *create_output_file PARAMS ((char const *, int)); 60static LINENUM locate_hunk PARAMS ((LINENUM)); 61static bool apply_hunk PARAMS ((struct outstate *, LINENUM)); 62static bool copy_till PARAMS ((struct outstate *, LINENUM)); 63static bool patch_match PARAMS ((LINENUM, LINENUM, LINENUM, LINENUM)); 64static bool similar PARAMS ((char const *, size_t, char const *, size_t)); 65static bool spew_output PARAMS ((struct outstate *)); 66static char const *make_temp PARAMS ((int)); 67static int numeric_string PARAMS ((char const *, int, char const *)); 68static void abort_hunk PARAMS ((void)); 69static void cleanup PARAMS ((void)); 70static void get_some_switches PARAMS ((void)); 71static void init_output PARAMS ((char const *, int, struct outstate *)); 72static void init_reject PARAMS ((void)); 73static void reinitialize_almost_everything PARAMS ((void)); 74static void remove_if_needed PARAMS ((char const *, int volatile *)); 75static void usage PARAMS ((FILE *, int)) __attribute__((noreturn)); 76 77static int make_backups; 78static int backup_if_mismatch; 79static char const *version_control; 80static char const *version_control_context; 81static int remove_empty_files; 82 83/* TRUE if -R was specified on command line. */ 84static int reverse_flag_specified; 85 86/* how many input lines have been irretractably output */ 87static LINENUM last_frozen_line; 88 89static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */ 90static char const if_defined[] = "\n#ifdef %s\n"; 91static char const not_defined[] = "#ifndef %s\n"; 92static char const else_defined[] = "\n#else\n"; 93static char const end_defined[] = "\n#endif /* %s */\n"; 94 95static int Argc; 96static char * const *Argv; 97 98static FILE *rejfp; /* reject file pointer */ 99 100static char const *patchname; 101static char *rejname; 102static char const * volatile TMPREJNAME; 103static int volatile TMPREJNAME_needs_removal; 104 105static LINENUM last_offset; 106static LINENUM maxfuzz = 2; 107 108static char serrbuf[BUFSIZ]; 109 110char const program_name[] = "patch"; 111 112/* Apply a set of diffs as appropriate. */ 113 114int main PARAMS ((int, char **)); 115 116int 117main (int argc, char **argv) 118{ 119 char const *val; 120 bool somefailed = FALSE; 121 struct outstate outstate; 122 char numbuf[LINENUM_LENGTH_BOUND + 1]; 123 124 init_time (); 125 126 setbuf(stderr, serrbuf); 127 128 xalloc_fail_func = memory_fatal; 129 bufsize = 8 * 1024; 130 buf = xmalloc (bufsize); 131 132 strippath = -1; 133 134 val = getenv ("QUOTING_STYLE"); 135 { 136 int i = val ? argmatch (val, quoting_style_args, 0, 0) : -1; 137 set_quoting_style ((struct quoting_options *) 0, 138 i < 0 ? shell_quoting_style : (enum quoting_style) i); 139 } 140 141 posixly_correct = getenv ("POSIXLY_CORRECT") != 0; 142 backup_if_mismatch = ! posixly_correct; 143 patch_get = ((val = getenv ("PATCH_GET")) 144 ? numeric_string (val, 1, "PATCH_GET value") 145 : posixly_correct - 1); 146 147 val = getenv ("SIMPLE_BACKUP_SUFFIX"); 148 if (val && *val) 149 simple_backup_suffix = val; 150 151 if ((version_control = getenv ("PATCH_VERSION_CONTROL"))) 152 version_control_context = "$PATCH_VERSION_CONTROL"; 153 else if ((version_control = getenv ("VERSION_CONTROL"))) 154 version_control_context = "$VERSION_CONTROL"; 155 156 /* Cons up the names of the global temporary files. 157 Do this before `cleanup' can possibly be called (e.g. by `pfatal'). */ 158 TMPOUTNAME = make_temp ('o'); 159 TMPINNAME = make_temp ('i'); 160 TMPREJNAME = make_temp ('r'); 161 TMPPATNAME = make_temp ('p'); 162 163 /* parse switches */ 164 Argc = argc; 165 Argv = argv; 166 get_some_switches(); 167 168 if (make_backups | backup_if_mismatch) 169 backup_type = get_version (version_control_context, version_control); 170 171 init_output (outfile, 0, &outstate); 172 173 /* Make sure we clean up in case of disaster. */ 174 set_signals(0); 175 176 for ( 177 open_patch_file (patchname); 178 there_is_another_patch(); 179 reinitialize_almost_everything() 180 ) { /* for each patch in patch file */ 181 int hunk = 0; 182 int failed = 0; 183 int mismatch = 0; 184 char *outname = outfile ? outfile : inname; 185 186 if (!skip_rest_of_patch) 187 get_input_file (inname, outname); 188 189 if (diff_type == ED_DIFF) { 190 outstate.zero_output = 0; 191 if (! dry_run) 192 { 193 do_ed_script (outstate.ofp); 194 if (! outfile) 195 { 196 struct stat statbuf; 197 if (stat (TMPOUTNAME, &statbuf) != 0) 198 pfatal ("%s", TMPOUTNAME); 199 outstate.zero_output = statbuf.st_size == 0; 200 } 201 } 202 } else { 203 int got_hunk; 204 int apply_anyway = 0; 205 206 /* initialize the patched file */ 207 if (! skip_rest_of_patch && ! outfile) 208 { 209 int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL; 210 TMPOUTNAME_needs_removal = 1; 211 init_output (TMPOUTNAME, exclusive, &outstate); 212 } 213 214 /* initialize reject file */ 215 init_reject (); 216 217 /* find out where all the lines are */ 218 if (!skip_rest_of_patch) 219 scan_input (inname); 220 221 /* from here on, open no standard i/o files, because malloc */ 222 /* might misfire and we can't catch it easily */ 223 224 /* apply each hunk of patch */ 225 while (0 < (got_hunk = another_hunk (diff_type, reverse))) { 226 LINENUM where = 0; /* Pacify `gcc -Wall'. */ 227 LINENUM newwhere; 228 LINENUM fuzz = 0; 229 LINENUM prefix_context = pch_prefix_context (); 230 LINENUM suffix_context = pch_suffix_context (); 231 LINENUM context = (prefix_context < suffix_context 232 ? suffix_context : prefix_context); 233 LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context); 234 hunk++; 235 if (!skip_rest_of_patch) { 236 do { 237 where = locate_hunk(fuzz); 238 if (! where || fuzz || last_offset) 239 mismatch = 1; 240 if (hunk == 1 && ! where && ! (force | apply_anyway) 241 && reverse == reverse_flag_specified) { 242 /* dwim for reversed patch? */ 243 if (!pch_swap()) { 244 say ( 245"Not enough memory to try swapped hunk! Assuming unswapped.\n"); 246 continue; 247 } 248 /* Try again. */ 249 where = locate_hunk (fuzz); 250 if (where 251 && (ok_to_reverse 252 ("%s patch detected!", 253 (reverse 254 ? "Unreversed" 255 : "Reversed (or previously applied)")))) 256 reverse ^= 1; 257 else 258 { 259 /* Put it back to normal. */ 260 if (! pch_swap ()) 261 fatal ("lost hunk on alloc error!"); 262 if (where) 263 { 264 apply_anyway = 1; 265 fuzz--; /* Undo `++fuzz' below. */ 266 where = 0; 267 } 268 } 269 } 270 } while (!skip_rest_of_patch && !where 271 && ++fuzz <= mymaxfuzz); 272 273 if (skip_rest_of_patch) { /* just got decided */ 274 if (outstate.ofp && ! outfile) 275 { 276 fclose (outstate.ofp); 277 outstate.ofp = 0; 278 } 279 } 280 } 281 282 newwhere = pch_newfirst() + last_offset; 283 if (skip_rest_of_patch) { 284 abort_hunk(); 285 failed++; 286 if (verbosity == VERBOSE) 287 say ("Hunk #%d ignored at %s.\n", hunk, 288 format_linenum (numbuf, newwhere)); 289 } 290 else if (!where 291 || (where == 1 && pch_says_nonexistent (reverse) == 2 292 && instat.st_size)) { 293 294 if (where) 295 say ("Patch attempted to create file %s, which already exists.\n", 296 quotearg (inname)); 297 298 abort_hunk(); 299 failed++; 300 if (verbosity != SILENT) 301 say ("Hunk #%d FAILED at %s.\n", hunk, 302 format_linenum (numbuf, newwhere)); 303 } 304 else if (! apply_hunk (&outstate, where)) { 305 abort_hunk (); 306 failed++; 307 if (verbosity != SILENT) 308 say ("Hunk #%d FAILED at %s.\n", hunk, 309 format_linenum (numbuf, newwhere)); 310 } else { 311 if (verbosity == VERBOSE 312 || (verbosity != SILENT && (fuzz || last_offset))) { 313 say ("Hunk #%d succeeded at %s", hunk, 314 format_linenum (numbuf, newwhere)); 315 if (fuzz) 316 say (" with fuzz %s", format_linenum (numbuf, fuzz)); 317 if (last_offset) 318 say (" (offset %s line%s)", 319 format_linenum (numbuf, last_offset), 320 "s" + (last_offset == 1)); 321 say (".\n"); 322 } 323 } 324 } 325 326 if (!skip_rest_of_patch) 327 { 328 if (got_hunk < 0 && using_plan_a) 329 { 330 if (outfile) 331 fatal ("out of memory using Plan A"); 332 say ("\n\nRan out of memory using Plan A -- trying again...\n\n"); 333 if (outstate.ofp) 334 { 335 fclose (outstate.ofp); 336 outstate.ofp = 0; 337 } 338 fclose (rejfp); 339 continue; 340 } 341 342 /* Finish spewing out the new file. */ 343 assert (hunk); 344 if (! spew_output (&outstate)) 345 { 346 say ("Skipping patch.\n"); 347 skip_rest_of_patch = TRUE; 348 } 349 } 350 } 351 352 /* and put the output where desired */ 353 ignore_signals (); 354 if (! skip_rest_of_patch && ! outfile) { 355 if (outstate.zero_output 356 && (remove_empty_files 357 || (pch_says_nonexistent (reverse ^ 1) == 2 358 && ! posixly_correct))) 359 { 360 if (verbosity == VERBOSE) 361 say ("Removing file %s%s\n", quotearg (outname), 362 dry_run ? " and any empty ancestor directories" : ""); 363 if (! dry_run) 364 { 365 move_file ((char *) 0, (int *) 0, outname, (mode_t) 0, 366 (make_backups 367 || (backup_if_mismatch && (mismatch | failed)))); 368 removedirs (outname); 369 } 370 } 371 else 372 { 373 if (! outstate.zero_output 374 && pch_says_nonexistent (reverse ^ 1)) 375 { 376 mismatch = 1; 377 if (verbosity != SILENT) 378 say ("File %s is not empty after patch, as expected\n", 379 quotearg (outname)); 380 } 381 382 if (! dry_run) 383 { 384 time_t t; 385 386 move_file (TMPOUTNAME, &TMPOUTNAME_needs_removal, 387 outname, instat.st_mode, 388 (make_backups 389 || (backup_if_mismatch && (mismatch | failed)))); 390 391 if ((set_time | set_utc) 392 && (t = pch_timestamp (reverse ^ 1)) != (time_t) -1) 393 { 394 struct utimbuf utimbuf; 395 utimbuf.actime = utimbuf.modtime = t; 396 397 if (! force && ! inerrno 398 && pch_says_nonexistent (reverse) != 2 399 && (t = pch_timestamp (reverse)) != (time_t) -1 400 && t != instat.st_mtime) 401 say ("Not setting time of file %s (time mismatch)\n", 402 quotearg (outname)); 403 else if (! force && (mismatch | failed)) 404 say ("Not setting time of file %s (contents mismatch)\n", 405 quotearg (outname)); 406 else if (utime (outname, &utimbuf) != 0) 407 pfatal ("Can't set timestamp on file %s", 408 quotearg (outname)); 409 } 410 411 if (! inerrno && chmod (outname, instat.st_mode) != 0) 412 pfatal ("Can't set permissions on file %s", 413 quotearg (outname)); 414 } 415 } 416 } 417 if (diff_type != ED_DIFF) { 418 if (fclose (rejfp) != 0) 419 write_fatal (); 420 if (failed) { 421 somefailed = TRUE; 422 say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1), 423 skip_rest_of_patch ? "ignored" : "FAILED"); 424 if (outname) { 425 char *rej = rejname; 426 if (!rejname) { 427 rej = xmalloc (strlen (outname) + 5); 428 strcpy (rej, outname); 429 addext (rej, ".rej", '#'); 430 } 431 say (" -- saving rejects to file %s", quotearg (rej)); 432 if (! dry_run) 433 { 434 move_file (TMPREJNAME, &TMPREJNAME_needs_removal, 435 rej, instat.st_mode, FALSE); 436 if (! inerrno 437 && (chmod (rej, (instat.st_mode 438 & ~(S_IXUSR|S_IXGRP|S_IXOTH))) 439 != 0)) 440 pfatal ("can't set permissions on file %s", 441 quotearg (rej)); 442 } 443 if (!rejname) 444 free (rej); 445 } 446 say ("\n"); 447 } 448 } 449 set_signals (1); 450 } 451 if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0)) 452 write_fatal (); 453 cleanup (); 454 if (somefailed) 455 exit (1); 456 return 0; 457} 458 459/* Prepare to find the next patch to do in the patch file. */ 460 461static void 462reinitialize_almost_everything (void) 463{ 464 re_patch(); 465 re_input(); 466 467 input_lines = 0; 468 last_frozen_line = 0; 469 470 if (inname) { 471 free (inname); 472 inname = 0; 473 } 474 475 last_offset = 0; 476 477 diff_type = NO_DIFF; 478 479 if (revision) { 480 free(revision); 481 revision = 0; 482 } 483 484 reverse = reverse_flag_specified; 485 skip_rest_of_patch = FALSE; 486} 487 488static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z"; 489static struct option const longopts[] = 490{ 491 {"backup", no_argument, NULL, 'b'}, 492 {"prefix", required_argument, NULL, 'B'}, 493 {"context", no_argument, NULL, 'c'}, 494 {"directory", required_argument, NULL, 'd'}, 495 {"ifdef", required_argument, NULL, 'D'}, 496 {"ed", no_argument, NULL, 'e'}, 497 {"remove-empty-files", no_argument, NULL, 'E'}, 498 {"force", no_argument, NULL, 'f'}, 499 {"fuzz", required_argument, NULL, 'F'}, 500 {"get", no_argument, NULL, 'g'}, 501 {"input", required_argument, NULL, 'i'}, 502 {"ignore-whitespace", no_argument, NULL, 'l'}, 503 {"normal", no_argument, NULL, 'n'}, 504 {"forward", no_argument, NULL, 'N'}, 505 {"output", required_argument, NULL, 'o'}, 506 {"strip", required_argument, NULL, 'p'}, 507 {"reject-file", required_argument, NULL, 'r'}, 508 {"reverse", no_argument, NULL, 'R'}, 509 {"quiet", no_argument, NULL, 's'}, 510 {"silent", no_argument, NULL, 's'}, 511 {"batch", no_argument, NULL, 't'}, 512 {"set-time", no_argument, NULL, 'T'}, 513 {"unified", no_argument, NULL, 'u'}, 514 {"version", no_argument, NULL, 'v'}, 515 {"version-control", required_argument, NULL, 'V'}, 516 {"debug", required_argument, NULL, 'x'}, 517 {"basename-prefix", required_argument, NULL, 'Y'}, 518 {"suffix", required_argument, NULL, 'z'}, 519 {"set-utc", no_argument, NULL, 'Z'}, 520 {"dry-run", no_argument, NULL, CHAR_MAX + 1}, 521 {"verbose", no_argument, NULL, CHAR_MAX + 2}, 522 {"binary", no_argument, NULL, CHAR_MAX + 3}, 523 {"help", no_argument, NULL, CHAR_MAX + 4}, 524 {"backup-if-mismatch", no_argument, NULL, CHAR_MAX + 5}, 525 {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6}, 526 {"posix", no_argument, NULL, CHAR_MAX + 7}, 527 {"quoting-style", required_argument, NULL, CHAR_MAX + 8}, 528 {NULL, no_argument, NULL, 0} 529}; 530 531static char const *const option_help[] = 532{ 533"Input options:", 534"", 535" -p NUM --strip=NUM Strip NUM leading components from file names.", 536" -F LINES --fuzz LINES Set the fuzz factor to LINES for inexact matching.", 537" -l --ignore-whitespace Ignore white space changes between patch and input.", 538"", 539" -c --context Interpret the patch as a context difference.", 540" -e --ed Interpret the patch as an ed script.", 541" -n --normal Interpret the patch as a normal difference.", 542" -u --unified Interpret the patch as a unified difference.", 543"", 544" -N --forward Ignore patches that appear to be reversed or already applied.", 545" -R --reverse Assume patches were created with old and new files swapped.", 546"", 547" -i PATCHFILE --input=PATCHFILE Read patch from PATCHFILE instead of stdin.", 548"", 549"Output options:", 550"", 551" -o FILE --output=FILE Output patched files to FILE.", 552" -r FILE --reject-file=FILE Output rejects to FILE.", 553"", 554" -D NAME --ifdef=NAME Make merged if-then-else output using NAME.", 555" -E --remove-empty-files Remove output files that are empty after patching.", 556"", 557" -Z --set-utc Set times of patched files, assuming diff uses UTC (GMT).", 558" -T --set-time Likewise, assuming local time.", 559"", 560" --quoting-style=WORD output file names using quoting style WORD.", 561" Valid WORDs are: literal, shell, shell-always, c, escape.", 562" Default is taken from QUOTING_STYLE env variable, or 'shell' if unset.", 563"", 564"Backup and version control options:", 565"", 566" -b --backup Back up the original contents of each file.", 567" --backup-if-mismatch Back up if the patch does not match exactly.", 568" --no-backup-if-mismatch Back up mismatches only if otherwise requested.", 569"", 570" -V STYLE --version-control=STYLE Use STYLE version control.", 571" STYLE is either 'simple', 'numbered', or 'existing'.", 572" -B PREFIX --prefix=PREFIX Prepend PREFIX to backup file names.", 573" -Y PREFIX --basename-prefix=PREFIX Prepend PREFIX to backup file basenames.", 574" -z SUFFIX --suffix=SUFFIX Append SUFFIX to backup file names.", 575"", 576" -g NUM --get=NUM Get files from RCS etc. if positive; ask if negative.", 577"", 578"Miscellaneous options:", 579"", 580" -t --batch Ask no questions; skip bad-Prereq patches; assume reversed.", 581" -f --force Like -t, but ignore bad-Prereq patches, and assume unreversed.", 582" -s --quiet --silent Work silently unless an error occurs.", 583" --verbose Output extra information about the work being done.", 584" --dry-run Do not actually change any files; just print what would happen.", 585" --posix Conform to the POSIX standard.", 586"", 587" -d DIR --directory=DIR Change the working directory to DIR first.", 588#if HAVE_SETMODE 589" --binary Read and write data in binary mode.", 590#else 591" --binary Read and write data in binary mode (no effect on this platform).", 592#endif 593"", 594" -v --version Output version info.", 595" --help Output this help.", 596"", 597"Report bugs to <bug-gnu-utils@gnu.org>.", 5980 599}; 600 601static void 602usage (FILE *stream, int status) 603{ 604 char const * const *p; 605 606 if (status != 0) 607 { 608 fprintf (stream, "%s: Try `%s --help' for more information.\n", 609 program_name, Argv[0]); 610 } 611 else 612 { 613 fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n", 614 Argv[0]); 615 for (p = option_help; *p; p++) 616 fprintf (stream, "%s\n", *p); 617 } 618 619 exit (status); 620} 621 622/* Process switches and filenames. */ 623 624static void 625get_some_switches (void) 626{ 627 register int optc; 628 629 if (rejname) 630 free (rejname); 631 rejname = 0; 632 if (optind == Argc) 633 return; 634 while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0)) 635 != -1) { 636 switch (optc) { 637 case 'b': 638 make_backups = 1; 639 /* Special hack for backward compatibility with CVS 1.9. 640 If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE', 641 treat `-b' as if it were `-b -z'. */ 642 if (Argc - optind == 3 643 && strcmp (Argv[optind - 1], "-b") == 0 644 && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1]) 645 && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1]) 646 && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1])) 647 { 648 optarg = Argv[optind++]; 649 if (verbosity != SILENT) 650 say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n", 651 optarg, optarg); 652 goto case_z; 653 } 654 break; 655 case 'B': 656 if (!*optarg) 657 fatal ("backup prefix is empty"); 658 origprae = savestr (optarg); 659 break; 660 case 'c': 661 diff_type = CONTEXT_DIFF; 662 break; 663 case 'd': 664 if (chdir(optarg) < 0) 665 pfatal ("Can't change to directory %s", quotearg (optarg)); 666 break; 667 case 'D': 668 do_defines = savestr (optarg); 669 break; 670 case 'e': 671 diff_type = ED_DIFF; 672 break; 673 case 'E': 674 remove_empty_files = TRUE; 675 break; 676 case 'f': 677 force = TRUE; 678 break; 679 case 'F': 680 maxfuzz = numeric_string (optarg, 0, "fuzz factor"); 681 break; 682 case 'g': 683 patch_get = numeric_string (optarg, 1, "get option value"); 684 break; 685 case 'i': 686 patchname = savestr (optarg); 687 break; 688 case 'l': 689 canonicalize = TRUE; 690 break; 691 case 'n': 692 diff_type = NORMAL_DIFF; 693 break; 694 case 'N': 695 noreverse = TRUE; 696 break; 697 case 'o': 698 if (strcmp (optarg, "-") == 0) 699 fatal ("can't output patches to standard output"); 700 outfile = savestr (optarg); 701 break; 702 case 'p': 703 strippath = numeric_string (optarg, 0, "strip count"); 704 break; 705 case 'r': 706 rejname = savestr (optarg); 707 break; 708 case 'R': 709 reverse = 1; 710 reverse_flag_specified = 1; 711 break; 712 case 's': 713 verbosity = SILENT; 714 break; 715 case 't': 716 batch = TRUE; 717 break; 718 case 'T': 719 set_time = 1; 720 break; 721 case 'u': 722 diff_type = UNI_DIFF; 723 break; 724 case 'v': 725 version(); 726 exit (0); 727 break; 728 case 'V': 729 version_control = optarg; 730 version_control_context = "--version-control or -V option"; 731 break; 732#if DEBUGGING 733 case 'x': 734 debug = numeric_string (optarg, 1, "debugging option"); 735 break; 736#endif 737 case 'Y': 738 if (!*optarg) 739 fatal ("backup basename prefix is empty"); 740 origbase = savestr (optarg); 741 break; 742 case 'z': 743 case_z: 744 if (!*optarg) 745 fatal ("backup suffix is empty"); 746 simple_backup_suffix = savestr (optarg); 747 break; 748 case 'Z': 749 set_utc = 1; 750 break; 751 case CHAR_MAX + 1: 752 dry_run = TRUE; 753 break; 754 case CHAR_MAX + 2: 755 verbosity = VERBOSE; 756 break; 757 case CHAR_MAX + 3: 758#if HAVE_SETMODE 759 binary_transput = O_BINARY; 760#endif 761 break; 762 case CHAR_MAX + 4: 763 usage (stdout, 0); 764 case CHAR_MAX + 5: 765 backup_if_mismatch = 1; 766 break; 767 case CHAR_MAX + 6: 768 backup_if_mismatch = 0; 769 break; 770 case CHAR_MAX + 7: 771 posixly_correct = 1; 772 break; 773 case CHAR_MAX + 8: 774 { 775 int i = argmatch (optarg, quoting_style_args, 0, 0); 776 if (i < 0) 777 { 778 invalid_arg ("quoting style", optarg, i); 779 usage (stderr, 2); 780 } 781 set_quoting_style ((struct quoting_options *) 0, 782 (enum quoting_style) i); 783 } 784 break; 785 default: 786 usage (stderr, 2); 787 } 788 } 789 790 /* Process any filename args. */ 791 if (optind < Argc) 792 { 793 inname = savestr (Argv[optind++]); 794 invc = -1; 795 if (optind < Argc) 796 { 797 patchname = savestr (Argv[optind++]); 798 if (optind < Argc) 799 { 800 fprintf (stderr, "%s: %s: extra operand\n", 801 program_name, quotearg (Argv[optind])); 802 usage (stderr, 2); 803 } 804 } 805 } 806} 807 808/* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero) 809 of type ARGTYPE_MSGID by converting it to an integer, 810 returning the result. */ 811static int 812numeric_string (char const *string, 813 int negative_allowed, 814 char const *argtype_msgid) 815{ 816 int value = 0; 817 char const *p = string; 818 int sign = *p == '-' ? -1 : 1; 819 820 p += *p == '-' || *p == '+'; 821 822 do 823 { 824 int v10 = value * 10; 825 int digit = *p - '0'; 826 int signed_digit = sign * digit; 827 int next_value = v10 + signed_digit; 828 829 if (9 < (unsigned) digit) 830 fatal ("%s %s is not a number", argtype_msgid, quotearg (string)); 831 832 if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0)) 833 fatal ("%s %s is too large", argtype_msgid, quotearg (string)); 834 835 value = next_value; 836 } 837 while (*++p); 838 839 if (value < 0 && ! negative_allowed) 840 fatal ("%s %s is negative", argtype_msgid, quotearg (string)); 841 842 return value; 843} 844 845/* Attempt to find the right place to apply this hunk of patch. */ 846 847static LINENUM 848locate_hunk (LINENUM fuzz) 849{ 850 register LINENUM first_guess = pch_first () + last_offset; 851 register LINENUM offset; 852 LINENUM pat_lines = pch_ptrn_lines(); 853 LINENUM prefix_context = pch_prefix_context (); 854 LINENUM suffix_context = pch_suffix_context (); 855 LINENUM context = (prefix_context < suffix_context 856 ? suffix_context : prefix_context); 857 LINENUM prefix_fuzz = fuzz + prefix_context - context; 858 LINENUM suffix_fuzz = fuzz + suffix_context - context; 859 LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1; 860 LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz); 861 LINENUM max_pos_offset = max_where - first_guess; 862 LINENUM max_neg_offset = first_guess - min_where; 863 LINENUM max_offset = (max_pos_offset < max_neg_offset 864 ? max_neg_offset : max_pos_offset); 865 866 if (!pat_lines) /* null range matches always */ 867 return first_guess; 868 869 /* Do not try lines <= 0. */ 870 if (first_guess <= max_neg_offset) 871 max_neg_offset = first_guess - 1; 872 873 if (prefix_fuzz < 0) 874 { 875 /* Can only match start of file. */ 876 877 if (suffix_fuzz < 0) 878 /* Can only match entire file. */ 879 if (pat_lines != input_lines || prefix_context < last_frozen_line) 880 return 0; 881 882 offset = 1 - first_guess; 883 if (last_frozen_line <= prefix_context 884 && offset <= max_pos_offset 885 && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz)) 886 { 887 last_offset = offset; 888 return first_guess + offset; 889 } 890 else 891 return 0; 892 } 893 894 if (suffix_fuzz < 0) 895 { 896 /* Can only match end of file. */ 897 offset = first_guess - (input_lines - pat_lines + 1); 898 if (offset <= max_neg_offset 899 && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0)) 900 { 901 last_offset = - offset; 902 return first_guess - offset; 903 } 904 else 905 return 0; 906 } 907 908 for (offset = 0; offset <= max_offset; offset++) { 909 char numbuf0[LINENUM_LENGTH_BOUND + 1]; 910 char numbuf1[LINENUM_LENGTH_BOUND + 1]; 911 if (offset <= max_pos_offset 912 && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) { 913 if (debug & 1) 914 say ("Offset changing from %s to %s\n", 915 format_linenum (numbuf0, last_offset), 916 format_linenum (numbuf1, offset)); 917 last_offset = offset; 918 return first_guess+offset; 919 } 920 if (0 < offset && offset <= max_neg_offset 921 && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) { 922 if (debug & 1) 923 say ("Offset changing from %s to %s\n", 924 format_linenum (numbuf0, last_offset), 925 format_linenum (numbuf1, -offset)); 926 last_offset = -offset; 927 return first_guess-offset; 928 } 929 } 930 return 0; 931} 932 933/* We did not find the pattern, dump out the hunk so they can handle it. */ 934 935static void 936abort_hunk (void) 937{ 938 register LINENUM i; 939 register LINENUM pat_end = pch_end (); 940 /* add in last_offset to guess the same as the previous successful hunk */ 941 LINENUM oldfirst = pch_first() + last_offset; 942 LINENUM newfirst = pch_newfirst() + last_offset; 943 LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; 944 LINENUM newlast = newfirst + pch_repl_lines() - 1; 945 char const *stars = 946 (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : ""; 947 char const *minuses = 948 (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----"; 949 950 fprintf(rejfp, "***************\n"); 951 for (i=0; i<=pat_end; i++) { 952 char numbuf0[LINENUM_LENGTH_BOUND + 1]; 953 char numbuf1[LINENUM_LENGTH_BOUND + 1]; 954 switch (pch_char(i)) { 955 case '*': 956 if (oldlast < oldfirst) 957 fprintf(rejfp, "*** 0%s\n", stars); 958 else if (oldlast == oldfirst) 959 fprintf (rejfp, "*** %s%s\n", 960 format_linenum (numbuf0, oldfirst), stars); 961 else 962 fprintf (rejfp, "*** %s,%s%s\n", 963 format_linenum (numbuf0, oldfirst), 964 format_linenum (numbuf1, oldlast), stars); 965 break; 966 case '=': 967 if (newlast < newfirst) 968 fprintf(rejfp, "--- 0%s\n", minuses); 969 else if (newlast == newfirst) 970 fprintf (rejfp, "--- %s%s\n", 971 format_linenum (numbuf0, newfirst), minuses); 972 else 973 fprintf (rejfp, "--- %s,%s%s\n", 974 format_linenum (numbuf0, newfirst), 975 format_linenum (numbuf1, newlast), minuses); 976 break; 977 case ' ': case '-': case '+': case '!': 978 fprintf (rejfp, "%c ", pch_char (i)); 979 /* fall into */ 980 case '\n': 981 pch_write_line (i, rejfp); 982 break; 983 default: 984 fatal ("fatal internal error in abort_hunk"); 985 } 986 if (ferror (rejfp)) 987 write_fatal (); 988 } 989} 990 991/* We found where to apply it (we hope), so do it. */ 992 993static bool 994apply_hunk (struct outstate *outstate, LINENUM where) 995{ 996 register LINENUM old = 1; 997 register LINENUM lastline = pch_ptrn_lines (); 998 register LINENUM new = lastline+1; 999 register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE; 1000 register char const *R_do_defines = do_defines; 1001 register LINENUM pat_end = pch_end (); 1002 register FILE *fp = outstate->ofp; 1003 1004 where--; 1005 while (pch_char(new) == '=' || pch_char(new) == '\n') 1006 new++; 1007 1008 while (old <= lastline) { 1009 if (pch_char(old) == '-') { 1010 assert (outstate->after_newline); 1011 if (! copy_till (outstate, where + old - 1)) 1012 return FALSE; 1013 if (R_do_defines) { 1014 if (def_state == OUTSIDE) { 1015 fprintf (fp, outstate->after_newline + if_defined, 1016 R_do_defines); 1017 def_state = IN_IFNDEF; 1018 } 1019 else if (def_state == IN_IFDEF) { 1020 fprintf (fp, outstate->after_newline + else_defined); 1021 def_state = IN_ELSE; 1022 } 1023 if (ferror (fp)) 1024 write_fatal (); 1025 outstate->after_newline = pch_write_line (old, fp); 1026 outstate->zero_output = 0; 1027 } 1028 last_frozen_line++; 1029 old++; 1030 } 1031 else if (new > pat_end) { 1032 break; 1033 } 1034 else if (pch_char(new) == '+') { 1035 if (! copy_till (outstate, where + old - 1)) 1036 return FALSE; 1037 if (R_do_defines) { 1038 if (def_state == IN_IFNDEF) { 1039 fprintf (fp, outstate->after_newline + else_defined); 1040 def_state = IN_ELSE; 1041 } 1042 else if (def_state == OUTSIDE) { 1043 fprintf (fp, outstate->after_newline + if_defined, 1044 R_do_defines); 1045 def_state = IN_IFDEF; 1046 } 1047 if (ferror (fp)) 1048 write_fatal (); 1049 } 1050 outstate->after_newline = pch_write_line (new, fp); 1051 outstate->zero_output = 0; 1052 new++; 1053 } 1054 else if (pch_char(new) != pch_char(old)) { 1055 char numbuf0[LINENUM_LENGTH_BOUND + 1]; 1056 char numbuf1[LINENUM_LENGTH_BOUND + 1]; 1057 if (debug & 1) 1058 say ("oldchar = '%c', newchar = '%c'\n", 1059 pch_char (old), pch_char (new)); 1060 fatal ("Out-of-sync patch, lines %s,%s -- mangled text or line numbers, maybe?", 1061 format_linenum (numbuf0, pch_hunk_beg() + old), 1062 format_linenum (numbuf1, pch_hunk_beg() + new)); 1063 } 1064 else if (pch_char(new) == '!') { 1065 assert (outstate->after_newline); 1066 if (! copy_till (outstate, where + old - 1)) 1067 return FALSE; 1068 assert (outstate->after_newline); 1069 if (R_do_defines) { 1070 fprintf (fp, not_defined, R_do_defines); 1071 if (ferror (fp)) 1072 write_fatal (); 1073 def_state = IN_IFNDEF; 1074 } 1075 1076 do 1077 { 1078 if (R_do_defines) { 1079 outstate->after_newline = pch_write_line (old, fp); 1080 } 1081 last_frozen_line++; 1082 old++; 1083 } 1084 while (pch_char (old) == '!'); 1085 1086 if (R_do_defines) { 1087 fprintf (fp, outstate->after_newline + else_defined); 1088 if (ferror (fp)) 1089 write_fatal (); 1090 def_state = IN_ELSE; 1091 } 1092 1093 do 1094 { 1095 outstate->after_newline = pch_write_line (new, fp); 1096 new++; 1097 } 1098 while (pch_char (new) == '!'); 1099 outstate->zero_output = 0; 1100 } 1101 else { 1102 assert(pch_char(new) == ' '); 1103 old++; 1104 new++; 1105 if (R_do_defines && def_state != OUTSIDE) { 1106 fprintf (fp, outstate->after_newline + end_defined, 1107 R_do_defines); 1108 if (ferror (fp)) 1109 write_fatal (); 1110 outstate->after_newline = 1; 1111 def_state = OUTSIDE; 1112 } 1113 } 1114 } 1115 if (new <= pat_end && pch_char(new) == '+') { 1116 if (! copy_till (outstate, where + old - 1)) 1117 return FALSE; 1118 if (R_do_defines) { 1119 if (def_state == OUTSIDE) { 1120 fprintf (fp, outstate->after_newline + if_defined, 1121 R_do_defines); 1122 def_state = IN_IFDEF; 1123 } 1124 else if (def_state == IN_IFNDEF) { 1125 fprintf (fp, outstate->after_newline + else_defined); 1126 def_state = IN_ELSE; 1127 } 1128 if (ferror (fp)) 1129 write_fatal (); 1130 outstate->zero_output = 0; 1131 } 1132 1133 do 1134 { 1135 if (! outstate->after_newline && putc ('\n', fp) == EOF) 1136 write_fatal (); 1137 outstate->after_newline = pch_write_line (new, fp); 1138 outstate->zero_output = 0; 1139 new++; 1140 } 1141 while (new <= pat_end && pch_char (new) == '+'); 1142 } 1143 if (R_do_defines && def_state != OUTSIDE) { 1144 fprintf (fp, outstate->after_newline + end_defined, R_do_defines); 1145 if (ferror (fp)) 1146 write_fatal (); 1147 outstate->after_newline = 1; 1148 } 1149 return TRUE; 1150} 1151 1152/* Create an output file. */ 1153 1154static FILE * 1155create_output_file (char const *name, int open_flags) 1156{ 1157 int fd = create_file (name, O_WRONLY | binary_transput | open_flags, 1158 instat.st_mode); 1159 FILE *f = fdopen (fd, binary_transput ? "wb" : "w"); 1160 if (! f) 1161 pfatal ("Can't create file %s", quotearg (name)); 1162 return f; 1163} 1164 1165/* Open the new file. */ 1166 1167static void 1168init_output (char const *name, int open_flags, struct outstate *outstate) 1169{ 1170 outstate->ofp = name ? create_output_file (name, open_flags) : (FILE *) 0; 1171 outstate->after_newline = 1; 1172 outstate->zero_output = 1; 1173} 1174 1175/* Open a file to put hunks we can't locate. */ 1176 1177static void 1178init_reject (void) 1179{ 1180 int exclusive = TMPREJNAME_needs_removal ? 0 : O_EXCL; 1181 TMPREJNAME_needs_removal = 1; 1182 rejfp = create_output_file (TMPREJNAME, exclusive); 1183} 1184 1185/* Copy input file to output, up to wherever hunk is to be applied. */ 1186 1187static bool 1188copy_till (register struct outstate *outstate, register LINENUM lastline) 1189{ 1190 register LINENUM R_last_frozen_line = last_frozen_line; 1191 register FILE *fp = outstate->ofp; 1192 register char const *s; 1193 size_t size; 1194 1195 if (R_last_frozen_line > lastline) 1196 { 1197 say ("misordered hunks! output would be garbled\n"); 1198 return FALSE; 1199 } 1200 while (R_last_frozen_line < lastline) 1201 { 1202 s = ifetch (++R_last_frozen_line, 0, &size); 1203 if (size) 1204 { 1205 if ((! outstate->after_newline && putc ('\n', fp) == EOF) 1206 || ! fwrite (s, sizeof *s, size, fp)) 1207 write_fatal (); 1208 outstate->after_newline = s[size - 1] == '\n'; 1209 outstate->zero_output = 0; 1210 } 1211 } 1212 last_frozen_line = R_last_frozen_line; 1213 return TRUE; 1214} 1215 1216/* Finish copying the input file to the output file. */ 1217 1218static bool 1219spew_output (struct outstate *outstate) 1220{ 1221 if (debug & 256) 1222 { 1223 char numbuf0[LINENUM_LENGTH_BOUND + 1]; 1224 char numbuf1[LINENUM_LENGTH_BOUND + 1]; 1225 say ("il=%s lfl=%s\n", 1226 format_linenum (numbuf0, input_lines), 1227 format_linenum (numbuf1, last_frozen_line)); 1228 } 1229 1230 if (last_frozen_line < input_lines) 1231 if (! copy_till (outstate, input_lines)) 1232 return FALSE; 1233 1234 if (outstate->ofp && ! outfile) 1235 { 1236 if (fclose (outstate->ofp) != 0) 1237 write_fatal (); 1238 outstate->ofp = 0; 1239 } 1240 1241 return TRUE; 1242} 1243 1244/* Does the patch pattern match at line base+offset? */ 1245 1246static bool 1247patch_match (LINENUM base, LINENUM offset, 1248 LINENUM prefix_fuzz, LINENUM suffix_fuzz) 1249{ 1250 register LINENUM pline = 1 + prefix_fuzz; 1251 register LINENUM iline; 1252 register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz; 1253 size_t size; 1254 register char const *p; 1255 1256 for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) { 1257 p = ifetch (iline, offset >= 0, &size); 1258 if (canonicalize) { 1259 if (!similar(p, size, 1260 pfetch(pline), 1261 pch_line_len(pline) )) 1262 return FALSE; 1263 } 1264 else if (size != pch_line_len (pline) 1265 || memcmp (p, pfetch (pline), size) != 0) 1266 return FALSE; 1267 } 1268 return TRUE; 1269} 1270 1271/* Do two lines match with canonicalized white space? */ 1272 1273static bool 1274similar (register char const *a, register size_t alen, 1275 register char const *b, register size_t blen) 1276{ 1277 /* Ignore presence or absence of trailing newlines. */ 1278 alen -= alen && a[alen - 1] == '\n'; 1279 blen -= blen && b[blen - 1] == '\n'; 1280 1281 for (;;) 1282 { 1283 if (!blen || (*b == ' ' || *b == '\t')) 1284 { 1285 while (blen && (*b == ' ' || *b == '\t')) 1286 b++, blen--; 1287 if (alen) 1288 { 1289 if (!(*a == ' ' || *a == '\t')) 1290 return FALSE; 1291 do a++, alen--; 1292 while (alen && (*a == ' ' || *a == '\t')); 1293 } 1294 if (!alen || !blen) 1295 return alen == blen; 1296 } 1297 else if (!alen || *a++ != *b++) 1298 return FALSE; 1299 else 1300 alen--, blen--; 1301 } 1302} 1303 1304/* Make a temporary file. */ 1305 1306#if HAVE_MKTEMP 1307char *mktemp PARAMS ((char *)); 1308#endif 1309 1310#ifndef TMPDIR 1311#define TMPDIR "/tmp" 1312#endif 1313 1314static char const * 1315make_temp (int letter) 1316{ 1317 char *r; 1318#if HAVE_MKTEMP 1319 char const *tmpdir = getenv ("TMPDIR"); /* Unix tradition */ 1320 if (!tmpdir) tmpdir = getenv ("TMP"); /* DOS tradition */ 1321 if (!tmpdir) tmpdir = getenv ("TEMP"); /* another DOS tradition */ 1322 if (!tmpdir) tmpdir = TMPDIR; 1323 r = xmalloc (strlen (tmpdir) + 10); 1324 sprintf (r, "%s/p%cXXXXXX", tmpdir, letter); 1325 mktemp (r); 1326 if (!*r) 1327 pfatal ("mktemp"); 1328#else 1329 r = xmalloc (L_tmpnam); 1330 if (! (tmpnam (r) == r && *r)) 1331 pfatal ("tmpnam"); 1332#endif 1333 return r; 1334} 1335 1336/* Fatal exit with cleanup. */ 1337 1338void 1339fatal_exit (int sig) 1340{ 1341 cleanup (); 1342 1343 if (sig) 1344 exit_with_signal (sig); 1345 1346 exit (2); 1347} 1348 1349static void 1350remove_if_needed (char const *name, int volatile *needs_removal) 1351{ 1352 if (*needs_removal) 1353 { 1354 unlink (name); 1355 *needs_removal = 0; 1356 } 1357} 1358 1359static void 1360cleanup (void) 1361{ 1362 remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal); 1363 remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal); 1364 remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal); 1365 remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal); 1366} 1367