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