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

Annotation of src/usr.bin/patch/patch.c, Revision 1.2

1.2     ! niklas      1: /*     $OpenBSD$       */
        !             2:
1.1       deraadt     3: /* patch - a program to apply diffs to original files
                      4:  *
                      5:  * Copyright 1986, Larry Wall
                      6:  *
                      7:  * This program may be copied as long as you don't try to make any
                      8:  * money off of it, or pretend that you wrote it.
                      9:  */
                     10:
                     11: #ifndef lint
1.2     ! niklas     12: static char rcsid[] = "$OpenBSD: patch.c,v 1.1.1.1 1995/10/18 08:45:55 deraadt Exp $";
1.1       deraadt    13: #endif /* not lint */
                     14:
                     15: #include "INTERN.h"
                     16: #include "common.h"
                     17: #include "EXTERN.h"
                     18: #include "version.h"
                     19: #include "util.h"
                     20: #include "pch.h"
                     21: #include "inp.h"
                     22: #include "backupfile.h"
                     23:
                     24: /* procedures */
                     25:
                     26: void reinitialize_almost_everything();
                     27: void get_some_switches();
                     28: LINENUM locate_hunk();
                     29: void abort_hunk();
                     30: void apply_hunk();
                     31: void init_output();
                     32: void init_reject();
                     33: void copy_till();
                     34: void spew_output();
                     35: void dump_line();
                     36: bool patch_match();
                     37: bool similar();
                     38: void re_input();
                     39: void my_exit();
                     40:
                     41: /* TRUE if -E was specified on command line.  */
                     42: static int remove_empty_files = FALSE;
                     43:
                     44: /* TRUE if -R was specified on command line.  */
                     45: static int reverse_flag_specified = FALSE;
                     46:
                     47: /* Apply a set of diffs as appropriate. */
                     48:
                     49: int
                     50: main(argc,argv)
                     51: int argc;
                     52: char **argv;
                     53: {
                     54:     LINENUM where;
                     55:     LINENUM newwhere;
                     56:     LINENUM fuzz;
                     57:     LINENUM mymaxfuzz;
                     58:     int hunk = 0;
                     59:     int failed = 0;
                     60:     int failtotal = 0;
                     61:     int i;
                     62:
                     63:     setbuf(stderr, serrbuf);
                     64:     for (i = 0; i<MAXFILEC; i++)
                     65:        filearg[i] = Nullch;
                     66:
                     67:     myuid = getuid();
                     68:
                     69:     /* Cons up the names of the temporary files.  */
                     70:     {
                     71:       /* Directory for temporary files.  */
                     72:       char *tmpdir;
                     73:       int tmpname_len;
                     74:
                     75:       tmpdir = getenv ("TMPDIR");
                     76:       if (tmpdir == NULL) {
                     77:        tmpdir = "/tmp";
                     78:       }
                     79:       tmpname_len = strlen (tmpdir) + 20;
                     80:
                     81:       TMPOUTNAME = (char *) malloc (tmpname_len);
                     82:       strcpy (TMPOUTNAME, tmpdir);
                     83:       strcat (TMPOUTNAME, "/patchoXXXXXX");
                     84:       Mktemp(TMPOUTNAME);
                     85:
                     86:       TMPINNAME = (char *) malloc (tmpname_len);
                     87:       strcpy (TMPINNAME, tmpdir);
                     88:       strcat (TMPINNAME, "/patchiXXXXXX");
                     89:       Mktemp(TMPINNAME);
                     90:
                     91:       TMPREJNAME = (char *) malloc (tmpname_len);
                     92:       strcpy (TMPREJNAME, tmpdir);
                     93:       strcat (TMPREJNAME, "/patchrXXXXXX");
                     94:       Mktemp(TMPREJNAME);
                     95:
                     96:       TMPPATNAME = (char *) malloc (tmpname_len);
                     97:       strcpy (TMPPATNAME, tmpdir);
                     98:       strcat (TMPPATNAME, "/patchpXXXXXX");
                     99:       Mktemp(TMPPATNAME);
                    100:     }
                    101:
                    102:     {
                    103:       char *v;
                    104:
                    105:       v = getenv ("SIMPLE_BACKUP_SUFFIX");
                    106:       if (v)
                    107:        simple_backup_suffix = v;
                    108:       else
                    109:        simple_backup_suffix = ORIGEXT;
                    110: #ifndef NODIR
                    111:       v = getenv ("VERSION_CONTROL");
                    112:       backup_type = get_version (v); /* OK to pass NULL. */
                    113: #endif
                    114:     }
                    115:
                    116:     /* parse switches */
                    117:     Argc = argc;
                    118:     Argv = argv;
                    119:     get_some_switches();
                    120:
                    121:     /* make sure we clean up /tmp in case of disaster */
                    122:     set_signals(0);
                    123:
                    124:     for (
                    125:        open_patch_file(filearg[1]);
                    126:        there_is_another_patch();
                    127:        reinitialize_almost_everything()
                    128:     ) {                                        /* for each patch in patch file */
                    129:
                    130:        if (outname == Nullch)
                    131:            outname = savestr(filearg[0]);
                    132:
                    133:        /* for ed script just up and do it and exit */
                    134:        if (diff_type == ED_DIFF) {
                    135:            do_ed_script();
                    136:            continue;
                    137:        }
                    138:
                    139:        /* initialize the patched file */
                    140:        if (!skip_rest_of_patch)
                    141:            init_output(TMPOUTNAME);
                    142:
                    143:        /* initialize reject file */
                    144:        init_reject(TMPREJNAME);
                    145:
                    146:        /* find out where all the lines are */
                    147:        if (!skip_rest_of_patch)
                    148:            scan_input(filearg[0]);
                    149:
                    150:        /* from here on, open no standard i/o files, because malloc */
                    151:        /* might misfire and we can't catch it easily */
                    152:
                    153:        /* apply each hunk of patch */
                    154:        hunk = 0;
                    155:        failed = 0;
                    156:        out_of_mem = FALSE;
                    157:        while (another_hunk()) {
                    158:            hunk++;
                    159:            fuzz = Nulline;
                    160:            mymaxfuzz = pch_context();
                    161:            if (maxfuzz < mymaxfuzz)
                    162:                mymaxfuzz = maxfuzz;
                    163:            if (!skip_rest_of_patch) {
                    164:                do {
                    165:                    where = locate_hunk(fuzz);
                    166:                    if (hunk == 1 && where == Nulline && !force) {
                    167:                                                /* dwim for reversed patch? */
                    168:                        if (!pch_swap()) {
                    169:                            if (fuzz == Nulline)
                    170:                                say1(
                    171: "Not enough memory to try swapped hunk!  Assuming unswapped.\n");
                    172:                            continue;
                    173:                        }
                    174:                        reverse = !reverse;
                    175:                        where = locate_hunk(fuzz);  /* try again */
                    176:                        if (where == Nulline) {     /* didn't find it swapped */
                    177:                            if (!pch_swap())         /* put it back to normal */
                    178:                                fatal1("lost hunk on alloc error!\n");
                    179:                            reverse = !reverse;
                    180:                        }
                    181:                        else if (noreverse) {
                    182:                            if (!pch_swap())         /* put it back to normal */
                    183:                                fatal1("lost hunk on alloc error!\n");
                    184:                            reverse = !reverse;
                    185:                            say1(
                    186: "Ignoring previously applied (or reversed) patch.\n");
                    187:                            skip_rest_of_patch = TRUE;
                    188:                        }
                    189:                        else if (batch) {
                    190:                            if (verbose)
                    191:                                say3(
                    192: "%seversed (or previously applied) patch detected!  %s -R.",
                    193:                                reverse ? "R" : "Unr",
                    194:                                reverse ? "Assuming" : "Ignoring");
                    195:                        }
                    196:                        else {
                    197:                            ask3(
                    198: "%seversed (or previously applied) patch detected!  %s -R? [y] ",
                    199:                                reverse ? "R" : "Unr",
                    200:                                reverse ? "Assume" : "Ignore");
                    201:                            if (*buf == 'n') {
                    202:                                ask1("Apply anyway? [n] ");
                    203:                                if (*buf != 'y')
                    204:                                    skip_rest_of_patch = TRUE;
                    205:                                where = Nulline;
                    206:                                reverse = !reverse;
                    207:                                if (!pch_swap())  /* put it back to normal */
                    208:                                    fatal1("lost hunk on alloc error!\n");
                    209:                            }
                    210:                        }
                    211:                    }
                    212:                } while (!skip_rest_of_patch && where == Nulline &&
                    213:                    ++fuzz <= mymaxfuzz);
                    214:
                    215:                if (skip_rest_of_patch) {               /* just got decided */
                    216:                    Fclose(ofp);
                    217:                    ofp = Nullfp;
                    218:                }
                    219:            }
                    220:
                    221:            newwhere = pch_newfirst() + last_offset;
                    222:            if (skip_rest_of_patch) {
                    223:                abort_hunk();
                    224:                failed++;
                    225:                if (verbose)
                    226:                    say3("Hunk #%d ignored at %ld.\n", hunk, newwhere);
                    227:            }
                    228:            else if (where == Nulline) {
                    229:                abort_hunk();
                    230:                failed++;
                    231:                if (verbose)
                    232:                    say3("Hunk #%d failed at %ld.\n", hunk, newwhere);
                    233:            }
                    234:            else {
                    235:                apply_hunk(where);
                    236:                if (verbose) {
                    237:                    say3("Hunk #%d succeeded at %ld", hunk, newwhere);
                    238:                    if (fuzz)
                    239:                        say2(" with fuzz %ld", fuzz);
                    240:                    if (last_offset)
                    241:                        say3(" (offset %ld line%s)",
                    242:                            last_offset, last_offset==1L?"":"s");
                    243:                    say1(".\n");
                    244:                }
                    245:            }
                    246:        }
                    247:
                    248:        if (out_of_mem && using_plan_a) {
                    249:            Argc = Argc_last;
                    250:            Argv = Argv_last;
                    251:            say1("\n\nRan out of memory using Plan A--trying again...\n\n");
                    252:            if (ofp)
                    253:                Fclose(ofp);
                    254:            ofp = Nullfp;
                    255:            if (rejfp)
                    256:                Fclose(rejfp);
                    257:            rejfp = Nullfp;
                    258:            continue;
                    259:        }
                    260:
                    261:        assert(hunk);
                    262:
                    263:        /* finish spewing out the new file */
                    264:        if (!skip_rest_of_patch)
                    265:            spew_output();
                    266:
                    267:        /* and put the output where desired */
                    268:        ignore_signals();
                    269:        if (!skip_rest_of_patch) {
                    270:            struct stat statbuf;
                    271:            char *realout = outname;
                    272:
                    273:            if (move_file(TMPOUTNAME, outname) < 0) {
                    274:                toutkeep = TRUE;
                    275:                realout = TMPOUTNAME;
                    276:                chmod(TMPOUTNAME, filemode);
                    277:            }
                    278:            else
                    279:                chmod(outname, filemode);
                    280:
                    281:            if (remove_empty_files && stat(realout, &statbuf) == 0
                    282:                && statbuf.st_size == 0) {
                    283:                if (verbose)
                    284:                    say2("Removing %s (empty after patching).\n", realout);
                    285:                while (unlink(realout) >= 0) ; /* while is for Eunice.  */
                    286:            }
                    287:        }
                    288:        Fclose(rejfp);
                    289:        rejfp = Nullfp;
                    290:        if (failed) {
                    291:            failtotal += failed;
                    292:            if (!*rejname) {
                    293:                Strcpy(rejname, outname);
                    294: #ifndef FLEXFILENAMES
                    295:                {
                    296:                    char *s = rindex(rejname,'/');
                    297:
                    298:                    if (!s)
                    299:                        s = rejname;
                    300:                    if (strlen(s) > 13)
                    301:                        if (s[12] == '.')       /* try to preserve difference */
                    302:                            s[12] = s[13];      /* between .h, .c, .y, etc. */
                    303:                        s[13] = '\0';
                    304:                }
                    305: #endif
                    306:                Strcat(rejname, REJEXT);
                    307:            }
                    308:            if (skip_rest_of_patch) {
                    309:                say4("%d out of %d hunks ignored--saving rejects to %s\n",
                    310:                    failed, hunk, rejname);
                    311:            }
                    312:            else {
                    313:                say4("%d out of %d hunks failed--saving rejects to %s\n",
                    314:                    failed, hunk, rejname);
                    315:            }
                    316:            if (move_file(TMPREJNAME, rejname) < 0)
                    317:                trejkeep = TRUE;
                    318:        }
                    319:        set_signals(1);
                    320:     }
                    321:     my_exit(failtotal);
                    322: }
                    323:
                    324: /* Prepare to find the next patch to do in the patch file. */
                    325:
                    326: void
                    327: reinitialize_almost_everything()
                    328: {
                    329:     re_patch();
                    330:     re_input();
                    331:
                    332:     input_lines = 0;
                    333:     last_frozen_line = 0;
                    334:
                    335:     filec = 0;
                    336:     if (filearg[0] != Nullch && !out_of_mem) {
                    337:        free(filearg[0]);
                    338:        filearg[0] = Nullch;
                    339:     }
                    340:
                    341:     if (outname != Nullch) {
                    342:        free(outname);
                    343:        outname = Nullch;
                    344:     }
                    345:
                    346:     last_offset = 0;
                    347:
                    348:     diff_type = 0;
                    349:
                    350:     if (revision != Nullch) {
                    351:        free(revision);
                    352:        revision = Nullch;
                    353:     }
                    354:
                    355:     reverse = reverse_flag_specified;
                    356:     skip_rest_of_patch = FALSE;
                    357:
                    358:     get_some_switches();
                    359:
                    360:     if (filec >= 2)
                    361:        fatal1("you may not change to a different patch file\n");
                    362: }
                    363:
                    364: static char *
                    365: nextarg()
                    366: {
                    367:     if (!--Argc)
                    368:        fatal2("missing argument after `%s'\n", *Argv);
                    369:     return *++Argv;
                    370: }
                    371:
