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

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