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;		/* ftell(bf) */
    197 		if (J[f] == 0)
    198 			continue;
    199 		do {
    200 			tlen = readline(bt, tbuf);
    201 			ixnew[t] = ixnew[t-1] + tlen + 1;	/* ftell(bt) */
    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)*sizeof(changes[0]));
    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 }