code: purgatorio

ref: db3b70f1424dfd8c5ea45206790df066c53bfab0
dir: /appl/lib/readdir.b/

View raw version
implement Readdir;

include "sys.m";
	sys: Sys;
	Dir: import sys;
include "readdir.m";

init(path: string, sortkey: int): (array of ref Dir, int)
{
	sys = load Sys Sys->PATH;
	fd := sys->open(path, Sys->OREAD);
	if(fd == nil)
		return (nil, -1);
	return readall(fd, sortkey);
}

readall(fd: ref Sys->FD, sortkey: int): (array of ref Dir, int)
{
	sys = load Sys Sys->PATH;
	dl: list of array of Dir;
	n := 0;
	for(;;){
		(nr, b) := sys->dirread(fd);
		if(nr <= 0){
			# any error makes the whole directory unreadable
			if(nr < 0)
				return (nil, -1);
			break;
		}
		dl = b :: dl;
		n += nr;
	}
	rl := dl;
	for(dl = nil; rl != nil; rl = tl rl)
		dl = hd rl :: dl;
	a := makerefs(dl, n, sortkey & COMPACT);
	sortkey &= ~COMPACT;
	if((sortkey & ~DESCENDING) == NONE)
		return (a, len a);
	return sortdir(a, sortkey);
}

makerefs(dl: list of array of Dir, n: int, compact: int): array of ref Dir
{
	a := array[n] of ref Dir;
	ht: array of list of string;
	if(compact)
		ht = array[41] of list of string;
	j := 0;
	for(; dl != nil; dl = tl dl){
		d := hd dl;
		for(i := 0; i < len d; i++)
			if(ht == nil || hashadd(ht, d[i].name))
				a[j++] = ref d[i];
	}
	if(j != n)
		a = a[0:j];
	return a;
}

sortdir(a: array of ref Dir, key: int): (array of ref Dir, int)
{
	mergesort(a, array[len a] of ref Dir, key);
	return (a, len a);
}
	
# mergesort because it's stable.
mergesort(a, b: array of ref Dir, key: int)
{
	r := len a;
	if (r > 1) {
		m := (r-1)/2 + 1;
		mergesort(a[0:m], b[0:m], key);
		mergesort(a[m:], b[m:], key);
		b[0:] = a;
		for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
			if (greater(b[i], b[j], key))
				a[k] = b[j++];
			else
				a[k] = b[i++];
		}
		if (i < m)
			a[k:] = b[i:m];
		else if (j < r)
			a[k:] = b[j:r];
	}
}

greater(x, y: ref Dir, sortkey: int): int
{
	case (sortkey) {
	NAME => return(x.name > y.name);
	ATIME => return(x.atime < y.atime);
	MTIME => return(x.mtime < y.mtime);
	SIZE => return(x.length > y.length);
	NAME|DESCENDING => return(x.name < y.name);
	ATIME|DESCENDING => return(x.atime > y.atime);
	MTIME|DESCENDING => return(x.mtime > y.mtime);
	SIZE|DESCENDING => return(x.length < y.length);
	}
	return 0;
}

# from tcl_strhash.b
hashfn(key: string, n : int): int
{
	h := 0;
	for(i := 0; i < len key; i++){
		h = 10*h + key[i];
		h = h%n;
	}
	return h%n;
}

hashadd(ht: array of list of string, nm: string): int
{
	idx := hashfn(nm, len ht);
	for (ent := ht[idx]; ent != nil; ent = tl ent)
		if (hd ent == nm)
			return 0;
	ht[idx] = nm :: ht[idx];
	return 1;
}