[BACK]Return to patch.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / patch

Diff for /src/usr.bin/patch/patch.c between version 1.19 and 1.20

version 1.19, 2003/07/18 02:00:09 version 1.20, 2003/07/21 14:00:41
Line 1 
Line 1 
 /*      $OpenBSD$       */  /* $OpenBSD$     */
   
 /* patch - a program to apply diffs to original files  /*
  *   * patch - a program to apply diffs to original files
    *
  * Copyright 1986, Larry Wall   * Copyright 1986, Larry Wall
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following condition   * modification, are permitted provided that the following condition is met:
  * is met:   * 1. Redistributions of source code must retain the above copyright notice,
  *  1. Redistributions of source code must retain the above copyright   * this condition and the following disclaimer.
  *     notice, this condition and the following disclaimer.   *
  *   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.   * SUCH DAMAGE.
  *   *
  * -C option added in 1998, original code by Marc Espie,   * -C option added in 1998, original code by Marc Espie, based on FreeBSD
  * based on FreeBSD behaviour   * behaviour
  */   */
   
 #ifndef lint  #ifndef lint
Line 38 
Line 38 
 #include "inp.h"  #include "inp.h"
 #include "backupfile.h"  #include "backupfile.h"
   
 /* procedures */  void    reinitialize_almost_everything(void);
   void    get_some_switches(void);
   LINENUM locate_hunk(LINENUM);
   void    abort_hunk(void);
   void    apply_hunk(LINENUM);
   void    init_output(char *);
   void    init_reject(char *);
   void    copy_till(LINENUM);
   void    spew_output(void);
   void    dump_line(LINENUM);
   bool    patch_match(LINENUM, LINENUM, LINENUM);
   bool    similar(char *, char *, int);
   void    re_input(void);
   void    my_exit(int) __attribute__((noreturn));
   int     optcmp(const void *, const void *);
   char    decode_long_option(char *);
   
 void reinitialize_almost_everything(void);  
 void get_some_switches(void);  
 LINENUM locate_hunk(LINENUM);  
 void abort_hunk(void);  
 void apply_hunk(LINENUM);  
 void init_output(char *);  
 void init_reject(char *);  
 void copy_till(LINENUM);  
 void spew_output(void);  
 void dump_line(LINENUM);  
 bool patch_match(LINENUM, LINENUM, LINENUM);  
 bool similar(char *, char *, int);  
 void re_input(void);  
 void my_exit(int) __attribute__((noreturn));  
 int optcmp(const void *, const void *);  
 char decode_long_option(char *);  
   
 /* TRUE if -E was specified on command line.  */  /* TRUE if -E was specified on command line.  */
 static int remove_empty_files = FALSE;  static int      remove_empty_files = FALSE;
   
 /* TRUE if -R was specified on command line.  */  /* TRUE if -R was specified on command line.  */
 static int reverse_flag_specified = FALSE;  static int      reverse_flag_specified = FALSE;
   
 /* TRUE if -C was specified on command line.  */  /* TRUE if -C was specified on command line.  */
 bool check_only = FALSE;  bool    check_only = FALSE;
   
 /* Apply a set of diffs as appropriate. */  /* Apply a set of diffs as appropriate. */
   
 int  int
 main(int argc, char *argv[])  main(int argc, char *argv[])
 {  {
     LINENUM where;          int     hunk = 0, failed = 0, failtotal = 0, patch_seen = 0, i;
     LINENUM newwhere;          LINENUM where, newwhere, fuzz, mymaxfuzz;
     LINENUM fuzz;          char    *tmpdir, *v;
     LINENUM mymaxfuzz;  
     int hunk = 0;  
     int failed = 0;  
     int failtotal = 0;  
     int patch_seen = 0;  
     int i;  
   
     setbuf(stderr, serrbuf);          setbuf(stderr, serrbuf);
     for (i = 0; i<MAXFILEC; i++)          for (i = 0; i < MAXFILEC; i++)
         filearg[i] = Nullch;                  filearg[i] = Nullch;
   
     myuid = getuid();          myuid = getuid();
   
     /* Cons up the names of the temporary files.  */          /* Cons up the names of the temporary files.  */
     {          tmpdir = getenv("TMPDIR");
       /* Directory for temporary files.  */          if (tmpdir == NULL) {
       char *tmpdir;                  tmpdir = "/tmp";
           }
           if (asprintf(&TMPOUTNAME, "%s/patchoXXXXXXXXXX", tmpdir) == -1)
                   fatal("cannot allocate memory");
           if ((i = mkstemp(TMPOUTNAME)) < 0)
                   pfatal("can't create %s", TMPOUTNAME);
           close(i);
   
       tmpdir = getenv ("TMPDIR");          if (asprintf(&TMPINNAME, "%s/patchiXXXXXXXXXX", tmpdir) == -1)
       if (tmpdir == NULL) {                  fatal("cannot allocate memory");
         tmpdir = "/tmp";          if ((i = mkstemp(TMPINNAME)) < 0)
       }                  pfatal("can't create %s", TMPINNAME);
           close(i);
   
       if (asprintf(&TMPOUTNAME, "%s/patchoXXXXXXXXXX", tmpdir) == -1)          if (asprintf(&TMPREJNAME, "%s/patchrXXXXXXXXXX", tmpdir) == -1)
         fatal("cannot allocate memory");                  fatal("cannot allocate memory");
       if ((i = mkstemp(TMPOUTNAME)) < 0)          if ((i = mkstemp(TMPREJNAME)) < 0)
         pfatal("can't create %s", TMPOUTNAME);                  pfatal("can't create %s", TMPREJNAME);
       close(i);          close(i);
   
       if (asprintf(&TMPINNAME, "%s/patchiXXXXXXXXXX", tmpdir) == -1)          if (asprintf(&TMPPATNAME, "%s/patchpXXXXXXXXXX", tmpdir) == -1)
         fatal("cannot allocate memory");                  fatal("cannot allocate memory");
       if ((i = mkstemp(TMPINNAME)) < 0)          if ((i = mkstemp(TMPPATNAME)) < 0)
         pfatal("can't create %s", TMPINNAME);                  pfatal("can't create %s", TMPPATNAME);
       close(i);          close(i);
   
       if (asprintf(&TMPREJNAME, "%s/patchrXXXXXXXXXX", tmpdir) == -1)          v = getenv("SIMPLE_BACKUP_SUFFIX");
         fatal("cannot allocate memory");          if (v)
       if ((i = mkstemp(TMPREJNAME)) < 0)                  simple_backup_suffix = v;
         pfatal("can't create %s", TMPREJNAME);          else
       close(i);                  simple_backup_suffix = ORIGEXT;
   
       if (asprintf(&TMPPATNAME, "%s/patchpXXXXXXXXXX", tmpdir) == -1)          v = getenv("VERSION_CONTROL");
         fatal("cannot allocate memory");          backup_type = get_version(v);   /* OK to pass NULL. */
       if ((i = mkstemp(TMPPATNAME)) < 0)  
         pfatal("can't create %s", TMPPATNAME);  
       close(i);  
     }  
   
     {          /* parse switches */
       char *v;          Argc = argc;
           Argv = argv;
           get_some_switches();
   
       v = getenv ("SIMPLE_BACKUP_SUFFIX");          /* make sure we clean up /tmp in case of disaster */
       if (v)          set_signals(0);
         simple_backup_suffix = v;  
       else  
         simple_backup_suffix = ORIGEXT;  
 #ifndef NODIR  
       v = getenv ("VERSION_CONTROL");  
       backup_type = get_version (v); /* OK to pass NULL. */  
 #endif  
     }  
   
     /* parse switches */          for (open_patch_file(filearg[1]); there_is_another_patch();
     Argc = argc;              reinitialize_almost_everything()) {
     Argv = argv;                  /* for each patch in patch file */
     get_some_switches();  
   
     /* make sure we clean up /tmp in case of disaster */                  patch_seen = TRUE;
     set_signals(0);  
   
     for (                  if (outname == Nullch)
         open_patch_file(filearg[1]);                          outname = savestr(filearg[0]);
         there_is_another_patch();  
         reinitialize_almost_everything()  
     ) {                                 /* for each patch in patch file */  
         patch_seen = TRUE;  
   
         if (outname == Nullch)                  /* for ed script just up and do it and exit */
             outname = savestr(filearg[0]);                  if (diff_type == ED_DIFF) {
                           do_ed_script();
                           continue;
                   }
                   /* initialize the patched file */
                   if (!skip_rest_of_patch)
                           init_output(TMPOUTNAME);
   
         /* for ed script just up and do it and exit */                  /* initialize reject file */
         if (diff_type == ED_DIFF) {                  init_reject(TMPREJNAME);
             do_ed_script();  
             continue;  
         }  
   
         /* initialize the patched file */                  /* find out where all the lines are */
         if (!skip_rest_of_patch)                  if (!skip_rest_of_patch)
             init_output(TMPOUTNAME);                          scan_input(filearg[0]);
   
         /* initialize reject file */                  /* from here on, open no standard i/o files, because malloc */
         init_reject(TMPREJNAME);                  /* might misfire and we can't catch it easily */
   
         /* find out where all the lines are */                  /* apply each hunk of patch */
         if (!skip_rest_of_patch)                  hunk = 0;
             scan_input(filearg[0]);                  failed = 0;
                   out_of_mem = FALSE;
         /* from here on, open no standard i/o files, because malloc */                  while (another_hunk()) {
         /* might misfire and we can't catch it easily */                          hunk++;
                           fuzz = Nulline;
         /* apply each hunk of patch */                          mymaxfuzz = pch_context();
         hunk = 0;                          if (maxfuzz < mymaxfuzz)
         failed = 0;                                  mymaxfuzz = maxfuzz;
         out_of_mem = FALSE;                          if (!skip_rest_of_patch) {
         while (another_hunk()) {                                  do {
             hunk++;                                          where = locate_hunk(fuzz);
             fuzz = Nulline;                                          if (hunk == 1 && where == Nulline && !force) {
             mymaxfuzz = pch_context();  
             if (maxfuzz < mymaxfuzz)  
                 mymaxfuzz = maxfuzz;  
             if (!skip_rest_of_patch) {  
                 do {  
                     where = locate_hunk(fuzz);  
                     if (hunk == 1 && where == Nulline && !force) {  
                                                 /* dwim for reversed patch? */                                                  /* dwim for reversed patch? */
                         if (!pch_swap()) {                                                  if (!pch_swap()) {
                             if (fuzz == Nulline)                                                          if (fuzz == Nulline)
                                 say(                                                                  say("Not enough memory to try swapped hunk!  Assuming unswapped.\n");
 "Not enough memory to try swapped hunk!  Assuming unswapped.\n");                                                          continue;
                             continue;                                                  }
                                                   reverse = !reverse;
                                                   /* try again */
                                                   where = locate_hunk(fuzz);
                                                   if (where == Nulline) {
                                                           /* didn't find it swapped */
                                                           if (!pch_swap())
                                                                   /* put it back to normal */
                                                                   fatal("lost hunk on alloc error!\n");
                                                           reverse = !reverse;
                                                   } else if (noreverse) {
                                                           if (!pch_swap())
                                                                   /* put it back to normal */
                                                                   fatal("lost hunk on alloc error!\n");
                                                           reverse = !reverse;
                                                           say("Ignoring previously applied (or reversed) patch.\n");
                                                           skip_rest_of_patch = TRUE;
                                                   } else if (batch) {
                                                           if (verbose)
                                                                   say("%seversed (or previously applied) patch detected!  %s -R.",
                                                                       reverse ? "R" : "Unr",
                                                                       reverse ? "Assuming" : "Ignoring");
                                                   } else {
                                                           ask("%seversed (or previously applied) patch detected!  %s -R? [y] ",
                                                               reverse ? "R" : "Unr",
                                                               reverse ? "Assume" : "Ignore");
                                                           if (*buf == 'n') {
                                                                   ask("Apply anyway? [n] ");
                                                                   if (*buf != 'y')
                                                                           skip_rest_of_patch = TRUE;
                                                                   where = Nulline;
                                                                   reverse = !reverse;
                                                                   if (!pch_swap())
                                                                           /* put it back to normal */
                                                                           fatal("lost hunk on alloc error!\n");
                                                           }
                                                   }
                                           }
                                   } while (!skip_rest_of_patch && where == Nulline &&
                                            ++fuzz <= mymaxfuzz);
   
                                   if (skip_rest_of_patch) {       /* just got decided */
                                           fclose(ofp);
                                           ofp = Nullfp;
                                   }
                         }                          }
                         reverse = !reverse;                          newwhere = pch_newfirst() + last_offset;
                         where = locate_hunk(fuzz);  /* try again */                          if (skip_rest_of_patch) {
                         if (where == Nulline) {     /* didn't find it swapped */                                  abort_hunk();
                             if (!pch_swap())         /* put it back to normal */                                  failed++;
                                 fatal("lost hunk on alloc error!\n");                                  if (verbose)
                             reverse = !reverse;                                          say("Hunk #%d ignored at %ld.\n",
                                               hunk, newwhere);
                           } else if (where == Nulline) {
                                   abort_hunk();
                                   failed++;
                                   if (verbose)
                                           say("Hunk #%d failed at %ld.\n",
                                               hunk, newwhere);
                           } else {
                                   apply_hunk(where);
                                   if (verbose) {
                                           say("Hunk #%d succeeded at %ld",
                                               hunk, newwhere);
                                           if (fuzz)
                                                   say(" with fuzz %ld", fuzz);
                                           if (last_offset)
                                                   say(" (offset %ld line%s)",
                                                       last_offset,
                                                       last_offset == 1L ? "" : "s");
                                           say(".\n");
                                   }
                         }                          }
                         else if (noreverse) {  
                             if (!pch_swap())         /* put it back to normal */  
                                 fatal("lost hunk on alloc error!\n");  
                             reverse = !reverse;  
                             say(  
 "Ignoring previously applied (or reversed) patch.\n");  
                             skip_rest_of_patch = TRUE;  
                         }  
                         else if (batch) {  
                             if (verbose)  
                                 say(  
 "%seversed (or previously applied) patch detected!  %s -R.",  
                                 reverse ? "R" : "Unr",  
                                 reverse ? "Assuming" : "Ignoring");  
                         }  
                         else {  
                             ask(  
 "%seversed (or previously applied) patch detected!  %s -R? [y] ",  
                                 reverse ? "R" : "Unr",  
                                 reverse ? "Assume" : "Ignore");  
                             if (*buf == 'n') {  
                                 ask("Apply anyway? [n] ");  
                                 if (*buf != 'y')  
                                     skip_rest_of_patch = TRUE;  
                                 where = Nulline;  
                                 reverse = !reverse;  
                                 if (!pch_swap())  /* put it back to normal */  
                                     fatal("lost hunk on alloc error!\n");  
                             }  
                         }  
                     }  
                 } while (!skip_rest_of_patch && where == Nulline &&  
                     ++fuzz <= mymaxfuzz);  
   
                 if (skip_rest_of_patch) {               /* just got decided */  
                     fclose(ofp);  
                     ofp = Nullfp;  
                 }                  }
             }  
   
             newwhere = pch_newfirst() + last_offset;                  if (out_of_mem && using_plan_a) {
             if (skip_rest_of_patch) {                          Argc = Argc_last;
                 abort_hunk();                          Argv = Argv_last;
                 failed++;                          say("\n\nRan out of memory using Plan A--trying again...\n\n");
                 if (verbose)                          if (ofp)
                     say("Hunk #%d ignored at %ld.\n", hunk, newwhere);                                  fclose(ofp);
             }                          ofp = Nullfp;
             else if (where == Nulline) {                          if (rejfp)
                 abort_hunk();                                  fclose(rejfp);
                 failed++;                          rejfp = Nullfp;
                 if (verbose)                          continue;
                     say("Hunk #%d failed at %ld.\n", hunk, newwhere);  
             }  
             else {  
                 apply_hunk(where);  
                 if (verbose) {  
                     say("Hunk #%d succeeded at %ld", hunk, newwhere);  
                     if (fuzz)  
                         say(" with fuzz %ld", fuzz);  
                     if (last_offset)  
                         say(" (offset %ld line%s)",  
                             last_offset, last_offset==1L?"":"s");  
                     say(".\n");  
                 }                  }
             }                  assert(hunk);
         }  
   
         if (out_of_mem && using_plan_a) {                  /* finish spewing out the new file */
             Argc = Argc_last;                  if (!skip_rest_of_patch)
             Argv = Argv_last;                          spew_output();
             say("\n\nRan out of memory using Plan A--trying again...\n\n");  
             if (ofp)  
                 fclose(ofp);  
             ofp = Nullfp;  
             if (rejfp)  
                 fclose(rejfp);  
             rejfp = Nullfp;  
             continue;  
         }  
   
         assert(hunk);                  /* and put the output where desired */
                   ignore_signals();
                   if (!skip_rest_of_patch) {
                           struct stat     statbuf;
                           char    *realout = outname;
   
         /* finish spewing out the new file */                          if (!check_only) {
         if (!skip_rest_of_patch)                                  if (move_file(TMPOUTNAME, outname) < 0) {
             spew_output();                                          toutkeep = TRUE;
                                           realout = TMPOUTNAME;
                                           chmod(TMPOUTNAME, filemode);
                                   } else
                                           chmod(outname, filemode);
   
         /* and put the output where desired */                                  if (remove_empty_files &&
         ignore_signals();                                      stat(realout, &statbuf) == 0 &&
         if (!skip_rest_of_patch) {                                      statbuf.st_size == 0) {
             struct stat statbuf;                                          if (verbose)
             char *realout = outname;                                                  say("Removing %s (empty after patching).\n",
                                                       realout);
             if (!check_only) {                                          unlink(realout);
                 if (move_file(TMPOUTNAME, outname) < 0) {                                  }
                     toutkeep = TRUE;                          }
                     realout = TMPOUTNAME;  
                     chmod(TMPOUTNAME, filemode);  
                 }                  }
                 else                  fclose(rejfp);
                     chmod(outname, filemode);                  rejfp = Nullfp;
                   if (failed) {
                 if (remove_empty_files && stat(realout, &statbuf) == 0                          failtotal += failed;
                     && statbuf.st_size == 0) {                          if (!*rejname) {
                     if (verbose)                                  if (strlcpy(rejname, outname,
                         say("Removing %s (empty after patching).\n", realout);                                      sizeof(rejname)) >= sizeof(rejname))
                     while (unlink(realout) >= 0) ; /* while is for Eunice.  */                                          fatal("filename %s is too long\n", outname);
                                   if (strlcat(rejname, REJEXT,
                                       sizeof(rejname)) >= sizeof(rejname))
                                           fatal("filename %s is too long\n", outname);
                           }
                           if (skip_rest_of_patch) {
                                   say("%d out of %d hunks ignored--saving rejects to %s\n",
                                       failed, hunk, rejname);
                           } else {
                                   say("%d out of %d hunks failed--saving rejects to %s\n",
                                       failed, hunk, rejname);
                           }
                           if (!check_only && move_file(TMPREJNAME, rejname) < 0)
                                   trejkeep = TRUE;
                 }                  }
             }                  set_signals(1);
         }          }
         fclose(rejfp);          if (!patch_seen)
         rejfp = Nullfp;                  failtotal++;
         if (failed) {          my_exit(failtotal);
             failtotal += failed;          /* NOTREACHED */
             if (!*rejname) {  
                 if (strlcpy(rejname, outname, sizeof(rejname)) >= sizeof(rejname))  
                     fatal("filename %s is too long\n", outname);  
                 if (strlcat(rejname, REJEXT, sizeof(rejname)) >= sizeof(rejname))  
                     fatal("filename %s is too long\n", outname);  
             }  
             if (skip_rest_of_patch) {  
                 say("%d out of %d hunks ignored--saving rejects to %s\n",  
                     failed, hunk, rejname);  
             }  
             else {  
                 say("%d out of %d hunks failed--saving rejects to %s\n",  
                     failed, hunk, rejname);  
             }  
             if (!check_only && move_file(TMPREJNAME, rejname) < 0)  
                 trejkeep = TRUE;  
         }  
         set_signals(1);  
     }  
     if (!patch_seen)  
         failtotal++;  
     my_exit(failtotal);  
     /* NOTREACHED */  
 }  }
   
 /* Prepare to find the next patch to do in the patch file. */  /* Prepare to find the next patch to do in the patch file. */
   
 void  void
 reinitialize_almost_everything()  reinitialize_almost_everything(void)
 {  {
     re_patch();          re_patch();
     re_input();          re_input();
   
     input_lines = 0;          input_lines = 0;
     last_frozen_line = 0;          last_frozen_line = 0;
   
     filec = 0;          filec = 0;
     if (filearg[0] != Nullch && !out_of_mem) {          if (filearg[0] != Nullch && !out_of_mem) {
         free(filearg[0]);                  free(filearg[0]);
         filearg[0] = Nullch;                  filearg[0] = Nullch;
     }          }
           if (outname != Nullch) {
                   free(outname);
                   outname = Nullch;
           }
           last_offset = 0;
   
     if (outname != Nullch) {          diff_type = 0;
         free(outname);  
         outname = Nullch;  
     }  
   
     last_offset = 0;          if (revision != Nullch) {
                   free(revision);
                   revision = Nullch;
           }
           reverse = reverse_flag_specified;
           skip_rest_of_patch = FALSE;
   
     diff_type = 0;          get_some_switches();
   
     if (revision != Nullch) {          if (filec >= 2)
         free(revision);                  fatal("you may not change to a different patch file\n");
         revision = Nullch;  
     }  
   
     reverse = reverse_flag_specified;  
     skip_rest_of_patch = FALSE;  
   
     get_some_switches();  
   
     if (filec >= 2)  
         fatal("you may not change to a different patch file\n");  
 }  }
   
 static char *  static char *
 nextarg(void)  nextarg(void)
 {  {
     if (!--Argc)          if (!--Argc)
         fatal("missing argument after `%s'\n", *Argv);                  fatal("missing argument after `%s'\n", *Argv);
     return *++Argv;          return *++Argv;
 }  }
   
 /* Module for handling of long options.  */  /* Module for handling of long options.  */
   
 struct option {  struct option {
     char *long_opt;          char    *long_opt;
     char short_opt;          char    short_opt;
 };  };
   
 int  int
 optcmp(const void *v1, const void *v2)  optcmp(const void *v1, const void *v2)
 {  {
     const struct option *a = v1, *b = v2;          const struct option *a = v1, *b = v2;
   
     return strcmp (a->long_opt, b->long_opt);          return strcmp(a->long_opt, b->long_opt);
 }  }
   
 /* Decode Long options beginning with "--" to their short equivalents.  */  /* Decode Long options beginning with "--" to their short equivalents.  */
