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