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

Annotation of src/usr.bin/sdiff/sdiff.c, Revision 1.1

1.1     ! tedu        1: /*     $Id: sdiff.c,v 1.106 2005/12/16 20:31:25 ray Exp $      */
        !             2:
        !             3: /*
        !             4:  * Written by Raymond Lai <ray@cyth.net>.
        !             5:  * Public domain.
        !             6:  */
        !             7:
        !             8: #include <sys/param.h>
        !             9: #include <sys/queue.h>
        !            10: #include <sys/types.h>
        !            11: #include <sys/wait.h>
        !            12:
        !            13: #include <assert.h>
        !            14: #include <ctype.h>
        !            15: #include <err.h>
        !            16: #include <getopt.h>
        !            17: #include <limits.h>
        !            18: #include <stdio.h>
        !            19: #include <stdlib.h>
        !            20: #include <string.h>
        !            21: #include <unistd.h>
        !            22: #include <util.h>
        !            23:
        !            24: #include "extern.h"
        !            25:
        !            26: #define WIDTH 130
        !            27: /*
        !            28:  * Each column must be at least one character wide, plus three
        !            29:  * characters between the columns (space, [<|>], space).
        !            30:  */
        !            31: #define WIDTH_MIN 5
        !            32:
        !            33: /* A single diff line. */
        !            34: struct diffline {
        !            35:        SIMPLEQ_ENTRY(diffline) diffentries;
        !            36:        const char      *left;
        !            37:        char             div;
        !            38:        const char      *right;
        !            39: };
        !            40:
        !            41: static void astrcat(char **, const char *);
        !            42: static void enqueue(const char *, const char, const char *);
        !            43: static void freediff(const struct diffline *);
        !            44: static void int_usage(void);
        !            45: static int parsecmd(FILE *, FILE *);
        !            46: static void printa(FILE *, size_t);
        !            47: static void printc(FILE *, size_t, FILE *, size_t);
        !            48: static void printcol(const char *, size_t *, const size_t);
        !            49: static void printd(FILE *, FILE *, size_t);
        !            50: static void println(const char *, const char, const char *);
        !            51: static void processq(void);
        !            52: static void prompt(const char *, const char *);
        !            53: static void undiff(char *);
        !            54: __dead static void usage(void);
        !            55: static char *xfgets(FILE *);
        !            56: static size_t xstrtonum(const char *);
        !            57:
        !            58: SIMPLEQ_HEAD(, diffline) diffhead = SIMPLEQ_HEAD_INITIALIZER(diffhead);
        !            59: size_t  line_width;    /* width of a line (two columns and divider) */
        !            60: size_t  width;         /* width of each column */
        !            61: size_t  file1ln, file2ln;      /* line number of file1 and file2 */
        !            62: int     Dflag;         /* debug - verify lots of things */
        !            63: int     lflag;         /* print only left column for identical lines */
        !            64: int     sflag;         /* skip identical lines */
        !            65:
        !            66: static struct option longopts[] = {
        !            67:        { "text",                       no_argument,            NULL,   'a' },
        !            68:        { "ignore-blank-lines",         no_argument,            NULL,   'B' },
        !            69:        { "ignore-space-change",        no_argument,            NULL,   'b' },
        !            70:        { "minimal",                    no_argument,            NULL,   'd' },
        !            71:        { "ignore-tab-expansion",       no_argument,            NULL,   'E' },
        !            72:        { "diff-program",               required_argument,      NULL,   'F' },
        !            73:        { "speed-large-files",          no_argument,            NULL,   'H' },
        !            74:        { "ignore-matching-lines",      required_argument,      NULL,   'I' },
        !            75:        { "left-column",                no_argument,            NULL,   'l' },
        !            76:        { "output",                     required_argument,      NULL,   'o' },
        !            77:        { "strip-trailing-cr",          no_argument,            NULL,   'S' },
        !            78:        { "suppress-common-lines",      no_argument,            NULL,   's' },
        !            79:        { "expand-tabs",                no_argument,            NULL,   't' },
        !            80:        { "ignore-all-space",           no_argument,            NULL,   'W' },
        !            81:        { "width",                      required_argument,      NULL,   'w' },
        !            82:        { NULL,                         0,                      NULL,    0  }
        !            83: };
        !            84:
        !            85: int
        !            86: main(int argc, char **argv)
        !            87: {
        !            88:        FILE *difffile, *origfile;
        !            89:        size_t argc_max, diffargc, wflag;
        !            90:        int ch, fd[2], status;
        !            91:        pid_t pid;
        !            92:        const char *cmd, **diffargv, *diffprog;
        !            93:
        !            94:        /* Initialize variables. */
        !            95:        Dflag = lflag = sflag = 0;
        !            96:        diffargc = 0;
        !            97:        diffprog = "diff";
        !            98:        outfile = NULL;
        !            99:        wflag = WIDTH;
        !           100:
        !           101:        /*
        !           102:         * Process diff flags.
        !           103:         */
        !           104:        /*
        !           105:         * Allocate memory for diff arguments and NULL.
        !           106:         * Each flag has at most one argument, so doubling argc gives an
        !           107:         * upper limit of how many diff args can be passed.  argv[0],
        !           108:         * file1, and file2 won't have arguments so doubling them will
        !           109:         * waste some memory; however we need an extra space for the
        !           110:         * NULL at the end, so it sort of works out.
        !           111:         */
        !           112:        argc_max = argc * 2;
        !           113:        if (!(diffargv = malloc(sizeof(char **) * argc_max)))
        !           114:                err(2, "out of memory");
        !           115:
        !           116:        /* Add first argument, the program name. */
        !           117:        diffargv[diffargc++] = diffprog;
        !           118:
        !           119:        while ((ch = getopt_long(argc, argv, "aBbDdEHI:ilo:stWw:",
        !           120:            longopts, NULL)) != -1) {
        !           121:                const char *errstr;
        !           122:
        !           123:                switch (ch) {
        !           124:                case 'a':
        !           125:                        diffargv[diffargc++] = "-a";
        !           126:                        break;
        !           127:                case 'B':
        !           128:                        diffargv[diffargc++] = "-B";
        !           129:                        break;
        !           130:                case 'b':
        !           131:                        diffargv[diffargc++] = "-b";
        !           132:                        break;
        !           133:                case 'D':
        !           134:                        Dflag = 1;
        !           135:                        break;
        !           136:                case 'd':
        !           137:                        diffargv[diffargc++] = "-d";
        !           138:                        break;
        !           139:                case 'E':
        !           140:                        diffargv[diffargc++] = "-E";
        !           141:                        break;
        !           142:                case 'F':
        !           143:                        diffprog = optarg;
        !           144:                        break;
        !           145:                case 'H':
        !           146:                        diffargv[diffargc++] = "-H";
        !           147:                        break;
        !           148:                case 'I':
        !           149:                        diffargv[diffargc++] = "-I";
        !           150:                        diffargv[diffargc++] = optarg;
        !           151:                        break;
        !           152:                case 'i':
        !           153:                        diffargv[diffargc++] = "-i";
        !           154:                        break;
        !           155:                case 'l':
        !           156:                        lflag = 1;
        !           157:                        break;
        !           158:                case 'o':
        !           159:                        if ((outfile = fopen(optarg, "w")) == NULL)
        !           160:                                err(2, "could not open: %s", optarg);
        !           161:                        break;
        !           162:                case 'S':
        !           163:                        diffargv[diffargc++] = "--strip-trailing-cr";
        !           164:                        break;
        !           165:                case 's':
        !           166:                        sflag = 1;
        !           167:                        break;
        !           168:                case 't':
        !           169:                        diffargv[diffargc++] = "-t";
        !           170:                        break;
        !           171:                case 'W':
        !           172:                        diffargv[diffargc++] = "-w";
        !           173:                        break;
        !           174:                case 'w':
        !           175:                        wflag = strtonum(optarg, WIDTH_MIN,
        !           176:                            (MIN(SIZE_T_MAX, LLONG_MAX)), &errstr);
        !           177:                        if (errstr)
        !           178:                                errx(2, "width is %s: %s", errstr, optarg);
        !           179:                        break;
        !           180:                default:
        !           181:                        usage();
        !           182:                        /* NOTREACHED */
        !           183:                }
        !           184:
        !           185:                /* Don't exceed buffer after adding file1, file2, and NULL. */
        !           186:                assert(diffargc + 3 <= argc_max);
        !           187:        }
        !           188:        argc -= optind;
        !           189:        argv += optind;
        !           190:
        !           191:        /* file1 */
        !           192:        diffargv[diffargc++] = argv[0];
        !           193:        /* file2 */
        !           194:        diffargv[diffargc++] = argv[1];
        !           195:        /* Add NULL to end of array to indicate end of array. */
        !           196:        diffargv[diffargc++] = NULL;
        !           197:
        !           198:        /* Subtract column divider and divide by two. */
        !           199:        width = (wflag - 3) / 2;
        !           200:        if (Dflag)
        !           201:                assert(width > 0);
        !           202:        /* Make sure line_width can fit in size_t. */
        !           203:        if (width > (SIZE_T_MAX - 3) / 2)
        !           204:                errx(2, "width is too large: %zu", width);
        !           205:        line_width = width * 2 + 3;
        !           206:
        !           207:        if (argc != 2) {
        !           208:                usage();
        !           209:                /* NOTREACHED */
        !           210:        }
        !           211:
        !           212:        if (pipe(fd))
        !           213:                err(2, "pipe");
        !           214:
        !           215:        switch(pid = fork()) {
        !           216:        case 0:
        !           217:                /* child */
        !           218:                /* We don't read from the pipe. */
        !           219:                if (close(fd[0]))
        !           220:                        err(2, "child could not close pipe input");
        !           221:                if (dup2(fd[1], STDOUT_FILENO) == -1)
        !           222:                        err(2, "child could not duplicate descriptor");
        !           223:                /* Free unused descriptor. */
        !           224:                if (close(fd[1]))
        !           225:                        err(2, "child could not close pipe output");
        !           226:
        !           227:                execvp(diffprog, (char *const *)diffargv);
        !           228:                err(2, "could not execute diff: %s", diffprog);
        !           229:        case -1:
        !           230:                err(2, "could not fork");
        !           231:        }
        !           232:
        !           233:        /* parent */
        !           234:        /* We don't write to the pipe. */
        !           235:        if (close(fd[1]))
        !           236:                err(2, "could not close pipe output");
        !           237:
        !           238:        /* Open pipe to diff command. */
        !           239:        if ((difffile = fdopen(fd[0], "r")) == NULL)
        !           240:                err(2, "could not open diff pipe");
        !           241:        /* If file1 was given as `-', open stdin. */
        !           242:        /* XXX - Does not work. */
        !           243:        if (strcmp(argv[0], "-") == 0)
        !           244:                origfile = stdin;
        !           245:        /* Otherwise, open as normal file. */
        !           246:        else if ((origfile = fopen(argv[0], "r")) == NULL)
        !           247:                err(2, "could not open file1: %s", argv[0]);
        !           248:        /* Line numbers start at one. */
        !           249:        file1ln = file2ln = 1;
        !           250:
        !           251:        /* Read and parse diff output. */
        !           252:        while (parsecmd(difffile, origfile) != EOF)
        !           253:                ;
        !           254:        if (fclose(difffile))
        !           255:                err(2, "could not close diff pipe");
        !           256:
        !           257:        /* Wait for diff to exit. */
        !           258:        if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) ||
        !           259:            WEXITSTATUS(status) >= 2)
        !           260:                err(2, "diff exited abnormally");
        !           261:
        !           262:        /* No more diffs, so print common lines. */
        !           263:        while ((cmd = xfgets(origfile)))
        !           264:                enqueue(cmd, ' ', lflag ? NULL : cmd);
        !           265:        if (fclose(origfile))
        !           266:                err(2, "could not close file1: %s", argv[0]);
        !           267:        /* Process unmodified lines. */
        !           268:        processq();
        !           269:
        !           270:        /* Return diff exit status. */
        !           271:        return (WEXITSTATUS(status));
        !           272: }
        !           273:
        !           274: /*
        !           275:  * Takes a string nptr and returns a numeric value.  The first character
        !           276:  * must be a digit.  Parsing ends when a non-numerical character is
        !           277:  * reached.
        !           278:  */
        !           279: static size_t
        !           280: xstrtonum(const char *nptr)
        !           281: {
        !           282:        size_t n;
        !           283:        const char *errstr;
        !           284:        char *copy, *ptr;
        !           285:
        !           286:        /* Make copy of numeric string. */
        !           287:        if ((copy = strdup(nptr)) == NULL)
        !           288:                err(2, "out of memory");
        !           289:
        !           290:        /* Look for first non-digit. */
        !           291:        for (ptr = copy; isdigit(*ptr); ++ptr)
        !           292:                ;
        !           293:
        !           294:        /* End string at first non-digit. */
        !           295:        if (*ptr != '\0')
        !           296:                *ptr = '\0';
        !           297:
        !           298:        /* Parse number. */
        !           299:        /* XXX - Is it safe to compare SIZE_T_MAX and LLONG_MAX? */
        !           300:        n = strtonum(copy, 0, MIN(SIZE_T_MAX, LLONG_MAX), &errstr);
        !           301:        if (errstr)
        !           302:                errx(2, "line number in diff is %s: %s", errstr, nptr);
        !           303:
        !           304:        /* Free copy of numeric string. */
        !           305:        free(copy);
        !           306:
        !           307:        return (n);
        !           308: }
        !           309:
        !           310: /*
        !           311:  * Prints an individual column (left or right), taking into account
        !           312:  * that tabs are variable-width.  Takes a string, the current column
        !           313:  * the cursor is on the screen, and the maximum value of the column.
        !           314:  * The column value is updated as we go along.
        !           315:  */
        !           316: static void
        !           317: printcol(const char *s, size_t *col, const size_t col_max)
        !           318: {
        !           319:        if (Dflag) {
        !           320:                assert(s);
        !           321:                assert(*col <= col_max);
        !           322:        }
        !           323:
        !           324:        for (; *s && *col < col_max; ++s) {
        !           325:                size_t new_col;
        !           326:
        !           327:                if (Dflag)
        !           328:                        assert(*s != '\n');
        !           329:
        !           330:                switch (*s) {
        !           331:                case '\t':
        !           332:                        /*
        !           333:                         * If rounding to next multiple of eight causes
        !           334:                         * an integer overflow, just return.
        !           335:                         */
        !           336:                        if (*col > SIZE_T_MAX - 8)
        !           337:                                return;
        !           338:
        !           339:                        /* Round to next multiple of eight. */
        !           340:                        new_col = (*col / 8 + 1) * 8;
        !           341:
        !           342:                        /*
        !           343:                         * If printing the tab goes past the column
        !           344:                         * width, don't print it and just quit.
        !           345:                         */
        !           346:                        if (new_col > col_max)
        !           347:                                return;
        !           348:                        *col = new_col;
        !           349:                        break;
        !           350:
        !           351:                default:
        !           352:                        ++(*col);
        !           353:                }
        !           354:
        !           355:                putchar(*s);
        !           356:        }
        !           357: }
        !           358:
        !           359: /*
        !           360:  * Prompts user to either choose between two strings or edit one, both,
        !           361:  * or neither.
        !           362:  */
        !           363: static void
        !           364: prompt(const char *s1, const char *s2)
        !           365: {
        !           366:        const char *cmd;
        !           367:
        !           368:        /* Print command prompt. */
        !           369:        putchar('%');
        !           370:
        !           371:        /* Get user input. */
        !           372:        for (; (cmd = xfgets(stdin)); free((char *)cmd)) {
        !           373:                const char *p;
        !           374:
        !           375:                /* Skip leading whitespace. */
        !           376:                for (p = cmd; isspace(*p); ++p)
        !           377:                        ;
        !           378:
        !           379:                switch (*p) {
        !           380:                case 'e':
        !           381:                        /* Skip `e'. */
        !           382:                        ++p;
        !           383:
        !           384:                        if (eparse(p, s1, s2) == -1)
        !           385:                                goto USAGE;
        !           386:                        break;
        !           387:
        !           388:                case 'l':
        !           389:                        /* Choose left column as-is. */
        !           390:                        if (s1 != NULL)
        !           391:                                fprintf(outfile, "%s\n", s1);
        !           392:
        !           393:                        /* End of command parsing. */
        !           394:                        break;
        !           395:
        !           396:                case 'q':
        !           397:                        goto QUIT;
        !           398:
        !           399:                case 'r':
        !           400:                        /* Choose right column as-is. */
        !           401:                        if (s2 != NULL)
        !           402:                                fprintf(outfile, "%s\n", s2);
        !           403:
        !           404:                        /* End of command parsing. */
        !           405:                        break;
        !           406:
        !           407:                case 's':
        !           408:                        sflag = 1;
        !           409:                        goto PROMPT;
        !           410:
        !           411:                case 'v':
        !           412:                        sflag = 0;
        !           413:                        /* FALLTHROUGH */
        !           414:
        !           415:                default:
        !           416:                        /* Interactive usage help. */
        !           417: USAGE:
        !           418:                        int_usage();
        !           419: PROMPT:
        !           420:                        putchar('%');
        !           421:
        !           422:                        /* Prompt user again. */
        !           423:                        continue;
        !           424:                }
        !           425:
        !           426:                free((char *)cmd);
        !           427:                return;
        !           428:        }
        !           429:
        !           430:        /*
        !           431:         * If there was no error, we received an EOF from stdin, so we
        !           432:         * should quit.
        !           433:         */
        !           434: QUIT:
        !           435:        if (fclose(outfile))
        !           436:                err(2, "could not close output file");
        !           437:        exit(0);
        !           438: }
        !           439:
        !           440: /*
        !           441:  * Takes two strings, separated by a column divider.  NULL strings are
        !           442:  * treated as empty columns.  If the divider is the ` ' character, the
        !           443:  * second column is not printed (-l flag).  In this case, the second
        !           444:  * string must be NULL.  When the second column is NULL, the divider
        !           445:  * does not print the trailing space following the divider character.
        !           446:  *
        !           447:  * Takes into account that tabs can take multiple columns.
        !           448:  */
        !           449: static void
        !           450: println(const char *s1, const char div, const char *s2)
        !           451: {
        !           452:        size_t col;
        !           453:
        !           454:        if (Dflag) {
        !           455:                /* These are the only legal column dividers. */
        !           456:                assert(div == '<' || div == '|' || div == '>' || div == ' ');
        !           457:                /* These are the only valid combinations. */
        !           458:                assert((s1 != NULL && div == '<' && s2 == NULL) || div != '<');
        !           459:                assert((s1 == NULL && div == '>' && s2 != NULL) || div != '>');
        !           460:                assert((s1 != NULL && div == '|' && s2 != NULL && s2 != s1) ||
        !           461:                    div != '|');
        !           462:                assert((s1 != NULL && div == ' ' && (s2 == s1 || s2 == NULL)) ||
        !           463:                    div != ' ');
        !           464:        }
        !           465:
        !           466:        /* Print first column.  Skips if s1 == NULL. */
        !           467:        col = 0;
        !           468:        if (s1) {
        !           469:                /* Skip angle bracket and space. */
        !           470:                printcol(s1, &col, width);
        !           471:
        !           472:                /* We should never exceed the width. */
        !           473:                if (Dflag)
        !           474:                        assert(col <= width);
        !           475:        }
        !           476:
        !           477:        /* Only print left column. */
        !           478:        if (div == ' ' && !s2) {
        !           479:                putchar('\n');
        !           480:                return;
        !           481:        }
        !           482:
        !           483:        /* Otherwise, we pad this column up to width. */
        !           484:        for (; col < width; ++col)
        !           485:                putchar(' ');
        !           486:
        !           487:        /*
        !           488:         * Print column divider.  If there is no second column, we don't
        !           489:         * need to add the space for padding.
        !           490:         */
        !           491:        if (!s2) {
        !           492:                printf(" %c\n", div);
        !           493:                return;
        !           494:        }
        !           495:        printf(" %c ", div);
        !           496:        col += 3;
        !           497:
        !           498:        /* Skip angle bracket and space. */
        !           499:        printcol(s2, &col, line_width);
        !           500:
        !           501:        /* We should never exceed the line width. */
        !           502:        if (Dflag)
        !           503:                assert(col <= line_width);
        !           504:
        !           505:        putchar('\n');
        !           506: }
        !           507:
        !           508: /*
        !           509:  * Reads a line from file and returns as a string.  If EOF is reached,
        !           510:  * NULL is returned.  The returned string must be freed afterwards.
        !           511:  */
        !           512: static char *
        !           513: xfgets(FILE *file)
        !           514: {
        !           515:        const char delim[3] = {'\0', '\0', '\0'};
        !           516:        char *s;
        !           517:
        !           518:        if (Dflag)
        !           519:                assert(file);
        !           520:
        !           521:        /* XXX - Is this necessary? */
        !           522:        clearerr(file);
        !           523:
        !           524:        if (!(s = fparseln(file, NULL, NULL, delim, 0)) &&
        !           525:            ferror(file))
        !           526:                err(2, "error reading file");
        !           527:
        !           528:        if (!s) {
        !           529:                /* NULL from fparseln() should mean EOF. */
        !           530:                if (Dflag)
        !           531:                        assert(feof(file));
        !           532:
        !           533:                return (NULL);
        !           534:        }
        !           535:
        !           536:        return (s);
        !           537: }
        !           538:
        !           539: /*
        !           540:  * Parse ed commands from diff and print lines from difffile
        !           541:  * (lines to add or change) or origfile (lines to change or delete).
        !           542:  * Returns EOF or not.
        !           543:  */
        !           544: static int
        !           545: parsecmd(FILE *difffile, FILE *origfile)
        !           546: {
        !           547:        size_t file1start, file1end, file2start, file2end;
        !           548:        /* ed command line and pointer to characters in line */
        !           549:        const char *line, *p;
        !           550:        char cmd;
        !           551:
        !           552:        /* Read ed command. */
        !           553:        if (!(line = xfgets(difffile)))
        !           554:                return (EOF);
        !           555:
        !           556:        file1start = xstrtonum(line);
        !           557:        p = line;
        !           558:        /* Go to character after line number. */
        !           559:        while (isdigit(*p))
        !           560:                ++p;
        !           561:
        !           562:        /* A range is specified for file1. */
        !           563:        if (*p == ',') {
        !           564:                /* Go to range end. */
        !           565:                ++p;
        !           566:
        !           567:                file1end = xstrtonum(p);
        !           568:                if (file1start > file1end)
        !           569:                        errx(2, "invalid line range in file1: %s", line);
        !           570:
        !           571:                /* Go to character after file2end. */
        !           572:                while (isdigit(*p))
        !           573:                        ++p;
        !           574:        } else
        !           575:                file1end = file1start;
        !           576:
        !           577:        /* This character should be the ed command now. */
        !           578:        cmd = *p;
        !           579:
        !           580:        /* Check that cmd is valid. */
        !           581:        if (!(cmd == 'a' || cmd == 'c' || cmd == 'd'))
        !           582:                errx(2, "ed command not recognized: %c: %s", cmd, line);
        !           583:
        !           584:        /* Go to file2 line range. */
        !           585:        ++p;
        !           586:
        !           587:        file2start = xstrtonum(p);
        !           588:        /* Go to character after line number. */
        !           589:        while (isdigit(*p))
        !           590:                ++p;
        !           591:
        !           592:        /*
        !           593:         * There should either be a comma signifying a second line
        !           594:         * number or the line should just end here.
        !           595:         */
        !           596:        if (!(*p == ',' || *p == '\0'))
        !           597:                errx(2, "invalid line range in file2: %c: %s", *p, line);
        !           598:
        !           599:        if (*p == ',') {
        !           600:                ++p;
        !           601:
        !           602:                file2end = xstrtonum(p);
        !           603:                if (file2start >= file2end)
        !           604:                        errx(2, "invalid line range in file2: %s", line);
        !           605:        } else
        !           606:                file2end = file2start;
        !           607:
        !           608:        /* Appends happen _after_ stated line. */
        !           609:        if (cmd == 'a') {
        !           610:                if (file1start != file1end)
        !           611:                        errx(2, "append cannot have a file1 range: %s",
        !           612:                            line);
        !           613:                if (file1start == SIZE_T_MAX)
        !           614:                        errx(2, "file1 line range too high: %s", line);
        !           615:                file1start = ++file1end;
        !           616:        }
        !           617:        /*
        !           618:         * I'm not sure what the deal is with the line numbers for
        !           619:         * deletes, though.
        !           620:         */
        !           621:        else if (cmd == 'd') {
        !           622:                if (file2start != file2end)
        !           623:                        errx(2, "delete cannot have a file2 range: %s",
        !           624:                            line);
        !           625:                if (file2start == SIZE_T_MAX)
        !           626:                        errx(2, "file2 line range too high: %s", line);
        !           627:                file2start = ++file2end;
        !           628:        }
        !           629:
        !           630:        /* Skip unmodified lines. */
        !           631:        for (; file1ln < file1start; ++file1ln, ++file2ln) {
        !           632:                const char *line;
        !           633:
        !           634:                if (!(line = xfgets(origfile)))
        !           635:                        errx(2, "file1 shorter than expected");
        !           636:
        !           637:                /* If the -l flag was specified, print only left column. */
        !           638:                enqueue(line, ' ', lflag ? NULL : line);
        !           639:        }
        !           640:        /* Process unmodified lines. */
        !           641:        processq();
        !           642:
        !           643:        if (Dflag) {
        !           644:                /*
        !           645:                 * We are now at the line where adds, changes,
        !           646:                 * or deletions occur.
        !           647:                 */
        !           648:                assert(file1start == file1ln);
        !           649:                assert(file2start == file2ln);
        !           650:                assert(file1start <= file1end);
        !           651:                assert(file2start <= file2end);
        !           652:        }
        !           653:        switch (cmd) {
        !           654:        case 'a':
        !           655:                /* A range cannot be specified for file1. */
        !           656:                if (Dflag)
        !           657:                        assert(file1start == file1end);
        !           658:
        !           659:                printa(difffile, file2end);
        !           660:                break;
        !           661:
        !           662:        case 'c':
        !           663:                printc(origfile, file1end, difffile, file2end);
        !           664:                break;
        !           665:
        !           666:        case 'd':
        !           667:                /* A range cannot be specified for file2. */
        !           668:                if (Dflag)
        !           669:                        assert(file2start == file2end);
        !           670:
        !           671:                printd(origfile, difffile, file1end);
        !           672:                break;
        !           673:
        !           674:        default:
        !           675:                errx(2, "invalid diff command: %c: %s", cmd, line);
        !           676:        }
        !           677:
        !           678:        return (!EOF);
        !           679: }
        !           680:
        !           681: /*
        !           682:  * Queues up a diff line.
        !           683:  */
        !           684: static void
        !           685: enqueue(const char *left, const char div, const char *right)
        !           686: {
        !           687:        struct diffline *diffp;
        !           688:
        !           689:        if (!(diffp = malloc(sizeof(struct diffline))))
        !           690:                err(2, "could not allocate memory");
        !           691:        diffp->left = left;
        !           692:        diffp->div = div;
        !           693:        diffp->right = right;
        !           694:        SIMPLEQ_INSERT_TAIL(&diffhead, diffp, diffentries);
        !           695: }
        !           696:
        !           697: /*
        !           698:  * Free a diffline structure and its elements.
        !           699:  */
        !           700: static void
        !           701: freediff(const struct diffline *diffp)
        !           702: {
        !           703:        if (Dflag)
        !           704:                assert(diffp);
        !           705:
        !           706:        if (diffp->left)
        !           707:                free((char *)diffp->left);
        !           708:        /*
        !           709:         * Free right string only if it is different than left.
        !           710:         * The strings are the same when the lines are identical.
        !           711:         */
        !           712:        if (diffp->right && diffp->right != diffp->left)
        !           713:                free((char *)diffp->right);
        !           714: }
        !           715:
        !           716: /*
        !           717:  * Append second string into first.  Repeated appends to the same string
        !           718:  * are cached, making this an O(n) function, where n = strlen(append).
        !           719:  */
        !           720: static void
        !           721: astrcat(char **s, const char *append)
        !           722: {
        !           723:        /* Length of string in previous run. */
        !           724:        static size_t offset = 0;
        !           725:        size_t copied, newlen;
        !           726:        /*
        !           727:         * String from previous run.  Compared to *s to see if we are
        !           728:         * dealing with the same string.  If so, we can use offset.
        !           729:         */
        !           730:        const static char *oldstr = NULL;
        !           731:        char *newstr;
        !           732:
        !           733:        if (Dflag)
        !           734:                assert(append);
        !           735:
        !           736:        /*
        !           737:         * First string is NULL, so just copy append.
        !           738:         */
        !           739:        if (!*s) {
        !           740:                if (!(*s = strdup(append)))
        !           741:                        err(2, "could not allocate memory");
        !           742:
        !           743:                /* Keep track of string. */
        !           744:                offset = strlen(*s);
        !           745:                oldstr = *s;
        !           746:
        !           747:                return;
        !           748:        }
        !           749:
        !           750:        /*
        !           751:         * *s is a string so concatenate.
        !           752:         */
        !           753:
        !           754:        /* Did we process the same string in the last run? */
        !           755:        /*
        !           756:         * If this is a different string from the one we just processed
        !           757:         * cache new string.
        !           758:         */
        !           759:        if (oldstr != *s) {
        !           760:                offset = strlen(*s);
        !           761:                oldstr = *s;
        !           762:        }
        !           763:        /* This should always be the end of the string. */
        !           764:        if (Dflag) {
        !           765:                assert(*(*s + offset) == '\0');
        !           766:                assert(strlen(*s) == offset);
        !           767:        }
        !           768:
        !           769:        /* Length = strlen(*s) + \n + strlen(append) + '\0'. */
        !           770:        newlen = offset + 1 + strlen(append) + 1;
        !           771:
        !           772:        /* Resize *s to fit new string. */
        !           773:        newstr = realloc(*s, newlen);
        !           774:        if (newstr == NULL)
        !           775:                err(2, "could not allocate memory");
        !           776:        *s = newstr;
        !           777:
        !           778:        /* Concatenate. */
        !           779:        strlcpy(*s + offset, "\n", newlen - offset);
        !           780:        copied = strlcat(*s + offset, append, newlen - offset);
        !           781:
        !           782:        /*
        !           783:         * We should have copied exactly newlen characters, including
        !           784:         * the terminating NUL.  `copied' includes the \n character.
        !           785:         */
        !           786:        if (Dflag)
        !           787:                assert(offset + copied + sizeof((char)'\0') == newlen);
        !           788:
        !           789:        /* Store generated string's values. */
        !           790:        offset = newlen - sizeof((char)'\0');
        !           791:        oldstr = *s;
        !           792: }
        !           793:
        !           794: /*
        !           795:  * Process diff set queue, printing, prompting, and saving each diff
        !           796:  * line stored in queue.
        !           797:  */
        !           798: static void
        !           799: processq(void)
        !           800: {
        !           801:        struct diffline *diffp;
        !           802:        char div, *left, *right;
        !           803:
        !           804:        /* Don't process empty queue. */
        !           805:        if (SIMPLEQ_EMPTY(&diffhead))
        !           806:                return;
        !           807:
        !           808:        div = '\0';
        !           809:        left = NULL;
        !           810:        right = NULL;
        !           811:        /*
        !           812:         * Go through set of diffs, concatenating each line in left or
        !           813:         * right column into two long strings, `left' and `right'.
        !           814:         */
        !           815:        SIMPLEQ_FOREACH(diffp, &diffhead, diffentries) {
        !           816:                /*
        !           817:                 * Make sure that div is consistent throughout set.
        !           818:                 * If div is set, compare to next entry's div.  They
        !           819:                 * should be the same.  If div is not set, then store
        !           820:                 * this as this set's div.
        !           821:                 */
        !           822:                if (Dflag)
        !           823:                        assert(div == diffp->div || !div);
        !           824:                if (!div)
        !           825:                        div = diffp->div;
        !           826:
        !           827:                /*
        !           828:                 * If the -s flag was not given or the lines are not
        !           829:                 * identical then print columns.
        !           830:                 */
        !           831:                if (!sflag || diffp->div != ' ')
        !           832:                        println(diffp->left, diffp->div, diffp->right);
        !           833:
        !           834:                /* Append new lines to diff set. */
        !           835:                if (diffp->left)
        !           836:                        astrcat(&left, diffp->left);
        !           837:                if (diffp->right)
        !           838:                        astrcat(&right, diffp->right);
        !           839:        }
        !           840:
        !           841:        /* div should no longer be NUL. */
        !           842:        if (Dflag)
        !           843:                assert(div);
        !           844:
        !           845:        /* Empty queue and free each diff line and its elements. */
        !           846:        while (!SIMPLEQ_EMPTY(&diffhead)) {
        !           847:                diffp = SIMPLEQ_FIRST(&diffhead);
        !           848:                freediff(diffp);
        !           849:                SIMPLEQ_REMOVE_HEAD(&diffhead, diffentries);
        !           850:                free(diffp);
        !           851:        }
        !           852:
        !           853:        /* Write to outfile, prompting user if lines are different. */
        !           854:        if (outfile) {
        !           855:                if (div == ' ')
        !           856:                        fprintf(outfile, "%s\n", left);
        !           857:                else
        !           858:                        prompt(left, right);
        !           859:        }
        !           860:
        !           861:        /* Free left and right. */
        !           862:        if (left)
        !           863:                free(left);
        !           864:        if (right)
        !           865:                free(right);
        !           866: }
        !           867:
        !           868: /*
        !           869:  * Remove angle bracket in front of diff line.
        !           870:  */
        !           871: static void
        !           872: undiff(char *s)
        !           873: {
        !           874:        size_t len;
        !           875:
        !           876:        if (Dflag) {
        !           877:                assert(s);
        !           878:                assert(*s == '<' || *s == '>');
        !           879:                assert(*(s + 1) == ' ');
        !           880:        }
        !           881:
        !           882:        /* Remove angle bracket and space but keep the NUL. */
        !           883:        len = strlen(s) - 2 + 1;
        !           884:        /* Copy at least the NUL. */
        !           885:        if (Dflag)
        !           886:                assert(len > 0);
        !           887:        /* Move everything two characters over. */
        !           888:        memmove(s, s + 2, len);
        !           889: }
        !           890:
        !           891: /*
        !           892:  * Print lines following an (a)ppend command.
        !           893:  */
        !           894: static void
        !           895: printa(FILE *file, size_t line2)
        !           896: {
        !           897:        char *line;
        !           898:
        !           899:        if (Dflag) {
        !           900:                assert(file);
        !           901:                assert(file2ln <= line2);
        !           902:        }
        !           903:
        !           904:        for (; file2ln <= line2; ++file2ln) {
        !           905:                if (!(line = xfgets(file)))
        !           906:                        errx(2, "append ended early");
        !           907:                undiff(line);
        !           908:                enqueue(NULL, '>', line);
        !           909:        }
        !           910:
        !           911:        processq();
        !           912: }
        !           913:
        !           914: /*
        !           915:  * Print lines following a (c)hange command, from file1ln to file1end
        !           916:  * and from file2ln to file2end.
        !           917:  */
        !           918: static void
        !           919: printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end)
        !           920: {
        !           921:        struct fileline {
        !           922:                SIMPLEQ_ENTRY(fileline) fileentries;
        !           923:                const char      *line;
        !           924:        };
        !           925:        SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead);
        !           926:        char *line;
        !           927:
        !           928:        if (Dflag) {
        !           929:                assert(file1);
        !           930:                assert(file2);
        !           931:                assert(file1ln <= file1end);
        !           932:                assert(file2ln <= file2end);
        !           933:                /* Change diff sets always start out with an empty queue. */
        !           934:                assert(SIMPLEQ_EMPTY(&diffhead));
        !           935:        }
        !           936:
        !           937:        /* Read lines to be deleted. */
        !           938:        for (; file1ln <= file1end; ++file1ln) {
        !           939:                struct fileline *linep;
        !           940:                const char *line1, *line2;
        !           941:
        !           942:                /* Read lines from both. */
        !           943:                if (!(line1 = xfgets(file1)))
        !           944:                        errx(2, "error reading file1 in delete in change");
        !           945:                if (!(line2 = xfgets(file2)))
        !           946:                        errx(2, "error reading diff in delete in change");
        !           947:
        !           948:                /* Verify lines. */
        !           949:                if (Dflag && strncmp("< ", line2, 2) != 0)
        !           950:                        errx(2, "invalid del/change diff: %s", line2);
        !           951:                if (Dflag && strcmp(line1, line2 + 2))
        !           952:                        warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s",
        !           953:                            line2, line1);
        !           954:
        !           955:                /* Unused now. */
        !           956:                free((char *)line2);
        !           957:
        !           958:                /* Add to delete queue. */
        !           959:                if (!(linep = malloc(sizeof(struct fileline))))
        !           960:                        err(2, "could not allocate memory");
        !           961:                linep->line = line1;
        !           962:                SIMPLEQ_INSERT_TAIL(&delqhead, linep, fileentries);
        !           963:        }
        !           964:
        !           965:        /* There should be a divider here. */
        !           966:        if (!(line = xfgets(file2)))
        !           967:                errx(2, "error reading diff in change: expected divider");
        !           968:        if (Dflag && strcmp("---", line))
        !           969:                errx(2, "divider expected: %s", line);
        !           970:        free(line);
        !           971:
        !           972: #define getaddln(add) do {                                     \
        !           973:        /* Read diff for line. */                               \
        !           974:        if (!((add) = xfgets(file2)))                           \
        !           975:                errx(2, "error reading add in change");         \
        !           976:        /* Verify line. */                                      \
        !           977:        if (Dflag && strncmp("> ", (add), 2))                   \
        !           978:                errx(2, "invalid add/change diff: %s", (add));  \
        !           979:        /* Remove ``> ''. */                                    \
        !           980:        undiff(add);                                            \
        !           981: } while (0)
        !           982:        /* Process changed lines.. */
        !           983:        for (; !SIMPLEQ_EMPTY(&delqhead) && file2ln <= file2end;
        !           984:            ++file2ln) {
        !           985:                struct fileline *del;
        !           986:                char *add;
        !           987:
        !           988:                /* Get add line. */
        !           989:                getaddln(add);
        !           990:
        !           991:                del = SIMPLEQ_FIRST(&delqhead);
        !           992:                enqueue(del->line, '|', add);
        !           993:                SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
        !           994:                /*
        !           995:                 * Free fileline structure but not its elements since
        !           996:                 * they are queued up.
        !           997:                 */
        !           998:                free(del);
        !           999:        }
        !          1000:        processq();
        !          1001:
        !          1002:        /* Process remaining lines to add. */
        !          1003:        for (; file2ln <= file2end; ++file2ln) {
        !          1004:                char *add;
        !          1005:
        !          1006:                /* Get add line. */
        !          1007:                getaddln(add);
        !          1008:
        !          1009:                enqueue(NULL, '>', add);
        !          1010:        }
        !          1011:        processq();
        !          1012: #undef getaddln
        !          1013:
        !          1014:        /* Process remaining lines to delete. */
        !          1015:        while (!SIMPLEQ_EMPTY(&delqhead)) {
        !          1016:                struct fileline *filep;
        !          1017:
        !          1018:                filep = SIMPLEQ_FIRST(&delqhead);
        !          1019:                enqueue(filep->line, '<', NULL);
        !          1020:                SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
        !          1021:                free(filep);
        !          1022:        }
        !          1023:        processq();
        !          1024: }
        !          1025:
        !          1026: /*
        !          1027:  * Print deleted lines from file, from file1ln to file1end.
        !          1028:  */
        !          1029: static void
        !          1030: printd(FILE *file1, FILE *file2, size_t file1end)
        !          1031: {
        !          1032:        const char *line1, *line2;
        !          1033:
        !          1034:        if (Dflag) {
        !          1035:                assert(file1);
        !          1036:                assert(file2);
        !          1037:                assert(file1ln <= file1end);
        !          1038:                /* Delete diff sets always start with an empty queue. */
        !          1039:                assert(SIMPLEQ_EMPTY(&diffhead));
        !          1040:        }
        !          1041:
        !          1042:        /* Print out lines file1ln to line2. */
        !          1043:        for (; file1ln <= file1end; ++file1ln) {
        !          1044:                /* XXX - Why can't this handle stdin? */
        !          1045:                if (!(line1 = xfgets(file1)))
        !          1046:                        errx(2, "file1 ended early in delete");
        !          1047:                if (!(line2 = xfgets(file2)))
        !          1048:                        errx(2, "diff ended early in delete");
        !          1049:                /* Compare delete line from diff to file1. */
        !          1050:                if (Dflag && strcmp(line1, line2 + 2) != 0)
        !          1051:                        warnx("diff differs from file1:\ndiff:\n%s\nfile:\n%s",
        !          1052:                            line2, line1);
        !          1053:                free((char *)line2);
        !          1054:                enqueue(line1, '<', NULL);
        !          1055:        }
        !          1056:        processq();
        !          1057: }
        !          1058:
        !          1059: /*
        !          1060:  * Interactive mode usage.
        !          1061:  */
        !          1062: static void
        !          1063: int_usage(void)
        !          1064: {
        !          1065:        puts("e:\tedit blank diff\n"
        !          1066:            "eb:\tedit both diffs concatenated\n"
        !          1067:            "el:\tedit left diff\n"
        !          1068:            "er:\tedit right diff\n"
        !          1069:            "l:\tchoose left diff\n"
        !          1070:            "r:\tchoose right diff\n"
        !          1071:            "s:\tsilent mode--don't print identical lines\n"
        !          1072:            "v:\tverbose mode--print identical lines\n"
        !          1073:            "q:\tquit");
        !          1074: }
        !          1075:
        !          1076: static void
        !          1077: usage(void)
        !          1078: {
        !          1079:        extern char *__progname;
        !          1080:
        !          1081:        fprintf(stderr,
        !          1082:            "usage: %s [-abDdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n",
        !          1083:            __progname);
        !          1084:        exit(2);
        !          1085: }