113b5b548SBaptiste Daroussin /* $OpenBSD: sdiff.c,v 1.36 2015/12/29 19:04:46 gsoares Exp $ */ 213b5b548SBaptiste Daroussin 313b5b548SBaptiste Daroussin /* 413b5b548SBaptiste Daroussin * Written by Raymond Lai <ray@cyth.net>. 513b5b548SBaptiste Daroussin * Public domain. 613b5b548SBaptiste Daroussin */ 713b5b548SBaptiste Daroussin 813b5b548SBaptiste Daroussin #include <sys/param.h> 913b5b548SBaptiste Daroussin #include <sys/queue.h> 1013b5b548SBaptiste Daroussin #include <sys/stat.h> 1113b5b548SBaptiste Daroussin #include <sys/wait.h> 1213b5b548SBaptiste Daroussin 1313b5b548SBaptiste Daroussin #include <ctype.h> 1413b5b548SBaptiste Daroussin #include <err.h> 1513b5b548SBaptiste Daroussin #include <errno.h> 1613b5b548SBaptiste Daroussin #include <fcntl.h> 1713b5b548SBaptiste Daroussin #include <getopt.h> 1813b5b548SBaptiste Daroussin #include <limits.h> 1913b5b548SBaptiste Daroussin #include <paths.h> 203cc86989SDag-Erling Smørgrav #include <stdbool.h> 2113b5b548SBaptiste Daroussin #include <stdint.h> 2213b5b548SBaptiste Daroussin #include <stdio.h> 2313b5b548SBaptiste Daroussin #include <stdlib.h> 2413b5b548SBaptiste Daroussin #include <string.h> 2513b5b548SBaptiste Daroussin #include <unistd.h> 2613b5b548SBaptiste Daroussin 2713b5b548SBaptiste Daroussin #include "extern.h" 2813b5b548SBaptiste Daroussin 292c320002SBaptiste Daroussin static char diff_path[] = "/usr/bin/diff"; 3013b5b548SBaptiste Daroussin 3113b5b548SBaptiste Daroussin #define WIDTH 126 3213b5b548SBaptiste Daroussin /* 3313b5b548SBaptiste Daroussin * Each column must be at least one character wide, plus three 3413b5b548SBaptiste Daroussin * characters between the columns (space, [<|>], space). 3513b5b548SBaptiste Daroussin */ 3613b5b548SBaptiste Daroussin #define WIDTH_MIN 5 3713b5b548SBaptiste Daroussin 3813b5b548SBaptiste Daroussin /* 3 kilobytes of chars */ 3913b5b548SBaptiste Daroussin #define MAX_CHECK 768 4013b5b548SBaptiste Daroussin 4113b5b548SBaptiste Daroussin /* A single diff line. */ 4213b5b548SBaptiste Daroussin struct diffline { 4313b5b548SBaptiste Daroussin STAILQ_ENTRY(diffline) diffentries; 4413b5b548SBaptiste Daroussin char *left; 4513b5b548SBaptiste Daroussin char div; 4613b5b548SBaptiste Daroussin char *right; 4713b5b548SBaptiste Daroussin }; 4813b5b548SBaptiste Daroussin 4913b5b548SBaptiste Daroussin static void astrcat(char **, const char *); 5013b5b548SBaptiste Daroussin static void enqueue(char *, char, char *); 5113b5b548SBaptiste Daroussin static char *mktmpcpy(const char *); 5213b5b548SBaptiste Daroussin static int istextfile(FILE *); 53ad7bef8bSDag-Erling Smørgrav static int bindiff(FILE *, char *, FILE *, char *); 5413b5b548SBaptiste Daroussin static void freediff(struct diffline *); 5513b5b548SBaptiste Daroussin static void int_usage(void); 5613b5b548SBaptiste Daroussin static int parsecmd(FILE *, FILE *, FILE *); 5713b5b548SBaptiste Daroussin static void printa(FILE *, size_t); 5813b5b548SBaptiste Daroussin static void printc(FILE *, size_t, FILE *, size_t); 5913b5b548SBaptiste Daroussin static void printcol(const char *, size_t *, const size_t); 6013b5b548SBaptiste Daroussin static void printd(FILE *, size_t); 6113b5b548SBaptiste Daroussin static void println(const char *, const char, const char *); 6213b5b548SBaptiste Daroussin static void processq(void); 6313b5b548SBaptiste Daroussin static void prompt(const char *, const char *); 6413b5b548SBaptiste Daroussin static void usage(void) __dead2; 6513b5b548SBaptiste Daroussin static char *xfgets(FILE *); 6613b5b548SBaptiste Daroussin 6713b5b548SBaptiste Daroussin static STAILQ_HEAD(, diffline) diffhead = STAILQ_HEAD_INITIALIZER(diffhead); 6813b5b548SBaptiste Daroussin static size_t line_width; /* width of a line (two columns and divider) */ 6913b5b548SBaptiste Daroussin static size_t width; /* width of each column */ 7013b5b548SBaptiste Daroussin static size_t file1ln, file2ln; /* line number of file1 and file2 */ 713cc86989SDag-Erling Smørgrav static bool Iflag; /* ignore sets matching regexp */ 723cc86989SDag-Erling Smørgrav static bool lflag; /* print only left column for identical lines */ 733cc86989SDag-Erling Smørgrav static bool sflag; /* skip identical lines */ 74a834edfcSDag-Erling Smørgrav static bool tflag; /* expand tabs */ 75a834edfcSDag-Erling Smørgrav static int tabsize = 8; /* tab size */ 7613b5b548SBaptiste Daroussin FILE *outfp; /* file to save changes to */ 7713b5b548SBaptiste Daroussin const char *tmpdir; /* TMPDIR or /tmp */ 7813b5b548SBaptiste Daroussin 7913b5b548SBaptiste Daroussin enum { 8013b5b548SBaptiste Daroussin HELP_OPT = CHAR_MAX + 1, 8113b5b548SBaptiste Daroussin NORMAL_OPT, 8213b5b548SBaptiste Daroussin FCASE_SENSITIVE_OPT, 8313b5b548SBaptiste Daroussin FCASE_IGNORE_OPT, 8413b5b548SBaptiste Daroussin STRIPCR_OPT, 8513b5b548SBaptiste Daroussin TSIZE_OPT, 8613b5b548SBaptiste Daroussin DIFFPROG_OPT, 8713b5b548SBaptiste Daroussin }; 8813b5b548SBaptiste Daroussin 8913b5b548SBaptiste Daroussin static struct option longopts[] = { 9013b5b548SBaptiste Daroussin /* options only processed in sdiff */ 9113b5b548SBaptiste Daroussin { "suppress-common-lines", no_argument, NULL, 's' }, 9213b5b548SBaptiste Daroussin { "width", required_argument, NULL, 'w' }, 9313b5b548SBaptiste Daroussin 9413b5b548SBaptiste Daroussin { "output", required_argument, NULL, 'o' }, 9513b5b548SBaptiste Daroussin { "diff-program", required_argument, NULL, DIFFPROG_OPT }, 9613b5b548SBaptiste Daroussin 9713b5b548SBaptiste Daroussin /* Options processed by diff. */ 9813b5b548SBaptiste Daroussin { "ignore-file-name-case", no_argument, NULL, FCASE_IGNORE_OPT }, 9913b5b548SBaptiste Daroussin { "no-ignore-file-name-case", no_argument, NULL, FCASE_SENSITIVE_OPT }, 10013b5b548SBaptiste Daroussin { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, 10113b5b548SBaptiste Daroussin { "tabsize", required_argument, NULL, TSIZE_OPT }, 10213b5b548SBaptiste Daroussin { "help", no_argument, NULL, HELP_OPT }, 10313b5b548SBaptiste Daroussin { "text", no_argument, NULL, 'a' }, 10413b5b548SBaptiste Daroussin { "ignore-blank-lines", no_argument, NULL, 'B' }, 10513b5b548SBaptiste Daroussin { "ignore-space-change", no_argument, NULL, 'b' }, 10613b5b548SBaptiste Daroussin { "minimal", no_argument, NULL, 'd' }, 10713b5b548SBaptiste Daroussin { "ignore-tab-expansion", no_argument, NULL, 'E' }, 10813b5b548SBaptiste Daroussin { "ignore-matching-lines", required_argument, NULL, 'I' }, 10913b5b548SBaptiste Daroussin { "ignore-case", no_argument, NULL, 'i' }, 110d481a925SBaptiste Daroussin { "left-column", no_argument, NULL, 'l' }, 11113b5b548SBaptiste Daroussin { "expand-tabs", no_argument, NULL, 't' }, 11213b5b548SBaptiste Daroussin { "speed-large-files", no_argument, NULL, 'H' }, 11313b5b548SBaptiste Daroussin { "ignore-all-space", no_argument, NULL, 'W' }, 11413b5b548SBaptiste Daroussin 11513b5b548SBaptiste Daroussin { NULL, 0, NULL, '\0'} 11613b5b548SBaptiste Daroussin }; 11713b5b548SBaptiste Daroussin 11813b5b548SBaptiste Daroussin static const char *help_msg[] = { 11923421d54SBaptiste Daroussin "usage: sdiff [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", 12023421d54SBaptiste Daroussin "-l, --left-column: only print the left column for identical lines.", 12123421d54SBaptiste Daroussin "-o OUTFILE, --output=OUTFILE: interactively merge file1 and file2 into outfile.", 12223421d54SBaptiste Daroussin "-s, --suppress-common-lines: skip identical lines.", 12323421d54SBaptiste Daroussin "-w WIDTH, --width=WIDTH: print a maximum of WIDTH characters on each line.", 12423421d54SBaptiste Daroussin "", 12523421d54SBaptiste Daroussin "Options passed to diff(1) are:", 12623421d54SBaptiste Daroussin "\t-a, --text: treat file1 and file2 as text files.", 12723421d54SBaptiste Daroussin "\t-b, --ignore-trailing-cr: ignore trailing blank spaces.", 12823421d54SBaptiste Daroussin "\t-d, --minimal: minimize diff size.", 12923421d54SBaptiste Daroussin "\t-I RE, --ignore-matching-lines=RE: ignore changes whose line matches RE.", 13023421d54SBaptiste Daroussin "\t-i, --ignore-case: do a case-insensitive comparison.", 131a834edfcSDag-Erling Smørgrav "\t-t, --expand-tabs: expand tabs to spaces.", 132*ca75b7daSDag-Erling Smørgrav "\t-W, --ignore-all-space: ignore all whitespace.", 13323421d54SBaptiste Daroussin "\t--speed-large-files: assume large file with scattered changes.", 13423421d54SBaptiste Daroussin "\t--strip-trailing-cr: strip trailing carriage return.", 13523421d54SBaptiste Daroussin "\t--ignore-file-name-case: ignore case of file names.", 13623421d54SBaptiste Daroussin "\t--no-ignore-file-name-case: do not ignore file name case", 13723421d54SBaptiste Daroussin "\t--tabsize NUM: change size of tabs (default 8.)", 13813b5b548SBaptiste Daroussin 13913b5b548SBaptiste Daroussin NULL, 14013b5b548SBaptiste Daroussin }; 14113b5b548SBaptiste Daroussin 14213b5b548SBaptiste Daroussin /* 14313b5b548SBaptiste Daroussin * Create temporary file if source_file is not a regular file. 14413b5b548SBaptiste Daroussin * Returns temporary file name if one was malloced, NULL if unnecessary. 14513b5b548SBaptiste Daroussin */ 14613b5b548SBaptiste Daroussin static char * 14713b5b548SBaptiste Daroussin mktmpcpy(const char *source_file) 14813b5b548SBaptiste Daroussin { 14913b5b548SBaptiste Daroussin struct stat sb; 15013b5b548SBaptiste Daroussin ssize_t rcount; 15113b5b548SBaptiste Daroussin int ifd, ofd; 15213b5b548SBaptiste Daroussin u_char buf[BUFSIZ]; 15313b5b548SBaptiste Daroussin char *target_file; 15413b5b548SBaptiste Daroussin 15513b5b548SBaptiste Daroussin /* Open input and output. */ 15613b5b548SBaptiste Daroussin ifd = open(source_file, O_RDONLY, 0); 15713b5b548SBaptiste Daroussin /* File was opened successfully. */ 15813b5b548SBaptiste Daroussin if (ifd != -1) { 15913b5b548SBaptiste Daroussin if (fstat(ifd, &sb) == -1) 16013b5b548SBaptiste Daroussin err(2, "error getting file status from %s", source_file); 16113b5b548SBaptiste Daroussin 16213b5b548SBaptiste Daroussin /* Regular file. */ 16313b5b548SBaptiste Daroussin if (S_ISREG(sb.st_mode)) { 16413b5b548SBaptiste Daroussin close(ifd); 16513b5b548SBaptiste Daroussin return (NULL); 16613b5b548SBaptiste Daroussin } 16713b5b548SBaptiste Daroussin } else { 16813b5b548SBaptiste Daroussin /* If ``-'' does not exist the user meant stdin. */ 16913b5b548SBaptiste Daroussin if (errno == ENOENT && strcmp(source_file, "-") == 0) 17013b5b548SBaptiste Daroussin ifd = STDIN_FILENO; 17113b5b548SBaptiste Daroussin else 17213b5b548SBaptiste Daroussin err(2, "error opening %s", source_file); 17313b5b548SBaptiste Daroussin } 17413b5b548SBaptiste Daroussin 17513b5b548SBaptiste Daroussin /* Not a regular file, so copy input into temporary file. */ 17613b5b548SBaptiste Daroussin if (asprintf(&target_file, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) 17713b5b548SBaptiste Daroussin err(2, "asprintf"); 17813b5b548SBaptiste Daroussin if ((ofd = mkstemp(target_file)) == -1) { 17913b5b548SBaptiste Daroussin warn("error opening %s", target_file); 18013b5b548SBaptiste Daroussin goto FAIL; 18113b5b548SBaptiste Daroussin } 18213b5b548SBaptiste Daroussin while ((rcount = read(ifd, buf, sizeof(buf))) != -1 && 18313b5b548SBaptiste Daroussin rcount != 0) { 18413b5b548SBaptiste Daroussin ssize_t wcount; 18513b5b548SBaptiste Daroussin 18613b5b548SBaptiste Daroussin wcount = write(ofd, buf, (size_t)rcount); 18713b5b548SBaptiste Daroussin if (-1 == wcount || rcount != wcount) { 18813b5b548SBaptiste Daroussin warn("error writing to %s", target_file); 18913b5b548SBaptiste Daroussin goto FAIL; 19013b5b548SBaptiste Daroussin } 19113b5b548SBaptiste Daroussin } 19213b5b548SBaptiste Daroussin if (rcount == -1) { 19313b5b548SBaptiste Daroussin warn("error reading from %s", source_file); 19413b5b548SBaptiste Daroussin goto FAIL; 19513b5b548SBaptiste Daroussin } 19613b5b548SBaptiste Daroussin 19713b5b548SBaptiste Daroussin close(ifd); 19813b5b548SBaptiste Daroussin close(ofd); 19913b5b548SBaptiste Daroussin 20013b5b548SBaptiste Daroussin return (target_file); 20113b5b548SBaptiste Daroussin 20213b5b548SBaptiste Daroussin FAIL: 20313b5b548SBaptiste Daroussin unlink(target_file); 20413b5b548SBaptiste Daroussin exit(2); 20513b5b548SBaptiste Daroussin } 20613b5b548SBaptiste Daroussin 20713b5b548SBaptiste Daroussin int 20813b5b548SBaptiste Daroussin main(int argc, char **argv) 20913b5b548SBaptiste Daroussin { 2103cc86989SDag-Erling Smørgrav FILE *diffpipe, *file1, *file2; 2113cc86989SDag-Erling Smørgrav size_t diffargc = 0, flagc = 0, wval = WIDTH; 212ad7bef8bSDag-Erling Smørgrav int ch, fd[2], i, ret, status; 2133cc86989SDag-Erling Smørgrav pid_t pid; 2143cc86989SDag-Erling Smørgrav const char *errstr, *outfile = NULL; 2153cc86989SDag-Erling Smørgrav char **diffargv, *diffprog = diff_path, *flagv; 2163cc86989SDag-Erling Smørgrav char *filename1, *filename2, *tmp1, *tmp2, *s1, *s2; 2172c320002SBaptiste Daroussin char I_arg[] = "-I"; 2182c320002SBaptiste Daroussin char speed_lf[] = "--speed-large-files"; 21913b5b548SBaptiste Daroussin 22013b5b548SBaptiste Daroussin /* 22113b5b548SBaptiste Daroussin * Process diff flags. 22213b5b548SBaptiste Daroussin */ 22313b5b548SBaptiste Daroussin /* 22413b5b548SBaptiste Daroussin * Allocate memory for diff arguments and NULL. 22513b5b548SBaptiste Daroussin * Each flag has at most one argument, so doubling argc gives an 22613b5b548SBaptiste Daroussin * upper limit of how many diff args can be passed. argv[0], 22713b5b548SBaptiste Daroussin * file1, and file2 won't have arguments so doubling them will 22813b5b548SBaptiste Daroussin * waste some memory; however we need an extra space for the 22913b5b548SBaptiste Daroussin * NULL at the end, so it sort of works out. 23013b5b548SBaptiste Daroussin */ 2313cc86989SDag-Erling Smørgrav if ((diffargv = calloc(argc, sizeof(char *) * 2)) == NULL) 2323cc86989SDag-Erling Smørgrav err(2, NULL); 23313b5b548SBaptiste Daroussin 23413b5b548SBaptiste Daroussin /* Add first argument, the program name. */ 23513b5b548SBaptiste Daroussin diffargv[diffargc++] = diffprog; 23613b5b548SBaptiste Daroussin 2373cc86989SDag-Erling Smørgrav /* create a dynamic string for merging single-character options */ 2383cc86989SDag-Erling Smørgrav if ((flagv = malloc(flagc + 2)) == NULL) 2393cc86989SDag-Erling Smørgrav err(2, NULL); 2403cc86989SDag-Erling Smørgrav flagv[flagc] = '-'; 2413cc86989SDag-Erling Smørgrav flagv[flagc + 1] = '\0'; 2423cc86989SDag-Erling Smørgrav diffargv[diffargc++] = flagv; 24313b5b548SBaptiste Daroussin 24413b5b548SBaptiste Daroussin while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:", 24513b5b548SBaptiste Daroussin longopts, NULL)) != -1) { 24613b5b548SBaptiste Daroussin switch (ch) { 24713b5b548SBaptiste Daroussin /* only compatible --long-name-form with diff */ 24813b5b548SBaptiste Daroussin case FCASE_IGNORE_OPT: 24913b5b548SBaptiste Daroussin case FCASE_SENSITIVE_OPT: 25013b5b548SBaptiste Daroussin case STRIPCR_OPT: 25113b5b548SBaptiste Daroussin case 'S': 25213b5b548SBaptiste Daroussin break; 25313b5b548SBaptiste Daroussin /* combine no-arg single switches */ 25413b5b548SBaptiste Daroussin case 'a': 25513b5b548SBaptiste Daroussin case 'B': 25613b5b548SBaptiste Daroussin case 'b': 25713b5b548SBaptiste Daroussin case 'd': 25813b5b548SBaptiste Daroussin case 'E': 25913b5b548SBaptiste Daroussin case 'i': 26013b5b548SBaptiste Daroussin case 'W': 2613cc86989SDag-Erling Smørgrav flagc++; 2623cc86989SDag-Erling Smørgrav flagv = realloc(flagv, flagc + 2); 26313b5b548SBaptiste Daroussin /* 26413b5b548SBaptiste Daroussin * In diff, the 'W' option is 'w' and the 'w' is 'W'. 26513b5b548SBaptiste Daroussin */ 2663cc86989SDag-Erling Smørgrav flagv[flagc] = ch == 'W' ? 'w' : ch; 2673cc86989SDag-Erling Smørgrav flagv[flagc + 1] = '\0'; 26813b5b548SBaptiste Daroussin break; 26985bb63bdSBaptiste Daroussin case 'H': 2702c320002SBaptiste Daroussin diffargv[diffargc++] = speed_lf; 27185bb63bdSBaptiste Daroussin break; 27213b5b548SBaptiste Daroussin case DIFFPROG_OPT: 27313b5b548SBaptiste Daroussin diffargv[0] = diffprog = optarg; 27413b5b548SBaptiste Daroussin break; 27513b5b548SBaptiste Daroussin case 'I': 2763cc86989SDag-Erling Smørgrav Iflag = true; 2772c320002SBaptiste Daroussin diffargv[diffargc++] = I_arg; 27813b5b548SBaptiste Daroussin diffargv[diffargc++] = optarg; 27913b5b548SBaptiste Daroussin break; 28013b5b548SBaptiste Daroussin case 'l': 2813cc86989SDag-Erling Smørgrav lflag = true; 28213b5b548SBaptiste Daroussin break; 28313b5b548SBaptiste Daroussin case 'o': 28413b5b548SBaptiste Daroussin outfile = optarg; 28513b5b548SBaptiste Daroussin break; 28613b5b548SBaptiste Daroussin case 's': 2873cc86989SDag-Erling Smørgrav sflag = true; 28813b5b548SBaptiste Daroussin break; 289a834edfcSDag-Erling Smørgrav case 't': 290a834edfcSDag-Erling Smørgrav tflag = true; 291a834edfcSDag-Erling Smørgrav break; 29213b5b548SBaptiste Daroussin case 'w': 2933cc86989SDag-Erling Smørgrav wval = strtonum(optarg, WIDTH_MIN, 29413b5b548SBaptiste Daroussin INT_MAX, &errstr); 29513b5b548SBaptiste Daroussin if (errstr) 29613b5b548SBaptiste Daroussin errx(2, "width is %s: %s", errstr, optarg); 29713b5b548SBaptiste Daroussin break; 29813b5b548SBaptiste Daroussin case HELP_OPT: 29913b5b548SBaptiste Daroussin for (i = 0; help_msg[i] != NULL; i++) 30013b5b548SBaptiste Daroussin printf("%s\n", help_msg[i]); 30113b5b548SBaptiste Daroussin exit(0); 30213b5b548SBaptiste Daroussin break; 303a834edfcSDag-Erling Smørgrav case TSIZE_OPT: 304a834edfcSDag-Erling Smørgrav tabsize = strtonum(optarg, 1, INT_MAX, &errstr); 305a834edfcSDag-Erling Smørgrav if (errstr) 306a834edfcSDag-Erling Smørgrav errx(2, "tabsize is %s: %s", errstr, optarg); 307a834edfcSDag-Erling Smørgrav break; 30813b5b548SBaptiste Daroussin default: 30913b5b548SBaptiste Daroussin usage(); 31013b5b548SBaptiste Daroussin break; 31113b5b548SBaptiste Daroussin } 31213b5b548SBaptiste Daroussin } 31313b5b548SBaptiste Daroussin 3143cc86989SDag-Erling Smørgrav /* no single-character options were used */ 3153cc86989SDag-Erling Smørgrav if (flagc == 0) { 3163cc86989SDag-Erling Smørgrav memmove(diffargv + 1, diffargv + 2, 3173cc86989SDag-Erling Smørgrav sizeof(char *) * (diffargc - 2)); 31813b5b548SBaptiste Daroussin diffargc--; 3193cc86989SDag-Erling Smørgrav free(flagv); 32013b5b548SBaptiste Daroussin } 32113b5b548SBaptiste Daroussin 32213b5b548SBaptiste Daroussin argc -= optind; 32313b5b548SBaptiste Daroussin argv += optind; 32413b5b548SBaptiste Daroussin 32513b5b548SBaptiste Daroussin if (argc != 2) 32613b5b548SBaptiste Daroussin usage(); 32713b5b548SBaptiste Daroussin 32813b5b548SBaptiste Daroussin if (outfile && (outfp = fopen(outfile, "w")) == NULL) 32913b5b548SBaptiste Daroussin err(2, "could not open: %s", optarg); 33013b5b548SBaptiste Daroussin 33113b5b548SBaptiste Daroussin if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') 33213b5b548SBaptiste Daroussin tmpdir = _PATH_TMP; 33313b5b548SBaptiste Daroussin 33413b5b548SBaptiste Daroussin filename1 = argv[0]; 33513b5b548SBaptiste Daroussin filename2 = argv[1]; 33613b5b548SBaptiste Daroussin 33713b5b548SBaptiste Daroussin /* 33813b5b548SBaptiste Daroussin * Create temporary files for diff and sdiff to share if file1 33913b5b548SBaptiste Daroussin * or file2 are not regular files. This allows sdiff and diff 34013b5b548SBaptiste Daroussin * to read the same inputs if one or both inputs are stdin. 34113b5b548SBaptiste Daroussin * 34213b5b548SBaptiste Daroussin * If any temporary files were created, their names would be 34313b5b548SBaptiste Daroussin * saved in tmp1 or tmp2. tmp1 should never equal tmp2. 34413b5b548SBaptiste Daroussin */ 34513b5b548SBaptiste Daroussin tmp1 = tmp2 = NULL; 34613b5b548SBaptiste Daroussin /* file1 and file2 are the same, so copy to same temp file. */ 34713b5b548SBaptiste Daroussin if (strcmp(filename1, filename2) == 0) { 34813b5b548SBaptiste Daroussin if ((tmp1 = mktmpcpy(filename1))) 34913b5b548SBaptiste Daroussin filename1 = filename2 = tmp1; 35013b5b548SBaptiste Daroussin /* Copy file1 and file2 into separate temp files. */ 35113b5b548SBaptiste Daroussin } else { 35213b5b548SBaptiste Daroussin if ((tmp1 = mktmpcpy(filename1))) 35313b5b548SBaptiste Daroussin filename1 = tmp1; 35413b5b548SBaptiste Daroussin if ((tmp2 = mktmpcpy(filename2))) 35513b5b548SBaptiste Daroussin filename2 = tmp2; 35613b5b548SBaptiste Daroussin } 35713b5b548SBaptiste Daroussin 358ad7bef8bSDag-Erling Smørgrav if ((file1 = fopen(filename1, "r")) == NULL) 359ad7bef8bSDag-Erling Smørgrav err(2, "could not open %s", filename1); 360ad7bef8bSDag-Erling Smørgrav if ((file2 = fopen(filename2, "r")) == NULL) 361ad7bef8bSDag-Erling Smørgrav err(2, "could not open %s", filename2); 362ad7bef8bSDag-Erling Smørgrav if (!istextfile(file1) || !istextfile(file2)) { 363ad7bef8bSDag-Erling Smørgrav ret = bindiff(file1, filename1, file2, filename2); 364ad7bef8bSDag-Erling Smørgrav goto done; 365ad7bef8bSDag-Erling Smørgrav } 366ad7bef8bSDag-Erling Smørgrav 36713b5b548SBaptiste Daroussin diffargv[diffargc++] = filename1; 36813b5b548SBaptiste Daroussin diffargv[diffargc++] = filename2; 36913b5b548SBaptiste Daroussin /* Add NULL to end of array to indicate end of array. */ 37013b5b548SBaptiste Daroussin diffargv[diffargc++] = NULL; 37113b5b548SBaptiste Daroussin 37213b5b548SBaptiste Daroussin /* Subtract column divider and divide by two. */ 3733cc86989SDag-Erling Smørgrav width = (wval - 3) / 2; 37413b5b548SBaptiste Daroussin /* Make sure line_width can fit in size_t. */ 37513b5b548SBaptiste Daroussin if (width > (SIZE_MAX - 3) / 2) 37613b5b548SBaptiste Daroussin errx(2, "width is too large: %zu", width); 37713b5b548SBaptiste Daroussin line_width = width * 2 + 3; 37813b5b548SBaptiste Daroussin 37913b5b548SBaptiste Daroussin if (pipe(fd)) 38013b5b548SBaptiste Daroussin err(2, "pipe"); 38113b5b548SBaptiste Daroussin 3823cc86989SDag-Erling Smørgrav if ((pid = fork()) < 0) 3833cc86989SDag-Erling Smørgrav err(1, "fork()"); 3843cc86989SDag-Erling Smørgrav if (pid == 0) { 38513b5b548SBaptiste Daroussin /* child */ 38613b5b548SBaptiste Daroussin /* We don't read from the pipe. */ 38713b5b548SBaptiste Daroussin close(fd[0]); 3883cc86989SDag-Erling Smørgrav if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) 3893cc86989SDag-Erling Smørgrav _exit(2); 39013b5b548SBaptiste Daroussin /* Free unused descriptor. */ 39113b5b548SBaptiste Daroussin close(fd[1]); 39213b5b548SBaptiste Daroussin execvp(diffprog, diffargv); 3933cc86989SDag-Erling Smørgrav _exit(2); 39413b5b548SBaptiste Daroussin } 39513b5b548SBaptiste Daroussin 39613b5b548SBaptiste Daroussin /* parent */ 39713b5b548SBaptiste Daroussin /* We don't write to the pipe. */ 39813b5b548SBaptiste Daroussin close(fd[1]); 39913b5b548SBaptiste Daroussin 40013b5b548SBaptiste Daroussin /* Open pipe to diff command. */ 40113b5b548SBaptiste Daroussin if ((diffpipe = fdopen(fd[0], "r")) == NULL) 40213b5b548SBaptiste Daroussin err(2, "could not open diff pipe"); 4039dc3843eSBaptiste Daroussin 40413b5b548SBaptiste Daroussin /* Line numbers start at one. */ 40513b5b548SBaptiste Daroussin file1ln = file2ln = 1; 40613b5b548SBaptiste Daroussin 40713b5b548SBaptiste Daroussin /* Read and parse diff output. */ 40813b5b548SBaptiste Daroussin while (parsecmd(diffpipe, file1, file2) != EOF) 40913b5b548SBaptiste Daroussin ; 41013b5b548SBaptiste Daroussin fclose(diffpipe); 41113b5b548SBaptiste Daroussin 41213b5b548SBaptiste Daroussin /* Wait for diff to exit. */ 41313b5b548SBaptiste Daroussin if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || 41413b5b548SBaptiste Daroussin WEXITSTATUS(status) >= 2) 415ad7bef8bSDag-Erling Smørgrav errx(2, "diff exited abnormally"); 416ad7bef8bSDag-Erling Smørgrav ret = WEXITSTATUS(status); 41713b5b548SBaptiste Daroussin 418ad7bef8bSDag-Erling Smørgrav /* No more diffs, so enqueue common lines. */ 41913b5b548SBaptiste Daroussin if (lflag) 42013b5b548SBaptiste Daroussin while ((s1 = xfgets(file1))) 42113b5b548SBaptiste Daroussin enqueue(s1, ' ', NULL); 42213b5b548SBaptiste Daroussin else 42313b5b548SBaptiste Daroussin for (;;) { 42413b5b548SBaptiste Daroussin s1 = xfgets(file1); 42513b5b548SBaptiste Daroussin s2 = xfgets(file2); 42613b5b548SBaptiste Daroussin if (s1 || s2) 42713b5b548SBaptiste Daroussin enqueue(s1, ' ', s2); 42813b5b548SBaptiste Daroussin else 42913b5b548SBaptiste Daroussin break; 43013b5b548SBaptiste Daroussin } 43113b5b548SBaptiste Daroussin fclose(file1); 43213b5b548SBaptiste Daroussin fclose(file2); 43313b5b548SBaptiste Daroussin /* Process unmodified lines. */ 43413b5b548SBaptiste Daroussin processq(); 43513b5b548SBaptiste Daroussin 436ad7bef8bSDag-Erling Smørgrav done: 437ad7bef8bSDag-Erling Smørgrav /* Delete and free unneeded temporary files. */ 438ad7bef8bSDag-Erling Smørgrav if (tmp1 != NULL) { 439ad7bef8bSDag-Erling Smørgrav if (unlink(tmp1) != 0) 440ad7bef8bSDag-Erling Smørgrav warn("failed to delete %s", tmp1); 441ad7bef8bSDag-Erling Smørgrav free(tmp1); 442ad7bef8bSDag-Erling Smørgrav } 443ad7bef8bSDag-Erling Smørgrav if (tmp2 != NULL) { 444ad7bef8bSDag-Erling Smørgrav if (unlink(tmp2) != 0) 445ad7bef8bSDag-Erling Smørgrav warn("failed to delete %s", tmp2); 446ad7bef8bSDag-Erling Smørgrav free(tmp2); 447ad7bef8bSDag-Erling Smørgrav } 448ad7bef8bSDag-Erling Smørgrav 44913b5b548SBaptiste Daroussin /* Return diff exit status. */ 4503cc86989SDag-Erling Smørgrav free(diffargv); 4513cc86989SDag-Erling Smørgrav if (flagc > 0) 4523cc86989SDag-Erling Smørgrav free(flagv); 453ad7bef8bSDag-Erling Smørgrav return (ret); 45413b5b548SBaptiste Daroussin } 45513b5b548SBaptiste Daroussin 45613b5b548SBaptiste Daroussin /* 457ad7bef8bSDag-Erling Smørgrav * When sdiff detects a binary file as input. 45813b5b548SBaptiste Daroussin */ 459ad7bef8bSDag-Erling Smørgrav static int 460ad7bef8bSDag-Erling Smørgrav bindiff(FILE *f1, char *fn1, FILE *f2, char *fn2) 46113b5b548SBaptiste Daroussin { 462ad7bef8bSDag-Erling Smørgrav int ch1, ch2; 46313b5b548SBaptiste Daroussin 464ad7bef8bSDag-Erling Smørgrav flockfile(f1); 465ad7bef8bSDag-Erling Smørgrav flockfile(f2); 466ad7bef8bSDag-Erling Smørgrav do { 467ad7bef8bSDag-Erling Smørgrav ch1 = getc_unlocked(f1); 468ad7bef8bSDag-Erling Smørgrav ch2 = getc_unlocked(f2); 469ad7bef8bSDag-Erling Smørgrav } while (ch1 != EOF && ch2 != EOF && ch1 == ch2); 470ad7bef8bSDag-Erling Smørgrav funlockfile(f2); 471ad7bef8bSDag-Erling Smørgrav funlockfile(f1); 472ad7bef8bSDag-Erling Smørgrav if (ferror(f1)) { 473ad7bef8bSDag-Erling Smørgrav warn("%s", fn1); 474ad7bef8bSDag-Erling Smørgrav return (2); 475ad7bef8bSDag-Erling Smørgrav } 476ad7bef8bSDag-Erling Smørgrav if (ferror(f2)) { 477ad7bef8bSDag-Erling Smørgrav warn("%s", fn2); 478ad7bef8bSDag-Erling Smørgrav return (2); 479ad7bef8bSDag-Erling Smørgrav } 480ad7bef8bSDag-Erling Smørgrav if (ch1 != EOF || ch2 != EOF) { 481ad7bef8bSDag-Erling Smørgrav printf("Binary files %s and %s differ\n", fn1, fn2); 482ad7bef8bSDag-Erling Smørgrav return (1); 483ad7bef8bSDag-Erling Smørgrav } 484ad7bef8bSDag-Erling Smørgrav return (0); 48513b5b548SBaptiste Daroussin } 48613b5b548SBaptiste Daroussin 48713b5b548SBaptiste Daroussin /* 48813b5b548SBaptiste Daroussin * Checks whether a file appears to be a text file. 48913b5b548SBaptiste Daroussin */ 49013b5b548SBaptiste Daroussin static int 49113b5b548SBaptiste Daroussin istextfile(FILE *f) 49213b5b548SBaptiste Daroussin { 493e29c5529SBjoern A. Zeeb int ch, i; 49413b5b548SBaptiste Daroussin 49513b5b548SBaptiste Daroussin if (f == NULL) 49613b5b548SBaptiste Daroussin return (1); 49713b5b548SBaptiste Daroussin rewind(f); 498da5c2c42SBaptiste Daroussin for (i = 0; i <= MAX_CHECK; i++) { 49913b5b548SBaptiste Daroussin ch = fgetc(f); 50013b5b548SBaptiste Daroussin if (ch == '\0') { 50113b5b548SBaptiste Daroussin rewind(f); 50213b5b548SBaptiste Daroussin return (0); 50313b5b548SBaptiste Daroussin } 504da5c2c42SBaptiste Daroussin if (ch == EOF) 505da5c2c42SBaptiste Daroussin break; 50613b5b548SBaptiste Daroussin } 50713b5b548SBaptiste Daroussin rewind(f); 50813b5b548SBaptiste Daroussin return (1); 50913b5b548SBaptiste Daroussin } 51013b5b548SBaptiste Daroussin 51113b5b548SBaptiste Daroussin /* 51213b5b548SBaptiste Daroussin * Prints an individual column (left or right), taking into account 51313b5b548SBaptiste Daroussin * that tabs are variable-width. Takes a string, the current column 51413b5b548SBaptiste Daroussin * the cursor is on the screen, and the maximum value of the column. 51513b5b548SBaptiste Daroussin * The column value is updated as we go along. 51613b5b548SBaptiste Daroussin */ 51713b5b548SBaptiste Daroussin static void 51813b5b548SBaptiste Daroussin printcol(const char *s, size_t *col, const size_t col_max) 51913b5b548SBaptiste Daroussin { 52013b5b548SBaptiste Daroussin 52113b5b548SBaptiste Daroussin for (; *s && *col < col_max; ++s) { 52213b5b548SBaptiste Daroussin size_t new_col; 52313b5b548SBaptiste Daroussin 52413b5b548SBaptiste Daroussin switch (*s) { 52513b5b548SBaptiste Daroussin case '\t': 52613b5b548SBaptiste Daroussin /* 52713b5b548SBaptiste Daroussin * If rounding to next multiple of eight causes 52813b5b548SBaptiste Daroussin * an integer overflow, just return. 52913b5b548SBaptiste Daroussin */ 530a834edfcSDag-Erling Smørgrav if (*col > SIZE_MAX - tabsize) 53113b5b548SBaptiste Daroussin return; 53213b5b548SBaptiste Daroussin 53313b5b548SBaptiste Daroussin /* Round to next multiple of eight. */ 534a834edfcSDag-Erling Smørgrav new_col = (*col / tabsize + 1) * tabsize; 53513b5b548SBaptiste Daroussin 53613b5b548SBaptiste Daroussin /* 53713b5b548SBaptiste Daroussin * If printing the tab goes past the column 53813b5b548SBaptiste Daroussin * width, don't print it and just quit. 53913b5b548SBaptiste Daroussin */ 54013b5b548SBaptiste Daroussin if (new_col > col_max) 54113b5b548SBaptiste Daroussin return; 542a834edfcSDag-Erling Smørgrav 543a834edfcSDag-Erling Smørgrav if (tflag) { 544a834edfcSDag-Erling Smørgrav do { 545a834edfcSDag-Erling Smørgrav putchar(' '); 546a834edfcSDag-Erling Smørgrav } while (++*col < new_col); 547a834edfcSDag-Erling Smørgrav } else { 548a834edfcSDag-Erling Smørgrav putchar(*s); 54913b5b548SBaptiste Daroussin *col = new_col; 550a834edfcSDag-Erling Smørgrav } 55113b5b548SBaptiste Daroussin break; 55213b5b548SBaptiste Daroussin default: 553a834edfcSDag-Erling Smørgrav ++*col; 55413b5b548SBaptiste Daroussin putchar(*s); 55513b5b548SBaptiste Daroussin } 55613b5b548SBaptiste Daroussin } 557a834edfcSDag-Erling Smørgrav } 55813b5b548SBaptiste Daroussin 55913b5b548SBaptiste Daroussin /* 56013b5b548SBaptiste Daroussin * Prompts user to either choose between two strings or edit one, both, 56113b5b548SBaptiste Daroussin * or neither. 56213b5b548SBaptiste Daroussin */ 56313b5b548SBaptiste Daroussin static void 56413b5b548SBaptiste Daroussin prompt(const char *s1, const char *s2) 56513b5b548SBaptiste Daroussin { 56613b5b548SBaptiste Daroussin char *cmd; 56713b5b548SBaptiste Daroussin 56813b5b548SBaptiste Daroussin /* Print command prompt. */ 56913b5b548SBaptiste Daroussin putchar('%'); 57013b5b548SBaptiste Daroussin 57113b5b548SBaptiste Daroussin /* Get user input. */ 57213b5b548SBaptiste Daroussin for (; (cmd = xfgets(stdin)); free(cmd)) { 57313b5b548SBaptiste Daroussin const char *p; 57413b5b548SBaptiste Daroussin 57513b5b548SBaptiste Daroussin /* Skip leading whitespace. */ 5763cc86989SDag-Erling Smørgrav for (p = cmd; isspace((unsigned char)*p); ++p) 57713b5b548SBaptiste Daroussin ; 57813b5b548SBaptiste Daroussin switch (*p) { 57913b5b548SBaptiste Daroussin case 'e': 58013b5b548SBaptiste Daroussin /* Skip `e'. */ 58113b5b548SBaptiste Daroussin ++p; 58213b5b548SBaptiste Daroussin if (eparse(p, s1, s2) == -1) 58313b5b548SBaptiste Daroussin goto USAGE; 58413b5b548SBaptiste Daroussin break; 58513b5b548SBaptiste Daroussin case 'l': 58613b5b548SBaptiste Daroussin case '1': 58713b5b548SBaptiste Daroussin /* Choose left column as-is. */ 58813b5b548SBaptiste Daroussin if (s1 != NULL) 58913b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", s1); 59013b5b548SBaptiste Daroussin /* End of command parsing. */ 59113b5b548SBaptiste Daroussin break; 59213b5b548SBaptiste Daroussin case 'q': 59313b5b548SBaptiste Daroussin goto QUIT; 59413b5b548SBaptiste Daroussin case 'r': 59513b5b548SBaptiste Daroussin case '2': 59613b5b548SBaptiste Daroussin /* Choose right column as-is. */ 59713b5b548SBaptiste Daroussin if (s2 != NULL) 59813b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", s2); 59913b5b548SBaptiste Daroussin /* End of command parsing. */ 60013b5b548SBaptiste Daroussin break; 60113b5b548SBaptiste Daroussin case 's': 6023cc86989SDag-Erling Smørgrav sflag = true; 60313b5b548SBaptiste Daroussin goto PROMPT; 60413b5b548SBaptiste Daroussin case 'v': 6053cc86989SDag-Erling Smørgrav sflag = false; 60613b5b548SBaptiste Daroussin /* FALLTHROUGH */ 60713b5b548SBaptiste Daroussin default: 60813b5b548SBaptiste Daroussin /* Interactive usage help. */ 60913b5b548SBaptiste Daroussin USAGE: 61013b5b548SBaptiste Daroussin int_usage(); 61113b5b548SBaptiste Daroussin PROMPT: 61213b5b548SBaptiste Daroussin putchar('%'); 61313b5b548SBaptiste Daroussin 61413b5b548SBaptiste Daroussin /* Prompt user again. */ 61513b5b548SBaptiste Daroussin continue; 61613b5b548SBaptiste Daroussin } 61713b5b548SBaptiste Daroussin free(cmd); 61813b5b548SBaptiste Daroussin return; 61913b5b548SBaptiste Daroussin } 62013b5b548SBaptiste Daroussin 62113b5b548SBaptiste Daroussin /* 62213b5b548SBaptiste Daroussin * If there was no error, we received an EOF from stdin, so we 62313b5b548SBaptiste Daroussin * should quit. 62413b5b548SBaptiste Daroussin */ 62513b5b548SBaptiste Daroussin QUIT: 62613b5b548SBaptiste Daroussin fclose(outfp); 62713b5b548SBaptiste Daroussin exit(0); 62813b5b548SBaptiste Daroussin } 62913b5b548SBaptiste Daroussin 63013b5b548SBaptiste Daroussin /* 63113b5b548SBaptiste Daroussin * Takes two strings, separated by a column divider. NULL strings are 63213b5b548SBaptiste Daroussin * treated as empty columns. If the divider is the ` ' character, the 63313b5b548SBaptiste Daroussin * second column is not printed (-l flag). In this case, the second 63413b5b548SBaptiste Daroussin * string must be NULL. When the second column is NULL, the divider 63513b5b548SBaptiste Daroussin * does not print the trailing space following the divider character. 63613b5b548SBaptiste Daroussin * 63713b5b548SBaptiste Daroussin * Takes into account that tabs can take multiple columns. 63813b5b548SBaptiste Daroussin */ 63913b5b548SBaptiste Daroussin static void 6400ace12afSBaptiste Daroussin println(const char *s1, const char divider, const char *s2) 64113b5b548SBaptiste Daroussin { 64213b5b548SBaptiste Daroussin size_t col; 64313b5b548SBaptiste Daroussin 64413b5b548SBaptiste Daroussin /* Print first column. Skips if s1 == NULL. */ 64513b5b548SBaptiste Daroussin col = 0; 64613b5b548SBaptiste Daroussin if (s1) { 64713b5b548SBaptiste Daroussin /* Skip angle bracket and space. */ 64813b5b548SBaptiste Daroussin printcol(s1, &col, width); 64913b5b548SBaptiste Daroussin 65013b5b548SBaptiste Daroussin } 65113b5b548SBaptiste Daroussin 65213b5b548SBaptiste Daroussin /* Otherwise, we pad this column up to width. */ 65313b5b548SBaptiste Daroussin for (; col < width; ++col) 65413b5b548SBaptiste Daroussin putchar(' '); 65513b5b548SBaptiste Daroussin 65613b5b548SBaptiste Daroussin /* Only print left column. */ 6570ace12afSBaptiste Daroussin if (divider == ' ' && !s2) { 65813b5b548SBaptiste Daroussin printf(" (\n"); 65913b5b548SBaptiste Daroussin return; 66013b5b548SBaptiste Daroussin } 66113b5b548SBaptiste Daroussin 66213b5b548SBaptiste Daroussin /* 66313b5b548SBaptiste Daroussin * Print column divider. If there is no second column, we don't 66413b5b548SBaptiste Daroussin * need to add the space for padding. 66513b5b548SBaptiste Daroussin */ 66613b5b548SBaptiste Daroussin if (!s2) { 6670ace12afSBaptiste Daroussin printf(" %c\n", divider); 66813b5b548SBaptiste Daroussin return; 66913b5b548SBaptiste Daroussin } 6700ace12afSBaptiste Daroussin printf(" %c ", divider); 67113b5b548SBaptiste Daroussin col += 3; 67213b5b548SBaptiste Daroussin 67313b5b548SBaptiste Daroussin /* Skip angle bracket and space. */ 67413b5b548SBaptiste Daroussin printcol(s2, &col, line_width); 67513b5b548SBaptiste Daroussin 67613b5b548SBaptiste Daroussin putchar('\n'); 67713b5b548SBaptiste Daroussin } 67813b5b548SBaptiste Daroussin 67913b5b548SBaptiste Daroussin /* 68013b5b548SBaptiste Daroussin * Reads a line from file and returns as a string. If EOF is reached, 68113b5b548SBaptiste Daroussin * NULL is returned. The returned string must be freed afterwards. 68213b5b548SBaptiste Daroussin */ 68313b5b548SBaptiste Daroussin static char * 68413b5b548SBaptiste Daroussin xfgets(FILE *file) 68513b5b548SBaptiste Daroussin { 6860c4ac56eSBaptiste Daroussin size_t linecap; 6870c4ac56eSBaptiste Daroussin ssize_t l; 68813b5b548SBaptiste Daroussin char *s; 68913b5b548SBaptiste Daroussin 69013b5b548SBaptiste Daroussin clearerr(file); 6910c4ac56eSBaptiste Daroussin linecap = 0; 6920c4ac56eSBaptiste Daroussin s = NULL; 69313b5b548SBaptiste Daroussin 6940c4ac56eSBaptiste Daroussin if ((l = getline(&s, &linecap, file)) == -1) { 6950c4ac56eSBaptiste Daroussin if (ferror(file)) 69613b5b548SBaptiste Daroussin err(2, "error reading file"); 69713b5b548SBaptiste Daroussin return (NULL); 69813b5b548SBaptiste Daroussin } 69913b5b548SBaptiste Daroussin 7000c4ac56eSBaptiste Daroussin if (s[l-1] == '\n') 7010c4ac56eSBaptiste Daroussin s[l-1] = '\0'; 7020c4ac56eSBaptiste Daroussin 70313b5b548SBaptiste Daroussin return (s); 70413b5b548SBaptiste Daroussin } 70513b5b548SBaptiste Daroussin 70613b5b548SBaptiste Daroussin /* 70713b5b548SBaptiste Daroussin * Parse ed commands from diffpipe and print lines from file1 (lines 70813b5b548SBaptiste Daroussin * to change or delete) or file2 (lines to add or change). 70913b5b548SBaptiste Daroussin * Returns EOF or 0. 71013b5b548SBaptiste Daroussin */ 71113b5b548SBaptiste Daroussin static int 71213b5b548SBaptiste Daroussin parsecmd(FILE *diffpipe, FILE *file1, FILE *file2) 71313b5b548SBaptiste Daroussin { 71413b5b548SBaptiste Daroussin size_t file1start, file1end, file2start, file2end, n; 71513b5b548SBaptiste Daroussin /* ed command line and pointer to characters in line */ 71613b5b548SBaptiste Daroussin char *line, *p, *q; 71713b5b548SBaptiste Daroussin const char *errstr; 71813b5b548SBaptiste Daroussin char c, cmd; 71913b5b548SBaptiste Daroussin 72013b5b548SBaptiste Daroussin /* Read ed command. */ 72113b5b548SBaptiste Daroussin if (!(line = xfgets(diffpipe))) 72213b5b548SBaptiste Daroussin return (EOF); 72313b5b548SBaptiste Daroussin 72413b5b548SBaptiste Daroussin p = line; 72513b5b548SBaptiste Daroussin /* Go to character after line number. */ 7263cc86989SDag-Erling Smørgrav while (isdigit((unsigned char)*p)) 72713b5b548SBaptiste Daroussin ++p; 72813b5b548SBaptiste Daroussin c = *p; 72913b5b548SBaptiste Daroussin *p++ = 0; 73013b5b548SBaptiste Daroussin file1start = strtonum(line, 0, INT_MAX, &errstr); 73113b5b548SBaptiste Daroussin if (errstr) 73213b5b548SBaptiste Daroussin errx(2, "file1 start is %s: %s", errstr, line); 73313b5b548SBaptiste Daroussin 73413b5b548SBaptiste Daroussin /* A range is specified for file1. */ 73513b5b548SBaptiste Daroussin if (c == ',') { 73613b5b548SBaptiste Daroussin q = p; 73713b5b548SBaptiste Daroussin /* Go to character after file2end. */ 7383cc86989SDag-Erling Smørgrav while (isdigit((unsigned char)*p)) 73913b5b548SBaptiste Daroussin ++p; 74013b5b548SBaptiste Daroussin c = *p; 74113b5b548SBaptiste Daroussin *p++ = 0; 74213b5b548SBaptiste Daroussin file1end = strtonum(q, 0, INT_MAX, &errstr); 74313b5b548SBaptiste Daroussin if (errstr) 74413b5b548SBaptiste Daroussin errx(2, "file1 end is %s: %s", errstr, line); 74513b5b548SBaptiste Daroussin if (file1start > file1end) 74613b5b548SBaptiste Daroussin errx(2, "invalid line range in file1: %s", line); 74713b5b548SBaptiste Daroussin } else 74813b5b548SBaptiste Daroussin file1end = file1start; 74913b5b548SBaptiste Daroussin 75013b5b548SBaptiste Daroussin cmd = c; 75113b5b548SBaptiste Daroussin /* Check that cmd is valid. */ 75213b5b548SBaptiste Daroussin if (!(cmd == 'a' || cmd == 'c' || cmd == 'd')) 75313b5b548SBaptiste Daroussin errx(2, "ed command not recognized: %c: %s", cmd, line); 75413b5b548SBaptiste Daroussin 75513b5b548SBaptiste Daroussin q = p; 75613b5b548SBaptiste Daroussin /* Go to character after line number. */ 7573cc86989SDag-Erling Smørgrav while (isdigit((unsigned char)*p)) 75813b5b548SBaptiste Daroussin ++p; 75913b5b548SBaptiste Daroussin c = *p; 76013b5b548SBaptiste Daroussin *p++ = 0; 76113b5b548SBaptiste Daroussin file2start = strtonum(q, 0, INT_MAX, &errstr); 76213b5b548SBaptiste Daroussin if (errstr) 76313b5b548SBaptiste Daroussin errx(2, "file2 start is %s: %s", errstr, line); 76413b5b548SBaptiste Daroussin 76513b5b548SBaptiste Daroussin /* 76613b5b548SBaptiste Daroussin * There should either be a comma signifying a second line 76713b5b548SBaptiste Daroussin * number or the line should just end here. 76813b5b548SBaptiste Daroussin */ 76913b5b548SBaptiste Daroussin if (c != ',' && c != '\0') 77013b5b548SBaptiste Daroussin errx(2, "invalid line range in file2: %c: %s", c, line); 77113b5b548SBaptiste Daroussin 77213b5b548SBaptiste Daroussin if (c == ',') { 77313b5b548SBaptiste Daroussin 77413b5b548SBaptiste Daroussin file2end = strtonum(p, 0, INT_MAX, &errstr); 77513b5b548SBaptiste Daroussin if (errstr) 77613b5b548SBaptiste Daroussin errx(2, "file2 end is %s: %s", errstr, line); 77713b5b548SBaptiste Daroussin if (file2start >= file2end) 77813b5b548SBaptiste Daroussin errx(2, "invalid line range in file2: %s", line); 77913b5b548SBaptiste Daroussin } else 78013b5b548SBaptiste Daroussin file2end = file2start; 78113b5b548SBaptiste Daroussin 78213b5b548SBaptiste Daroussin /* Appends happen _after_ stated line. */ 78313b5b548SBaptiste Daroussin if (cmd == 'a') { 78413b5b548SBaptiste Daroussin if (file1start != file1end) 78513b5b548SBaptiste Daroussin errx(2, "append cannot have a file1 range: %s", 78613b5b548SBaptiste Daroussin line); 78713b5b548SBaptiste Daroussin if (file1start == SIZE_MAX) 78813b5b548SBaptiste Daroussin errx(2, "file1 line range too high: %s", line); 78913b5b548SBaptiste Daroussin file1start = ++file1end; 79013b5b548SBaptiste Daroussin } 79113b5b548SBaptiste Daroussin /* 79213b5b548SBaptiste Daroussin * I'm not sure what the deal is with the line numbers for 79313b5b548SBaptiste Daroussin * deletes, though. 79413b5b548SBaptiste Daroussin */ 79513b5b548SBaptiste Daroussin else if (cmd == 'd') { 79613b5b548SBaptiste Daroussin if (file2start != file2end) 79713b5b548SBaptiste Daroussin errx(2, "delete cannot have a file2 range: %s", 79813b5b548SBaptiste Daroussin line); 79913b5b548SBaptiste Daroussin if (file2start == SIZE_MAX) 80013b5b548SBaptiste Daroussin errx(2, "file2 line range too high: %s", line); 80113b5b548SBaptiste Daroussin file2start = ++file2end; 80213b5b548SBaptiste Daroussin } 80313b5b548SBaptiste Daroussin 80413b5b548SBaptiste Daroussin /* 80513b5b548SBaptiste Daroussin * Continue reading file1 and file2 until we reach line numbers 80613b5b548SBaptiste Daroussin * specified by diff. Should only happen with -I flag. 80713b5b548SBaptiste Daroussin */ 80813b5b548SBaptiste Daroussin for (; file1ln < file1start && file2ln < file2start; 80913b5b548SBaptiste Daroussin ++file1ln, ++file2ln) { 81013b5b548SBaptiste Daroussin char *s1, *s2; 81113b5b548SBaptiste Daroussin 81213b5b548SBaptiste Daroussin if (!(s1 = xfgets(file1))) 81313b5b548SBaptiste Daroussin errx(2, "file1 shorter than expected"); 81413b5b548SBaptiste Daroussin if (!(s2 = xfgets(file2))) 81513b5b548SBaptiste Daroussin errx(2, "file2 shorter than expected"); 81613b5b548SBaptiste Daroussin 81713b5b548SBaptiste Daroussin /* If the -l flag was specified, print only left column. */ 81813b5b548SBaptiste Daroussin if (lflag) { 81913b5b548SBaptiste Daroussin free(s2); 82013b5b548SBaptiste Daroussin /* 82113b5b548SBaptiste Daroussin * XXX - If -l and -I are both specified, all 82213b5b548SBaptiste Daroussin * unchanged or ignored lines are shown with a 82313b5b548SBaptiste Daroussin * `(' divider. This matches GNU sdiff, but I 82413b5b548SBaptiste Daroussin * believe it is a bug. Just check out: 82513b5b548SBaptiste Daroussin * gsdiff -l -I '^$' samefile samefile. 82613b5b548SBaptiste Daroussin */ 82713b5b548SBaptiste Daroussin if (Iflag) 82813b5b548SBaptiste Daroussin enqueue(s1, '(', NULL); 82913b5b548SBaptiste Daroussin else 83013b5b548SBaptiste Daroussin enqueue(s1, ' ', NULL); 83113b5b548SBaptiste Daroussin } else 83213b5b548SBaptiste Daroussin enqueue(s1, ' ', s2); 83313b5b548SBaptiste Daroussin } 83413b5b548SBaptiste Daroussin /* Ignore deleted lines. */ 83513b5b548SBaptiste Daroussin for (; file1ln < file1start; ++file1ln) { 83613b5b548SBaptiste Daroussin char *s; 83713b5b548SBaptiste Daroussin 83813b5b548SBaptiste Daroussin if (!(s = xfgets(file1))) 83913b5b548SBaptiste Daroussin errx(2, "file1 shorter than expected"); 84013b5b548SBaptiste Daroussin 84113b5b548SBaptiste Daroussin enqueue(s, '(', NULL); 84213b5b548SBaptiste Daroussin } 84313b5b548SBaptiste Daroussin /* Ignore added lines. */ 84413b5b548SBaptiste Daroussin for (; file2ln < file2start; ++file2ln) { 84513b5b548SBaptiste Daroussin char *s; 84613b5b548SBaptiste Daroussin 84713b5b548SBaptiste Daroussin if (!(s = xfgets(file2))) 84813b5b548SBaptiste Daroussin errx(2, "file2 shorter than expected"); 84913b5b548SBaptiste Daroussin 85013b5b548SBaptiste Daroussin /* If -l flag was given, don't print right column. */ 85113b5b548SBaptiste Daroussin if (lflag) 85213b5b548SBaptiste Daroussin free(s); 85313b5b548SBaptiste Daroussin else 85413b5b548SBaptiste Daroussin enqueue(NULL, ')', s); 85513b5b548SBaptiste Daroussin } 85613b5b548SBaptiste Daroussin 85713b5b548SBaptiste Daroussin /* Process unmodified or skipped lines. */ 85813b5b548SBaptiste Daroussin processq(); 85913b5b548SBaptiste Daroussin 86013b5b548SBaptiste Daroussin switch (cmd) { 86113b5b548SBaptiste Daroussin case 'a': 86213b5b548SBaptiste Daroussin printa(file2, file2end); 86313b5b548SBaptiste Daroussin n = file2end - file2start + 1; 86413b5b548SBaptiste Daroussin break; 86513b5b548SBaptiste Daroussin case 'c': 86613b5b548SBaptiste Daroussin printc(file1, file1end, file2, file2end); 86713b5b548SBaptiste Daroussin n = file1end - file1start + 1 + 1 + file2end - file2start + 1; 86813b5b548SBaptiste Daroussin break; 86913b5b548SBaptiste Daroussin case 'd': 87013b5b548SBaptiste Daroussin printd(file1, file1end); 87113b5b548SBaptiste Daroussin n = file1end - file1start + 1; 87213b5b548SBaptiste Daroussin break; 87313b5b548SBaptiste Daroussin default: 87413b5b548SBaptiste Daroussin errx(2, "invalid diff command: %c: %s", cmd, line); 87513b5b548SBaptiste Daroussin } 87613b5b548SBaptiste Daroussin free(line); 87713b5b548SBaptiste Daroussin 87813b5b548SBaptiste Daroussin /* Skip to next ed line. */ 87913b5b548SBaptiste Daroussin while (n--) { 88013b5b548SBaptiste Daroussin if (!(line = xfgets(diffpipe))) 88113b5b548SBaptiste Daroussin errx(2, "diff ended early"); 88213b5b548SBaptiste Daroussin free(line); 88313b5b548SBaptiste Daroussin } 88413b5b548SBaptiste Daroussin 88513b5b548SBaptiste Daroussin return (0); 88613b5b548SBaptiste Daroussin } 88713b5b548SBaptiste Daroussin 88813b5b548SBaptiste Daroussin /* 88913b5b548SBaptiste Daroussin * Queues up a diff line. 89013b5b548SBaptiste Daroussin */ 89113b5b548SBaptiste Daroussin static void 8920ace12afSBaptiste Daroussin enqueue(char *left, char divider, char *right) 89313b5b548SBaptiste Daroussin { 89413b5b548SBaptiste Daroussin struct diffline *diffp; 89513b5b548SBaptiste Daroussin 89613b5b548SBaptiste Daroussin if (!(diffp = malloc(sizeof(struct diffline)))) 89713b5b548SBaptiste Daroussin err(2, "enqueue"); 89813b5b548SBaptiste Daroussin diffp->left = left; 8990ace12afSBaptiste Daroussin diffp->div = divider; 90013b5b548SBaptiste Daroussin diffp->right = right; 90113b5b548SBaptiste Daroussin STAILQ_INSERT_TAIL(&diffhead, diffp, diffentries); 90213b5b548SBaptiste Daroussin } 90313b5b548SBaptiste Daroussin 90413b5b548SBaptiste Daroussin /* 90513b5b548SBaptiste Daroussin * Free a diffline structure and its elements. 90613b5b548SBaptiste Daroussin */ 90713b5b548SBaptiste Daroussin static void 90813b5b548SBaptiste Daroussin freediff(struct diffline *diffp) 90913b5b548SBaptiste Daroussin { 91013b5b548SBaptiste Daroussin 91113b5b548SBaptiste Daroussin free(diffp->left); 91213b5b548SBaptiste Daroussin free(diffp->right); 91313b5b548SBaptiste Daroussin free(diffp); 91413b5b548SBaptiste Daroussin } 91513b5b548SBaptiste Daroussin 91613b5b548SBaptiste Daroussin /* 91713b5b548SBaptiste Daroussin * Append second string into first. Repeated appends to the same string 91813b5b548SBaptiste Daroussin * are cached, making this an O(n) function, where n = strlen(append). 91913b5b548SBaptiste Daroussin */ 92013b5b548SBaptiste Daroussin static void 92113b5b548SBaptiste Daroussin astrcat(char **s, const char *append) 92213b5b548SBaptiste Daroussin { 92313b5b548SBaptiste Daroussin /* Length of string in previous run. */ 92413b5b548SBaptiste Daroussin static size_t offset = 0; 92513b5b548SBaptiste Daroussin size_t newsiz; 92613b5b548SBaptiste Daroussin /* 92713b5b548SBaptiste Daroussin * String from previous run. Compared to *s to see if we are 92813b5b548SBaptiste Daroussin * dealing with the same string. If so, we can use offset. 92913b5b548SBaptiste Daroussin */ 93013b5b548SBaptiste Daroussin static const char *oldstr = NULL; 93113b5b548SBaptiste Daroussin char *newstr; 93213b5b548SBaptiste Daroussin 93313b5b548SBaptiste Daroussin /* 93413b5b548SBaptiste Daroussin * First string is NULL, so just copy append. 93513b5b548SBaptiste Daroussin */ 93613b5b548SBaptiste Daroussin if (!*s) { 93713b5b548SBaptiste Daroussin if (!(*s = strdup(append))) 93813b5b548SBaptiste Daroussin err(2, "astrcat"); 93913b5b548SBaptiste Daroussin 94013b5b548SBaptiste Daroussin /* Keep track of string. */ 94113b5b548SBaptiste Daroussin offset = strlen(*s); 94213b5b548SBaptiste Daroussin oldstr = *s; 94313b5b548SBaptiste Daroussin 94413b5b548SBaptiste Daroussin return; 94513b5b548SBaptiste Daroussin } 94613b5b548SBaptiste Daroussin 94713b5b548SBaptiste Daroussin /* 94813b5b548SBaptiste Daroussin * *s is a string so concatenate. 94913b5b548SBaptiste Daroussin */ 95013b5b548SBaptiste Daroussin 95113b5b548SBaptiste Daroussin /* Did we process the same string in the last run? */ 95213b5b548SBaptiste Daroussin /* 95313b5b548SBaptiste Daroussin * If this is a different string from the one we just processed 95413b5b548SBaptiste Daroussin * cache new string. 95513b5b548SBaptiste Daroussin */ 95613b5b548SBaptiste Daroussin if (oldstr != *s) { 95713b5b548SBaptiste Daroussin offset = strlen(*s); 95813b5b548SBaptiste Daroussin oldstr = *s; 95913b5b548SBaptiste Daroussin } 96013b5b548SBaptiste Daroussin 96113b5b548SBaptiste Daroussin /* Size = strlen(*s) + \n + strlen(append) + '\0'. */ 96213b5b548SBaptiste Daroussin newsiz = offset + 1 + strlen(append) + 1; 96313b5b548SBaptiste Daroussin 96413b5b548SBaptiste Daroussin /* Resize *s to fit new string. */ 96513b5b548SBaptiste Daroussin newstr = realloc(*s, newsiz); 96613b5b548SBaptiste Daroussin if (newstr == NULL) 96713b5b548SBaptiste Daroussin err(2, "astrcat"); 96813b5b548SBaptiste Daroussin *s = newstr; 96913b5b548SBaptiste Daroussin 97013b5b548SBaptiste Daroussin /* *s + offset should be end of string. */ 97113b5b548SBaptiste Daroussin /* Concatenate. */ 97213b5b548SBaptiste Daroussin strlcpy(*s + offset, "\n", newsiz - offset); 97313b5b548SBaptiste Daroussin strlcat(*s + offset, append, newsiz - offset); 97413b5b548SBaptiste Daroussin 97513b5b548SBaptiste Daroussin /* New string length should be exactly newsiz - 1 characters. */ 97613b5b548SBaptiste Daroussin /* Store generated string's values. */ 97713b5b548SBaptiste Daroussin offset = newsiz - 1; 97813b5b548SBaptiste Daroussin oldstr = *s; 97913b5b548SBaptiste Daroussin } 98013b5b548SBaptiste Daroussin 98113b5b548SBaptiste Daroussin /* 98213b5b548SBaptiste Daroussin * Process diff set queue, printing, prompting, and saving each diff 98313b5b548SBaptiste Daroussin * line stored in queue. 98413b5b548SBaptiste Daroussin */ 98513b5b548SBaptiste Daroussin static void 98613b5b548SBaptiste Daroussin processq(void) 98713b5b548SBaptiste Daroussin { 98813b5b548SBaptiste Daroussin struct diffline *diffp; 98913b5b548SBaptiste Daroussin char divc, *left, *right; 99013b5b548SBaptiste Daroussin 99113b5b548SBaptiste Daroussin /* Don't process empty queue. */ 99213b5b548SBaptiste Daroussin if (STAILQ_EMPTY(&diffhead)) 99313b5b548SBaptiste Daroussin return; 99413b5b548SBaptiste Daroussin 99513b5b548SBaptiste Daroussin /* Remember the divider. */ 99613b5b548SBaptiste Daroussin divc = STAILQ_FIRST(&diffhead)->div; 99713b5b548SBaptiste Daroussin 99813b5b548SBaptiste Daroussin left = NULL; 99913b5b548SBaptiste Daroussin right = NULL; 100013b5b548SBaptiste Daroussin /* 100113b5b548SBaptiste Daroussin * Go through set of diffs, concatenating each line in left or 100213b5b548SBaptiste Daroussin * right column into two long strings, `left' and `right'. 100313b5b548SBaptiste Daroussin */ 100413b5b548SBaptiste Daroussin STAILQ_FOREACH(diffp, &diffhead, diffentries) { 100513b5b548SBaptiste Daroussin /* 100613b5b548SBaptiste Daroussin * Print changed lines if -s was given, 100713b5b548SBaptiste Daroussin * print all lines if -s was not given. 100813b5b548SBaptiste Daroussin */ 100913b5b548SBaptiste Daroussin if (!sflag || diffp->div == '|' || diffp->div == '<' || 101013b5b548SBaptiste Daroussin diffp->div == '>') 101113b5b548SBaptiste Daroussin println(diffp->left, diffp->div, diffp->right); 101213b5b548SBaptiste Daroussin 101313b5b548SBaptiste Daroussin /* Append new lines to diff set. */ 101413b5b548SBaptiste Daroussin if (diffp->left) 101513b5b548SBaptiste Daroussin astrcat(&left, diffp->left); 101613b5b548SBaptiste Daroussin if (diffp->right) 101713b5b548SBaptiste Daroussin astrcat(&right, diffp->right); 101813b5b548SBaptiste Daroussin } 101913b5b548SBaptiste Daroussin 102013b5b548SBaptiste Daroussin /* Empty queue and free each diff line and its elements. */ 102113b5b548SBaptiste Daroussin while (!STAILQ_EMPTY(&diffhead)) { 102213b5b548SBaptiste Daroussin diffp = STAILQ_FIRST(&diffhead); 102313b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&diffhead, diffentries); 102413b5b548SBaptiste Daroussin freediff(diffp); 102513b5b548SBaptiste Daroussin } 102613b5b548SBaptiste Daroussin 102713b5b548SBaptiste Daroussin /* Write to outfp, prompting user if lines are different. */ 102813b5b548SBaptiste Daroussin if (outfp) 102913b5b548SBaptiste Daroussin switch (divc) { 103013b5b548SBaptiste Daroussin case ' ': case '(': case ')': 103113b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", left); 103213b5b548SBaptiste Daroussin break; 103313b5b548SBaptiste Daroussin case '|': case '<': case '>': 103413b5b548SBaptiste Daroussin prompt(left, right); 103513b5b548SBaptiste Daroussin break; 103613b5b548SBaptiste Daroussin default: 103713b5b548SBaptiste Daroussin errx(2, "invalid divider: %c", divc); 103813b5b548SBaptiste Daroussin } 103913b5b548SBaptiste Daroussin 104013b5b548SBaptiste Daroussin /* Free left and right. */ 104113b5b548SBaptiste Daroussin free(left); 104213b5b548SBaptiste Daroussin free(right); 104313b5b548SBaptiste Daroussin } 104413b5b548SBaptiste Daroussin 104513b5b548SBaptiste Daroussin /* 104613b5b548SBaptiste Daroussin * Print lines following an (a)ppend command. 104713b5b548SBaptiste Daroussin */ 104813b5b548SBaptiste Daroussin static void 104913b5b548SBaptiste Daroussin printa(FILE *file, size_t line2) 105013b5b548SBaptiste Daroussin { 105113b5b548SBaptiste Daroussin char *line; 105213b5b548SBaptiste Daroussin 105313b5b548SBaptiste Daroussin for (; file2ln <= line2; ++file2ln) { 105413b5b548SBaptiste Daroussin if (!(line = xfgets(file))) 105513b5b548SBaptiste Daroussin errx(2, "append ended early"); 105613b5b548SBaptiste Daroussin enqueue(NULL, '>', line); 105713b5b548SBaptiste Daroussin } 105813b5b548SBaptiste Daroussin processq(); 105913b5b548SBaptiste Daroussin } 106013b5b548SBaptiste Daroussin 106113b5b548SBaptiste Daroussin /* 106213b5b548SBaptiste Daroussin * Print lines following a (c)hange command, from file1ln to file1end 106313b5b548SBaptiste Daroussin * and from file2ln to file2end. 106413b5b548SBaptiste Daroussin */ 106513b5b548SBaptiste Daroussin static void 106613b5b548SBaptiste Daroussin printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end) 106713b5b548SBaptiste Daroussin { 106813b5b548SBaptiste Daroussin struct fileline { 106913b5b548SBaptiste Daroussin STAILQ_ENTRY(fileline) fileentries; 107013b5b548SBaptiste Daroussin char *line; 107113b5b548SBaptiste Daroussin }; 107213b5b548SBaptiste Daroussin STAILQ_HEAD(, fileline) delqhead = STAILQ_HEAD_INITIALIZER(delqhead); 107313b5b548SBaptiste Daroussin 107413b5b548SBaptiste Daroussin /* Read lines to be deleted. */ 107513b5b548SBaptiste Daroussin for (; file1ln <= file1end; ++file1ln) { 107613b5b548SBaptiste Daroussin struct fileline *linep; 107713b5b548SBaptiste Daroussin char *line1; 107813b5b548SBaptiste Daroussin 107913b5b548SBaptiste Daroussin /* Read lines from both. */ 108013b5b548SBaptiste Daroussin if (!(line1 = xfgets(file1))) 108113b5b548SBaptiste Daroussin errx(2, "error reading file1 in delete in change"); 108213b5b548SBaptiste Daroussin 108313b5b548SBaptiste Daroussin /* Add to delete queue. */ 108413b5b548SBaptiste Daroussin if (!(linep = malloc(sizeof(struct fileline)))) 108513b5b548SBaptiste Daroussin err(2, "printc"); 108613b5b548SBaptiste Daroussin linep->line = line1; 108713b5b548SBaptiste Daroussin STAILQ_INSERT_TAIL(&delqhead, linep, fileentries); 108813b5b548SBaptiste Daroussin } 108913b5b548SBaptiste Daroussin 109013b5b548SBaptiste Daroussin /* Process changed lines.. */ 109113b5b548SBaptiste Daroussin for (; !STAILQ_EMPTY(&delqhead) && file2ln <= file2end; 109213b5b548SBaptiste Daroussin ++file2ln) { 109313b5b548SBaptiste Daroussin struct fileline *del; 109413b5b548SBaptiste Daroussin char *add; 109513b5b548SBaptiste Daroussin 109613b5b548SBaptiste Daroussin /* Get add line. */ 109713b5b548SBaptiste Daroussin if (!(add = xfgets(file2))) 109813b5b548SBaptiste Daroussin errx(2, "error reading add in change"); 109913b5b548SBaptiste Daroussin 110013b5b548SBaptiste Daroussin del = STAILQ_FIRST(&delqhead); 110113b5b548SBaptiste Daroussin enqueue(del->line, '|', add); 110213b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&delqhead, fileentries); 110313b5b548SBaptiste Daroussin /* 110413b5b548SBaptiste Daroussin * Free fileline structure but not its elements since 110513b5b548SBaptiste Daroussin * they are queued up. 110613b5b548SBaptiste Daroussin */ 110713b5b548SBaptiste Daroussin free(del); 110813b5b548SBaptiste Daroussin } 110913b5b548SBaptiste Daroussin processq(); 111013b5b548SBaptiste Daroussin 111113b5b548SBaptiste Daroussin /* Process remaining lines to add. */ 111213b5b548SBaptiste Daroussin for (; file2ln <= file2end; ++file2ln) { 111313b5b548SBaptiste Daroussin char *add; 111413b5b548SBaptiste Daroussin 111513b5b548SBaptiste Daroussin /* Get add line. */ 111613b5b548SBaptiste Daroussin if (!(add = xfgets(file2))) 111713b5b548SBaptiste Daroussin errx(2, "error reading add in change"); 111813b5b548SBaptiste Daroussin 111913b5b548SBaptiste Daroussin enqueue(NULL, '>', add); 112013b5b548SBaptiste Daroussin } 112113b5b548SBaptiste Daroussin processq(); 112213b5b548SBaptiste Daroussin 112313b5b548SBaptiste Daroussin /* Process remaining lines to delete. */ 112413b5b548SBaptiste Daroussin while (!STAILQ_EMPTY(&delqhead)) { 112513b5b548SBaptiste Daroussin struct fileline *filep; 112613b5b548SBaptiste Daroussin 112713b5b548SBaptiste Daroussin filep = STAILQ_FIRST(&delqhead); 112813b5b548SBaptiste Daroussin enqueue(filep->line, '<', NULL); 112913b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&delqhead, fileentries); 113013b5b548SBaptiste Daroussin free(filep); 113113b5b548SBaptiste Daroussin } 113213b5b548SBaptiste Daroussin processq(); 113313b5b548SBaptiste Daroussin } 113413b5b548SBaptiste Daroussin 113513b5b548SBaptiste Daroussin /* 113613b5b548SBaptiste Daroussin * Print deleted lines from file, from file1ln to file1end. 113713b5b548SBaptiste Daroussin */ 113813b5b548SBaptiste Daroussin static void 113913b5b548SBaptiste Daroussin printd(FILE *file1, size_t file1end) 114013b5b548SBaptiste Daroussin { 114113b5b548SBaptiste Daroussin char *line1; 114213b5b548SBaptiste Daroussin 114313b5b548SBaptiste Daroussin /* Print out lines file1ln to line2. */ 114413b5b548SBaptiste Daroussin for (; file1ln <= file1end; ++file1ln) { 114513b5b548SBaptiste Daroussin if (!(line1 = xfgets(file1))) 114613b5b548SBaptiste Daroussin errx(2, "file1 ended early in delete"); 114713b5b548SBaptiste Daroussin enqueue(line1, '<', NULL); 114813b5b548SBaptiste Daroussin } 114913b5b548SBaptiste Daroussin processq(); 115013b5b548SBaptiste Daroussin } 115113b5b548SBaptiste Daroussin 115213b5b548SBaptiste Daroussin /* 115313b5b548SBaptiste Daroussin * Interactive mode usage. 115413b5b548SBaptiste Daroussin */ 115513b5b548SBaptiste Daroussin static void 115613b5b548SBaptiste Daroussin int_usage(void) 115713b5b548SBaptiste Daroussin { 115813b5b548SBaptiste Daroussin 115913b5b548SBaptiste Daroussin puts("e:\tedit blank diff\n" 116013b5b548SBaptiste Daroussin "eb:\tedit both diffs concatenated\n" 116113b5b548SBaptiste Daroussin "el:\tedit left diff\n" 116213b5b548SBaptiste Daroussin "er:\tedit right diff\n" 116313b5b548SBaptiste Daroussin "l | 1:\tchoose left diff\n" 116413b5b548SBaptiste Daroussin "r | 2:\tchoose right diff\n" 116513b5b548SBaptiste Daroussin "s:\tsilent mode--don't print identical lines\n" 116613b5b548SBaptiste Daroussin "v:\tverbose mode--print identical lines\n" 116713b5b548SBaptiste Daroussin "q:\tquit"); 116813b5b548SBaptiste Daroussin } 116913b5b548SBaptiste Daroussin 117013b5b548SBaptiste Daroussin static void 117113b5b548SBaptiste Daroussin usage(void) 117213b5b548SBaptiste Daroussin { 117313b5b548SBaptiste Daroussin 117413b5b548SBaptiste Daroussin fprintf(stderr, 117585bb63bdSBaptiste Daroussin "usage: sdiff [-abdilstHW] [-I regexp] [-o outfile] [-w width] file1" 117613b5b548SBaptiste Daroussin " file2\n"); 117713b5b548SBaptiste Daroussin exit(2); 117813b5b548SBaptiste Daroussin } 1179