13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <bio.h>
4219b2ee8SDavid 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
readline(Biobuf * bp,char * buf)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
readhash(Biobuf * bp,char * buf)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 *
prepare(int i,char * arg)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 }
1309a747e4fSDavid du Colombier if (binary)
1319a747e4fSDavid du Colombier return bp;
1323e12c5d1SDavid du Colombier nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN));
1333e12c5d1SDavid du Colombier if (nbytes > 0) {
1343e12c5d1SDavid du Colombier cp = buf;
135bd389b36SDavid du Colombier while (cp < buf+nbytes-UTFmax) {
1363e12c5d1SDavid du Colombier /*
1373e12c5d1SDavid du Colombier * heuristic for a binary file in the
1383e12c5d1SDavid du Colombier * brave new UNICODE world
1393e12c5d1SDavid du Colombier */
1403e12c5d1SDavid du Colombier cp += chartorune(&r, cp);
1413e12c5d1SDavid du Colombier if (r == 0 || (r > 0x7f && r <= 0xa0)) {
1429a747e4fSDavid du Colombier binary++;
1439a747e4fSDavid du Colombier return bp;
1443e12c5d1SDavid du Colombier }
1453e12c5d1SDavid du Colombier }
1463e12c5d1SDavid du Colombier Bseek(bp, 0, 0);
1473e12c5d1SDavid du Colombier }
1483e12c5d1SDavid du Colombier p = MALLOC(struct line, 3);
1493e12c5d1SDavid du Colombier for (j = 0; h = readhash(bp, buf); p[j].value = h)
1503e12c5d1SDavid du Colombier p = REALLOC(p, struct line, (++j+3));
1513e12c5d1SDavid du Colombier len[i] = j;
1523e12c5d1SDavid du Colombier file[i] = p;
1533e12c5d1SDavid du Colombier input[i] = bp; /*fix*/
1543e12c5d1SDavid du Colombier if (i == 0) { /*fix*/
1553e12c5d1SDavid du Colombier file1 = arg;
1563e12c5d1SDavid du Colombier firstchange = 0;
1573e12c5d1SDavid du Colombier }
1583e12c5d1SDavid du Colombier else
1593e12c5d1SDavid du Colombier file2 = arg;
1603e12c5d1SDavid du Colombier return bp;
1613e12c5d1SDavid du Colombier }
1623e12c5d1SDavid du Colombier
1633e12c5d1SDavid du Colombier static int
squishspace(char * buf)1643e12c5d1SDavid du Colombier squishspace(char *buf)
1653e12c5d1SDavid du Colombier {
1663e12c5d1SDavid du Colombier char *p, *q;
1673e12c5d1SDavid du Colombier int space;
1683e12c5d1SDavid du Colombier
1693e12c5d1SDavid du Colombier for (space = 0, q = p = buf; *q; q++) {
1703e12c5d1SDavid du Colombier if (isspace(*q)) {
1713e12c5d1SDavid du Colombier space++;
1723e12c5d1SDavid du Colombier continue;
1733e12c5d1SDavid du Colombier }
1743e12c5d1SDavid du Colombier if (space && bflag == 1) {
1753e12c5d1SDavid du Colombier *p++ = ' ';
1763e12c5d1SDavid du Colombier space = 0;
1773e12c5d1SDavid du Colombier }
1783e12c5d1SDavid du Colombier *p++ = *q;
1793e12c5d1SDavid du Colombier }
1803e12c5d1SDavid du Colombier *p = 0;
1813e12c5d1SDavid du Colombier return p - buf;
1823e12c5d1SDavid du Colombier }
1833e12c5d1SDavid du Colombier
1843e12c5d1SDavid du Colombier /*
1853e12c5d1SDavid du Colombier * need to fix up for unexpected EOF's
1863e12c5d1SDavid du Colombier */
1873e12c5d1SDavid du Colombier void
check(Biobuf * bf,Biobuf * bt)1883e12c5d1SDavid du Colombier check(Biobuf *bf, Biobuf *bt)
1893e12c5d1SDavid du Colombier {
1903e12c5d1SDavid du Colombier int f, t, flen, tlen;
1913e12c5d1SDavid du Colombier char fbuf[MAXLINELEN], tbuf[MAXLINELEN];
1923e12c5d1SDavid du Colombier
1933e12c5d1SDavid du Colombier ixold[0] = ixnew[0] = 0;
1943e12c5d1SDavid du Colombier for (f = t = 1; f < len[0]; f++) {
1953e12c5d1SDavid du Colombier flen = readline(bf, fbuf);
1963e12c5d1SDavid du Colombier ixold[f] = ixold[f-1] + flen + 1; /* ftell(bf) */
1973e12c5d1SDavid du Colombier if (J[f] == 0)
1983e12c5d1SDavid du Colombier continue;
1993e12c5d1SDavid du Colombier do {
2003e12c5d1SDavid du Colombier tlen = readline(bt, tbuf);
2013e12c5d1SDavid du Colombier ixnew[t] = ixnew[t-1] + tlen + 1; /* ftell(bt) */
2023e12c5d1SDavid du Colombier } while (t++ < J[f]);
2033e12c5d1SDavid du Colombier if (bflag) {
2043e12c5d1SDavid du Colombier flen = squishspace(fbuf);
2053e12c5d1SDavid du Colombier tlen = squishspace(tbuf);
2063e12c5d1SDavid du Colombier }
2073e12c5d1SDavid du Colombier if (flen != tlen || strcmp(fbuf, tbuf))
2083e12c5d1SDavid du Colombier J[f] = 0;
2093e12c5d1SDavid du Colombier }
2103e12c5d1SDavid du Colombier while (t < len[1]) {
2113e12c5d1SDavid du Colombier tlen = readline(bt, tbuf);
2123e12c5d1SDavid du Colombier ixnew[t] = ixnew[t-1] + tlen + 1; /* fseek(bt) */
2133e12c5d1SDavid du Colombier t++;
2143e12c5d1SDavid du Colombier }
2153e12c5d1SDavid du Colombier }
2163e12c5d1SDavid du Colombier
2173e12c5d1SDavid du Colombier static void
range(int a,int b,char * separator)2183e12c5d1SDavid du Colombier range(int a, int b, char *separator)
2193e12c5d1SDavid du Colombier {
2203e12c5d1SDavid du Colombier Bprint(&stdout, "%d", a > b ? b: a);
2213e12c5d1SDavid du Colombier if (a < b)
2223e12c5d1SDavid du Colombier Bprint(&stdout, "%s%d", separator, b);
2233e12c5d1SDavid du Colombier }
2243e12c5d1SDavid du Colombier
2253e12c5d1SDavid du Colombier static void
fetch(long * f,int a,int b,Biobuf * bp,char * s)2263e12c5d1SDavid du Colombier fetch(long *f, int a, int b, Biobuf *bp, char *s)
2273e12c5d1SDavid du Colombier {
2283e12c5d1SDavid du Colombier char buf[MAXLINELEN];
229d35914a8SDavid du Colombier int maxb;
2303e12c5d1SDavid du Colombier
231d35914a8SDavid du Colombier if(a <= 1)
23222a0222bSDavid du Colombier a = 1;
233d35914a8SDavid du Colombier if(bp == input[0])
234d35914a8SDavid du Colombier maxb = len[0];
235d35914a8SDavid du Colombier else
236d35914a8SDavid du Colombier maxb = len[1];
237d35914a8SDavid du Colombier if(b > maxb)
238d35914a8SDavid du Colombier b = maxb;
239d35914a8SDavid du Colombier if(a > maxb)
240d35914a8SDavid du Colombier return;
2413e12c5d1SDavid du Colombier Bseek(bp, f[a-1], 0);
2423e12c5d1SDavid du Colombier while (a++ <= b) {
243d35914a8SDavid du Colombier readline(bp, buf);
2443e12c5d1SDavid du Colombier Bprint(&stdout, "%s%s\n", s, buf);
2453e12c5d1SDavid du Colombier }
2463e12c5d1SDavid du Colombier }
2473e12c5d1SDavid du Colombier
248d35914a8SDavid du Colombier typedef struct Change Change;
249d35914a8SDavid du Colombier struct Change
250d35914a8SDavid du Colombier {
251d35914a8SDavid du Colombier int a;
252d35914a8SDavid du Colombier int b;
253d35914a8SDavid du Colombier int c;
254d35914a8SDavid du Colombier int d;
255d35914a8SDavid du Colombier };
256d35914a8SDavid du Colombier
257d35914a8SDavid du Colombier Change *changes;
258d35914a8SDavid du Colombier int nchanges;
259d35914a8SDavid du Colombier
2603e12c5d1SDavid du Colombier void
change(int a,int b,int c,int d)2613e12c5d1SDavid du Colombier change(int a, int b, int c, int d)
2623e12c5d1SDavid du Colombier {
2637dd7cddfSDavid du Colombier char verb;
2647dd7cddfSDavid du Colombier char buf[4];
265d35914a8SDavid du Colombier Change *ch;
2667dd7cddfSDavid du Colombier
2673e12c5d1SDavid du Colombier if (a > b && c > d)
2683e12c5d1SDavid du Colombier return;
2693e12c5d1SDavid du Colombier anychange = 1;
2703e12c5d1SDavid du Colombier if (mflag && firstchange == 0) {
2717dd7cddfSDavid du Colombier if(mode) {
2727dd7cddfSDavid du Colombier buf[0] = '-';
2737dd7cddfSDavid du Colombier buf[1] = mode;
2747dd7cddfSDavid du Colombier buf[2] = ' ';
2757dd7cddfSDavid du Colombier buf[3] = '\0';
2767dd7cddfSDavid du Colombier } else {
2777dd7cddfSDavid du Colombier buf[0] = '\0';
2787dd7cddfSDavid du Colombier }
2797dd7cddfSDavid du Colombier Bprint(&stdout, "diff %s%s %s\n", buf, file1, file2);
2803e12c5d1SDavid du Colombier firstchange = 1;
2813e12c5d1SDavid du Colombier }
2827dd7cddfSDavid du Colombier verb = a > b ? 'a': c > d ? 'd': 'c';
2837dd7cddfSDavid du Colombier switch(mode) {
2847dd7cddfSDavid du Colombier case 'e':
2853e12c5d1SDavid du Colombier range(a, b, ",");
2867dd7cddfSDavid du Colombier Bputc(&stdout, verb);
2877dd7cddfSDavid du Colombier break;
2887dd7cddfSDavid du Colombier case 0:
2897dd7cddfSDavid du Colombier range(a, b, ",");
2907dd7cddfSDavid du Colombier Bputc(&stdout, verb);
2913e12c5d1SDavid du Colombier range(c, d, ",");
2927dd7cddfSDavid du Colombier break;
2937dd7cddfSDavid du Colombier case 'n':
2947dd7cddfSDavid du Colombier Bprint(&stdout, "%s:", file1);
2957dd7cddfSDavid du Colombier range(a, b, ",");
2967dd7cddfSDavid du Colombier Bprint(&stdout, " %c ", verb);
2977dd7cddfSDavid du Colombier Bprint(&stdout, "%s:", file2);
2987dd7cddfSDavid du Colombier range(c, d, ",");
2997dd7cddfSDavid du Colombier break;
3007dd7cddfSDavid du Colombier case 'f':
3017dd7cddfSDavid du Colombier Bputc(&stdout, verb);
3023e12c5d1SDavid du Colombier range(a, b, " ");
3037dd7cddfSDavid du Colombier break;
304d35914a8SDavid du Colombier case 'c':
30527e10919SDavid du Colombier case 'a':
306d35914a8SDavid du Colombier if(nchanges%1024 == 0)
307d35914a8SDavid du Colombier changes = erealloc(changes, (nchanges+1024)*sizeof(changes[0]));
308d35914a8SDavid du Colombier ch = &changes[nchanges++];
309d35914a8SDavid du Colombier ch->a = a;
310d35914a8SDavid du Colombier ch->b = b;
311d35914a8SDavid du Colombier ch->c = c;
312d35914a8SDavid du Colombier ch->d = d;
313d35914a8SDavid du Colombier return;
3143e12c5d1SDavid du Colombier }
3153e12c5d1SDavid du Colombier Bputc(&stdout, '\n');
3167dd7cddfSDavid du Colombier if (mode == 0 || mode == 'n') {
3173e12c5d1SDavid du Colombier fetch(ixold, a, b, input[0], "< ");
3183e12c5d1SDavid du Colombier if (a <= b && c <= d)
3193e12c5d1SDavid du Colombier Bprint(&stdout, "---\n");
3203e12c5d1SDavid du Colombier }
3217dd7cddfSDavid du Colombier fetch(ixnew, c, d, input[1], mode == 0 || mode == 'n' ? "> ": "");
3227dd7cddfSDavid du Colombier if (mode != 0 && mode != 'n' && c <= d)
3233e12c5d1SDavid du Colombier Bprint(&stdout, ".\n");
3243e12c5d1SDavid du Colombier }
325d35914a8SDavid du Colombier
326d35914a8SDavid du Colombier enum
327d35914a8SDavid du Colombier {
328d35914a8SDavid du Colombier Lines = 3, /* number of lines of context shown */
329d35914a8SDavid du Colombier };
330d35914a8SDavid du Colombier
331d35914a8SDavid du Colombier int
changeset(int i)332d35914a8SDavid du Colombier changeset(int i)
333d35914a8SDavid du Colombier {
334d35914a8SDavid du Colombier while(i<nchanges && changes[i].b+1+2*Lines > changes[i+1].a)
335d35914a8SDavid du Colombier i++;
336d35914a8SDavid du Colombier if(i<nchanges)
337d35914a8SDavid du Colombier return i+1;
338d35914a8SDavid du Colombier return nchanges;
33922a0222bSDavid du Colombier }
3409a747e4fSDavid du Colombier
341d35914a8SDavid du Colombier void
flushchanges(void)342d35914a8SDavid du Colombier flushchanges(void)
343d35914a8SDavid du Colombier {
344d35914a8SDavid du Colombier int a, b, c, d, at;
345d35914a8SDavid du Colombier int i, j;
346d35914a8SDavid du Colombier
347d35914a8SDavid du Colombier if(nchanges == 0)
348d35914a8SDavid du Colombier return;
349d35914a8SDavid du Colombier
350d35914a8SDavid du Colombier for(i=0; i<nchanges; ){
351d35914a8SDavid du Colombier j = changeset(i);
35227e10919SDavid du Colombier a = changes[i].a-Lines;
35327e10919SDavid du Colombier b = changes[j-1].b+Lines;
35427e10919SDavid du Colombier c = changes[i].c-Lines;
35527e10919SDavid du Colombier d = changes[j-1].d+Lines;
35627e10919SDavid du Colombier if(a < 1)
35727e10919SDavid du Colombier a = 1;
35827e10919SDavid du Colombier if(c < 1)
35927e10919SDavid du Colombier c = 1;
36027e10919SDavid du Colombier if(b > len[0])
36127e10919SDavid du Colombier b = len[0];
36227e10919SDavid du Colombier if(d > len[1])
36327e10919SDavid du Colombier d = len[1];
36427e10919SDavid du Colombier if(mode == 'a'){
36527e10919SDavid du Colombier a = 1;
36627e10919SDavid du Colombier b = len[0];
36727e10919SDavid du Colombier c = 1;
36827e10919SDavid du Colombier d = len[1];
36927e10919SDavid du Colombier j = nchanges;
37027e10919SDavid du Colombier }
371d35914a8SDavid du Colombier Bprint(&stdout, "%s:", file1);
372d35914a8SDavid du Colombier range(a, b, ",");
373d35914a8SDavid du Colombier Bprint(&stdout, " - ");
374d35914a8SDavid du Colombier Bprint(&stdout, "%s:", file2);
375d35914a8SDavid du Colombier range(c, d, ",");
376d35914a8SDavid du Colombier Bputc(&stdout, '\n');
37727e10919SDavid du Colombier at = a;
378d35914a8SDavid du Colombier for(; i<j; i++){
379d35914a8SDavid du Colombier fetch(ixold, at, changes[i].a-1, input[0], " ");
380*ee55fa65SDavid du Colombier fetch(ixold, changes[i].a, changes[i].b, input[0], "- ");
381*ee55fa65SDavid du Colombier fetch(ixnew, changes[i].c, changes[i].d, input[1], "+ ");
382d35914a8SDavid du Colombier at = changes[i].b+1;
383d35914a8SDavid du Colombier }
38427e10919SDavid du Colombier fetch(ixold, at, b, input[0], " ");
385d35914a8SDavid du Colombier }
386d35914a8SDavid du Colombier nchanges = 0;
387d35914a8SDavid du Colombier }
388