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