xref: /inferno-os/appl/cmd/diff.b (revision ca1042d3d05e5e9b2b5094b04197c96ec3b34bfe)
137da2899SCharles.Forsythimplement Diff;
237da2899SCharles.Forsyth
337da2899SCharles.Forsyth#	diff - differential file comparison
437da2899SCharles.Forsyth#
537da2899SCharles.Forsyth#	Uses an algorithm due to Harold Stone, which finds
637da2899SCharles.Forsyth#	a pair of longest identical subsequences in the two
737da2899SCharles.Forsyth#	files.
837da2899SCharles.Forsyth#
937da2899SCharles.Forsyth#	The major goal is to generate the match vector J.
1037da2899SCharles.Forsyth#	J[i] is the index of the line in file1 corresponding
1137da2899SCharles.Forsyth#	to line i file0. J[i] = 0 if there is no
1237da2899SCharles.Forsyth#	such line in file1.
1337da2899SCharles.Forsyth#
1437da2899SCharles.Forsyth#	Lines are hashed so as to work in core. All potential
1537da2899SCharles.Forsyth#	matches are located by sorting the lines of each file
1637da2899SCharles.Forsyth#	on the hash (called value). In particular, this
1737da2899SCharles.Forsyth#	collects the equivalence classes in file1 together.
1837da2899SCharles.Forsyth#	Subroutine equiv replaces the value of each line in
1937da2899SCharles.Forsyth#	file0 by the index of the first element of its
2037da2899SCharles.Forsyth#	matching equivalence in (the reordered) file1.
2137da2899SCharles.Forsyth#	To save space equiv squeezes file1 into a single
2237da2899SCharles.Forsyth#	array member in which the equivalence classes
2337da2899SCharles.Forsyth#	are simply concatenated, except that their first
2437da2899SCharles.Forsyth#	members are flagged by changing sign.
2537da2899SCharles.Forsyth#
2637da2899SCharles.Forsyth#	Next the indices that point into member are unsorted into
2737da2899SCharles.Forsyth#	array class according to the original order of file0.
2837da2899SCharles.Forsyth#
2937da2899SCharles.Forsyth#	The cleverness lies in routine stone. This marches
3037da2899SCharles.Forsyth#	through the lines of file0, developing a vector klist
3137da2899SCharles.Forsyth#	of "k-candidates". At step i a k-candidate is a matched
3237da2899SCharles.Forsyth#	pair of lines x,y (x in file0 y in file1) such that
3337da2899SCharles.Forsyth#	there is a common subsequence of lenght k
3437da2899SCharles.Forsyth#	between the first i lines of file0 and the first y
3537da2899SCharles.Forsyth#	lines of file1, but there is no such subsequence for
3637da2899SCharles.Forsyth#	any smaller y. x is the earliest possible mate to y
3737da2899SCharles.Forsyth#	that occurs in such a subsequence.
3837da2899SCharles.Forsyth#
3937da2899SCharles.Forsyth#	Whenever any of the members of the equivalence class of
4037da2899SCharles.Forsyth#	lines in file1 matable to a line in file0 has serial number
4137da2899SCharles.Forsyth#	less than the y of some k-candidate, that k-candidate
4237da2899SCharles.Forsyth#	with the smallest such y is replaced. The new
4337da2899SCharles.Forsyth#	k-candidate is chained (via pred) to the current
4437da2899SCharles.Forsyth#	k-1 candidate so that the actual subsequence can
4537da2899SCharles.Forsyth#	be recovered. When a member has serial number greater
4637da2899SCharles.Forsyth#	that the y of all k-candidates, the klist is extended.
4737da2899SCharles.Forsyth#	At the end, the longest subsequence is pulled out
4837da2899SCharles.Forsyth#	and placed in the array J by unravel.
4937da2899SCharles.Forsyth#
5037da2899SCharles.Forsyth#	With J in hand, the matches there recorded are
5137da2899SCharles.Forsyth#	check'ed against reality to assure that no spurious
5237da2899SCharles.Forsyth#	matches have crept in due to hashing. If they have,
5337da2899SCharles.Forsyth#	they are broken, and "jackpot " is recorded--a harmless
5437da2899SCharles.Forsyth#	matter except that a true match for a spuriously
5537da2899SCharles.Forsyth#	mated line may now be unnecessarily reported as a change.
5637da2899SCharles.Forsyth#
5737da2899SCharles.Forsyth#	Much of the complexity of the program comes simply
5837da2899SCharles.Forsyth#	from trying to minimize core utilization and
5937da2899SCharles.Forsyth#	maximize the range of doable problems by dynamically
6037da2899SCharles.Forsyth#	allocating what is needed and reusing what is not.
6137da2899SCharles.Forsyth#	The core requirements for problems larger than somewhat
6237da2899SCharles.Forsyth#	are (in words) 2*length(file0) + length(file1) +
6337da2899SCharles.Forsyth#	3*(number of k-candidates installed),  typically about
6437da2899SCharles.Forsyth#	6n words for files of length n.
6537da2899SCharles.Forsyth#
6637da2899SCharles.Forsyth#
6737da2899SCharles.Forsyth
6837da2899SCharles.Forsythinclude "sys.m";
6937da2899SCharles.Forsyth	sys: Sys;
7037da2899SCharles.Forsyth
7137da2899SCharles.Forsythinclude "bufio.m";
7237da2899SCharles.Forsyth	bufio : Bufio;
7337da2899SCharles.ForsythIobuf : import bufio;
7437da2899SCharles.Forsyth
7537da2899SCharles.Forsythinclude "draw.m";
7637da2899SCharles.Forsyth	draw: Draw;
7737da2899SCharles.Forsythinclude "readdir.m";
7837da2899SCharles.Forsyth	readdir : Readdir;
7937da2899SCharles.Forsythinclude "string.m";
8037da2899SCharles.Forsyth	str : String;
8137da2899SCharles.Forsythinclude "arg.m";
8237da2899SCharles.Forsyth
8337da2899SCharles.ForsythDiff : module
8437da2899SCharles.Forsyth{
8537da2899SCharles.Forsyth	init: fn(ctxt: ref Draw->Context, argv: list of string);
8637da2899SCharles.Forsyth};
8737da2899SCharles.Forsyth
8837da2899SCharles.Forsythstderr: ref Sys->FD;
8937da2899SCharles.Forsyth
9037da2899SCharles.Forsythmode : int;			# '\0', 'e', 'f', 'h'
9137da2899SCharles.Forsythbflag : int;			# ignore multiple and trailing blanks
9237da2899SCharles.Forsythrflag : int;			# recurse down directory trees
9337da2899SCharles.Forsythmflag : int;			# pseudo flag: doing multiple files, one dir
9437da2899SCharles.Forsyth
9537da2899SCharles.ForsythREG,
9637da2899SCharles.ForsythBIN: con iota;
9737da2899SCharles.Forsyth
9837da2899SCharles.ForsythHALFINT : con 16;
9937da2899SCharles.ForsythUsage : con  "usage: diff [ -efbwr ] file1 ... file2";
10037da2899SCharles.Forsyth
10137da2899SCharles.Forsythcand : adt {
10237da2899SCharles.Forsyth	x : int;
10337da2899SCharles.Forsyth	y : int;
10437da2899SCharles.Forsyth	pred : int;
10537da2899SCharles.Forsyth};
10637da2899SCharles.Forsyth
10737da2899SCharles.Forsythline : adt {
10837da2899SCharles.Forsyth	serial : int;
10937da2899SCharles.Forsyth	value : int;
11037da2899SCharles.Forsyth};
11137da2899SCharles.Forsyth
11237da2899SCharles.Forsythout : ref Iobuf;
11337da2899SCharles.Forsythfile := array[2] of array of line;
11437da2899SCharles.Forsythsfile := array[2] of array of line;	# shortened by pruning common prefix and suffix
11537da2899SCharles.Forsythslen := array[2] of int;
11637da2899SCharles.Forsythilen := array[2] of int;
11737da2899SCharles.Forsythpref, suff, clen : int;			# length of prefix and suffix
11837da2899SCharles.Forsythfirstchange : int;
11937da2899SCharles.Forsythclist : array of cand;			# merely a free storage pot for candidates
12037da2899SCharles.ForsythJ : array of int;			# will be overlaid on class
12137da2899SCharles.Forsythixold, ixnew : array of int;
12237da2899SCharles.Forsythinput := array[2] of ref Iobuf ;
12337da2899SCharles.Forsythfile1, file2 : string;
12437da2899SCharles.Forsythtmpname := array[] of {"/tmp/diff1", "/tmp/diff2"};
12537da2899SCharles.Forsythwhichtmp : int;
12637da2899SCharles.Forsythanychange := 0;
12737da2899SCharles.Forsyth
12837da2899SCharles.Forsythinit(nil: ref Draw->Context, args: list of string)
12937da2899SCharles.Forsyth{
13037da2899SCharles.Forsyth	sys = load Sys Sys->PATH;
13137da2899SCharles.Forsyth	stderr = sys->fildes(2);
13237da2899SCharles.Forsyth	draw = load Draw Draw->PATH;
13337da2899SCharles.Forsyth	bufio = load Bufio Bufio->PATH;
13437da2899SCharles.Forsyth	readdir = load Readdir Readdir->PATH;
13537da2899SCharles.Forsyth	str = load String String->PATH;
13637da2899SCharles.Forsyth	if (bufio==nil)
13737da2899SCharles.Forsyth		fatal(sys->sprint("cannot load %s: %r", Bufio->PATH));
13837da2899SCharles.Forsyth	if (readdir==nil)
13937da2899SCharles.Forsyth		fatal(sys->sprint("cannot load %s: %r", Readdir->PATH));
14037da2899SCharles.Forsyth	if (str==nil)
14137da2899SCharles.Forsyth		fatal(sys->sprint("cannot load %s: %r", String->PATH));
14237da2899SCharles.Forsyth	arg := load Arg Arg->PATH;
14337da2899SCharles.Forsyth	if (arg==nil)
14437da2899SCharles.Forsyth		fatal(sys->sprint("cannot load %s: %r", Arg->PATH));
14537da2899SCharles.Forsyth	fsb, tsb : Sys->Dir;
14637da2899SCharles.Forsyth	arg->init(args);
14737da2899SCharles.Forsyth	while((o := arg->opt()) != 0)
14837da2899SCharles.Forsyth		case o {
14937da2899SCharles.Forsyth		'e' or 'f' =>
15037da2899SCharles.Forsyth			mode = o;
15137da2899SCharles.Forsyth		'w' =>
15237da2899SCharles.Forsyth			bflag = 2;
15337da2899SCharles.Forsyth		'b' =>
15437da2899SCharles.Forsyth			bflag = 1;
15537da2899SCharles.Forsyth		'r' =>
15637da2899SCharles.Forsyth			rflag = 1;
15737da2899SCharles.Forsyth		'm' =>
15837da2899SCharles.Forsyth			mflag = 1;
15937da2899SCharles.Forsyth		* =>
16037da2899SCharles.Forsyth			fatal(Usage);
16137da2899SCharles.Forsyth		}
16237da2899SCharles.Forsyth	tmp := arg->argv();
16337da2899SCharles.Forsyth	arg = nil;
16437da2899SCharles.Forsyth	j := len tmp;
16537da2899SCharles.Forsyth	if (j < 2)
16637da2899SCharles.Forsyth		fatal(Usage);
16737da2899SCharles.Forsyth	arr := array[j] of string;
16837da2899SCharles.Forsyth	for(i:=0;i<j;i++){
16937da2899SCharles.Forsyth		arr[i]= hd tmp;
17037da2899SCharles.Forsyth		tmp = tl tmp;
17137da2899SCharles.Forsyth	}
17237da2899SCharles.Forsyth
17337da2899SCharles.Forsyth	(i,tsb)=sys->stat(arr[j-1]);
17437da2899SCharles.Forsyth	if (i == -1)
17537da2899SCharles.Forsyth		fatal(sys->sprint("can't stat %s: %r", arr[j-1]));
17637da2899SCharles.Forsyth	if (j > 2) {
17737da2899SCharles.Forsyth		if (!(tsb.qid.qtype&Sys->QTDIR))
17837da2899SCharles.Forsyth			fatal(Usage);
17937da2899SCharles.Forsyth		mflag = 1;
18037da2899SCharles.Forsyth	}
18137da2899SCharles.Forsyth	else {
18237da2899SCharles.Forsyth		(i,fsb)=sys->stat(arr[0]);
18337da2899SCharles.Forsyth		if (i == -1)
18437da2899SCharles.Forsyth			fatal(sys->sprint("can't stat %s: %r", arr[0]));
18537da2899SCharles.Forsyth		if ((fsb.qid.qtype&Sys->QTDIR) && (tsb.qid.qtype&Sys->QTDIR))
18637da2899SCharles.Forsyth			mflag = 1;
18737da2899SCharles.Forsyth	}
18837da2899SCharles.Forsyth	out=bufio->fopen(sys->fildes(1),Bufio->OWRITE);
18937da2899SCharles.Forsyth	for (i = 0; i < j-1; i++) {
19037da2899SCharles.Forsyth		diff(arr[i], arr[j-1], 0);
19137da2899SCharles.Forsyth		rmtmpfiles();
19237da2899SCharles.Forsyth	}
19337da2899SCharles.Forsyth	rmtmpfiles();
19437da2899SCharles.Forsyth	out.flush();
19537da2899SCharles.Forsyth	if (anychange)
19637da2899SCharles.Forsyth		raise "fail:some";
19737da2899SCharles.Forsyth}
19837da2899SCharles.Forsyth
19937da2899SCharles.Forsyth############################# diffreg from here ....
20037da2899SCharles.Forsyth
20137da2899SCharles.Forsyth# shellsort CACM #201
20237da2899SCharles.Forsyth
20337da2899SCharles.Forsythsort(a : array of line, n : int)
20437da2899SCharles.Forsyth{
20537da2899SCharles.Forsyth	w : line;
20637da2899SCharles.Forsyth	m := 0;
20737da2899SCharles.Forsyth	for (i := 1; i <= n; i *= 2)
20837da2899SCharles.Forsyth		m = 2*i - 1;
20937da2899SCharles.Forsyth	for (m /= 2; m != 0; m /= 2) {
21037da2899SCharles.Forsyth		for (j := 1; j <= n-m ; j++) {
21137da2899SCharles.Forsyth			ai:=j;
21237da2899SCharles.Forsyth			aim:=j+m;
21337da2899SCharles.Forsyth			do {
21437da2899SCharles.Forsyth				if (a[aim].value > a[ai].value ||
21537da2899SCharles.Forsyth				   a[aim].value == a[ai].value &&
21637da2899SCharles.Forsyth				   a[aim].serial > a[ai].serial)
21737da2899SCharles.Forsyth					break;
21837da2899SCharles.Forsyth				w = a[ai];
21937da2899SCharles.Forsyth				a[ai] = a[aim];
22037da2899SCharles.Forsyth				a[aim] = w;
22137da2899SCharles.Forsyth				aim=ai;
22237da2899SCharles.Forsyth				ai-=m;
22337da2899SCharles.Forsyth			} while (ai > 0 && aim >= ai);
22437da2899SCharles.Forsyth		}
22537da2899SCharles.Forsyth	}
22637da2899SCharles.Forsyth}
22737da2899SCharles.Forsyth
22837da2899SCharles.Forsythunsort(f : array of line, l : int) : array of int
22937da2899SCharles.Forsyth{
23037da2899SCharles.Forsyth	i : int;
23137da2899SCharles.Forsyth	a := array[l+1] of int;
23237da2899SCharles.Forsyth	for(i=1;i<=l;i++)
23337da2899SCharles.Forsyth		a[f[i].serial] = f[i].value;
23437da2899SCharles.Forsyth	return a;
23537da2899SCharles.Forsyth}
23637da2899SCharles.Forsyth
23737da2899SCharles.Forsythprune()
23837da2899SCharles.Forsyth{
23937da2899SCharles.Forsyth	for(pref=0;pref< ilen[0]&&pref< ilen[1]&&
24037da2899SCharles.Forsyth		file[0][pref+1].value==file[1][pref+1].value;
24137da2899SCharles.Forsyth		pref++ ) ;
24237da2899SCharles.Forsyth	for(suff=0;suff< ilen[0]-pref&&suff< ilen[1]-pref&&
24337da2899SCharles.Forsyth		file[0][ilen[0]-suff].value==file[1][ilen[1]-suff].value;
24437da2899SCharles.Forsyth		suff++) ;
24537da2899SCharles.Forsyth	for(j:=0;j<2;j++) {
24637da2899SCharles.Forsyth		sfile[j] = file[j][pref:];
24737da2899SCharles.Forsyth		slen[j]= ilen[j]-pref-suff;
24837da2899SCharles.Forsyth		for(i:=0;i<=slen[j];i++)
24937da2899SCharles.Forsyth			sfile[j][i].serial = i;
25037da2899SCharles.Forsyth	}
25137da2899SCharles.Forsyth}
25237da2899SCharles.Forsyth
25337da2899SCharles.Forsythequiv(a: array of line, n:int , b: array of line, m: int, c : array of int)
25437da2899SCharles.Forsyth{
25537da2899SCharles.Forsyth	i := 1;
25637da2899SCharles.Forsyth	j := 1;
25737da2899SCharles.Forsyth	while(i<=n && j<=m) {
25837da2899SCharles.Forsyth		if(a[i].value < b[j].value)
25937da2899SCharles.Forsyth			a[i++].value = 0;
26037da2899SCharles.Forsyth		else if(a[i].value == b[j].value)
26137da2899SCharles.Forsyth			a[i++].value = j;
26237da2899SCharles.Forsyth		else
26337da2899SCharles.Forsyth			j++;
26437da2899SCharles.Forsyth	}
26537da2899SCharles.Forsyth	while(i <= n)
26637da2899SCharles.Forsyth		a[i++].value = 0;
26737da2899SCharles.Forsyth	b[m+1].value = 0; # huh ?
26837da2899SCharles.Forsyth	j = 1;
26937da2899SCharles.Forsyth	while(j <= m) {
27037da2899SCharles.Forsyth		c[j] = -b[j].serial;
27137da2899SCharles.Forsyth		while(b[j+1].value == b[j].value) {
27237da2899SCharles.Forsyth			j++;
27337da2899SCharles.Forsyth			c[j] = b[j].serial;
27437da2899SCharles.Forsyth		}
27537da2899SCharles.Forsyth		j++;
27637da2899SCharles.Forsyth	}
27737da2899SCharles.Forsyth	c[j] = -1;
27837da2899SCharles.Forsyth}
27937da2899SCharles.Forsyth
28037da2899SCharles.Forsythnewcand(x, y, pred : int) : int
28137da2899SCharles.Forsyth{
28237da2899SCharles.Forsyth	if (clen==len clist){
28337da2899SCharles.Forsyth		q := array[clen*2] of cand;
28437da2899SCharles.Forsyth		q[0:]=clist;
28537da2899SCharles.Forsyth		clist= array[clen*2] of cand;
28637da2899SCharles.Forsyth		clist[0:]=q;
28737da2899SCharles.Forsyth		q=nil;
28837da2899SCharles.Forsyth	}
28937da2899SCharles.Forsyth	clist[clen].x=x;
29037da2899SCharles.Forsyth	clist[clen].y=y;
29137da2899SCharles.Forsyth	clist[clen].pred=pred;
29237da2899SCharles.Forsyth	return clen++;
29337da2899SCharles.Forsyth}
29437da2899SCharles.Forsyth
29537da2899SCharles.Forsythsearch(c : array of int, k,y : int) : int
29637da2899SCharles.Forsyth{
29737da2899SCharles.Forsyth	if(clist[c[k]].y < y)	# quick look for typical case
29837da2899SCharles.Forsyth		return k+1;
29937da2899SCharles.Forsyth	i := 0;
30037da2899SCharles.Forsyth	j := k+1;
30137da2899SCharles.Forsyth	while((l:=(i+j)/2) > i) {
30237da2899SCharles.Forsyth		t := clist[c[l]].y;
30337da2899SCharles.Forsyth		if(t > y)
30437da2899SCharles.Forsyth			j = l;
30537da2899SCharles.Forsyth		else if(t < y)
30637da2899SCharles.Forsyth			i = l;
30737da2899SCharles.Forsyth		else
30837da2899SCharles.Forsyth			return l;
30937da2899SCharles.Forsyth	}
31037da2899SCharles.Forsyth	return l+1;
31137da2899SCharles.Forsyth}
31237da2899SCharles.Forsyth
31337da2899SCharles.Forsythstone(a : array of int ,n : int, b: array of int , c : array of int) : int
31437da2899SCharles.Forsyth{
31537da2899SCharles.Forsyth	oldc, oldl, tc, l ,y : int;
31637da2899SCharles.Forsyth	k := 0;
31737da2899SCharles.Forsyth	c[0] = newcand(0,0,0);
31837da2899SCharles.Forsyth	for(i:=1; i<=n; i++) {
31937da2899SCharles.Forsyth		j := a[i];
32037da2899SCharles.Forsyth		if(j==0)
32137da2899SCharles.Forsyth			continue;
32237da2899SCharles.Forsyth		y = -b[j];
32337da2899SCharles.Forsyth		oldl = 0;
32437da2899SCharles.Forsyth		oldc = c[0];
32537da2899SCharles.Forsyth		do {
32637da2899SCharles.Forsyth			if(y <= clist[oldc].y)
32737da2899SCharles.Forsyth				continue;
32837da2899SCharles.Forsyth			l = search(c, k, y);
32937da2899SCharles.Forsyth			if(l!=oldl+1)
33037da2899SCharles.Forsyth				oldc = c[l-1];
33137da2899SCharles.Forsyth			if(l<=k) {
33237da2899SCharles.Forsyth				if(clist[c[l]].y <= y)
33337da2899SCharles.Forsyth					continue;
33437da2899SCharles.Forsyth				tc = c[l];
33537da2899SCharles.Forsyth				c[l] = newcand(i,y,oldc);
33637da2899SCharles.Forsyth				oldc = tc;
33737da2899SCharles.Forsyth				oldl = l;
33837da2899SCharles.Forsyth			} else {
33937da2899SCharles.Forsyth				c[l] = newcand(i,y,oldc);
34037da2899SCharles.Forsyth				k++;
34137da2899SCharles.Forsyth				break;
34237da2899SCharles.Forsyth			}
34337da2899SCharles.Forsyth		} while((y=b[j+=1]) > 0);
34437da2899SCharles.Forsyth	}
34537da2899SCharles.Forsyth	return k;
34637da2899SCharles.Forsyth}
34737da2899SCharles.Forsyth
34837da2899SCharles.Forsythunravel(p : int)
34937da2899SCharles.Forsyth{
35037da2899SCharles.Forsyth	for(i:=0; i<=ilen[0]; i++) {
35137da2899SCharles.Forsyth		if (i <= pref)
35237da2899SCharles.Forsyth			J[i] = i;
35337da2899SCharles.Forsyth		else if (i > ilen[0]-suff)
35437da2899SCharles.Forsyth			J[i] = i+ ilen[1]-ilen[0];
35537da2899SCharles.Forsyth		else
35637da2899SCharles.Forsyth			J[i] = 0;
35737da2899SCharles.Forsyth	}
35837da2899SCharles.Forsyth	for(q:=clist[p];q.y!=0;q=clist[q.pred])
35937da2899SCharles.Forsyth		J[q.x+pref] = q.y+pref;
36037da2899SCharles.Forsyth}
36137da2899SCharles.Forsyth
36237da2899SCharles.Forsythoutput()
36337da2899SCharles.Forsyth{
36437da2899SCharles.Forsyth	i1: int;
36537da2899SCharles.Forsyth	m := ilen[0];
36637da2899SCharles.Forsyth	J[0] = 0;
36737da2899SCharles.Forsyth	J[m+1] = ilen[1]+1;
36837da2899SCharles.Forsyth	if (mode != 'e') {
36937da2899SCharles.Forsyth		for (i0 := 1; i0 <= m; i0 = i1+1) {
37037da2899SCharles.Forsyth			while (i0 <= m && J[i0] == J[i0-1]+1)
37137da2899SCharles.Forsyth				i0++;
37237da2899SCharles.Forsyth			j0 := J[i0-1]+1;
37337da2899SCharles.Forsyth			i1 = i0-1;
37437da2899SCharles.Forsyth			while (i1 < m && J[i1+1] == 0)
37537da2899SCharles.Forsyth				i1++;
37637da2899SCharles.Forsyth			j1 := J[i1+1]-1;
37737da2899SCharles.Forsyth			J[i1] = j1;
37837da2899SCharles.Forsyth			change(i0, i1, j0, j1);
37937da2899SCharles.Forsyth		}
38037da2899SCharles.Forsyth	}
38137da2899SCharles.Forsyth	else {
38237da2899SCharles.Forsyth		for (i0 := m; i0 >= 1; i0 = i1-1) {
38337da2899SCharles.Forsyth			while (i0 >= 1 && J[i0] == J[i0+1]-1 && J[i0])
38437da2899SCharles.Forsyth				i0--;
38537da2899SCharles.Forsyth			j0 := J[i0+1]-1;
38637da2899SCharles.Forsyth			i1 = i0+1;
38737da2899SCharles.Forsyth			while (i1 > 1 && J[i1-1] == 0)
38837da2899SCharles.Forsyth				i1--;
38937da2899SCharles.Forsyth			j1 := J[i1-1]+1;
39037da2899SCharles.Forsyth			J[i1] = j1;
39137da2899SCharles.Forsyth			change(i1 , i0, j1, j0);
39237da2899SCharles.Forsyth		}
39337da2899SCharles.Forsyth	}
39437da2899SCharles.Forsyth	if (m == 0)
39537da2899SCharles.Forsyth		change(1, 0, 1, ilen[1]);
39637da2899SCharles.Forsyth	out.flush();
39737da2899SCharles.Forsyth}
39837da2899SCharles.Forsyth
39937da2899SCharles.Forsythdiffreg(f,t : string)
40037da2899SCharles.Forsyth{
40137da2899SCharles.Forsyth	k : int;
40237da2899SCharles.Forsyth
40337da2899SCharles.Forsyth	(b0, b0type) := prepare(0, f);
40437da2899SCharles.Forsyth	if (b0==nil)
40537da2899SCharles.Forsyth		return;
40637da2899SCharles.Forsyth	(b1, b1type) := prepare(1, t);
40737da2899SCharles.Forsyth	if (b1==nil) {
40837da2899SCharles.Forsyth		b0=nil;
40937da2899SCharles.Forsyth		return;
41037da2899SCharles.Forsyth	}
41137da2899SCharles.Forsyth	if (b0type == BIN || b1type == BIN) {
41237da2899SCharles.Forsyth		if (cmp(b0, b1)) {
41337da2899SCharles.Forsyth			out.puts(sys->sprint("Binary files %s %s differ\n", f, t));
41437da2899SCharles.Forsyth			anychange = 1;
41537da2899SCharles.Forsyth		}
41637da2899SCharles.Forsyth		b0 = nil;
41737da2899SCharles.Forsyth		b1 = nil;
41837da2899SCharles.Forsyth		return;
41937da2899SCharles.Forsyth	}
42037da2899SCharles.Forsyth	clen=0;
42137da2899SCharles.Forsyth	prune();
42237da2899SCharles.Forsyth	file[0]=nil;
42337da2899SCharles.Forsyth	file[1]=nil;
42437da2899SCharles.Forsyth	sort(sfile[0],slen[0]);
42537da2899SCharles.Forsyth	sort(sfile[1],slen[1]);
42637da2899SCharles.Forsyth	member := array[slen[1]+2] of int;
42737da2899SCharles.Forsyth	equiv(sfile[0], slen[0],sfile[1],slen[1], member);
42837da2899SCharles.Forsyth	class:=unsort(sfile[0],slen[0]);
42937da2899SCharles.Forsyth	sfile[0]=nil;
43037da2899SCharles.Forsyth	sfile[1]=nil;
43137da2899SCharles.Forsyth	klist := array[slen[0]+2] of int;
43237da2899SCharles.Forsyth	clist = array[1] of cand;
43337da2899SCharles.Forsyth	k = stone(class, slen[0], member, klist);
43437da2899SCharles.Forsyth	J = array[ilen[0]+2] of int;
43537da2899SCharles.Forsyth	unravel(klist[k]);
43637da2899SCharles.Forsyth	clist=nil;
43737da2899SCharles.Forsyth	klist=nil;
43837da2899SCharles.Forsyth	class=nil;
43937da2899SCharles.Forsyth	member=nil;
44037da2899SCharles.Forsyth	ixold = array[ilen[0]+2] of int;
44137da2899SCharles.Forsyth	ixnew = array[ilen[1]+2] of int;
44237da2899SCharles.Forsyth
44337da2899SCharles.Forsyth	b0.seek(big 0, 0);
44437da2899SCharles.Forsyth	b1.seek(big 0, 0);
44537da2899SCharles.Forsyth	check(b0, b1);
44637da2899SCharles.Forsyth	output();
44737da2899SCharles.Forsyth	ixold=nil;
44837da2899SCharles.Forsyth	ixnew=nil;
44937da2899SCharles.Forsyth	b0=nil;
45037da2899SCharles.Forsyth	b1=nil;
45137da2899SCharles.Forsyth}
45237da2899SCharles.Forsyth
45337da2899SCharles.Forsyth######################## diffio starts here...
45437da2899SCharles.Forsyth
45537da2899SCharles.Forsyth
45637da2899SCharles.Forsyth# hashing has the effect of
45737da2899SCharles.Forsyth# arranging line in 7-bit bytes and then
45837da2899SCharles.Forsyth# summing 1-s complement in 16-bit hunks
45937da2899SCharles.Forsyth
46037da2899SCharles.Forsythreadhash(bp : ref Iobuf) : int
46137da2899SCharles.Forsyth{
46237da2899SCharles.Forsyth	sum := 1;
46337da2899SCharles.Forsyth	shift := 0;
46437da2899SCharles.Forsyth	buf := bp.gets('\n');
46537da2899SCharles.Forsyth	if (buf == nil)
46637da2899SCharles.Forsyth		return 0;
46737da2899SCharles.Forsyth	buf = buf[0:len buf -1];
46837da2899SCharles.Forsyth	p := 0;
46937da2899SCharles.Forsyth	case bflag {
47037da2899SCharles.Forsyth		# various types of white space handling
47137da2899SCharles.Forsyth		0 =>
47237da2899SCharles.Forsyth			while (p< len buf) {
47337da2899SCharles.Forsyth				sum += (buf[p] << (shift &= (HALFINT-1)));
47437da2899SCharles.Forsyth				p++;
47537da2899SCharles.Forsyth				shift += 7;
47637da2899SCharles.Forsyth			}
47737da2899SCharles.Forsyth		1 =>
47837da2899SCharles.Forsyth
47937da2899SCharles.Forsyth			 # coalesce multiple white-space
48037da2899SCharles.Forsyth
48137da2899SCharles.Forsyth			for (space := 0; p< len buf; p++) {
48237da2899SCharles.Forsyth				if (buf[p]==' ' || buf[p]=='\t') {
48337da2899SCharles.Forsyth					space++;
48437da2899SCharles.Forsyth					continue;
48537da2899SCharles.Forsyth				}
48637da2899SCharles.Forsyth				if (space) {
48737da2899SCharles.Forsyth					shift += 7;
48837da2899SCharles.Forsyth					space = 0;
48937da2899SCharles.Forsyth				}
49037da2899SCharles.Forsyth				sum +=  (buf[p] << (shift &= (HALFINT-1)));
49137da2899SCharles.Forsyth				p++;
49237da2899SCharles.Forsyth				shift += 7;
49337da2899SCharles.Forsyth			}
49437da2899SCharles.Forsyth		* =>
49537da2899SCharles.Forsyth
49637da2899SCharles.Forsyth			 # strip all white-space
49737da2899SCharles.Forsyth
49837da2899SCharles.Forsyth			while (p< len buf) {
49937da2899SCharles.Forsyth				if (buf[p]==' ' || buf[p]=='\t') {
50037da2899SCharles.Forsyth					p++;
50137da2899SCharles.Forsyth					continue;
50237da2899SCharles.Forsyth				}
50337da2899SCharles.Forsyth				sum +=  (buf[p] << (shift &= (HALFINT-1)));
50437da2899SCharles.Forsyth				p++;
50537da2899SCharles.Forsyth				shift += 7;
50637da2899SCharles.Forsyth			}
50737da2899SCharles.Forsyth	}
50837da2899SCharles.Forsyth	return sum;
50937da2899SCharles.Forsyth}
51037da2899SCharles.Forsyth
51137da2899SCharles.Forsythprepare(i : int, arg : string) : (ref Iobuf, int)
51237da2899SCharles.Forsyth{
51337da2899SCharles.Forsyth	h : int;
51437da2899SCharles.Forsyth	bp := bufio->open(arg,Bufio->OREAD);
51537da2899SCharles.Forsyth	if (bp==nil) {
51637da2899SCharles.Forsyth		error(sys->sprint("cannot open %s: %r", arg));
51737da2899SCharles.Forsyth		return (nil, 0);
51837da2899SCharles.Forsyth	}
51937da2899SCharles.Forsyth	buf := array[1024] of byte;
52037da2899SCharles.Forsyth	n :=bp.read(buf, len buf);
52137da2899SCharles.Forsyth	str1 := string buf[0:n];
52237da2899SCharles.Forsyth	for (j:=0;j<len str1 -2;j++)
52337da2899SCharles.Forsyth		if (str1[j] == Sys->UTFerror)
52437da2899SCharles.Forsyth			return (bp, BIN);
52537da2899SCharles.Forsyth	bp.seek(big 0, Sys->SEEKSTART);
52637da2899SCharles.Forsyth	p := array[4] of line;
52737da2899SCharles.Forsyth	for (j = 0; h = readhash(bp); p[j].value = h){
52837da2899SCharles.Forsyth		j++;
52937da2899SCharles.Forsyth		if (j+3>=len p){
53037da2899SCharles.Forsyth			newp:=array[len p*2] of line;
53137da2899SCharles.Forsyth			newp[0:]=p[0:];
53237da2899SCharles.Forsyth			p=array[len p*2] of line;
53337da2899SCharles.Forsyth			p=newp;
53437da2899SCharles.Forsyth			newp=nil;
53537da2899SCharles.Forsyth		}
53637da2899SCharles.Forsyth	}
53737da2899SCharles.Forsyth	ilen[i]=j;
53837da2899SCharles.Forsyth	file[i] = p;
53937da2899SCharles.Forsyth	input[i] = bp;
54037da2899SCharles.Forsyth	if (i == 0) {
54137da2899SCharles.Forsyth		file1 = arg;
54237da2899SCharles.Forsyth		firstchange = 0;
54337da2899SCharles.Forsyth	}
54437da2899SCharles.Forsyth	else
54537da2899SCharles.Forsyth		file2 = arg;
54637da2899SCharles.Forsyth	return (bp, REG);
54737da2899SCharles.Forsyth}
54837da2899SCharles.Forsyth
54937da2899SCharles.Forsythsquishspace(buf : string) : string
55037da2899SCharles.Forsyth{
55137da2899SCharles.Forsyth	q:=0;
55237da2899SCharles.Forsyth	p:=0;
55337da2899SCharles.Forsyth	for (space := 0; q<len buf; q++) {
55437da2899SCharles.Forsyth		if (buf[q]==' ' || buf[q]=='\t') {
55537da2899SCharles.Forsyth			space++;
55637da2899SCharles.Forsyth			continue;
55737da2899SCharles.Forsyth		}
55837da2899SCharles.Forsyth		if (space && bflag == 1) {
55937da2899SCharles.Forsyth			buf[p] = ' ';
56037da2899SCharles.Forsyth			p++;
56137da2899SCharles.Forsyth			space = 0;
56237da2899SCharles.Forsyth		}
56337da2899SCharles.Forsyth		buf[p]=buf[q];
56437da2899SCharles.Forsyth		p++;
56537da2899SCharles.Forsyth	}
56637da2899SCharles.Forsyth	buf=buf[0:p];
56737da2899SCharles.Forsyth	return buf;
56837da2899SCharles.Forsyth}
56937da2899SCharles.Forsyth
57037da2899SCharles.Forsyth
57137da2899SCharles.Forsyth# need to fix up for unexpected EOF's
57237da2899SCharles.Forsyth
57337da2899SCharles.Forsythftell(b: ref Iobuf): int
57437da2899SCharles.Forsyth{
57537da2899SCharles.Forsyth	return int b.offset();
57637da2899SCharles.Forsyth}
57737da2899SCharles.Forsyth
57837da2899SCharles.Forsythcheck(bf, bt : ref Iobuf)
57937da2899SCharles.Forsyth{
58037da2899SCharles.Forsyth	fbuf, tbuf : string;
58137da2899SCharles.Forsyth	f:=1;
58237da2899SCharles.Forsyth	t:=1;
58337da2899SCharles.Forsyth	ixold[0] = ixnew[0] = 0;
58437da2899SCharles.Forsyth	for (; f < ilen[0]; f++) {
58537da2899SCharles.Forsyth		fbuf = bf.gets('\n');
58637da2899SCharles.Forsyth		if (fbuf!=nil)
58737da2899SCharles.Forsyth			fbuf=fbuf[0:len fbuf -1];
58837da2899SCharles.Forsyth		ixold[f] = ftell(bf);
58937da2899SCharles.Forsyth		if (J[f] == 0)
59037da2899SCharles.Forsyth			continue;
59137da2899SCharles.Forsyth		tbuflen: int;
59237da2899SCharles.Forsyth		do {
59337da2899SCharles.Forsyth			tbuf = bt.gets('\n');
59437da2899SCharles.Forsyth			if (tbuf!=nil)
59537da2899SCharles.Forsyth				tbuf=tbuf[0:len tbuf -1];
59637da2899SCharles.Forsyth			tbuflen = len array of byte tbuf;
59737da2899SCharles.Forsyth			ixnew[t] = ftell(bt);
59837da2899SCharles.Forsyth		} while (t++ < J[f]);
59937da2899SCharles.Forsyth		if (bflag) {
60037da2899SCharles.Forsyth			fbuf = squishspace(fbuf);
60137da2899SCharles.Forsyth			tbuf = squishspace(tbuf);
60237da2899SCharles.Forsyth		}
60337da2899SCharles.Forsyth		if (len fbuf != len tbuf || fbuf!=tbuf)
60437da2899SCharles.Forsyth			J[f] = 0;
60537da2899SCharles.Forsyth	}
60637da2899SCharles.Forsyth	while (t < ilen[1]) {
60737da2899SCharles.Forsyth		tbuf = bt.gets('\n');
60837da2899SCharles.Forsyth		if (tbuf!=nil)
60937da2899SCharles.Forsyth			tbuf=tbuf[0:len tbuf -1];
61037da2899SCharles.Forsyth		ixnew[t] = ftell(bt);
61137da2899SCharles.Forsyth		t++;
61237da2899SCharles.Forsyth	}
61337da2899SCharles.Forsyth}
61437da2899SCharles.Forsyth
61537da2899SCharles.Forsythrange(a, b : int, separator : string)
61637da2899SCharles.Forsyth{
61737da2899SCharles.Forsyth	if (a>b)
61837da2899SCharles.Forsyth		out.puts(sys->sprint("%d", b));
61937da2899SCharles.Forsyth	else
62037da2899SCharles.Forsyth		out.puts(sys->sprint("%d", a));
62137da2899SCharles.Forsyth	if (a < b)
62237da2899SCharles.Forsyth		out.puts(sys->sprint("%s%d", separator, b));
62337da2899SCharles.Forsyth}
62437da2899SCharles.Forsyth
62537da2899SCharles.Forsythfetch(f : array of int, a,b : int , bp : ref Iobuf, s : string)
62637da2899SCharles.Forsyth{
62737da2899SCharles.Forsyth	buf : string;
62837da2899SCharles.Forsyth	bp.seek(big f[a-1], 0);
62937da2899SCharles.Forsyth	while (a++ <= b) {
63037da2899SCharles.Forsyth		buf=bp.gets('\n');
63137da2899SCharles.Forsyth		out.puts(s);
63237da2899SCharles.Forsyth		out.puts(buf);
63337da2899SCharles.Forsyth	}
63437da2899SCharles.Forsyth}
63537da2899SCharles.Forsyth
63637da2899SCharles.Forsythchange(a, b, c, d : int)
63737da2899SCharles.Forsyth{
63837da2899SCharles.Forsyth	if (a > b && c > d)
63937da2899SCharles.Forsyth		return;
64037da2899SCharles.Forsyth	anychange = 1;
64137da2899SCharles.Forsyth	if (mflag && firstchange == 0) {
64237da2899SCharles.Forsyth		out.puts(sys->sprint( "diff %s %s\n", file1, file2));
64337da2899SCharles.Forsyth		firstchange = 1;
64437da2899SCharles.Forsyth	}
64537da2899SCharles.Forsyth	if (mode != 'f') {
64637da2899SCharles.Forsyth		range(a, b, ",");
64737da2899SCharles.Forsyth		if (a>b)
64837da2899SCharles.Forsyth			out.putc('a');
64937da2899SCharles.Forsyth		else if (c>d)
65037da2899SCharles.Forsyth			out.putc('d');
65137da2899SCharles.Forsyth		else
65237da2899SCharles.Forsyth			out.putc('c');
65337da2899SCharles.Forsyth		if (mode != 'e')
65437da2899SCharles.Forsyth			range(c, d, ",");
65537da2899SCharles.Forsyth	}
65637da2899SCharles.Forsyth	else {
65737da2899SCharles.Forsyth		if (a>b)
65837da2899SCharles.Forsyth			out.putc('a');
65937da2899SCharles.Forsyth		else if (c>d)
66037da2899SCharles.Forsyth			out.putc('d');
66137da2899SCharles.Forsyth		else
66237da2899SCharles.Forsyth			out.putc('c');
66337da2899SCharles.Forsyth		range(a, b, " ");
66437da2899SCharles.Forsyth	}
66537da2899SCharles.Forsyth	out.putc('\n');
66637da2899SCharles.Forsyth	if (mode == 0) {
66737da2899SCharles.Forsyth		fetch(ixold, a, b, input[0], "< ");
66837da2899SCharles.Forsyth		if (a <= b && c <= d)
66937da2899SCharles.Forsyth			out.puts("---\n");
67037da2899SCharles.Forsyth	}
67137da2899SCharles.Forsyth	if (mode==0)
67237da2899SCharles.Forsyth		fetch(ixnew, c, d, input[1], "> ");
67337da2899SCharles.Forsyth	else
67437da2899SCharles.Forsyth		fetch(ixnew, c, d, input[1], "");
67537da2899SCharles.Forsyth
67637da2899SCharles.Forsyth	if (mode != 0 && c <= d)
67737da2899SCharles.Forsyth		out.puts(".\n");
67837da2899SCharles.Forsyth}
67937da2899SCharles.Forsyth
68037da2899SCharles.Forsyth
68137da2899SCharles.Forsyth######################### diffdir starts here ......
68237da2899SCharles.Forsyth
68337da2899SCharles.Forsythscandir(name : string) : array of string
68437da2899SCharles.Forsyth{
68537da2899SCharles.Forsyth	(db,nitems):= readdir->init(name,Readdir->NAME);
68637da2899SCharles.Forsyth	cp := array[nitems] of string;
68737da2899SCharles.Forsyth	for(i:=0;i<nitems;i++)
68837da2899SCharles.Forsyth		cp[i]=db[i].name;
68937da2899SCharles.Forsyth	return cp;
69037da2899SCharles.Forsyth}
69137da2899SCharles.Forsyth
69237da2899SCharles.Forsyth
69337da2899SCharles.Forsythdiffdir(f, t : string, level : int)
69437da2899SCharles.Forsyth{
69537da2899SCharles.Forsyth	df, dt : array of string;
69637da2899SCharles.Forsyth	fb, tb : string;
69737da2899SCharles.Forsyth	i:=0;
69837da2899SCharles.Forsyth	j:=0;
69937da2899SCharles.Forsyth	df = scandir(f);
70037da2899SCharles.Forsyth	dt = scandir(t);
70137da2899SCharles.Forsyth	while ((i<len df) || (j<len dt)) {
70237da2899SCharles.Forsyth		if ((j==len dt) || (i<len df && df[i] < dt[j])) {
70337da2899SCharles.Forsyth			if (mode == 0)
70437da2899SCharles.Forsyth				out.puts(sys->sprint("Only in %s: %s\n", f, df[i]));
70537da2899SCharles.Forsyth			i++;
70637da2899SCharles.Forsyth			continue;
70737da2899SCharles.Forsyth		}
70837da2899SCharles.Forsyth		if ((i==len df) || (j<len dt && df[i] > dt[j])) {
70937da2899SCharles.Forsyth			if (mode == 0)
71037da2899SCharles.Forsyth				out.puts(sys->sprint("Only in %s: %s\n", t, dt[j]));
71137da2899SCharles.Forsyth			j++;
71237da2899SCharles.Forsyth			continue;
71337da2899SCharles.Forsyth		}
71437da2899SCharles.Forsyth		fb=sys->sprint("%s/%s", f, df[i]);
71537da2899SCharles.Forsyth		tb=sys->sprint("%s/%s", t, dt[j]);
71637da2899SCharles.Forsyth		diff(fb, tb, level+1);
71737da2899SCharles.Forsyth		i++; j++;
71837da2899SCharles.Forsyth	}
71937da2899SCharles.Forsyth}
72037da2899SCharles.Forsyth
72137da2899SCharles.Forsythcmp(b0, b1: ref Iobuf): int
72237da2899SCharles.Forsyth{
72337da2899SCharles.Forsyth	b0.seek(big 0, Sys->SEEKSTART);
72437da2899SCharles.Forsyth	b1.seek(big 0, Sys->SEEKSTART);
72537da2899SCharles.Forsyth	buf0 := array[1024] of byte;
72637da2899SCharles.Forsyth	buf1 := array[1024] of byte;
72737da2899SCharles.Forsyth	for (;;) {
72837da2899SCharles.Forsyth		n0 := b0.read(buf0, len buf0);
72937da2899SCharles.Forsyth		n1 := b1.read(buf1, len buf1);
73037da2899SCharles.Forsyth
73137da2899SCharles.Forsyth		if (n0 != n1)
73237da2899SCharles.Forsyth			return 1;
73337da2899SCharles.Forsyth
73437da2899SCharles.Forsyth		if (n0 == 0)
73537da2899SCharles.Forsyth			return 0;
73637da2899SCharles.Forsyth
73737da2899SCharles.Forsyth		for (i := 0; i < n0; i++)
73837da2899SCharles.Forsyth			if (buf0[i] != buf1[i])
73937da2899SCharles.Forsyth				return 1;
74037da2899SCharles.Forsyth	}
74137da2899SCharles.Forsyth}
74237da2899SCharles.Forsyth
74337da2899SCharles.Forsyth################## main from here.....
74437da2899SCharles.Forsyth
74537da2899SCharles.ForsythREGULAR_FILE(s : Sys->Dir) : int
74637da2899SCharles.Forsyth{
74737da2899SCharles.Forsyth	# both pipes and networks contain non-zero-length files
74837da2899SCharles.Forsyth	# which are not seekable.
74937da2899SCharles.Forsyth	return (s.qid.qtype&Sys->QTDIR) == 0 &&
75037da2899SCharles.Forsyth		s.dtype != '|' &&
75137da2899SCharles.Forsyth		s.dtype != 'I';
75237da2899SCharles.Forsyth#		&& s.length > 0;	device files have zero length.
75337da2899SCharles.Forsyth}
75437da2899SCharles.Forsyth
75537da2899SCharles.Forsythrmtmpfiles()
75637da2899SCharles.Forsyth{
75737da2899SCharles.Forsyth	while (whichtmp > 0) {
75837da2899SCharles.Forsyth		whichtmp--;
75937da2899SCharles.Forsyth		sys->remove(tmpname[whichtmp]);
76037da2899SCharles.Forsyth	}
76137da2899SCharles.Forsyth}
76237da2899SCharles.Forsyth
76337da2899SCharles.Forsythmktmpfile(inputf : ref Sys->FD) : (string, Sys->Dir)
76437da2899SCharles.Forsyth{
76537da2899SCharles.Forsyth	i, j : int;
76637da2899SCharles.Forsyth	sb : Sys->Dir;
76737da2899SCharles.Forsyth	p : string;
76837da2899SCharles.Forsyth	buf := array[8192] of byte;
76937da2899SCharles.Forsyth
77037da2899SCharles.Forsyth	p = tmpname[whichtmp++];
77137da2899SCharles.Forsyth	fd := sys->create(p, Sys->OWRITE, 8r600);
77237da2899SCharles.Forsyth	if (fd == nil) {
77337da2899SCharles.Forsyth		error(sys->sprint("cannot create %s: %r", p));
77437da2899SCharles.Forsyth		return (nil, sb);
77537da2899SCharles.Forsyth	}
77637da2899SCharles.Forsyth	while ((i = sys->read(inputf, buf, len buf)) > 0) {
77737da2899SCharles.Forsyth		if ((i = sys->write(fd, buf, i)) < 0)
77837da2899SCharles.Forsyth			break;
77937da2899SCharles.Forsyth	}
78037da2899SCharles.Forsyth	(j,sb)=sys->fstat(fd);
78137da2899SCharles.Forsyth	if (i < 0 || j < 0) {
78237da2899SCharles.Forsyth		error(sys->sprint("cannot read/write %s: %r", p));
78337da2899SCharles.Forsyth		return (nil, sb);
78437da2899SCharles.Forsyth	}
78537da2899SCharles.Forsyth	return (p, sb);
78637da2899SCharles.Forsyth}
78737da2899SCharles.Forsyth
78837da2899SCharles.Forsyth
78937da2899SCharles.Forsythstatfile(file : string) : (string,Sys->Dir)
79037da2899SCharles.Forsyth{
79137da2899SCharles.Forsyth	(ret,sb):=sys->stat(file);
79237da2899SCharles.Forsyth	if (ret==-1) {
793*ca1042d3SCharles.Forsyth		if (file != "-" || sys->fstat(sys->fildes(0)).t0 == -1){
79437da2899SCharles.Forsyth			error(sys->sprint("cannot stat %s: %r", file));
79537da2899SCharles.Forsyth			return (nil,sb);
79637da2899SCharles.Forsyth		}
79737da2899SCharles.Forsyth		(file, sb) = mktmpfile(sys->fildes(0));
79837da2899SCharles.Forsyth	}
79937da2899SCharles.Forsyth	else if (!REGULAR_FILE(sb) && !(sb.qid.qtype&Sys->QTDIR)) {
80037da2899SCharles.Forsyth		if ((i := sys->open(file, Sys->OREAD)) == nil) {
80137da2899SCharles.Forsyth			error(sys->sprint("cannot open %s: %r", file));
80237da2899SCharles.Forsyth			return (nil, sb);
80337da2899SCharles.Forsyth		}
80437da2899SCharles.Forsyth		(file, sb) = mktmpfile(i);
80537da2899SCharles.Forsyth	}
80637da2899SCharles.Forsyth	return (file,sb);
80737da2899SCharles.Forsyth}
80837da2899SCharles.Forsyth
80937da2899SCharles.Forsythdiff(f, t : string, level : int)
81037da2899SCharles.Forsyth{
81137da2899SCharles.Forsyth	fp,tp,p,rest,fb,tb : string;
81237da2899SCharles.Forsyth	fsb, tsb : Sys->Dir;
81337da2899SCharles.Forsyth	(fp,fsb) = statfile(f);
81437da2899SCharles.Forsyth	if (fp == nil)
81537da2899SCharles.Forsyth		return;
81637da2899SCharles.Forsyth	(tp,tsb) = statfile(t);
81737da2899SCharles.Forsyth	if (tp == nil)
81837da2899SCharles.Forsyth		return;
81937da2899SCharles.Forsyth	if ((fsb.qid.qtype&Sys->QTDIR) && (tsb.qid.qtype&Sys->QTDIR)) {
82037da2899SCharles.Forsyth		if (rflag || level == 0)
82137da2899SCharles.Forsyth			diffdir(fp, tp, level);
82237da2899SCharles.Forsyth		else
82337da2899SCharles.Forsyth			out.puts(sys->sprint("Common subdirectories: %s and %s\n", fp, tp));
82437da2899SCharles.Forsyth	}
82537da2899SCharles.Forsyth	else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb)){
82637da2899SCharles.Forsyth		diffreg(fp, tp);
82737da2899SCharles.Forsyth	} else {
82837da2899SCharles.Forsyth		if (!(fsb.qid.qtype&Sys->QTDIR)) {
82937da2899SCharles.Forsyth			(p,rest)=str->splitr(f,"/");
83037da2899SCharles.Forsyth			if (rest!=nil)
83137da2899SCharles.Forsyth				p = rest;
83237da2899SCharles.Forsyth			tb=sys->sprint("%s/%s", tp, p);
83337da2899SCharles.Forsyth			diffreg(fp, tb);
83437da2899SCharles.Forsyth		}
83537da2899SCharles.Forsyth		else {
83637da2899SCharles.Forsyth			(p,rest)=str->splitr(t,"/");
83737da2899SCharles.Forsyth			if (rest!=nil)
83837da2899SCharles.Forsyth				p = rest;
83937da2899SCharles.Forsyth			fb=sys->sprint("%s/%s", fp, p);
84037da2899SCharles.Forsyth			diffreg(fb, tp);
84137da2899SCharles.Forsyth		}
84237da2899SCharles.Forsyth	}
84337da2899SCharles.Forsyth}
84437da2899SCharles.Forsyth
84537da2899SCharles.Forsythfatal(s: string)
84637da2899SCharles.Forsyth{
84737da2899SCharles.Forsyth	sys->fprint(stderr, "diff: %s\n", s);
84837da2899SCharles.Forsyth	raise "fail:error";
84937da2899SCharles.Forsyth}
85037da2899SCharles.Forsyth
85137da2899SCharles.Forsytherror(s: string)
85237da2899SCharles.Forsyth{
85337da2899SCharles.Forsyth	sys->fprint(stderr, "diff: %s\n", s);
85437da2899SCharles.Forsyth}
855