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