xref: /inferno-os/appl/lib/readdir.b (revision 8911721efbf3b3721376e2baa30bae002c2975c2)
1implement Readdir;
2
3include "sys.m";
4	sys: Sys;
5	Dir: import sys;
6include "readdir.m";
7
8init(path: string, sortkey: int): (array of ref Dir, int)
9{
10	sys = load Sys Sys->PATH;
11	fd := sys->open(path, Sys->OREAD);
12	if(fd == nil)
13		return (nil, -1);
14	return readall(fd, sortkey);
15}
16
17readall(fd: ref Sys->FD, sortkey: int): (array of ref Dir, int)
18{
19	sys = load Sys Sys->PATH;
20	dl: list of array of Dir;
21	n := 0;
22	for(;;){
23		(nr, b) := sys->dirread(fd);
24		if(nr <= 0){
25			# any error makes the whole directory unreadable
26			if(nr < 0)
27				return (nil, -1);
28			break;
29		}
30		dl = b :: dl;
31		n += nr;
32	}
33	rl := dl;
34	for(dl = nil; rl != nil; rl = tl rl)
35		dl = hd rl :: dl;
36	a := makerefs(dl, n, sortkey & COMPACT);
37	sortkey &= ~COMPACT;
38	if((sortkey & ~DESCENDING) == NONE)
39		return (a, len a);
40	return sortdir(a, sortkey);
41}
42
43makerefs(dl: list of array of Dir, n: int, compact: int): array of ref Dir
44{
45	a := array[n] of ref Dir;
46	ht: array of list of string;
47	if(compact)
48		ht = array[41] of list of string;
49	j := 0;
50	for(; dl != nil; dl = tl dl){
51		d := hd dl;
52		for(i := 0; i < len d; i++)
53			if(ht == nil || hashadd(ht, d[i].name))
54				a[j++] = ref d[i];
55	}
56	if(j != n)
57		a = a[0:j];
58	return a;
59}
60
61sortdir(a: array of ref Dir, key: int): (array of ref Dir, int)
62{
63	mergesort(a, array[len a] of ref Dir, key);
64	return (a, len a);
65}
66
67# mergesort because it's stable.
68mergesort(a, b: array of ref Dir, key: int)
69{
70	r := len a;
71	if (r > 1) {
72		m := (r-1)/2 + 1;
73		mergesort(a[0:m], b[0:m], key);
74		mergesort(a[m:], b[m:], key);
75		b[0:] = a;
76		for ((i, j, k) := (0, m, 0); i < m && j < r; k++) {
77			if (greater(b[i], b[j], key))
78				a[k] = b[j++];
79			else
80				a[k] = b[i++];
81		}
82		if (i < m)
83			a[k:] = b[i:m];
84		else if (j < r)
85			a[k:] = b[j:r];
86	}
87}
88
89greater(x, y: ref Dir, sortkey: int): int
90{
91	case (sortkey) {
92	NAME => return(x.name > y.name);
93	ATIME => return(x.atime < y.atime);
94	MTIME => return(x.mtime < y.mtime);
95	SIZE => return(x.length > y.length);
96	NAME|DESCENDING => return(x.name < y.name);
97	ATIME|DESCENDING => return(x.atime > y.atime);
98	MTIME|DESCENDING => return(x.mtime > y.mtime);
99	SIZE|DESCENDING => return(x.length < y.length);
100	}
101	return 0;
102}
103
104# from tcl_strhash.b
105hashfn(key: string, n : int): int
106{
107	h := 0;
108	for(i := 0; i < len key; i++){
109		h = 10*h + key[i];
110		h = h%n;
111	}
112	return h%n;
113}
114
115hashadd(ht: array of list of string, nm: string): int
116{
117	idx := hashfn(nm, len ht);
118	for (ent := ht[idx]; ent != nil; ent = tl ent)
119		if (hd ent == nm)
120			return 0;
121	ht[idx] = nm :: ht[idx];
122	return 1;
123}
124