1.2     ! niklas    372: /* Module for handling of long options.  */
        !           373:
        !           374: struct option {
        !           375:     char *long_opt;
        !           376:     char short_opt;
        !           377: };
        !           378:
        !           379: int
        !           380: optcmp(a, b)
        !           381:     struct option *a, *b;
        !           382: {
        !           383:     return strcmp (a->long_opt, b->long_opt);
        !           384: }
        !           385:
        !           386: /* Decode Long options beginning with "--" to their short equivalents.  */
        !           387:
        !           388: char
        !           389: decode_long_option(opt)
        !           390:     char *opt;
        !           391: {
        !           392:     /* This table must be sorted on the first field.  We also decode
        !           393:        unimplemented options as those will be handled later anyway.  */
        !           394:     static struct option options[] = {
        !           395:       { "batch",               't' },
        !           396:       { "check",               'C' },
        !           397:       { "context",             'c' },
        !           398:       { "debug",               'x' },
        !           399:       { "directory",           'd' },
        !           400:       { "ed",                  'e' },
        !           401:       { "force",               'f' },
        !           402:       { "forward",             'N' },
        !           403:       { "fuzz",                        'F' },
        !           404:       { "ifdef",               'D' },
        !           405:       { "ignore-whitespace",   'l' },
        !           406:       { "normal",              'n' },
        !           407:       { "output",              'o' },
        !           408:       { "prefix",              'B' },
        !           409:       { "quiet",               's' },
        !           410:       { "reject-file",         'r' },
        !           411:       { "remove-empty-files",  'E' },
        !           412:       { "reverse",             'R' },
        !           413:       { "silent",              's' },
        !           414:       { "skip",                        'S' },
        !           415:       { "strip",               'p' },
        !           416:       { "suffix",              'b' },
        !           417:       { "unified",             'u' },
        !           418:       { "version",             'v' },
        !           419:       { "version-control",     'V' },
        !           420:     };
        !           421:     struct option key, *found;
        !           422:
        !           423:     key.long_opt = opt;
        !           424:     found = (struct option *)bsearch(&key, options,
        !           425:                                     sizeof(options) / sizeof(options[0]),
        !           426:                                     sizeof(options[0]), optcmp);
        !           427:     return found ? found->short_opt : '\0';
        !           428: }
        !           429:
