diffio.c - 9base - revived minimalist port of Plan 9 userland to Unix | |
git clone git://git.suckless.org/9base | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
diffio.c (6620B) | |
--- | |
1 #include <u.h> | |
2 #include <libc.h> | |
3 #include <bio.h> | |
4 #include <ctype.h> | |
5 #include "diff.h" | |
6 | |
7 struct line { | |
8 int serial; | |
9 int value; | |
10 }; | |
11 extern struct line *file[2]; | |
12 extern int len[2]; | |
13 extern long *ixold, *ixnew; | |
14 extern int *J; | |
15 | |
16 static Biobuf *input[2]; | |
17 static char *file1, *file2; | |
18 static int firstchange; | |
19 | |
20 #define MAXLINELEN 4096 | |
21 #define MIN(x, y) ((x) < (y) ? (x): (y)) | |
22 | |
23 static int | |
24 readline(Biobuf *bp, char *buf) | |
25 { | |
26 int c; | |
27 char *p, *e; | |
28 | |
29 p = buf; | |
30 e = p + MAXLINELEN-1; | |
31 do { | |
32 c = Bgetc(bp); | |
33 if (c < 0) { | |
34 if (p == buf) | |
35 return -1; | |
36 break; | |
37 } | |
38 if (c == '\n') | |
39 break; | |
40 *p++ = c; | |
41 } while (p < e); | |
42 *p = 0; | |
43 if (c != '\n' && c >= 0) { | |
44 do c = Bgetc(bp); | |
45 while (c >= 0 && c != '\n'); | |
46 } | |
47 return p - buf; | |
48 } | |
49 | |
50 #define HALFLONG 16 | |
51 #define low(x) (x&((1L<<HALFLONG)-1)) | |
52 #define high(x) (x>>HALFLONG) | |
53 | |
54 /* | |
55 * hashing has the effect of | |
56 * arranging line in 7-bit bytes and then | |
57 * summing 1-s complement in 16-bit hunks | |
58 */ | |
59 static int | |
60 readhash(Biobuf *bp, char *buf) | |
61 { | |
62 long sum; | |
63 unsigned shift; | |
64 char *p; | |
65 int len, space; | |
66 | |
67 sum = 1; | |
68 shift = 0; | |
69 if ((len = readline(bp, buf)) == -1) | |
70 return 0; | |
71 p = buf; | |
72 switch(bflag) /* various types of white space handling */ | |
73 { | |
74 case 0: | |
75 while (len--) { | |
76 sum += (long)*p++ << (shift &= (HALFLONG-1)); | |
77 shift += 7; | |
78 } | |
79 break; | |
80 case 1: | |
81 /* | |
82 * coalesce multiple white-space | |
83 */ | |
84 for (space = 0; len--; p++) { | |
85 if (isspace((uchar)*p)) { | |
86 space++; | |
87 continue; | |
88 } | |
89 if (space) { | |
90 shift += 7; | |
91 space = 0; | |
92 } | |
93 sum += (long)*p << (shift &= (HALFLONG-1)); | |
94 shift += 7; | |
95 } | |
96 break; | |
97 default: | |
98 /* | |
99 * strip all white-space | |
100 */ | |
101 while (len--) { | |
102 if (isspace((uchar)*p)) { | |
103 p++; | |
104 continue; | |
105 } | |
106 sum += (long)*p++ << (shift &= (HALFLONG-1)); | |
107 shift += 7; | |
108 } | |
109 break; | |
110 } | |
111 sum = low(sum) + high(sum); | |
112 return ((short)low(sum) + (short)high(sum)); | |
113 } | |
114 | |
115 Biobuf * | |
116 prepare(int i, char *arg) | |
117 { | |
118 struct line *p; | |
119 int j, h; | |
120 Biobuf *bp; | |
121 char *cp, buf[MAXLINELEN]; | |
122 int nbytes; | |
123 Rune r; | |
124 | |
125 bp = Bopen(arg, OREAD); | |
126 if (!bp) { | |
127 panic(mflag ? 0: 2, "cannot open %s: %r\n", arg); | |
128 return 0; | |
129 } | |
130 if (binary) | |
131 return bp; | |
132 nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN)); | |
133 if (nbytes > 0) { | |
134 cp = buf; | |
135 while (cp < buf+nbytes-UTFmax) { | |
136 /* | |
137 * heuristic for a binary file in the | |
138 * brave new UNICODE world | |
139 */ | |
140 cp += chartorune(&r, cp); | |
141 if (r == 0 || (r > 0x7f && r <= 0xa0)) { | |
142 binary++; | |
143 return bp; | |
144 } | |
145 } | |
146 Bseek(bp, 0, 0); | |
147 } | |
148 p = MALLOC(struct line, 3); | |
149 for (j = 0; h = readhash(bp, buf); p[j].value = h) | |
150 p = REALLOC(p, struct line, (++j+3)); | |
151 len[i] = j; | |
152 file[i] = p; | |
153 input[i] = bp; /*fix*/ | |
154 if (i == 0) { /*fix*/ | |
155 file1 = arg; | |
156 firstchange = 0; | |
157 } | |
158 else | |
159 file2 = arg; | |
160 return bp; | |
161 } | |
162 | |
163 static int | |
164 squishspace(char *buf) | |
165 { | |
166 char *p, *q; | |
167 int space; | |
168 | |
169 for (space = 0, q = p = buf; *q; q++) { | |
170 if (isspace((uchar)*q)) { | |
171 space++; | |
172 continue; | |
173 } | |
174 if (space && bflag == 1) { | |
175 *p++ = ' '; | |
176 space = 0; | |
177 } | |
178 *p++ = *q; | |
179 } | |
180 *p = 0; | |
181 return p - buf; | |
182 } | |
183 | |
184 /* | |
185 * need to fix up for unexpected EOF's | |
186 */ | |
187 void | |
188 check(Biobuf *bf, Biobuf *bt) | |
189 { | |
190 int f, t, flen, tlen; | |
191 char fbuf[MAXLINELEN], tbuf[MAXLINELEN]; | |
192 | |
193 ixold[0] = ixnew[0] = 0; | |
194 for (f = t = 1; f < len[0]; f++) { | |
195 flen = readline(bf, fbuf); | |
196 ixold[f] = ixold[f-1] + flen + 1; /* ftel… | |
197 if (J[f] == 0) | |
198 continue; | |
199 do { | |
200 tlen = readline(bt, tbuf); | |
201 ixnew[t] = ixnew[t-1] + tlen + 1; /* ftel… | |
202 } while (t++ < J[f]); | |
203 if (bflag) { | |
204 flen = squishspace(fbuf); | |
205 tlen = squishspace(tbuf); | |
206 } | |
207 if (flen != tlen || strcmp(fbuf, tbuf)) | |
208 J[f] = 0; | |
209 } | |
210 while (t < len[1]) { | |
211 tlen = readline(bt, tbuf); | |
212 ixnew[t] = ixnew[t-1] + tlen + 1; /* fseek(bt) */ | |
213 t++; | |
214 } | |
215 } | |
216 | |
217 static void | |
218 range(int a, int b, char *separator) | |
219 { | |
220 Bprint(&stdout, "%d", a > b ? b: a); | |
221 if (a < b) | |
222 Bprint(&stdout, "%s%d", separator, b); | |
223 } | |
224 | |
225 static void | |
226 fetch(long *f, int a, int b, Biobuf *bp, char *s) | |
227 { | |
228 char buf[MAXLINELEN]; | |
229 int maxb; | |
230 | |
231 if(a <= 1) | |
232 a = 1; | |
233 if(bp == input[0]) | |
234 maxb = len[0]; | |
235 else | |
236 maxb = len[1]; | |
237 if(b > maxb) | |
238 b = maxb; | |
239 if(a > maxb) | |
240 return; | |
241 Bseek(bp, f[a-1], 0); | |
242 while (a++ <= b) { | |
243 readline(bp, buf); | |
244 Bprint(&stdout, "%s%s\n", s, buf); | |
245 } | |
246 } | |
247 | |
248 typedef struct Change Change; | |
249 struct Change | |
250 { | |
251 int a; | |
252 int b; | |
253 int c; | |
254 int d; | |
255 }; | |
256 | |
257 Change *changes; | |
258 int nchanges; | |
259 | |
260 void | |
261 change(int a, int b, int c, int d) | |
262 { | |
263 char verb; | |
264 char buf[4]; | |
265 Change *ch; | |
266 | |
267 if (a > b && c > d) | |
268 return; | |
269 anychange = 1; | |
270 if (mflag && firstchange == 0) { | |
271 if(mode) { | |
272 buf[0] = '-'; | |
273 buf[1] = mode; | |
274 buf[2] = ' '; | |
275 buf[3] = '\0'; | |
276 } else { | |
277 buf[0] = '\0'; | |
278 } | |
279 Bprint(&stdout, "diff %s%s %s\n", buf, file1, file2); | |
280 firstchange = 1; | |
281 } | |
282 verb = a > b ? 'a': c > d ? 'd': 'c'; | |
283 switch(mode) { | |
284 case 'e': | |
285 range(a, b, ","); | |
286 Bputc(&stdout, verb); | |
287 break; | |
288 case 0: | |
289 range(a, b, ","); | |
290 Bputc(&stdout, verb); | |
291 range(c, d, ","); | |
292 break; | |
293 case 'n': | |
294 Bprint(&stdout, "%s:", file1); | |
295 range(a, b, ","); | |
296 Bprint(&stdout, " %c ", verb); | |
297 Bprint(&stdout, "%s:", file2); | |
298 range(c, d, ","); | |
299 break; | |
300 case 'f': | |
301 Bputc(&stdout, verb); | |
302 range(a, b, " "); | |
303 break; | |
304 case 'c': | |
305 case 'a': | |
306 if(nchanges%1024 == 0) | |
307 changes = erealloc(changes, (nchanges+1024)*size… | |
308 ch = &changes[nchanges++]; | |
309 ch->a = a; | |
310 ch->b = b; | |
311 ch->c = c; | |
312 ch->d = d; | |
313 return; | |
314 } | |
315 Bputc(&stdout, '\n'); | |
316 if (mode == 0 || mode == 'n') { | |
317 fetch(ixold, a, b, input[0], "< "); | |
318 if (a <= b && c <= d) | |
319 Bprint(&stdout, "---\n"); | |
320 } | |
321 fetch(ixnew, c, d, input[1], mode == 0 || mode == 'n' ? "> ": ""… | |
322 if (mode != 0 && mode != 'n' && c <= d) | |
323 Bprint(&stdout, ".\n"); | |
324 } | |
325 | |
326 enum | |
327 { | |
328 Lines = 3 /* number of lines of context shown */ | |
329 }; | |
330 | |
331 int | |
332 changeset(int i) | |
333 { | |
334 while(i<nchanges && changes[i].b+1+2*Lines > changes[i+1].a) | |
335 i++; | |
336 if(i<nchanges) | |
337 return i+1; | |
338 return nchanges; | |
339 } | |
340 | |
341 void | |
342 flushchanges(void) | |
343 { | |
344 int a, b, c, d, at; | |
345 int i, j; | |
346 | |
347 if(nchanges == 0) | |
348 return; | |
349 | |
350 for(i=0; i<nchanges; ){ | |
351 j = changeset(i); | |
352 a = changes[i].a-Lines; | |
353 b = changes[j-1].b+Lines; | |
354 c = changes[i].c-Lines; | |
355 d = changes[j-1].d+Lines; | |
356 if(a < 1) | |
357 a = 1; | |
358 if(c < 1) | |
359 c = 1; | |
360 if(b > len[0]) | |
361 b = len[0]; | |
362 if(d > len[1]) | |
363 d = len[1]; | |
364 if(mode == 'a'){ | |
365 a = 1; | |
366 b = len[0]; | |
367 c = 1; | |
368 d = len[1]; | |
369 j = nchanges; | |
370 } | |
371 Bprint(&stdout, "%s:", file1); | |
372 range(a, b, ","); | |
373 Bprint(&stdout, " - "); | |
374 Bprint(&stdout, "%s:", file2); | |
375 range(c, d, ","); | |
376 Bputc(&stdout, '\n'); | |
377 at = a; | |
378 for(; i<j; i++){ | |
379 fetch(ixold, at, changes[i].a-1, input[0], " "); | |
380 fetch(ixold, changes[i].a, changes[i].b, input[0… | |
381 fetch(ixnew, changes[i].c, changes[i].d, input[1… | |
382 at = changes[i].b+1; | |
383 } | |
384 fetch(ixold, at, b, input[0], " "); | |
385 } | |
386 nchanges = 0; | |
387 } |