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

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