1 /*
2  * POSIX standard
3  *	test expression
4  *	[ expression ]
5  *
6  * Plan 9 additions:
7  *	-A file exists and is append-only
8  *	-L file exists and is exclusive-use
9  *	-T file exists and is temporary
10  */
11 
12 #include <u.h>
13 #include <libc.h>
14 
15 #define isatty plan9_isatty
16 
17 #define EQ(a,b)	((tmp=a)==0?0:(strcmp(tmp,b)==0))
18 
19 int	ap;
20 int	ac;
21 char	**av;
22 char	*tmp;
23 
24 void	synbad(char *, char *);
25 int	fsizep(char *);
26 int	isdir(char *);
27 int	isreg(char *);
28 int	isatty(int);
29 int	isint(char *, int *);
30 int	isolder(char *, char *);
31 int	isolderthan(char *, char *);
32 int	isnewerthan(char *, char *);
33 int	hasmode(char *, ulong);
34 int	tio(char *, int);
35 int	e(void), e1(void), e2(void), e3(void);
36 char	*nxtarg(int);
37 
38 void
main(int argc,char * argv[])39 main(int argc, char *argv[])
40 {
41 	int r;
42 	char *c;
43 
44 	ac = argc; av = argv; ap = 1;
45 	if(EQ(argv[0],"[")) {
46 		if(!EQ(argv[--ac],"]"))
47 			synbad("] missing","");
48 	}
49 	argv[ac] = 0;
50 	if (ac<=1)
51 		exits("usage");
52 	r = e();
53 	/*
54 	 * nice idea but short-circuit -o and -a operators may have
55 	 * not consumed their right-hand sides.
56 	 */
57 	if(0 && (c = nxtarg(1)) != nil)
58 		synbad("unexpected operator/operand: ", c);
59 	exits(r?0:"false");
60 }
61 
62 char *
nxtarg(int mt)63 nxtarg(int mt)
64 {
65 	if(ap>=ac){
66 		if(mt){
67 			ap++;
68 			return(0);
69 		}
70 		synbad("argument expected","");
71 	}
72 	return(av[ap++]);
73 }
74 
75 int
nxtintarg(int * pans)76 nxtintarg(int *pans)
77 {
78 	if(ap<ac && isint(av[ap], pans)){
79 		ap++;
80 		return 1;
81 	}
82 	return 0;
83 }
84 
85 int
e(void)86 e(void)
87 {
88 	int p1;
89 
90 	p1 = e1();
91 	if (EQ(nxtarg(1), "-o"))
92 		return(p1 || e());
93 	ap--;
94 	return(p1);
95 }
96 
97 int
e1(void)98 e1(void)
99 {
100 	int p1;
101 
102 	p1 = e2();
103 	if (EQ(nxtarg(1), "-a"))
104 		return (p1 && e1());
105 	ap--;
106 	return(p1);
107 }
108 
109 int
e2(void)110 e2(void)
111 {
112 	if (EQ(nxtarg(0), "!"))
113 		return(!e2());
114 	ap--;
115 	return(e3());
116 }
117 
118 int
e3(void)119 e3(void)
120 {
121 	int p1, int1, int2;
122 	char *a, *p2;
123 
124 	a = nxtarg(0);
125 	if(EQ(a, "(")) {
126 		p1 = e();
127 		if(!EQ(nxtarg(0), ")"))
128 			synbad(") expected","");
129 		return(p1);
130 	}
131 
132 	if(EQ(a, "-A"))
133 		return(hasmode(nxtarg(0), DMAPPEND));
134 
135 	if(EQ(a, "-L"))
136 		return(hasmode(nxtarg(0), DMEXCL));
137 
138 	if(EQ(a, "-T"))
139 		return(hasmode(nxtarg(0), DMTMP));
140 
141 	if(EQ(a, "-f"))
142 		return(isreg(nxtarg(0)));
143 
144 	if(EQ(a, "-d"))
145 		return(isdir(nxtarg(0)));
146 
147 	if(EQ(a, "-r"))
148 		return(tio(nxtarg(0), 4));
149 
150 	if(EQ(a, "-w"))
151 		return(tio(nxtarg(0), 2));
152 
153 	if(EQ(a, "-x"))
154 		return(tio(nxtarg(0), 1));
155 
156 	if(EQ(a, "-e"))
157 		return(tio(nxtarg(0), 0));
158 
159 	if(EQ(a, "-c"))
160 		return(0);
161 
162 	if(EQ(a, "-b"))
163 		return(0);
164 
165 	if(EQ(a, "-u"))
166 		return(0);
167 
168 	if(EQ(a, "-g"))
169 		return(0);
170 
171 	if(EQ(a, "-s"))
172 		return(fsizep(nxtarg(0)));
173 
174 	if(EQ(a, "-t"))
175 		if(ap>=ac)
176 			return(isatty(1));
177 		else if(nxtintarg(&int1))
178 			return(isatty(int1));
179 		else
180 			synbad("not a valid file descriptor number ", "");
181 
182 	if(EQ(a, "-n"))
183 		return(!EQ(nxtarg(0), ""));
184 	if(EQ(a, "-z"))
185 		return(EQ(nxtarg(0), ""));
186 
187 	p2 = nxtarg(1);
188 	if (p2==0)
189 		return(!EQ(a,""));
190 	if(EQ(p2, "="))
191 		return(EQ(nxtarg(0), a));
192 
193 	if(EQ(p2, "!="))
194 		return(!EQ(nxtarg(0), a));
195 
196 	if(EQ(p2, "-older"))
197 		return(isolder(nxtarg(0), a));
198 
199 	if(EQ(p2, "-ot"))
200 		return(isolderthan(nxtarg(0), a));
201 
202 	if(EQ(p2, "-nt"))
203 		return(isnewerthan(nxtarg(0), a));
204 
205 	if(!isint(a, &int1))
206 		synbad("unexpected operator/operand: ", p2);
207 
208 	if(nxtintarg(&int2)){
209 		if(EQ(p2, "-eq"))
210 			return(int1==int2);
211 		if(EQ(p2, "-ne"))
212 			return(int1!=int2);
213 		if(EQ(p2, "-gt"))
214 			return(int1>int2);
215 		if(EQ(p2, "-lt"))
216 			return(int1<int2);
217 		if(EQ(p2, "-ge"))
218 			return(int1>=int2);
219 		if(EQ(p2, "-le"))
220 			return(int1<=int2);
221 	}
222 
223 	synbad("unknown operator ",p2);
224 	return 0;		/* to shut ken up */
225 }
226 
227 int
tio(char * a,int f)228 tio(char *a, int f)
229 {
230 	return access (a, f) >= 0;
231 }
232 
233 /* copy to local memory; clear names for safety */
234 int
localstat(char * f,Dir * dir)235 localstat(char *f, Dir *dir)
236 {
237 	Dir *d;
238 
239 	d = dirstat(f);
240 	if(d == nil)
241 		return(-1);
242 	*dir = *d;
243 	free(d);
244 	dir->name = 0;
245 	dir->uid = 0;
246 	dir->gid = 0;
247 	dir->muid = 0;
248 	return 0;
249 }
250 
251 /* copy to local memory; clear names for safety */
252 int
localfstat(int f,Dir * dir)253 localfstat(int f, Dir *dir)
254 {
255 	Dir *d;
256 
257 	d = dirfstat(f);
258 	if(d == nil)
259 		return(-1);
260 	*dir = *d;
261 	free(d);
262 	dir->name = 0;
263 	dir->uid = 0;
264 	dir->gid = 0;
265 	dir->muid = 0;
266 	return 0;
267 }
268 
269 int
hasmode(char * f,ulong m)270 hasmode(char *f, ulong m)
271 {
272 	Dir dir;
273 
274 	if(localstat(f,&dir)<0)
275 		return(0);
276 	return(dir.mode&m);
277 }
278 
279 int
isdir(char * f)280 isdir(char *f)
281 {
282 	Dir dir;
283 
284 	if(localstat(f,&dir)<0)
285 		return(0);
286 	return(dir.mode&DMDIR);
287 }
288 
289 int
isreg(char * f)290 isreg(char *f)
291 {
292 	Dir dir;
293 
294 	if(localstat(f,&dir)<0)
295 		return(0);
296 	return(!(dir.mode&DMDIR));
297 }
298 
299 int
isatty(int fd)300 isatty(int fd)
301 {
302 	Dir d1, d2;
303 
304 	if(localfstat(fd, &d1) < 0)
305 		return 0;
306 	if(localstat("/dev/cons", &d2) < 0)
307 		return 0;
308 	return d1.type==d2.type && d1.dev==d2.dev && d1.qid.path==d2.qid.path;
309 }
310 
311 int
fsizep(char * f)312 fsizep(char *f)
313 {
314 	Dir dir;
315 
316 	if(localstat(f,&dir)<0)
317 		return(0);
318 	return(dir.length>0);
319 }
320 
321 void
synbad(char * s1,char * s2)322 synbad(char *s1, char *s2)
323 {
324 	int len;
325 
326 	write(2, "test: ", 6);
327 	if ((len = strlen(s1)) != 0)
328 		write(2, s1, len);
329 	if ((len = strlen(s2)) != 0)
330 		write(2, s2, len);
331 	write(2, "\n", 1);
332 	exits("bad syntax");
333 }
334 
335 int
isint(char * s,int * pans)336 isint(char *s, int *pans)
337 {
338 	char *ep;
339 
340 	*pans = strtol(s, &ep, 0);
341 	return (*ep == 0);
342 }
343 
344 int
isolder(char * pin,char * f)345 isolder(char *pin, char *f)
346 {
347 	char *p = pin;
348 	ulong n, m;
349 	Dir dir;
350 
351 	if(localstat(f,&dir)<0)
352 		return(0);
353 
354 	/* parse time */
355 	n = 0;
356 	while(*p){
357 		m = strtoul(p, &p, 0);
358 		switch(*p){
359 		case 0:
360 			n = m;
361 			break;
362 		case 'y':
363 			m *= 12;
364 			/* fall through */
365 		case 'M':
366 			m *= 30;
367 			/* fall through */
368 		case 'd':
369 			m *= 24;
370 			/* fall through */
371 		case 'h':
372 			m *= 60;
373 			/* fall through */
374 		case 'm':
375 			m *= 60;
376 			/* fall through */
377 		case 's':
378 			n += m;
379 			p++;
380 			break;
381 		default:
382 			synbad("bad time syntax, ", pin);
383 		}
384 	}
385 
386 	return(dir.mtime+n < time(0));
387 }
388 
389 int
isolderthan(char * a,char * b)390 isolderthan(char *a, char *b)
391 {
392 	Dir ad, bd;
393 
394 	if(localstat(a, &ad)<0)
395 		return(0);
396 	if(localstat(b, &bd)<0)
397 		return(0);
398 	return ad.mtime > bd.mtime;
399 }
400 
401 int
isnewerthan(char * a,char * b)402 isnewerthan(char *a, char *b)
403 {
404 	Dir ad, bd;
405 
406 	if(localstat(a, &ad)<0)
407 		return(0);
408 	if(localstat(b, &bd)<0)
409 		return(0);
410 	return ad.mtime < bd.mtime;
411 }
412