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

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