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

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