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/cdefs.h> 913b5b548SBaptiste Daroussin __FBSDID("$FreeBSD$"); 1013b5b548SBaptiste Daroussin 1113b5b548SBaptiste Daroussin #include <sys/param.h> 1213b5b548SBaptiste Daroussin #include <sys/queue.h> 1313b5b548SBaptiste Daroussin #include <sys/stat.h> 1413b5b548SBaptiste Daroussin #include <sys/types.h> 1513b5b548SBaptiste Daroussin #include <sys/wait.h> 1613b5b548SBaptiste Daroussin 1713b5b548SBaptiste Daroussin #include <ctype.h> 1813b5b548SBaptiste Daroussin #include <err.h> 1913b5b548SBaptiste Daroussin #include <errno.h> 2013b5b548SBaptiste Daroussin #include <fcntl.h> 2113b5b548SBaptiste Daroussin #include <getopt.h> 2213b5b548SBaptiste Daroussin #include <limits.h> 2313b5b548SBaptiste Daroussin #include <paths.h> 2413b5b548SBaptiste Daroussin #include <stdint.h> 2513b5b548SBaptiste Daroussin #include <stdio.h> 2613b5b548SBaptiste Daroussin #include <stdlib.h> 2713b5b548SBaptiste Daroussin #include <string.h> 2813b5b548SBaptiste Daroussin #include <unistd.h> 2913b5b548SBaptiste Daroussin 3013b5b548SBaptiste Daroussin #include "extern.h" 3113b5b548SBaptiste Daroussin 3213b5b548SBaptiste Daroussin #define DIFF_PATH "/usr/bin/diff" 3313b5b548SBaptiste Daroussin 3413b5b548SBaptiste Daroussin #define WIDTH 126 3513b5b548SBaptiste Daroussin /* 3613b5b548SBaptiste Daroussin * Each column must be at least one character wide, plus three 3713b5b548SBaptiste Daroussin * characters between the columns (space, [<|>], space). 3813b5b548SBaptiste Daroussin */ 3913b5b548SBaptiste Daroussin #define WIDTH_MIN 5 4013b5b548SBaptiste Daroussin 4113b5b548SBaptiste Daroussin /* 3 kilobytes of chars */ 4213b5b548SBaptiste Daroussin #define MAX_CHECK 768 4313b5b548SBaptiste Daroussin 4413b5b548SBaptiste Daroussin /* A single diff line. */ 4513b5b548SBaptiste Daroussin struct diffline { 4613b5b548SBaptiste Daroussin STAILQ_ENTRY(diffline) diffentries; 4713b5b548SBaptiste Daroussin char *left; 4813b5b548SBaptiste Daroussin char div; 4913b5b548SBaptiste Daroussin char *right; 5013b5b548SBaptiste Daroussin }; 5113b5b548SBaptiste Daroussin 5213b5b548SBaptiste Daroussin static void astrcat(char **, const char *); 5313b5b548SBaptiste Daroussin static void enqueue(char *, char, char *); 5413b5b548SBaptiste Daroussin static char *mktmpcpy(const char *); 5513b5b548SBaptiste Daroussin static int istextfile(FILE *); 5613b5b548SBaptiste Daroussin static void binexec(char *, char *, char *) __dead2; 5713b5b548SBaptiste Daroussin static void freediff(struct diffline *); 5813b5b548SBaptiste Daroussin static void int_usage(void); 5913b5b548SBaptiste Daroussin static int parsecmd(FILE *, FILE *, FILE *); 6013b5b548SBaptiste Daroussin static void printa(FILE *, size_t); 6113b5b548SBaptiste Daroussin static void printc(FILE *, size_t, FILE *, size_t); 6213b5b548SBaptiste Daroussin static void printcol(const char *, size_t *, const size_t); 6313b5b548SBaptiste Daroussin static void printd(FILE *, size_t); 6413b5b548SBaptiste Daroussin static void println(const char *, const char, const char *); 6513b5b548SBaptiste Daroussin static void processq(void); 6613b5b548SBaptiste Daroussin static void prompt(const char *, const char *); 6713b5b548SBaptiste Daroussin static void usage(void) __dead2; 6813b5b548SBaptiste Daroussin static char *xfgets(FILE *); 6913b5b548SBaptiste Daroussin 7013b5b548SBaptiste Daroussin static STAILQ_HEAD(, diffline) diffhead = STAILQ_HEAD_INITIALIZER(diffhead); 7113b5b548SBaptiste Daroussin static size_t line_width; /* width of a line (two columns and divider) */ 7213b5b548SBaptiste Daroussin static size_t width; /* width of each column */ 7313b5b548SBaptiste Daroussin static size_t file1ln, file2ln; /* line number of file1 and file2 */ 7413b5b548SBaptiste Daroussin static int Iflag = 0; /* ignore sets matching regexp */ 7513b5b548SBaptiste Daroussin static int lflag; /* print only left column for identical lines */ 7613b5b548SBaptiste Daroussin static int sflag; /* skip identical lines */ 7713b5b548SBaptiste Daroussin FILE *outfp; /* file to save changes to */ 7813b5b548SBaptiste Daroussin const char *tmpdir; /* TMPDIR or /tmp */ 7913b5b548SBaptiste Daroussin 8013b5b548SBaptiste Daroussin enum { 8113b5b548SBaptiste Daroussin HELP_OPT = CHAR_MAX + 1, 8213b5b548SBaptiste Daroussin NORMAL_OPT, 8313b5b548SBaptiste Daroussin FCASE_SENSITIVE_OPT, 8413b5b548SBaptiste Daroussin FCASE_IGNORE_OPT, 8513b5b548SBaptiste Daroussin STRIPCR_OPT, 8613b5b548SBaptiste Daroussin TSIZE_OPT, 8713b5b548SBaptiste Daroussin DIFFPROG_OPT, 8813b5b548SBaptiste Daroussin }; 8913b5b548SBaptiste Daroussin 9013b5b548SBaptiste Daroussin static struct option longopts[] = { 9113b5b548SBaptiste Daroussin /* options only processed in sdiff */ 9213b5b548SBaptiste Daroussin { "suppress-common-lines", no_argument, NULL, 's' }, 9313b5b548SBaptiste Daroussin { "width", required_argument, NULL, 'w' }, 9413b5b548SBaptiste Daroussin 9513b5b548SBaptiste Daroussin { "output", required_argument, NULL, 'o' }, 9613b5b548SBaptiste Daroussin { "diff-program", required_argument, NULL, DIFFPROG_OPT }, 9713b5b548SBaptiste Daroussin 9813b5b548SBaptiste Daroussin /* Options processed by diff. */ 9913b5b548SBaptiste Daroussin { "ignore-file-name-case", no_argument, NULL, FCASE_IGNORE_OPT }, 10013b5b548SBaptiste Daroussin { "no-ignore-file-name-case", no_argument, NULL, FCASE_SENSITIVE_OPT }, 10113b5b548SBaptiste Daroussin { "strip-trailing-cr", no_argument, NULL, STRIPCR_OPT }, 10213b5b548SBaptiste Daroussin { "tabsize", required_argument, NULL, TSIZE_OPT }, 10313b5b548SBaptiste Daroussin { "help", no_argument, NULL, HELP_OPT }, 10413b5b548SBaptiste Daroussin { "text", no_argument, NULL, 'a' }, 10513b5b548SBaptiste Daroussin { "ignore-blank-lines", no_argument, NULL, 'B' }, 10613b5b548SBaptiste Daroussin { "ignore-space-change", no_argument, NULL, 'b' }, 10713b5b548SBaptiste Daroussin { "minimal", no_argument, NULL, 'd' }, 10813b5b548SBaptiste Daroussin { "ignore-tab-expansion", no_argument, NULL, 'E' }, 10913b5b548SBaptiste Daroussin { "ignore-matching-lines", required_argument, NULL, 'I' }, 11013b5b548SBaptiste Daroussin { "ignore-case", no_argument, NULL, 'i' }, 111d481a925SBaptiste Daroussin { "left-column", no_argument, NULL, 'l' }, 11213b5b548SBaptiste Daroussin { "expand-tabs", no_argument, NULL, 't' }, 11313b5b548SBaptiste Daroussin { "speed-large-files", no_argument, NULL, 'H' }, 11413b5b548SBaptiste Daroussin { "ignore-all-space", no_argument, NULL, 'W' }, 11513b5b548SBaptiste Daroussin 11613b5b548SBaptiste Daroussin { NULL, 0, NULL, '\0'} 11713b5b548SBaptiste Daroussin }; 11813b5b548SBaptiste Daroussin 11913b5b548SBaptiste Daroussin static const char *help_msg[] = { 12023421d54SBaptiste Daroussin "usage: sdiff [-abdilstW] [-I regexp] [-o outfile] [-w width] file1 file2\n", 12123421d54SBaptiste Daroussin "-l, --left-column: only print the left column for identical lines.", 12223421d54SBaptiste Daroussin "-o OUTFILE, --output=OUTFILE: interactively merge file1 and file2 into outfile.", 12323421d54SBaptiste Daroussin "-s, --suppress-common-lines: skip identical lines.", 12423421d54SBaptiste Daroussin "-w WIDTH, --width=WIDTH: print a maximum of WIDTH characters on each line.", 12523421d54SBaptiste Daroussin "", 12623421d54SBaptiste Daroussin "Options passed to diff(1) are:", 12723421d54SBaptiste Daroussin "\t-a, --text: treat file1 and file2 as text files.", 12823421d54SBaptiste Daroussin "\t-b, --ignore-trailing-cr: ignore trailing blank spaces.", 12923421d54SBaptiste Daroussin "\t-d, --minimal: minimize diff size.", 13023421d54SBaptiste Daroussin "\t-I RE, --ignore-matching-lines=RE: ignore changes whose line matches RE.", 13123421d54SBaptiste Daroussin "\t-i, --ignore-case: do a case-insensitive comparison.", 13223421d54SBaptiste Daroussin "\t-t, --expand-tabs: sxpand tabs to spaces.", 13323421d54SBaptiste Daroussin "\t-W, --ignore-all-spaces: ignore all spaces.", 13423421d54SBaptiste Daroussin "\t--speed-large-files: assume large file with scattered changes.", 13523421d54SBaptiste Daroussin "\t--strip-trailing-cr: strip trailing carriage return.", 13623421d54SBaptiste Daroussin "\t--ignore-file-name-case: ignore case of file names.", 13723421d54SBaptiste Daroussin "\t--no-ignore-file-name-case: do not ignore file name case", 13823421d54SBaptiste Daroussin "\t--tabsize NUM: change size of tabs (default 8.)", 13913b5b548SBaptiste Daroussin 14013b5b548SBaptiste Daroussin NULL, 14113b5b548SBaptiste Daroussin }; 14213b5b548SBaptiste Daroussin 14313b5b548SBaptiste Daroussin /* 14413b5b548SBaptiste Daroussin * Create temporary file if source_file is not a regular file. 14513b5b548SBaptiste Daroussin * Returns temporary file name if one was malloced, NULL if unnecessary. 14613b5b548SBaptiste Daroussin */ 14713b5b548SBaptiste Daroussin static char * 14813b5b548SBaptiste Daroussin mktmpcpy(const char *source_file) 14913b5b548SBaptiste Daroussin { 15013b5b548SBaptiste Daroussin struct stat sb; 15113b5b548SBaptiste Daroussin ssize_t rcount; 15213b5b548SBaptiste Daroussin int ifd, ofd; 15313b5b548SBaptiste Daroussin u_char buf[BUFSIZ]; 15413b5b548SBaptiste Daroussin char *target_file; 15513b5b548SBaptiste Daroussin 15613b5b548SBaptiste Daroussin /* Open input and output. */ 15713b5b548SBaptiste Daroussin ifd = open(source_file, O_RDONLY, 0); 15813b5b548SBaptiste Daroussin /* File was opened successfully. */ 15913b5b548SBaptiste Daroussin if (ifd != -1) { 16013b5b548SBaptiste Daroussin if (fstat(ifd, &sb) == -1) 16113b5b548SBaptiste Daroussin err(2, "error getting file status from %s", source_file); 16213b5b548SBaptiste Daroussin 16313b5b548SBaptiste Daroussin /* Regular file. */ 16413b5b548SBaptiste Daroussin if (S_ISREG(sb.st_mode)) { 16513b5b548SBaptiste Daroussin close(ifd); 16613b5b548SBaptiste Daroussin return (NULL); 16713b5b548SBaptiste Daroussin } 16813b5b548SBaptiste Daroussin } else { 16913b5b548SBaptiste Daroussin /* If ``-'' does not exist the user meant stdin. */ 17013b5b548SBaptiste Daroussin if (errno == ENOENT && strcmp(source_file, "-") == 0) 17113b5b548SBaptiste Daroussin ifd = STDIN_FILENO; 17213b5b548SBaptiste Daroussin else 17313b5b548SBaptiste Daroussin err(2, "error opening %s", source_file); 17413b5b548SBaptiste Daroussin } 17513b5b548SBaptiste Daroussin 17613b5b548SBaptiste Daroussin /* Not a regular file, so copy input into temporary file. */ 17713b5b548SBaptiste Daroussin if (asprintf(&target_file, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1) 17813b5b548SBaptiste Daroussin err(2, "asprintf"); 17913b5b548SBaptiste Daroussin if ((ofd = mkstemp(target_file)) == -1) { 18013b5b548SBaptiste Daroussin warn("error opening %s", target_file); 18113b5b548SBaptiste Daroussin goto FAIL; 18213b5b548SBaptiste Daroussin } 18313b5b548SBaptiste Daroussin while ((rcount = read(ifd, buf, sizeof(buf))) != -1 && 18413b5b548SBaptiste Daroussin rcount != 0) { 18513b5b548SBaptiste Daroussin ssize_t wcount; 18613b5b548SBaptiste Daroussin 18713b5b548SBaptiste Daroussin wcount = write(ofd, buf, (size_t)rcount); 18813b5b548SBaptiste Daroussin if (-1 == wcount || rcount != wcount) { 18913b5b548SBaptiste Daroussin warn("error writing to %s", target_file); 19013b5b548SBaptiste Daroussin goto FAIL; 19113b5b548SBaptiste Daroussin } 19213b5b548SBaptiste Daroussin } 19313b5b548SBaptiste Daroussin if (rcount == -1) { 19413b5b548SBaptiste Daroussin warn("error reading from %s", source_file); 19513b5b548SBaptiste Daroussin goto FAIL; 19613b5b548SBaptiste Daroussin } 19713b5b548SBaptiste Daroussin 19813b5b548SBaptiste Daroussin close(ifd); 19913b5b548SBaptiste Daroussin close(ofd); 20013b5b548SBaptiste Daroussin 20113b5b548SBaptiste Daroussin return (target_file); 20213b5b548SBaptiste Daroussin 20313b5b548SBaptiste Daroussin FAIL: 20413b5b548SBaptiste Daroussin unlink(target_file); 20513b5b548SBaptiste Daroussin exit(2); 20613b5b548SBaptiste Daroussin } 20713b5b548SBaptiste Daroussin 20813b5b548SBaptiste Daroussin int 20913b5b548SBaptiste Daroussin main(int argc, char **argv) 21013b5b548SBaptiste Daroussin { 21113b5b548SBaptiste Daroussin FILE *diffpipe=NULL, *file1, *file2; 21213b5b548SBaptiste Daroussin size_t diffargc = 0, wflag = WIDTH; 21313b5b548SBaptiste Daroussin int ch, fd[2] = {-1}, status; 2149dc3843eSBaptiste Daroussin pid_t pid=0; 21513b5b548SBaptiste Daroussin const char *outfile = NULL; 21613b5b548SBaptiste Daroussin struct option *popt; 21713b5b548SBaptiste Daroussin char **diffargv, *diffprog = DIFF_PATH, *filename1, *filename2, 21813b5b548SBaptiste Daroussin *tmp1, *tmp2, *s1, *s2; 21913b5b548SBaptiste Daroussin int i; 22013b5b548SBaptiste Daroussin 22113b5b548SBaptiste Daroussin /* 22213b5b548SBaptiste Daroussin * Process diff flags. 22313b5b548SBaptiste Daroussin */ 22413b5b548SBaptiste Daroussin /* 22513b5b548SBaptiste Daroussin * Allocate memory for diff arguments and NULL. 22613b5b548SBaptiste Daroussin * Each flag has at most one argument, so doubling argc gives an 22713b5b548SBaptiste Daroussin * upper limit of how many diff args can be passed. argv[0], 22813b5b548SBaptiste Daroussin * file1, and file2 won't have arguments so doubling them will 22913b5b548SBaptiste Daroussin * waste some memory; however we need an extra space for the 23013b5b548SBaptiste Daroussin * NULL at the end, so it sort of works out. 23113b5b548SBaptiste Daroussin */ 23213b5b548SBaptiste Daroussin if (!(diffargv = calloc(argc, sizeof(char **) * 2))) 23313b5b548SBaptiste Daroussin err(2, "main"); 23413b5b548SBaptiste Daroussin 23513b5b548SBaptiste Daroussin /* Add first argument, the program name. */ 23613b5b548SBaptiste Daroussin diffargv[diffargc++] = diffprog; 23713b5b548SBaptiste Daroussin 23813b5b548SBaptiste Daroussin /* create a dynamic string for merging single-switch options */ 23913b5b548SBaptiste Daroussin if ( asprintf(&diffargv[diffargc++], "-") < 0 ) 24013b5b548SBaptiste Daroussin err(2, "main"); 24113b5b548SBaptiste Daroussin 24213b5b548SBaptiste Daroussin while ((ch = getopt_long(argc, argv, "aBbdEHI:ilo:stWw:", 24313b5b548SBaptiste Daroussin longopts, NULL)) != -1) { 24413b5b548SBaptiste Daroussin const char *errstr; 24513b5b548SBaptiste Daroussin 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 TSIZE_OPT: 25213b5b548SBaptiste Daroussin case 'S': 25313b5b548SBaptiste Daroussin break; 25413b5b548SBaptiste Daroussin /* combine no-arg single switches */ 25513b5b548SBaptiste Daroussin case 'a': 25613b5b548SBaptiste Daroussin case 'B': 25713b5b548SBaptiste Daroussin case 'b': 25813b5b548SBaptiste Daroussin case 'd': 25913b5b548SBaptiste Daroussin case 'E': 26013b5b548SBaptiste Daroussin case 'i': 26113b5b548SBaptiste Daroussin case 't': 26213b5b548SBaptiste Daroussin case 'W': 26313b5b548SBaptiste Daroussin for(popt = longopts; ch != popt->val && popt->name != NULL; popt++); 26413b5b548SBaptiste Daroussin diffargv[1] = realloc(diffargv[1], sizeof(char) * strlen(diffargv[1]) + 2); 26513b5b548SBaptiste Daroussin /* 26613b5b548SBaptiste Daroussin * In diff, the 'W' option is 'w' and the 'w' is 'W'. 26713b5b548SBaptiste Daroussin */ 26813b5b548SBaptiste Daroussin if (ch == 'W') 26913b5b548SBaptiste Daroussin sprintf(diffargv[1], "%sw", diffargv[1]); 27013b5b548SBaptiste Daroussin else 27113b5b548SBaptiste Daroussin sprintf(diffargv[1], "%s%c", diffargv[1], ch); 27213b5b548SBaptiste Daroussin break; 273*85bb63bdSBaptiste Daroussin case 'H': 274*85bb63bdSBaptiste Daroussin diffargv[diffargc++] = "--speed-large-files"; 275*85bb63bdSBaptiste Daroussin break; 27613b5b548SBaptiste Daroussin case DIFFPROG_OPT: 27713b5b548SBaptiste Daroussin diffargv[0] = diffprog = optarg; 27813b5b548SBaptiste Daroussin break; 27913b5b548SBaptiste Daroussin case 'I': 28013b5b548SBaptiste Daroussin Iflag = 1; 28113b5b548SBaptiste Daroussin diffargv[diffargc++] = "-I"; 28213b5b548SBaptiste Daroussin diffargv[diffargc++] = optarg; 28313b5b548SBaptiste Daroussin break; 28413b5b548SBaptiste Daroussin case 'l': 28513b5b548SBaptiste Daroussin lflag = 1; 28613b5b548SBaptiste Daroussin break; 28713b5b548SBaptiste Daroussin case 'o': 28813b5b548SBaptiste Daroussin outfile = optarg; 28913b5b548SBaptiste Daroussin break; 29013b5b548SBaptiste Daroussin case 's': 29113b5b548SBaptiste Daroussin sflag = 1; 29213b5b548SBaptiste Daroussin break; 29313b5b548SBaptiste Daroussin case 'w': 29413b5b548SBaptiste Daroussin wflag = strtonum(optarg, WIDTH_MIN, 29513b5b548SBaptiste Daroussin INT_MAX, &errstr); 29613b5b548SBaptiste Daroussin if (errstr) 29713b5b548SBaptiste Daroussin errx(2, "width is %s: %s", errstr, optarg); 29813b5b548SBaptiste Daroussin break; 29913b5b548SBaptiste Daroussin case HELP_OPT: 30013b5b548SBaptiste Daroussin for (i = 0; help_msg[i] != NULL; i++) 30113b5b548SBaptiste Daroussin printf("%s\n", help_msg[i]); 30213b5b548SBaptiste Daroussin exit(0); 30313b5b548SBaptiste Daroussin break; 30413b5b548SBaptiste Daroussin default: 30513b5b548SBaptiste Daroussin usage(); 30613b5b548SBaptiste Daroussin break; 30713b5b548SBaptiste Daroussin } 30813b5b548SBaptiste Daroussin } 30913b5b548SBaptiste Daroussin 31013b5b548SBaptiste Daroussin /* no single switches were used */ 31113b5b548SBaptiste Daroussin if (strcmp(diffargv[1], "-") == 0 ) { 31213b5b548SBaptiste Daroussin for ( i = 1; i < argc-1; i++) { 31313b5b548SBaptiste Daroussin diffargv[i] = diffargv[i+1]; 31413b5b548SBaptiste Daroussin } 31513b5b548SBaptiste Daroussin diffargv[diffargc-1] = NULL; 31613b5b548SBaptiste Daroussin diffargc--; 31713b5b548SBaptiste Daroussin } 31813b5b548SBaptiste Daroussin 31913b5b548SBaptiste Daroussin argc -= optind; 32013b5b548SBaptiste Daroussin argv += optind; 32113b5b548SBaptiste Daroussin 32213b5b548SBaptiste Daroussin if (argc != 2) 32313b5b548SBaptiste Daroussin usage(); 32413b5b548SBaptiste Daroussin 32513b5b548SBaptiste Daroussin if (outfile && (outfp = fopen(outfile, "w")) == NULL) 32613b5b548SBaptiste Daroussin err(2, "could not open: %s", optarg); 32713b5b548SBaptiste Daroussin 32813b5b548SBaptiste Daroussin if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') 32913b5b548SBaptiste Daroussin tmpdir = _PATH_TMP; 33013b5b548SBaptiste Daroussin 33113b5b548SBaptiste Daroussin filename1 = argv[0]; 33213b5b548SBaptiste Daroussin filename2 = argv[1]; 33313b5b548SBaptiste Daroussin 33413b5b548SBaptiste Daroussin /* 33513b5b548SBaptiste Daroussin * Create temporary files for diff and sdiff to share if file1 33613b5b548SBaptiste Daroussin * or file2 are not regular files. This allows sdiff and diff 33713b5b548SBaptiste Daroussin * to read the same inputs if one or both inputs are stdin. 33813b5b548SBaptiste Daroussin * 33913b5b548SBaptiste Daroussin * If any temporary files were created, their names would be 34013b5b548SBaptiste Daroussin * saved in tmp1 or tmp2. tmp1 should never equal tmp2. 34113b5b548SBaptiste Daroussin */ 34213b5b548SBaptiste Daroussin tmp1 = tmp2 = NULL; 34313b5b548SBaptiste Daroussin /* file1 and file2 are the same, so copy to same temp file. */ 34413b5b548SBaptiste Daroussin if (strcmp(filename1, filename2) == 0) { 34513b5b548SBaptiste Daroussin if ((tmp1 = mktmpcpy(filename1))) 34613b5b548SBaptiste Daroussin filename1 = filename2 = tmp1; 34713b5b548SBaptiste Daroussin /* Copy file1 and file2 into separate temp files. */ 34813b5b548SBaptiste Daroussin } else { 34913b5b548SBaptiste Daroussin if ((tmp1 = mktmpcpy(filename1))) 35013b5b548SBaptiste Daroussin filename1 = tmp1; 35113b5b548SBaptiste Daroussin if ((tmp2 = mktmpcpy(filename2))) 35213b5b548SBaptiste Daroussin filename2 = tmp2; 35313b5b548SBaptiste Daroussin } 35413b5b548SBaptiste Daroussin 35513b5b548SBaptiste Daroussin diffargv[diffargc++] = filename1; 35613b5b548SBaptiste Daroussin diffargv[diffargc++] = filename2; 35713b5b548SBaptiste Daroussin /* Add NULL to end of array to indicate end of array. */ 35813b5b548SBaptiste Daroussin diffargv[diffargc++] = NULL; 35913b5b548SBaptiste Daroussin 36013b5b548SBaptiste Daroussin /* Subtract column divider and divide by two. */ 36113b5b548SBaptiste Daroussin width = (wflag - 3) / 2; 36213b5b548SBaptiste Daroussin /* Make sure line_width can fit in size_t. */ 36313b5b548SBaptiste Daroussin if (width > (SIZE_MAX - 3) / 2) 36413b5b548SBaptiste Daroussin errx(2, "width is too large: %zu", width); 36513b5b548SBaptiste Daroussin line_width = width * 2 + 3; 36613b5b548SBaptiste Daroussin 36713b5b548SBaptiste Daroussin if (pipe(fd)) 36813b5b548SBaptiste Daroussin err(2, "pipe"); 36913b5b548SBaptiste Daroussin 37013b5b548SBaptiste Daroussin switch (pid = fork()) { 37113b5b548SBaptiste Daroussin case 0: 37213b5b548SBaptiste Daroussin /* child */ 37313b5b548SBaptiste Daroussin /* We don't read from the pipe. */ 37413b5b548SBaptiste Daroussin close(fd[0]); 37513b5b548SBaptiste Daroussin if (dup2(fd[1], STDOUT_FILENO) == -1) 37613b5b548SBaptiste Daroussin err(2, "child could not duplicate descriptor"); 37713b5b548SBaptiste Daroussin /* Free unused descriptor. */ 37813b5b548SBaptiste Daroussin close(fd[1]); 37913b5b548SBaptiste Daroussin execvp(diffprog, diffargv); 38013b5b548SBaptiste Daroussin err(2, "could not execute diff: %s", diffprog); 38113b5b548SBaptiste Daroussin break; 38213b5b548SBaptiste Daroussin case -1: 38313b5b548SBaptiste Daroussin err(2, "could not fork"); 38413b5b548SBaptiste Daroussin break; 38513b5b548SBaptiste Daroussin } 38613b5b548SBaptiste Daroussin 38713b5b548SBaptiste Daroussin /* parent */ 38813b5b548SBaptiste Daroussin /* We don't write to the pipe. */ 38913b5b548SBaptiste Daroussin close(fd[1]); 39013b5b548SBaptiste Daroussin 39113b5b548SBaptiste Daroussin /* Open pipe to diff command. */ 39213b5b548SBaptiste Daroussin if ((diffpipe = fdopen(fd[0], "r")) == NULL) 39313b5b548SBaptiste Daroussin err(2, "could not open diff pipe"); 3949dc3843eSBaptiste Daroussin 39513b5b548SBaptiste Daroussin if ((file1 = fopen(filename1, "r")) == NULL) 39613b5b548SBaptiste Daroussin err(2, "could not open %s", filename1); 39713b5b548SBaptiste Daroussin if ((file2 = fopen(filename2, "r")) == NULL) 39813b5b548SBaptiste Daroussin err(2, "could not open %s", filename2); 39913b5b548SBaptiste Daroussin if (!istextfile(file1) || !istextfile(file2)) { 40013b5b548SBaptiste Daroussin /* Close open files and pipe, delete temps */ 40113b5b548SBaptiste Daroussin fclose(file1); 40213b5b548SBaptiste Daroussin fclose(file2); 403a60711fdSConrad Meyer if (diffpipe != NULL) 40413b5b548SBaptiste Daroussin fclose(diffpipe); 40513b5b548SBaptiste Daroussin if (tmp1) 40613b5b548SBaptiste Daroussin if (unlink(tmp1)) 40713b5b548SBaptiste Daroussin warn("Error deleting %s.", tmp1); 40813b5b548SBaptiste Daroussin if (tmp2) 40913b5b548SBaptiste Daroussin if (unlink(tmp2)) 41013b5b548SBaptiste Daroussin warn("Error deleting %s.", tmp2); 41113b5b548SBaptiste Daroussin free(tmp1); 41213b5b548SBaptiste Daroussin free(tmp2); 41313b5b548SBaptiste Daroussin binexec(diffprog, filename1, filename2); 41413b5b548SBaptiste Daroussin } 41513b5b548SBaptiste Daroussin /* Line numbers start at one. */ 41613b5b548SBaptiste Daroussin file1ln = file2ln = 1; 41713b5b548SBaptiste Daroussin 41813b5b548SBaptiste Daroussin /* Read and parse diff output. */ 41913b5b548SBaptiste Daroussin while (parsecmd(diffpipe, file1, file2) != EOF) 42013b5b548SBaptiste Daroussin ; 42113b5b548SBaptiste Daroussin fclose(diffpipe); 42213b5b548SBaptiste Daroussin 42313b5b548SBaptiste Daroussin /* Wait for diff to exit. */ 42413b5b548SBaptiste Daroussin if (waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) || 42513b5b548SBaptiste Daroussin WEXITSTATUS(status) >= 2) 42613b5b548SBaptiste Daroussin err(2, "diff exited abnormally."); 42713b5b548SBaptiste Daroussin 42813b5b548SBaptiste Daroussin /* Delete and free unneeded temporary files. */ 42913b5b548SBaptiste Daroussin if (tmp1) 43013b5b548SBaptiste Daroussin if (unlink(tmp1)) 43113b5b548SBaptiste Daroussin warn("Error deleting %s.", tmp1); 43213b5b548SBaptiste Daroussin if (tmp2) 43313b5b548SBaptiste Daroussin if (unlink(tmp2)) 43413b5b548SBaptiste Daroussin warn("Error deleting %s.", tmp2); 43513b5b548SBaptiste Daroussin free(tmp1); 43613b5b548SBaptiste Daroussin free(tmp2); 43713b5b548SBaptiste Daroussin filename1 = filename2 = tmp1 = tmp2 = NULL; 43813b5b548SBaptiste Daroussin 43913b5b548SBaptiste Daroussin /* No more diffs, so print common lines. */ 44013b5b548SBaptiste Daroussin if (lflag) 44113b5b548SBaptiste Daroussin while ((s1 = xfgets(file1))) 44213b5b548SBaptiste Daroussin enqueue(s1, ' ', NULL); 44313b5b548SBaptiste Daroussin else 44413b5b548SBaptiste Daroussin for (;;) { 44513b5b548SBaptiste Daroussin s1 = xfgets(file1); 44613b5b548SBaptiste Daroussin s2 = xfgets(file2); 44713b5b548SBaptiste Daroussin if (s1 || s2) 44813b5b548SBaptiste Daroussin enqueue(s1, ' ', s2); 44913b5b548SBaptiste Daroussin else 45013b5b548SBaptiste Daroussin break; 45113b5b548SBaptiste Daroussin } 45213b5b548SBaptiste Daroussin fclose(file1); 45313b5b548SBaptiste Daroussin fclose(file2); 45413b5b548SBaptiste Daroussin /* Process unmodified lines. */ 45513b5b548SBaptiste Daroussin processq(); 45613b5b548SBaptiste Daroussin 45713b5b548SBaptiste Daroussin /* Return diff exit status. */ 45813b5b548SBaptiste Daroussin return (WEXITSTATUS(status)); 45913b5b548SBaptiste Daroussin } 46013b5b548SBaptiste Daroussin 46113b5b548SBaptiste Daroussin /* 462c59a0a34SBaptiste Daroussin * When sdiff detects a binary file as input, executes them with 463c59a0a34SBaptiste Daroussin * diff to maintain the same behavior as GNU sdiff with binary input. 46413b5b548SBaptiste Daroussin */ 46513b5b548SBaptiste Daroussin static void 46613b5b548SBaptiste Daroussin binexec(char *diffprog, char *f1, char *f2) 46713b5b548SBaptiste Daroussin { 46813b5b548SBaptiste Daroussin 46913b5b548SBaptiste Daroussin char *args[] = {diffprog, f1, f2, (char *) 0}; 47013b5b548SBaptiste Daroussin execv(diffprog, args); 47113b5b548SBaptiste Daroussin 47213b5b548SBaptiste Daroussin /* If execv() fails, sdiff's execution will continue below. */ 4736c6e3889SEd Maste errx(1, "could not execute diff process"); 47413b5b548SBaptiste Daroussin } 47513b5b548SBaptiste Daroussin 47613b5b548SBaptiste Daroussin /* 47713b5b548SBaptiste Daroussin * Checks whether a file appears to be a text file. 47813b5b548SBaptiste Daroussin */ 47913b5b548SBaptiste Daroussin static int 48013b5b548SBaptiste Daroussin istextfile(FILE *f) 48113b5b548SBaptiste Daroussin { 482e29c5529SBjoern A. Zeeb int ch, i; 48313b5b548SBaptiste Daroussin 48413b5b548SBaptiste Daroussin if (f == NULL) 48513b5b548SBaptiste Daroussin return (1); 48613b5b548SBaptiste Daroussin rewind(f); 487da5c2c42SBaptiste Daroussin for (i = 0; i <= MAX_CHECK; i++) { 48813b5b548SBaptiste Daroussin ch = fgetc(f); 48913b5b548SBaptiste Daroussin if (ch == '\0') { 49013b5b548SBaptiste Daroussin rewind(f); 49113b5b548SBaptiste Daroussin return (0); 49213b5b548SBaptiste Daroussin } 493da5c2c42SBaptiste Daroussin if (ch == EOF) 494da5c2c42SBaptiste Daroussin break; 49513b5b548SBaptiste Daroussin } 49613b5b548SBaptiste Daroussin rewind(f); 49713b5b548SBaptiste Daroussin return (1); 49813b5b548SBaptiste Daroussin } 49913b5b548SBaptiste Daroussin 50013b5b548SBaptiste Daroussin /* 50113b5b548SBaptiste Daroussin * Prints an individual column (left or right), taking into account 50213b5b548SBaptiste Daroussin * that tabs are variable-width. Takes a string, the current column 50313b5b548SBaptiste Daroussin * the cursor is on the screen, and the maximum value of the column. 50413b5b548SBaptiste Daroussin * The column value is updated as we go along. 50513b5b548SBaptiste Daroussin */ 50613b5b548SBaptiste Daroussin static void 50713b5b548SBaptiste Daroussin printcol(const char *s, size_t *col, const size_t col_max) 50813b5b548SBaptiste Daroussin { 50913b5b548SBaptiste Daroussin 51013b5b548SBaptiste Daroussin for (; *s && *col < col_max; ++s) { 51113b5b548SBaptiste Daroussin size_t new_col; 51213b5b548SBaptiste Daroussin 51313b5b548SBaptiste Daroussin switch (*s) { 51413b5b548SBaptiste Daroussin case '\t': 51513b5b548SBaptiste Daroussin /* 51613b5b548SBaptiste Daroussin * If rounding to next multiple of eight causes 51713b5b548SBaptiste Daroussin * an integer overflow, just return. 51813b5b548SBaptiste Daroussin */ 51913b5b548SBaptiste Daroussin if (*col > SIZE_MAX - 8) 52013b5b548SBaptiste Daroussin return; 52113b5b548SBaptiste Daroussin 52213b5b548SBaptiste Daroussin /* Round to next multiple of eight. */ 52313b5b548SBaptiste Daroussin new_col = (*col / 8 + 1) * 8; 52413b5b548SBaptiste Daroussin 52513b5b548SBaptiste Daroussin /* 52613b5b548SBaptiste Daroussin * If printing the tab goes past the column 52713b5b548SBaptiste Daroussin * width, don't print it and just quit. 52813b5b548SBaptiste Daroussin */ 52913b5b548SBaptiste Daroussin if (new_col > col_max) 53013b5b548SBaptiste Daroussin return; 53113b5b548SBaptiste Daroussin *col = new_col; 53213b5b548SBaptiste Daroussin break; 53313b5b548SBaptiste Daroussin default: 53413b5b548SBaptiste Daroussin ++(*col); 53513b5b548SBaptiste Daroussin } 53613b5b548SBaptiste Daroussin putchar(*s); 53713b5b548SBaptiste Daroussin } 53813b5b548SBaptiste Daroussin } 53913b5b548SBaptiste Daroussin 54013b5b548SBaptiste Daroussin /* 54113b5b548SBaptiste Daroussin * Prompts user to either choose between two strings or edit one, both, 54213b5b548SBaptiste Daroussin * or neither. 54313b5b548SBaptiste Daroussin */ 54413b5b548SBaptiste Daroussin static void 54513b5b548SBaptiste Daroussin prompt(const char *s1, const char *s2) 54613b5b548SBaptiste Daroussin { 54713b5b548SBaptiste Daroussin char *cmd; 54813b5b548SBaptiste Daroussin 54913b5b548SBaptiste Daroussin /* Print command prompt. */ 55013b5b548SBaptiste Daroussin putchar('%'); 55113b5b548SBaptiste Daroussin 55213b5b548SBaptiste Daroussin /* Get user input. */ 55313b5b548SBaptiste Daroussin for (; (cmd = xfgets(stdin)); free(cmd)) { 55413b5b548SBaptiste Daroussin const char *p; 55513b5b548SBaptiste Daroussin 55613b5b548SBaptiste Daroussin /* Skip leading whitespace. */ 55713b5b548SBaptiste Daroussin for (p = cmd; isspace(*p); ++p) 55813b5b548SBaptiste Daroussin ; 55913b5b548SBaptiste Daroussin switch (*p) { 56013b5b548SBaptiste Daroussin case 'e': 56113b5b548SBaptiste Daroussin /* Skip `e'. */ 56213b5b548SBaptiste Daroussin ++p; 56313b5b548SBaptiste Daroussin if (eparse(p, s1, s2) == -1) 56413b5b548SBaptiste Daroussin goto USAGE; 56513b5b548SBaptiste Daroussin break; 56613b5b548SBaptiste Daroussin case 'l': 56713b5b548SBaptiste Daroussin case '1': 56813b5b548SBaptiste Daroussin /* Choose left column as-is. */ 56913b5b548SBaptiste Daroussin if (s1 != NULL) 57013b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", s1); 57113b5b548SBaptiste Daroussin /* End of command parsing. */ 57213b5b548SBaptiste Daroussin break; 57313b5b548SBaptiste Daroussin case 'q': 57413b5b548SBaptiste Daroussin goto QUIT; 57513b5b548SBaptiste Daroussin case 'r': 57613b5b548SBaptiste Daroussin case '2': 57713b5b548SBaptiste Daroussin /* Choose right column as-is. */ 57813b5b548SBaptiste Daroussin if (s2 != NULL) 57913b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", s2); 58013b5b548SBaptiste Daroussin /* End of command parsing. */ 58113b5b548SBaptiste Daroussin break; 58213b5b548SBaptiste Daroussin case 's': 58313b5b548SBaptiste Daroussin sflag = 1; 58413b5b548SBaptiste Daroussin goto PROMPT; 58513b5b548SBaptiste Daroussin case 'v': 58613b5b548SBaptiste Daroussin sflag = 0; 58713b5b548SBaptiste Daroussin /* FALLTHROUGH */ 58813b5b548SBaptiste Daroussin default: 58913b5b548SBaptiste Daroussin /* Interactive usage help. */ 59013b5b548SBaptiste Daroussin USAGE: 59113b5b548SBaptiste Daroussin int_usage(); 59213b5b548SBaptiste Daroussin PROMPT: 59313b5b548SBaptiste Daroussin putchar('%'); 59413b5b548SBaptiste Daroussin 59513b5b548SBaptiste Daroussin /* Prompt user again. */ 59613b5b548SBaptiste Daroussin continue; 59713b5b548SBaptiste Daroussin } 59813b5b548SBaptiste Daroussin free(cmd); 59913b5b548SBaptiste Daroussin return; 60013b5b548SBaptiste Daroussin } 60113b5b548SBaptiste Daroussin 60213b5b548SBaptiste Daroussin /* 60313b5b548SBaptiste Daroussin * If there was no error, we received an EOF from stdin, so we 60413b5b548SBaptiste Daroussin * should quit. 60513b5b548SBaptiste Daroussin */ 60613b5b548SBaptiste Daroussin QUIT: 60713b5b548SBaptiste Daroussin fclose(outfp); 60813b5b548SBaptiste Daroussin exit(0); 60913b5b548SBaptiste Daroussin } 61013b5b548SBaptiste Daroussin 61113b5b548SBaptiste Daroussin /* 61213b5b548SBaptiste Daroussin * Takes two strings, separated by a column divider. NULL strings are 61313b5b548SBaptiste Daroussin * treated as empty columns. If the divider is the ` ' character, the 61413b5b548SBaptiste Daroussin * second column is not printed (-l flag). In this case, the second 61513b5b548SBaptiste Daroussin * string must be NULL. When the second column is NULL, the divider 61613b5b548SBaptiste Daroussin * does not print the trailing space following the divider character. 61713b5b548SBaptiste Daroussin * 61813b5b548SBaptiste Daroussin * Takes into account that tabs can take multiple columns. 61913b5b548SBaptiste Daroussin */ 62013b5b548SBaptiste Daroussin static void 62113b5b548SBaptiste Daroussin println(const char *s1, const char div, const char *s2) 62213b5b548SBaptiste Daroussin { 62313b5b548SBaptiste Daroussin size_t col; 62413b5b548SBaptiste Daroussin 62513b5b548SBaptiste Daroussin /* Print first column. Skips if s1 == NULL. */ 62613b5b548SBaptiste Daroussin col = 0; 62713b5b548SBaptiste Daroussin if (s1) { 62813b5b548SBaptiste Daroussin /* Skip angle bracket and space. */ 62913b5b548SBaptiste Daroussin printcol(s1, &col, width); 63013b5b548SBaptiste Daroussin 63113b5b548SBaptiste Daroussin } 63213b5b548SBaptiste Daroussin 63313b5b548SBaptiste Daroussin /* Otherwise, we pad this column up to width. */ 63413b5b548SBaptiste Daroussin for (; col < width; ++col) 63513b5b548SBaptiste Daroussin putchar(' '); 63613b5b548SBaptiste Daroussin 63713b5b548SBaptiste Daroussin /* Only print left column. */ 63813b5b548SBaptiste Daroussin if (div == ' ' && !s2) { 63913b5b548SBaptiste Daroussin printf(" (\n"); 64013b5b548SBaptiste Daroussin return; 64113b5b548SBaptiste Daroussin } 64213b5b548SBaptiste Daroussin 64313b5b548SBaptiste Daroussin /* 64413b5b548SBaptiste Daroussin * Print column divider. If there is no second column, we don't 64513b5b548SBaptiste Daroussin * need to add the space for padding. 64613b5b548SBaptiste Daroussin */ 64713b5b548SBaptiste Daroussin if (!s2) { 64813b5b548SBaptiste Daroussin printf(" %c\n", div); 64913b5b548SBaptiste Daroussin return; 65013b5b548SBaptiste Daroussin } 65113b5b548SBaptiste Daroussin printf(" %c ", div); 65213b5b548SBaptiste Daroussin col += 3; 65313b5b548SBaptiste Daroussin 65413b5b548SBaptiste Daroussin /* Skip angle bracket and space. */ 65513b5b548SBaptiste Daroussin printcol(s2, &col, line_width); 65613b5b548SBaptiste Daroussin 65713b5b548SBaptiste Daroussin putchar('\n'); 65813b5b548SBaptiste Daroussin } 65913b5b548SBaptiste Daroussin 66013b5b548SBaptiste Daroussin /* 66113b5b548SBaptiste Daroussin * Reads a line from file and returns as a string. If EOF is reached, 66213b5b548SBaptiste Daroussin * NULL is returned. The returned string must be freed afterwards. 66313b5b548SBaptiste Daroussin */ 66413b5b548SBaptiste Daroussin static char * 66513b5b548SBaptiste Daroussin xfgets(FILE *file) 66613b5b548SBaptiste Daroussin { 6670c4ac56eSBaptiste Daroussin size_t linecap; 6680c4ac56eSBaptiste Daroussin ssize_t l; 66913b5b548SBaptiste Daroussin char *s; 67013b5b548SBaptiste Daroussin 67113b5b548SBaptiste Daroussin clearerr(file); 6720c4ac56eSBaptiste Daroussin linecap = 0; 6730c4ac56eSBaptiste Daroussin s = NULL; 67413b5b548SBaptiste Daroussin 6750c4ac56eSBaptiste Daroussin if ((l = getline(&s, &linecap, file)) == -1) { 6760c4ac56eSBaptiste Daroussin if (ferror(file)) 67713b5b548SBaptiste Daroussin err(2, "error reading file"); 67813b5b548SBaptiste Daroussin return (NULL); 67913b5b548SBaptiste Daroussin } 68013b5b548SBaptiste Daroussin 6810c4ac56eSBaptiste Daroussin if (s[l-1] == '\n') 6820c4ac56eSBaptiste Daroussin s[l-1] = '\0'; 6830c4ac56eSBaptiste Daroussin 68413b5b548SBaptiste Daroussin return (s); 68513b5b548SBaptiste Daroussin } 68613b5b548SBaptiste Daroussin 68713b5b548SBaptiste Daroussin /* 68813b5b548SBaptiste Daroussin * Parse ed commands from diffpipe and print lines from file1 (lines 68913b5b548SBaptiste Daroussin * to change or delete) or file2 (lines to add or change). 69013b5b548SBaptiste Daroussin * Returns EOF or 0. 69113b5b548SBaptiste Daroussin */ 69213b5b548SBaptiste Daroussin static int 69313b5b548SBaptiste Daroussin parsecmd(FILE *diffpipe, FILE *file1, FILE *file2) 69413b5b548SBaptiste Daroussin { 69513b5b548SBaptiste Daroussin size_t file1start, file1end, file2start, file2end, n; 69613b5b548SBaptiste Daroussin /* ed command line and pointer to characters in line */ 69713b5b548SBaptiste Daroussin char *line, *p, *q; 69813b5b548SBaptiste Daroussin const char *errstr; 69913b5b548SBaptiste Daroussin char c, cmd; 70013b5b548SBaptiste Daroussin 70113b5b548SBaptiste Daroussin /* Read ed command. */ 70213b5b548SBaptiste Daroussin if (!(line = xfgets(diffpipe))) 70313b5b548SBaptiste Daroussin return (EOF); 70413b5b548SBaptiste Daroussin 70513b5b548SBaptiste Daroussin p = line; 70613b5b548SBaptiste Daroussin /* Go to character after line number. */ 70713b5b548SBaptiste Daroussin while (isdigit(*p)) 70813b5b548SBaptiste Daroussin ++p; 70913b5b548SBaptiste Daroussin c = *p; 71013b5b548SBaptiste Daroussin *p++ = 0; 71113b5b548SBaptiste Daroussin file1start = strtonum(line, 0, INT_MAX, &errstr); 71213b5b548SBaptiste Daroussin if (errstr) 71313b5b548SBaptiste Daroussin errx(2, "file1 start is %s: %s", errstr, line); 71413b5b548SBaptiste Daroussin 71513b5b548SBaptiste Daroussin /* A range is specified for file1. */ 71613b5b548SBaptiste Daroussin if (c == ',') { 71713b5b548SBaptiste Daroussin q = p; 71813b5b548SBaptiste Daroussin /* Go to character after file2end. */ 71913b5b548SBaptiste Daroussin while (isdigit(*p)) 72013b5b548SBaptiste Daroussin ++p; 72113b5b548SBaptiste Daroussin c = *p; 72213b5b548SBaptiste Daroussin *p++ = 0; 72313b5b548SBaptiste Daroussin file1end = strtonum(q, 0, INT_MAX, &errstr); 72413b5b548SBaptiste Daroussin if (errstr) 72513b5b548SBaptiste Daroussin errx(2, "file1 end is %s: %s", errstr, line); 72613b5b548SBaptiste Daroussin if (file1start > file1end) 72713b5b548SBaptiste Daroussin errx(2, "invalid line range in file1: %s", line); 72813b5b548SBaptiste Daroussin } else 72913b5b548SBaptiste Daroussin file1end = file1start; 73013b5b548SBaptiste Daroussin 73113b5b548SBaptiste Daroussin cmd = c; 73213b5b548SBaptiste Daroussin /* Check that cmd is valid. */ 73313b5b548SBaptiste Daroussin if (!(cmd == 'a' || cmd == 'c' || cmd == 'd')) 73413b5b548SBaptiste Daroussin errx(2, "ed command not recognized: %c: %s", cmd, line); 73513b5b548SBaptiste Daroussin 73613b5b548SBaptiste Daroussin q = p; 73713b5b548SBaptiste Daroussin /* Go to character after line number. */ 73813b5b548SBaptiste Daroussin while (isdigit(*p)) 73913b5b548SBaptiste Daroussin ++p; 74013b5b548SBaptiste Daroussin c = *p; 74113b5b548SBaptiste Daroussin *p++ = 0; 74213b5b548SBaptiste Daroussin file2start = strtonum(q, 0, INT_MAX, &errstr); 74313b5b548SBaptiste Daroussin if (errstr) 74413b5b548SBaptiste Daroussin errx(2, "file2 start is %s: %s", errstr, line); 74513b5b548SBaptiste Daroussin 74613b5b548SBaptiste Daroussin /* 74713b5b548SBaptiste Daroussin * There should either be a comma signifying a second line 74813b5b548SBaptiste Daroussin * number or the line should just end here. 74913b5b548SBaptiste Daroussin */ 75013b5b548SBaptiste Daroussin if (c != ',' && c != '\0') 75113b5b548SBaptiste Daroussin errx(2, "invalid line range in file2: %c: %s", c, line); 75213b5b548SBaptiste Daroussin 75313b5b548SBaptiste Daroussin if (c == ',') { 75413b5b548SBaptiste Daroussin 75513b5b548SBaptiste Daroussin file2end = strtonum(p, 0, INT_MAX, &errstr); 75613b5b548SBaptiste Daroussin if (errstr) 75713b5b548SBaptiste Daroussin errx(2, "file2 end is %s: %s", errstr, line); 75813b5b548SBaptiste Daroussin if (file2start >= file2end) 75913b5b548SBaptiste Daroussin errx(2, "invalid line range in file2: %s", line); 76013b5b548SBaptiste Daroussin } else 76113b5b548SBaptiste Daroussin file2end = file2start; 76213b5b548SBaptiste Daroussin 76313b5b548SBaptiste Daroussin /* Appends happen _after_ stated line. */ 76413b5b548SBaptiste Daroussin if (cmd == 'a') { 76513b5b548SBaptiste Daroussin if (file1start != file1end) 76613b5b548SBaptiste Daroussin errx(2, "append cannot have a file1 range: %s", 76713b5b548SBaptiste Daroussin line); 76813b5b548SBaptiste Daroussin if (file1start == SIZE_MAX) 76913b5b548SBaptiste Daroussin errx(2, "file1 line range too high: %s", line); 77013b5b548SBaptiste Daroussin file1start = ++file1end; 77113b5b548SBaptiste Daroussin } 77213b5b548SBaptiste Daroussin /* 77313b5b548SBaptiste Daroussin * I'm not sure what the deal is with the line numbers for 77413b5b548SBaptiste Daroussin * deletes, though. 77513b5b548SBaptiste Daroussin */ 77613b5b548SBaptiste Daroussin else if (cmd == 'd') { 77713b5b548SBaptiste Daroussin if (file2start != file2end) 77813b5b548SBaptiste Daroussin errx(2, "delete cannot have a file2 range: %s", 77913b5b548SBaptiste Daroussin line); 78013b5b548SBaptiste Daroussin if (file2start == SIZE_MAX) 78113b5b548SBaptiste Daroussin errx(2, "file2 line range too high: %s", line); 78213b5b548SBaptiste Daroussin file2start = ++file2end; 78313b5b548SBaptiste Daroussin } 78413b5b548SBaptiste Daroussin 78513b5b548SBaptiste Daroussin /* 78613b5b548SBaptiste Daroussin * Continue reading file1 and file2 until we reach line numbers 78713b5b548SBaptiste Daroussin * specified by diff. Should only happen with -I flag. 78813b5b548SBaptiste Daroussin */ 78913b5b548SBaptiste Daroussin for (; file1ln < file1start && file2ln < file2start; 79013b5b548SBaptiste Daroussin ++file1ln, ++file2ln) { 79113b5b548SBaptiste Daroussin char *s1, *s2; 79213b5b548SBaptiste Daroussin 79313b5b548SBaptiste Daroussin if (!(s1 = xfgets(file1))) 79413b5b548SBaptiste Daroussin errx(2, "file1 shorter than expected"); 79513b5b548SBaptiste Daroussin if (!(s2 = xfgets(file2))) 79613b5b548SBaptiste Daroussin errx(2, "file2 shorter than expected"); 79713b5b548SBaptiste Daroussin 79813b5b548SBaptiste Daroussin /* If the -l flag was specified, print only left column. */ 79913b5b548SBaptiste Daroussin if (lflag) { 80013b5b548SBaptiste Daroussin free(s2); 80113b5b548SBaptiste Daroussin /* 80213b5b548SBaptiste Daroussin * XXX - If -l and -I are both specified, all 80313b5b548SBaptiste Daroussin * unchanged or ignored lines are shown with a 80413b5b548SBaptiste Daroussin * `(' divider. This matches GNU sdiff, but I 80513b5b548SBaptiste Daroussin * believe it is a bug. Just check out: 80613b5b548SBaptiste Daroussin * gsdiff -l -I '^$' samefile samefile. 80713b5b548SBaptiste Daroussin */ 80813b5b548SBaptiste Daroussin if (Iflag) 80913b5b548SBaptiste Daroussin enqueue(s1, '(', NULL); 81013b5b548SBaptiste Daroussin else 81113b5b548SBaptiste Daroussin enqueue(s1, ' ', NULL); 81213b5b548SBaptiste Daroussin } else 81313b5b548SBaptiste Daroussin enqueue(s1, ' ', s2); 81413b5b548SBaptiste Daroussin } 81513b5b548SBaptiste Daroussin /* Ignore deleted lines. */ 81613b5b548SBaptiste Daroussin for (; file1ln < file1start; ++file1ln) { 81713b5b548SBaptiste Daroussin char *s; 81813b5b548SBaptiste Daroussin 81913b5b548SBaptiste Daroussin if (!(s = xfgets(file1))) 82013b5b548SBaptiste Daroussin errx(2, "file1 shorter than expected"); 82113b5b548SBaptiste Daroussin 82213b5b548SBaptiste Daroussin enqueue(s, '(', NULL); 82313b5b548SBaptiste Daroussin } 82413b5b548SBaptiste Daroussin /* Ignore added lines. */ 82513b5b548SBaptiste Daroussin for (; file2ln < file2start; ++file2ln) { 82613b5b548SBaptiste Daroussin char *s; 82713b5b548SBaptiste Daroussin 82813b5b548SBaptiste Daroussin if (!(s = xfgets(file2))) 82913b5b548SBaptiste Daroussin errx(2, "file2 shorter than expected"); 83013b5b548SBaptiste Daroussin 83113b5b548SBaptiste Daroussin /* If -l flag was given, don't print right column. */ 83213b5b548SBaptiste Daroussin if (lflag) 83313b5b548SBaptiste Daroussin free(s); 83413b5b548SBaptiste Daroussin else 83513b5b548SBaptiste Daroussin enqueue(NULL, ')', s); 83613b5b548SBaptiste Daroussin } 83713b5b548SBaptiste Daroussin 83813b5b548SBaptiste Daroussin /* Process unmodified or skipped lines. */ 83913b5b548SBaptiste Daroussin processq(); 84013b5b548SBaptiste Daroussin 84113b5b548SBaptiste Daroussin switch (cmd) { 84213b5b548SBaptiste Daroussin case 'a': 84313b5b548SBaptiste Daroussin printa(file2, file2end); 84413b5b548SBaptiste Daroussin n = file2end - file2start + 1; 84513b5b548SBaptiste Daroussin break; 84613b5b548SBaptiste Daroussin case 'c': 84713b5b548SBaptiste Daroussin printc(file1, file1end, file2, file2end); 84813b5b548SBaptiste Daroussin n = file1end - file1start + 1 + 1 + file2end - file2start + 1; 84913b5b548SBaptiste Daroussin break; 85013b5b548SBaptiste Daroussin case 'd': 85113b5b548SBaptiste Daroussin printd(file1, file1end); 85213b5b548SBaptiste Daroussin n = file1end - file1start + 1; 85313b5b548SBaptiste Daroussin break; 85413b5b548SBaptiste Daroussin default: 85513b5b548SBaptiste Daroussin errx(2, "invalid diff command: %c: %s", cmd, line); 85613b5b548SBaptiste Daroussin } 85713b5b548SBaptiste Daroussin free(line); 85813b5b548SBaptiste Daroussin 85913b5b548SBaptiste Daroussin /* Skip to next ed line. */ 86013b5b548SBaptiste Daroussin while (n--) { 86113b5b548SBaptiste Daroussin if (!(line = xfgets(diffpipe))) 86213b5b548SBaptiste Daroussin errx(2, "diff ended early"); 86313b5b548SBaptiste Daroussin free(line); 86413b5b548SBaptiste Daroussin } 86513b5b548SBaptiste Daroussin 86613b5b548SBaptiste Daroussin return (0); 86713b5b548SBaptiste Daroussin } 86813b5b548SBaptiste Daroussin 86913b5b548SBaptiste Daroussin /* 87013b5b548SBaptiste Daroussin * Queues up a diff line. 87113b5b548SBaptiste Daroussin */ 87213b5b548SBaptiste Daroussin static void 87313b5b548SBaptiste Daroussin enqueue(char *left, char div, char *right) 87413b5b548SBaptiste Daroussin { 87513b5b548SBaptiste Daroussin struct diffline *diffp; 87613b5b548SBaptiste Daroussin 87713b5b548SBaptiste Daroussin if (!(diffp = malloc(sizeof(struct diffline)))) 87813b5b548SBaptiste Daroussin err(2, "enqueue"); 87913b5b548SBaptiste Daroussin diffp->left = left; 88013b5b548SBaptiste Daroussin diffp->div = div; 88113b5b548SBaptiste Daroussin diffp->right = right; 88213b5b548SBaptiste Daroussin STAILQ_INSERT_TAIL(&diffhead, diffp, diffentries); 88313b5b548SBaptiste Daroussin } 88413b5b548SBaptiste Daroussin 88513b5b548SBaptiste Daroussin /* 88613b5b548SBaptiste Daroussin * Free a diffline structure and its elements. 88713b5b548SBaptiste Daroussin */ 88813b5b548SBaptiste Daroussin static void 88913b5b548SBaptiste Daroussin freediff(struct diffline *diffp) 89013b5b548SBaptiste Daroussin { 89113b5b548SBaptiste Daroussin 89213b5b548SBaptiste Daroussin free(diffp->left); 89313b5b548SBaptiste Daroussin free(diffp->right); 89413b5b548SBaptiste Daroussin free(diffp); 89513b5b548SBaptiste Daroussin } 89613b5b548SBaptiste Daroussin 89713b5b548SBaptiste Daroussin /* 89813b5b548SBaptiste Daroussin * Append second string into first. Repeated appends to the same string 89913b5b548SBaptiste Daroussin * are cached, making this an O(n) function, where n = strlen(append). 90013b5b548SBaptiste Daroussin */ 90113b5b548SBaptiste Daroussin static void 90213b5b548SBaptiste Daroussin astrcat(char **s, const char *append) 90313b5b548SBaptiste Daroussin { 90413b5b548SBaptiste Daroussin /* Length of string in previous run. */ 90513b5b548SBaptiste Daroussin static size_t offset = 0; 90613b5b548SBaptiste Daroussin size_t newsiz; 90713b5b548SBaptiste Daroussin /* 90813b5b548SBaptiste Daroussin * String from previous run. Compared to *s to see if we are 90913b5b548SBaptiste Daroussin * dealing with the same string. If so, we can use offset. 91013b5b548SBaptiste Daroussin */ 91113b5b548SBaptiste Daroussin static const char *oldstr = NULL; 91213b5b548SBaptiste Daroussin char *newstr; 91313b5b548SBaptiste Daroussin 91413b5b548SBaptiste Daroussin /* 91513b5b548SBaptiste Daroussin * First string is NULL, so just copy append. 91613b5b548SBaptiste Daroussin */ 91713b5b548SBaptiste Daroussin if (!*s) { 91813b5b548SBaptiste Daroussin if (!(*s = strdup(append))) 91913b5b548SBaptiste Daroussin err(2, "astrcat"); 92013b5b548SBaptiste Daroussin 92113b5b548SBaptiste Daroussin /* Keep track of string. */ 92213b5b548SBaptiste Daroussin offset = strlen(*s); 92313b5b548SBaptiste Daroussin oldstr = *s; 92413b5b548SBaptiste Daroussin 92513b5b548SBaptiste Daroussin return; 92613b5b548SBaptiste Daroussin } 92713b5b548SBaptiste Daroussin 92813b5b548SBaptiste Daroussin /* 92913b5b548SBaptiste Daroussin * *s is a string so concatenate. 93013b5b548SBaptiste Daroussin */ 93113b5b548SBaptiste Daroussin 93213b5b548SBaptiste Daroussin /* Did we process the same string in the last run? */ 93313b5b548SBaptiste Daroussin /* 93413b5b548SBaptiste Daroussin * If this is a different string from the one we just processed 93513b5b548SBaptiste Daroussin * cache new string. 93613b5b548SBaptiste Daroussin */ 93713b5b548SBaptiste Daroussin if (oldstr != *s) { 93813b5b548SBaptiste Daroussin offset = strlen(*s); 93913b5b548SBaptiste Daroussin oldstr = *s; 94013b5b548SBaptiste Daroussin } 94113b5b548SBaptiste Daroussin 94213b5b548SBaptiste Daroussin /* Size = strlen(*s) + \n + strlen(append) + '\0'. */ 94313b5b548SBaptiste Daroussin newsiz = offset + 1 + strlen(append) + 1; 94413b5b548SBaptiste Daroussin 94513b5b548SBaptiste Daroussin /* Resize *s to fit new string. */ 94613b5b548SBaptiste Daroussin newstr = realloc(*s, newsiz); 94713b5b548SBaptiste Daroussin if (newstr == NULL) 94813b5b548SBaptiste Daroussin err(2, "astrcat"); 94913b5b548SBaptiste Daroussin *s = newstr; 95013b5b548SBaptiste Daroussin 95113b5b548SBaptiste Daroussin /* *s + offset should be end of string. */ 95213b5b548SBaptiste Daroussin /* Concatenate. */ 95313b5b548SBaptiste Daroussin strlcpy(*s + offset, "\n", newsiz - offset); 95413b5b548SBaptiste Daroussin strlcat(*s + offset, append, newsiz - offset); 95513b5b548SBaptiste Daroussin 95613b5b548SBaptiste Daroussin /* New string length should be exactly newsiz - 1 characters. */ 95713b5b548SBaptiste Daroussin /* Store generated string's values. */ 95813b5b548SBaptiste Daroussin offset = newsiz - 1; 95913b5b548SBaptiste Daroussin oldstr = *s; 96013b5b548SBaptiste Daroussin } 96113b5b548SBaptiste Daroussin 96213b5b548SBaptiste Daroussin /* 96313b5b548SBaptiste Daroussin * Process diff set queue, printing, prompting, and saving each diff 96413b5b548SBaptiste Daroussin * line stored in queue. 96513b5b548SBaptiste Daroussin */ 96613b5b548SBaptiste Daroussin static void 96713b5b548SBaptiste Daroussin processq(void) 96813b5b548SBaptiste Daroussin { 96913b5b548SBaptiste Daroussin struct diffline *diffp; 97013b5b548SBaptiste Daroussin char divc, *left, *right; 97113b5b548SBaptiste Daroussin 97213b5b548SBaptiste Daroussin /* Don't process empty queue. */ 97313b5b548SBaptiste Daroussin if (STAILQ_EMPTY(&diffhead)) 97413b5b548SBaptiste Daroussin return; 97513b5b548SBaptiste Daroussin 97613b5b548SBaptiste Daroussin /* Remember the divider. */ 97713b5b548SBaptiste Daroussin divc = STAILQ_FIRST(&diffhead)->div; 97813b5b548SBaptiste Daroussin 97913b5b548SBaptiste Daroussin left = NULL; 98013b5b548SBaptiste Daroussin right = NULL; 98113b5b548SBaptiste Daroussin /* 98213b5b548SBaptiste Daroussin * Go through set of diffs, concatenating each line in left or 98313b5b548SBaptiste Daroussin * right column into two long strings, `left' and `right'. 98413b5b548SBaptiste Daroussin */ 98513b5b548SBaptiste Daroussin STAILQ_FOREACH(diffp, &diffhead, diffentries) { 98613b5b548SBaptiste Daroussin /* 98713b5b548SBaptiste Daroussin * Print changed lines if -s was given, 98813b5b548SBaptiste Daroussin * print all lines if -s was not given. 98913b5b548SBaptiste Daroussin */ 99013b5b548SBaptiste Daroussin if (!sflag || diffp->div == '|' || diffp->div == '<' || 99113b5b548SBaptiste Daroussin diffp->div == '>') 99213b5b548SBaptiste Daroussin println(diffp->left, diffp->div, diffp->right); 99313b5b548SBaptiste Daroussin 99413b5b548SBaptiste Daroussin /* Append new lines to diff set. */ 99513b5b548SBaptiste Daroussin if (diffp->left) 99613b5b548SBaptiste Daroussin astrcat(&left, diffp->left); 99713b5b548SBaptiste Daroussin if (diffp->right) 99813b5b548SBaptiste Daroussin astrcat(&right, diffp->right); 99913b5b548SBaptiste Daroussin } 100013b5b548SBaptiste Daroussin 100113b5b548SBaptiste Daroussin /* Empty queue and free each diff line and its elements. */ 100213b5b548SBaptiste Daroussin while (!STAILQ_EMPTY(&diffhead)) { 100313b5b548SBaptiste Daroussin diffp = STAILQ_FIRST(&diffhead); 100413b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&diffhead, diffentries); 100513b5b548SBaptiste Daroussin freediff(diffp); 100613b5b548SBaptiste Daroussin } 100713b5b548SBaptiste Daroussin 100813b5b548SBaptiste Daroussin /* Write to outfp, prompting user if lines are different. */ 100913b5b548SBaptiste Daroussin if (outfp) 101013b5b548SBaptiste Daroussin switch (divc) { 101113b5b548SBaptiste Daroussin case ' ': case '(': case ')': 101213b5b548SBaptiste Daroussin fprintf(outfp, "%s\n", left); 101313b5b548SBaptiste Daroussin break; 101413b5b548SBaptiste Daroussin case '|': case '<': case '>': 101513b5b548SBaptiste Daroussin prompt(left, right); 101613b5b548SBaptiste Daroussin break; 101713b5b548SBaptiste Daroussin default: 101813b5b548SBaptiste Daroussin errx(2, "invalid divider: %c", divc); 101913b5b548SBaptiste Daroussin } 102013b5b548SBaptiste Daroussin 102113b5b548SBaptiste Daroussin /* Free left and right. */ 102213b5b548SBaptiste Daroussin free(left); 102313b5b548SBaptiste Daroussin free(right); 102413b5b548SBaptiste Daroussin } 102513b5b548SBaptiste Daroussin 102613b5b548SBaptiste Daroussin /* 102713b5b548SBaptiste Daroussin * Print lines following an (a)ppend command. 102813b5b548SBaptiste Daroussin */ 102913b5b548SBaptiste Daroussin static void 103013b5b548SBaptiste Daroussin printa(FILE *file, size_t line2) 103113b5b548SBaptiste Daroussin { 103213b5b548SBaptiste Daroussin char *line; 103313b5b548SBaptiste Daroussin 103413b5b548SBaptiste Daroussin for (; file2ln <= line2; ++file2ln) { 103513b5b548SBaptiste Daroussin if (!(line = xfgets(file))) 103613b5b548SBaptiste Daroussin errx(2, "append ended early"); 103713b5b548SBaptiste Daroussin enqueue(NULL, '>', line); 103813b5b548SBaptiste Daroussin } 103913b5b548SBaptiste Daroussin processq(); 104013b5b548SBaptiste Daroussin } 104113b5b548SBaptiste Daroussin 104213b5b548SBaptiste Daroussin /* 104313b5b548SBaptiste Daroussin * Print lines following a (c)hange command, from file1ln to file1end 104413b5b548SBaptiste Daroussin * and from file2ln to file2end. 104513b5b548SBaptiste Daroussin */ 104613b5b548SBaptiste Daroussin static void 104713b5b548SBaptiste Daroussin printc(FILE *file1, size_t file1end, FILE *file2, size_t file2end) 104813b5b548SBaptiste Daroussin { 104913b5b548SBaptiste Daroussin struct fileline { 105013b5b548SBaptiste Daroussin STAILQ_ENTRY(fileline) fileentries; 105113b5b548SBaptiste Daroussin char *line; 105213b5b548SBaptiste Daroussin }; 105313b5b548SBaptiste Daroussin STAILQ_HEAD(, fileline) delqhead = STAILQ_HEAD_INITIALIZER(delqhead); 105413b5b548SBaptiste Daroussin 105513b5b548SBaptiste Daroussin /* Read lines to be deleted. */ 105613b5b548SBaptiste Daroussin for (; file1ln <= file1end; ++file1ln) { 105713b5b548SBaptiste Daroussin struct fileline *linep; 105813b5b548SBaptiste Daroussin char *line1; 105913b5b548SBaptiste Daroussin 106013b5b548SBaptiste Daroussin /* Read lines from both. */ 106113b5b548SBaptiste Daroussin if (!(line1 = xfgets(file1))) 106213b5b548SBaptiste Daroussin errx(2, "error reading file1 in delete in change"); 106313b5b548SBaptiste Daroussin 106413b5b548SBaptiste Daroussin /* Add to delete queue. */ 106513b5b548SBaptiste Daroussin if (!(linep = malloc(sizeof(struct fileline)))) 106613b5b548SBaptiste Daroussin err(2, "printc"); 106713b5b548SBaptiste Daroussin linep->line = line1; 106813b5b548SBaptiste Daroussin STAILQ_INSERT_TAIL(&delqhead, linep, fileentries); 106913b5b548SBaptiste Daroussin } 107013b5b548SBaptiste Daroussin 107113b5b548SBaptiste Daroussin /* Process changed lines.. */ 107213b5b548SBaptiste Daroussin for (; !STAILQ_EMPTY(&delqhead) && file2ln <= file2end; 107313b5b548SBaptiste Daroussin ++file2ln) { 107413b5b548SBaptiste Daroussin struct fileline *del; 107513b5b548SBaptiste Daroussin char *add; 107613b5b548SBaptiste Daroussin 107713b5b548SBaptiste Daroussin /* Get add line. */ 107813b5b548SBaptiste Daroussin if (!(add = xfgets(file2))) 107913b5b548SBaptiste Daroussin errx(2, "error reading add in change"); 108013b5b548SBaptiste Daroussin 108113b5b548SBaptiste Daroussin del = STAILQ_FIRST(&delqhead); 108213b5b548SBaptiste Daroussin enqueue(del->line, '|', add); 108313b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&delqhead, fileentries); 108413b5b548SBaptiste Daroussin /* 108513b5b548SBaptiste Daroussin * Free fileline structure but not its elements since 108613b5b548SBaptiste Daroussin * they are queued up. 108713b5b548SBaptiste Daroussin */ 108813b5b548SBaptiste Daroussin free(del); 108913b5b548SBaptiste Daroussin } 109013b5b548SBaptiste Daroussin processq(); 109113b5b548SBaptiste Daroussin 109213b5b548SBaptiste Daroussin /* Process remaining lines to add. */ 109313b5b548SBaptiste Daroussin for (; file2ln <= file2end; ++file2ln) { 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 enqueue(NULL, '>', add); 110113b5b548SBaptiste Daroussin } 110213b5b548SBaptiste Daroussin processq(); 110313b5b548SBaptiste Daroussin 110413b5b548SBaptiste Daroussin /* Process remaining lines to delete. */ 110513b5b548SBaptiste Daroussin while (!STAILQ_EMPTY(&delqhead)) { 110613b5b548SBaptiste Daroussin struct fileline *filep; 110713b5b548SBaptiste Daroussin 110813b5b548SBaptiste Daroussin filep = STAILQ_FIRST(&delqhead); 110913b5b548SBaptiste Daroussin enqueue(filep->line, '<', NULL); 111013b5b548SBaptiste Daroussin STAILQ_REMOVE_HEAD(&delqhead, fileentries); 111113b5b548SBaptiste Daroussin free(filep); 111213b5b548SBaptiste Daroussin } 111313b5b548SBaptiste Daroussin processq(); 111413b5b548SBaptiste Daroussin } 111513b5b548SBaptiste Daroussin 111613b5b548SBaptiste Daroussin /* 111713b5b548SBaptiste Daroussin * Print deleted lines from file, from file1ln to file1end. 111813b5b548SBaptiste Daroussin */ 111913b5b548SBaptiste Daroussin static void 112013b5b548SBaptiste Daroussin printd(FILE *file1, size_t file1end) 112113b5b548SBaptiste Daroussin { 112213b5b548SBaptiste Daroussin char *line1; 112313b5b548SBaptiste Daroussin 112413b5b548SBaptiste Daroussin /* Print out lines file1ln to line2. */ 112513b5b548SBaptiste Daroussin for (; file1ln <= file1end; ++file1ln) { 112613b5b548SBaptiste Daroussin if (!(line1 = xfgets(file1))) 112713b5b548SBaptiste Daroussin errx(2, "file1 ended early in delete"); 112813b5b548SBaptiste Daroussin enqueue(line1, '<', NULL); 112913b5b548SBaptiste Daroussin } 113013b5b548SBaptiste Daroussin processq(); 113113b5b548SBaptiste Daroussin } 113213b5b548SBaptiste Daroussin 113313b5b548SBaptiste Daroussin /* 113413b5b548SBaptiste Daroussin * Interactive mode usage. 113513b5b548SBaptiste Daroussin */ 113613b5b548SBaptiste Daroussin static void 113713b5b548SBaptiste Daroussin int_usage(void) 113813b5b548SBaptiste Daroussin { 113913b5b548SBaptiste Daroussin 114013b5b548SBaptiste Daroussin puts("e:\tedit blank diff\n" 114113b5b548SBaptiste Daroussin "eb:\tedit both diffs concatenated\n" 114213b5b548SBaptiste Daroussin "el:\tedit left diff\n" 114313b5b548SBaptiste Daroussin "er:\tedit right diff\n" 114413b5b548SBaptiste Daroussin "l | 1:\tchoose left diff\n" 114513b5b548SBaptiste Daroussin "r | 2:\tchoose right diff\n" 114613b5b548SBaptiste Daroussin "s:\tsilent mode--don't print identical lines\n" 114713b5b548SBaptiste Daroussin "v:\tverbose mode--print identical lines\n" 114813b5b548SBaptiste Daroussin "q:\tquit"); 114913b5b548SBaptiste Daroussin } 115013b5b548SBaptiste Daroussin 115113b5b548SBaptiste Daroussin static void 115213b5b548SBaptiste Daroussin usage(void) 115313b5b548SBaptiste Daroussin { 115413b5b548SBaptiste Daroussin 115513b5b548SBaptiste Daroussin fprintf(stderr, 1156*85bb63bdSBaptiste Daroussin "usage: sdiff [-abdilstHW] [-I regexp] [-o outfile] [-w width] file1" 115713b5b548SBaptiste Daroussin " file2\n"); 115813b5b548SBaptiste Daroussin exit(2); 115913b5b548SBaptiste Daroussin } 1160