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 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 } 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 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 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 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 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 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': 305*27e10919SDavid 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 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 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); 352*27e10919SDavid du Colombier a = changes[i].a-Lines; 353*27e10919SDavid du Colombier b = changes[j-1].b+Lines; 354*27e10919SDavid du Colombier c = changes[i].c-Lines; 355*27e10919SDavid du Colombier d = changes[j-1].d+Lines; 356*27e10919SDavid du Colombier if(a < 1) 357*27e10919SDavid du Colombier a = 1; 358*27e10919SDavid du Colombier if(c < 1) 359*27e10919SDavid du Colombier c = 1; 360*27e10919SDavid du Colombier if(b > len[0]) 361*27e10919SDavid du Colombier b = len[0]; 362*27e10919SDavid du Colombier if(d > len[1]) 363*27e10919SDavid du Colombier d = len[1]; 364*27e10919SDavid du Colombier if(mode == 'a'){ 365*27e10919SDavid du Colombier a = 1; 366*27e10919SDavid du Colombier b = len[0]; 367*27e10919SDavid du Colombier c = 1; 368*27e10919SDavid du Colombier d = len[1]; 369*27e10919SDavid du Colombier j = nchanges; 370*27e10919SDavid 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'); 377*27e10919SDavid du Colombier at = a; 378d35914a8SDavid du Colombier for(; i<j; i++){ 379d35914a8SDavid du Colombier fetch(ixold, at, changes[i].a-1, input[0], " "); 380d35914a8SDavid du Colombier fetch(ixold, changes[i].a, changes[i].b, input[0], "< "); 381d35914a8SDavid du Colombier fetch(ixnew, changes[i].c, changes[i].d, input[1], "> "); 382d35914a8SDavid du Colombier at = changes[i].b+1; 383d35914a8SDavid du Colombier } 384*27e10919SDavid du Colombier fetch(ixold, at, b, input[0], " "); 385d35914a8SDavid du Colombier } 386d35914a8SDavid du Colombier nchanges = 0; 387d35914a8SDavid du Colombier } 388