master xplshn/aruu / cmd / posix / patch.c
   1/* See LICENSE file for copyright and license details. */
   2#include "paths.h"
   3#include "util.h"
   4#include "wexec.h"
   5
   6#include <sys/file.h>
   7#include <sys/mman.h>
   8#include <sys/param.h>
   9#include <sys/stat.h>
  10#include <sys/wait.h>
  11
  12#include <ctype.h>
  13#include <dirent.h>
  14#include <errno.h>
  15#include <fcntl.h>
  16#include <getopt.h>
  17#include <libgen.h>
  18#include <limits.h>
  19#include <stdarg.h>
  20#include <stddef.h>
  21#include <stdint.h>
  22#include <stdio.h>
  23#include <stdlib.h>
  24#include <string.h>
  25#include <unistd.h>
  26
  27#define DEBUGGING
  28
  29#define MAXHUNKSIZE 100000
  30#define INITHUNKMAX 125
  31#define INITLINELEN 8192
  32#define BUFFERSIZE  1024
  33#define LINENUM_MAX LONG_MAX
  34
  35#define SCCSPREFIX "s."
  36#define GET        "get -e %s"
  37#define SCCSDIFF   "get -p %s | diff - %s >/dev/null"
  38
  39#define RCSSUFFIX ",v"
  40#define CHECKOUT  "co"
  41#define RCSDIFF   "rcsdiff"
  42
  43#define ORIGEXT ".orig"
  44#define REJEXT  ".rej"
  45
  46#define strNE(s1, s2)     (strcmp(s1, s2))
  47#define strEQ(s1, s2)     (!strcmp(s1, s2))
  48#define strnNE(s1, s2, l) (strncmp(s1, s2, l))
  49#define strnEQ(s1, s2, l) (!strncmp(s1, s2, l))
  50
  51#define OLD_FILE   0
  52#define NEW_FILE   1
  53#define INDEX_FILE 2
  54#define MAX_FILE   3
  55
  56#define CONTEXT_DIFF     1
  57#define NORMAL_DIFF      2
  58#define ED_DIFF          3
  59#define NEW_CONTEXT_DIFF 4
  60#define UNI_DIFF         5
  61
  62#define _PATH_ED "ed"
  63
  64#define ISDIGIT(c) (isascii((unsigned char)c) && isdigit((unsigned char)c))
  65
  66#ifndef __UNCONST
  67#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
  68#endif
  69
  70#ifndef __dead
  71#define __dead __attribute__((__noreturn__))
  72#endif
  73
  74typedef long LINENUM;
  75
  76enum BackupType {
  77  BACKUP_UNDEFINED,
  78  BACKUP_NONE,
  79  BACKUP_SIMPLE,
  80  BACKUP_NUMBERED_EXISTING,
  81  BACKUP_NUMBERED
  82};
  83
  84struct FileName {
  85  char *path;
  86  int   exists;
  87};
  88
  89mode_t filemode = 0644;
  90char  *buf;
  91size_t bufsz;
  92int    using_plan_a = 1;
  93int    out_of_mem   = 0;
  94
  95#define MAXFILEC 2
  96char *filearg[MAXFILEC];
  97int   ok_to_create_file = 0;
  98char *outname           = NULL;
  99char *origprae          = NULL;
 100char *TMPOUTNAME;
 101char *TMPINNAME;
 102char *TMPREJNAME;
 103char *TMPPATNAME;
 104int   toutkeep = 0;
 105int   trejkeep = 0;
 106int   warn_on_invalid_line;
 107int   last_line_missing_eol;
 108
 109#ifdef DEBUGGING
 110int debug = 0;
 111#endif
 112
 113int     force              = 0;
 114int     batch              = 0;
 115int     verbose            = 1;
 116int     reverse            = 0;
 117int     noreverse          = 0;
 118int     skip_rest_of_patch = 0;
 119int     strippath          = 957;
 120int     canonicalize       = 0;
 121int     check_only         = 0;
 122int     diff_type          = 0;
 123char   *revision           = NULL;
 124LINENUM input_lines        = 0;
 125int     posix              = 0;
 126int     backup_if_mismatch = -1;
 127
 128enum BackupType backup_type          = BACKUP_UNDEFINED;
 129const char     *simple_backup_suffix = "~";
 130
 131static int     remove_empty_files     = 0;
 132static int     reverse_flag_specified = 0;
 133static char    rejname[PATH_MAX];
 134static char    serrbuf[BUFSIZ];
 135static LINENUM last_frozen_line = 0;
 136static int     Argc;
 137static char  **Argv;
 138static int     Argc_last;
 139static char  **Argv_last;
 140static FILE   *ofp         = NULL;
 141static FILE   *rejfp       = NULL;
 142static int     filec       = 0;
 143static LINENUM last_offset = 0;
 144static LINENUM maxfuzz     = 2;
 145
 146static int        do_defines = 0;
 147static char       if_defined[128];
 148static char       not_defined[128];
 149static const char else_defined[] = "#else\n";
 150static char       end_defined[128];
 151
 152/* function declarations */
 153static void    reinitialize_almost_everything(void);
 154static void    get_some_switches(void);
 155static void    reset_getopt_state(void);
 156static LINENUM locate_hunk(LINENUM);
 157static void    rej_line(int, LINENUM);
 158static void    abort_hunk(void);
 159static void    apply_hunk(LINENUM);
 160static void    init_output(const char *);
 161static void    init_reject(const char *);
 162static void    copy_till(LINENUM, int);
 163static int     spew_output(void);
 164static void    dump_input_line(LINENUM, int);
 165static int     patch_match(LINENUM, LINENUM, LINENUM);
 166static int     similar(const char *, const char *, ssize_t);
 167static void    usage(void);
 168
 169void    re_patch(void);
 170void    open_patch_file(const char *);
 171void    set_hunkmax(void);
 172int     there_is_another_patch(void);
 173int     another_hunk(void);
 174int     pch_swap(void);
 175char   *pfetch(LINENUM);
 176ssize_t pch_line_len(LINENUM);
 177LINENUM pch_first(void);
 178LINENUM pch_ptrn_lines(void);
 179LINENUM pch_newfirst(void);
 180LINENUM pch_repl_lines(void);
 181LINENUM pch_end(void);
 182LINENUM pch_context(void);
 183LINENUM pch_hunk_beg(void);
 184char    pch_char(LINENUM);
 185void    do_ed_script(void);
 186
 187void  re_input(void);
 188void  scan_input(const char *);
 189char *ifetch(LINENUM, int);
 190
 191char           *fetchname(const char *, int *, int);
 192char           *checked_in(char *);
 193LINENUM         strtolinenum(char *, char **);
 194int             backup_file(const char *);
 195int             move_file(const char *, const char *);
 196int             copy_file(const char *, const char *);
 197void            say(const char *, ...);
 198__dead void     fatal(const char *, ...);
 199__dead void     pfatal(const char *, ...);
 200void            ask(const char *, ...);
 201char           *savestr(const char *);
 202void            set_signals(int);
 203void            ignore_signals(void);
 204void            makedirs(const char *, int);
 205__dead void     version(void);
 206__dead void     my_exit(int);
 207static void    *pch_realloc(void *, size_t, size_t);
 208int             mkpath(char *);
 209char           *find_backup_file_name(const char *);
 210enum BackupType get_version(const char *);
 211
 212static off_t  i_size;
 213static char  *i_womp;
 214static char **i_ptr;
 215static char   empty_line[] = {'\0'};
 216
 217static int     tifd = -1;
 218static char   *tibuf[2];
 219static LINENUM tiline[2] = {-1, -1};
 220static LINENUM lines_per_buf;
 221static int     tireclen;
 222
 223static int  rev_in_string(const char *);
 224static int  reallocate_lines(size_t *);
 225static int  plan_a(const char *);
 226static void plan_b(const char *);
 227
 228static long     p_filesize;
 229static LINENUM  p_first;
 230static LINENUM  p_newfirst;
 231static LINENUM  p_ptrn_lines;
 232static LINENUM  p_repl_lines;
 233static LINENUM  p_end = -1;
 234static LINENUM  p_max;
 235static LINENUM  p_context    = 3;
 236static LINENUM  p_input_line = 0;
 237static char   **p_line       = NULL;
 238static ssize_t *p_len        = NULL;
 239static char    *p_char       = NULL;
 240static int      hunkmax      = INITHUNKMAX;
 241static int      p_indent;
 242static LINENUM  p_base;
 243static LINENUM  p_bline;
 244static LINENUM  p_start;
 245static LINENUM  p_sline;
 246static LINENUM  p_hunk_beg;
 247static LINENUM  p_efake   = -1;
 248static LINENUM  p_bfake   = -1;
 249static FILE    *pfp       = NULL;
 250static char    *bestguess = NULL;
 251
 252static void   grow_hunkmax(void);
 253static int    intuit_diff_type(void);
 254static void   next_intuit_at(LINENUM, LINENUM);
 255static void   skip_to(LINENUM, LINENUM);
 256static int    pgetline(char **, size_t *, FILE *);
 257static char  *best_name(const struct FileName *, int);
 258static char  *posix_name(const struct FileName *, int);
 259static size_t num_components(const char *);
 260
 261static char *patch_concat(const char *, const char *);
 262static char *make_version_name(const char *, int);
 263static int   max_backup_version(const char *, const char *);
 264static int   version_number(const char *, const char *, size_t);
 265static int   argmatch(const char *, const char **);
 266static void  invalid_arg(const char *, const char *, int);
 267
 268static const char *backup_args[] = {
 269    "none", "never", "simple", "nil", "existing", "t", "numbered", 0
 270};
 271static enum BackupType backup_types[] = {
 272    BACKUP_NONE,
 273    BACKUP_SIMPLE,
 274    BACKUP_SIMPLE,
 275    BACKUP_NUMBERED_EXISTING,
 276    BACKUP_NUMBERED_EXISTING,
 277    BACKUP_NUMBERED,
 278    BACKUP_NUMBERED
 279};
 280
 281/* implementation */
 282
 283// ?man patch: apply a diff file to an original
 284int
 285main(int argc, char *argv[])
 286{
 287  int         error, hunk, failed, i, fd;
 288  LINENUM     where, newwhere, fuzz, mymaxfuzz;
 289  const char *tmpdir;
 290  char       *v;
 291
 292  error = 0;
 293  where = 0;
 294  bufsz = INITLINELEN;
 295  if ((buf = malloc(bufsz)) == NULL)
 296    pfatal("allocating input buffer");
 297  buf[0] = '\0';
 298
 299  setbuf(stderr, serrbuf);
 300  for (i = 0; i < MAXFILEC; i++)
 301    filearg[i] = NULL;
 302
 303  if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
 304    tmpdir = ARUU_PATH_TMP;
 305  for (i = strlen(tmpdir) - 1; i > 0 && tmpdir[i] == '/'; i--)
 306    ;
 307  i++;
 308  if (asprintf(&TMPOUTNAME, "%.*s/patchoXXXXXXXXXX", i, tmpdir) == -1)
 309    fatal("cannot allocate memory");
 310  if ((fd = mkstemp(TMPOUTNAME)) < 0)
 311    pfatal("cant create %s", TMPOUTNAME);
 312  close(fd);
 313
 314  if (asprintf(&TMPINNAME, "%.*s/patchiXXXXXXXXXX", i, tmpdir) == -1)
 315    fatal("cannot allocate memory");
 316  if ((fd = mkstemp(TMPINNAME)) < 0)
 317    pfatal("cant create %s", TMPINNAME);
 318  close(fd);
 319
 320  if (asprintf(&TMPREJNAME, "%.*s/patchrXXXXXXXXXX", i, tmpdir) == -1)
 321    fatal("cannot allocate memory");
 322  if ((fd = mkstemp(TMPREJNAME)) < 0)
 323    pfatal("cant create %s", TMPREJNAME);
 324  close(fd);
 325
 326  if (asprintf(&TMPPATNAME, "%.*s/patchpXXXXXXXXXX", i, tmpdir) == -1)
 327    fatal("cannot allocate memory");
 328  if ((fd = mkstemp(TMPPATNAME)) < 0)
 329    pfatal("cant create %s", TMPPATNAME);
 330  close(fd);
 331
 332  v = getenv("SIMPLE_BACKUP_SUFFIX");
 333  if (v)
 334    simple_backup_suffix = v;
 335  else
 336    simple_backup_suffix = ORIGEXT;
 337
 338  if ((v = getenv("PATCH_VERSION_CONTROL")) == NULL)
 339    v = getenv("VERSION_CONTROL");
 340  if (v != NULL)
 341    backup_type = get_version(v);
 342
 343  Argc = argc;
 344  Argv = argv;
 345  get_some_switches();
 346
 347  if (backup_type == BACKUP_UNDEFINED)
 348    backup_type = posix ? BACKUP_NONE : BACKUP_NUMBERED_EXISTING;
 349
 350  set_signals(0);
 351
 352  for (open_patch_file(filearg[1]); there_is_another_patch(); reinitialize_almost_everything()) {
 353    warn_on_invalid_line = 1;
 354    if (outname == NULL)
 355      outname = savestr(filearg[0]);
 356
 357    if (diff_type == ED_DIFF) {
 358      do_ed_script();
 359      continue;
 360    }
 361    if (!skip_rest_of_patch)
 362      init_output(TMPOUTNAME);
 363
 364    init_reject(TMPREJNAME);
 365
 366    if (!skip_rest_of_patch)
 367      scan_input(filearg[0]);
 368
 369    hunk       = 0;
 370    failed     = 0;
 371    out_of_mem = 0;
 372    while (another_hunk()) {
 373      hunk++;
 374      fuzz      = 0;
 375      mymaxfuzz = pch_context();
 376      if (maxfuzz < mymaxfuzz)
 377        mymaxfuzz = maxfuzz;
 378      if (!skip_rest_of_patch) {
 379        do {
 380          where = locate_hunk(fuzz);
 381          if (hunk == 1 && where == 0 && !force) {
 382            if (!pch_swap()) {
 383              if (fuzz == 0)
 384                say("Not "
 385                    "enough "
 386                    "memory to "
 387                    "try "
 388                    "swapped "
 389                    "hunk! "
 390                    "Assuming "
 391                    "unswapped."
 392                    "\n");
 393              continue;
 394            }
 395            reverse = !reverse;
 396            where   = locate_hunk(fuzz);
 397            if (where == 0) {
 398              if (!pch_swap())
 399                fatal(
 400                    "lost "
 401                    "hunk on "
 402                    "alloc "
 403                    "error!"
 404                    "\n"
 405                );
 406              reverse = !reverse;
 407            } else if (noreverse) {
 408              if (!pch_swap())
 409                fatal(
 410                    "lost "
 411                    "hunk on "
 412                    "alloc "
 413                    "error!"
 414                    "\n"
 415                );
 416              reverse = !reverse;
 417              say("Ignoring "
 418                  "previously "
 419                  "applied (or "
 420                  "reversed) "
 421                  "patch.\n");
 422              skip_rest_of_patch = 1;
 423            } else if (batch) {
 424              if (verbose)
 425                say("%seversed "
 426                    "(or "
 427                    "%sprevious"
 428                    "ly "
 429                    "applied) "
 430                    "patch "
 431                    "detected! "
 432                    "%s -R.",
 433                    reverse ? "R" : "Unr",
 434                    reverse ? "" : "not ",
 435                    reverse ? "Assuming" : "Ignoring");
 436            } else {
 437              ask("%seversed (or "
 438                  "%spreviously "
 439                  "applied) patch "
 440                  "detected! %s -R? "
 441                  "[y] ",
 442                  reverse ? "R" : "Unr",
 443                  reverse ? "" : "not ",
 444                  reverse ? "Assume" : "Ignore");
 445              if (*buf == 'n') {
 446                ask("Apply "
 447                    "anyway? "
 448                    "[n] ");
 449                if (*buf != 'y')
 450                  skip_rest_of_patch = 1;
 451                where   = 0;
 452                reverse = !reverse;
 453                if (!pch_swap())
 454                  fatal("lost hunk on alloc error!\n");
 455              }
 456            }
 457          }
 458        } while (!skip_rest_of_patch && where == 0 && ++fuzz <= mymaxfuzz);
 459
 460        if (skip_rest_of_patch) {
 461          if (ferror(ofp) || fclose(ofp)) {
 462            say("Error writing %s\n", TMPOUTNAME);
 463            error = 1;
 464          }
 465          ofp = NULL;
 466        }
 467      }
 468      newwhere = pch_newfirst() + last_offset;
 469      if (skip_rest_of_patch) {
 470        abort_hunk();
 471        failed++;
 472        if (verbose)
 473          say("Hunk #%d ignored at %ld.\n", hunk, newwhere);
 474      } else if (where == 0) {
 475        abort_hunk();
 476        failed++;
 477        if (verbose)
 478          say("Hunk #%d failed at %ld.\n", hunk, newwhere);
 479      } else {
 480        apply_hunk(where);
 481        if (verbose) {
 482          say("Hunk #%d succeeded at %ld", hunk, newwhere);
 483          if (fuzz != 0)
 484            say(" with fuzz %ld", fuzz);
 485          if (last_offset)
 486            say(" (offset %ld line%s)", last_offset, last_offset == 1L ? "" : "s");
 487          say(".\n");
 488        }
 489      }
 490    }
 491
 492    if (out_of_mem && using_plan_a) {
 493      Argc = Argc_last;
 494      Argv = Argv_last;
 495      say("\n\nRan out of memory using Plan A--trying "
 496          "again...\n\n");
 497      if (ofp)
 498        fclose(ofp);
 499      ofp = NULL;
 500      if (rejfp)
 501        fclose(rejfp);
 502      rejfp = NULL;
 503      continue;
 504    }
 505    if (hunk == 0)
 506      fatal("internal error: hunk should not be 0\n");
 507
 508    if (!skip_rest_of_patch && !spew_output()) {
 509      say("cant write %s\n", TMPOUTNAME);
 510      error = 1;
 511    }
 512
 513    ignore_signals();
 514    if (!skip_rest_of_patch) {
 515      struct stat statbuf;
 516      char       *realout = outname;
 517
 518      if (!check_only) {
 519        enum BackupType saved = backup_type;
 520        if (failed > 0 && backup_if_mismatch > 0 && backup_type == BACKUP_NONE)
 521          backup_type = BACKUP_SIMPLE;
 522        if (move_file(TMPOUTNAME, outname) < 0) {
 523          toutkeep = 1;
 524          realout  = TMPOUTNAME;
 525          chmod(TMPOUTNAME, filemode);
 526        } else
 527          chmod(outname, filemode);
 528        backup_type = saved;
 529
 530        if (remove_empty_files && stat(realout, &statbuf) == 0 && statbuf.st_size == 0) {
 531          if (verbose)
 532            say("Removing %s (empty after "
 533                "patching).\n",
 534                realout);
 535          unlink(realout);
 536        }
 537      }
 538    }
 539    if (ferror(rejfp) || fclose(rejfp)) {
 540      say("Error writing %s\n", rejname);
 541      error = 1;
 542    }
 543    rejfp = NULL;
 544    if (failed) {
 545      error = 1;
 546      if (*rejname == '\0') {
 547        if (strlcpy(rejname, outname, sizeof(rejname)) >= sizeof(rejname))
 548          fatal("filename %s is too long\n", outname);
 549        if (strlcat(rejname, REJEXT, sizeof(rejname)) >= sizeof(rejname))
 550          fatal("filename %s is too long\n", outname);
 551      }
 552      if (skip_rest_of_patch) {
 553        say("%d out of %d hunks ignored--saving "
 554            "rejects to %s\n",
 555            failed,
 556            hunk,
 557            rejname);
 558      } else {
 559        say("%d out of %d hunks FAILED -- saving "
 560            "rejects to %s\n",
 561            failed,
 562            hunk,
 563            rejname);
 564      }
 565      if (!check_only && move_file(TMPREJNAME, rejname) < 0)
 566        trejkeep = 1;
 567    }
 568    set_signals(1);
 569  }
 570  my_exit(error);
 571}
 572
 573static void
 574reset_getopt_state(void)
 575{
 576#if defined(__GLIBC__) || defined(__linux__)
 577  optind = 0;
 578#else
 579  optreset = 1;
 580  optind   = 1;
 581#endif
 582}
 583
 584static void
 585reinitialize_almost_everything(void)
 586{
 587  re_patch();
 588  re_input();
 589
 590  input_lines      = 0;
 591  last_frozen_line = 0;
 592  filec            = 0;
 593  if (!out_of_mem) {
 594    free(filearg[0]);
 595    filearg[0] = NULL;
 596  }
 597  free(outname);
 598  outname     = NULL;
 599  last_offset = 0;
 600  diff_type   = 0;
 601  free(revision);
 602  revision           = NULL;
 603  reverse            = reverse_flag_specified;
 604  skip_rest_of_patch = 0;
 605  get_some_switches();
 606}
 607
 608static void
 609get_some_switches(void)
 610{
 611  const char          *options    = "b::B:cCd:D:eEfF:i:lnNo:p:r:RstuvV:x:z:";
 612  static struct option longopts[] = {
 613      {"backup", no_argument, 0, 'b'},
 614      {"backup-if-mismatch", no_argument, &backup_if_mismatch, 1},
 615      {"batch", no_argument, 0, 't'},
 616      {"check", no_argument, 0, 'C'},
 617      {"context", no_argument, 0, 'c'},
 618      {"debug", required_argument, 0, 'x'},
 619      {"directory", required_argument, 0, 'd'},
 620      {"ed", no_argument, 0, 'e'},
 621      {"force", no_argument, 0, 'f'},
 622      {"forward", no_argument, 0, 'N'},
 623      {"fuzz", required_argument, 0, 'F'},
 624      {"ifdef", required_argument, 0, 'D'},
 625      {"input", required_argument, 0, 'i'},
 626      {"ignore-whitespace", no_argument, 0, 'l'},
 627      {"no-backup-if-mismatch", no_argument, &backup_if_mismatch, 0},
 628      {"normal", no_argument, 0, 'n'},
 629      {"output", required_argument, 0, 'o'},
 630      {"prefix", required_argument, 0, 'B'},
 631      {"quiet", no_argument, 0, 's'},
 632      {"reject-file", required_argument, 0, 'r'},
 633      {"remove-empty-files", no_argument, 0, 'E'},
 634      {"reverse", no_argument, 0, 'R'},
 635      {"silent", no_argument, 0, 's'},
 636      {"strip", required_argument, 0, 'p'},
 637      {"suffix", required_argument, 0, 'z'},
 638      {"unified", no_argument, 0, 'u'},
 639      {"version", no_argument, 0, 'v'},
 640      {"version-control", required_argument, 0, 'V'},
 641      {"posix", no_argument, &posix, 1},
 642      {NULL, 0, 0, 0}
 643  };
 644  int ch;
 645
 646  rejname[0] = '\0';
 647  Argc_last  = Argc;
 648  Argv_last  = Argv;
 649  if (!Argc)
 650    return;
 651  reset_getopt_state();
 652  while ((ch = getopt_long(Argc, Argv, options, longopts, NULL)) != -1) {
 653    switch (ch) {
 654      case 'b':
 655        if (backup_type == BACKUP_UNDEFINED)
 656          backup_type = BACKUP_NUMBERED_EXISTING;
 657        if (optarg == NULL)
 658          break;
 659        if (verbose)
 660          say("Warning, the -b suffix option has been "
 661              "obsoleted by the -z option\n");
 662        /* FALLTHROUGH */
 663      case 'z':
 664        simple_backup_suffix = savestr(optarg);
 665        break;
 666      case 'B':
 667        origprae = savestr(optarg);
 668        break;
 669      case 'c':
 670        diff_type = CONTEXT_DIFF;
 671        break;
 672      case 'C':
 673        check_only = 1;
 674        break;
 675      case 'd':
 676        if (chdir(optarg) < 0)
 677          pfatal("cant cd to %s", optarg);
 678        break;
 679      case 'D':
 680        do_defines = 1;
 681        if (!isalpha((unsigned char)*optarg) && *optarg != '_')
 682          fatal("argument to -D is not an identifier\n");
 683        snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", optarg);
 684        snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", optarg);
 685        snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", optarg);
 686        break;
 687      case 'e':
 688        diff_type = ED_DIFF;
 689        break;
 690      case 'E':
 691        remove_empty_files = 1;
 692        break;
 693      case 'f':
 694        force = 1;
 695        break;
 696      case 'F':
 697        maxfuzz = strtolinenum(optarg, NULL);
 698        break;
 699      case 'i':
 700        free(filearg[1]);
 701        filearg[1] = fetchname(optarg, &warn_on_invalid_line, 0);
 702        break;
 703      case 'l':
 704        canonicalize = 1;
 705        break;
 706      case 'n':
 707        diff_type = NORMAL_DIFF;
 708        break;
 709      case 'N':
 710        noreverse = 1;
 711        break;
 712      case 'o':
 713        outname = savestr(optarg);
 714        break;
 715      case 'p':
 716        strippath = (int)strtolinenum(optarg, NULL);
 717        break;
 718      case 'r':
 719        if (strlcpy(rejname, optarg, sizeof(rejname)) >= sizeof(rejname))
 720          fatal("reject filename %s too long\n", optarg);
 721        break;
 722      case 'R':
 723        reverse                = 1;
 724        reverse_flag_specified = 1;
 725        break;
 726      case 's':
 727        verbose = 0;
 728        break;
 729      case 't':
 730        batch = 1;
 731        break;
 732      case 'u':
 733        diff_type = UNI_DIFF;
 734        break;
 735      case 'v':
 736        version();
 737        break;
 738      case 'V':
 739        backup_type = get_version(optarg);
 740        break;
 741      case 'x':
 742#ifdef DEBUGGING
 743        debug = (int)strtolinenum(optarg, NULL);
 744#endif
 745        break;
 746      default:
 747        if (ch == 0)
 748          break;
 749        usage();
 750    }
 751  }
 752  Argc -= optind;
 753  Argv += optind;
 754
 755  for (ch = 0; ch < Argc; ch++) {
 756    if (filec >= MAXFILEC)
 757      usage();
 758    filearg[filec++] = savestr(Argv[ch]);
 759  }
 760}
 761
 762static void
 763usage(void)
 764{
 765  fprintf(
 766      stderr,
 767      "usage: patch [-bCcEeflNnRstuv] [-B backup-prefix] [-D symbol] "
 768      "[-d directory] [-F max-fuzz] [-i patchfile] [-o out-file] "
 769      "[-p strip-count] [-r rej-name] [-V method] [-x number] "
 770      "[-z backup-ext] [origfile [patchfile]]\n"
 771  );
 772  my_exit(2);
 773}
 774
 775static LINENUM
 776locate_hunk(LINENUM fuzz)
 777{
 778  LINENUM first_hunk_line = pch_first();
 779  LINENUM count           = pch_ptrn_lines();
 780  LINENUM max_line        = input_lines - count + 1;
 781  LINENUM line;
 782  LINENUM offset;
 783
 784  if (!count)
 785    return first_hunk_line + last_offset;
 786
 787  if (first_hunk_line + last_offset <= 0)
 788    offset = 1 - first_hunk_line;
 789  else if (first_hunk_line + last_offset > max_line)
 790    offset = max_line - first_hunk_line;
 791  else
 792    offset = last_offset;
 793
 794  for (line = first_hunk_line + offset; line <= max_line; line++) {
 795    if (patch_match(line, count, fuzz)) {
 796      last_offset = line - first_hunk_line;
 797      return line;
 798    }
 799  }
 800  for (line = first_hunk_line + offset - 1; line >= 1; line--) {
 801    if (patch_match(line, count, fuzz)) {
 802      last_offset = line - first_hunk_line;
 803      return line;
 804    }
 805  }
 806  return 0;
 807}
 808
 809static int
 810patch_match(LINENUM line, LINENUM count, LINENUM fuzz)
 811{
 812  LINENUM i;
 813  LINENUM pat_line;
 814  char   *str;
 815  char   *pat;
 816  ssize_t len;
 817
 818  for (i = 1; i <= count; i++) {
 819    pat_line = i;
 820    if (i <= fuzz)
 821      continue;
 822    if (i > count - fuzz)
 823      continue;
 824    str = ifetch(line + i - 1 - fuzz, 0);
 825    if (str == NULL)
 826      return 0;
 827    pat = pfetch(pat_line);
 828    len = pch_line_len(pat_line);
 829    if (canonicalize) {
 830      if (!similar(str, pat, len))
 831        return 0;
 832    } else if (strncmp(str, pat, len) != 0)
 833      return 0;
 834  }
 835  return 1;
 836}
 837
 838static int
 839similar(const char *a, const char *b, ssize_t len)
 840{
 841  while (len > 0) {
 842    if (isspace((unsigned char)*b)) {
 843      if (!isspace((unsigned char)*a))
 844        return 0;
 845      while (len > 0 && isspace((unsigned char)*b)) {
 846        b++;
 847        len--;
 848      }
 849      while (isspace((unsigned char)*a) && *a != '\n')
 850        a++;
 851    } else {
 852      if (*a != *b)
 853        return 0;
 854      a++;
 855      b++;
 856      len--;
 857    }
 858  }
 859  if (*a == '\n' || *a == '\r') {
 860    while (*a == '\n' || *a == '\r')
 861      a++;
 862    return *a == '\0';
 863  }
 864  return *a == '\0' || isspace((unsigned char)*a);
 865}
 866
 867static void
 868abort_hunk(void)
 869{
 870  LINENUM i;
 871
 872  for (i = 1; i <= pch_end(); i++)
 873    rej_line(i, i);
 874}
 875
 876static void
 877rej_line(int pat_line, LINENUM i)
 878{
 879  char *str = pfetch(pat_line);
 880  char  ch  = pch_char(pat_line);
 881  (void)i;
 882  if (rejfp != NULL) {
 883    if (ch == ' ')
 884      fprintf(rejfp, " %s", str);
 885    else
 886      fprintf(rejfp, "%c%s", ch, str);
 887  }
 888}
 889
 890static void
 891apply_hunk(LINENUM where)
 892{
 893  LINENUM       old      = 1;
 894  const LINENUM lastline = pch_ptrn_lines();
 895  LINENUM new            = lastline + 1;
 896#define OUTSIDE   0
 897#define IN_IFNDEF 1
 898#define IN_IFDEF  2
 899#define IN_ELSE   3
 900  int           def_state = OUTSIDE;
 901  const LINENUM pat_end   = pch_end();
 902
 903  where--;
 904  while (pch_char(new) == '=' || pch_char(new) == '\n')
 905    new ++;
 906
 907  while (old <= lastline) {
 908    if (pch_char(old) == '-') {
 909      copy_till(where + old - 1, 0);
 910      if (do_defines) {
 911        if (def_state == OUTSIDE) {
 912          fputs(not_defined, ofp);
 913          def_state = IN_IFNDEF;
 914        } else if (def_state == IN_IFDEF) {
 915          fputs(else_defined, ofp);
 916          def_state = IN_ELSE;
 917        }
 918        fputs(pfetch(old), ofp);
 919      }
 920      last_frozen_line++;
 921      old++;
 922    } else if (new > pat_end) {
 923      break;
 924    } else if (pch_char(new) == '+') {
 925      copy_till(where + old - 1, 0);
 926      if (do_defines) {
 927        if (def_state == IN_IFNDEF) {
 928          fputs(else_defined, ofp);
 929          def_state = IN_ELSE;
 930        } else if (def_state == OUTSIDE) {
 931          fputs(if_defined, ofp);
 932          def_state = IN_IFDEF;
 933        }
 934      }
 935      fputs(pfetch(new), ofp);
 936      new ++;
 937    } else if (pch_char(new) != pch_char(old)) {
 938      say("Out-of-sync patch, lines %ld,%ld--mangled text or "
 939          "line numbers, maybe?\n",
 940          pch_hunk_beg() + old,
 941          pch_hunk_beg() + new);
 942      my_exit(2);
 943    } else if (pch_char(new) == '!') {
 944      copy_till(where + old - 1, 0);
 945      if (do_defines) {
 946        fputs(not_defined, ofp);
 947        def_state = IN_IFNDEF;
 948      }
 949      while (pch_char(old) == '!') {
 950        if (do_defines) {
 951          fputs(pfetch(old), ofp);
 952        }
 953        last_frozen_line++;
 954        old++;
 955      }
 956      if (do_defines) {
 957        fputs(else_defined, ofp);
 958        def_state = IN_ELSE;
 959      }
 960      while (pch_char(new) == '!') {
 961        fputs(pfetch(new), ofp);
 962        new ++;
 963      }
 964    } else {
 965      if (pch_char(new) != ' ')
 966        fatal("Internal error: expected ' '\n");
 967      old++;
 968      new ++;
 969      if (do_defines && def_state != OUTSIDE) {
 970        fputs(end_defined, ofp);
 971        def_state = OUTSIDE;
 972      }
 973    }
 974  }
 975  if (new <= pat_end &&pch_char(new) == '+') {
 976    copy_till(where + old - 1, 0);
 977    if (do_defines) {
 978      if (def_state == OUTSIDE) {
 979        fputs(if_defined, ofp);
 980        def_state = IN_IFDEF;
 981      } else if (def_state == IN_IFNDEF) {
 982        fputs(else_defined, ofp);
 983        def_state = IN_ELSE;
 984      }
 985    }
 986    while (new <= pat_end &&pch_char(new) == '+') {
 987      fputs(pfetch(new), ofp);
 988      new ++;
 989    }
 990  }
 991  if (do_defines && def_state != OUTSIDE) {
 992    fputs(end_defined, ofp);
 993  }
 994#undef OUTSIDE
 995#undef IN_IFNDEF
 996#undef IN_IFDEF
 997#undef IN_ELSE
 998}
 999
