[news.software.anu-news] Patch part 3 of 7

fritz@unocss.UUCP (Tim Russell) (08/27/89)

+-+-+-+ Beginning of part 3 +-+-+-+
X`009filearg[0] = Nullch;
X    `125
X
X    if (outname != Nullch) `123
X`009free(outname);
X`009outname = Nullch;
X    `125
X
X    last_offset = 0;
X
X    diff_type = 0;
X
X    if (revision != Nullch) `123
X`009free(revision);
X`009revision = Nullch;
X    `125
X
X    reverse = FALSE;
X    skip_rest_of_patch = FALSE;
X
X    get_some_switches();
X
X    if (filec >= 2)
X`009fatal1("You may not change to a different patch file.\n");
X`125
X
X/* Process switches and filenames up to next '+' or end of list. */
X
Xvoid
Xget_some_switches()
X`123
X    Reg1 char *s;
X
X    rejname[0] = '\0';
X    Argc_last = Argc;
X    Argv_last = Argv;
X    if (!Argc)
X`009return;
X    for (Argc--,Argv++; Argc; Argc--,Argv++) `123
X`009s = Argv[0];
X`009if (strEQ(s, "+")) `123
X`009    return;`009`009`009/* + will be skipped by for loop */
X`009`125
X#ifdef VMS
X`009if (*s == '<')`009`009`009/* Parse '<filename' syntax */
X`009    filearg[1] = savestr(s + 1);
X`009if ((*s != '-' && *s != '<') `124`124 !s[1]) `123
X#else
X`009if (*s != '-' `124`124 !s[1]) `123
X#endif
X`009    if (filec == MAXFILEC)
X`009`009fatal1("Too many file arguments.\n");
X`009    filearg[filec++] = savestr(s);
X`009`125
X#ifdef VMS
X`009if ((*s == '-') && s[1]) `123
X#else
X`009else `123
X#endif
X`009    switch (*++s) `123
X`009    case 'b':
X`009`009origext = savestr(Argv[1]);
X`009`009Argc--,Argv++;
X`009`009break;
X`009    case 'c':
X`009`009diff_type = CONTEXT_DIFF;
X`009`009break;
X`009    case 'd':
X`009`009if (!*++s) `123
X`009`009    Argc--,Argv++;
X`009`009    s = Argv[0];
X`009`009`125
X`009`009if (chdir(s) < 0)
X`009`009    fatal2("Can't cd to %s.\n", s);
X`009`009break;
X`009    case 'D':
X`009    `009do_defines = TRUE;
X`009`009if (!*++s) `123
X`009`009    Argc--,Argv++;
X`009`009    s = Argv[0];
X`009`009`125
X`009`009Sprintf(if_defined, "#ifdef %s\n", s);
X`009`009Sprintf(not_defined, "#ifndef %s\n", s);
X`009`009Sprintf(end_defined, "#endif /* %s */\n", s);
X`009`009break;
X`009    case 'e':
X`009`009diff_type = ED_DIFF;
X`009`009break;
X`009    case 'f':
X`009`009force = TRUE;
X`009`009break;
X`009    case 'F':
X`009`009if (*++s == '=')
X`009`009    s++;
X`009`009maxfuzz = atoi(s);
X`009`009break;
X`009    case 'l':
X`009`009canonicalize = TRUE;
X`009`009break;
X`009    case 'n':
X`009`009diff_type = NORMAL_DIFF;
X`009`009break;
X`009    case 'N':
X`009`009noreverse = TRUE;
X`009`009break;
X`009    case 'o':
X`009`009outname = savestr(Argv[1]);
X`009`009Argc--,Argv++;
X`009`009break;
X`009    case 'p':
X#ifdef VMS
X            case 'P':
X#endif
X`009`009if (*++s == '=')
X`009`009    s++;
X`009`009strippath = atoi(s);
X`009`009break;
X`009    case 'r':
X`009`009Strcpy(rejname, Argv[1]);
X`009`009Argc--,Argv++;
X`009`009break;
X`009    case 'R':
X`009`009reverse = TRUE;
X`009`009break;
X`009    case 's':
X`009`009verbose = FALSE;
X`009`009break;
X`009    case 'S':
X`009`009skip_rest_of_patch = TRUE;
X`009`009break;
X`009    case 'v':
X`009`009version();
X`009`009break;
X#ifdef DEBUGGING
X`009    case 'x':
X`009`009debug = atoi(s+1);
X`009`009break;
X#endif
X`009    default:
X`009`009fatal2("Unrecognized switch: %s\n", Argv[0]);
X`009    `125
X`009`125
X    `125
X`125
X
X/* Attempt to find the right place to apply this hunk of patch. */
X
XLINENUM
Xlocate_hunk(fuzz)
XLINENUM fuzz;
X`123
X    Reg1 LINENUM first_guess = pch_first() + last_offset;
X    Reg2 LINENUM offset;
X    LINENUM pat_lines = pch_ptrn_lines();
X    Reg3 LINENUM max_pos_offset = input_lines - first_guess
X`009`009`009`009- pat_lines + 1;`032
X    Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
X`009`009`009`009+ pch_context();
X
X    if (!pat_lines)`009`009`009/* null range matches always */
X`009return first_guess;
X    if (max_neg_offset >= first_guess)`009/* do not try lines < 0 */
X`009max_neg_offset = first_guess - 1;
V    if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz)
X)
X`009return first_guess;
X    for (offset = 1; ; offset++) `123
X`009Reg5 bool check_after = (offset <= max_pos_offset);
X`009Reg6 bool check_before = (offset <= max_neg_offset);
X
X`009if (check_after && patch_match(first_guess, offset, fuzz)) `123
X#ifdef DEBUGGING
X`009    if (debug & 1)
X`009`009say3("Offset changing from %ld to %ld\n", last_offset, offset);
X#endif
X`009    last_offset = offset;
X`009    return first_guess+offset;
X`009`125
X`009else if (check_before && patch_match(first_guess, -offset, fuzz)) `123
X#ifdef DEBUGGING
X`009    if (debug & 1)
X`009`009say3("Offset changing from %ld to %ld\n", last_offset, -offset);
X#endif
X`009    last_offset = -offset;
X`009    return first_guess-offset;
X`009`125
X`009else if (!check_before && !check_after)
X`009    return Nulline;
X    `125
X`125
X
X/* We did not find the pattern, dump out the hunk so they can handle it. */
X
Xvoid
Xabort_hunk()
X`123
X    Reg1 LINENUM i;
X    Reg2 LINENUM pat_end = pch_end();
V    /* add in last_offset to guess the same as the previous successful hunk *
X/
X    LINENUM oldfirst = pch_first() + last_offset;
X    LINENUM newfirst = pch_newfirst() + last_offset;
X    LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
X    LINENUM newlast = newfirst + pch_repl_lines() - 1;
X    char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : "");
X    char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----");
X
X    fprintf(rejfp, "***************\n");
X    for (i=0; i<=pat_end; i++) `123
X`009switch (pch_char(i)) `123
X`009case '*':
X`009    if (oldlast < oldfirst)
X`009`009fprintf(rejfp, "*** 0%s\n", stars);
X`009    else if (oldlast == oldfirst)
X`009`009fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
X`009    else
X`009`009fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
X`009    break;
X`009case '=':
X`009    if (newlast < newfirst)
X`009`009fprintf(rejfp, "--- 0%s\n", minuses);
X`009    else if (newlast == newfirst)
X`009`009fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
X`009    else
X`009`009fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
X`009    break;
X`009case '\n':
X`009    fprintf(rejfp, "%s", pfetch(i));
X`009    break;
X`009case ' ': case '-': case '+': case '!':
X`009    fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
X`009    break;
X`009default:
X`009    say1("Fatal internal error in abort_hunk().\n");`032
X`009    abort();
X`009`125
X    `125
X`125
X
X/* We found where to apply it (we hope), so do it. */
X
Xvoid
Xapply_hunk(where)
XLINENUM where;
X`123
X    Reg1 LINENUM old = 1;
X    Reg2 LINENUM lastline = pch_ptrn_lines();
X    Reg3 LINENUM new = lastline+1;
X#define OUTSIDE 0
X#define IN_IFNDEF 1
X#define IN_IFDEF 2
X#define IN_ELSE 3
X    Reg4 int def_state = OUTSIDE;
X    Reg5 bool R_do_defines = do_defines;
X    Reg6 LINENUM pat_end = pch_end();
X
X    where--;
X    while (pch_char(new) == '=' `124`124 pch_char(new) == '\n')
X`009new++;
X   `032
X    while (old <= lastline) `123
X`009if (pch_char(old) == '-') `123
X`009    copy_till(where + old - 1);
X`009    if (R_do_defines) `123
X`009`009if (def_state == OUTSIDE) `123
X`009`009    fputs(not_defined, ofp);
X`009`009    def_state = IN_IFNDEF;
X`009`009`125
X`009`009else if (def_state == IN_IFDEF) `123
X`009`009    fputs(else_defined, ofp);
X`009`009    def_state = IN_ELSE;
X`009`009`125
X`009`009fputs(pfetch(old), ofp);
X`009    `125
X`009    last_frozen_line++;
X`009    old++;
X`009`125
X`009else if (new > pat_end)
X`009    break;
X`009else if (pch_char(new) == '+') `123
X`009    copy_till(where + old - 1);
X`009    if (R_do_defines) `123
X`009`009if (def_state == IN_IFNDEF) `123
X`009`009    fputs(else_defined, ofp);
X`009`009    def_state = IN_ELSE;
X`009`009`125
X`009`009else if (def_state == OUTSIDE) `123
X`009`009    fputs(if_defined, ofp);
X`009`009    def_state = IN_IFDEF;
X`009`009`125
X`009    `125
X`009    fputs(pfetch(new), ofp);
X`009    new++;
X`009`125
X`009else `123
X`009    if (pch_char(new) != pch_char(old)) `123
V`009`009say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers,
X maybe?\n",
X`009`009    pch_hunk_beg() + old,
X`009`009    pch_hunk_beg() + new);
X#ifdef DEBUGGING
X`009`009say3("oldchar = '%c', newchar = '%c'\n",
X`009`009    pch_char(old), pch_char(new));
X#endif
X`009`009my_exit(1);
X`009    `125
X`009    if (pch_char(new) == '!') `123
X`009`009copy_till(where + old - 1);
X`009`009if (R_do_defines) `123
X`009`009   fputs(not_defined, ofp);
X`009`009   def_state = IN_IFNDEF;
X`009`009`125
X`009`009while (pch_char(old) == '!') `123
X`009`009    if (R_do_defines) `123
X`009`009`009fputs(pfetch(old), ofp);
X`009`009    `125
X`009`009    last_frozen_line++;
X`009`009    old++;
X`009`009`125
X`009`009if (R_do_defines) `123
X`009`009    fputs(else_defined, ofp);
X`009`009    def_state = IN_ELSE;
X`009`009`125
X`009`009while (pch_char(new) == '!') `123
X`009`009    fputs(pfetch(new), ofp);
X`009`009    new++;
X`009`009`125
X`009`009if (R_do_defines) `123
X`009`009    fputs(end_defined, ofp);
X`009`009    def_state = OUTSIDE;
X`009`009`125
X`009    `125
X`009    else `123
X`009`009assert(pch_char(new) == ' ');
X`009`009old++;
X`009`009new++;
X`009    `125
X`009`125
X    `125
X    if (new <= pat_end && pch_char(new) == '+') `123
X`009copy_till(where + old - 1);
X`009if (R_do_defines) `123
X`009    if (def_state == OUTSIDE) `123
X`009    `009fputs(if_defined, ofp);
X`009`009def_state = IN_IFDEF;
X`009    `125
X`009    else if (def_state == IN_IFNDEF) `123
X`009`009fputs(else_defined, ofp);
X`009`009def_state = IN_ELSE;
X`009    `125
X`009`125
X`009while (new <= pat_end && pch_char(new) == '+') `123
X`009    fputs(pfetch(new), ofp);
X`009    new++;
X`009`125
X    `125
X    if (R_do_defines && def_state != OUTSIDE) `123
X`009fputs(end_defined, ofp);
X    `125
X`125
X
X/* Open the new file. */
X
Xvoid
Xinit_output(name)
Xchar *name;
X`123
X    ofp = fopen(name, "w");
X    if (ofp == Nullfp)
X`009fatal2("patch: can't create %s.\n", name);
X`125
X
X/* Open a file to put hunks we can't locate. */
X
Xvoid
Xinit_reject(name)
Xchar *name;
X`123
X    rejfp = fopen(name, "w");
X    if (rejfp == Nullfp)
X`009fatal2("patch: can't create %s.\n", name);
X`125
X
X/* Copy input file to output, up to wherever hunk is to be applied. */
X
Xvoid
Xcopy_till(lastline)
XReg1 LINENUM lastline;
X`123
X    Reg2 LINENUM R_last_frozen_line = last_frozen_line;
X
X    if (R_last_frozen_line > lastline)
X`009say1("patch: misordered hunks! output will be garbled.\n");
X    while (R_last_frozen_line < lastline) `123
X`009dump_line(++R_last_frozen_line);
X    `125
X    last_frozen_line = R_last_frozen_line;
X`125
X
X/* Finish copying the input file to the output file. */
X
Xvoid
Xspew_output()
X`123
X#ifdef DEBUGGING
X    if (debug & 256)
X`009say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
X#endif
X    if (input_lines)
X`009copy_till(input_lines);`009`009/* dump remainder of file */
X    Fclose(ofp);
X    ofp = Nullfp;
X`125
X
X/* Copy one line from input to output. */
X
Xvoid
Xdump_line(line)
XLINENUM line;
X`123
X    Reg1 char *s;
X    Reg2 char R_newline = '\n';
X
X    /* Note: string is not null terminated. */
X    for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
X`125
X
X/* Does the patch pattern match at line base+offset? */
X
Xbool
Xpatch_match(base, offset, fuzz)
XLINENUM base;
XLINENUM offset;
XLINENUM fuzz;
X`123
X    Reg1 LINENUM pline = 1 + fuzz;
X    Reg2 LINENUM iline;
X    Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
X
X    for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) `123
X`009if (canonicalize) `123
X`009    if (!similar(ifetch(iline, (offset >= 0)),
X`009`009`009 pfetch(pline),
X`009`009`009 pch_line_len(pline) ))
X`009`009return FALSE;
X`009`125
X`009else if (strnNE(ifetch(iline, (offset >= 0)),
X`009`009   pfetch(pline),
X`009`009   pch_line_len(pline) ))
X`009    return FALSE;
X    `125
X    return TRUE;
X`125
X
X/* Do two lines match with canonicalized white space? */
X
Xbool
Xsimilar(a,b,len)
XReg1 char *a;
XReg2 char *b;
XReg3 int len;
X`123
X    while (len) `123
X`009if (isspace(*b)) `123`009`009/* whitespace (or \n) to match? */
X`009    if (!isspace(*a))`009`009/* no corresponding whitespace? */
X`009`009return FALSE;
X`009    while (len && isspace(*b) && *b != '\n')
X`009`009b++,len--;`009`009/* skip pattern whitespace */
X`009    while (isspace(*a) && *a != '\n')
X`009`009a++;`009`009`009/* skip target whitespace */
X`009    if (*a == '\n' `124`124 *b == '\n')
X`009`009return (*a == *b);`009/* should end in sync */
X`009`125
X`009else if (*a++ != *b++)`009`009/* match non-whitespace chars */
X`009    return FALSE;
X`009else
X`009    len--;`009`009`009/* probably not necessary */
X    `125
X    return TRUE;`009`009`009/* actually, this is not reached */
X`009`009`009`009`009/* since there is always a \n */
X`125
X
X/* Exit with cleanup. */
X
Xvoid
Xmy_exit(status)
Xint status;
X`123
X    while (unlink(TMPINNAME) >= 0);
X    if (!toutkeep) `123
X`009while (unlink(TMPOUTNAME) >= 0);
X    `125
X    if (!trejkeep) `123
X`009while (unlink(TMPREJNAME) >= 0);
X    `125
X    while (unlink(TMPPATNAME) >= 0);
X    exit(status);
X`125
$ GOSUB UNPACK_FILE

$ FILE_IS = "PATCH.DOC"
$ CHECKSUM_IS = 964225761
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X
X
X
X
XPATCH(1)            UNIX Programmer's Manual             PATCH(1)
X
X
X
XNAME
X     patch - a program for applying a diff file to an original
X
XSYNOPSIS
X     patch [options] orig patchfile [+ [options] orig]
X
X     but usually just
X
X     patch <patchfile
X
XDESCRIPTION
X     Patch will take a patch file containing any of the three
X     forms of difference listing produced by the diff program and
X     apply those differences to an original file, producing a
X     patched version.  By default, the patched version is put in
X     place of the original, with the original file backed up to
X     the same name with the extension ".orig", or as specified by
X     the -b switch.  You may also specify where you want the out-
X     put to go with a -o switch.  If patchfile is omitted, or is
X     a hyphen, the patch will be read from standard input.
X
X     Upon startup, patch will attempt to determine the type of
X     the diff listing, unless over-ruled by a -c, -e, or -n
X     switch.  Context diffs and normal diffs are applied by the
X     patch program itself, while ed diffs are simply fed to the
X     ed editor via a pipe.
X
X     Patch will try to skip any leading garbage, apply the diff,
X     and then skip any trailing garbage.  Thus you could feed an
X     article or message containing a diff listing to patch, and
X     it should work.  If the entire diff is indented by a con-
X     sistent amount, this will be taken into account.
X
X     With context diffs, and to a lesser extent with normal
X     diffs, patch can detect when the line numbers mentioned in
X     the patch are incorrect, and will attempt to find the
-+-+-+-+-+ End of part 3 +-+-+-+-+-
-- 
---------------------------------+--------------------------------------------
 Tim Russell, Computer Operator  | Internet: russell@zeus.unl.edu
 Campus Computing                | Bitnet:   russell@unoma1
 University of Nebraska at Omaha | UUCP:     uunet!zeus.unl.edu!russell