xref: /plan9/sys/src/cmd/diff/diffio.c (revision 3e12c5d1bb89fc02707907988834ef147769ddaf)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "diff.h"
5 
6 struct line {
7 	int	serial;
8 	int	value;
9 };
10 extern struct line *file[2];
11 extern int len[2];
12 extern long *ixold, *ixnew;
13 extern int *J;
14 
15 static Biobuf *input[2];
16 static char *file1, *file2;
17 static int firstchange;
18 
19 #define MAXLINELEN	4096
20 #define MIN(x, y)	((x) < (y) ? (x): (y))
21 
22 static int
23 readline(Biobuf *bp, char *buf)
24 {
25 	int c;
26 	char *p, *e;
27 
28 	p = buf;
29 	e = p + MAXLINELEN-1;
30 	do {
31 		c = Bgetc(bp);
32 		if (c < 0) {
33 			if (p == buf)
34 				return -1;
35 			break;
36 		}
37 		if (c == '\n')
38 			break;
39 		*p++ = c;
40 	} while (p < e);
41 	*p = 0;
42 	if (c != '\n' && c >= 0) {
43 		do c = Bgetc(bp);
44 		while (c >= 0 && c != '\n');
45 	}
46 	return p - buf;
47 }
48 
49 #define HALFLONG 16
50 #define low(x)	(x&((1L<<HALFLONG)-1))
51 #define high(x)	(x>>HALFLONG)
52 
53 /*
54  * hashing has the effect of
55  * arranging line in 7-bit bytes and then
56  * summing 1-s complement in 16-bit hunks
57  */
58 static int
59 readhash(Biobuf *bp, char *buf)
60 {
61 	long sum;
62 	unsigned shift;
63 	char *p;
64 	int len, space;
65 
66 	sum = 1;
67 	shift = 0;
68 	if ((len = readline(bp, buf)) == -1)
69 		return 0;
70 	p = buf;
71 	switch(bflag)	/* various types of white space handling */
72 	{
73 	case 0:
74 		while (len--) {
75 			sum += (long)*p++ << (shift &= (HALFLONG-1));
76 			shift += 7;
77 		}
78 		break;
79 	case 1:
80 		/*
81 		 * coalesce multiple white-space
82 		 */
83 		for (space = 0; len--; p++) {
84 			if (isspace(*p)) {
85 				space++;
86 				continue;
87 			}
88 			if (space) {
89 				shift += 7;
90 				space = 0;
91 			}
92 			sum += (long)*p << (shift &= (HALFLONG-1));
93 			shift += 7;
94 		}
95 		break;
96 	default:
97 		/*
98 		 * strip all white-space
99 		 */
100 		while (len--) {
101 			if (isspace(*p)) {
102 				p++;
103 				continue;
104 			}
105 			sum += (long)*p++ << (shift &= (HALFLONG-1));
106 			shift += 7;
107 		}
108 		break;
109 	}
110 	sum = low(sum) + high(sum);
111 	return ((short)low(sum) + (short)high(sum));
112 }
113 
114 Biobuf *
115 prepare(int i, char *arg)
116 {
117 	struct line *p;
118 	int j, h;
119 	Biobuf *bp;
120 	char *cp, buf[MAXLINELEN];
121 	int nbytes;
122 	Rune r;
123 
124 	bp = Bopen(arg, OREAD);
125 	if (!bp) {
126 		panic(mflag ? 0: 2, "cannot open %s\n", arg);
127 		return 0;
128 	}
129 	nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN));
130 	if (nbytes > 0) {
131 		cp = buf;
132 		while (cp < buf+nbytes-1) {
133 			/*
134 			 * heuristic for a binary file in the
135 			 * brave new UNICODE world
136 			 */
137 			cp += chartorune(&r, cp);
138 			if (r == 0 || (r > 0x7f && r <= 0xa0)) {
139 				Bclose(bp);
140 				panic(mflag ? 0: 2, "binary file %s\n", arg);
141 				return 0;
142 			}
143 		}
144 		Bseek(bp, 0, 0);
145 	}
146 	p = MALLOC(struct line, 3);
147 	for (j = 0; h = readhash(bp, buf); p[j].value = h)
148 		p = REALLOC(p, struct line, (++j+3));
149 	len[i] = j;
150 	file[i] = p;
151 	input[i] = bp;			/*fix*/
152 	if (i == 0) {			/*fix*/
153 		file1 = arg;
154 		firstchange = 0;
155 	}
156 	else
157 		file2 = arg;
158 	return bp;
159 }
160 
161 static int
162 squishspace(char *buf)
163 {
164 	char *p, *q;
165 	int space;
166 
167 	for (space = 0, q = p = buf; *q; q++) {
168 		if (isspace(*q)) {
169 			space++;
170 			continue;
171 		}
172 		if (space && bflag == 1) {
173 			*p++ = ' ';
174 			space = 0;
175 		}
176 		*p++ = *q;
177 	}
178 	*p = 0;
179 	return p - buf;
180 }
181 
182 /*
183  * need to fix up for unexpected EOF's
184  */
185 void
186 check(Biobuf *bf, Biobuf *bt)
187 {
188 	int f, t, flen, tlen;
189 	char fbuf[MAXLINELEN], tbuf[MAXLINELEN];
190 
191 	ixold[0] = ixnew[0] = 0;
192 	for (f = t = 1; f < len[0]; f++) {
193 		flen = readline(bf, fbuf);
194 		ixold[f] = ixold[f-1] + flen + 1;		/* ftell(bf) */
195 		if (J[f] == 0)
196 			continue;
197 		do {
198 			tlen = readline(bt, tbuf);
199 			ixnew[t] = ixnew[t-1] + tlen + 1;	/* ftell(bt) */
200 		} while (t++ < J[f]);
201 		if (bflag) {
202 			flen = squishspace(fbuf);
203 			tlen = squishspace(tbuf);
204 		}
205 		if (flen != tlen || strcmp(fbuf, tbuf))
206 			J[f] = 0;
207 	}
208 	while (t < len[1]) {
209 		tlen = readline(bt, tbuf);
210 		ixnew[t] = ixnew[t-1] + tlen + 1;	/* fseek(bt) */
211 		t++;
212 	}
213 }
214 
215 static void
216 range(int a, int b, char *separator)
217 {
218 	Bprint(&stdout, "%d", a > b ? b: a);
219 	if (a < b)
220 		Bprint(&stdout, "%s%d", separator, b);
221 }
222 
223 static void
224 fetch(long *f, int a, int b, Biobuf *bp, char *s)
225 {
226 	char buf[MAXLINELEN];
227 
228 	Bseek(bp, f[a-1], 0);
229 	while (a++ <= b) {
230 		readline(bp, buf);
231 		Bprint(&stdout, "%s%s\n", s, buf);
232 	}
233 }
234 
235 void
236 change(int a, int b, int c, int d)
237 {
238 	if (a > b && c > d)
239 		return;
240 	anychange = 1;
241 	if (mflag && firstchange == 0) {
242 		Bprint(&stdout, "diff %s %s\n", file1, file2);
243 		firstchange = 1;
244 	}
245 	if (mode != 'f') {
246 		range(a, b, ",");
247 		Bputc(&stdout, a > b ? 'a': c > d ? 'd': 'c');
248 		if (mode != 'e')
249 			range(c, d, ",");
250 	}
251 	else {
252 		Bputc(&stdout, a > b ? 'a': c > d ? 'd': 'c');
253 		range(a, b, " ");
254 	}
255 	Bputc(&stdout, '\n');
256 	if (mode == 0) {
257 		fetch(ixold, a, b, input[0], "< ");
258 		if (a <= b && c <= d)
259 			Bprint(&stdout, "---\n");
260 	}
261 	fetch(ixnew, c, d, input[1], mode == 0 ? "> ": "");
262 	if (mode != 0 && c <= d)
263 		Bprint(&stdout, ".\n");
264 }
265