xref: /plan9/sys/src/cmd/diff/diffio.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
4*219b2ee8SDavid du Colombier #include <ctype.h>
53e12c5d1SDavid du Colombier #include "diff.h"
63e12c5d1SDavid du Colombier 
73e12c5d1SDavid du Colombier struct line {
83e12c5d1SDavid du Colombier 	int	serial;
93e12c5d1SDavid du Colombier 	int	value;
103e12c5d1SDavid du Colombier };
113e12c5d1SDavid du Colombier extern struct line *file[2];
123e12c5d1SDavid du Colombier extern int len[2];
133e12c5d1SDavid du Colombier extern long *ixold, *ixnew;
143e12c5d1SDavid du Colombier extern int *J;
153e12c5d1SDavid du Colombier 
163e12c5d1SDavid du Colombier static Biobuf *input[2];
173e12c5d1SDavid du Colombier static char *file1, *file2;
183e12c5d1SDavid du Colombier static int firstchange;
193e12c5d1SDavid du Colombier 
203e12c5d1SDavid du Colombier #define MAXLINELEN	4096
213e12c5d1SDavid du Colombier #define MIN(x, y)	((x) < (y) ? (x): (y))
223e12c5d1SDavid du Colombier 
233e12c5d1SDavid du Colombier static int
243e12c5d1SDavid du Colombier readline(Biobuf *bp, char *buf)
253e12c5d1SDavid du Colombier {
263e12c5d1SDavid du Colombier 	int c;
273e12c5d1SDavid du Colombier 	char *p, *e;
283e12c5d1SDavid du Colombier 
293e12c5d1SDavid du Colombier 	p = buf;
303e12c5d1SDavid du Colombier 	e = p + MAXLINELEN-1;
313e12c5d1SDavid du Colombier 	do {
323e12c5d1SDavid du Colombier 		c = Bgetc(bp);
333e12c5d1SDavid du Colombier 		if (c < 0) {
343e12c5d1SDavid du Colombier 			if (p == buf)
353e12c5d1SDavid du Colombier 				return -1;
363e12c5d1SDavid du Colombier 			break;
373e12c5d1SDavid du Colombier 		}
383e12c5d1SDavid du Colombier 		if (c == '\n')
393e12c5d1SDavid du Colombier 			break;
403e12c5d1SDavid du Colombier 		*p++ = c;
413e12c5d1SDavid du Colombier 	} while (p < e);
423e12c5d1SDavid du Colombier 	*p = 0;
433e12c5d1SDavid du Colombier 	if (c != '\n' && c >= 0) {
443e12c5d1SDavid du Colombier 		do c = Bgetc(bp);
453e12c5d1SDavid du Colombier 		while (c >= 0 && c != '\n');
463e12c5d1SDavid du Colombier 	}
473e12c5d1SDavid du Colombier 	return p - buf;
483e12c5d1SDavid du Colombier }
493e12c5d1SDavid du Colombier 
503e12c5d1SDavid du Colombier #define HALFLONG 16
513e12c5d1SDavid du Colombier #define low(x)	(x&((1L<<HALFLONG)-1))
523e12c5d1SDavid du Colombier #define high(x)	(x>>HALFLONG)
533e12c5d1SDavid du Colombier 
543e12c5d1SDavid du Colombier /*
553e12c5d1SDavid du Colombier  * hashing has the effect of
563e12c5d1SDavid du Colombier  * arranging line in 7-bit bytes and then
573e12c5d1SDavid du Colombier  * summing 1-s complement in 16-bit hunks
583e12c5d1SDavid du Colombier  */
593e12c5d1SDavid du Colombier static int
603e12c5d1SDavid du Colombier readhash(Biobuf *bp, char *buf)
613e12c5d1SDavid du Colombier {
623e12c5d1SDavid du Colombier 	long sum;
633e12c5d1SDavid du Colombier 	unsigned shift;
643e12c5d1SDavid du Colombier 	char *p;
653e12c5d1SDavid du Colombier 	int len, space;
663e12c5d1SDavid du Colombier 
673e12c5d1SDavid du Colombier 	sum = 1;
683e12c5d1SDavid du Colombier 	shift = 0;
693e12c5d1SDavid du Colombier 	if ((len = readline(bp, buf)) == -1)
703e12c5d1SDavid du Colombier 		return 0;
713e12c5d1SDavid du Colombier 	p = buf;
723e12c5d1SDavid du Colombier 	switch(bflag)	/* various types of white space handling */
733e12c5d1SDavid du Colombier 	{
743e12c5d1SDavid du Colombier 	case 0:
753e12c5d1SDavid du Colombier 		while (len--) {
763e12c5d1SDavid du Colombier 			sum += (long)*p++ << (shift &= (HALFLONG-1));
773e12c5d1SDavid du Colombier 			shift += 7;
783e12c5d1SDavid du Colombier 		}
793e12c5d1SDavid du Colombier 		break;
803e12c5d1SDavid du Colombier 	case 1:
813e12c5d1SDavid du Colombier 		/*
823e12c5d1SDavid du Colombier 		 * coalesce multiple white-space
833e12c5d1SDavid du Colombier 		 */
843e12c5d1SDavid du Colombier 		for (space = 0; len--; p++) {
853e12c5d1SDavid du Colombier 			if (isspace(*p)) {
863e12c5d1SDavid du Colombier 				space++;
873e12c5d1SDavid du Colombier 				continue;
883e12c5d1SDavid du Colombier 			}
893e12c5d1SDavid du Colombier 			if (space) {
903e12c5d1SDavid du Colombier 				shift += 7;
913e12c5d1SDavid du Colombier 				space = 0;
923e12c5d1SDavid du Colombier 			}
933e12c5d1SDavid du Colombier 			sum += (long)*p << (shift &= (HALFLONG-1));
943e12c5d1SDavid du Colombier 			shift += 7;
953e12c5d1SDavid du Colombier 		}
963e12c5d1SDavid du Colombier 		break;
973e12c5d1SDavid du Colombier 	default:
983e12c5d1SDavid du Colombier 		/*
993e12c5d1SDavid du Colombier 		 * strip all white-space
1003e12c5d1SDavid du Colombier 		 */
1013e12c5d1SDavid du Colombier 		while (len--) {
1023e12c5d1SDavid du Colombier 			if (isspace(*p)) {
1033e12c5d1SDavid du Colombier 				p++;
1043e12c5d1SDavid du Colombier 				continue;
1053e12c5d1SDavid du Colombier 			}
1063e12c5d1SDavid du Colombier 			sum += (long)*p++ << (shift &= (HALFLONG-1));
1073e12c5d1SDavid du Colombier 			shift += 7;
1083e12c5d1SDavid du Colombier 		}
1093e12c5d1SDavid du Colombier 		break;
1103e12c5d1SDavid du Colombier 	}
1113e12c5d1SDavid du Colombier 	sum = low(sum) + high(sum);
1123e12c5d1SDavid du Colombier 	return ((short)low(sum) + (short)high(sum));
1133e12c5d1SDavid du Colombier }
1143e12c5d1SDavid du Colombier 
1153e12c5d1SDavid du Colombier Biobuf *
1163e12c5d1SDavid du Colombier prepare(int i, char *arg)
1173e12c5d1SDavid du Colombier {
1183e12c5d1SDavid du Colombier 	struct line *p;
1193e12c5d1SDavid du Colombier 	int j, h;
1203e12c5d1SDavid du Colombier 	Biobuf *bp;
1213e12c5d1SDavid du Colombier 	char *cp, buf[MAXLINELEN];
1223e12c5d1SDavid du Colombier 	int nbytes;
1233e12c5d1SDavid du Colombier 	Rune r;
1243e12c5d1SDavid du Colombier 
1253e12c5d1SDavid du Colombier 	bp = Bopen(arg, OREAD);
1263e12c5d1SDavid du Colombier 	if (!bp) {
127bd389b36SDavid du Colombier 		panic(mflag ? 0: 2, "cannot open %s: %r\n", arg);
1283e12c5d1SDavid du Colombier 		return 0;
1293e12c5d1SDavid du Colombier 	}
1303e12c5d1SDavid du Colombier 	nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN));
1313e12c5d1SDavid du Colombier 	if (nbytes > 0) {
1323e12c5d1SDavid du Colombier 		cp = buf;
133bd389b36SDavid du Colombier 		while (cp < buf+nbytes-UTFmax) {
1343e12c5d1SDavid du Colombier 			/*
1353e12c5d1SDavid du Colombier 			 * heuristic for a binary file in the
1363e12c5d1SDavid du Colombier 			 * brave new UNICODE world
1373e12c5d1SDavid du Colombier 			 */
1383e12c5d1SDavid du Colombier 			cp += chartorune(&r, cp);
1393e12c5d1SDavid du Colombier 			if (r == 0 || (r > 0x7f && r <= 0xa0)) {
140*219b2ee8SDavid du Colombier 				Bterm(bp);
1413e12c5d1SDavid du Colombier 				panic(mflag ? 0: 2, "binary file %s\n", arg);
1423e12c5d1SDavid du Colombier 				return 0;
1433e12c5d1SDavid du Colombier 			}
1443e12c5d1SDavid du Colombier 		}
1453e12c5d1SDavid du Colombier 		Bseek(bp, 0, 0);
1463e12c5d1SDavid du Colombier 	}
1473e12c5d1SDavid du Colombier 	p = MALLOC(struct line, 3);
1483e12c5d1SDavid du Colombier 	for (j = 0; h = readhash(bp, buf); p[j].value = h)
1493e12c5d1SDavid du Colombier 		p = REALLOC(p, struct line, (++j+3));
1503e12c5d1SDavid du Colombier 	len[i] = j;
1513e12c5d1SDavid du Colombier 	file[i] = p;
1523e12c5d1SDavid du Colombier 	input[i] = bp;			/*fix*/
1533e12c5d1SDavid du Colombier 	if (i == 0) {			/*fix*/
1543e12c5d1SDavid du Colombier 		file1 = arg;
1553e12c5d1SDavid du Colombier 		firstchange = 0;
1563e12c5d1SDavid du Colombier 	}
1573e12c5d1SDavid du Colombier 	else
1583e12c5d1SDavid du Colombier 		file2 = arg;
1593e12c5d1SDavid du Colombier 	return bp;
1603e12c5d1SDavid du Colombier }
1613e12c5d1SDavid du Colombier 
1623e12c5d1SDavid du Colombier static int
1633e12c5d1SDavid du Colombier squishspace(char *buf)
1643e12c5d1SDavid du Colombier {
1653e12c5d1SDavid du Colombier 	char *p, *q;
1663e12c5d1SDavid du Colombier 	int space;
1673e12c5d1SDavid du Colombier 
1683e12c5d1SDavid du Colombier 	for (space = 0, q = p = buf; *q; q++) {
1693e12c5d1SDavid du Colombier 		if (isspace(*q)) {
1703e12c5d1SDavid du Colombier 			space++;
1713e12c5d1SDavid du Colombier 			continue;
1723e12c5d1SDavid du Colombier 		}
1733e12c5d1SDavid du Colombier 		if (space && bflag == 1) {
1743e12c5d1SDavid du Colombier 			*p++ = ' ';
1753e12c5d1SDavid du Colombier 			space = 0;
1763e12c5d1SDavid du Colombier 		}
1773e12c5d1SDavid du Colombier 		*p++ = *q;
1783e12c5d1SDavid du Colombier 	}
1793e12c5d1SDavid du Colombier 	*p = 0;
1803e12c5d1SDavid du Colombier 	return p - buf;
1813e12c5d1SDavid du Colombier }
1823e12c5d1SDavid du Colombier 
1833e12c5d1SDavid du Colombier /*
1843e12c5d1SDavid du Colombier  * need to fix up for unexpected EOF's
1853e12c5d1SDavid du Colombier  */
1863e12c5d1SDavid du Colombier void
1873e12c5d1SDavid du Colombier check(Biobuf *bf, Biobuf *bt)
1883e12c5d1SDavid du Colombier {
1893e12c5d1SDavid du Colombier 	int f, t, flen, tlen;
1903e12c5d1SDavid du Colombier 	char fbuf[MAXLINELEN], tbuf[MAXLINELEN];
1913e12c5d1SDavid du Colombier 
1923e12c5d1SDavid du Colombier 	ixold[0] = ixnew[0] = 0;
1933e12c5d1SDavid du Colombier 	for (f = t = 1; f < len[0]; f++) {
1943e12c5d1SDavid du Colombier 		flen = readline(bf, fbuf);
1953e12c5d1SDavid du Colombier 		ixold[f] = ixold[f-1] + flen + 1;		/* ftell(bf) */
1963e12c5d1SDavid du Colombier 		if (J[f] == 0)
1973e12c5d1SDavid du Colombier 			continue;
1983e12c5d1SDavid du Colombier 		do {
1993e12c5d1SDavid du Colombier 			tlen = readline(bt, tbuf);
2003e12c5d1SDavid du Colombier 			ixnew[t] = ixnew[t-1] + tlen + 1;	/* ftell(bt) */
2013e12c5d1SDavid du Colombier 		} while (t++ < J[f]);
2023e12c5d1SDavid du Colombier 		if (bflag) {
2033e12c5d1SDavid du Colombier 			flen = squishspace(fbuf);
2043e12c5d1SDavid du Colombier 			tlen = squishspace(tbuf);
2053e12c5d1SDavid du Colombier 		}
2063e12c5d1SDavid du Colombier 		if (flen != tlen || strcmp(fbuf, tbuf))
2073e12c5d1SDavid du Colombier 			J[f] = 0;
2083e12c5d1SDavid du Colombier 	}
2093e12c5d1SDavid du Colombier 	while (t < len[1]) {
2103e12c5d1SDavid du Colombier 		tlen = readline(bt, tbuf);
2113e12c5d1SDavid du Colombier 		ixnew[t] = ixnew[t-1] + tlen + 1;	/* fseek(bt) */
2123e12c5d1SDavid du Colombier 		t++;
2133e12c5d1SDavid du Colombier 	}
2143e12c5d1SDavid du Colombier }
2153e12c5d1SDavid du Colombier 
2163e12c5d1SDavid du Colombier static void
2173e12c5d1SDavid du Colombier range(int a, int b, char *separator)
2183e12c5d1SDavid du Colombier {
2193e12c5d1SDavid du Colombier 	Bprint(&stdout, "%d", a > b ? b: a);
2203e12c5d1SDavid du Colombier 	if (a < b)
2213e12c5d1SDavid du Colombier 		Bprint(&stdout, "%s%d", separator, b);
2223e12c5d1SDavid du Colombier }
2233e12c5d1SDavid du Colombier 
2243e12c5d1SDavid du Colombier static void
2253e12c5d1SDavid du Colombier fetch(long *f, int a, int b, Biobuf *bp, char *s)
2263e12c5d1SDavid du Colombier {
2273e12c5d1SDavid du Colombier 	char buf[MAXLINELEN];
2283e12c5d1SDavid du Colombier 
2293e12c5d1SDavid du Colombier 	Bseek(bp, f[a-1], 0);
2303e12c5d1SDavid du Colombier 	while (a++ <= b) {
2313e12c5d1SDavid du Colombier 		readline(bp, buf);
2323e12c5d1SDavid du Colombier 		Bprint(&stdout, "%s%s\n", s, buf);
2333e12c5d1SDavid du Colombier 	}
2343e12c5d1SDavid du Colombier }
2353e12c5d1SDavid du Colombier 
2363e12c5d1SDavid du Colombier void
2373e12c5d1SDavid du Colombier change(int a, int b, int c, int d)
2383e12c5d1SDavid du Colombier {
2393e12c5d1SDavid du Colombier 	if (a > b && c > d)
2403e12c5d1SDavid du Colombier 		return;
2413e12c5d1SDavid du Colombier 	anychange = 1;
2423e12c5d1SDavid du Colombier 	if (mflag && firstchange == 0) {
2433e12c5d1SDavid du Colombier 		Bprint(&stdout, "diff %s %s\n", file1, file2);
2443e12c5d1SDavid du Colombier 		firstchange = 1;
2453e12c5d1SDavid du Colombier 	}
2463e12c5d1SDavid du Colombier 	if (mode != 'f') {
2473e12c5d1SDavid du Colombier 		range(a, b, ",");
2483e12c5d1SDavid du Colombier 		Bputc(&stdout, a > b ? 'a': c > d ? 'd': 'c');
2493e12c5d1SDavid du Colombier 		if (mode != 'e')
2503e12c5d1SDavid du Colombier 			range(c, d, ",");
2513e12c5d1SDavid du Colombier 	}
2523e12c5d1SDavid du Colombier 	else {
2533e12c5d1SDavid du Colombier 		Bputc(&stdout, a > b ? 'a': c > d ? 'd': 'c');
2543e12c5d1SDavid du Colombier 		range(a, b, " ");
2553e12c5d1SDavid du Colombier 	}
2563e12c5d1SDavid du Colombier 	Bputc(&stdout, '\n');
2573e12c5d1SDavid du Colombier 	if (mode == 0) {
2583e12c5d1SDavid du Colombier 		fetch(ixold, a, b, input[0], "< ");
2593e12c5d1SDavid du Colombier 		if (a <= b && c <= d)
2603e12c5d1SDavid du Colombier 			Bprint(&stdout, "---\n");
2613e12c5d1SDavid du Colombier 	}
2623e12c5d1SDavid du Colombier 	fetch(ixnew, c, d, input[1], mode == 0 ? "> ": "");
2633e12c5d1SDavid du Colombier 	if (mode != 0 && c <= d)
2643e12c5d1SDavid du Colombier 		Bprint(&stdout, ".\n");
2653e12c5d1SDavid du Colombier }
266