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