pch.c

Go to the documentation of this file.
00001 /* $Header: /opt/proj/minix/cvsroot/src/commands/patch/pch.c,v 1.1.1.1 2005/04/21 14:55:11 beng Exp $
00002  *
00003  * $Log: pch.c,v $
00004  * Revision 1.1.1.1  2005/04/21 14:55:11  beng
00005  * Initial import of pre-3.0.1
00006  *
00007  * Revision 1.1.1.1  2005/04/20 13:33:19  beng
00008  * Initial import of minix 2.0.4
00009  *
00010  * Revision 2.0.1.7  88/06/03  15:13:28  lwall
00011  * patch10: Can now find patches in shar scripts.
00012  * patch10: Hunks that swapped and then swapped back could core dump.
00013  * 
00014  * Revision 2.0.1.6  87/06/04  16:18:13  lwall
00015  * pch_swap didn't swap p_bfake and p_efake.
00016  * 
00017  * Revision 2.0.1.5  87/01/30  22:47:42  lwall
00018  * Improved responses to mangled patches.
00019  * 
00020  * Revision 2.0.1.4  87/01/05  16:59:53  lwall
00021  * New-style context diffs caused double call to free().
00022  * 
00023  * Revision 2.0.1.3  86/11/14  10:08:33  lwall
00024  * Fixed problem where a long pattern wouldn't grow the hunk.
00025  * Also restored p_input_line when backtracking so error messages are right.
00026  * 
00027  * Revision 2.0.1.2  86/11/03  17:49:52  lwall
00028  * New-style delete triggers spurious assertion error.
00029  * 
00030  * Revision 2.0.1.1  86/10/29  15:52:08  lwall
00031  * Could falsely report new-style context diff.
00032  * 
00033  * Revision 2.0  86/09/17  15:39:37  lwall
00034  * Baseline for netwide release.
00035  * 
00036  */
00037 
00038 #include "EXTERN.h"
00039 #include "common.h"
00040 #include "util.h"
00041 #include "INTERN.h"
00042 #include "pch.h"
00043 
00044 /* Patch (diff listing) abstract type. */
00045 
00046 static long p_filesize;                 /* size of the patch file */
00047 static LINENUM p_first;                 /* 1st line number */
00048 static LINENUM p_newfirst;              /* 1st line number of replacement */
00049 static LINENUM p_ptrn_lines;            /* # lines in pattern */
00050 static LINENUM p_repl_lines;            /* # lines in replacement text */
00051 static LINENUM p_end = -1;              /* last line in hunk */
00052 static LINENUM p_max;                   /* max allowed value of p_end */
00053 static LINENUM p_context = 3;           /* # of context lines */
00054 static LINENUM p_input_line = 0;        /* current line # from patch file */
00055 #ifdef SMALL
00056 static long *p_line = Null(long *);     /* the text of the hunk */
00057 #else
00058 static char **p_line = Null(char**);    /* the text of the hunk */
00059 #endif
00060 static short *p_len = Null(short*);     /* length of each line */
00061 static char *p_char = Nullch;           /* +, -, and ! */
00062 static int hunkmax = INITHUNKMAX;       /* size of above arrays to begin with */
00063 static int p_indent;                    /* indent to patch */
00064 static LINENUM p_base;                  /* where to intuit this time */
00065 static LINENUM p_bline;                 /* line # of p_base */
00066 static LINENUM p_start;                 /* where intuit found a patch */
00067 static LINENUM p_sline;                 /* and the line number for it */
00068 static LINENUM p_hunk_beg;              /* line number of current hunk */
00069 static LINENUM p_efake = -1;            /* end of faked up lines--don't free */
00070 static LINENUM p_bfake = -1;            /* beg of faked up lines */
00071 
00072 /* Prepare to look for the next patch in the patch file. */
00073 
00074 void
00075 re_patch()
00076 {
00077     p_first = Nulline;
00078     p_newfirst = Nulline;
00079     p_ptrn_lines = Nulline;
00080     p_repl_lines = Nulline;
00081     p_end = (LINENUM)-1;
00082     p_max = Nulline;
00083     p_indent = 0;
00084 }
00085 
00086 /* Open the patch file at the beginning of time. */
00087 
00088 void
00089 open_patch_file(filename)
00090 char *filename;
00091 {
00092     if (filename == Nullch || !*filename || strEQ(filename, "-")) {
00093         pfp = fopen(TMPPATNAME, "w");
00094         if (pfp == Nullfp)
00095             fatal2("patch: can't create %s.\n", TMPPATNAME);
00096         while (fgets(buf, sizeof buf, stdin) != Nullch)
00097             fputs(buf, pfp);
00098         Fclose(pfp);
00099         filename = TMPPATNAME;
00100     }
00101     pfp = fopen(filename, "r");
00102     if (pfp == Nullfp)
00103         fatal2("patch file %s not found\n", filename);
00104     Fstat(fileno(pfp), &filestat);
00105     p_filesize = filestat.st_size;
00106     next_intuit_at(0L,1L);                      /* start at the beginning */
00107     set_hunkmax();
00108 }
00109 
00110 /* Make sure our dynamically realloced tables are malloced to begin with. */
00111 
00112 void
00113 set_hunkmax()
00114 {
00115 #ifndef lint
00116 #ifdef SMALL
00117     if (p_line == Null(long*))
00118 #else
00119     if (p_line == Null(char**))
00120 #endif
00121 #ifdef SMALL
00122         p_line = (long *) malloc((MEM)hunkmax * sizeof(long));
00123 #else
00124         p_line = (char**) malloc((MEM)hunkmax * sizeof(char *));
00125 #endif
00126     if (p_len == Null(short*))
00127         p_len  = (short*) malloc((MEM)hunkmax * sizeof(short));
00128 #endif
00129     if (p_char == Nullch)
00130         p_char = (char*)  malloc((MEM)hunkmax * sizeof(char));
00131 }
00132 
00133 /* Enlarge the arrays containing the current hunk of patch. */
00134 
00135 void
00136 grow_hunkmax()
00137 {
00138     hunkmax *= 2;
00139     /* 
00140      * Note that on most systems, only the p_line array ever gets fresh memory
00141      * since p_len can move into p_line's old space, and p_char can move into
00142      * p_len's old space.  Not on PDP-11's however.  But it doesn't matter.
00143      */
00144 #ifdef SMALL
00145     assert(p_line != Null(long*) && p_len != Null(short*) && p_char != Nullch);
00146 #else
00147     assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch);
00148 #endif
00149 #ifndef lint
00150 #ifdef SMALL
00151     p_line = (long*) realloc((char*)p_line, (MEM)hunkmax * sizeof(long));
00152 #else
00153     p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *));
00154 #endif
00155     p_len  = (short*) realloc((char*)p_len,  (MEM)hunkmax * sizeof(short));
00156     p_char = (char*)  realloc((char*)p_char, (MEM)hunkmax * sizeof(char));
00157 #endif
00158 #ifdef SMALL
00159     if (p_line != Null(long*) && p_len != Null(short*) && p_char != Nullch)
00160 #else
00161     if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch)
00162 #endif
00163         return;
00164     if (!using_plan_a)
00165         fatal1("patch: out of memory (grow_hunkmax)\n");
00166     out_of_mem = TRUE;          /* whatever is null will be allocated again */
00167                                 /* from within plan_a(), of all places */
00168 }
00169 
00170 /* True if the remainder of the patch file contains a diff of some sort. */
00171 
00172 bool
00173 there_is_another_patch()
00174 {
00175     if (p_base != 0L && p_base >= p_filesize) {
00176         if (verbose)
00177             say1("done\n");
00178         return FALSE;
00179     }
00180     if (verbose)
00181         say1("Hmm...");
00182     diff_type = intuit_diff_type();
00183     if (!diff_type) {
00184         if (p_base != 0L) {
00185             if (verbose)
00186                 say1("  Ignoring the trailing garbage.\ndone\n");
00187         }
00188         else
00189             say1("  I can't seem to find a patch in there anywhere.\n");
00190         return FALSE;
00191     }
00192     if (verbose)
00193         say3("  %sooks like %s to me...\n",
00194             (p_base == 0L ? "L" : "The next patch l"),
00195             diff_type == CONTEXT_DIFF ? "a context diff" :
00196             diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
00197             diff_type == NORMAL_DIFF ? "a normal diff" :
00198             "an ed script" );
00199     if (p_indent && verbose)
00200         say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s");
00201     skip_to(p_start,p_sline);
00202     while (filearg[0] == Nullch) {
00203         if (force) {
00204             say1("No file to patch.  Skipping...\n");
00205             filearg[0] = savestr(bestguess);
00206             return TRUE;
00207         }
00208         ask1("File to patch: ");
00209         if (*buf != '\n') {
00210             if (bestguess)
00211                 free(bestguess);
00212             bestguess = savestr(buf);
00213             filearg[0] = fetchname(buf, 0, FALSE);
00214         }
00215         if (filearg[0] == Nullch) {
00216             ask1("No file found--skip this patch? [n] ");
00217             if (*buf != 'y') {
00218                 continue;
00219             }
00220             if (verbose)
00221                 say1("Skipping patch...\n");
00222             filearg[0] = fetchname(bestguess, 0, TRUE);
00223             skip_rest_of_patch = TRUE;
00224             return TRUE;
00225         }
00226     }
00227     return TRUE;
00228 }
00229 
00230 /* Determine what kind of diff is in the remaining part of the patch file. */
00231 
00232 int
00233 intuit_diff_type()
00234 {
00235     Reg4 long this_line = 0;
00236     Reg5 long previous_line;
00237     Reg6 long first_command_line = -1;
00238     long fcl_line;
00239     Reg7 bool last_line_was_command = FALSE;
00240     Reg8 bool this_is_a_command = FALSE;
00241     Reg9 bool stars_last_line = FALSE;
00242     Reg10 bool stars_this_line = FALSE;
00243     Reg3 int indent;
00244     Reg1 char *s;
00245     Reg2 char *t;
00246     char *indtmp = Nullch;
00247     char *oldtmp = Nullch;
00248     char *newtmp = Nullch;
00249     char *indname = Nullch;
00250     char *oldname = Nullch;
00251     char *newname = Nullch;
00252     Reg11 int retval;
00253     bool no_filearg = (filearg[0] == Nullch);
00254 
00255     ok_to_create_file = FALSE;
00256     Fseek(pfp, p_base, 0);
00257     p_input_line = p_bline - 1;
00258     for (;;) {
00259         previous_line = this_line;
00260         last_line_was_command = this_is_a_command;
00261         stars_last_line = stars_this_line;
00262         this_line = ftell(pfp);
00263         indent = 0;
00264         p_input_line++;
00265         if (fgets(buf, sizeof buf, pfp) == Nullch) {
00266             if (first_command_line >= 0L) {
00267                                         /* nothing but deletes!? */
00268                 p_start = first_command_line;
00269                 p_sline = fcl_line;
00270                 retval = ED_DIFF;
00271                 goto scan_exit;
00272             }
00273             else {
00274                 p_start = this_line;
00275                 p_sline = p_input_line;
00276                 retval = 0;
00277                 goto scan_exit;
00278             }
00279         }
00280         for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
00281             if (*s == '\t')
00282                 indent += 8 - (indent % 8);
00283             else
00284                 indent++;
00285         }
00286         for (t=s; isdigit(*t) || *t == ','; t++) ; 
00287         this_is_a_command = (isdigit(*s) &&
00288           (*t == 'd' || *t == 'c' || *t == 'a') );
00289         if (first_command_line < 0L && this_is_a_command) { 
00290             first_command_line = this_line;
00291             fcl_line = p_input_line;
00292             p_indent = indent;          /* assume this for now */
00293         }
00294         if (!stars_last_line && strnEQ(s, "*** ", 4))
00295             oldtmp = savestr(s+4);
00296         else if (strnEQ(s, "--- ", 4))
00297             newtmp = savestr(s+4);
00298         else if (strnEQ(s, "Index:", 6))
00299             indtmp = savestr(s+6);
00300         else if (strnEQ(s, "Prereq:", 7)) {
00301             for (t=s+7; isspace(*t); t++) ;
00302             revision = savestr(t);
00303             for (t=revision; *t && !isspace(*t); t++) ;
00304             *t = '\0';
00305             if (!*revision) {
00306                 free(revision);
00307                 revision = Nullch;
00308             }
00309         }
00310         if ((!diff_type || diff_type == ED_DIFF) &&
00311           first_command_line >= 0L &&
00312           strEQ(s, ".\n") ) {
00313             p_indent = indent;
00314             p_start = first_command_line;
00315             p_sline = fcl_line;
00316             retval = ED_DIFF;
00317             goto scan_exit;
00318         }
00319         stars_this_line = strnEQ(s, "********", 8);
00320         if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line &&
00321                  strnEQ(s, "*** ", 4)) {
00322             if (!atol(s+4))
00323                 ok_to_create_file = TRUE;
00324             /* if this is a new context diff the character just before */
00325             /* the newline is a '*'. */
00326             while (*s != '\n')
00327                 s++;
00328             p_indent = indent;
00329             p_start = previous_line;
00330             p_sline = p_input_line - 1;
00331             retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);
00332             goto scan_exit;
00333         }
00334         if ((!diff_type || diff_type == NORMAL_DIFF) && 
00335           last_line_was_command &&
00336           (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) {
00337             p_start = previous_line;
00338             p_sline = p_input_line - 1;
00339             p_indent = indent;
00340             retval = NORMAL_DIFF;
00341             goto scan_exit;
00342         }
00343     }
00344   scan_exit:
00345     if (no_filearg) {
00346         if (indtmp != Nullch)
00347             indname = fetchname(indtmp, strippath, ok_to_create_file);
00348         if (oldtmp != Nullch)
00349             oldname = fetchname(oldtmp, strippath, ok_to_create_file);
00350         if (newtmp != Nullch)
00351             newname = fetchname(newtmp, strippath, ok_to_create_file);
00352         if (oldname && newname) {
00353             if (strlen(oldname) < strlen(newname))
00354                 filearg[0] = savestr(oldname);
00355             else
00356                 filearg[0] = savestr(newname);
00357         }
00358         else if (oldname)
00359             filearg[0] = savestr(oldname);
00360         else if (newname)
00361             filearg[0] = savestr(newname);
00362         else if (indname)
00363             filearg[0] = savestr(indname);
00364     }
00365     if (bestguess) {
00366         free(bestguess);
00367         bestguess = Nullch;
00368     }
00369     if (filearg[0] != Nullch)
00370         bestguess = savestr(filearg[0]);
00371     else if (indtmp != Nullch)
00372         bestguess = fetchname(indtmp, strippath, TRUE);
00373     else {
00374         if (oldtmp != Nullch)
00375             oldname = fetchname(oldtmp, strippath, TRUE);
00376         if (newtmp != Nullch)
00377             newname = fetchname(newtmp, strippath, TRUE);
00378         if (oldname && newname) {
00379             if (strlen(oldname) < strlen(newname))
00380                 bestguess = savestr(oldname);
00381             else
00382                 bestguess = savestr(newname);
00383         }
00384         else if (oldname)
00385             bestguess = savestr(oldname);
00386         else if (newname)
00387             bestguess = savestr(newname);
00388     }
00389     if (indtmp != Nullch)
00390         free(indtmp);
00391     if (oldtmp != Nullch)
00392         free(oldtmp);
00393     if (newtmp != Nullch)
00394         free(newtmp);
00395     if (indname != Nullch)
00396         free(indname);
00397     if (oldname != Nullch)
00398         free(oldname);
00399     if (newname != Nullch)
00400         free(newname);
00401     return retval;
00402 }
00403 
00404 /* Remember where this patch ends so we know where to start up again. */
00405 
00406 void
00407 next_intuit_at(file_pos,file_line)
00408 long file_pos;
00409 long file_line;
00410 {
00411     p_base = file_pos;
00412     p_bline = file_line;
00413 }
00414 
00415 /* Basically a verbose fseek() to the actual diff listing. */
00416 
00417 void
00418 skip_to(file_pos,file_line)
00419 long file_pos;
00420 long file_line;
00421 {
00422     char *ret;
00423 
00424     assert(p_base <= file_pos);
00425     if (verbose && p_base < file_pos) {
00426         Fseek(pfp, p_base, 0);
00427         say1("The text leading up to this was:\n--------------------------\n");
00428         while (ftell(pfp) < file_pos) {
00429             ret = fgets(buf, sizeof buf, pfp);
00430             assert(ret != Nullch);
00431             say2("|%s", buf);
00432         }
00433         say1("--------------------------\n");
00434     }
00435     else
00436         Fseek(pfp, file_pos, 0);
00437     p_input_line = file_line - 1;
00438 }
00439 
00440 /* True if there is more of the current diff listing to process. */
00441 
00442 bool
00443 another_hunk()
00444 {
00445     Reg1 char *s;
00446     Reg8 char *ret;
00447     Reg2 int context = 0;
00448 
00449     while (p_end >= 0) {
00450         if (p_end == p_efake)
00451             p_end = p_bfake;            /* don't free twice */
00452 #ifndef SMALL
00453         else
00454             free(p_line[p_end]);
00455 #endif
00456         p_end--;
00457     }
00458     assert(p_end == -1);
00459     p_efake = -1;
00460 #ifdef SMALL
00461     if (sfp != Nullfp)
00462         Fclose(sfp);
00463     sfp = fopen(TMPSTRNAME, "w");
00464     if (sfp == Nullfp)
00465         fatal2("patch: can't create %s.\n", TMPSTRNAME);
00466 #endif
00467 
00468     p_max = hunkmax;                    /* gets reduced when --- found */
00469     if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) {
00470         long line_beginning = ftell(pfp);
00471                                         /* file pos of the current line */
00472         LINENUM repl_beginning = 0;     /* index of --- line */
00473         Reg4 LINENUM fillcnt = 0;       /* #lines of missing ptrn or repl */
00474         Reg5 LINENUM fillsrc;           /* index of first line to copy */
00475         Reg6 LINENUM filldst;           /* index of first missing line */
00476         bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */
00477         Reg9 bool repl_could_be_missing = TRUE;
00478                                         /* no + or ! lines in this hunk */
00479         bool repl_missing = FALSE;      /* we are now backtracking */
00480         long repl_backtrack_position = 0;
00481                                         /* file pos of first repl line */
00482         LINENUM repl_patch_line;        /* input line number for same */
00483         Reg7 LINENUM ptrn_copiable = 0;
00484                                         /* # of copiable lines in ptrn */
00485 
00486         ret = pgets(buf, sizeof buf, pfp);
00487         p_input_line++;
00488         if (ret == Nullch || strnNE(buf, "********", 8)) {
00489             next_intuit_at(line_beginning,p_input_line);
00490             return FALSE;
00491         }
00492         p_context = 100;
00493         p_hunk_beg = p_input_line + 1;
00494         while (p_end < p_max) {
00495             line_beginning = ftell(pfp);
00496             ret = pgets(buf, sizeof buf, pfp);
00497             p_input_line++;
00498             if (ret == Nullch) {
00499                 if (p_max - p_end < 4)
00500                     Strcpy(buf, "  \n");  /* assume blank lines got chopped */
00501                 else {
00502                     if (repl_beginning && repl_could_be_missing) {
00503                         repl_missing = TRUE;
00504                         goto hunk_done;
00505                     }
00506                     fatal1("Unexpected end of file in patch.\n");
00507                 }
00508             }
00509             p_end++;
00510             assert(p_end < hunkmax);
00511             p_char[p_end] = *buf;
00512 #ifdef zilog
00513             p_line[(short)p_end] = Nullch;
00514 #else
00515 #ifdef SMALL
00516             p_line[p_end] = -1L;
00517             p_len[p_end] = 0;
00518 #else
00519             p_line[p_end] = Nullch;
00520 #endif
00521 #endif
00522             switch (*buf) {
00523             case '*':
00524                 if (strnEQ(buf, "********", 8)) {
00525                     if (repl_beginning && repl_could_be_missing) {
00526                         repl_missing = TRUE;
00527                         goto hunk_done;
00528                     }
00529                     else
00530                         fatal2("Unexpected end of hunk at line %ld.\n",
00531                             p_input_line);
00532                 }
00533                 if (p_end != 0) {
00534                     if (repl_beginning && repl_could_be_missing) {
00535                         repl_missing = TRUE;
00536                         goto hunk_done;
00537                     }
00538                     fatal3("Unexpected *** at line %ld: %s", p_input_line, buf);
00539                 }
00540                 context = 0;
00541 #ifdef SMALL
00542                 p_line[p_end] = saveStr(buf, p_len+p_end);
00543 #else
00544                 p_line[p_end] = savestr(buf);
00545                 if (out_of_mem) {
00546                     p_end--;
00547                     return FALSE;
00548                 }
00549 #endif
00550                 for (s=buf; *s && !isdigit(*s); s++) ;
00551                 if (!*s)
00552                     goto malformed;
00553                 if (strnEQ(s,"0,0",3))
00554                     strcpy(s,s+2);
00555                 p_first = (LINENUM) atol(s);
00556                 while (isdigit(*s)) s++;
00557                 if (*s == ',') {
00558                     for (; *s && !isdigit(*s); s++) ;
00559                     if (!*s)
00560                         goto malformed;
00561                     p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1;
00562                 }
00563                 else if (p_first)
00564                     p_ptrn_lines = 1;
00565                 else {
00566                     p_ptrn_lines = 0;
00567                     p_first = 1;
00568                 }
00569                 p_max = p_ptrn_lines + 6;       /* we need this much at least */
00570                 while (p_max >= hunkmax)
00571                     grow_hunkmax();
00572                 p_max = hunkmax;
00573                 break;
00574             case '-':
00575                 if (buf[1] == '-') {
00576                     if (repl_beginning ||
00577                         (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n')))
00578                     {
00579                         if (p_end == 1) {
00580                             /* `old' lines were omitted - set up to fill */
00581                             /* them in from 'new' context lines. */
00582                             p_end = p_ptrn_lines + 1;
00583                             fillsrc = p_end + 1;
00584                             filldst = 1;
00585                             fillcnt = p_ptrn_lines;
00586                         }
00587                         else {
00588                             if (repl_beginning) {
00589                                 if (repl_could_be_missing){
00590                                     repl_missing = TRUE;
00591                                     goto hunk_done;
00592                                 }
00593                                 fatal3(
00594 "Duplicate \"---\" at line %ld--check line numbers at line %ld.\n",
00595                                     p_input_line, p_hunk_beg + repl_beginning);
00596                             }
00597                             else {
00598                                 fatal4(
00599 "%s \"---\" at line %ld--check line numbers at line %ld.\n",
00600                                     (p_end <= p_ptrn_lines
00601                                         ? "Premature"
00602                                         : "Overdue" ),
00603                                     p_input_line, p_hunk_beg);
00604                             }
00605                         }
00606                     }
00607                     repl_beginning = p_end;
00608                     repl_backtrack_position = ftell(pfp);
00609                     repl_patch_line = p_input_line;
00610 #ifdef SMALL
00611                     p_line[p_end] = saveStr(buf, p_len+p_end);
00612 #else
00613                     p_line[p_end] = savestr(buf);
00614                     if (out_of_mem) {
00615                         p_end--;
00616                         return FALSE;
00617                     }
00618 #endif
00619                     p_char[p_end] = '=';
00620                     for (s=buf; *s && !isdigit(*s); s++) ;
00621                     if (!*s)
00622                         goto malformed;
00623                     p_newfirst = (LINENUM) atol(s);
00624                     while (isdigit(*s)) s++;
00625                     if (*s == ',') {
00626                         for (; *s && !isdigit(*s); s++) ;
00627                         if (!*s)
00628                             goto malformed;
00629                         p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1;
00630                     }
00631                     else if (p_newfirst)
00632                         p_repl_lines = 1;
00633                     else {
00634                         p_repl_lines = 0;
00635                         p_newfirst = 1;
00636                     }
00637                     p_max = p_repl_lines + p_end;
00638                     if (p_max > MAXHUNKSIZE)
00639                         fatal4("Hunk too large (%ld lines) at line %ld: %s",
00640                               p_max, p_input_line, buf);
00641                     while (p_max >= hunkmax)
00642                         grow_hunkmax();
00643                     if (p_repl_lines != ptrn_copiable)
00644                         repl_could_be_missing = FALSE;
00645                     break;
00646                 }
00647                 goto change_line;
00648             case '+':  case '!':
00649                 repl_could_be_missing = FALSE;
00650               change_line:
00651                 if (buf[1] == '\n' && canonicalize)
00652                     strcpy(buf+1," \n");
00653                 if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' &&
00654                   repl_beginning && repl_could_be_missing) {
00655                     repl_missing = TRUE;
00656                     goto hunk_done;
00657                 }
00658                 if (context > 0) {
00659                     if (context < p_context)
00660                         p_context = context;
00661                     context = -1000;
00662                 }
00663 #ifdef SMALL
00664                 p_line[p_end] = saveStr(buf+2, p_len+p_end);
00665 #else
00666                 p_line[p_end] = savestr(buf+2);
00667                 if (out_of_mem) {
00668                     p_end--;
00669                     return FALSE;
00670                 }
00671 #endif
00672                 break;
00673             case '\t': case '\n':       /* assume the 2 spaces got eaten */
00674                 if (repl_beginning && repl_could_be_missing &&
00675                   (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) {
00676                     repl_missing = TRUE;
00677                     goto hunk_done;
00678                 }
00679 #ifdef SMALL
00680                 p_line[p_end] = saveStr(buf, p_len+p_end);
00681 #else
00682                 p_line[p_end] = savestr(buf);
00683                 if (out_of_mem) {
00684                     p_end--;
00685                     return FALSE;
00686                 }
00687 #endif
00688                 if (p_end != p_ptrn_lines + 1) {
00689                     ptrn_spaces_eaten |= (repl_beginning != 0);
00690                     context++;
00691                     if (!repl_beginning)
00692                         ptrn_copiable++;
00693                     p_char[p_end] = ' ';
00694                 }
00695                 break;
00696             case ' ':
00697                 if (!isspace(buf[1]) &&
00698                   repl_beginning && repl_could_be_missing) {
00699                     repl_missing = TRUE;
00700                     goto hunk_done;
00701                 }
00702                 context++;
00703                 if (!repl_beginning)
00704                     ptrn_copiable++;
00705 #ifdef SMALL
00706                 p_line[p_end] = saveStr(buf+2, p_len+p_end);
00707 #else
00708                 p_line[p_end] = savestr(buf+2);
00709                 if (out_of_mem) {
00710                     p_end--;
00711                     return FALSE;
00712                 }
00713 #endif
00714                 break;
00715             default:
00716                 if (repl_beginning && repl_could_be_missing) {
00717                     repl_missing = TRUE;
00718                     goto hunk_done;
00719                 }
00720                 goto malformed;
00721             }
00722             /* set up p_len for strncmp() so we don't have to */
00723             /* assume null termination */
00724 #ifndef SMALL
00725             if (p_line[p_end])
00726                 p_len[p_end] = strlen(p_line[p_end]);
00727             else
00728                 p_len[p_end] = 0;
00729 #endif
00730         }
00731         
00732     hunk_done:
00733         if (p_end >=0 && !repl_beginning)
00734             fatal2("No --- found in patch at line %ld\n", pch_hunk_beg());
00735 
00736         if (repl_missing) {
00737             
00738             /* reset state back to just after --- */
00739             p_input_line = repl_patch_line;
00740 #ifndef SMALL
00741             for (p_end--; p_end > repl_beginning; p_end--)
00742                 free(p_line[p_end]);
00743 #endif
00744             Fseek(pfp, repl_backtrack_position, 0);
00745             
00746             /* redundant 'new' context lines were omitted - set */
00747             /* up to fill them in from the old file context */
00748             fillsrc = 1;
00749             filldst = repl_beginning+1;
00750             fillcnt = p_repl_lines;
00751             p_end = p_max;
00752         }
00753 
00754         if (diff_type == CONTEXT_DIFF &&
00755           (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) {
00756             if (verbose)
00757                 say4("%s\n%s\n%s\n",
00758 "(Fascinating--this is really a new-style context diff but without",
00759 "the telltale extra asterisks on the *** line that usually indicate",
00760 "the new style...)");
00761             diff_type = NEW_CONTEXT_DIFF;
00762         }
00763         
00764         /* if there were omitted context lines, fill them in now */
00765         if (fillcnt) {
00766             p_bfake = filldst;          /* remember where not to free() */
00767             p_efake = filldst + fillcnt - 1;
00768             while (fillcnt-- > 0) {
00769                 while (fillsrc <= p_end && p_char[fillsrc] != ' ')
00770                     fillsrc++;
00771                 if (fillsrc > p_end)
00772                     fatal2("Replacement text or line numbers mangled in hunk at line %ld\n",
00773                         p_hunk_beg);
00774                 p_line[filldst] = p_line[fillsrc];
00775                 p_char[filldst] = p_char[fillsrc];
00776                 p_len[filldst] = p_len[fillsrc];
00777                 fillsrc++; filldst++;
00778             }
00779             while (fillsrc <= p_end && fillsrc != repl_beginning &&
00780               p_char[fillsrc] != ' ')
00781                 fillsrc++;
00782 #ifdef DEBUGGING
00783             if (debug & 64)
00784                 printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n",
00785                     fillsrc,filldst,repl_beginning,p_end+1);
00786 #endif
00787             assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
00788             assert(filldst==p_end+1 || filldst==repl_beginning);
00789         }
00790     }
00791     else {                              /* normal diff--fake it up */
00792         char hunk_type;
00793         Reg3 int i;
00794         LINENUM min, max;
00795         long line_beginning = ftell(pfp);
00796 
00797         p_context = 0;
00798         ret = pgets(buf, sizeof buf, pfp);
00799         p_input_line++;
00800         if (ret == Nullch || !isdigit(*buf)) {
00801             next_intuit_at(line_beginning,p_input_line);
00802             return FALSE;
00803         }
00804         p_first = (LINENUM)atol(buf);
00805         for (s=buf; isdigit(*s); s++) ;
00806         if (*s == ',') {
00807             p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1;
00808             while (isdigit(*s)) s++;
00809         }
00810         else
00811             p_ptrn_lines = (*s != 'a');
00812         hunk_type = *s;
00813         if (hunk_type == 'a')
00814             p_first++;                  /* do append rather than insert */
00815         min = (LINENUM)atol(++s);
00816         for (; isdigit(*s); s++) ;
00817         if (*s == ',')
00818             max = (LINENUM)atol(++s);
00819         else
00820             max = min;
00821         if (hunk_type == 'd')
00822             min++;
00823         p_end = p_ptrn_lines + 1 + max - min + 1;
00824         if (p_end > MAXHUNKSIZE)
00825             fatal4("Hunk too large (%ld lines) at line %ld: %s",
00826                   p_end, p_input_line, buf);
00827         while (p_end >= hunkmax)
00828             grow_hunkmax();
00829         p_newfirst = min;
00830         p_repl_lines = max - min + 1;
00831         Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1);
00832 #ifdef SMALL
00833         p_line[0] = saveStr(buf, p_len);
00834 #else
00835         p_line[0] = savestr(buf);
00836         if (out_of_mem) {
00837             p_end = -1;
00838             return FALSE;
00839         }
00840 #endif
00841         p_char[0] = '*';
00842         for (i=1; i<=p_ptrn_lines; i++) {
00843             ret = pgets(buf, sizeof buf, pfp);
00844             p_input_line++;
00845             if (ret == Nullch)
00846                 fatal2("Unexpected end of file in patch at line %ld.\n",
00847                   p_input_line);
00848             if (*buf != '<')
00849                 fatal2("< expected at line %ld of patch.\n", p_input_line);
00850 #ifdef SMALL
00851             p_line[i] = saveStr(buf+2, p_len+i);
00852 #else
00853             p_line[i] = savestr(buf+2);
00854             if (out_of_mem) {
00855                 p_end = i-1;
00856                 return FALSE;
00857             }
00858 #endif
00859 #ifndef SMALL
00860             p_len[i] = strlen(p_line[i]);
00861 #endif
00862             p_char[i] = '-';
00863         }
00864         if (hunk_type == 'c') {
00865             ret = pgets(buf, sizeof buf, pfp);
00866             p_input_line++;
00867             if (ret == Nullch)
00868                 fatal2("Unexpected end of file in patch at line %ld.\n",
00869                     p_input_line);
00870             if (*buf != '-')
00871                 fatal2("--- expected at line %ld of patch.\n", p_input_line);
00872         }
00873         Sprintf(buf, "--- %ld,%ld\n", min, max);
00874 #ifdef SMALL
00875         p_line[i] = saveStr(buf, p_len+i);
00876 #else
00877         p_line[i] = savestr(buf);
00878         if (out_of_mem) {
00879             p_end = i-1;
00880             return FALSE;
00881         }
00882 #endif
00883         p_char[i] = '=';
00884         for (i++; i<=p_end; i++) {
00885             ret = pgets(buf, sizeof buf, pfp);
00886             p_input_line++;
00887             if (ret == Nullch)
00888                 fatal2("Unexpected end of file in patch at line %ld.\n",
00889                     p_input_line);
00890             if (*buf != '>')
00891                 fatal2("> expected at line %ld of patch.\n", p_input_line);
00892 #ifdef SMALL
00893             p_line[i] = saveStr(buf+2, p_len+i);
00894 #else
00895             p_line[i] = savestr(buf+2);
00896             if (out_of_mem) {
00897                 p_end = i-1;
00898                 return FALSE;
00899             }
00900 #endif
00901 #ifndef SMALL
00902             p_len[i] = strlen(p_line[i]);
00903 #endif
00904             p_char[i] = '+';
00905         }
00906     }
00907     if (reverse)                        /* backwards patch? */
00908         if (!pch_swap())
00909             say1("Not enough memory to swap next hunk!\n");
00910 #ifdef SMALL
00911     Fclose(sfp);
00912     sfp = fopen(TMPSTRNAME, "r");
00913 #endif
00914 #ifdef DEBUGGING
00915     if (debug & 2) {
00916         int i;
00917         char special;
00918 
00919         for (i=0; i <= p_end; i++) {
00920             if (i == p_ptrn_lines)
00921                 special = '^';
00922             else
00923                 special = ' ';
00924 #ifdef SMALL
00925             fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, pfetch(i));
00926 #else
00927             fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]);
00928 #endif
00929             Fflush(stderr);
00930         }
00931     }
00932 #endif
00933     if (p_end+1 < hunkmax)      /* paranoia reigns supreme... */
00934         p_char[p_end+1] = '^';  /* add a stopper for apply_hunk */
00935     return TRUE;
00936 
00937 malformed:
00938 #ifdef SMALL
00939     Fclose(sfp);
00940     sfp = Nullfp;
00941 #endif
00942     fatal3("Malformed patch at line %ld: %s", p_input_line, buf);
00943                 /* about as informative as "Syntax error" in C */
00944     return FALSE;       /* for lint */
00945 }
00946 
00947 /* Input a line from the patch file, worrying about indentation. */
00948 
00949 char *
00950 pgets(bf,sz,fp)
00951 char *bf;
00952 int sz;
00953 FILE *fp;
00954 {
00955     char *ret = fgets(bf, sz, fp);
00956     Reg1 char *s;
00957     Reg2 int indent = 0;
00958 
00959     if (p_indent && ret != Nullch) {
00960         for (s=buf;
00961           indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) {
00962             if (*s == '\t')
00963                 indent += 8 - (indent % 7);
00964             else
00965                 indent++;
00966         }
00967         if (buf != s)
00968             Strcpy(buf, s);
00969     }
00970     return ret;
00971 }
00972 
00973 /* Reverse the old and new portions of the current hunk. */
00974 
00975 bool
00976 pch_swap()
00977 {
00978 #ifdef SMALL
00979     long *tp_line;              /* the text of the hunk */
00980 #else
00981     char **tp_line;             /* the text of the hunk */
00982 #endif
00983     short *tp_len;              /* length of each line */
00984     char *tp_char;              /* +, -, and ! */
00985     Reg1 LINENUM i;
00986     Reg2 LINENUM n;
00987     bool blankline = FALSE;
00988     Reg3 char *s;
00989 
00990     i = p_first;
00991     p_first = p_newfirst;
00992     p_newfirst = i;
00993     
00994     /* make a scratch copy */
00995 
00996     tp_line = p_line;
00997     tp_len = p_len;
00998     tp_char = p_char;
00999 #ifdef SMALL
01000     p_line = Null(long*);       /* force set_hunkmax to allocate again */
01001 #else
01002     p_line = Null(char**);      /* force set_hunkmax to allocate again */
01003 #endif
01004     p_len = Null(short*);
01005     p_char = Nullch;
01006     set_hunkmax();
01007 #ifdef SMALL
01008     if (p_line == Null(long*) || p_len == Null(short*) || p_char == Nullch) {
01009 #else
01010     if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) {
01011 #endif
01012 #ifndef lint
01013 #ifdef SMALL
01014         if (p_line == Null(long*))
01015 #else
01016         if (p_line == Null(char**))
01017 #endif
01018             free((char*)p_line);
01019         p_line = tp_line;
01020         if (p_len == Null(short*))
01021             free((char*)p_len);
01022         p_len = tp_len;
01023 #endif
01024         if (p_char == Nullch)
01025             free((char*)p_char);
01026         p_char = tp_char;
01027         return FALSE;           /* not enough memory to swap hunk! */
01028     }
01029 
01030     /* now turn the new into the old */
01031 
01032     i = p_ptrn_lines + 1;
01033     if (tp_char[i] == '\n') {           /* account for possible blank line */
01034         blankline = TRUE;
01035         i++;
01036     }
01037     if (p_efake >= 0) {                 /* fix non-freeable ptr range */
01038         if (p_efake <= i)
01039             n = p_end - i + 1;
01040         else
01041             n = -i;
01042         p_efake += n;
01043         p_bfake += n;
01044     }
01045     for (n=0; i <= p_end; i++,n++) {
01046         p_line[n] = tp_line[i];
01047         p_char[n] = tp_char[i];
01048         if (p_char[n] == '+')
01049             p_char[n] = '-';
01050         p_len[n] = tp_len[i];
01051     }
01052     if (blankline) {
01053         i = p_ptrn_lines + 1;
01054         p_line[n] = tp_line[i];
01055         p_char[n] = tp_char[i];
01056         p_len[n] = tp_len[i];
01057         n++;
01058     }
01059     assert(p_char[0] == '=');
01060     p_char[0] = '*';
01061 #ifdef SMALL
01062     strEdit(p_line[0], '*', '-');
01063 #else
01064     for (s=p_line[0]; *s; s++)
01065         if (*s == '-')
01066             *s = '*';
01067 #endif
01068 
01069     /* now turn the old into the new */
01070 
01071     assert(tp_char[0] == '*');
01072     tp_char[0] = '=';
01073 #ifdef SMALL
01074     strEdit(tp_line[0], '-', '*');
01075 #else
01076     for (s=tp_line[0]; *s; s++)
01077         if (*s == '*')
01078             *s = '-';
01079 #endif
01080     for (i=0; n <= p_end; i++,n++) {
01081         p_line[n] = tp_line[i];
01082         p_char[n] = tp_char[i];
01083         if (p_char[n] == '-')
01084             p_char[n] = '+';
01085         p_len[n] = tp_len[i];
01086     }
01087     assert(i == p_ptrn_lines + 1);
01088     i = p_ptrn_lines;
01089     p_ptrn_lines = p_repl_lines;
01090     p_repl_lines = i;
01091 #ifndef lint
01092 #ifdef SMALL
01093     if (tp_line == Null(long*))
01094 #else
01095     if (tp_line == Null(char**))
01096 #endif
01097         free((char*)tp_line);
01098     if (tp_len == Null(short*))
01099         free((char*)tp_len);
01100 #endif
01101     if (tp_char == Nullch)
01102         free((char*)tp_char);
01103     return TRUE;
01104 }
01105 
01106 /* Return the specified line position in the old file of the old context. */
01107 
01108 LINENUM
01109 pch_first()
01110 {
01111     return p_first;
01112 }
01113 
01114 /* Return the number of lines of old context. */
01115 
01116 LINENUM
01117 pch_ptrn_lines()
01118 {
01119     return p_ptrn_lines;
01120 }
01121 
01122 /* Return the probable line position in the new file of the first line. */
01123 
01124 LINENUM
01125 pch_newfirst()
01126 {
01127     return p_newfirst;
01128 }
01129 
01130 /* Return the number of lines in the replacement text including context. */
01131 
01132 LINENUM
01133 pch_repl_lines()
01134 {
01135     return p_repl_lines;
01136 }
01137 
01138 /* Return the number of lines in the whole hunk. */
01139 
01140 LINENUM
01141 pch_end()
01142 {
01143     return p_end;
01144 }
01145 
01146 /* Return the number of context lines before the first changed line. */
01147 
01148 LINENUM
01149 pch_context()
01150 {
01151     return p_context;
01152 }
01153 
01154 /* Return the length of a particular patch line. */
01155 
01156 short
01157 pch_line_len(line)
01158 LINENUM line;
01159 {
01160     return p_len[line];
01161 }
01162 
01163 /* Return the control character (+, -, *, !, etc) for a patch line. */
01164 
01165 char
01166 pch_char(line)
01167 LINENUM line;
01168 {
01169     return p_char[line];
01170 }
01171 
01172 /* Return a pointer to a particular patch line. */
01173 
01174 #ifdef SMALL
01175 long
01176 saveStr(str, plen)
01177 char *str;
01178 short *plen;
01179 {
01180     long pos, ftell();
01181     int len;
01182 
01183     pos = ftell(sfp);
01184     len = strlen(str);
01185     fwrite(str, sizeof(char), len+1, sfp);
01186     *plen = len;
01187     return pos;
01188 }
01189 
01190 char *
01191 pfetch(line)
01192 LINENUM line;
01193 {
01194     static char *s, strbuf[BUFSIZ];
01195     int i, c;
01196 
01197     if (p_line[line] == -1L)
01198         return Nullch;
01199     else {
01200         Fseek(sfp, p_line[line], 0);
01201         for (i = 0, s = strbuf;
01202                 i < BUFSIZ && (c = fgetc(sfp)) != EOF && c; i++)
01203             *s++ = c;
01204         if (i == BUFSIZ)
01205                 fatal2("too long line (%.40s ..\n", strbuf);
01206     }
01207     *s = '\0';
01208     return strbuf;
01209 }
01210 
01211 void
01212 strEdit(pos, to, from)
01213 long pos;
01214 int to, from;
01215 {
01216     static char *s, strbuf[BUFSIZ];
01217     int i, c;
01218 
01219     if (pos != -1L) {
01220         for (i = 0, s = strbuf;
01221                 i < BUFSIZ && (c = fgetc(sfp)) != EOF && c; i++)
01222             *s++ = c;
01223         for (s = strbuf; *s; s++)
01224             if (*s == from)
01225                 *s = to;
01226         fwrite(strbuf, sizeof(char), i+1, sfp);
01227     }
01228 }
01229 #else
01230 char *
01231 pfetch(line)
01232 LINENUM line;
01233 {
01234     return p_line[line];
01235 }
01236 #endif
01237 
01238 /* Return where in the patch file this hunk began, for error messages. */
01239 
01240 LINENUM
01241 pch_hunk_beg()
01242 {
01243     return p_hunk_beg;
01244 }
01245 
01246 /* Apply an ed script by feeding ed itself. */
01247 
01248 void
01249 do_ed_script()
01250 {
01251     Reg1 char *t;
01252     Reg2 long beginning_of_this_line;
01253     Reg3 bool this_line_is_command = FALSE;
01254     Reg4 FILE *pipefp;
01255 
01256     if (!skip_rest_of_patch) {
01257         Unlink(TMPOUTNAME);
01258         copy_file(filearg[0], TMPOUTNAME);
01259         if (verbose)
01260             Sprintf(buf, "/bin/ed %s", TMPOUTNAME);
01261         else
01262             Sprintf(buf, "/bin/ed - %s", TMPOUTNAME);
01263         pipefp = popen(buf, "w");
01264     }
01265     for (;;) {
01266         beginning_of_this_line = ftell(pfp);
01267         if (pgets(buf, sizeof buf, pfp) == Nullch) {
01268             next_intuit_at(beginning_of_this_line,p_input_line);
01269             break;
01270         }
01271         p_input_line++;
01272         for (t=buf; isdigit(*t) || *t == ','; t++) ;
01273         this_line_is_command = (isdigit(*buf) &&
01274           (*t == 'd' || *t == 'c' || *t == 'a') );
01275         if (this_line_is_command) {
01276             if (!skip_rest_of_patch)
01277                 fputs(buf, pipefp);
01278             if (*t != 'd') {
01279                 while (pgets(buf, sizeof buf, pfp) != Nullch) {
01280                     p_input_line++;
01281                     if (!skip_rest_of_patch)
01282                         fputs(buf, pipefp);
01283                     if (strEQ(buf, ".\n"))
01284                         break;
01285                 }
01286             }
01287         }
01288         else {
01289             next_intuit_at(beginning_of_this_line,p_input_line);
01290             break;
01291         }
01292     }
01293     if (skip_rest_of_patch)
01294         return;
01295     fprintf(pipefp, "w\n");
01296     fprintf(pipefp, "q\n");
01297     Fflush(pipefp);
01298     Pclose(pipefp);
01299     ignore_signals();
01300     if (move_file(TMPOUTNAME, outname) < 0) {
01301         toutkeep = TRUE;
01302         chmod(TMPOUTNAME, filemode);
01303     }
01304     else
01305         chmod(outname, filemode);
01306     set_signals(1);
01307 }

Generated on Fri Apr 14 22:57:01 2006 for minix by  doxygen 1.4.6