xref: /inferno-os/appl/cmd/ls.b (revision 95e7767e5b06c4582d7f542cedef2cb626ab69ab)
1implement Ls;
2
3include "sys.m";
4	sys: Sys;
5	FD, Dir: import Sys;
6
7include "draw.m";
8	Context: import Draw;
9
10include "daytime.m";
11	daytime: Daytime;
12
13include "readdir.m";
14	readdir: Readdir;
15
16include "bufio.m";
17	bufio: Bufio;
18	Iobuf: import bufio;
19
20include "string.m";
21	str: String;
22
23include "arg.m";
24
25Ls: module
26{
27	init:	fn(ctxt: ref Context, argv: list of string);
28};
29
30PREFIX: con 16r40000000;
31
32dopt := 0;
33eopt := 0;
34lopt := 0;
35mopt := 0;
36nopt := 0;
37popt := 0;
38qopt := 0;
39sopt := 0;
40topt := 0;
41uopt := 0;
42Fopt := 0;
43Topt := 0;
44now:	int;
45sortby:	int;
46
47out: ref Bufio->Iobuf;
48stderr: ref FD;
49
50delaydir: array of ref Dir;
51delayindex: int;
52
53badmodule(p: string)
54{
55	sys->fprint(stderr, "ls: cannot load %s: %r\n", p);
56	raise "fail:bad module";
57}
58
59init(nil: ref Context, argv: list of string)
60{
61	sys = load Sys Sys->PATH;
62	bufio = load Bufio Bufio->PATH;
63	if(bufio == nil)
64		badmodule(Bufio->PATH);
65	readdir = load Readdir Readdir->PATH;
66	if(readdir == nil)
67		badmodule(Readdir->PATH);
68	str = load String String->PATH;
69	if(str == nil)
70		badmodule(String->PATH);
71
72	stderr = sys->fildes(2);
73	out = bufio->fopen(sys->fildes(1), Bufio->OWRITE);
74	rev := 0;
75	sortby = Readdir->NAME;
76	compact := 0;
77
78	arg := load Arg Arg->PATH;
79	if(arg == nil)
80		badmodule(Arg->PATH);
81	arg->init(argv);
82	while((o := arg->opt()) != 0){
83		case o {
84		'l' =>
85			lopt++;
86			daytime = load Daytime Daytime->PATH;
87			if(daytime == nil)
88				badmodule(Daytime->PATH);
89			now = daytime->now();
90		'p' =>
91			popt++;
92		'q' =>
93			qopt++;
94		'd' =>
95			dopt++;
96		'e' =>
97			eopt++;
98		'm' =>
99			mopt++;
100		'n' =>
101			nopt++;
102		'k' =>
103			sopt++;
104		't' =>
105			topt++;
106		'u' =>
107			uopt++;
108		's' =>
109			sortby = Readdir->SIZE;
110		'c' =>
111			compact = Readdir->COMPACT;
112		'r' =>
113			rev = Readdir->DESCENDING;
114		'T' =>
115			Topt++;
116		'F' =>
117			Fopt++;
118		* =>
119			sys->fprint(stderr, "usage: ls [-delmnpqrstucFT] [files]\n");
120			raise "fail:usage";
121		}
122	}
123	argv = arg->argv();
124	arg = nil;
125
126	if(nopt == 0) {
127		if(topt){
128			if(uopt)
129				sortby = Readdir->ATIME;
130			else
131				sortby = Readdir->MTIME;
132		}
133	} else
134		sortby = Readdir->NONE;
135	sortby |= rev|compact;
136
137	if(argv == nil) {
138		argv = list of {"."};
139		popt++;
140	}
141
142	errs := 0;
143	for(; argv != nil; argv = tl argv)
144		errs |= !ls(hd argv);
145	delaywrite();
146	out.flush();
147	if(errs != 0)
148		raise "fail:errors";
149}
150
151ls(file: string): int
152{
153	dir: Dir;
154 	ok: int;
155
156	(ok, dir) = sys->stat(file);
157	if(ok == -1) {
158		sys->fprint(stderr, "ls: %s: %r\n", file);
159		return 0;
160	}
161	if(dopt || (dir.mode & Sys->DMDIR) == 0) {
162		# delay write: save it in the queue to sort by sortby
163		if(delayindex == 0)
164			delaydir = array[30] of ref Dir;
165		else if(len delaydir == delayindex) {
166			tmp := array[2 * delayindex] of ref Dir;
167			tmp[0:] = delaydir;
168			delaydir = tmp;
169		}
170		(dirname, filename) := str->splitstrr(file, "/");
171		if(dirname != "") {
172			dir.name = dirname + filename;
173			dir.dev |= PREFIX;
174		}
175		delaydir[delayindex++] = ref dir;
176		return 1;
177	}
178
179	delaywrite();
180
181	(d, n) := readdir->init(file, sortby);
182	if(n < 0){
183		sys->fprint(stderr, "ls: %s: %r\n", file);
184		return 0;
185	}
186	lsprint(file, d[0:n]);
187	return 1;
188}
189
190delaywrite()
191{
192	if(delayindex == 0)
193		return;
194
195	(b, n) := readdir->sortdir(delaydir[0:delayindex], sortby);
196
197	lsprint("", b[0:n]);
198
199	delayindex = 0;
200	delaydir = nil;
201}
202
203Widths: adt {
204	vers, dev, uid, gid, muid, length, size: int;
205};
206
207dowidths(dir: array of ref Dir): ref Widths
208{
209	w := Widths(0, 0, 0, 0, 0, 0, 0);
210	for (i := 0; i < len dir; i++) {
211		n: int;
212		d := dir[i];
213		if(sopt)
214			if((n = len string ((d.length+big 1023)/big 1024)) > w.size)
215				w.size = n;
216		if(mopt)
217			if((n = len d.muid+2) > w.muid)
218				w.muid = n;
219		if(qopt)
220			if((n = len string d.qid.vers) > w.vers)
221				w.vers = n;
222		if(lopt) {
223			if((n = len string (d.dev & ~PREFIX)) > w.dev)
224				w.dev = n;
225			if((n = len d.uid) > w.uid)
226				w.uid = n;
227			if((n = len d.gid) > w.gid)
228				w.gid = n;
229			if((n = len string d.length) > w.length)
230				w.length = n;
231		}
232	}
233	return ref w;
234}
235
236
237lsprint(dirname: string, dir: array of ref Dir)
238{
239	w := dowidths(dir);
240
241	for (i := 0; i < len dir; i++)
242		lslineprint(dirname, dir[i].name, dir[i], w);
243}
244
245lslineprint(dirname, name: string, dir: ref Dir, w: ref Widths)
246{
247	if(sopt)
248		out.puts(sys->sprint("%*bd ", w.size, (dir.length+big 1023)/big 1024));
249	if(mopt){
250		out.puts(sys->sprint("[%s] ", dir.muid));
251		for(i := len dir.muid+2; i < w.muid; i++)
252			out.putc(' ');
253	}
254	if(qopt)
255		out.puts(sys->sprint("(%.16bux %*ud %.2ux) ", dir.qid.path, w.vers, dir.qid.vers, dir.qid.qtype));
256	if(Topt){
257		if(dir.mode & Sys->DMTMP)
258			out.puts("t ");
259		else
260			out.puts("- ");
261	}
262
263	file := name;
264	pf := dir.dev & PREFIX;
265	dir.dev &= ~PREFIX;
266	if(popt) {
267		if(pf)
268			(nil, file) = str->splitstrr(dir.name, "/");
269		else
270			file = dir.name;
271	} else if(dirname != "") {
272		if(dirname[len dirname-1] == '/')
273			file = dirname + file;
274		else
275			file = dirname + "/" + file;
276	}
277	if(Fopt)
278		file += fileflag(dir);
279
280
281	if(lopt) {
282		time := dir.mtime;
283		if(uopt)
284			time = dir.atime;
285		if(eopt)
286			out.puts(sys->sprint("%s %c %*d %*s %*s %*bud %d %s\n",
287				modes(dir.mode), dir.dtype, w.dev, dir.dev,
288				-w.uid, dir.uid, -w.gid, dir.gid, w.length, dir.length,
289				time, file));
290		else
291			out.puts(sys->sprint("%s %c %*d %*s %*s %*bud %s %s\n",
292				modes(dir.mode), dir.dtype, w.dev, dir.dev,
293				-w.uid, dir.uid, -w.gid, dir.gid, w.length, dir.length,
294				daytime->filet(now, time), file));
295	} else
296		out.puts(file+"\n");
297}
298
299fileflag(dir: ref Dir): string
300{
301	if(dir.qid.qtype & Sys->QTDIR)
302		return "/";
303	if(dir.mode & 8r111)
304		return "*";
305	return "";
306}
307
308mtab := array[] of {
309	"---",	"--x",	"-w-",	"-wx",
310	"r--",	"r-x",	"rw-",	"rwx"
311};
312
313modes(mode: int): string
314{
315	s: string;
316
317	if(mode & Sys->DMDIR)
318		s = "d";
319	else if(mode & Sys->DMAPPEND)
320		s = "a";
321	else if(mode & Sys->DMAUTH)
322		s = "A";
323	else
324		s = "-";
325	if(mode & Sys->DMEXCL)
326		s += "l";
327	else
328		s += "-";
329	s += mtab[(mode>>6)&7]+mtab[(mode>>3)&7]+mtab[mode&7];
330	return s;
331}
332
333