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