[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.15

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