xref: /inferno-os/appl/cmd/test.b (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
1implement Test;
2
3#
4#	venerable
5#		test expression
6#
7
8include "sys.m";
9	sys: Sys;
10	stderr: ref Sys->FD;
11
12include "draw.m";
13
14include "daytime.m";
15	daytime: Daytime;
16
17Test: module
18{
19	init:	fn(ctxt: ref Draw->Context, argv: list of string);
20};
21
22gargs: list of string;
23
24init(nil: ref Draw->Context, args: list of string)
25{
26	if(args == nil)
27		return;
28	gargs = tl args;
29
30	sys = load Sys Sys->PATH;
31	stderr = sys->fildes(2);
32
33	if(gargs == nil)
34		raise "fail:usage";
35	if(!e())
36		raise "fail:false";
37}
38
39nextarg(mt: int): string
40{
41	if(gargs == nil){
42		if(mt)
43			return nil;
44		synbad("argument expected");
45	}
46	s := hd gargs;
47	gargs = tl gargs;
48	return s;
49}
50
51nextintarg(): (int, int)
52{
53	if(gargs != nil && isint(hd gargs))
54		return (1, int nextarg(0));
55	return (0, 0);
56}
57
58isnextarg(s: string): int
59{
60	if(gargs != nil && hd gargs == s){
61		gargs = tl gargs;
62		return 1;
63	}
64	return 0;
65}
66
67e(): int
68{
69	p1 := e1();
70	if(isnextarg("-o"))
71		return p1 || e();
72	return p1;
73}
74
75e1(): int
76{
77	p1 := e2();
78	if(isnextarg("-a"))
79		return p1 && e1();
80	return p1;
81}
82
83e2(): int
84{
85	if(isnextarg("!"))
86		return !e2();
87	return e3();
88}
89
90e3(): int
91{
92	a := nextarg(0);
93	case a {
94	"(" =>
95		p1 := e();
96		if(nextarg(0) != ")")
97			synbad(") expected");
98		return p1;
99	"-A" =>
100		return hasmode(nextarg(0), Sys->DMAPPEND);
101	"-L" =>
102		return hasmode(nextarg(0), Sys->DMEXCL);
103	"-T" =>
104		return hasmode(nextarg(0), Sys->DMTMP);
105	"-f" =>
106		f := nextarg(0);
107		return exists(f) && !hasmode(f, Sys->DMDIR);
108	"-d" =>
109		return hasmode(nextarg(0), Sys->DMDIR);
110	"-r" =>
111		return sys->open(nextarg(0), Sys->OREAD) != nil;
112	"-w" =>
113		return sys->open(nextarg(0), Sys->OWRITE) != nil;
114	"-x" =>
115		fd := sys->open(nextarg(0), Sys->OREAD);
116		if(fd == nil)
117			return 0;
118		(ok, d) := sys->fstat(fd);
119		if(ok < 0)
120			return 0;
121		return (d.mode & 8r111) != 0;
122	"-e" =>
123		return exists(nextarg(0));
124	"-s" =>
125		(ok, d) := sys->stat(nextarg(0));
126		if(ok < 0)
127			return 0;
128		return d.length > big 0;
129	"-t" =>
130		(ok, fd) := nextintarg();
131		if(!ok)
132			return iscons(1);
133		return iscons(fd);
134	"-n" =>
135		return nextarg(0) != "";
136	"-z" =>
137		return nextarg(0) == "";
138	* =>
139		p2 := nextarg(1);
140		if(p2 == nil)
141			return a != nil;
142		case p2 {
143		"=" =>
144			return nextarg(0) == a;
145		"!=" =>
146			return nextarg(0) != a;
147		"-older" =>
148			return isolder(nextarg(0), a);
149		"-ot" =>
150			return isolderthan(a, nextarg(0));
151		"-nt" =>
152			return isnewerthan(a, nextarg(0));
153		}
154
155		if(!isint(a))
156			return a != nil;
157
158		int1 := int a;
159		(ok, int2) := nextintarg();
160		if(ok){
161			case p2 {
162			"-eq" =>
163				return int1 == int2;
164			"-ne" =>
165				return int1 != int2;
166			"-gt" =>
167				return int1 > int2;
168			"-lt" =>
169				return int1 < int2;
170			"-ge" =>
171				return int1 >= int2;
172			"-le" =>
173				return int1 <= int2;
174			}
175		}
176
177		synbad("unknown operator " + p2);
178		return 0;
179	}
180}
181
182synbad(s: string)
183{
184	sys->fprint(stderr, "test: bad syntax: %s\n", s);
185	raise "fail:bad syntax";
186}
187
188isint(s: string): int
189{
190	if(s == nil)
191		return 0;
192	for(i := 0; i < len s; i++)
193		if(s[i] < '0' || s[i] > '9')
194			return 0;
195	return 1;
196}
197
198exists(f: string): int
199{
200	return sys->stat(f).t0 >= 0;
201}
202
203hasmode(f: string, m: int): int
204{
205	(ok, d) := sys->stat(f);
206	if(ok < 0)
207		return 0;
208	return (d.mode & m) != 0;
209}
210
211iscons(fno: int): int
212{
213	fd := sys->fildes(fno);
214	if(fd == nil)
215		return 0;
216	s := sys->fd2path(fd);
217	n := len "/dev/cons";
218	return s == "#c/cons" || len s >= n && s[len s-n:] == "/dev/cons";
219}
220
221isolder(t: string, f: string): int
222{
223	(ok, dir) := sys->stat(f);
224	if(ok < 0)
225		return 0;
226
227	n := 0;
228	for(i := 0; i < len t;){
229		for(j := i; j < len t; j++)
230			if(!(t[j] >= '0' && t[j] <= '9'))
231				break;
232		if(i == j)
233			synbad("bad time syntax, "+t);
234		m := int t[i:j];
235		i = j;
236		if(i == len t){
237			n = m;
238			break;
239		}
240		case t[i++] {
241		'y' =>	n += m*12*30*24*3600;
242		'M' =>	n += m*30*24*3600;
243		'd' =>	n += m*24*3600;
244		'h' =>	n += m*3600;
245		'm' =>	n += m*60;
246		's' =>		n += m;
247		* =>		synbad("bad time syntax, "+t);
248		}
249	}
250
251	return dir.mtime+n < now();
252}
253
254isolderthan(a: string, b: string): int
255{
256	(aok, ad) := sys->stat(a);
257	if(aok < 0)
258		return 0;
259	(bok, bd) := sys->stat(b);
260	if(bok < 0)
261		return 0;
262	return ad.mtime < bd.mtime;
263}
264
265isnewerthan(a: string, b: string): int
266{
267	(aok, ad) := sys->stat(a);
268	if(aok < 0)
269		return 0;
270	(bok, bd) := sys->stat(b);
271	if(bok < 0)
272		return 0;
273	return ad.mtime > bd.mtime;
274}
275
276now(): int
277{
278	if(daytime == nil){
279		daytime = load Daytime Daytime->PATH;
280		if(daytime == nil)
281			synbad(sys->sprint("can't load %s: %r", Daytime->PATH));
282	}
283	return daytime->now();
284}
285