home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 278.lha / Patch_v0.91 / patch.c < prev    next >
C/C++ Source or Header  |  1989-08-06  |  24KB  |  836 lines

  1. char rcsid[] =
  2.       "$Header: patch.c,v 2.0.1.6 88/06/22 20:46:39 lwall Locked $";
  3.  
  4. /* patch - a program to apply diffs to original files
  5.  *
  6.  * Copyright 1986, Larry Wall
  7.  *
  8.  * This program may be copied as long as you don't try to make any
  9.  * money off of it, or pretend that you wrote it.
  10.  *
  11.  * $Log:        patch.c,v $
  12.  * Revision 2.0.1.6  88/06/22  20:46:39  lwall
  13.  * patch12: rindex() wasn't declared
  14.  *
  15.  * Revision 2.0.1.5  88/06/03  15:09:37  lwall
  16.  * patch10: exit code improved.
  17.  * patch10: better support for non-flexfilenames.
  18.  *
  19.  * Revision 2.0.1.4  87/02/16  14:00:04  lwall
  20.  * Short replacement caused spurious "Out of sync" message.
  21.  *
  22.  * Revision 2.0.1.3  87/01/30  22:45:50  lwall
  23.  * Improved diagnostic on sync error.
  24.  * Moved do_ed_script() to pch.c.
  25.  *
  26.  * Revision 2.0.1.2  86/11/21  09:39:15  lwall
  27.  * Fuzz factor caused offset of installed lines.
  28.  *
  29.  * Revision 2.0.1.1  86/10/29  13:10:22  lwall
  30.  * Backwards search could terminate prematurely.
  31.  *
  32.  * Revision 2.0  86/09/17  15:37:32  lwall
  33.  * Baseline for netwide release.
  34.  *
  35.  * Revision 1.5  86/08/01  20:53:24  lwall
  36.  * Changed some %d's to %ld's.
  37.  * Linted.
  38.  *
  39.  * Revision 1.4  86/08/01  19:17:29  lwall
  40.  * Fixes for machines that can't vararg.
  41.  * Added fuzz factor.
  42.  * Generalized -p.
  43.  * General cleanup.
  44.  *
  45.  * 85/08/15 van%ucbmonet@berkeley
  46.  * Changes for 4.3bsd diff -c.
  47.  *
  48.  * Revision 1.3  85/03/26  15:07:43  lwall
  49.  * Frozen.
  50.  *
  51.  * Revision 1.2.1.9  85/03/12  17:03:35  lwall
  52.  * Changed pfp->_file to fileno(pfp).
  53.  *
  54.  * Revision 1.2.1.8  85/03/12  16:30:43  lwall
  55.  * Check i_ptr and i_womp to make sure they aren't null before freeing.
  56.  * Also allow ed output to be suppressed.
  57.  *
  58.  * Revision 1.2.1.7  85/03/12  15:56:13  lwall
  59.  * Added -p option from jromine@uci-750a.
  60.  *
  61.  * Revision 1.2.1.6  85/03/12  12:12:51  lwall
  62.  * Now checks for normalness of file to patch.
  63.  *
  64.  * Revision 1.2.1.5  85/03/12  11:52:12  lwall
  65.  * Added -D (#ifdef) option from joe@fluke.
  66.  *
  67.  * Revision 1.2.1.4  84/12/06  11:14:15  lwall
  68.  * Made smarter about SCCS subdirectories.
  69.  *
  70.  * Revision 1.2.1.3  84/12/05  11:18:43  lwall
  71.  * Added -l switch to do loose string comparison.
  72.  *
  73.  * Revision 1.2.1.2  84/12/04  09:47:13  lwall
  74.  * Failed hunk count not reset on multiple patch file.
  75.  *
  76.  * Revision 1.2.1.1  84/12/04  09:42:37  lwall
  77.  * Branch for sdcrdcf changes.
  78.  *
  79.  * Revision 1.2  84/11/29  13:29:51  lwall
  80.  * Linted.  Identifiers uniqified.  Fixed i_ptr malloc() bug.  Fixed
  81.  * multiple calls to mktemp().  Will now work on machines that can only
  82.  * read 32767 chars.  Added -R option for diffs with new and old swapped.
  83.  * Various cosmetic changes.
  84.  *
  85.  * Revision 1.1  84/11/09  17:03:58  lwall
  86.  * Initial revision
  87.  *
  88.  */
  89.  
  90. #include "INTERN.h"
  91. #include "common.h"
  92. #include "EXTERN.h"
  93. #include "version.h"
  94. #include "util.h"
  95. #include "pch.h"
  96. #include "inp.h"
  97.  
  98. /* procedures */
  99.  
  100. void reinitialize_almost_everything();
  101. void get_some_switches();
  102. LINENUM locate_hunk();
  103. void abort_hunk();
  104. void apply_hunk();
  105. void init_output();
  106. void init_reject();
  107. void copy_till();
  108. void spew_output();
  109. void dump_line();
  110. bool patch_match();
  111. bool similar();
  112. void re_input();
  113. void my_exit();
  114.  
  115. /* Apply a set of diffs as appropriate. */
  116.  
  117. main(argc,argv)
  118. int argc;
  119. char **argv;
  120. {
  121.     LINENUM where;
  122.     LINENUM newwhere;
  123.     LINENUM fuzz;
  124.     LINENUM mymaxfuzz;
  125.     int hunk = 0;
  126.     int failed = 0;
  127.     int failtotal = 0;
  128.     int i;
  129.  
  130.  
  131. #ifdef AMIGA  /* set up heapmem.o for Manx */
  132.    extern long _Heapsize;
  133.  
  134.    _Heapsize = 60000;   /* works only on expanded machines, probably. */
  135. #endif
  136.  
  137.     setbuf(stderr, serrbuf);
  138.     for (i = 0; i<MAXFILEC; i++)
  139.         filearg[i] = Nullch;
  140.     Mktemp(TMPOUTNAME);
  141.     Mktemp(TMPINNAME);
  142.     Mktemp(TMPREJNAME);
  143.     Mktemp(TMPPATNAME);
  144.  
  145.     /* parse switches */
  146.     Argc = argc;
  147.     Argv = argv;
  148.     get_some_switches();
  149.  
  150.     /* make sure we clean up /tmp in case of disaster */
  151.     set_signals(0);
  152.  
  153.     for (
  154.         open_patch_file(filearg[1]);
  155.         there_is_another_patch();
  156.         reinitialize_almost_everything()
  157.     ) {                                 /* for each patch in patch file */
  158.  
  159.         if (outname == Nullch)
  160.             outname = savestr(filearg[0]);
  161.  
  162.         /* initialize the patched file */
  163.         if (!skip_rest_of_patch)
  164.             init_output(TMPOUTNAME);
  165.  
  166.         /* for ed script just up and do it and exit */
  167.         if (diff_type == ED_DIFF) {
  168.             do_ed_script();
  169.             continue;
  170.         }
  171.  
  172.         /* initialize reject file */
  173.         init_reject(TMPREJNAME);
  174.  
  175.         /* find out where all the lines are */
  176.         if (!skip_rest_of_patch)
  177.             scan_input(filearg[0]);
  178.  
  179.         /* from here on, open no standard i/o files, because malloc */
  180.         /* might misfire and we can't catch it easily */
  181.  
  182.         /* apply each hunk of patch */
  183.         hunk = 0;
  184.         failed = 0;
  185.         out_of_mem = FALSE;
  186.         while (another_hunk()) {
  187.             hunk++;
  188.             fuzz = Nulline;
  189.             mymaxfuzz = pch_context();
  190.             if (maxfuzz < mymaxfuzz)
  191.                 mymaxfuzz = maxfuzz;
  192.             if (!skip_rest_of_patch) {
  193.                 do {
  194.                     where = locate_hunk(fuzz);
  195.                     if (hunk == 1 && where == Nulline && !force) {
  196.                                                 /* dwim for reversed patch? */
  197.                         if (!pch_swap()) {
  198.                             if (fuzz == Nulline)
  199.                                 say1(
  200. "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
  201.                             continue;
  202.                         }
  203.                         reverse = !reverse;
  204.                         where = locate_hunk(fuzz);  /* try again */
  205.                         if (where == Nulline) {     /* didn't find it swapped */
  206.                             if (!pch_swap())         /* put it back to normal */
  207.                                 fatal1("Lost hunk on alloc error!\n");
  208.                             reverse = !reverse;
  209.                         }
  210.                         else if (noreverse) {
  211.                             if (!pch_swap())         /* put it back to normal */
  212.                                 fatal1("Lost hunk on alloc error!\n");
  213.                             reverse = !reverse;
  214.                             say1(
  215. "Ignoring previously applied (or reversed) patch.\n");
  216.                             skip_rest_of_patch = TRUE;
  217.                         }
  218.                         else {
  219.                             ask3(
  220. "%seversed (or previously applied) patch detected!  %s -R? [y] ",
  221.                                 reverse ? "R" : "Unr",
  222.                                 reverse ? "Assume" : "Ignore");
  223.                             if (*buf == 'n') {
  224.                                 ask1("Apply anyway? [n] ");
  225.                                 if (*buf != 'y')
  226.                                     skip_rest_of_patch = TRUE;
  227.                                 where = Nulline;
  228.                                 reverse = !reverse;
  229.                                 if (!pch_swap())  /* put it back to normal */
  230.                                     fatal1("Lost hunk on alloc error!\n");
  231.                             }
  232.                         }
  233.                     }
  234.                 } while (!skip_rest_of_patch && where == Nulline &&
  235.                     ++fuzz <= mymaxfuzz);
  236.  
  237.                 if (skip_rest_of_patch) {               /* just got decided */
  238.                     Fclose(ofp);
  239.                     ofp = Nullfp;
  240.                 }
  241.             }
  242.  
  243.             newwhere = pch_newfirst() + last_offset;
  244.             if (skip_rest_of_patch) {
  245.                 abort_hunk();
  246.                 failed++;
  247.                 if (verbose)
  248.                     say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
  249.             }
  250.             else if (where == Nulline) {
  251.                 abort_hunk();
  252.                 failed++;
  253.                 if (verbose)
  254.                     say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
  255.             }
  256.             else {
  257.                 apply_hunk(where);
  258.                 if (verbose) {
  259.                     say3("Hunk #%d succeeded at %ld", hunk, newwhere);
  260.                     if (fuzz)
  261.                         say2(" with fuzz %ld", fuzz);
  262.                     if (last_offset)
  263.                         say3(" (offset %ld line%s)",
  264.                             last_offset, last_offset==1L?"":"s");
  265.                     say1(".\n");
  266.                 }
  267.             }
  268.         }
  269.  
  270.         if (out_of_mem && using_plan_a) {
  271.             Argc = Argc_last;
  272.             Argv = Argv_last;
  273.             say1("\n\nRan out of memory using Plan A--trying again...\n\n");
  274.             Fclose(ofp);     /* fixed -- elg. */
  275.             Fclose(rejfp);
  276.             continue;
  277.         }
  278.  
  279.         assert(hunk);
  280.  
  281.         /* finish spewing out the new file */
  282.         if (!skip_rest_of_patch)
  283.             spew_output();
  284.  
  285.         /* and put the output where desired */
  286.         ignore_signals();
  287.         if (!skip_rest_of_patch) {
  288.             if (move_file(TMPOUTNAME, outname) < 0) {
  289.                 toutkeep = TRUE;
  290.                 chmod(TMPOUTNAME, filemode);
  291.             }
  292.             else
  293.                 chmod(outname, filemode);
  294.         }
  295.         Fclose(rejfp);
  296.         rejfp = Nullfp;
  297.         if (failed) {
  298.             failtotal += failed;
  299.             if (!*rejname) {
  300.                 Strcpy(rejname, outname);
  301. #ifndef FLEXFILENAMES
  302.                 {
  303.                     char *rindex();
  304.                     char *s = rindex(rejname,'/');
  305.  
  306.                     if (!s)
  307.                         s = rejname;
  308.                     if (strlen(s) > 13)
  309.                         if (s[12] == '.')       /* try to preserve difference */
  310.                             s[12] = s[13];      /* between .h, .c, .y, etc. */
  311.                         s[13] = '\0';
  312.                 }
  313. #endif
  314.                 Strcat(rejname, REJEXT);
  315.             }
  316.             if (skip_rest_of_patch) {
  317.                 say4("%d out of %d hunks ignored--saving rejects to %s\n",
  318.                     failed, hunk, rejname);
  319.             }
  320.             else {
  321.                 say4("%d out of %d hunks failed--saving rejects to %s\n",
  322.                     failed, hunk, rejname);
  323.             }
  324.             if (move_file(TMPREJNAME, rejname) < 0)
  325.                 trejkeep = TRUE;
  326.         }
  327.         set_signals(1);
  328.     }
  329.     my_exit(failtotal);
  330. }
  331.  
  332. /* Prepare to find the next patch to do in the patch file. */
  333.  
  334. void
  335. reinitialize_almost_everything()
  336. {
  337.     re_patch();
  338.     re_input();
  339.  
  340.     input_lines = 0;
  341.     last_frozen_line = 0;
  342.  
  343.     filec = 0;
  344.     if (filearg[0] != Nullch && !out_of_mem) {
  345.         free(filearg[0]);
  346.         filearg[0] = Nullch;
  347.     }
  348.  
  349.     if (outname != Nullch) {
  350.         free(outname);
  351.         outname = Nullch;
  352.     }
  353.  
  354.     last_offset = 0;
  355.  
  356.     diff_type = 0;
  357.  
  358.     if (revision != Nullch) {
  359.         free(revision);
  360.         revision = Nullch;
  361.     }
  362.  
  363.     reverse = FALSE;
  364.     skip_rest_of_patch = FALSE;
  365.  
  366.     get_some_switches();
  367.  
  368.     if (filec >= 2)
  369.         fatal1("You may not change to a different patch file.\n");
  370. }
  371.  
  372. /* Process switches and filenames up to next '+' or end of list. */
  373.  
  374. void
  375. get_some_switches()
  376. {
  377.     Reg1 char *s;
  378.  
  379.     rejname[0] = '\0';
  380.     Argc_last = Argc;
  381.     Argv_last = Argv;
  382.     if (!Argc)
  383.         return;
  384.     for (Argc--,Argv++; Argc; Argc--,Argv++) {
  385.         s = Argv[0];
  386.         if (strEQ(s, "+")) {
  387.             return;                     /* + will be skipped by for loop */
  388.         }
  389.         if (*s != '-' || !s[1]) {
  390.             if (filec == MAXFILEC)
  391.                 fatal1("Too many file arguments.\n");
  392.             filearg[filec++] = savestr(s);
  393.         }
  394.         else {
  395.             switch (*++s) {
  396.             case 'b':
  397.                 origext = savestr(Argv[1]);
  398.                 Argc--,Argv++;
  399.                 break;
  400.             case 'B':
  401.                 origprae = savestr(Argv[1]);
  402.                 Argc--,Argv++;
  403.                 break;
  404.             case 'c':
  405.                 diff_type = CONTEXT_DIFF;
  406.                 break;
  407.             case 'd':
  408.                 if (!*++s) {
  409.                     Argc--,Argv++;
  410.                     s = Argv[0];
  411.                 }
  412.       /* note that I have written chdir for the Amiga now. */
  413.                 if (chdir(s) < 0)
  414.                     fatal2("Can't cd to %s.\n", s);
  415.                 break;
  416.             case 'D':
  417.                 do_defines = TRUE;
  418.                 if (!*++s) {
  419.                     Argc--,Argv++;
  420.                     s = Argv[0];
  421.                 }
  422.                 if (!isalpha(*s))
  423.                     fatal1("Argument to -D not an identifier.\n");
  424.                 Sprintf(if_defined, "#ifdef %s\n", s);
  425.                 Sprintf(not_defined, "#ifndef %s\n", s);
  426.                 Sprintf(end_defined, "#endif /* %s */\n", s);
  427.                 break;
  428.             case 'e':
  429.                 diff_type = ED_DIFF;
  430.                 break;
  431.             case 'f':
  432.                 force = TRUE;
  433.                 break;
  434.             case 'F':
  435.                 if (*++s == '=')
  436.                     s++;
  437.                 maxfuzz = atoi(s);
  438.                 break;
  439.             case 'l':
  440.                 canonicalize = TRUE;
  441.                 break;
  442.             case 'n':
  443.                 diff_type = NORMAL_DIFF;
  444.                 break;
  445.             case 'N':
  446.                 noreverse = TRUE;
  447.                 break;
  448.             case 'o':
  449.                 outname = savestr(Argv[1]);
  450.                 Argc--,Argv++;
  451.                 break;
  452.             case 'p':
  453.                 if (*++s == '=')
  454.                     s++;
  455.                 strippath = atoi(s);
  456.                 break;
  457.             case 'r':
  458.                 Strcpy(rejname, Argv[1]);
  459.                 Argc--,Argv++;
  460.                 break;
  461.             case 'R':
  462.                 reverse = TRUE;
  463.                 break;
  464.             case 's':
  465.                 verbose = FALSE;
  466.                 break;
  467.             case 'S':
  468.                 skip_rest_of_patch = TRUE;
  469.                 break;
  470.             case 'v':
  471.                 version();
  472.                 break;
  473. #ifdef DEBUGGING
  474.             case 'x':
  475.                 debug = atoi(s+1);
  476.                 break;
  477. #endif
  478.             default:
  479.                 fatal2("Unrecognized switch: %s\n", Argv[0]);
  480.             }
  481.         }
  482.     }
  483. }
  484.  
  485. /* Attempt to find the right place to apply this hunk of patch. */
  486.  
  487. LINENUM
  488. locate_hunk(fuzz)
  489. LINENUM fuzz;
  490. {
  491.     Reg1 LINENUM first_guess = pch_first() + last_offset;
  492.     Reg2 LINENUM offset;
  493.     LINENUM pat_lines = pch_ptrn_lines();
  494.     Reg3 LINENUM max_pos_offset = input_lines - first_guess
  495.                                 - pat_lines + 1;
  496.     Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
  497.                                 + pch_context();
  498.  
  499.     if (!pat_lines)                     /* null range matches always */
  500.         return first_guess;
  501.     if (max_neg_offset >= first_guess)  /* do not try lines < 0 */
  502.         max_neg_offset = first_guess - 1;
  503.     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
  504.         return first_guess;
  505.     for (offset = 1; ; offset++) {
  506.         Reg5 bool check_after = (offset <= max_pos_offset);
  507.       Reg6 bool check_before = (offset <= max_neg_offset);
  508.  
  509.         if (check_after && patch_match(first_guess, offset, fuzz)) {
  510. #ifdef DEBUGGING
  511.             if (debug & 1)
  512.                 say3("Offset changing from %ld to %ld\n", last_offset, offset);
  513. #endif
  514.             last_offset = offset;
  515.             return first_guess+offset;
  516.         }
  517.         else if (check_before && patch_match(first_guess, -offset, fuzz)) {
  518. #ifdef DEBUGGING
  519.             if (debug & 1)
  520.                 say3("Offset changing from %ld to %ld\n", last_offset, -offset);
  521. #endif
  522.             last_offset = -offset;
  523.             return first_guess-offset;
  524.         }
  525.         else if (!check_before && !check_after)
  526.             return Nulline;
  527.     }
  528. }
  529.  
  530. /* We did not find the pattern, dump out the hunk so they can handle it. */
  531.  
  532. void
  533. abort_hunk()
  534. {
  535.     Reg1 LINENUM i;
  536.     Reg2 LINENUM pat_end = pch_end();
  537.     /* add in last_offset to guess the same as the previous successful hunk */
  538.     LINENUM oldfirst = pch_first() + last_offset;
  539.     LINENUM newfirst = pch_newfirst() + last_offset;
  540.     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
  541.     LINENUM newlast = newfirst + pch_repl_lines() - 1;
  542.     char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : "");
  543.     char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----");
  544.  
  545.     fprintf(rejfp, "***************\n");
  546.     for (i=0; i<=pat_end; i++) {
  547.         switch (pch_char(i)) {
  548.         case '*':
  549.             if (oldlast < oldfirst)
  550.                 fprintf(rejfp, "*** 0%s\n", stars);
  551.             else if (oldlast == oldfirst)
  552.                 fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
  553.             else
  554.                 fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
  555.             break;
  556.         case '=':
  557.             if (newlast < newfirst)
  558.                 fprintf(rejfp, "--- 0%s\n", minuses);
  559.             else if (newlast == newfirst)
  560.                 fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
  561.             else
  562.                 fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
  563.             break;
  564.         case '\n':
  565.             fprintf(rejfp, "%s", pfetch(i));
  566.             break;
  567.         case ' ': case '-': case '+': case '!':
  568.             fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
  569.             break;
  570.         default:
  571.             say1("Fatal internal error in abort_hunk().\n");
  572.             abort();
  573.         }
  574.     }
  575. }
  576.  
  577. /* We found where to apply it (we hope), so do it. */
  578.  
  579. void
  580. apply_hunk(where)
  581. LINENUM where;
  582. {
  583.     Reg1 LINENUM old = 1;
  584.     Reg2 LINENUM lastline = pch_ptrn_lines();
  585.     Reg3 LINENUM new = lastline+1;
  586. #define OUTSIDE 0
  587. #define IN_IFNDEF 1
  588. #define IN_IFDEF 2
  589. #define IN_ELSE 3
  590.     Reg4 int def_state = OUTSIDE;
  591.     Reg5 bool R_do_defines = do_defines;
  592.     Reg6 LINENUM pat_end = pch_end();
  593.  
  594.     where--;
  595.     while (pch_char(new) == '=' || pch_char(new) == '\n')
  596.         new++;
  597.  
  598.     while (old <= lastline) {
  599.         if (pch_char(old) == '-') {
  600.             copy_till(where + old - 1);
  601.             if (R_do_defines) {
  602.                 if (def_state == OUTSIDE) {
  603.                     fputs(not_defined, ofp);
  604.                     def_state = IN_IFNDEF;
  605.                 }
  606.                 else if (def_state == IN_IFDEF) {
  607.                     fputs(else_defined, ofp);
  608.                     def_state = IN_ELSE;
  609.                 }
  610.                 fputs(pfetch(old), ofp);
  611.             }
  612.             last_frozen_line++;
  613.             old++;
  614.         }
  615.         else if (new > pat_end)
  616.             break;
  617.         else if (pch_char(new) == '+') {
  618.             copy_till(where + old - 1);
  619.             if (R_do_defines) {
  620.                 if (def_state == IN_IFNDEF) {
  621.                     fputs(else_defined, ofp);
  622.                     def_state = IN_ELSE;
  623.                 }
  624.                 else if (def_state == OUTSIDE) {
  625.                     fputs(if_defined, ofp);
  626.                     def_state = IN_IFDEF;
  627.                 }
  628.             }
  629.             fputs(pfetch(new), ofp);
  630.             new++;
  631.         }
  632.         else {
  633.             if (pch_char(new) != pch_char(old)) {
  634.                 say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
  635.                     pch_hunk_beg() + old,
  636.                     pch_hunk_beg() + new);
  637. #ifdef DEBUGGING
  638.                 say3("oldchar = '%c', newchar = '%c'\n",
  639.                     pch_char(old), pch_char(new));
  640. #endif
  641.                 my_exit(1);
  642.             }
  643.             if (pch_char(new) == '!') {
  644.                 copy_till(where + old - 1);
  645.                 if (R_do_defines) {
  646.                    fputs(not_defined, ofp);
  647.                    def_state = IN_IFNDEF;
  648.                 }
  649.                 while (pch_char(old) == '!') {
  650.                     if (R_do_defines) {
  651.                         fputs(pfetch(old), ofp);
  652.                     }
  653.                     last_frozen_line++;
  654.                     old++;
  655.                 }
  656.                 if (R_do_defines) {
  657.                     fputs(else_defined, ofp);
  658.                     def_state = IN_ELSE;
  659.                 }
  660.                 while (pch_char(new) == '!') {
  661.                     fputs(pfetch(new), ofp);
  662.                     new++;
  663.                 }
  664.                 if (R_do_defines) {
  665.                     fputs(end_defined, ofp);
  666.                     def_state = OUTSIDE;
  667.                 }
  668.             }
  669.             else {
  670.                 assert(pch_char(new) == ' ');
  671.                 old++;
  672.                 new++;
  673.             }
  674.         }
  675.     }
  676.     if (new <= pat_end && pch_char(new) == '+') {
  677.         copy_till(where + old - 1);
  678.         if (R_do_defines) {
  679.             if (def_state == OUTSIDE) {
  680.                 fputs(if_defined, ofp);
  681.                 def_state = IN_IFDEF;
  682.             }
  683.             else if (def_state == IN_IFNDEF) {
  684.                 fputs(else_defined, ofp);
  685.                 def_state = IN_ELSE;
  686.             }
  687.         }
  688.         while (new <= pat_end && pch_char(new) == '+') {
  689.             fputs(pfetch(new), ofp);
  690.             new++;
  691.         }
  692.     }
  693.     if (R_do_defines && def_state != OUTSIDE) {
  694.         fputs(end_defined, ofp);
  695.     }
  696. }
  697.  
  698.  
  699. /* Open the new file. */
  700.  
  701. void
  702. init_output(name)
  703. char *name;
  704. {
  705.     ofp = fopen(name, "w");
  706.     if (ofp == Nullfp)
  707.         fatal2("patch init output: can't create %s.\n", name);
  708. }
  709.  
  710. /* Open a file to put hunks we can't locate. */
  711.  
  712. void
  713. init_reject(name)
  714. char *name;
  715. {
  716.     rejfp = fopen(name, "w");
  717.     if (rejfp == Nullfp)
  718.         fatal2("patch init reject: can't create %s.\n", name);
  719. }
  720.  
  721. /* Copy input file to output, up to wherever hunk is to be applied. */
  722.  
  723. void
  724. copy_till(lastline)
  725. Reg1 LINENUM lastline;
  726. {
  727.     Reg2 LINENUM R_last_frozen_line = last_frozen_line;
  728.  
  729.     if (R_last_frozen_line > lastline)
  730.         say1("patch: misordered hunks! output will be garbled.\n");
  731.     while (R_last_frozen_line < lastline) {
  732.         dump_line(++R_last_frozen_line);
  733.     }
  734.     last_frozen_line = R_last_frozen_line;
  735. }
  736.  
  737. /* Finish copying the input file to the output file. */
  738.  
  739. void
  740. spew_output()
  741. {
  742. #ifdef DEBUGGING
  743.     if (debug & 256)
  744.         say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
  745. #endif
  746.     if (input_lines)
  747.         copy_till(input_lines);         /* dump remainder of file */
  748.     Fclose(ofp);
  749.     ofp = Nullfp;
  750. }
  751.  
  752. /* Copy one line from input to output. */
  753.  
  754. void
  755. dump_line(line)
  756. LINENUM line;
  757. {
  758.     Reg1 char *s;
  759.     Reg2 char R_newline = '\n';
  760.  
  761.     /* Note: string is not null terminated. */
  762.     for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
  763. }
  764.  
  765. /* Does the patch pattern match at line base+offset? */
  766.  
  767. bool
  768. patch_match(base, offset, fuzz)
  769. LINENUM base;
  770. LINENUM offset;
  771. LINENUM fuzz;
  772. {
  773.     Reg1 LINENUM pline = 1 + fuzz;
  774.     Reg2 LINENUM iline;
  775.     Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
  776.  
  777.     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
  778.         if (canonicalize) {
  779.             if (!similar(ifetch(iline, (offset >= 0)),
  780.                          pfetch(pline),
  781.                          pch_line_len(pline) ))
  782.                 return FALSE;
  783.         }
  784.         else if (strnNE(ifetch(iline, (offset >= 0)),
  785.                    pfetch(pline),
  786.                    pch_line_len(pline) ))
  787.             return FALSE;
  788.     }
  789.     return TRUE;
  790. }
  791.  
  792. /* Do two lines match with canonicalized white space? */
  793.  
  794. bool
  795. similar(a,b,len)
  796. Reg1 char *a;
  797. Reg2 char *b;
  798. Reg3 int len;
  799. {
  800.     while (len) {
  801.         if (isspace(*b)) {              /* whitespace (or \n) to match? */
  802.             if (!isspace(*a))           /* no corresponding whitespace? */
  803.                 return FALSE;
  804.             while (len && isspace(*b) && *b != '\n')
  805.                 b++,len--;              /* skip pattern whitespace */
  806.             while (isspace(*a) && *a != '\n')
  807.                 a++;                    /* skip target whitespace */
  808.             if (*a == '\n' || *b == '\n')
  809.                 return (*a == *b);      /* should end in sync */
  810.         }
  811.         else if (*a++ != *b++)          /* match non-whitespace chars */
  812.             return FALSE;
  813.         else
  814.             len--;                      /* probably not necessary */
  815.     }
  816.     return TRUE;                        /* actually, this is not reached */
  817.                                         /* since there is always a \n */
  818. }
  819.  
  820. /* Exit with cleanup. */
  821.  
  822. void
  823. my_exit(status)
  824. int status;
  825. {
  826.     Unlink(TMPINNAME);
  827.     if (!toutkeep) {
  828.         Unlink(TMPOUTNAME);
  829.     }
  830.     if (!trejkeep) {
  831.         Unlink(TMPREJNAME);
  832.     }
  833.     Unlink(TMPPATNAME);
  834.     exit(status);
  835. }
  836.