1.1       deraadt   430: /* Process switches and filenames up to next '+' or end of list. */
                    431:
                    432: void
                    433: get_some_switches()
                    434: {
                    435:     Reg1 char *s;
                    436:
                    437:     rejname[0] = '\0';
                    438:     Argc_last = Argc;
                    439:     Argv_last = Argv;
                    440:     if (!Argc)
                    441:        return;
                    442:     for (Argc--,Argv++; Argc; Argc--,Argv++) {
                    443:        s = Argv[0];
                    444:        if (strEQ(s, "+")) {
                    445:            return;                     /* + will be skipped by for loop */
                    446:        }
                    447:        if (*s != '-' || !s[1]) {
                    448:            if (filec == MAXFILEC)
                    449:                fatal1("too many file arguments\n");
                    450:            filearg[filec++] = savestr(s);
                    451:        }
                    452:        else {
1.2     ! niklas    453:            char opt;
        !           454:
        !           455:            if (*(s + 1) == '-') {
        !           456:                opt = decode_long_option(s + 2);
        !           457:                s += strlen(s) - 1;
        !           458:            }
        !           459:            else
        !           460:                opt = *++s;
        !           461:            switch (opt) {
1.1       deraadt   462:            case 'b':
                    463:                simple_backup_suffix = savestr(nextarg());
                    464:                break;
                    465:            case 'B':
                    466:                origprae = savestr(nextarg());
                    467:                break;
                    468:            case 'c':
                    469:                diff_type = CONTEXT_DIFF;
                    470:                break;
                    471:            case 'd':
                    472:                if (!*++s)
                    473:                    s = nextarg();
                    474:                if (chdir(s) < 0)
                    475:                    pfatal2("can't cd to %s", s);
                    476:                break;
                    477:            case 'D':
                    478:                do_defines = TRUE;
                    479:                if (!*++s)
                    480:                    s = nextarg();
                    481:                if (!isalpha(*s) && '_' != *s)
                    482:                    fatal1("argument to -D is not an identifier\n");
                    483:                Sprintf(if_defined, "#ifdef %s\n", s);
                    484:                Sprintf(not_defined, "#ifndef %s\n", s);
                    485:                Sprintf(end_defined, "#endif /* %s */\n", s);
                    486:                break;
                    487:            case 'e':
                    488:                diff_type = ED_DIFF;
                    489:                break;
                    490:            case 'E':
                    491:                remove_empty_files = TRUE;
                    492:                break;
                    493:            case 'f':
                    494:                force = TRUE;
                    495:                break;
                    496:            case 'F':
1.2     ! niklas    497:                if (!*++s)
        !           498:                    s = nextarg();
        !           499:                else if (*s == '=')
1.1       deraadt   500:                    s++;
                    501:                maxfuzz = atoi(s);
                    502:                break;
                    503:            case 'l':
                    504:                canonicalize = TRUE;
                    505:                break;
                    506:            case 'n':
                    507:                diff_type = NORMAL_DIFF;
                    508:                break;
                    509:            case 'N':
                    510:                noreverse = TRUE;
                    511:                break;
                    512:            case 'o':
                    513:                outname = savestr(nextarg());
                    514:                break;
                    515:            case 'p':
1.2     ! niklas    516:                if (!*++s)
        !           517:                    s = nextarg();
        !           518:                else if (*s == '=')
1.1       deraadt   519:                    s++;
                    520:                strippath = atoi(s);
                    521:                break;
                    522:            case 'r':
                    523:                Strcpy(rejname, nextarg());
                    524:                break;
                    525:            case 'R':
                    526:                reverse = TRUE;
                    527:                reverse_flag_specified = TRUE;
                    528:                break;
                    529:            case 's':
                    530:                verbose = FALSE;
                    531:                break;
                    532:            case 'S':
                    533:                skip_rest_of_patch = TRUE;
                    534:                break;
                    535:            case 't':
                    536:                batch = TRUE;
                    537:                break;
                    538:            case 'u':
                    539:                diff_type = UNI_DIFF;
                    540:                break;
                    541:            case 'v':
                    542:                version();
                    543:                break;
                    544:            case 'V':
                    545: #ifndef NODIR
                    546:                backup_type = get_version (nextarg ());
                    547: #endif
                    548:                break;
                    549: #ifdef DEBUGGING
                    550:            case 'x':
1.2     ! niklas    551:                if (!*++s)
        !           552:                    s = nextarg();
        !           553:                debug = atoi(s);
1.1       deraadt   554:                break;
                    555: #endif
                    556:            default:
                    557:                fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]);
                    558:                fprintf(stderr, "\
                    559: Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\
                    560: Options:\n\
                    561:        [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\
                    562:        [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\
                    563:        [-r rej-name] [-V {numbered,existing,simple}]\n");
                    564:                my_exit(1);
                    565:            }
                    566:        }
                    567:     }
                    568: }
                    569:
                    570: /* Attempt to find the right place to apply this hunk of patch. */
                    571:
                    572: LINENUM
                    573: locate_hunk(fuzz)
                    574: LINENUM fuzz;
                    575: {
                    576:     Reg1 LINENUM first_guess = pch_first() + last_offset;
                    577:     Reg2 LINENUM offset;
                    578:     LINENUM pat_lines = pch_ptrn_lines();
                    579:     Reg3 LINENUM max_pos_offset = input_lines - first_guess
                    580:                                - pat_lines + 1;
                    581:     Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1
                    582:                                + pch_context();
                    583:
                    584:     if (!pat_lines)                    /* null range matches always */
                    585:        return first_guess;
                    586:     if (max_neg_offset >= first_guess) /* do not try lines < 0 */
                    587:        max_neg_offset = first_guess - 1;
                    588:     if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz))
                    589:        return first_guess;
                    590:     for (offset = 1; ; offset++) {
                    591:        Reg5 bool check_after = (offset <= max_pos_offset);
                    592:        Reg6 bool check_before = (offset <= max_neg_offset);
                    593:
                    594:        if (check_after && patch_match(first_guess, offset, fuzz)) {
                    595: #ifdef DEBUGGING
                    596:            if (debug & 1)
                    597:                say3("Offset changing from %ld to %ld\n", last_offset, offset);
                    598: #endif
                    599:            last_offset = offset;
                    600:            return first_guess+offset;
                    601:        }
                    602:        else if (check_before && patch_match(first_guess, -offset, fuzz)) {
                    603: #ifdef DEBUGGING
                    604:            if (debug & 1)
                    605:                say3("Offset changing from %ld to %ld\n", last_offset, -offset);
                    606: #endif
                    607:            last_offset = -offset;
                    608:            return first_guess-offset;
                    609:        }
                    610:        else if (!check_before && !check_after)
                    611:            return Nulline;
                    612:     }
                    613: }
                    614:
                    615: /* We did not find the pattern, dump out the hunk so they can handle it. */
                    616:
                    617: void
                    618: abort_hunk()
                    619: {
                    620:     Reg1 LINENUM i;
                    621:     Reg2 LINENUM pat_end = pch_end();
                    622:     /* add in last_offset to guess the same as the previous successful hunk */
                    623:     LINENUM oldfirst = pch_first() + last_offset;
                    624:     LINENUM newfirst = pch_newfirst() + last_offset;
                    625:     LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
                    626:     LINENUM newlast = newfirst + pch_repl_lines() - 1;
                    627:     char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : "");
                    628:     char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----");
                    629:
                    630:     fprintf(rejfp, "***************\n");
                    631:     for (i=0; i<=pat_end; i++) {
                    632:        switch (pch_char(i)) {
                    633:        case '*':
                    634:            if (oldlast < oldfirst)
                    635:                fprintf(rejfp, "*** 0%s\n", stars);
                    636:            else if (oldlast == oldfirst)
                    637:                fprintf(rejfp, "*** %ld%s\n", oldfirst, stars);
                    638:            else
                    639:                fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars);
                    640:            break;
                    641:        case '=':
                    642:            if (newlast < newfirst)
                    643:                fprintf(rejfp, "--- 0%s\n", minuses);
                    644:            else if (newlast == newfirst)
                    645:                fprintf(rejfp, "--- %ld%s\n", newfirst, minuses);
                    646:            else
                    647:                fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses);
                    648:            break;
                    649:        case '\n':
                    650:            fprintf(rejfp, "%s", pfetch(i));
                    651:            break;
                    652:        case ' ': case '-': case '+': case '!':
                    653:            fprintf(rejfp, "%c %s", pch_char(i), pfetch(i));
                    654:            break;
                    655:        default:
                    656:            fatal1("fatal internal error in abort_hunk\n");
                    657:        }
                    658:     }
                    659: }
                    660:
                    661: /* We found where to apply it (we hope), so do it. */
                    662:
                    663: void
                    664: apply_hunk(where)
                    665: LINENUM where;
                    666: {
                    667:     Reg1 LINENUM old = 1;
                    668:     Reg2 LINENUM lastline = pch_ptrn_lines();
                    669:     Reg3 LINENUM new = lastline+1;
                    670: #define OUTSIDE 0
                    671: #define IN_IFNDEF 1
                    672: #define IN_IFDEF 2
                    673: #define IN_ELSE 3
                    674:     Reg4 int def_state = OUTSIDE;
                    675:     Reg5 bool R_do_defines = do_defines;
                    676:     Reg6 LINENUM pat_end = pch_end();
                    677:
                    678:     where--;
                    679:     while (pch_char(new) == '=' || pch_char(new) == '\n')
                    680:        new++;
                    681:
                    682:     while (old <= lastline) {
                    683:        if (pch_char(old) == '-') {
                    684:            copy_till(where + old - 1);
                    685:            if (R_do_defines) {
                    686:                if (def_state == OUTSIDE) {
                    687:                    fputs(not_defined, ofp);
                    688:                    def_state = IN_IFNDEF;
                    689:                }
                    690:                else if (def_state == IN_IFDEF) {
                    691:                    fputs(else_defined, ofp);
                    692:                    def_state = IN_ELSE;
                    693:                }
                    694:                fputs(pfetch(old), ofp);
                    695:            }
                    696:            last_frozen_line++;
                    697:            old++;
                    698:        }
                    699:        else if (new > pat_end) {
                    700:            break;
                    701:        }
                    702:        else if (pch_char(new) == '+') {
                    703:            copy_till(where + old - 1);
                    704:            if (R_do_defines) {
                    705:                if (def_state == IN_IFNDEF) {
                    706:                    fputs(else_defined, ofp);
                    707:                    def_state = IN_ELSE;
                    708:                }
                    709:                else if (def_state == OUTSIDE) {
                    710:                    fputs(if_defined, ofp);
                    711:                    def_state = IN_IFDEF;
                    712:                }
                    713:            }
                    714:            fputs(pfetch(new), ofp);
                    715:            new++;
                    716:        }
                    717:        else if (pch_char(new) != pch_char(old)) {
                    718:            say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n",
                    719:                pch_hunk_beg() + old,
                    720:                pch_hunk_beg() + new);
                    721: #ifdef DEBUGGING
                    722:            say3("oldchar = '%c', newchar = '%c'\n",
                    723:                pch_char(old), pch_char(new));
                    724: #endif
                    725:            my_exit(1);
                    726:        }
                    727:        else if (pch_char(new) == '!') {
                    728:            copy_till(where + old - 1);
                    729:            if (R_do_defines) {
                    730:               fputs(not_defined, ofp);
                    731:               def_state = IN_IFNDEF;
                    732:            }
                    733:            while (pch_char(old) == '!') {
                    734:                if (R_do_defines) {
                    735:                    fputs(pfetch(old), ofp);
                    736:                }
                    737:                last_frozen_line++;
                    738:                old++;
                    739:            }
                    740:            if (R_do_defines) {
                    741:                fputs(else_defined, ofp);
                    742:                def_state = IN_ELSE;
                    743:            }
                    744:            while (pch_char(new) == '!') {
                    745:                fputs(pfetch(new), ofp);
                    746:                new++;
                    747:            }
                    748:        }
                    749:        else {
                    750:            assert(pch_char(new) == ' ');
                    751:            old++;
                    752:            new++;
                    753:            if (R_do_defines && def_state != OUTSIDE) {
                    754:                fputs(end_defined, ofp);
                    755:                def_state = OUTSIDE;
                    756:            }
                    757:        }
                    758:     }
                    759:     if (new <= pat_end && pch_char(new) == '+') {
                    760:        copy_till(where + old - 1);
                    761:        if (R_do_defines) {
                    762:            if (def_state == OUTSIDE) {
                    763:                fputs(if_defined, ofp);
                    764:                def_state = IN_IFDEF;
                    765:            }
                    766:            else if (def_state == IN_IFNDEF) {
                    767:                fputs(else_defined, ofp);
                    768:                def_state = IN_ELSE;
                    769:            }
                    770:        }
                    771:        while (new <= pat_end && pch_char(new) == '+') {
                    772:            fputs(pfetch(new), ofp);
                    773:            new++;
                    774:        }
                    775:     }
                    776:     if (R_do_defines && def_state != OUTSIDE) {
                    777:        fputs(end_defined, ofp);
                    778:     }
                    779: }
                    780:
                    781: /* Open the new file. */
                    782:
                    783: void
                    784: init_output(name)
                    785: char *name;
                    786: {
                    787:     ofp = fopen(name, "w");
                    788:     if (ofp == Nullfp)
                    789:        pfatal2("can't create %s", name);
                    790: }
                    791:
                    792: /* Open a file to put hunks we can't locate. */
                    793:
                    794: void
                    795: init_reject(name)
                    796: char *name;
                    797: {
                    798:     rejfp = fopen(name, "w");
                    799:     if (rejfp == Nullfp)
                    800:        pfatal2("can't create %s", name);
                    801: }
                    802:
                    803: /* Copy input file to output, up to wherever hunk is to be applied. */
                    804:
                    805: void
                    806: copy_till(lastline)
                    807: Reg1 LINENUM lastline;
                    808: {
                    809:     Reg2 LINENUM R_last_frozen_line = last_frozen_line;
                    810:
                    811:     if (R_last_frozen_line > lastline)
                    812:        fatal1("misordered hunks! output would be garbled\n");
                    813:     while (R_last_frozen_line < lastline) {
                    814:        dump_line(++R_last_frozen_line);
                    815:     }
                    816:     last_frozen_line = R_last_frozen_line;
                    817: }
                    818:
                    819: /* Finish copying the input file to the output file. */
                    820:
                    821: void
                    822: spew_output()
                    823: {
                    824: #ifdef DEBUGGING
                    825:     if (debug & 256)
                    826:        say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line);
                    827: #endif
                    828:     if (input_lines)
                    829:        copy_till(input_lines);         /* dump remainder of file */
                    830:     Fclose(ofp);
                    831:     ofp = Nullfp;
                    832: }
                    833:
                    834: /* Copy one line from input to output. */
                    835:
                    836: void
                    837: dump_line(line)
                    838: LINENUM line;
                    839: {
                    840:     Reg1 char *s;
                    841:     Reg2 char R_newline = '\n';
                    842:
                    843:     /* Note: string is not null terminated. */
                    844:     for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ;
                    845: }
                    846:
                    847: /* Does the patch pattern match at line base+offset? */
                    848:
                    849: bool
                    850: patch_match(base, offset, fuzz)
                    851: LINENUM base;
                    852: LINENUM offset;
                    853: LINENUM fuzz;
                    854: {
                    855:     Reg1 LINENUM pline = 1 + fuzz;
                    856:     Reg2 LINENUM iline;
                    857:     Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz;
                    858:
                    859:     for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) {
                    860:        if (canonicalize) {
                    861:            if (!similar(ifetch(iline, (offset >= 0)),
                    862:                         pfetch(pline),
                    863:                         pch_line_len(pline) ))
                    864:                return FALSE;
                    865:        }
                    866:        else if (strnNE(ifetch(iline, (offset >= 0)),
                    867:                   pfetch(pline),
                    868:                   pch_line_len(pline) ))
                    869:            return FALSE;
                    870:     }
                    871:     return TRUE;
                    872: }
                    873:
                    874: /* Do two lines match with canonicalized white space? */
                    875:
                    876: bool
                    877: similar(a,b,len)
                    878: Reg1 char *a;
                    879: Reg2 char *b;
                    880: Reg3 int len;
                    881: {
                    882:     while (len) {
                    883:        if (isspace(*b)) {              /* whitespace (or \n) to match? */
                    884:            if (!isspace(*a))           /* no corresponding whitespace? */
                    885:                return FALSE;
                    886:            while (len && isspace(*b) && *b != '\n')
                    887:                b++,len--;              /* skip pattern whitespace */
                    888:            while (isspace(*a) && *a != '\n')
                    889:                a++;                    /* skip target whitespace */
                    890:            if (*a == '\n' || *b == '\n')
                    891:                return (*a == *b);      /* should end in sync */
                    892:        }
                    893:        else if (*a++ != *b++)          /* match non-whitespace chars */
                    894:            return FALSE;
                    895:        else
                    896:            len--;                      /* probably not necessary */
                    897:     }
                    898:     return TRUE;                       /* actually, this is not reached */
                    899:                                        /* since there is always a \n */
                    900: }
                    901:
                    902: /* Exit with cleanup. */
                    903:
                    904: void
                    905: my_exit(status)
                    906: int status;
                    907: {
                    908:     Unlink(TMPINNAME);
                    909:     if (!toutkeep) {
                    910:        Unlink(TMPOUTNAME);
                    911:     }
                    912:     if (!trejkeep) {
                    913:        Unlink(TMPREJNAME);
                    914:     }
                    915:     Unlink(TMPPATNAME);
                    916:     exit(status);
                    917: }