1*3791d282Srillig /* $NetBSD: sdiff.c,v 1.3 2021/08/27 17:38:57 rillig Exp $ */
25533f1ebSrmind /* $OpenBSD: sdiff.c,v 1.20 2006/09/19 05:52:23 otto Exp $ */
35533f1ebSrmind
45533f1ebSrmind /*
55533f1ebSrmind * Written by Raymond Lai <ray@cyth.net>.
65533f1ebSrmind * Public domain.
75533f1ebSrmind */
85533f1ebSrmind
95533f1ebSrmind #include <sys/param.h>
105533f1ebSrmind #include <sys/queue.h>
115533f1ebSrmind #include <sys/stat.h>
125533f1ebSrmind #include <sys/types.h>
135533f1ebSrmind #include <sys/wait.h>
145533f1ebSrmind
155533f1ebSrmind #include <ctype.h>
165533f1ebSrmind #include <err.h>
175533f1ebSrmind #include <errno.h>
185533f1ebSrmind #include <fcntl.h>
195533f1ebSrmind #include <getopt.h>
205533f1ebSrmind #include <limits.h>
215533f1ebSrmind #include <paths.h>
225533f1ebSrmind #include <stdio.h>
235533f1ebSrmind #include <stdlib.h>
245533f1ebSrmind #include <string.h>
255533f1ebSrmind #include <unistd.h>
265533f1ebSrmind #include <util.h>
275533f1ebSrmind
285533f1ebSrmind #include "common.h"
295533f1ebSrmind #include "extern.h"
305533f1ebSrmind
315533f1ebSrmind #define WIDTH 130
325533f1ebSrmind /*
335533f1ebSrmind * Each column must be at least one character wide, plus three
345533f1ebSrmind * characters between the columns (space, [<|>], space).
355533f1ebSrmind */
365533f1ebSrmind #define WIDTH_MIN 5
375533f1ebSrmind
385533f1ebSrmind /* A single diff line. */
395533f1ebSrmind struct diffline {
405533f1ebSrmind SIMPLEQ_ENTRY(diffline) diffentries;
415533f1ebSrmind char *left;
425533f1ebSrmind char div;
435533f1ebSrmind char *right;
445533f1ebSrmind };
455533f1ebSrmind
465533f1ebSrmind static void astrcat(char **, const char *);
475533f1ebSrmind static void enqueue(char *, char, char *);
485533f1ebSrmind static char *mktmpcpy(const char *);
495533f1ebSrmind static void freediff(struct diffline *);
505533f1ebSrmind static void int_usage(void);
515533f1ebSrmind static int parsecmd(FILE *, FILE *, FILE *);
525533f1ebSrmind static void printa(FILE *, size_t);
535533f1ebSrmind static void printc(FILE *, size_t, FILE *, size_t);
545533f1ebSrmind static void printcol(const char *, size_t *, const size_t);
555533f1ebSrmind static void printd(FILE *, size_t);
565533f1ebSrmind static void println(const char *, const char, const char *);
575533f1ebSrmind static void processq(void);
585533f1ebSrmind static void prompt(const char *, const char *);
595533f1ebSrmind __dead static void usage(void);
605533f1ebSrmind static char *xfgets(FILE *);
615533f1ebSrmind
625533f1ebSrmind SIMPLEQ_HEAD(, diffline) diffhead = SIMPLEQ_HEAD_INITIALIZER(diffhead);
635533f1ebSrmind size_t line_width; /* width of a line (two columns and divider) */
645533f1ebSrmind size_t width; /* width of each column */
655533f1ebSrmind size_t file1ln, file2ln; /* line number of file1 and file2 */
665533f1ebSrmind int Iflag = 0; /* ignore sets matching regexp */
675533f1ebSrmind int lflag; /* print only left column for identical lines */
685533f1ebSrmind int sflag; /* skip identical lines */
695533f1ebSrmind FILE *outfile; /* file to save changes to */
705533f1ebSrmind const char *tmpdir; /* TMPDIR or /tmp */
715533f1ebSrmind
725533f1ebSrmind static struct option longopts[] = {
735533f1ebSrmind { "text", no_argument, NULL, 'a' },
745533f1ebSrmind { "ignore-blank-lines", no_argument, NULL, 'B' },
755533f1ebSrmind { "ignore-space-change", no_argument, NULL, 'b' },
765533f1ebSrmind { "minimal", no_argument, NULL, 'd' },
775533f1ebSrmind { "ignore-tab-expansion", no_argument, NULL, 'E' },
785533f1ebSrmind { "diff-program", required_argument, NULL, 'F' },
795533f1ebSrmind { "speed-large-files", no_argument, NULL, 'H' },
805533f1ebSrmind { "ignore-matching-lines", required_argument, NULL, 'I' },
815533f1ebSrmind { "left-column", no_argument, NULL, 'l' },
825533f1ebSrmind { "output", required_argument, NULL, 'o' },
835533f1ebSrmind { "strip-trailing-cr", no_argument, NULL, 'S' },
845533f1ebSrmind { "suppress-common-lines", no_argument, NULL, 's' },
855533f1ebSrmind { "expand-tabs", no_argument, NULL, 't' },
865533f1ebSrmind { "ignore-all-space", no_argument, NULL, 'W' },
875533f1ebSrmind { "width", required_argument, NULL, 'w' },
885533f1ebSrmind { NULL, 0, NULL, 0 }
895533f1ebSrmind };
905533f1ebSrmind
915533f1ebSrmind /*
925533f1ebSrmind * Create temporary file if source_file is not a regular file.
935533f1ebSrmind * Returns temporary file name if one was malloced, NULL if unnecessary.
945533f1ebSrmind */
955533f1ebSrmind static char *
mktmpcpy(const char * source_file)965533f1ebSrmind mktmpcpy(const char *source_file)
975533f1ebSrmind {
985533f1ebSrmind struct stat sb;
995533f1ebSrmind ssize_t rcount;
1005533f1ebSrmind int ifd, ofd;
1015533f1ebSrmind u_char buf[BUFSIZ];
1025533f1ebSrmind char *target_file;
1035533f1ebSrmind
1045533f1ebSrmind /* Open input and output. */
1055533f1ebSrmind ifd = open(source_file, O_RDONLY, 0);
1065533f1ebSrmind /* File was opened successfully. */
1075533f1ebSrmind if (ifd != -1) {
1085533f1ebSrmind if (fstat(ifd, &sb) == -1)
1095533f1ebSrmind err(2, "error getting file status from %s", source_file);
1105533f1ebSrmind
1115533f1ebSrmind /* Regular file. */
1125533f1ebSrmind if (S_ISREG(sb.st_mode))
1135533f1ebSrmind return (NULL);
1145533f1ebSrmind } else {
1155533f1ebSrmind /* If ``-'' does not exist the user meant stdin. */
1165533f1ebSrmind if (errno == ENOENT && strcmp(source_file, "-") == 0)
1175533f1ebSrmind ifd = STDIN_FILENO;
1185533f1ebSrmind else
1195533f1ebSrmind err(2, "error opening %s", source_file);
1205533f1ebSrmind }
1215533f1ebSrmind
1225533f1ebSrmind /* Not a regular file, so copy input into temporary file. */
1235533f1ebSrmind if (asprintf(&target_file, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
1245533f1ebSrmind err(2, "asprintf");
1255533f1ebSrmind if ((ofd = mkstemp(target_file)) == -1) {
1265533f1ebSrmind warn("error opening %s", target_file);
1275533f1ebSrmind goto FAIL;
1285533f1ebSrmind }
1295533f1ebSrmind while ((rcount = read(ifd, buf, sizeof(buf))) != -1 &&
1305533f1ebSrmind rcount != 0) {
1315533f1ebSrmind ssize_t wcount;
1325533f1ebSrmind
1335533f1ebSrmind wcount = write(ofd, buf, (size_t)rcount);
1345533f1ebSrmind if (-1 == wcount || rcount != wcount) {
1355533f1ebSrmind warn("error writing to %s", target_file);
1365533f1ebSrmind goto FAIL;
1375533f1ebSrmind }
1385533f1ebSrmind }
1395533f1ebSrmind if (rcount == -1) {
1405533f1ebSrmind warn("error reading from %s", source_file);
1415533f1ebSrmind goto FAIL;
1425533f1ebSrmind }
1435533f1ebSrmind
1445533f1ebSrmind close(ifd);
1455533f1ebSrmind close(ofd);
1465533f1ebSrmind
1475533f1ebSrmind return (target_file);
1485533f1ebSrmind
1495533f1ebSrmind FAIL:
1505533f1ebSrmind unlink(target_file);
1515533f1ebSrmind exit(2);
1525533f1ebSrmind }
1535533f1ebSrmind
1545533f1ebSrmind int
main(int argc,char ** argv)1555533f1ebSrmind main(int argc, char **argv)
1565533f1ebSrmind {
1575533f1ebSrmind FILE *diffpipe, *file1, *file2;
1585533f1ebSrmind size_t diffargc = 0, wflag = WIDTH;
1595533f1ebSrmind int ch, fd[2], status;
1605533f1ebSrmind pid_t pid;
1615533f1ebSrmind char **diffargv, *diffprog = "diff", *filename1, *filename2,
1625533f1ebSrmind *tmp1, *tmp2, *s1, *s2;
1635533f1ebSrmind
1645533f1ebSrmind /*
1655533f1ebSrmind * Process diff flags.
1665533f1ebSrmind */
1675533f1ebSrmind /*
1685533f1ebSrmind * Allocate memory for diff arguments and NULL.
1695533f1ebSrmind * Each flag has at most one argument, so doubling argc gives an
1705533f1ebSrmind * upper limit of how many diff args can be passed. argv[0],
1715533f1ebSrmind * file1, and file2 won't have arguments so doubling them will
1725533f1ebSrmind * waste some memory; however we need an extra space for the
1735533f1ebSrmind * NULL at the end, so it sort of works out.
1745533f1ebSrmind */
1755533f1ebSrmind if (!(diffargv = malloc(sizeof(char **) * argc * 2)))
1765533f1ebSrmind err(2, "main");
1775533f1ebSrmind
1785533f1ebSrmind /* Add first argument, the program name. */
1795533f1ebSrmind diffargv[diffargc++] = diffprog;
1805533f1ebSrmind
1815533f1ebSrmind while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:",
1825533f1ebSrmind longopts, NULL)) != -1) {
1835533f1ebSrmind const char *errstr;
1845533f1ebSrmind
1855533f1ebSrmind switch (ch) {
1865533f1ebSrmind case 'a':
1875533f1ebSrmind diffargv[diffargc++] = "-a";
1885533f1ebSrmind break;
1895533f1ebSrmind case 'B':
1905533f1ebSrmind diffargv[diffargc++] = "-B";
1915533f1ebSrmind break;
1925533f1ebSrmind case 'b':
1935533f1ebSrmind diffargv[diffargc++] = "-b";
1945533f1ebSrmind break;
1955533f1ebSrmind case 'd':
1965533f1ebSrmind diffargv[diffargc++] = "-d";
1975533f1ebSrmind break;
1985533f1ebSrmind case 'E':
1995533f1ebSrmind diffargv[diffargc++] = "-E";
2005533f1ebSrmind break;
2015533f1ebSrmind case 'F':
2025533f1ebSrmind diffargv[0] = diffprog = optarg;
2035533f1ebSrmind break;
2045533f1ebSrmind case 'H':
2055533f1ebSrmind diffargv[diffargc++] = "-H";
2065533f1ebSrmind break;
2075533f1ebSrmind case 'I':
2085533f1ebSrmind Iflag = 1;
2095533f1ebSrmind diffargv[diffargc++] = "-I";
2105533f1ebSrmind diffargv[diffargc++] = optarg;
2115533f1ebSrmind break;
2125533f1ebSrmind case 'i':
2135533f1ebSrmind diffargv[diffargc++] = "-i";
2145533f1ebSrmind break;
2155533f1ebSrmind case 'l':
2165533f1ebSrmind lflag = 1;
2175533f1ebSrmind break;
2185533f1ebSrmind case 'o':
2195533f1ebSrmind if ((outfile = fopen(optarg, "w")) == NULL)
2205533f1ebSrmind err(2, "could not open: %s", optarg);
2215533f1ebSrmind break;
2225533f1ebSrmind case 'S':
2235533f1ebSrmind diffargv[diffargc++] = "--strip-trailing-cr";
2245533f1ebSrmind break;
2255533f1ebSrmind case 's':
2265533f1ebSrmind sflag = 1;
2275533f1ebSrmind break;
2285533f1ebSrmind case 't':
2295533f1ebSrmind diffargv[diffargc++] = "-t";
2305533f1ebSrmind break;
2315533f1ebSrmind case 'W':
2325533f1ebSrmind diffargv[diffargc++] = "-w";
2335533f1ebSrmind break;
2345533f1ebSrmind case 'w':
2355533f1ebSrmind wflag = strtonum(optarg, WIDTH_MIN,
2365533f1ebSrmind INT_MAX, &errstr);
2375533f1ebSrmind if (errstr)
2385533f1ebSrmind errx(2, "width is %s: %s", errstr, optarg);
2395533f1ebSrmind break;
2405533f1ebSrmind default:
2415533f1ebSrmind usage();
2425533f1ebSrmind }
2435533f1ebSrmind
2445533f1ebSrmind }
2455533f1ebSrmind argc -= optind;
2465533f1ebSrmind argv += optind;
2475533f1ebSrmind
2485533f1ebSrmind if (argc != 2)
2495533f1ebSrmind usage();
2505533f1ebSrmind
2515533f1ebSrmind if ((tmpdir = getenv("TMPDIR")) == NULL)
2525533f1ebSrmind tmpdir = _PATH_TMP;
2535533f1ebSrmind
2545533f1ebSrmind filename1 = argv[0];
2555533f1ebSrmind filename2 = argv[1];
2565533f1ebSrmind
2575533f1ebSrmind /*
2585533f1ebSrmind * Create temporary files for diff and sdiff to share if file1
2595533f1ebSrmind * or file2 are not regular files. This allows sdiff and diff
2605533f1ebSrmind * to read the same inputs if one or both inputs are stdin.
2615533f1ebSrmind *
2625533f1ebSrmind * If any temporary files were created, their names would be
2635533f1ebSrmind * saved in tmp1 or tmp2. tmp1 should never equal tmp2.
2645533f1ebSrmind */
2655533f1ebSrmind tmp1 = tmp2 = NULL;
2665533f1ebSrmind /* file1 and file2 are the same, so copy to same temp file. */
2675533f1ebSrmind if (strcmp(filename1, filename2) == 0) {
2685533f1ebSrmind if ((tmp1 = mktmpcpy(filename1)))
2695533f1ebSrmind filename1 = filename2 = tmp1;
2705533f1ebSrmind /* Copy file1 and file2 into separate temp files. */
2715533f1ebSrmind } else {
2725533f1ebSrmind if ((tmp1 = mktmpcpy(filename1)))
2735533f1ebSrmind filename1 = tmp1;
2745533f1ebSrmind if ((tmp2 = mktmpcpy(filename2)))
2755533f1ebSrmind filename2 = tmp2;
2765533f1ebSrmind }
2775533f1ebSrmind
2785533f1ebSrmind diffargv[diffargc++] = filename1;
2795533f1ebSrmind diffargv[diffargc++] = filename2;
2805533f1ebSrmind /* Add NULL to end of array to indicate end of array. */
2815533f1ebSrmind diffargv[diffargc++] = NULL;
2825533f1ebSrmind
2835533f1ebSrmind /* Subtract column divider and divide by two. */
2845533f1ebSrmind width = (wflag - 3) / 2;
2855533f1ebSrmind /* Make sure line_width can fit in size_t. */
2865533f1ebSrmind if (width > (SIZE_T_MAX - 3) / 2)
2875533f1ebSrmind errx(2, "width is too large: %zu", width);
2885533f1ebSrmind line_width = width * 2 + 3;
2895533f1ebSrmind
2905533f1ebSrmind if (pipe(fd))
2915533f1ebSrmind err(2, "pipe");
2925533f1ebSrmind
2935533f1ebSrmind switch(pid = fork()) {
2945533f1ebSrmind case 0:
2955533f1ebSrmind /* child */
2965533f1ebSrmind /* We don't read from the pipe. */
2975533f1ebSrmind close(fd[0]);
2985533f1ebSrmind if (dup2(fd[1], STDOUT_FILENO) == -1)
2995533f1ebSrmind err(2, "child could not duplicate descriptor");
3005533f1ebSrmind /* Free unused descriptor. */
3015533f1ebSrmind close(fd[1]);
3025533f1ebSrmind
3035533f1ebSrmind execvp(diffprog, diffargv);
3045533f1ebSrmind err(2, "could not execute diff: %s", diffprog);
3055533f1ebSrmind case -1:
3065533f1ebSrmind err(2, "could not fork");
3075533f1ebSrmind }
3085533f1ebSrmind
3095533f1ebSrmind /* parent */
3105533f1ebSrmind /* We don't write to the pipe. */
3115533f1ebSrmind close(fd[1]);
3125533f1ebSrmind
3135533f1ebSrmind /* Open pipe to diff command. */
3145533f1ebSrmind if ((diffpipe = fdopen(fd[0], "r")) == NULL)
3155533f1ebSrmind err(2, "could not open diff pipe");
3165533f1ebSrmind if ((file1 = fopen(filename1, "r")) == NULL)
3175533f1ebSrmind err(2, "could not open %s", filename1);
3185533f1ebSrmind if ((file2 = fopen(filename2, "r")) == NULL)
3195533f1ebSrmind err(2, "could not open %s", filename2);
3205533f1ebSrmind
3215533f1ebSrmind /* Line numbers start at one. */
3225533f1ebSrmind file1ln = file2ln = 1;
3235533f1ebSrmind
3245533f1ebSrmind /* Read and parse diff output. */
3255533f1ebSrmind while (parsecmd(diffpipe, file1, file2) != EOF)
3265533f1ebSrmind ;
3275533f1ebSrmind fclose(diffpipe);
3285533f1ebSrmind
3295533f1ebSrmind /* Wait for diff to exit. */
3305533f1ebSrmind if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) ||
3315533f1ebSrmind WEXITSTATUS(status) >= 2)
3325533f1ebSrmind err(2, "diff exited abnormally");
3335533f1ebSrmind
3345533f1ebSrmind /* Delete and free unneeded temporary files. */
3355533f1ebSrmind if (tmp1)
3365533f1ebSrmind if (unlink(tmp1))
3375533f1ebSrmind warn("error deleting %s", tmp1);
3385533f1ebSrmind if (tmp2)
3395533f1ebSrmind if (unlink(tmp2))
3405533f1ebSrmind warn("error deleting %s", tmp2);
3415533f1ebSrmind free(tmp1);
3425533f1ebSrmind free(tmp2);
3435533f1ebSrmind filename1 = filename2 = tmp1 = tmp2 = NULL;
3445533f1ebSrmind
3455533f1ebSrmind /* No more diffs, so print common lines. */
3465533f1ebSrmind if (lflag)
3475533f1ebSrmind while ((s1 = xfgets(file1)))
3485533f1ebSrmind enqueue(s1, ' ', NULL);
3495533f1ebSrmind else
3505533f1ebSrmind for (;;) {
3515533f1ebSrmind s1 = xfgets(file1);
3525533f1ebSrmind s2 = xfgets(file2);
3535533f1ebSrmind if (s1 || s2)
3545533f1ebSrmind enqueue(s1, ' ', s2);
3555533f1ebSrmind else
3565533f1ebSrmind break;
3575533f1ebSrmind }
3585533f1ebSrmind fclose(file1);
3595533f1ebSrmind fclose(file2);
3605533f1ebSrmind /* Process unmodified lines. */
3615533f1ebSrmind processq();
3625533f1ebSrmind
3635533f1ebSrmind /* Return diff exit status. */
3645533f1ebSrmind return (WEXITSTATUS(status));
3655533f1ebSrmind }
3665533f1ebSrmind
3675533f1ebSrmind /*
3685533f1ebSrmind * Prints an individual column (left or right), taking into account
3695533f1ebSrmind * that tabs are variable-width. Takes a string, the current column
3705533f1ebSrmind * the cursor is on the screen, and the maximum value of the column.
3715533f1ebSrmind * The column value is updated as we go along.
3725533f1ebSrmind */
3735533f1ebSrmind static void
printcol(const char * s,size_t * col,const size_t col_max)3745533f1ebSrmind printcol(const char *s, size_t *col, const size_t col_max)
3755533f1ebSrmind {
3765533f1ebSrmind
3775533f1ebSrmind for (; *s && *col < col_max; ++s) {
3785533f1ebSrmind size_t new_col;
3795533f1ebSrmind
3805533f1ebSrmind switch (*s) {
3815533f1ebSrmind case '\t':
3825533f1ebSrmind /*
3835533f1ebSrmind * If rounding to next multiple of eight causes
3845533f1ebSrmind * an integer overflow, just return.
3855533f1ebSrmind */
3865533f1ebSrmind if (*col > SIZE_T_MAX - 8)
3875533f1ebSrmind return;
3885533f1ebSrmind
3895533f1ebSrmind /* Round to next multiple of eight. */
3905533f1ebSrmind new_col = (*col / 8 + 1) * 8;
3915533f1ebSrmind
3925533f1ebSrmind /*
3935533f1ebSrmind * If printing the tab goes past the column
3945533f1ebSrmind * width, don't print it and just quit.
3955533f1ebSrmind */
3965533f1ebSrmind if (new_col > col_max)
3975533f1ebSrmind return;
3985533f1ebSrmind *col = new_col;
3995533f1ebSrmind break;
4005533f1ebSrmind
4015533f1ebSrmind default:
4025533f1ebSrmind ++(*col);
4035533f1ebSrmind }
4045533f1ebSrmind
4055533f1ebSrmind putchar(*s);
4065533f1ebSrmind }
4075533f1ebSrmind }
4085533f1ebSrmind
4095533f1ebSrmind /*
4105533f1ebSrmind * Prompts user to either choose between two strings or edit one, both,
4115533f1ebSrmind * or neither.
4125533f1ebSrmind */
4135533f1ebSrmind static void
prompt(const char * s1,const char * s2)4145533f1ebSrmind prompt(const char *s1, const char *s2)
4155533f1ebSrmind {
4165533f1ebSrmind char *cmd;
4175533f1ebSrmind
4185533f1ebSrmind /* Print command prompt. */
4195533f1ebSrmind putchar('%');
4205533f1ebSrmind
4215533f1ebSrmind /* Get user input. */
4225533f1ebSrmind for (; (cmd = xfgets(stdin)); free(cmd)) {
4235533f1ebSrmind const char *p;
4245533f1ebSrmind
4255533f1ebSrmind /* Skip leading whitespace. */
426*3791d282Srillig for (p = cmd; isspace((unsigned char)(*p)); ++p)
4275533f1ebSrmind ;
4285533f1ebSrmind
4295533f1ebSrmind switch (*p) {
4305533f1ebSrmind case 'e':
4315533f1ebSrmind /* Skip `e'. */
4325533f1ebSrmind ++p;
4335533f1ebSrmind
4345533f1ebSrmind if (eparse(p, s1, s2) == -1)
4355533f1ebSrmind goto USAGE;
4365533f1ebSrmind break;
4375533f1ebSrmind
4385533f1ebSrmind case 'l':
4395533f1ebSrmind /* Choose left column as-is. */
4405533f1ebSrmind if (s1 != NULL)
4415533f1ebSrmind fprintf(outfile, "%s\n", s1);
4425533f1ebSrmind
4435533f1ebSrmind /* End of command parsing. */
4445533f1ebSrmind break;
4455533f1ebSrmind
4465533f1ebSrmind case 'q':
4475533f1ebSrmind goto QUIT;
4485533f1ebSrmind
4495533f1ebSrmind case 'r':
4505533f1ebSrmind /* Choose right column as-is. */
4515533f1ebSrmind if (s2 != NULL)
4525533f1ebSrmind fprintf(outfile, "%s\n", s2);
4535533f1ebSrmind
4545533f1ebSrmind /* End of command parsing. */
4555533f1ebSrmind break;
4565533f1ebSrmind
4575533f1ebSrmind case 's':
4585533f1ebSrmind sflag = 1;
4595533f1ebSrmind goto PROMPT;
4605533f1ebSrmind
4615533f1ebSrmind case 'v':
4625533f1ebSrmind sflag = 0;
4635533f1ebSrmind /* FALLTHROUGH */
4645533f1ebSrmind
4655533f1ebSrmind default:
4665533f1ebSrmind /* Interactive usage help. */
4675533f1ebSrmind USAGE:
4685533f1ebSrmind int_usage();
4695533f1ebSrmind PROMPT:
4705533f1ebSrmind putchar('%');
4715533f1ebSrmind
4725533f1ebSrmind /* Prompt user again. */
4735533f1ebSrmind continue;
4745533f1ebSrmind }
4755533f1ebSrmind
4765533f1ebSrmind free(cmd);
4775533f1ebSrmind return;
4785533f1ebSrmind }
4795533f1ebSrmind
4805533f1ebSrmind /*
4815533f1ebSrmind * If there was no error, we received an EOF from stdin, so we
4825533f1ebSrmind * should quit.
4835533f1ebSrmind */
4845533f1ebSrmind QUIT:
4855533f1ebSrmind fclose(outfile);
4865533f1ebSrmind exit(0);
4875533f1ebSrmind }
4885533f1ebSrmind
4895533f1ebSrmind /*
4905533f1ebSrmind * Takes two strings, separated by a column divider. NULL strings are
4915533f1ebSrmind * treated as empty columns. If the divider is the ` ' character, the
4925533f1ebSrmind * second column is not printed (-l flag). In this case, the second
4935533f1ebSrmind * string must be NULL. When the second column is NULL, the divider
4945533f1ebSrmind * does not print the trailing space following the divider character.
4955533f1ebSrmind *
4965533f1ebSrmind * Takes into account that tabs can take multiple columns.
4975533f1ebSrmind */
4985533f1ebSrmind static void
println(const char * s1,const char divc,const char * s2)499859f324dSlukem println(const char *s1, const char divc, const char *s2)
5005533f1ebSrmind {
5015533f1ebSrmind size_t col;
5025533f1ebSrmind
5035533f1ebSrmind /* Print first column. Skips if s1 == NULL. */
5045533f1ebSrmind col = 0;
5055533f1ebSrmind if (s1) {
5065533f1ebSrmind /* Skip angle bracket and space. */
5075533f1ebSrmind printcol(s1, &col, width);
5085533f1ebSrmind
5095533f1ebSrmind }
5105533f1ebSrmind
5115533f1ebSrmind /* Only print left column. */
512859f324dSlukem if (divc == ' ' && !s2) {
5135533f1ebSrmind putchar('\n');
5145533f1ebSrmind return;
5155533f1ebSrmind }
5165533f1ebSrmind
5175533f1ebSrmind /* Otherwise, we pad this column up to width. */
5185533f1ebSrmind for (; col < width; ++col)
5195533f1ebSrmind putchar(' ');
5205533f1ebSrmind
5215533f1ebSrmind /*
5225533f1ebSrmind * Print column divider. If there is no second column, we don't
5235533f1ebSrmind * need to add the space for padding.
5245533f1ebSrmind */
5255533f1ebSrmind if (!s2) {
526859f324dSlukem printf(" %c\n", divc);
5275533f1ebSrmind return;
5285533f1ebSrmind }
529859f324dSlukem printf(" %c ", divc);
5305533f1ebSrmind col += 3;
5315533f1ebSrmind
5325533f1ebSrmind /* Skip angle bracket and space. */
5335533f1ebSrmind printcol(s2, &col, line_width);
5345533f1ebSrmind
5355533f1ebSrmind putchar('\n');
5365533f1ebSrmind }
5375533f1ebSrmind
5385533f1ebSrmind /*
5395533f1ebSrmind * Reads a line from file and returns as a string. If EOF is reached,
5405533f1ebSrmind * NULL is returned. The returned string must be freed afterwards.
5415533f1ebSrmind */
5425533f1ebSrmind static char *
xfgets(FILE * file)5435533f1ebSrmind xfgets(FILE *file)
5445533f1ebSrmind {
5455533f1ebSrmind const char delim[3] = {'\0', '\0', '\0'};
5465533f1ebSrmind char *s;
5475533f1ebSrmind
5485533f1ebSrmind /* XXX - Is this necessary? */
5495533f1ebSrmind clearerr(file);
5505533f1ebSrmind
5515533f1ebSrmind if (!(s = fparseln(file, NULL, NULL, delim, 0)) &&
5525533f1ebSrmind ferror(file))
5535533f1ebSrmind err(2, "error reading file");
5545533f1ebSrmind
5555533f1ebSrmind if (!s) {
5565533f1ebSrmind return (NULL);
5575533f1ebSrmind }
5585533f1ebSrmind
5595533f1ebSrmind return (s);
5605533f1ebSrmind }
5615533f1ebSrmind
5625533f1ebSrmind /*
5635533f1ebSrmind * Parse ed commands from diffpipe and print lines from file1 (lines
5645533f1ebSrmind * to change or delete) or file2 (lines to add or change).
5655533f1ebSrmind * Returns EOF or 0.
5665533f1ebSrmind */
5675533f1ebSrmind static int
parsecmd(FILE * diffpipe,FILE * file1,FILE * file2)5685533f1ebSrmind parsecmd(FILE *diffpipe, FILE *file1, FILE *file2)
5695533f1ebSrmind {
5705533f1ebSrmind size_t file1start, file1end, file2start, file2end, n;
5715533f1ebSrmind /* ed command line and pointer to characters in line */
5725533f1ebSrmind char *line, *p, *q;
5735533f1ebSrmind const char *errstr;
5745533f1ebSrmind char c, cmd;
5755533f1ebSrmind
5765533f1ebSrmind /* Read ed command. */
5775533f1ebSrmind if (!(line = xfgets(diffpipe)))
5785533f1ebSrmind return (EOF);
5795533f1ebSrmind
5805533f1ebSrmind p = line;
5815533f1ebSrmind /* Go to character after line number. */
582*3791d282Srillig while (isdigit((unsigned char)(*p)))
5835533f1ebSrmind ++p;
5845533f1ebSrmind c = *p;
5855533f1ebSrmind *p++ = 0;
5865533f1ebSrmind file1start = strtonum(line, 0, INT_MAX, &errstr);
5875533f1ebSrmind if (errstr)
5885533f1ebSrmind errx(2, "file1 start is %s: %s", errstr, line);
5895533f1ebSrmind
5905533f1ebSrmind /* A range is specified for file1. */
5915533f1ebSrmind if (c == ',') {
5925533f1ebSrmind
5935533f1ebSrmind q = p;
5945533f1ebSrmind /* Go to character after file2end. */
595*3791d282Srillig while (isdigit((unsigned char)(*p)))
5965533f1ebSrmind ++p;
5975533f1ebSrmind c = *p;
5985533f1ebSrmind *p++ = 0;
5995533f1ebSrmind file1end = strtonum(q, 0, INT_MAX, &errstr);
6005533f1ebSrmind if (errstr)
6015533f1ebSrmind errx(2, "file1 end is %s: %s", errstr, line);
6025533f1ebSrmind if (file1start > file1end)
6035533f1ebSrmind errx(2, "invalid line range in file1: %s", line);
6045533f1ebSrmind
6055533f1ebSrmind } else
6065533f1ebSrmind file1end = file1start;
6075533f1ebSrmind
6085533f1ebSrmind cmd = c;
6095533f1ebSrmind /* Check that cmd is valid. */
6105533f1ebSrmind if (!(cmd == 'a' || cmd == 'c' || cmd == 'd'))
6115533f1ebSrmind errx(2, "ed command not recognized: %c: %s", cmd, line);
6125533f1ebSrmind
6135533f1ebSrmind q = p;
6145533f1ebSrmind /* Go to character after line number. */
615*3791d282Srillig while (isdigit((unsigned char)(*p)))
6165533f1ebSrmind ++p;
6175533f1ebSrmind c = *p;
6185533f1ebSrmind *p++ = 0;
6195533f1ebSrmind file2start = strtonum(q, 0, INT_MAX, &errstr);
6205533f1ebSrmind if (errstr)
6215533f1ebSrmind errx(2, "file2 start is %s: %s", errstr, line);
6225533f1ebSrmind
6235533f1ebSrmind /*
6245533f1ebSrmind * There should either be a comma signifying a second line
6255533f1ebSrmind * number or the line should just end here.
6265533f1ebSrmind */
6275533f1ebSrmind if (c != ',' && c != '\0')
6285533f1ebSrmind errx(2, "invalid line range in file2: %c: %s", c, line);
6295533f1ebSrmind
6305533f1ebSrmind if (c == ',') {
6315533f1ebSrmind
6325533f1ebSrmind file2end = strtonum(p, 0, INT_MAX, &errstr);
6335533f1ebSrmind if (errstr)
6345533f1ebSrmind errx(2, "file2 end is %s: %s", errstr, line);
6355533f1ebSrmind if (file2start >= file2end)
6365533f1ebSrmind errx(2, "invalid line range in file2: %s", line);
6375533f1ebSrmind } else
6385533f1ebSrmind file2end = file2start;
6395533f1ebSrmind
6405533f1ebSrmind /* Appends happen _after_ stated line. */
6415533f1ebSrmind if (cmd == 'a') {
6425533f1ebSrmind if (file1start != file1end)
6435533f1ebSrmind errx(2, "append cannot have a file1 range: %s",
6445533f1ebSrmind line);
6455533f1ebSrmind if (file1start == SIZE_T_MAX)
6465533f1ebSrmind errx(2, "file1 line range too high: %s", line);
6475533f1ebSrmind file1start = ++file1end;
6485533f1ebSrmind }
6495533f1ebSrmind /*
6505533f1ebSrmind * I'm not sure what the deal is with the line numbers for
6515533f1ebSrmind * deletes, though.
6525533f1ebSrmind */
6535533f1ebSrmind else if (cmd == 'd') {
6545533f1ebSrmind if (file2start != file2end)
6555533f1ebSrmind errx(2, "delete cannot have a file2 range: %s",
6565533f1ebSrmind line);
6575533f1ebSrmind if (file2start == SIZE_T_MAX)
6585533f1ebSrmind errx(2, "file2 line range too high: %s", line);
6595533f1ebSrmind file2start = ++file2end;
6605533f1ebSrmind }
6615533f1ebSrmind
6625533f1ebSrmind /*
6635533f1ebSrmind * Continue reading file1 and file2 until we reach line numbers
6645533f1ebSrmind * specified by diff. Should only happen with -I flag.
6655533f1ebSrmind */
6665533f1ebSrmind for (; file1ln < file1start && file2ln < file2start;
6675533f1ebSrmind ++file1ln, ++file2ln) {
6685533f1ebSrmind char *s1, *s2;
6695533f1ebSrmind
6705533f1ebSrmind if (!(s1 = xfgets(file1)))
6715533f1ebSrmind errx(2, "file1 shorter than expected");
6725533f1ebSrmind if (!(s2 = xfgets(file2)))
6735533f1ebSrmind errx(2, "file2 shorter than expected");
6745533f1ebSrmind
6755533f1ebSrmind /* If the -l flag was specified, print only left column. */
6765533f1ebSrmind if (lflag) {
6775533f1ebSrmind free(s2);
6785533f1ebSrmind /*
6795533f1ebSrmind * XXX - If -l and -I are both specified, all
6805533f1ebSrmind * unchanged or ignored lines are shown with a
6815533f1ebSrmind * `(' divider. This matches GNU sdiff, but I
6825533f1ebSrmind * believe it is a bug. Just check out:
6835533f1ebSrmind * gsdiff -l -I '^$' samefile samefile.
6845533f1ebSrmind */
6855533f1ebSrmind if (Iflag)
6865533f1ebSrmind enqueue(s1, '(', NULL);
6875533f1ebSrmind else
6885533f1ebSrmind enqueue(s1, ' ', NULL);
6895533f1ebSrmind } else
6905533f1ebSrmind enqueue(s1, ' ', s2);
6915533f1ebSrmind }
6925533f1ebSrmind /* Ignore deleted lines. */
6935533f1ebSrmind for (; file1ln < file1start; ++file1ln) {
6945533f1ebSrmind char *s;
6955533f1ebSrmind
6965533f1ebSrmind if (!(s = xfgets(file1)))
6975533f1ebSrmind errx(2, "file1 shorter than expected");
6985533f1ebSrmind
6995533f1ebSrmind enqueue(s, '(', NULL);
7005533f1ebSrmind }
7015533f1ebSrmind /* Ignore added lines. */
7025533f1ebSrmind for (; file2ln < file2start; ++file2ln) {
7035533f1ebSrmind char *s;
7045533f1ebSrmind
7055533f1ebSrmind if (!(s = xfgets(file2)))
7065533f1ebSrmind errx(2, "file2 shorter than expected");
7075533f1ebSrmind
7085533f1ebSrmind /* If -l flag was given, don't print right column. */
7095533f1ebSrmind if (lflag)
7105533f1ebSrmind free(s);
7115533f1ebSrmind else
7125533f1ebSrmind enqueue(NULL, ')', s);
7135533f1ebSrmind }
7145533f1ebSrmind
7155533f1ebSrmind /* Process unmodified or skipped lines. */
7165533f1ebSrmind processq();
7175533f1ebSrmind
7185533f1ebSrmind switch (cmd) {
7195533f1ebSrmind case 'a':
7205533f1ebSrmind printa(file2, file2end);
7215533f1ebSrmind n = file2end - file2start + 1;
7225533f1ebSrmind break;
7235533f1ebSrmind
7245533f1ebSrmind case 'c':
7255533f1ebSrmind printc(file1, file1end, file2, file2end);
7265533f1ebSrmind n = file1end - file1start + 1 + 1 + file2end - file2start + 1;
7275533f1ebSrmind break;
7285533f1ebSrmind
7295533f1ebSrmind case 'd':
7305533f1ebSrmind printd(file1, file1end);
7315533f1ebSrmind n = file1end - file1start + 1;
7325533f1ebSrmind break;
7335533f1ebSrmind
7345533f1ebSrmind default:
7355533f1ebSrmind errx(2, "invalid diff command: %c: %s", cmd, line);
7365533f1ebSrmind }
7375533f1ebSrmind
7385533f1ebSrmind /* Skip to next ed line. */
7395533f1ebSrmind while (n--)
7405533f1ebSrmind if (!xfgets(diffpipe))
7415533f1ebSrmind errx(2, "diff ended early");
7425533f1ebSrmind
7435533f1ebSrmind return (0);
7445533f1ebSrmind }
7455533f1ebSrmind
7465533f1ebSrmind /*
7475533f1ebSrmind * Queues up a diff line.
7485533f1ebSrmind */
7495533f1ebSrmind static void
enqueue(char * left,char divc,char * right)750859f324dSlukem enqueue(char *left, char divc, char *right)
7515533f1ebSrmind {
7525533f1ebSrmind struct diffline *diffp;
7535533f1ebSrmind
7545533f1ebSrmind if (!(diffp = malloc(sizeof(struct diffline))))
7555533f1ebSrmind err(2, "enqueue");
7565533f1ebSrmind diffp->left = left;
757859f324dSlukem diffp->div = divc;
7585533f1ebSrmind diffp->right = right;
7595533f1ebSrmind SIMPLEQ_INSERT_TAIL(&diffhead, diffp, diffentries);
7605533f1ebSrmind }
7615533f1ebSrmind
7625533f1ebSrmind /*
7635533f1ebSrmind * Free a diffline structure and its elements.
7645533f1ebSrmind */
7655533f1ebSrmind static void
freediff(struct diffline * diffp)7665533f1ebSrmind freediff(struct diffline *diffp)
7675533f1ebSrmind {
7685533f1ebSrmind free(diffp->left);
7695533f1ebSrmind free(diffp->right);
7705533f1ebSrmind free(diffp);
7715533f1ebSrmind }
7725533f1ebSrmind
7735533f1ebSrmind /*
7745533f1ebSrmind * Append second string into first. Repeated appends to the same string
7755533f1ebSrmind * are cached, making this an O(n) function, where n = strlen(append).
7765533f1ebSrmind */
7775533f1ebSrmind static void
astrcat(char ** s,const char * append)7785533f1ebSrmind astrcat(char **s, const char *append)
7795533f1ebSrmind {
7805533f1ebSrmind /* Length of string in previous run. */
7815533f1ebSrmind static size_t offset = 0;
7825533f1ebSrmind size_t newsiz;
7835533f1ebSrmind /*
7845533f1ebSrmind * String from previous run. Compared to *s to see if we are
7855533f1ebSrmind * dealing with the same string. If so, we can use offset.
7865533f1ebSrmind */
7875533f1ebSrmind static const char *oldstr = NULL;
7885533f1ebSrmind char *newstr;
7895533f1ebSrmind
7905533f1ebSrmind
7915533f1ebSrmind /*
7925533f1ebSrmind * First string is NULL, so just copy append.
7935533f1ebSrmind */
7945533f1ebSrmind if (!*s) {
7955533f1ebSrmind if (!(*s = strdup(append)))
7965533f1ebSrmind err(2, "astrcat");
7975533f1ebSrmind
7985533f1ebSrmind /* Keep track of string. */
7995533f1ebSrmind offset = strlen(*s);
8005533f1ebSrmind oldstr = *s;
8015533f1ebSrmind
8025533f1ebSrmind return;
8035533f1ebSrmind }
8045533f1ebSrmind
8055533f1ebSrmind /*
8065533f1ebSrmind * *s is a string so concatenate.
8075533f1ebSrmind */
8085533f1ebSrmind
8095533f1ebSrmind /* Did we process the same string in the last run? */
8105533f1ebSrmind /*
8115533f1ebSrmind * If this is a different string from the one we just processed
8125533f1ebSrmind * cache new string.
8135533f1ebSrmind */
8145533f1ebSrmind if (oldstr != *s) {
8155533f1ebSrmind offset = strlen(*s);
8165533f1ebSrmind oldstr = *s;
8175533f1ebSrmind }
8185533f1ebSrmind
8195533f1ebSrmind /* Size = strlen(*s) + \n + strlen(append) + '\0'. */
8205533f1ebSrmind newsiz = offset + 1 + strlen(append) + 1;
8215533f1ebSrmind
8225533f1ebSrmind /* Resize *s to fit new string. */
8235533f1ebSrmind newstr = realloc(*s, newsiz);
8245533f1ebSrmind if (newstr == NULL)
8255533f1ebSrmind err(2, "astrcat");
8265533f1ebSrmind *s = newstr;
8275533f1ebSrmind
8285533f1ebSrmind /* *s + offset should be end of string. */
8295533f1ebSrmind /* Concatenate. */
8305533f1ebSrmind strlcpy(*s + offset, "\n", newsiz - offset);
8315533f1ebSrmind strlcat(*s + offset, append, newsiz - offset);
8325533f1ebSrmind
8335533f1ebSrmind /* New string length should be exactly newsiz - 1 characters. */
8345533f1ebSrmind /* Store generated string's values. */
8355533f1ebSrmind offset = newsiz - 1;
8365533f1ebSrmind oldstr = *s;
8375533f1ebSrmind }
8385533f1ebSrmind
8395533f1ebSrmind /*
8405533f1ebSrmind * Process diff set queue, printing, prompting, and saving each diff
8415533f1ebSrmind * line stored in queue.
8425533f1ebSrmind */
8435533f1ebSrmind static void
processq(void)8445533f1ebSrmind processq(void)
8455533f1ebSrmind {
8465533f1ebSrmind struct diffline *diffp;
8475533f1ebSrmind char divc, *left, *right;
8485533f1ebSrmind
8495533f1ebSrmind /* Don't process empty queue. */
8505533f1ebSrmind if (SIMPLEQ_EMPTY(&diffhead))
8515533f1ebSrmind return;
8525533f1ebSrmind
8535533f1ebSrmind /* Remember the divider. */
8545533f1ebSrmind divc = SIMPLEQ_FIRST(&diffhead)->div;
8555533f1ebSrmind
8565533f1ebSrmind left = NULL;
8575533f1ebSrmind right = NULL;
8585533f1ebSrmind /*
8595533f1ebSrmind * Go through set of diffs, concatenating each line in left or
8605533f1ebSrmind * right column into two long strings, `left' and `right'.
8615533f1ebSrmind */
8625533f1ebSrmind SIMPLEQ_FOREACH(diffp, &diffhead, diffentries) {
8635533f1ebSrmind /*
8645533f1ebSrmind * Print changed lines if -s was given,
8655533f1ebSrmind * print all lines if -s was not given.
8665533f1ebSrmind */
8675533f1ebSrmind if (!sflag || diffp->div == '|' || diffp->div == '<' ||
8685533f1ebSrmind diffp->div == '>')
8695533f1ebSrmind println(diffp->left, diffp->div, diffp->right);
8705533f1ebSrmind
8715533f1ebSrmind /* Append new lines to diff set. */
8725533f1ebSrmind if (diffp->left)
8735533f1ebSrmind astrcat(&left, diffp->left);
8745533f1ebSrmind if (diffp->right)
8755533f1ebSrmind astrcat(&right, diffp->right);
8765533f1ebSrmind }
8775533f1ebSrmind
8785533f1ebSrmind /* Empty queue and free each diff line and its elements. */
8795533f1ebSrmind while (!SIMPLEQ_EMPTY(&diffhead)) {
8805533f1ebSrmind diffp = SIMPLEQ_FIRST(&diffhead);
8815533f1ebSrmind SIMPLEQ_REMOVE_HEAD(&diffhead, diffentries);
8825533f1ebSrmind freediff(diffp);
8835533f1ebSrmind }
8845533f1ebSrmind
8855533f1ebSrmind /* Write to outfile, prompting user if lines are different. */
8865533f1ebSrmind if (outfile)
8875533f1ebSrmind switch (divc) {
8885533f1ebSrmind case ' ': case '(': case ')':
8895533f1ebSrmind fprintf(outfile, "%s\n", left);
8905533f1ebSrmind break;
8915533f1ebSrmind case '|': case '<': case '>':
8925533f1ebSrmind prompt(left, right);
8935533f1ebSrmind break;
8945533f1ebSrmind default:
8955533f1ebSrmind errx(2, "invalid divider: %c", divc);
8965533f1ebSrmind }
8975533f1ebSrmind
8985533f1ebSrmind /* Free left and right. */
8995533f1ebSrmind free(left);
9005533f1ebSrmind free(right);
9015533f1ebSrmind }
9025533f1ebSrmind
9035533f1ebSrmind /*
9045533f1ebSrmind * Print lines following an (a)ppend command.
9055533f1ebSrmind */
9065533f1ebSrmind static void
printa(FILE * file,size_t line2)9075533f1ebSrmind printa(FILE *file, size_t line2)
9085533f1ebSrmind {
9095533f1ebSrmind char *line;
9105533f1ebSrmind
9115533f1ebSrmind for (; file2ln <= line2; ++file2ln) {
9125533f1ebSrmind if (!(line = xfgets(file)))
9135533f1ebSrmind errx(2, "append ended early");
9145533f1ebSrmind enqueue(NULL, '>', line);
9155533f1ebSrmind }
9165533f1ebSrmind
9175533f1ebSrmind processq();
9185533f1ebSrmind }
9195533f1ebSrmind
9205533f1ebSrmind /*
9215533f1ebSrmind * Print lines following a (c)hange command, from file1ln to file1end
9225533f1ebSrmind * and from file2ln to file2end.
9235533f1ebSrmind */
9245533f1ebSrmind static void
printc(FILE * file1,size_t file1end,FILE * file2,size_t file2end)9255533f1ebSrmind printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end)
9265533f1ebSrmind {
9275533f1ebSrmind struct fileline {
9285533f1ebSrmind SIMPLEQ_ENTRY(fileline) fileentries;
9295533f1ebSrmind char *line;
9305533f1ebSrmind };
9315533f1ebSrmind SIMPLEQ_HEAD(, fileline) delqhead = SIMPLEQ_HEAD_INITIALIZER(delqhead);
9325533f1ebSrmind
9335533f1ebSrmind /* Read lines to be deleted. */
9345533f1ebSrmind for (; file1ln <= file1end; ++file1ln) {
9355533f1ebSrmind struct fileline *linep;
9365533f1ebSrmind char *line1;
9375533f1ebSrmind
9385533f1ebSrmind /* Read lines from both. */
9395533f1ebSrmind if (!(line1 = xfgets(file1)))
9405533f1ebSrmind errx(2, "error reading file1 in delete in change");
9415533f1ebSrmind
9425533f1ebSrmind /* Add to delete queue. */
9435533f1ebSrmind if (!(linep = malloc(sizeof(struct fileline))))
9445533f1ebSrmind err(2, "printc");
9455533f1ebSrmind linep->line = line1;
9465533f1ebSrmind SIMPLEQ_INSERT_TAIL(&delqhead, linep, fileentries);
9475533f1ebSrmind }
9485533f1ebSrmind
9495533f1ebSrmind /* Process changed lines.. */
9505533f1ebSrmind for (; !SIMPLEQ_EMPTY(&delqhead) && file2ln <= file2end;
9515533f1ebSrmind ++file2ln) {
9525533f1ebSrmind struct fileline *del;
9535533f1ebSrmind char *add;
9545533f1ebSrmind
9555533f1ebSrmind /* Get add line. */
9565533f1ebSrmind if (!(add = xfgets(file2)))
9575533f1ebSrmind errx(2, "error reading add in change");
9585533f1ebSrmind
9595533f1ebSrmind del = SIMPLEQ_FIRST(&delqhead);
9605533f1ebSrmind enqueue(del->line, '|', add);
9615533f1ebSrmind SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
9625533f1ebSrmind /*
9635533f1ebSrmind * Free fileline structure but not its elements since
9645533f1ebSrmind * they are queued up.
9655533f1ebSrmind */
9665533f1ebSrmind free(del);
9675533f1ebSrmind }
9685533f1ebSrmind processq();
9695533f1ebSrmind
9705533f1ebSrmind /* Process remaining lines to add. */
9715533f1ebSrmind for (; file2ln <= file2end; ++file2ln) {
9725533f1ebSrmind char *add;
9735533f1ebSrmind
9745533f1ebSrmind /* Get add line. */
9755533f1ebSrmind if (!(add = xfgets(file2)))
9765533f1ebSrmind errx(2, "error reading add in change");
9775533f1ebSrmind
9785533f1ebSrmind enqueue(NULL, '>', add);
9795533f1ebSrmind }
9805533f1ebSrmind processq();
9815533f1ebSrmind
9825533f1ebSrmind /* Process remaining lines to delete. */
9835533f1ebSrmind while (!SIMPLEQ_EMPTY(&delqhead)) {
9845533f1ebSrmind struct fileline *filep;
9855533f1ebSrmind
9865533f1ebSrmind filep = SIMPLEQ_FIRST(&delqhead);
9875533f1ebSrmind enqueue(filep->line, '<', NULL);
9885533f1ebSrmind SIMPLEQ_REMOVE_HEAD(&delqhead, fileentries);
9895533f1ebSrmind free(filep);
9905533f1ebSrmind }
9915533f1ebSrmind processq();
9925533f1ebSrmind }
9935533f1ebSrmind
9945533f1ebSrmind /*
9955533f1ebSrmind * Print deleted lines from file, from file1ln to file1end.
9965533f1ebSrmind */
9975533f1ebSrmind static void
printd(FILE * file1,size_t file1end)9985533f1ebSrmind printd(FILE *file1, size_t file1end)
9995533f1ebSrmind {
10005533f1ebSrmind char *line1;
10015533f1ebSrmind
10025533f1ebSrmind /* Print out lines file1ln to line2. */
10035533f1ebSrmind for (; file1ln <= file1end; ++file1ln) {
10045533f1ebSrmind /* XXX - Why can't this handle stdin? */
10055533f1ebSrmind if (!(line1 = xfgets(file1)))
10065533f1ebSrmind errx(2, "file1 ended early in delete");
10075533f1ebSrmind enqueue(line1, '<', NULL);
10085533f1ebSrmind }
10095533f1ebSrmind processq();
10105533f1ebSrmind }
10115533f1ebSrmind
10125533f1ebSrmind /*
10135533f1ebSrmind * Interactive mode usage.
10145533f1ebSrmind */
10155533f1ebSrmind static void
int_usage(void)10165533f1ebSrmind int_usage(void)
10175533f1ebSrmind {
10185533f1ebSrmind puts("e:\tedit blank diff\n"
10195533f1ebSrmind "eb:\tedit both diffs concatenated\n"
10205533f1ebSrmind "el:\tedit left diff\n"
10215533f1ebSrmind "er:\tedit right diff\n"
10225533f1ebSrmind "l:\tchoose left diff\n"
10235533f1ebSrmind "r:\tchoose right diff\n"
10245533f1ebSrmind "s:\tsilent mode--don't print identical lines\n"
10255533f1ebSrmind "v:\tverbose mode--print identical lines\n"
10265533f1ebSrmind "q:\tquit");
10275533f1ebSrmind }
10285533f1ebSrmind
10295533f1ebSrmind static void
usage(void)10305533f1ebSrmind usage(void)
10315533f1ebSrmind {
10325533f1ebSrmind extern char *__progname;
10335533f1ebSrmind
10345533f1ebSrmind fprintf(stderr,
10355533f1ebSrmind "usage: %s [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n",
10365533f1ebSrmind __progname);
10375533f1ebSrmind exit(2);
10385533f1ebSrmind }
1039