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

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

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

CVSweb <webmaster@jp.NetBSD.org>