Line 408 
Line 384 
 char  char
 decode_long_option(char *opt)  decode_long_option(char *opt)
 {  {
     /* This table must be sorted on the first field.  We also decode          /*
        unimplemented options as those will be handled later anyway.  */           * This table must be sorted on the first field.  We also decode
     static struct option options[] = {           * unimplemented options as those will be handled later anyway.
       { "batch",                't' },           */
       { "check",                'C' },          static struct option options[] = {
       { "context",              'c' },                  {"batch", 't'},
       { "debug",                'x' },                  {"check", 'C'},
       { "directory",            'd' },                  {"context", 'c'},
       { "ed",                   'e' },                  {"debug", 'x'},
       { "force",                'f' },                  {"directory", 'd'},
       { "forward",              'N' },                  {"ed", 'e'},
       { "fuzz",                 'F' },                  {"force", 'f'},
       { "ifdef",                'D' },                  {"forward", 'N'},
       { "ignore-whitespace",    'l' },                  {"fuzz", 'F'},
       { "normal",               'n' },                  {"ifdef", 'D'},
       { "output",               'o' },                  {"ignore-whitespace", 'l'},
       { "prefix",               'B' },                  {"normal", 'n'},
       { "quiet",                's' },                  {"output", 'o'},
       { "reject-file",          'r' },                  {"prefix", 'B'},
       { "remove-empty-files",   'E' },                  {"quiet", 's'},
       { "reverse",              'R' },                  {"reject-file", 'r'},
       { "silent",               's' },                  {"remove-empty-files", 'E'},
       { "skip",                 'S' },                  {"reverse", 'R'},
       { "strip",                'p' },                  {"silent", 's'},
       { "suffix",               'b' },                  {"skip", 'S'},
       { "unified",              'u' },                  {"strip", 'p'},
       { "version",              'v' },                  {"suffix", 'b'},
       { "version-control",      'V' },                  {"unified", 'u'},
     };                  {"version", 'v'},
     struct option key, *found;                  {"version-control", 'V'},
           };
           struct option   key, *found;
   
     key.long_opt = opt;          key.long_opt = opt;
     found = (struct option *)bsearch(&key, options,          found = (struct option *) bsearch(&key, options,
                                      sizeof(options) / sizeof(options[0]),              sizeof(options) / sizeof(options[0]),
                                      sizeof(options[0]), optcmp);              sizeof(options[0]), optcmp);
     return found ? found->short_opt : '\0';          return found ? found->short_opt : '\0';
 }  }
   
 /* Process switches and filenames up to next '+' or end of list. */  /* Process switches and filenames up to next '+' or end of list. */
   
 void  void
 get_some_switches()  get_some_switches(void)
 {  {
     char *s;          char    *s;
   
     rejname[0] = '\0';          rejname[0] = '\0';
     Argc_last = Argc;          Argc_last = Argc;
     Argv_last = Argv;          Argv_last = Argv;
     if (!Argc)          if (!Argc)
         return;                  return;
     for (Argc--,Argv++; Argc; Argc--,Argv++) {          for (Argc--, Argv++; Argc; Argc--, Argv++) {
         s = Argv[0];                  s = Argv[0];
         if (strEQ(s, "+")) {                  if (strEQ(s, "+")) {
             return;                     /* + will be skipped by for loop */                          return; /* + will be skipped by for loop */
         }                  }
         if (*s != '-' || !s[1]) {                  if (*s != '-' || !s[1]) {
             if (filec == MAXFILEC)                          if (filec == MAXFILEC)
                 fatal("too many file arguments\n");                                  fatal("too many file arguments\n");
             filearg[filec++] = savestr(s);                          filearg[filec++] = savestr(s);
         }                  } else {
         else {                          char    opt;
             char opt;  
   
             if (*(s + 1) == '-') {                          if (*(s + 1) == '-') {
                 opt = decode_long_option(s + 2);                                  opt = decode_long_option(s + 2);
                 s += strlen(s) - 1;                                  s += strlen(s) - 1;
             }                          } else
             else                                  opt = *++s;
                 opt = *++s;                          switch (opt) {
             switch (opt) {                          case 'b':
             case 'b':                                  simple_backup_suffix = savestr(nextarg());
                 simple_backup_suffix = savestr(nextarg());                                  break;
                 break;                          case 'B':
             case 'B':                                  origprae = savestr(nextarg());
                 origprae = savestr(nextarg());                                  break;
                 break;                          case 'c':
             case 'c':                                  diff_type = CONTEXT_DIFF;
                 diff_type = CONTEXT_DIFF;                                  break;
                 break;                          case 'C':
             case 'C':                                  check_only = TRUE;
                 check_only = TRUE;                                  break;
                 break;                          case 'd':
             case 'd':                                  if (!*++s)
                 if (!*++s)                                          s = nextarg();
                     s = nextarg();                                  if (chdir(s) < 0)
                 if (chdir(s) < 0)                                          pfatal("can't cd to %s", s);
                     pfatal("can't cd to %s", s);                                  break;
                 break;                          case 'D':
             case 'D':                                  do_defines = TRUE;
                 do_defines = TRUE;                                  if (!*++s)
                 if (!*++s)                                          s = nextarg();
                     s = nextarg();                                  if (!isalpha(*s) && '_' != *s)
                 if (!isalpha(*s) && '_' != *s)                                          fatal("argument to -D is not an identifier\n");
                     fatal("argument to -D is not an identifier\n");                                  snprintf(if_defined, sizeof if_defined,
                 snprintf(if_defined, sizeof if_defined, "#ifdef %s\n", s);                                      "#ifdef %s\n", s);
                 snprintf(not_defined, sizeof not_defined, "#ifndef %s\n", s);                                  snprintf(not_defined, sizeof not_defined,
                 snprintf(end_defined, sizeof end_defined, "#endif /* %s */\n", s);                                      "#ifndef %s\n", s);
                 break;                                  snprintf(end_defined, sizeof end_defined,
             case 'e':                                      "#endif /* %s */\n", s);
                 diff_type = ED_DIFF;                                  break;
                 break;                          case 'e':
             case 'E':                                  diff_type = ED_DIFF;
                 remove_empty_files = TRUE;                                  break;
                 break;                          case 'E':
             case 'f':                                  remove_empty_files = TRUE;
                 force = TRUE;                                  break;
                 break;                          case 'f':
             case 'F':                                  force = TRUE;
                 if (!*++s)                                  break;
                     s = nextarg();                          case 'F':
                 else if (*s == '=')                                  if (!*++s)
                     s++;                                          s = nextarg();
                 maxfuzz = atoi(s);                                  else if (*s == '=')
                 break;                                          s++;
             case 'l':                                  maxfuzz = atoi(s);
                 canonicalize = TRUE;                                  break;
                 break;                          case 'l':
             case 'n':                                  canonicalize = TRUE;
                 diff_type = NORMAL_DIFF;                                  break;
                 break;                          case 'n':
             case 'N':                                  diff_type = NORMAL_DIFF;
                 noreverse = TRUE;                                  break;
                 break;                          case 'N':
             case 'o':                                  noreverse = TRUE;
                 outname = savestr(nextarg());                                  break;
                 break;                          case 'o':
             case 'p':                                  outname = savestr(nextarg());
                 if (!*++s)                                  break;
                     s = nextarg();                          case 'p':
                 else if (*s == '=')                                  if (!*++s)
                     s++;                                          s = nextarg();
                 strippath = atoi(s);                                  else if (*s == '=')
                 break;                                          s++;
             case 'r':                                  strippath = atoi(s);
                 if (strlcpy(rejname, nextarg(), sizeof(rejname)) >= sizeof(rejname))                                  break;
                     fatal("argument for -r is too long\n");                          case 'r':
                 break;                                  if (strlcpy(rejname, nextarg(),
             case 'R':                                      sizeof(rejname)) >= sizeof(rejname))
                 reverse = TRUE;                                          fatal("argument for -r is too long\n");
                 reverse_flag_specified = TRUE;                                  break;
                 break;                          case 'R':
             case 's':                                  reverse = TRUE;
                 verbose = FALSE;                                  reverse_flag_specified = TRUE;
                 break;                                  break;
             case 'S':                          case 's':
                 skip_rest_of_patch = TRUE;                                  verbose = FALSE;
                 break;                                  break;
             case 't':                          case 'S':
                 batch = TRUE;                                  skip_rest_of_patch = TRUE;
                 break;                                  break;
             case 'u':                          case 't':
                 diff_type = UNI_DIFF;                                  batch = TRUE;
                 break;                                  break;
             case 'v':                          case 'u':
                 version();                                  diff_type = UNI_DIFF;
                 break;                                  break;
             case 'V':                          case 'v':
 #ifndef NODIR                                  version();
                 backup_type = get_version (nextarg ());                                  break;
 #endif                          case 'V':
                 break;                                  backup_type = get_version(nextarg());
                                   break;
 #ifdef DEBUGGING  #ifdef DEBUGGING
             case 'x':                          case 'x':
                 if (!*++s)                                  if (!*++s)
                     s = nextarg();                                          s = nextarg();
                 debug = atoi(s);                                  debug = atoi(s);
                 break;                                  break;
 #endif  #endif
             default:                          default:
                 fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]);                                  fprintf(stderr, "patch: unrecognized option `%s'\n",
                 fprintf(stderr, "\                                      Argv[0]);
                                   fprintf(stderr, "\
 Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\  Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
 Options:\n\  Options:\n\
        [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\         [-cCeEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
        [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\         [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
        [-r rej-name] [-V {numbered,existing,simple}]\n");         [-r rej-name] [-V {numbered,existing,simple}]\n");
                 my_exit(1);                                  my_exit(1);
             }                          }
                   }
         }          }
     }  
 }  }
   
 /* Attempt to find the right place to apply this hunk of patch. */  /*
    * Attempt to find the right place to apply this hunk of patch.
    */
 LINENUM  LINENUM
 locate_hunk(fuzz)  locate_hunk(LINENUM fuzz)
 LINENUM fuzz;  
 {  {
     LINENUM first_guess = pch_first() + last_offset;          LINENUM first_guess = pch_first() + last_offset;
     LINENUM offset;          LINENUM offset;
     LINENUM pat_lines = pch_ptrn_lines();          LINENUM pat_lines = pch_ptrn_lines();
     LINENUM max_pos_offset = input_lines - first_guess          LINENUM max_pos_offset = input_lines - first_guess - pat_lines + 1;
                                 - pat_lines + 1;          LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + pch_context();
     LINENUM max_neg_offset = first_guess - last_frozen_line - 1  
                                 + pch_context();  
   
     if (!pat_lines)                     /* null range matches always */          if (!pat_lines)         /* null range matches always */
         return first_guess;                  return first_guess;
     if (max_neg_offset >= first_guess)  /* do not try lines < 0 */          if (max_neg_offset >= first_guess)      /* do not try lines < 0 */
         max_neg_offset = first_guess - 1;                  max_neg_offset = first_guess - 1;
     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))          if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
         return first_guess;                  return first_guess;
     for (offset = 1; ; offset++) {          for (offset = 1; ; offset++) {
         bool check_after = (offset <= max_pos_offset);                  bool    check_after = (offset <= max_pos_offset);
         bool check_before = (offset <= max_neg_offset);                  bool    check_before = (offset <= max_neg_offset);
   
         if (check_after && patch_match(first_guess, offset, fuzz)) {                  if (check_after && patch_match(first_guess, offset, fuzz)) {
 #ifdef DEBUGGING  #ifdef DEBUGGING
             if (debug & 1)                          if (debug & 1)
                 say("Offset changing from %ld to %ld\n", last_offset, offset);                                  say("Offset changing from %ld to %ld\n",
                                       last_offset, offset);
 #endif  #endif
             last_offset = offset;                          last_offset = offset;
             return first_guess+offset;                          return first_guess + offset;
         }                  } else if (check_before && patch_match(first_guess, -offset, fuzz)) {
         else if (check_before && patch_match(first_guess, -offset, fuzz)) {  
 #ifdef DEBUGGING  #ifdef DEBUGGING
             if (debug & 1)                          if (debug & 1)
                 say("Offset changing from %ld to %ld\n", last_offset, -offset);                                  say("Offset changing from %ld to %ld\n",
                                       last_offset, -offset);
 #endif  #endif
             last_offset = -offset;                          last_offset = -offset;
             return first_guess-offset;                          return first_guess - offset;
                   } else if (!check_before && !check_after)
                           return Nulline;
         }          }
         else if (!check_before && !check_after)  
             return Nulline;  
     }  
 }  }
   
 /* We did not find the pattern, dump out the hunk so they can handle it. */  /* We did not find the pattern, dump out the hunk so they can handle it. */
   
 void  void
 abort_hunk()  abort_hunk(void)
 {  {
     LINENUM i;          LINENUM i;
     LINENUM pat_end = pch_end();          LINENUM pat_end = pch_end();
     /* add in last_offset to guess the same as the previous successful hunk */          /*
     LINENUM oldfirst = pch_first() + last_offset;           * add in last_offset to guess the same as the previous successful
     LINENUM newfirst = pch_newfirst() + last_offset;           * hunk
     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;           */
     LINENUM newlast = newfirst + pch_repl_lines() - 1;          LINENUM oldfirst = pch_first() + last_offset;
     char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");          LINENUM newfirst = pch_newfirst() + last_offset;
     char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");          LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
           LINENUM newlast = newfirst + pch_repl_lines() - 1;
           char    *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
           char    *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
   
     fprintf(rejfp, "***************\n");          fprintf(rejfp, "***************\n");
     for (i=0; i<=pat_end; i++) {          for (i = 0; i <= pat_end; i++) {
         switch (pch_char(i)) {                  switch (pch_char(i)) {
         case '*':                  case '*':
             if (oldlast < oldfirst)                          if (oldlast < oldfirst)
                 fprintf(rejfp, "*** 0%s\n", stars);                                  fprintf(rejfp, "*** 0%s\n", stars);
             else if (oldlast == oldfirst)                          else if (oldlast == oldfirst)
                 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);                                  fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
             else                          else
                 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);                                  fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst,
             break;                                      oldlast, stars);
         case '=':                          break;
             if (newlast < newfirst)                  case '=':
                 fprintf(rejfp, "--- 0%s\n", minuses);                          if (newlast < newfirst)
             else if (newlast == newfirst)                                  fprintf(rejfp, "--- 0%s\n", minuses);
                 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);                          else if (newlast == newfirst)
             else                                  fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
                 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);                          else
             break;                                  fprintf(rejfp, "--- %ld,%ld%s\n", newfirst,
         case '\n':                                      newlast, minuses);
             fprintf(rejfp, "%s", pfetch(i));                          break;
             break;                  case '\n':
         case ' ': case '-': case '+': case '!':                          fprintf(rejfp, "%s", pfetch(i));
             fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));                          break;
             break;                  case ' ':
         default:                  case '-':
             fatal("fatal internal error in abort_hunk\n");                  case '+':
                   case '!':
                           fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
                           break;
                   default:
                           fatal("fatal internal error in abort_hunk\n");
                   }
         }          }
     }  
 }  }
   
 /* We found where to apply it (we hope), so do it. */  /* We found where to apply it (we hope), so do it. */
   
 void  void
 apply_hunk(where)  apply_hunk(LINENUM where)
 LINENUM where;  
 {  {
     LINENUM old = 1;          LINENUM old = 1;
     LINENUM lastline = pch_ptrn_lines();          LINENUM lastline = pch_ptrn_lines();
     LINENUM new = lastline+1;          LINENUM new = lastline + 1;
 #define OUTSIDE 0  #define OUTSIDE 0
 #define IN_IFNDEF 1  #define IN_IFNDEF 1
 #define IN_IFDEF 2  #define IN_IFDEF 2
 #define IN_ELSE 3  #define IN_ELSE 3
     int def_state = OUTSIDE;          int     def_state = OUTSIDE;
     bool R_do_defines = do_defines;          bool    R_do_defines = do_defines;
     LINENUM pat_end = pch_end();          LINENUM pat_end = pch_end();
   
     where--;          where--;
     while (pch_char(new) == '=' || pch_char(new) == '\n')          while (pch_char(new) == '=' || pch_char(new) == '\n')
         new++;                  new++;
   
     while (old <= lastline) {          while (old <= lastline) {
         if (pch_char(old) == '-') {                  if (pch_char(old) == '-') {
             copy_till(where + old - 1);                          copy_till(where + old - 1);
             if (R_do_defines) {                          if (R_do_defines) {
                 if (def_state == OUTSIDE) {                                  if (def_state == OUTSIDE) {
                     fputs(not_defined, ofp);                                          fputs(not_defined, ofp);
                     def_state = IN_IFNDEF;                                          def_state = IN_IFNDEF;
                 }                                  } else if (def_state == IN_IFDEF) {
                 else if (def_state == IN_IFDEF) {                                          fputs(else_defined, ofp);
                     fputs(else_defined, ofp);                                          def_state = IN_ELSE;
                     def_state = IN_ELSE;                                  }
                 }                                  fputs(pfetch(old), ofp);
                 fputs(pfetch(old), ofp);                          }
             }                          last_frozen_line++;
             last_frozen_line++;                          old++;
             old++;                  } else if (new > pat_end) {
         }                          break;
         else if (new > pat_end) {                  } else if (pch_char(new) == '+') {
             break;                          copy_till(where + old - 1);
         }                          if (R_do_defines) {
         else if (pch_char(new) == '+') {                                  if (def_state == IN_IFNDEF) {
             copy_till(where + old - 1);                                          fputs(else_defined, ofp);
             if (R_do_defines) {                                          def_state = IN_ELSE;
                 if (def_state == IN_IFNDEF) {                                  } else if (def_state == OUTSIDE) {
                     fputs(else_defined, ofp);                                          fputs(if_defined, ofp);
                     def_state = IN_ELSE;                                          def_state = IN_IFDEF;
                 }                                  }
                 else if (def_state == OUTSIDE) {                          }
                     fputs(if_defined, ofp);                          fputs(pfetch(new), ofp);
                     def_state = IN_IFDEF;                          new++;
                 }                  } else if (pch_char(new) != pch_char(old)) {
             }                          say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
             fputs(pfetch(new), ofp);                              pch_hunk_beg() + old,
             new++;                              pch_hunk_beg() + new);
         }  
         else if (pch_char(new) != pch_char(old)) {  
             say("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",  
                 pch_hunk_beg() + old,  
                 pch_hunk_beg() + new);  
 #ifdef DEBUGGING  #ifdef DEBUGGING
             say("oldchar = '%c', newchar = '%c'\n",                          say("oldchar = '%c', newchar = '%c'\n",
                 pch_char(old), pch_char(new));                              pch_char(old), pch_char(new));
 #endif  #endif
             my_exit(1);                          my_exit(1);
                   } else if (pch_char(new) == '!') {
                           copy_till(where + old - 1);
                           if (R_do_defines) {
                                   fputs(not_defined, ofp);
                                   def_state = IN_IFNDEF;
                           }
                           while (pch_char(old) == '!') {
                                   if (R_do_defines) {
                                           fputs(pfetch(old), ofp);
                                   }
                                   last_frozen_line++;
                                   old++;
                           }
                           if (R_do_defines) {
                                   fputs(else_defined, ofp);
                                   def_state = IN_ELSE;
                           }
                           while (pch_char(new) == '!') {
                                   fputs(pfetch(new), ofp);
                                   new++;
                           }
                   } else {
                           assert(pch_char(new) == ' ');
                           old++;
                           new++;
                           if (R_do_defines && def_state != OUTSIDE) {
                                   fputs(end_defined, ofp);
                                   def_state = OUTSIDE;
                           }
                   }
         }          }
         else if (pch_char(new) == '!') {          if (new <= pat_end && pch_char(new) == '+') {
             copy_till(where + old - 1);                  copy_till(where + old - 1);
             if (R_do_defines) {  
                fputs(not_defined, ofp);  
                def_state = IN_IFNDEF;  
             }  
             while (pch_char(old) == '!') {  
                 if (R_do_defines) {                  if (R_do_defines) {
                     fputs(pfetch(old), ofp);                          if (def_state == OUTSIDE) {
                                   fputs(if_defined, ofp);
                                   def_state = IN_IFDEF;
                           } else if (def_state == IN_IFNDEF) {
                                   fputs(else_defined, ofp);
                                   def_state = IN_ELSE;
                           }
                 }                  }
                 last_frozen_line++;                  while (new <= pat_end && pch_char(new) == '+') {
                 old++;                          fputs(pfetch(new), ofp);
             }                          new++;
             if (R_do_defines) {                  }
                 fputs(else_defined, ofp);  
                 def_state = IN_ELSE;  
             }  
             while (pch_char(new) == '!') {  
                 fputs(pfetch(new), ofp);  
                 new++;  
             }  
         }          }
         else {          if (R_do_defines && def_state != OUTSIDE) {
             assert(pch_char(new) == ' ');  
             old++;  
             new++;  
             if (R_do_defines && def_state != OUTSIDE) {  
                 fputs(end_defined, ofp);                  fputs(end_defined, ofp);
                 def_state = OUTSIDE;  
             }  
         }          }
     }  
     if (new <= pat_end && pch_char(new) == '+') {  
         copy_till(where + old - 1);  
         if (R_do_defines) {  
             if (def_state == OUTSIDE) {  
                 fputs(if_defined, ofp);  
                 def_state = IN_IFDEF;  
             }  
             else if (def_state == IN_IFNDEF) {  
                 fputs(else_defined, ofp);  
                 def_state = IN_ELSE;  
             }  
         }  
         while (new <= pat_end && pch_char(new) == '+') {  
             fputs(pfetch(new), ofp);  
             new++;  
         }  
     }  
     if (R_do_defines && def_state != OUTSIDE) {  
         fputs(end_defined, ofp);  
     }  
 }  }
   
 /* Open the new file. */  /*
    * Open the new file.
    */
 void  void
 init_output(name)  init_output(char *name)
 char *name;  
 {  {
     ofp = fopen(name, "w");          ofp = fopen(name, "w");
     if (ofp == Nullfp)          if (ofp == Nullfp)
         pfatal("can't create %s", name);                  pfatal("can't create %s", name);
 }  }
   
 /* Open a file to put hunks we can't locate. */  /*
    * Open a file to put hunks we can't locate.
    */
 void  void
 init_reject(name)  init_reject(char *name)
 char *name;  
 {  {
     rejfp = fopen(name, "w");          rejfp = fopen(name, "w");
     if (rejfp == Nullfp)          if (rejfp == Nullfp)
         pfatal("can't create %s", name);                  pfatal("can't create %s", name);
 }  }
   
 /* Copy input file to output, up to wherever hunk is to be applied. */  /*
    * Copy input file to output, up to wherever hunk is to be applied.
    */
 void  void
 copy_till(lastline)  copy_till(LINENUM lastline)
 LINENUM lastline;  
 {  {
     LINENUM R_last_frozen_line = last_frozen_line;          LINENUM R_last_frozen_line = last_frozen_line;
   
     if (R_last_frozen_line > lastline)          if (R_last_frozen_line > lastline)
         fatal("misordered hunks! output would be garbled\n");                  fatal("misordered hunks! output would be garbled\n");
     while (R_last_frozen_line < lastline) {          while (R_last_frozen_line < lastline)
         dump_line(++R_last_frozen_line);                  dump_line(++R_last_frozen_line);
     }          last_frozen_line = R_last_frozen_line;
     last_frozen_line = R_last_frozen_line;  
 }  }
   
 /* Finish copying the input file to the output file. */  /*
    * Finish copying the input file to the output file.
    */
 void  void
 spew_output()  spew_output(void)
 {  {
 #ifdef DEBUGGING  #ifdef DEBUGGING
     if (debug & 256)          if (debug & 256)
         say("il=%ld lfl=%ld\n",input_lines,last_frozen_line);                  say("il=%ld lfl=%ld\n", input_lines, last_frozen_line);
 #endif  #endif
     if (input_lines)          if (input_lines)
         copy_till(input_lines);         /* dump remainder of file */                  copy_till(input_lines); /* dump remainder of file */
     fclose(ofp);          fclose(ofp);
     ofp = Nullfp;          ofp = Nullfp;
 }  }
   
 /* Copy one line from input to output. */  /*
    * Copy one line from input to output.
    */
 void  void
 dump_line(line)  dump_line(LINENUM line)
 LINENUM line;  
 {  {
     char *s;          char    *s, R_newline = '\n';
     char R_newline = '\n';  
   
     s = ifetch(line, 0);          s = ifetch(line, 0);
     if (s == NULL)          if (s == NULL)
         return;                  return;
     /* Note: string is not null terminated. */          /* Note: string is not null terminated. */
     for (; putc(*s, ofp) != R_newline; s++) ;          for (; putc(*s, ofp) != R_newline; s++)
                   ;
 }  }
   
 /* Does the patch pattern match at line base+offset? */  /*
    * Does the patch pattern match at line base+offset?
    */
 bool  bool
 patch_match(base, offset, fuzz)  patch_match(LINENUM base, LINENUM offset, LINENUM fuzz)
 LINENUM base;  
 LINENUM offset;  
 LINENUM fuzz;  
 {  {
     LINENUM pline = 1 + fuzz;          LINENUM pline = 1 + fuzz;
     LINENUM iline;          LINENUM iline;
     LINENUM pat_lines = pch_ptrn_lines() - fuzz;          LINENUM pat_lines = pch_ptrn_lines() - fuzz;
   
     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {          for (iline = base + offset + fuzz; pline <= pat_lines; pline++, iline++) {
         if (canonicalize) {                  if (canonicalize) {
             if (!similar(ifetch(iline, (offset >= 0)),                          if (!similar(ifetch(iline, (offset >= 0)),
                          pfetch(pline),                              pfetch(pline), pch_line_len(pline)))
                          pch_line_len(pline) ))                                  return FALSE;
                 return FALSE;                  } else if (strnNE(ifetch(iline, (offset >= 0)),
                       pfetch(pline), pch_line_len(pline)))
                           return FALSE;
         }          }
         else if (strnNE(ifetch(iline, (offset >= 0)),          return TRUE;
                    pfetch(pline),  
                    pch_line_len(pline) ))  
             return FALSE;  
     }  
     return TRUE;  
 }  }
   
 /* Do two lines match with canonicalized white space? */  /*
    * Do two lines match with canonicalized white space?
    */
 bool  bool
 similar(a,b,len)  similar(char *a, char *b, int len)
 char *a;  
 char *b;  
 int len;  
 {  {
     if (a == NULL || b == NULL)          if (a == NULL || b == NULL)
         return FALSE;  
     while (len) {  
         if (isspace(*b)) {              /* whitespace (or \n) to match? */  
             if (!isspace(*a))           /* no corresponding whitespace? */  
                 return FALSE;                  return FALSE;
             while (len && isspace(*b) && *b != '\n')          while (len) {
                 b++,len--;              /* skip pattern whitespace */                  if (isspace(*b)) {      /* whitespace (or \n) to match? */
             while (isspace(*a) && *a != '\n')                          if (!isspace(*a))       /* no corresponding whitespace? */
                 a++;                    /* skip target whitespace */                                  return FALSE;
             if (*a == '\n' || *b == '\n')                          while (len && isspace(*b) && *b != '\n')
                 return (*a == *b);      /* should end in sync */                                  b++, len--;     /* skip pattern whitespace */
                           while (isspace(*a) && *a != '\n')
                                   a++;    /* skip target whitespace */
                           if (*a == '\n' || *b == '\n')
                                   return (*a == *b);      /* should end in sync */
                   } else if (*a++ != *b++)        /* match non-whitespace chars */
                           return FALSE;
                   else
                           len--;  /* probably not necessary */
         }          }
         else if (*a++ != *b++)          /* match non-whitespace chars */          return TRUE;            /* actually, this is not reached */
             return FALSE;          /* since there is always a \n */
         else  
             len--;                      /* probably not necessary */  
     }  
     return TRUE;                        /* actually, this is not reached */  
                                         /* since there is always a \n */  
 }  }
   
 /* Exit with cleanup. */  /*
    * Exit with cleanup.
    */
 void  void
 my_exit(status)  my_exit(int status)
 int status;  
 {  {
     unlink(TMPINNAME);          unlink(TMPINNAME);
     if (!toutkeep) {          if (!toutkeep)
         unlink(TMPOUTNAME);                  unlink(TMPOUTNAME);
     }          if (!trejkeep)
     if (!trejkeep) {                  unlink(TMPREJNAME);
         unlink(TMPREJNAME);          unlink(TMPPATNAME);
     }          exit(status);
     unlink(TMPPATNAME);  
     exit(status);  
 }  }

Legend:
Removed from v.1.19  
changed lines
  Added in v.1.20