1000static void
1001dump_input_line(LINENUM line, int write_newline)
1002{
1003  char *s = ifetch(line, 0);
1004  if (s == NULL)
1005    return;
1006  for (; *s != '\n' && *s != '\0'; s++)
1007    putc(*s, ofp);
1008  if (write_newline)
1009    putc('\n', ofp);
1010}
1011
1012static void
1013copy_till(LINENUM line, int end)
1014{
1015  if (last_frozen_line > line)
1016    fatal("misordered hunks! output would be garbled\n");
1017  while (last_frozen_line < line) {
1018    if (++last_frozen_line == line && end)
1019      dump_input_line(last_frozen_line, !last_line_missing_eol);
1020    else
1021      dump_input_line(last_frozen_line, 1);
1022  }
1023}
1024
1025static int
1026spew_output(void)
1027{
1028  if (input_lines)
1029    copy_till(input_lines, 1);
1030  if (ferror(ofp) || fclose(ofp))
1031    return 0;
1032  ofp = NULL;
1033  return 1;
1034}
1035
1036static void
1037init_output(const char *name)
1038{
1039  ofp = fopen(name, "w");
1040  if (ofp == NULL)
1041    pfatal("cant create %s", name);
1042}
1043
1044static void
1045init_reject(const char *name)
1046{
1047  rejfp = fopen(name, "w");
1048  if (rejfp == NULL)
1049    pfatal("cant create %s", name);
1050}
1051
1052void
1053re_input(void)
1054{
1055  if (using_plan_a) {
1056    i_size = 0;
1057    free(i_ptr);
1058    i_ptr = NULL;
1059    if (i_womp != NULL) {
1060      munmap(i_womp, i_size);
1061      i_womp = NULL;
1062    }
1063  } else {
1064    using_plan_a = 1;
1065    close(tifd);
1066    tifd = -1;
1067    free(tibuf[0]);
1068    free(tibuf[1]);
1069    tibuf[0] = tibuf[1] = NULL;
1070    tiline[0] = tiline[1] = -1;
1071    tireclen              = 0;
1072  }
1073}
1074
1075void
1076scan_input(const char *filename)
1077{
1078  if (!plan_a(filename))
1079    plan_b(filename);
1080  if (verbose) {
1081    say("Patching file %s using Plan %s...\n", filename, (using_plan_a ? "A" : "B"));
1082  }
1083}
1084
1085static int
1086reallocate_lines(size_t *lines_allocated)
1087{
1088  char **p;
1089  size_t new_size;
1090
1091  new_size = *lines_allocated * 3 / 2;
1092  p        = pch_realloc(i_ptr, new_size + 2, sizeof(char *));
1093  if (p == NULL) {
1094    munmap(i_womp, i_size);
1095    i_womp = NULL;
1096    free(i_ptr);
1097    i_ptr            = NULL;
1098    *lines_allocated = 0;
1099    return 0;
1100  }
1101  *lines_allocated = new_size;
1102  i_ptr            = p;
1103  return 1;
1104}
1105
1106static int
1107plan_a(const char *filename)
1108{
1109  int         ifd, statfailed;
1110  char       *p, *s, *lbuf;
1111  struct stat filestat;
1112  off_t       i;
1113  ptrdiff_t   sz;
1114  size_t      iline, lines_allocated, lbufsz;
1115
1116#ifdef DEBUGGING
1117  if (debug & 8)
1118    return 0;
1119#endif
1120
1121  if (filename == NULL || *filename == '\0')
1122    return 0;
1123
1124  statfailed = stat(filename, &filestat);
1125  if (statfailed && ok_to_create_file) {
1126    if (verbose)
1127      say("(Creating file %s...)\n", filename);
1128    if (check_only)
1129      return 1;
1130    makedirs(filename, 1);
1131    close(creat(filename, 0666));
1132    statfailed = stat(filename, &filestat);
1133  }
1134  if (statfailed && check_only)
1135    fatal("%s not found, -C mode, cant probe further\n", filename);
1136
1137  if (statfailed || (filestat.st_mode & 0222) == 0
1138      || ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
1139    char       *filebase, *filedir;
1140    struct stat cstat;
1141    char       *tmp_filename1, *tmp_filename2;
1142
1143    tmp_filename1 = strdup(filename);
1144    tmp_filename2 = strdup(filename);
1145    if (tmp_filename1 == NULL || tmp_filename2 == NULL)
1146      fatal("strdupping filename");
1147    filebase = basename(tmp_filename1);
1148    filedir  = dirname(tmp_filename2);
1149
1150    lbufsz = INITLINELEN;
1151    if ((lbuf = malloc(bufsz)) == NULL)
1152      pfatal("allocating line buffer");
1153    lbuf[0] = '\0';
1154
1155#define try(f, a1, a2, a3) (snprintf(lbuf, lbufsz, f, a1, a2, a3), stat(lbuf, &cstat) == 0)
1156
1157    if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX)
1158        || try("%s/RCS/%s%s", filedir, filebase, "")
1159        || try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
1160      if (!statfailed) {
1161        if ((filestat.st_mode & 0222) != 0)
1162          fatal(
1163              "file %s seems to be locked by "
1164              "somebody else under RCS\n",
1165              filename
1166          );
1167        if (verbose)
1168          say("Comparing file %s to default RCS "
1169              "version...\n",
1170              filename);
1171        {
1172          char *rcsdiff_argv[3];
1173          rcsdiff_argv[0] = __UNCONST(RCSDIFF);
1174          rcsdiff_argv[1] = __UNCONST(filename);
1175          rcsdiff_argv[2] = NULL;
1176          if (wexecvp(RCSDIFF, rcsdiff_argv) != 0)
1177            fatal(
1178                "cant check out file %s: "
1179                "differs from default "
1180                "RCS version\n",
1181                filename
1182            );
1183        }
1184      }
1185      if (verbose)
1186        say("Checking out file %s from RCS...\n", filename);
1187      {
1188        char *co_argv[4];
1189        co_argv[0] = __UNCONST(CHECKOUT);
1190        co_argv[1] = __UNCONST("-l");
1191        co_argv[2] = __UNCONST(filename);
1192        co_argv[3] = NULL;
1193        if (wexecvp(CHECKOUT, co_argv) != 0 || stat(filename, &filestat))
1194          fatal(
1195              "cant check out file %s from "
1196              "RCS\n",
1197              filename
1198          );
1199      }
1200    } else if (statfailed) {
1201      fatal("cant find %s\n", filename);
1202    }
1203#undef try
1204    free(lbuf);
1205    free(tmp_filename1);
1206    free(tmp_filename2);
1207  }
1208
1209  filemode = filestat.st_mode;
1210  if (!S_ISREG(filemode))
1211    fatal("%s is not a normal file--cant patch\n", filename);
1212  i_size = filestat.st_size;
1213  if (out_of_mem) {
1214    set_hunkmax();
1215    out_of_mem = 0;
1216    return 0;
1217  }
1218  if ((uintmax_t)i_size > (uintmax_t)SIZE_MAX) {
1219    say("block too large to mmap\n");
1220    return 0;
1221  }
1222  if ((ifd = open(filename, O_RDONLY)) < 0)
1223    pfatal("cant open file %s", filename);
1224
1225  if (i_size) {
1226    i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0);
1227    if (i_womp == MAP_FAILED) {
1228      perror("mmap failed");
1229      i_womp = NULL;
1230      close(ifd);
1231      return 0;
1232    }
1233  } else
1234    i_womp = NULL;
1235
1236  close(ifd);
1237  if (i_size)
1238    madvise(i_womp, i_size, MADV_SEQUENTIAL);
1239
1240  lines_allocated = i_size / 25;
1241  if (lines_allocated < 100)
1242    lines_allocated = 100;
1243
1244  if (!reallocate_lines(&lines_allocated))
1245    return 0;
1246
1247  iline        = 1;
1248  i_ptr[iline] = i_womp;
1249  for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) {
1250    if (*s == '\n') {
1251      if (iline == lines_allocated) {
1252        if (!reallocate_lines(&lines_allocated))
1253          return 0;
1254      }
1255      i_ptr[++iline] = s + 1;
1256    }
1257  }
1258  if (i_size > 0 && i_womp[i_size - 1] != '\n') {
1259    last_line_missing_eol = 1;
1260    sz                    = s - i_ptr[iline];
1261    p                     = malloc(sz + 1);
1262    if (p == NULL) {
1263      free(i_ptr);
1264      i_ptr = NULL;
1265      munmap(i_womp, i_size);
1266      i_womp = NULL;
1267      return 0;
1268    }
1269    memcpy(p, i_ptr[iline], sz);
1270    p[sz]          = '\n';
1271    i_ptr[iline]   = p;
1272    i_ptr[++iline] = empty_line;
1273  } else
1274    last_line_missing_eol = 0;
1275
1276  input_lines = iline - 1;
1277
1278  if (revision != NULL) {
1279    if (!rev_in_string(i_womp)) {
1280      if (force) {
1281        if (verbose)
1282          say("Warning: this file doesnt appear "
1283              "to be the %s version--patching "
1284              "anyway\n",
1285              revision);
1286      } else if (batch) {
1287        fatal(
1288            "this file doesnt appear to be the %s "
1289            "version--aborting\n",
1290            revision
1291        );
1292      } else {
1293        ask("This file doesnt appear to be the %s "
1294            "version--patch anyway? [n] ",
1295            revision);
1296        if (*buf != 'y')
1297          fatal("aborted\n");
1298      }
1299    } else if (verbose)
1300      say("Good. This file appears to be the %s version.\n", revision);
1301  }
1302  return 1;
1303}
1304
1305static void
1306plan_b(const char *filename)
1307{
1308  FILE  *ifp;
1309  size_t i = 0, j, maxlen = 1;
1310  char  *p;
1311  int    found_revision = (revision == NULL);
1312
1313  using_plan_a = 0;
1314  if ((ifp = fopen(filename, "r")) == NULL)
1315    pfatal("cant open file %s", filename);
1316  unlink(TMPINNAME);
1317  if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0)
1318    pfatal("cant open file %s", TMPINNAME);
1319  while (getline(&buf, &bufsz, ifp) != -1) {
1320    if (revision != NULL && !found_revision && rev_in_string(buf))
1321      found_revision = 1;
1322    if ((i = strlen(buf)) > maxlen)
1323      maxlen = i;
1324  }
1325  last_line_missing_eol = i > 0 && buf[i - 1] != '\n';
1326  if (last_line_missing_eol && maxlen == i)
1327    maxlen++;
1328
1329  if (revision != NULL) {
1330    if (!found_revision) {
1331      if (force) {
1332        if (verbose)
1333          say("Warning: this file doesnt appear "
1334              "to be the %s version--patching "
1335              "anyway\n",
1336              revision);
1337      } else if (batch) {
1338        fatal(
1339            "this file doesnt appear to be the %s "
1340            "version--aborting\n",
1341            revision
1342        );
1343      } else {
1344        ask("This file doesnt appear to be the %s "
1345            "version--patch anyway? [n] ",
1346            revision);
1347        if (*buf != 'y')
1348          fatal("aborted\n");
1349      }
1350    } else if (verbose)
1351      say("Good. This file appears to be the %s version.\n", revision);
1352  }
1353  fseek(ifp, 0L, SEEK_SET);
1354  lines_per_buf = BUFFERSIZE / maxlen;
1355  tireclen      = maxlen;
1356  tibuf[0]      = malloc(BUFFERSIZE + 1);
1357  if (tibuf[0] == NULL)
1358    fatal("out of memory\n");
1359  tibuf[1] = malloc(BUFFERSIZE + 1);
1360  if (tibuf[1] == NULL)
1361    fatal("out of memory\n");
1362  for (i = 1;; i++) {
1363    p = tibuf[0] + maxlen * (i % lines_per_buf);
1364    if (i % lines_per_buf == 0) {
1365      if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1366        pfatal("cant write temp file");
1367    }
1368    if (fgets(p, maxlen + 1, ifp) == NULL) {
1369      input_lines = i - 1;
1370      if (i % lines_per_buf != 0) {
1371        if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE)
1372          pfatal("cant write temp file");
1373      }
1374      break;
1375    }
1376    j = strlen(p);
1377    if (j == 0 || p[j - 1] != '\n')
1378      p[j] = '\n';
1379  }
1380  fclose(ifp);
1381  close(tifd);
1382  if ((tifd = open(TMPINNAME, O_RDONLY)) < 0)
1383    pfatal("cant reopen file %s", TMPINNAME);
1384}
1385
1386char *
1387ifetch(LINENUM line, int whichbuf)
1388{
1389  if (line < 1 || line > input_lines) {
1390    if (warn_on_invalid_line) {
1391      say("No such line %ld in input file, ignoring\n", line);
1392      warn_on_invalid_line = 0;
1393    }
1394    return NULL;
1395  }
1396  if (using_plan_a)
1397    return i_ptr[line];
1398  else {
1399    LINENUM offline  = line % lines_per_buf;
1400    LINENUM baseline = line - offline;
1401
1402    if (tiline[0] == baseline)
1403      whichbuf = 0;
1404    else if (tiline[1] == baseline)
1405      whichbuf = 1;
1406    else {
1407      tiline[whichbuf] = baseline;
1408      if (lseek(tifd, (off_t)(baseline / lines_per_buf * BUFFERSIZE), SEEK_SET) < 0)
1409        pfatal(
1410            "cannot seek in the temporary input "
1411            "file"
1412        );
1413      if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0)
1414        pfatal("error reading tmp file %s", TMPINNAME);
1415    }
1416    return tibuf[whichbuf] + (tireclen * offline);
1417  }
1418}
1419
1420static int
1421rev_in_string(const char *string)
1422{
1423  const char *s;
1424  size_t      patlen;
1425
1426  if (revision == NULL)
1427    return 1;
1428  patlen = strlen(revision);
1429  if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen]))
1430    return 1;
1431  for (s = string; *s; s++) {
1432    if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen)
1433        && isspace((unsigned char)s[patlen + 1]))
1434      return 1;
1435  }
1436  return 0;
1437}
1438
1439void
1440re_patch(void)
1441{
1442  p_first      = 0;
1443  p_newfirst   = 0;
1444  p_ptrn_lines = 0;
1445  p_repl_lines = 0;
1446  p_end        = (LINENUM)-1;
1447  p_max        = 0;
1448  p_indent     = 0;
1449}
1450
1451void
1452open_patch_file(const char *filename)
1453{
1454  struct stat filestat;
1455
1456  if (filename == NULL || *filename == '\0' || strEQ(filename, "-")) {
1457    pfp = fopen(TMPPATNAME, "w");
1458    if (pfp == NULL)
1459      pfatal("cant create %s", TMPPATNAME);
1460    while (getline(&buf, &bufsz, stdin) != -1)
1461      fprintf(pfp, "%s", buf);
1462    if (ferror(pfp) || fclose(pfp))
1463      pfatal("cant write %s", TMPPATNAME);
1464    filename = TMPPATNAME;
1465  }
1466  pfp = fopen(filename, "r");
1467  if (pfp == NULL)
1468    pfatal("patch file %s not found", filename);
1469  fstat(fileno(pfp), &filestat);
1470  p_filesize = filestat.st_size;
1471  next_intuit_at(0L, 1L);
1472  set_hunkmax();
1473}
1474
1475void
1476set_hunkmax(void)
1477{
1478  if (p_line == NULL)
1479    p_line = calloc((size_t)hunkmax, sizeof(char *));
1480  if (p_len == NULL)
1481    p_len = calloc((size_t)hunkmax, sizeof(ssize_t));
1482  if (p_char == NULL)
1483    p_char = calloc((size_t)hunkmax, sizeof(char));
1484}
1485
1486static void
1487grow_hunkmax(void)
1488{
1489  int      new_hunkmax;
1490  char   **new_p_line;
1491  ssize_t *new_p_len;
1492  char    *new_p_char;
1493
1494  new_hunkmax = hunkmax * 2;
1495  if (p_line == NULL || p_len == NULL || p_char == NULL)
1496    fatal("Internal memory allocation error\n");
1497
1498  new_p_line = pch_realloc(p_line, new_hunkmax, sizeof(char *));
1499  if (new_p_line == NULL)
1500    free(p_line);
1501  new_p_len = pch_realloc(p_len, new_hunkmax, sizeof(ssize_t));
1502  if (new_p_len == NULL)
1503    free(p_len);
1504  new_p_char = pch_realloc(p_char, new_hunkmax, sizeof(char));
1505  if (new_p_char == NULL)
1506    free(p_char);
1507
1508  p_char = new_p_char;
1509  p_len  = new_p_len;
1510  p_line = new_p_line;
1511
1512  if (p_line != NULL && p_len != NULL && p_char != NULL) {
1513    hunkmax = new_hunkmax;
1514    return;
1515  }
1516
1517  if (!using_plan_a)
1518    fatal("out of memory\n");
1519  out_of_mem = 1;
1520}
1521
1522int
1523there_is_another_patch(void)
1524{
1525  int exists = 0;
1526
1527  if (p_base != 0L && p_base >= p_filesize) {
1528    if (verbose)
1529      say("done\n");
1530    return 0;
1531  }
1532  if (verbose)
1533    say("Hmm...");
1534  diff_type = intuit_diff_type();
1535  if (!diff_type) {
1536    if (p_base != 0L) {
1537      if (verbose)
1538        say(" Ignoring the trailing garbage.\ndone\n");
1539    } else
1540      say(" I cant seem to find a patch in there anywhere\n");
1541    return 0;
1542  }
1543  if (verbose) {
1544    say(" %sooks like %s to me...\n",
1545        (p_base == 0L ? "L" : "The next patch l"),
1546        diff_type == UNI_DIFF           ? "a unified diff"
1547        : diff_type == CONTEXT_DIFF     ? "a context diff"
1548        : diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff"
1549        : diff_type == NORMAL_DIFF      ? "a normal diff"
1550                                        : "an ed script");
1551  }
1552  if (p_indent && verbose)
1553    say("(Patch is indented %d space%s.)\n", p_indent, p_indent == 1 ? "" : "s");
1554  skip_to(p_start, p_sline);
1555  while (filearg[0] == NULL) {
1556    if (force || batch) {
1557      say("No file to patch. Skipping...\n");
1558      filearg[0]         = savestr(bestguess);
1559      skip_rest_of_patch = 1;
1560      return 1;
1561    }
1562    ask("File to patch: ");
1563    if (*buf != '\n') {
1564      free(bestguess);
1565      bestguess  = savestr(buf);
1566      filearg[0] = fetchname(buf, &exists, 0);
1567    }
1568    if (!exists) {
1569      ask("No file found--skip this patch? [n] ");
1570      if (*buf != 'y')
1571        continue;
1572      if (verbose)
1573        say("Skipping patch...\n");
1574      free(filearg[0]);
1575      filearg[0]         = fetchname(bestguess, &exists, 0);
1576      skip_rest_of_patch = 1;
1577      return 1;
1578    }
1579  }
1580  return 1;
1581}
1582
1583static int
1584intuit_diff_type(void)
1585{
1586  long            this_line             = 0, previous_line;
1587  long            first_command_line    = -1;
1588  LINENUM         fcl_line              = -1;
1589  int             last_line_was_command = 0, this_is_a_command = 0;
1590  int             stars_last_line = 0, stars_this_line = 0;
1591  char           *s, *t;
1592  int             indent, retval;
1593  struct FileName names[MAX_FILE];
1594
1595  memset(names, 0, sizeof(names));
1596  ok_to_create_file = 0;
1597  fseek(pfp, p_base, SEEK_SET);
1598  p_input_line = p_bline - 1;
1599  for (;;) {
1600    previous_line         = this_line;
1601    last_line_was_command = this_is_a_command;
1602    stars_last_line       = stars_this_line;
1603    this_line             = ftell(pfp);
1604    indent                = 0;
1605    p_input_line++;
1606    if (getline(&buf, &bufsz, pfp) == -1) {
1607      if (first_command_line >= 0L) {
1608        p_start = first_command_line;
1609        p_sline = fcl_line;
1610        retval  = ED_DIFF;
1611        goto scan_exit;
1612      } else {
1613        p_start = this_line;
1614        p_sline = p_input_line;
1615        retval  = 0;
1616        goto scan_exit;
1617      }
1618    }
1619    for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
1620      if (*s == '\t')
1621        indent += 8 - (indent % 8);
1622      else
1623        indent++;
1624    }
1625    for (t = s; isdigit((unsigned char)*t) || *t == ','; t++)
1626      ;
1627    this_is_a_command = (isdigit((unsigned char)*s) && (*t == 'd' || *t == 'c' || *t == 'a'));
1628    if (first_command_line < 0L && this_is_a_command) {
1629      first_command_line = this_line;
1630      fcl_line           = p_input_line;
1631      p_indent           = indent;
1632    }
1633    if (!stars_last_line && strnEQ(s, "*** ", 4)) {
1634      names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1635    } else if (strnEQ(s, "--- ", 4)) {
1636      names[NEW_FILE].path = fetchname(s + 4, &names[NEW_FILE].exists, strippath);
1637    } else if (strnEQ(s, "+++ ", 4)) {
1638      names[OLD_FILE].path = fetchname(s + 4, &names[OLD_FILE].exists, strippath);
1639    } else if (strnEQ(s, "Index:", 6)) {
1640      names[INDEX_FILE].path = fetchname(s + 6, &names[INDEX_FILE].exists, strippath);
1641    } else if (strnEQ(s, "Prereq:", 7)) {
1642      for (t = s + 7; isspace((unsigned char)*t); t++)
1643        ;
1644      revision = savestr(t);
1645      for (t = revision; *t && !isspace((unsigned char)*t); t++)
1646        ;
1647      *t = '\0';
1648      if (*revision == '\0') {
1649        free(revision);
1650        revision = NULL;
1651      }
1652    }
1653    if ((!diff_type || diff_type == ED_DIFF) && first_command_line >= 0L && strEQ(s, ".\n")) {
1654      p_indent = indent;
1655      p_start  = first_command_line;
1656      p_sline  = fcl_line;
1657      retval   = ED_DIFF;
1658      goto scan_exit;
1659    }
1660    if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) {
1661      if (strnEQ(s + 4, "0,0", 3))
1662        ok_to_create_file = 1;
1663      p_indent = indent;
1664      p_start  = this_line;
1665      p_sline  = p_input_line;
1666      retval   = UNI_DIFF;
1667      goto scan_exit;
1668    }
1669    stars_this_line = strnEQ(s, "********", 8);
1670    if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && strnEQ(s, "*** ", 4)) {
1671      if (atol(s + 4) == 0)
1672        ok_to_create_file = 1;
1673      while (*s != '\n')
1674        s++;
1675      p_indent = indent;
1676      p_start  = previous_line;
1677      p_sline  = p_input_line - 1;
1678      retval   = (*(s - 1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
1679      goto scan_exit;
1680    }
1681    if ((!diff_type || diff_type == NORMAL_DIFF) && last_line_was_command
1682        && (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2))) {
1683      p_start  = previous_line;
1684      p_sline  = p_input_line - 1;
1685      p_indent = indent;
1686      retval   = NORMAL_DIFF;
1687      goto scan_exit;
1688    }
1689  }
1690scan_exit:
1691  if (retval == UNI_DIFF) {
1692    struct FileName tmp = names[OLD_FILE];
1693    names[OLD_FILE]     = names[NEW_FILE];
1694    names[NEW_FILE]     = tmp;
1695  }
1696  if (filearg[0] == NULL) {
1697    if (posix)
1698      filearg[0] = posix_name(names, ok_to_create_file);
1699    else {
1700      if (names[OLD_FILE].path != NULL || names[NEW_FILE].path != NULL) {
1701        free(names[INDEX_FILE].path);
1702        names[INDEX_FILE].path = NULL;
1703      }
1704      filearg[0] = best_name(names, ok_to_create_file);
1705    }
1706  }
1707  free(bestguess);
1708  bestguess = NULL;
1709  if (filearg[0] != NULL)
1710    bestguess = savestr(filearg[0]);
1711  else if (!ok_to_create_file) {
1712    if (posix)
1713      bestguess = posix_name(names, 1);
1714    else
1715      bestguess = best_name(names, 1);
1716  }
1717  free(names[OLD_FILE].path);
1718  free(names[NEW_FILE].path);
1719  free(names[INDEX_FILE].path);
1720  return retval;
1721}
1722
1723static void
1724next_intuit_at(LINENUM file_pos, LINENUM file_line)
1725{
1726  p_base  = file_pos;
1727  p_bline = file_line;
1728}
1729
1730static void
1731skip_to(LINENUM file_pos, LINENUM file_line)
1732{
1733  int ret;
1734
1735  if (p_base > file_pos)
1736    fatal("Internal error: seek %ld>%ld\n", p_base, file_pos);
1737  if (verbose && p_base < file_pos) {
1738    fseek(pfp, p_base, SEEK_SET);
1739    say("The text leading up to this "
1740        "was:\n--------------------------\n");
1741    while (ftell(pfp) < file_pos) {
1742      ret = getline(&buf, &bufsz, pfp);
1743      if (ret == -1)
1744        fatal("Unexpected end of file\n");
1745      say("|%s", buf);
1746    }
1747    say("--------------------------\n");
1748  } else
1749    fseek(pfp, file_pos, SEEK_SET);
1750  p_input_line = file_line - 1;
1751}
1752
1753__dead static void
1754malformed(void)
1755{
1756  fatal("malformed patch at line %ld: %s", p_input_line, buf);
1757}
1758
1759static LINENUM
1760getlinenum(const char *s)
1761{
1762  LINENUM l = (LINENUM)atol(s);
1763  if (l < 0) {
1764    l = 0;
1765    malformed();
1766  }
1767  return l;
1768}
1769
1770static LINENUM
1771getskiplinenum(char **p)
1772{
1773  char   *s = *p;
1774  LINENUM l = getlinenum(s);
1775  while (isdigit((unsigned char)*s))
1776    s++;
1777  *p = s;
1778  return l;
1779}
1780
1781static int
1782remove_special_line(void)
1783{
1784  int c;
1785
1786  c = fgetc(pfp);
1787  if (c == '\\') {
1788    do {
1789      c = fgetc(pfp);
1790    } while (c != EOF && c != '\n');
1791    return 1;
1792  }
1793  if (c != EOF)
1794    fseek(pfp, -1L, SEEK_CUR);
1795  return 0;
1796}
1797
1798int
1799another_hunk(void)
1800{
1801  long    line_beginning;
1802  LINENUM repl_beginning;
1803  LINENUM fillcnt;
1804  LINENUM fillsrc;
1805  LINENUM filldst;
1806  int     ptrn_spaces_eaten;
1807  int     repl_could_be_missing;
1808  int     repl_missing;
1809  long    repl_backtrack_position;
1810  LINENUM repl_patch_line;
1811  LINENUM ptrn_copiable;
1812  char   *s;
1813  int     context = 0;
1814  int     ret;
1815
1816  while (p_end >= 0) {
1817    if (p_end == p_efake)
1818      p_end = p_bfake;
1819    else
1820      free(p_line[p_end]);
1821    p_end--;
1822  }
1823  p_efake = -1;
1824  p_max   = hunkmax;
1825
1826  if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
1827    line_beginning          = ftell(pfp);
1828    repl_beginning          = 0;
1829    fillcnt                 = 0;
1830    fillsrc                 = 0;
1831    filldst                 = 0;
1832    ptrn_spaces_eaten       = 0;
1833    repl_could_be_missing   = 1;
1834    repl_missing            = 0;
1835    repl_backtrack_position = 0;
1836    repl_patch_line         = 0;
1837    ptrn_copiable           = 0;
1838
1839    ret = pgetline(&buf, &bufsz, pfp);
1840    p_input_line++;
1841    if (ret == -1 || strnNE(buf, "********", 8)) {
1842      next_intuit_at(line_beginning, p_input_line);
1843      return 0;
1844    }
1845    p_context  = 100;
1846    p_hunk_beg = p_input_line + 1;
1847    while (p_end < p_max) {
1848      ret = pgetline(&buf, &bufsz, pfp);
1849      p_input_line++;
1850      if (ret == -1) {
1851        if (repl_beginning && repl_could_be_missing) {
1852          repl_missing = 1;
1853          goto hunk_done;
1854        }
1855        fatal("unexpected end of file in patch\n");
1856      }
1857      p_end++;
1858      if (p_end >= hunkmax)
1859        fatal(
1860            "Internal error: hunk larger than hunk "
1861            "buffer size"
1862        );
1863      p_char[p_end] = *buf;
1864      p_line[p_end] = NULL;
1865      switch (*buf) {
1866        case '*':
1867          if (strnEQ(buf, "********", 8)) {
1868            if (repl_beginning && repl_could_be_missing) {
1869              repl_missing = 1;
1870              goto hunk_done;
1871            } else
1872              fatal(
1873                  "unexpected end of hunk "
1874                  "at line %ld\n",
1875                  p_input_line
1876              );
1877          }
1878          if (p_end != 0) {
1879            if (repl_beginning && repl_could_be_missing) {
1880              repl_missing = 1;
1881              goto hunk_done;
1882            }
1883            fatal("unexpected *** at line %ld: %s", p_input_line, buf);
1884          }
1885          context       = 0;
1886          p_line[p_end] = savestr(buf);
1887          if (out_of_mem) {
1888            p_end--;
1889            return 0;
1890          }
1891          for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1892            ;
1893          if (!*s)
1894            malformed();
1895          if (strnEQ(s, "0,0", 3))
1896            memmove(s, s + 2, strlen(s + 2) + 1);
1897          p_first = getskiplinenum(&s);
1898          if (*s == ',') {
1899            for (; *s && !isdigit((unsigned char)*s); s++)
1900              ;
1901            if (!*s)
1902              malformed();
1903            p_ptrn_lines = (getlinenum(s)) - p_first + 1;
1904            if (p_ptrn_lines < 0)
1905              malformed();
1906          } else if (p_first)
1907            p_ptrn_lines = 1;
1908          else {
1909            p_ptrn_lines = 0;
1910            p_first      = 1;
1911          }
1912          if (p_first >= LINENUM_MAX - p_ptrn_lines || p_ptrn_lines >= LINENUM_MAX - 6)
1913            malformed();
1914          p_max = p_ptrn_lines + 6;
1915          while (p_max >= hunkmax)
1916            grow_hunkmax();
1917          p_max = hunkmax;
1918          break;
1919        case '-':
1920          if (buf[1] == '-') {
1921            if (repl_beginning || (p_end != p_ptrn_lines + 1 + (p_char[p_end - 1] == '\n'))) {
1922              if (p_end == 1) {
1923                p_end   = p_ptrn_lines + 1;
1924                fillsrc = p_end + 1;
1925                filldst = 1;
1926                fillcnt = p_ptrn_lines;
1927              } else {
1928                if (repl_beginning) {
1929                  if (repl_could_be_missing) {
1930                    repl_missing = 1;
1931                    goto hunk_done;
1932                  }
1933                  fatal(
1934                      "duplicat"
1935                      "e "
1936                      "\"---\" "
1937                      "at line "
1938                      "%ld\n",
1939                      p_input_line
1940                  );
1941                } else {
1942                  fatal(
1943                      "prematur"
1944                      "e/"
1945                      "overdue "
1946                      "\"---\" "
1947                      "at line "
1948                      "%ld\n",
1949                      p_input_line
1950                  );
1951                }
1952              }
1953            }
1954            repl_beginning          = p_end;
1955            repl_backtrack_position = ftell(pfp);
1956            repl_patch_line         = p_input_line;
1957            p_line[p_end]           = savestr(buf);
1958            if (out_of_mem) {
1959              p_end--;
1960              return 0;
1961            }
1962            p_char[p_end] = '=';
1963            for (s = buf; *s && !isdigit((unsigned char)*s); s++)
1964              ;
1965            if (!*s)
1966              malformed();
1967            p_newfirst = getskiplinenum(&s);
1968            if (*s == ',') {
1969              for (; *s && !isdigit((unsigned char)*s); s++)
1970                ;
1971              if (!*s)
1972                malformed();
1973              p_repl_lines = (getlinenum(s)) - p_newfirst + 1;
1974              if (p_repl_lines < 0)
1975                malformed();
1976            } else if (p_newfirst)
1977              p_repl_lines = 1;
1978            else {
1979              p_repl_lines = 0;
1980              p_newfirst   = 1;
1981            }
1982            if (p_newfirst >= LINENUM_MAX - p_repl_lines || p_repl_lines >= LINENUM_MAX - p_end)
1983              malformed();
1984            p_max = p_repl_lines + p_end;
1985            if (p_max > MAXHUNKSIZE)
1986              fatal(
1987                  "hunk too large at line "
1988                  "%ld\n",
1989                  p_input_line
1990              );
1991            while (p_max >= hunkmax)
1992              grow_hunkmax();
1993            if (p_repl_lines != ptrn_copiable && (p_context != 0 || p_repl_lines != 1))
1994              repl_could_be_missing = 0;
1995            break;
1996          }
1997          goto change_line;
1998        case '+':
1999        case '!':
2000          repl_could_be_missing = 0;
2001        change_line:
2002          if (buf[1] == '\n' && canonicalize)
2003            strlcpy(buf + 1, " \n", bufsz - 1);
2004          if (!isspace((unsigned char)buf[1]) && buf[1] != '>' && buf[1] != '<' && repl_beginning
2005              && repl_could_be_missing) {
2006            repl_missing = 1;
2007            goto hunk_done;
2008          }
2009          if (context >= 0) {
2010            if (context < p_context)
2011              p_context = context;
2012            context = -1000;
2013          }
2014          p_line[p_end] = savestr(buf + 2);
2015          if (out_of_mem) {
2016            p_end--;
2017            return 0;
2018          }
2019          if (p_end == p_ptrn_lines) {
2020            if (remove_special_line()) {
2021              int length              = strlen(p_line[p_end]) - 1;
2022              (p_line[p_end])[length] = 0;
2023            }
2024          }
2025          break;
2026        case '\t':
2027        case '\n':
2028          if (repl_beginning && repl_could_be_missing
2029              && (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF)) {
2030            repl_missing = 1;
2031            goto hunk_done;
2032          }
2033          p_line[p_end] = savestr(buf);
2034          if (out_of_mem) {
2035            p_end--;
2036            return 0;
2037          }
2038          if (p_end != p_ptrn_lines + 1) {
2039            ptrn_spaces_eaten |= (repl_beginning != 0);
2040            context++;
2041            if (!repl_beginning)
2042              ptrn_copiable++;
2043            p_char[p_end] = ' ';
2044          }
2045          break;
2046        case ' ':
2047          if (!isspace((unsigned char)buf[1]) && repl_beginning && repl_could_be_missing) {
2048            repl_missing = 1;
2049            goto hunk_done;
2050          }
2051          context++;
2052          if (!repl_beginning)
2053            ptrn_copiable++;
2054          p_line[p_end] = savestr(buf + 2);
2055          if (out_of_mem) {
2056            p_end--;
2057            return 0;
2058          }
2059          break;
2060        default:
2061          if (repl_beginning && repl_could_be_missing) {
2062            repl_missing = 1;
2063            goto hunk_done;
2064          }
2065          malformed();
2066      }
2067      if (p_line[p_end])
2068        p_len[p_end] = strlen(p_line[p_end]);
2069      else
2070        p_len[p_end] = 0;
2071    }
2072
2073  hunk_done:
2074    if (p_end >= 0 && !repl_beginning)
2075      fatal("no --- found in patch at line %ld\n", pch_hunk_beg());
2076
2077    if (repl_missing) {
2078      p_input_line = repl_patch_line;
2079      for (p_end--; p_end > repl_beginning; p_end--)
2080        free(p_line[p_end]);
2081      fseek(pfp, repl_backtrack_position, SEEK_SET);
2082
2083      if (!p_context && p_repl_lines == 1) {
2084        p_repl_lines = 0;
2085        p_max--;
2086      }
2087      fillsrc = 1;
2088      filldst = repl_beginning + 1;
2089      fillcnt = p_repl_lines;
2090      p_end   = p_max;
2091    } else if (!p_context && fillcnt == 1) {
2092      while (filldst < p_end) {
2093        p_line[filldst] = p_line[filldst + 1];
2094        p_char[filldst] = p_char[filldst + 1];
2095        p_len[filldst]  = p_len[filldst + 1];
2096        filldst++;
2097      }
2098      p_end--;
2099      p_first++;
2100      fillcnt      = 0;
2101      p_ptrn_lines = 0;
2102    }
2103    if (diff_type == CONTEXT_DIFF && (fillcnt || (p_first > 1 && ptrn_copiable > 2 * p_context))) {
2104      diff_type = NEW_CONTEXT_DIFF;
2105    }
2106    if (fillcnt) {
2107      p_bfake = filldst;
2108      p_efake = filldst + fillcnt - 1;
2109      while (fillcnt-- > 0) {
2110        while (fillsrc <= p_end && p_char[fillsrc] != ' ')
2111          fillsrc++;
2112        if (fillsrc > p_end)
2113          fatal(
2114              "replacement text mangled in "
2115              "hunk at line %ld\n",
2116              p_hunk_beg
2117          );
2118        p_line[filldst] = p_line[fillsrc];
2119        p_char[filldst] = p_char[fillsrc];
2120        p_len[filldst]  = p_len[fillsrc];
2121        fillsrc++;
2122        filldst++;
2123      }
2124      while (fillsrc <= p_end && fillsrc != repl_beginning && p_char[fillsrc] != ' ')
2125        fillsrc++;
2126      if (fillsrc != p_end + 1 && fillsrc != repl_beginning)
2127        malformed();
2128      if (filldst != p_end + 1 && filldst != repl_beginning)
2129        malformed();
2130    }
2131    if (p_line[p_end] != NULL) {
2132      if (remove_special_line()) {
2133        p_len[p_end] -= 1;
2134        (p_line[p_end])[p_len[p_end]] = 0;
2135      }
2136    }
2137  } else if (diff_type == UNI_DIFF) {
2138    LINENUM fillold;
2139    LINENUM fillnew;
2140    char    ch;
2141
2142    line_beginning = ftell(pfp);
2143    ret            = pgetline(&buf, &bufsz, pfp);
2144    p_input_line++;
2145    if (ret == -1 || strnNE(buf, "@@ -", 4)) {
2146      next_intuit_at(line_beginning, p_input_line);
2147      return 0;
2148    }
2149    s = buf + 4;
2150    if (!*s)
2151      malformed();
2152    p_first = getskiplinenum(&s);
2153    if (*s == ',') {
2154      s++;
2155      p_ptrn_lines = getskiplinenum(&s);
2156    } else
2157      p_ptrn_lines = 1;
2158    if (p_first >= LINENUM_MAX - p_ptrn_lines)
2159      malformed();
2160    if (*s == ' ')
2161      s++;
2162    if (*s != '+' || !*++s)
2163      malformed();
2164    p_newfirst = getskiplinenum(&s);
2165    if (*s == ',') {
2166      s++;
2167      p_repl_lines = getskiplinenum(&s);
2168    } else
2169      p_repl_lines = 1;
2170    if (*s == ' ')
2171      s++;
2172    if (*s != '@')
2173      malformed();
2174    if (p_first >= LINENUM_MAX - p_ptrn_lines || p_newfirst > LINENUM_MAX - p_repl_lines
2175        || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2176      malformed();
2177    if (!p_ptrn_lines)
2178      p_first++;
2179    p_max = p_ptrn_lines + p_repl_lines + 1;
2180    while (p_max >= hunkmax)
2181      grow_hunkmax();
2182    fillold = 1;
2183    fillnew = fillold + p_ptrn_lines;
2184    p_end   = fillnew + p_repl_lines;
2185    snprintf(buf, bufsz, "*** %ld,%ld ****\n", p_first, p_first + p_ptrn_lines - 1);
2186    p_line[0] = savestr(buf);
2187    if (out_of_mem) {
2188      p_end = -1;
2189      return 0;
2190    }
2191    p_char[0] = '*';
2192    snprintf(buf, bufsz, "--- %ld,%ld ----\n", p_newfirst, p_newfirst + p_repl_lines - 1);
2193    p_line[fillnew] = savestr(buf);
2194    if (out_of_mem) {
2195      p_end = 0;
2196      return 0;
2197    }
2198    p_char[fillnew++] = '=';
2199    p_context         = 100;
2200    context           = 0;
2201    p_hunk_beg        = p_input_line + 1;
2202    while (fillold <= p_ptrn_lines || fillnew <= p_end) {
2203      ret = pgetline(&buf, &bufsz, pfp);
2204      p_input_line++;
2205      if (ret == -1) {
2206        if (p_max - fillnew < 3)
2207          strlcpy(buf, " \n", bufsz);
2208        else
2209          fatal(
2210              "unexpected end of file in "
2211              "patch\n"
2212          );
2213      }
2214      if (*buf == '\t' || *buf == '\n') {
2215        ch = ' ';
2216        s  = savestr(buf);
2217      } else {
2218        ch = *buf;
2219        s  = savestr(buf + 1);
2220      }
2221      if (out_of_mem) {
2222        while (--fillnew > p_ptrn_lines)
2223          free(p_line[fillnew]);
2224        p_end = fillold - 1;
2225        return 0;
2226      }
2227      switch (ch) {
2228        case '-':
2229          if (fillold > p_ptrn_lines) {
2230            free(s);
2231            p_end = fillnew - 1;
2232            malformed();
2233          }
2234          p_char[fillold]  = ch;
2235          p_line[fillold]  = s;
2236          p_len[fillold++] = strlen(s);
2237          if (fillold > p_ptrn_lines) {
2238            if (remove_special_line()) {
2239              p_len[fillold - 1] -= 1;
2240              s[p_len[fillold - 1]] = 0;
2241            }
2242          }
2243          break;
2244        case '=':
2245          ch = ' ';
2246          /* FALL THROUGH */
2247        case ' ':
2248          if (fillold > p_ptrn_lines) {
2249            free(s);
2250            while (--fillnew > p_ptrn_lines)
2251              free(p_line[fillnew]);
2252            p_end = fillold - 1;
2253            malformed();
2254          }
2255          context++;
2256          p_char[fillold]  = ch;
2257          p_line[fillold]  = s;
2258          p_len[fillold++] = strlen(s);
2259          s                = savestr(s);
2260          if (out_of_mem) {
2261            while (--fillnew > p_ptrn_lines)
2262              free(p_line[fillnew]);
2263            p_end = fillold - 1;
2264            return 0;
2265          }
2266          if (fillold > p_ptrn_lines) {
2267            if (remove_special_line()) {
2268              p_len[fillold - 1] -= 1;
2269              s[p_len[fillold - 1]] = 0;
2270            }
2271          }
2272          /* FALL THROUGH */
2273        case '+':
2274          if (fillnew > p_end) {
2275            free(s);
2276            while (--fillnew > p_ptrn_lines)
2277              free(p_line[fillnew]);
2278            p_end = fillold - 1;
2279            malformed();
2280          }
2281          p_char[fillnew]  = ch;
2282          p_line[fillnew]  = s;
2283          p_len[fillnew++] = strlen(s);
2284          if (fillold > p_ptrn_lines) {
2285            if (remove_special_line()) {
2286              p_len[fillnew - 1] -= 1;
2287              s[p_len[fillnew - 1]] = 0;
2288            }
2289          }
2290          break;
2291        default:
2292          p_end = fillnew;
2293          malformed();
2294      }
2295      if (ch != ' ' && context > 0) {
2296        if (context < p_context)
2297          p_context = context;
2298        context = -1000;
2299      }
2300    }
2301  } else {
2302    char    hunk_type;
2303    int     i;
2304    LINENUM min, max;
2305
2306    line_beginning = ftell(pfp);
2307    p_context      = 0;
2308    ret            = pgetline(&buf, &bufsz, pfp);
2309    p_input_line++;
2310    if (ret == -1 || !isdigit((unsigned char)*buf)) {
2311      next_intuit_at(line_beginning, p_input_line);
2312      return 0;
2313    }
2314    s       = buf;
2315    p_first = getskiplinenum(&s);
2316    if (*s == ',') {
2317      s++;
2318      p_ptrn_lines = getskiplinenum(&s) - p_first + 1;
2319    } else
2320      p_ptrn_lines = (*s != 'a');
2321    if (p_first >= LINENUM_MAX - p_ptrn_lines)
2322      malformed();
2323    hunk_type = *s++;
2324    if (hunk_type == 'a')
2325      p_first++;
2326    min = getskiplinenum(&s);
2327    if (*s == ',')
2328      max = getlinenum(++s);
2329    else
2330      max = min;
2331    if (min < 0 || min > max || max - min == LINENUM_MAX)
2332      malformed();
2333    if (hunk_type == 'd')
2334      min++;
2335    p_end        = p_ptrn_lines + p_repl_lines + 1;
2336    p_newfirst   = min;
2337    p_repl_lines = max - min + 1;
2338    if (p_newfirst > LINENUM_MAX - p_repl_lines || p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
2339      malformed();
2340    p_end = p_ptrn_lines + p_repl_lines + 1;
2341    if (p_end > MAXHUNKSIZE)
2342      fatal("hunk too large at line %ld\n", p_input_line);
2343    while (p_end >= hunkmax)
2344      grow_hunkmax();
2345    snprintf(buf, bufsz, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
2346    p_line[0] = savestr(buf);
2347    if (out_of_mem) {
2348      p_end = -1;
2349      return 0;
2350    }
2351    p_char[0] = '*';
2352    for (i = 1; i <= p_ptrn_lines; i++) {
2353      ret = pgetline(&buf, &bufsz, pfp);
2354      p_input_line++;
2355      if (ret == -1)
2356        fatal(
2357            "unexpected end of file in patch at line "
2358            "%ld\n",
2359            p_input_line
2360        );
2361      if (*buf != '<')
2362        fatal("< expected at line %ld of patch\n", p_input_line);
2363      p_line[i] = savestr(buf + 2);
2364      if (out_of_mem) {
2365        p_end = i - 1;
2366        return 0;
2367      }
2368      p_len[i]  = strlen(p_line[i]);
2369      p_char[i] = '-';
2370    }
2371    if (remove_special_line()) {
2372      p_len[i - 1] -= 1;
2373      (p_line[i - 1])[p_len[i - 1]] = 0;
2374    }
2375    if (hunk_type == 'c') {
2376      ret = pgetline(&buf, &bufsz, pfp);
2377      p_input_line++;
2378      if (ret == -1)
2379        fatal(
2380            "unexpected end of file in patch at line "
2381            "%ld\n",
2382            p_input_line
2383        );
2384      if (*buf != '-')
2385        fatal("--- expected at line %ld of patch\n", p_input_line);
2386    }
2387    snprintf(buf, bufsz, "--- %ld,%ld\n", min, max);
2388    p_line[i] = savestr(buf);
2389    if (out_of_mem) {
2390      p_end = i - 1;
2391      return 0;
2392    }
2393    p_char[i] = '=';
2394    for (i++; i <= p_end; i++) {
2395      ret = pgetline(&buf, &bufsz, pfp);
2396      p_input_line++;
2397      if (ret == -1)
2398        fatal(
2399            "unexpected end of file in patch at line "
2400            "%ld\n",
2401            p_input_line
2402        );
2403      if (*buf != '>')
2404        fatal("> expected at line %ld of patch\n", p_input_line);
2405      p_line[i] = savestr(buf + 2);
2406      if (out_of_mem) {
2407        p_end = i - 1;
2408        return 0;
2409      }
2410      p_len[i]  = strlen(p_line[i]);
2411      p_char[i] = '+';
2412    }
2413    if (remove_special_line()) {
2414      p_len[i - 1] -= 1;
2415      (p_line[i - 1])[p_len[i - 1]] = 0;
2416    }
2417  }
2418  if (reverse) {
2419    if (!pch_swap())
2420      say("Not enough memory to swap next hunk!\n");
2421  }
2422  if (p_end + 1 < hunkmax)
2423    p_char[p_end + 1] = '^';
2424  return 1;
2425}
2426
2427int
2428pgetline(char **bf, size_t *sz, FILE *fp)
2429{
2430  char *s;
2431  int   indent = 0;
2432  int   ret;
2433
2434  ret = getline(bf, sz, fp);
2435  if (p_indent && ret != -1) {
2436    for (s = buf; indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
2437      if (*s == '\t')
2438        indent += 8 - (indent % 7);
2439      else
2440        indent++;
2441    }
2442    if (buf != s && strlcpy(buf, s, bufsz) >= bufsz)
2443      fatal("buffer too small in pgetline()\n");
2444  }
2445  return ret;
2446}
2447
2448int
2449pch_swap(void)
2450{
2451  char   **tp_line;
2452  ssize_t *tp_len;
2453  char    *tp_char;
2454  LINENUM  i;
2455  LINENUM  n;
2456  int      blankline = 0;
2457  char    *s;
2458
2459  i          = p_first;
2460  p_first    = p_newfirst;
2461  p_newfirst = i;
2462
2463  tp_line = p_line;
2464  tp_len  = p_len;
2465  tp_char = p_char;
2466  p_line  = NULL;
2467  p_len   = NULL;
2468  p_char  = NULL;
2469  set_hunkmax();
2470  if (p_line == NULL || p_len == NULL || p_char == NULL) {
2471    free(p_line);
2472    p_line = tp_line;
2473    free(p_len);
2474    p_len = tp_len;
2475    free(p_char);
2476    p_char = tp_char;
2477    return 0;
2478  }
2479
2480  i = p_ptrn_lines + 1;
2481  if (tp_char[i] == '\n') {
2482    blankline = 1;
2483    i++;
2484  }
2485  if (p_efake >= 0) {
2486    if (p_efake <= i)
2487      n = p_end - i + 1;
2488    else
2489      n = -i;
2490    p_efake += n;
2491    p_bfake += n;
2492  }
2493  for (n = 0; i <= p_end; i++, n++) {
2494    p_line[n] = tp_line[i];
2495    p_char[n] = tp_char[i];
2496    if (p_char[n] == '+')
2497      p_char[n] = '-';
2498    p_len[n] = tp_len[i];
2499  }
2500  if (blankline) {
2501    i         = p_ptrn_lines + 1;
2502    p_line[n] = tp_line[i];
2503    p_char[n] = tp_char[i];
2504    p_len[n]  = tp_len[i];
2505    n++;
2506  }
2507  if (p_char[0] != '=')
2508    fatal("expected '=' found '%c'\n", p_char[0]);
2509  p_char[0] = '*';
2510  for (s = p_line[0]; *s; s++)
2511    if (*s == '-')
2512      *s = '*';
2513
2514  if (p_char[0] != '*')
2515    fatal("expected '*' found '%c'\n", p_char[0]);
2516  tp_char[0] = '=';
2517  for (s = tp_line[0]; *s; s++)
2518    if (*s == '*')
2519      *s = '-';
2520  for (i = 0; n <= p_end; i++, n++) {
2521    p_line[n] = tp_line[i];
2522    p_char[n] = tp_char[i];
2523    if (p_char[n] == '-')
2524      p_char[n] = '+';
2525    p_len[n] = tp_len[i];
2526  }
2527  if (i != p_ptrn_lines + 1)
2528    fatal("expected %ld lines, got %ld\n", p_ptrn_lines + 1, i);
2529
2530  i            = p_ptrn_lines;
2531  p_ptrn_lines = p_repl_lines;
2532  p_repl_lines = i;
2533
2534  free(tp_line);
2535  free(tp_len);
2536  free(tp_char);
2537  return 1;
2538}
2539
2540LINENUM
2541pch_first(void)
2542{
2543  return p_first;
2544}
2545
2546LINENUM
2547pch_ptrn_lines(void)
2548{
2549  return p_ptrn_lines;
2550}
2551
2552LINENUM
2553pch_newfirst(void)
2554{
2555  return p_newfirst;
2556}
2557
2558LINENUM
2559pch_repl_lines(void)
2560{
2561  return p_repl_lines;
2562}
2563
2564LINENUM
2565pch_end(void)
2566{
2567  return p_end;
2568}
2569
2570LINENUM
2571pch_context(void)
2572{
2573  return p_context;
2574}
2575
2576ssize_t
2577pch_line_len(LINENUM line)
2578{
2579  return p_len[line];
2580}
2581
2582char
2583pch_char(LINENUM line)
2584{
2585  return p_char[line];
2586}
2587
2588char *
2589pfetch(LINENUM line)
2590{
2591  return p_line[line];
2592}
2593
2594LINENUM
2595pch_hunk_beg(void)
2596{
2597  return p_hunk_beg;
2598}
2599
2600void
2601do_ed_script(void)
2602{
2603  char *t;
2604  long  beginning_of_this_line;
2605  FILE *pipefp = NULL;
2606  int   continuation;
2607
2608  if (!skip_rest_of_patch) {
2609    if (copy_file(filearg[0], TMPOUTNAME) < 0) {
2610      unlink(TMPOUTNAME);
2611      fatal("cant create temp file %s", TMPOUTNAME);
2612    }
2613    snprintf(buf, bufsz, "%s -S%s %s", _PATH_ED, verbose ? "" : "s", TMPOUTNAME);
2614    pipefp = wpopen(buf, NULL, "w");
2615  }
2616  for (;;) {
2617    beginning_of_this_line = ftell(pfp);
2618    if (pgetline(&buf, &bufsz, pfp) == -1) {
2619      next_intuit_at(beginning_of_this_line, p_input_line);
2620      break;
2621    }
2622    p_input_line++;
2623    for (t = buf; isdigit((unsigned char)*t) || *t == ','; t++)
2624      ;
2625    if (isdigit((unsigned char)*buf)
2626        && (*t == 'a' || *t == 'c' || *t == 'd' || *t == 'i' || *t == 's')) {
2627      if (pipefp != NULL)
2628        fprintf(pipefp, "%s", buf);
2629      if (*t == 's') {
2630        for (;;) {
2631          continuation = 0;
2632          t            = strchr(buf, '\0') - 1;
2633          while (--t >= buf && *t == '\\')
2634            continuation = !continuation;
2635          if (!continuation || pgetline(&buf, &bufsz, pfp) == -1)
2636            break;
2637          if (pipefp != NULL)
2638            fprintf(pipefp, "%s", buf);
2639        }
2640      } else if (*t != 'd') {
2641        while (pgetline(&buf, &bufsz, pfp) != -1) {
2642          p_input_line++;
2643          if (pipefp != NULL)
2644            fprintf(pipefp, "%s", buf);
2645          if (strEQ(buf, ".\n"))
2646            break;
2647        }
2648      }
2649    } else {
2650      next_intuit_at(beginning_of_this_line, p_input_line);
2651      break;
2652    }
2653  }
2654  if (pipefp == NULL)
2655    return;
2656  fprintf(pipefp, "w\n");
2657  fprintf(pipefp, "q\n");
2658  fflush(pipefp);
2659  wpclose(pipefp);
2660  ignore_signals();
2661  if (!check_only) {
2662    if (move_file(TMPOUTNAME, outname) < 0) {
2663      toutkeep = 1;
2664      chmod(TMPOUTNAME, filemode);
2665    } else
2666      chmod(outname, filemode);
2667  }
2668  set_signals(1);
2669}
2670
2671static char *
2672posix_name(const struct FileName *names, int assume_exists)
2673{
2674  char *path = NULL;
2675  int   i;
2676
2677  for (i = 0; i < MAX_FILE; i++) {
2678    if (names[i].path != NULL && names[i].exists) {
2679      path = names[i].path;
2680      break;
2681    }
2682  }
2683  if (path == NULL && !assume_exists) {
2684    for (i = 0; i < MAX_FILE; i++) {
2685      if (names[i].path != NULL && (path = checked_in(names[i].path)) != NULL)
2686        break;
2687    }
2688    if (path == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2689      path = names[NEW_FILE].path;
2690  }
2691  return path ? savestr(path) : NULL;
2692}
2693
2694static char *
2695best_name(const struct FileName *names, int assume_exists)
2696{
2697  size_t min_components, min_baselen, min_len, tmp;
2698  char  *best = NULL;
2699  int    i;
2700
2701  min_components = min_baselen = min_len = SIZE_MAX;
2702  for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2703    if (names[i].path == NULL || (!names[i].exists && !assume_exists))
2704      continue;
2705    if ((tmp = num_components(names[i].path)) > min_components)
2706      continue;
2707    min_components = tmp;
2708    if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2709      continue;
2710    min_baselen = tmp;
2711    if ((tmp = strlen(names[i].path)) > min_len)
2712      continue;
2713    min_len = tmp;
2714    best    = names[i].path;
2715  }
2716  if (best == NULL) {
2717    min_components = min_baselen = min_len = SIZE_MAX;
2718    for (i = INDEX_FILE; i >= OLD_FILE; i--) {
2719      if (names[i].path == NULL || checked_in(names[i].path) == NULL)
2720        continue;
2721      if ((tmp = num_components(names[i].path)) > min_components)
2722        continue;
2723      min_components = tmp;
2724      if ((tmp = strlen(basename(names[i].path))) > min_baselen)
2725        continue;
2726      min_baselen = tmp;
2727      if ((tmp = strlen(names[i].path)) > min_len)
2728        continue;
2729      min_len = tmp;
2730      best    = names[i].path;
2731    }
2732    if (best == NULL && ok_to_create_file && names[NEW_FILE].path != NULL)
2733      best = names[NEW_FILE].path;
2734  }
2735  return best ? savestr(best) : NULL;
2736}
2737
2738static size_t
2739num_components(const char *path)
2740{
2741  size_t      n;
2742  const char *cp;
2743
2744  for (n = 0, cp = path; (cp = strchr(cp, '/')) != NULL; n++, cp++) {
2745    while (*cp == '/')
2746      cp++;
2747  }
2748  return n;
2749}
2750
2751LINENUM
2752strtolinenum(char *nptr, char **endptr)
2753{
2754  LINENUM     rv;
2755  char        c;
2756  char       *p;
2757  const char *errstr;
2758
2759  for (p = nptr; isdigit((unsigned char)*p); p++)
2760    ;
2761
2762  if (p == nptr)
2763    malformed();
2764
2765  c  = *p;
2766  *p = '\0';
2767
2768  rv = strtonum(nptr, 0, LINENUM_MAX, &errstr);
2769  if (errstr != NULL)
2770    fatal("invalid line number at line %ld: `%s' is %s\n", p_input_line, nptr, errstr);
2771
2772  *p      = c;
2773  *endptr = p;
2774
2775  return rv;
2776}
2777
2778int
2779move_file(const char *from, const char *to)
2780{
2781  int     fromfd;
2782  ssize_t i;
2783
2784  if (strEQ(to, "-")) {
2785    fromfd = open(from, O_RDONLY);
2786    if (fromfd < 0)
2787      pfatal("internal error, cant reopen %s", from);
2788    while ((i = read(fromfd, buf, bufsz)) > 0) {
2789      if (write(STDOUT_FILENO, buf, i) != i)
2790        pfatal("write failed");
2791    }
2792    close(fromfd);
2793    return 0;
2794  }
2795  if (backup_file(to) < 0) {
2796    say("Cant backup %s, output is in %s: %s\n", to, from, strerror(errno));
2797    return -1;
2798  }
2799  if (rename(from, to) < 0) {
2800    if (errno != EXDEV || copy_file(from, to) < 0) {
2801      say("Cant create %s, output is in %s: %s\n", to, from, strerror(errno));
2802      return -1;
2803    }
2804  }
2805  return 0;
2806}
2807
2808int
2809backup_file(const char *orig)
2810{
2811  struct stat filestat;
2812  char        bakname[PATH_MAX], *s, *simplename;
2813  dev_t       orig_device;
2814  ino_t       orig_inode;
2815
2816  if (backup_type == BACKUP_NONE || stat(orig, &filestat) != 0)
2817    return 0;
2818  if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
2819    unlink(orig);
2820    return 0;
2821  }
2822  orig_device = filestat.st_dev;
2823  orig_inode  = filestat.st_ino;
2824
2825  if (origprae) {
2826    if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname)
2827        || strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
2828      fatal("filename too long\n");
2829  } else {
2830    if ((s = find_backup_file_name(orig)) == NULL)
2831      fatal("out of memory\n");
2832    if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
2833      fatal("filename too long\n");
2834    free(s);
2835  }
2836
2837  if ((simplename = strrchr(bakname, '/')) != NULL)
2838    simplename = simplename + 1;
2839  else
2840    simplename = bakname;
2841
2842  while (stat(bakname, &filestat) == 0 && orig_device == filestat.st_dev
2843         && orig_inode == filestat.st_ino) {
2844    for (s = simplename; *s && !islower((unsigned char)*s); s++)
2845      ;
2846    if (*s)
2847      *s = toupper((unsigned char)*s);
2848    else
2849      memmove(simplename, simplename + 1, strlen(simplename + 1) + 1);
2850  }
2851  if (rename(orig, bakname) < 0) {
2852    if (errno != EXDEV || copy_file(orig, bakname) < 0)
2853      return -1;
2854  }
2855  return 0;
2856}
2857
2858int
2859copy_file(const char *from, const char *to)
2860{
2861  int     tofd, fromfd;
2862  ssize_t i;
2863
2864  tofd = open(to, O_CREAT | O_TRUNC | O_WRONLY, 0666);
2865  if (tofd < 0)
2866    return -1;
2867  fromfd = open(from, O_RDONLY, 0);
2868  if (fromfd < 0)
2869    pfatal("internal error, cant reopen %s", from);
2870  while ((i = read(fromfd, buf, bufsz)) > 0) {
2871    if (write(tofd, buf, i) != i)
2872      pfatal("write to %s failed", to);
2873  }
2874  close(fromfd);
2875  close(tofd);
2876  return 0;
2877}
2878
2879char *
2880savestr(const char *s)
2881{
2882  char *rv;
2883
2884  if (!s)
2885    s = "Oops";
2886  rv = strdup(s);
2887  if (rv == NULL) {
2888    if (using_plan_a)
2889      out_of_mem = 1;
2890    else
2891      fatal("out of memory\n");
2892  }
2893  return rv;
2894}
2895
2896void
2897say(const char *fmt, ...)
2898{
2899  va_list ap;
2900
2901  va_start(ap, fmt);
2902  vfprintf(stderr, fmt, ap);
2903  va_end(ap);
2904  fflush(stderr);
2905}
2906
2907__dead void
2908fatal(const char *fmt, ...)
2909{
2910  va_list ap;
2911
2912  va_start(ap, fmt);
2913  fprintf(stderr, "patch: **** ");
2914  vfprintf(stderr, fmt, ap);
2915  va_end(ap);
2916  my_exit(2);
2917}
2918
2919__dead void
2920pfatal(const char *fmt, ...)
2921{
2922  va_list ap;
2923  int     errnum = errno;
2924
2925  fprintf(stderr, "patch: **** ");
2926  va_start(ap, fmt);
2927  vfprintf(stderr, fmt, ap);
2928  va_end(ap);
2929  fprintf(stderr, ": %s\n", strerror(errnum));
2930  my_exit(2);
2931}
2932
2933void
2934ask(const char *fmt, ...)
2935{
2936  va_list    ap;
2937  ssize_t    nr    = 0;
2938  static int ttyfd = -1;
2939
2940  va_start(ap, fmt);
2941  vfprintf(stdout, fmt, ap);
2942  va_end(ap);
2943  fflush(stdout);
2944  if (ttyfd < 0)
2945    ttyfd = open(ARUU_PATH_DEVTTY, O_RDONLY);
2946  if (ttyfd >= 0) {
2947    if ((nr = read(ttyfd, buf, bufsz)) > 0 && buf[nr - 1] == '\n')
2948      buf[nr - 1] = '\0';
2949  }
2950  if (ttyfd < 0 || nr <= 0) {
2951    putchar('\n');
2952    buf[0] = '\0';
2953  }
2954}
2955
2956void
2957set_signals(int reset)
2958{
2959  static sig_t hupval, intval;
2960
2961  if (!reset) {
2962    hupval = signal(SIGHUP, SIG_IGN);
2963    if (hupval != SIG_IGN)
2964      hupval = (sig_t)my_exit;
2965    intval = signal(SIGINT, SIG_IGN);
2966    if (intval != SIG_IGN)
2967      intval = (sig_t)my_exit;
2968  }
2969  signal(SIGHUP, hupval);
2970  signal(SIGINT, intval);
2971}
2972
2973void
2974ignore_signals(void)
2975{
2976  signal(SIGHUP, SIG_IGN);
2977  signal(SIGINT, SIG_IGN);
2978}
2979
2980void
2981makedirs(const char *filename, int striplast)
2982{
2983  char *tmpbuf;
2984
2985  if ((tmpbuf = strdup(filename)) == NULL)
2986    fatal("out of memory\n");
2987
2988  if (striplast) {
2989    char *s = strrchr(tmpbuf, '/');
2990    if (s == NULL) {
2991      free(tmpbuf);
2992      return;
2993    }
2994    *s = '\0';
2995  }
2996  if (mkpath(tmpbuf) != 0)
2997    pfatal("creation of %s failed", tmpbuf);
2998  free(tmpbuf);
2999}
3000
3001char *
3002fetchname(const char *at, int *exists, int strip_leading)
3003{
3004  char       *fullname, *name, *t;
3005  int         sleading, tab;
3006  struct stat filestat;
3007
3008  if (at == NULL || *at == '\0')
3009    return NULL;
3010  while (isspace((unsigned char)*at))
3011    at++;
3012  if (strnEQ(at, ARUU_PATH_DEVNULL, sizeof(ARUU_PATH_DEVNULL) - 1))
3013    return NULL;
3014  name = fullname = t = savestr(at);
3015  tab                 = strchr(t, '\t') != NULL;
3016  for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') || !isspace((unsigned char)*t));
3017       t++) {
3018    if (t[0] == '/' && t[1] != '/' && t[1] != '\0') {
3019      if (--sleading >= 0)
3020        name = t + 1;
3021    }
3022  }
3023  *t = '\0';
3024
3025  if (strip_leading == 957 && name != fullname && *fullname != '/') {
3026    name[-1] = '\0';
3027    if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
3028      name[-1] = '/';
3029      name     = fullname;
3030    }
3031  }
3032  name = savestr(name);
3033  free(fullname);
3034
3035  *exists = stat(name, &filestat) == 0;
3036  return name;
3037}
3038
3039char *
3040checked_in(char *file)
3041{
3042  char       *filebase, *filedir, tmpbuf[PATH_MAX];
3043  struct stat filestat;
3044
3045  filebase = basename(file);
3046  filedir  = dirname(file);
3047
3048#define try(f, a1, a2, a3)                                                                         \
3049  (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
3050
3051  if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || try("%s/RCS/%s%s", filedir, filebase, "")
3052      || try("%s/%s%s", filedir, filebase, RCSSUFFIX)
3053      || try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase)
3054      || try("%s/%s%s", filedir, SCCSPREFIX, filebase))
3055    return file;
3056
3057  return NULL;
3058}
3059
3060__dead void
3061version(void)
3062{
3063  printf("Patch version 2.0-12u9-NetBSD\n");
3064  my_exit(EXIT_SUCCESS);
3065}
3066
3067__dead void
3068my_exit(int status)
3069{
3070  unlink(TMPINNAME);
3071  if (!toutkeep)
3072    unlink(TMPOUTNAME);
3073  if (!trejkeep)
3074    unlink(TMPREJNAME);
3075  unlink(TMPPATNAME);
3076  exit(status);
3077}
3078
3079static void *
3080pch_realloc(void *ptr, size_t number, size_t size)
3081{
3082  if (number > SIZE_MAX / size) {
3083    errno = EOVERFLOW;
3084    return NULL;
3085  }
3086  return realloc(ptr, number * size);
3087}
3088
3089int
3090mkpath(char *path)
3091{
3092  struct stat sb;
3093  char       *slash;
3094  int         done = 0;
3095
3096  slash = path;
3097  while (!done) {
3098    slash += strspn(slash, "/");
3099    slash += strcspn(slash, "/");
3100    done   = (*slash == '\0');
3101    *slash = '\0';
3102    if (stat(path, &sb)) {
3103      if (errno != ENOENT || (mkdir(path, 0777) && errno != EEXIST)) {
3104        weprintf("%s", path);
3105        return -1;
3106      }
3107    } else if (!S_ISDIR(sb.st_mode)) {
3108      weprintf("%s: %s", path, strerror(ENOTDIR));
3109      return -1;
3110    }
3111    *slash = '/';
3112  }
3113  return 0;
3114}
3115
3116char *
3117find_backup_file_name(const char *file)
3118{
3119  char *dir, *base_versions, *tmp_file;
3120  int   highest_backup;
3121
3122  if (backup_type == BACKUP_SIMPLE)
3123    return patch_concat(file, simple_backup_suffix);
3124  tmp_file = strdup(file);
3125  if (tmp_file == NULL)
3126    return NULL;
3127  base_versions = patch_concat(basename(tmp_file), ".~");
3128  free(tmp_file);
3129  if (base_versions == NULL)
3130    return NULL;
3131  tmp_file = strdup(file);
3132  if (tmp_file == NULL) {
3133    free(base_versions);
3134    return NULL;
3135  }
3136  dir = dirname(tmp_file);
3137  if (dir == NULL) {
3138    free(base_versions);
3139    free(tmp_file);
3140    return NULL;
3141  }
3142  highest_backup = max_backup_version(base_versions, dir);
3143  free(base_versions);
3144  free(tmp_file);
3145  if (backup_type == BACKUP_NUMBERED_EXISTING && highest_backup == 0)
3146    return patch_concat(file, simple_backup_suffix);
3147  return make_version_name(file, highest_backup + 1);
3148}
3149
3150static int
3151max_backup_version(const char *file, const char *dir)
3152{
3153  DIR           *dirp;
3154  struct dirent *dp;
3155  int            highest_version, this_version;
3156  size_t         file_name_length;
3157
3158  dirp = opendir(dir);
3159  if (dirp == NULL)
3160    return 0;
3161
3162  highest_version  = 0;
3163  file_name_length = strlen(file);
3164  while ((dp = readdir(dirp)) != NULL) {
3165    if (strlen(dp->d_name) <= file_name_length)
3166      continue;
3167    this_version = version_number(file, dp->d_name, file_name_length);
3168    if (this_version > highest_version)
3169      highest_version = this_version;
3170  }
3171  closedir(dirp);
3172  return highest_version;
3173}
3174
3175static char *
3176make_version_name(const char *file, int version_num)
3177{
3178  char *backup_name;
3179
3180  if (asprintf(&backup_name, "%s.~%d~", file, version_num) == -1)
3181    return NULL;
3182  return backup_name;
3183}
3184
3185static int
3186version_number(const char *base, const char *backup, size_t base_length)
3187{
3188  int         version_num;
3189  const char *p;
3190
3191  version_num = 0;
3192  if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) {
3193    for (p = &backup[base_length]; ISDIGIT(*p); ++p)
3194      version_num = version_num * 10 + *p - '0';
3195    if (p[0] != '~' || p[1])
3196      version_num = 0;
3197  }
3198  return version_num;
3199}
3200
3201static char *
3202patch_concat(const char *str1, const char *str2)
3203{
3204  char *newstr;
3205
3206  if (asprintf(&newstr, "%s%s", str1, str2) == -1)
3207    return NULL;
3208  return newstr;
3209}
3210
3211static int
3212argmatch(const char *arg, const char **optlist)
3213{
3214  int    i;
3215  size_t arglen;
3216  int    matchind  = -1;
3217  int    ambiguous = 0;
3218
3219  arglen = strlen(arg);
3220  for (i = 0; optlist[i]; i++) {
3221    if (!strncmp(optlist[i], arg, arglen)) {
3222      if (strlen(optlist[i]) == arglen)
3223        return i;
3224      else if (matchind == -1)
3225        matchind = i;
3226      else
3227        ambiguous = 1;
3228    }
3229  }
3230  if (ambiguous)
3231    return -2;
3232  else
3233    return matchind;
3234}
3235
3236static void
3237invalid_arg(const char *kind, const char *value, int problem)
3238{
3239  fprintf(stderr, "patch: ");
3240  if (problem == -1)
3241    fprintf(stderr, "invalid");
3242  else
3243    fprintf(stderr, "ambiguous");
3244  fprintf(stderr, " %s `%s'\n", kind, value);
3245}
3246
3247enum BackupType
3248get_version(const char *version_str)
3249{
3250  int i;
3251
3252  if (version_str == NULL || *version_str == '\0')
3253    return BACKUP_NUMBERED_EXISTING;
3254  i = argmatch(version_str, backup_args);
3255  if (i >= 0)
3256    return backup_types[i];
3257  invalid_arg("version control type", version_str, i);
3258  exit(2);
3259}