VirtualBox

source: kBuild/trunk/src/kash/bltin/test.c@ 1205

Last change on this file since 1205 was 843, checked in by bird, 18 years ago

HAVE_SYS_CDEFS_H

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1/* $NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $ */
2
3/*
4 * test(1); version 7-like -- author Erik Baalbergen
5 * modified by Eric Gisin to be used as built-in.
6 * modified by Arnold Robbins to add SVR3 compatibility
7 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 * modified by J.T. Conklin for NetBSD.
9 *
10 * This program is in the Public Domain.
11 */
12
13#ifdef HAVE_SYS_CDEFS_H
14#include <sys/cdefs.h>
15#endif
16#ifndef lint
17__RCSID("$NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $");
18#endif
19
20#include <sys/stat.h>
21#include <sys/types.h>
22
23#include <ctype.h>
24#ifndef __sun__
25#include <err.h>
26#endif
27#include <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32#include <stdarg.h>
33
34/* test(1) accepts the following grammar:
35 oexpr ::= aexpr | aexpr "-o" oexpr ;
36 aexpr ::= nexpr | nexpr "-a" aexpr ;
37 nexpr ::= primary | "!" primary
38 primary ::= unary-operator operand
39 | operand binary-operator operand
40 | operand
41 | "(" oexpr ")"
42 ;
43 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
44 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
45
46 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
47 "-nt"|"-ot"|"-ef";
48 operand ::= <any legal UNIX file name>
49*/
50
51enum token {
52 EOI,
53 FILRD,
54 FILWR,
55 FILEX,
56 FILEXIST,
57 FILREG,
58 FILDIR,
59 FILCDEV,
60 FILBDEV,
61 FILFIFO,
62 FILSOCK,
63 FILSYM,
64 FILGZ,
65 FILTT,
66 FILSUID,
67 FILSGID,
68 FILSTCK,
69 FILNT,
70 FILOT,
71 FILEQ,
72 FILUID,
73 FILGID,
74 STREZ,
75 STRNZ,
76 STREQ,
77 STRNE,
78 STRLT,
79 STRGT,
80 INTEQ,
81 INTNE,
82 INTGE,
83 INTGT,
84 INTLE,
85 INTLT,
86 UNOT,
87 BAND,
88 BOR,
89 LPAREN,
90 RPAREN,
91 OPERAND
92};
93
94enum token_types {
95 UNOP,
96 BINOP,
97 BUNOP,
98 BBINOP,
99 PAREN
100};
101
102static struct t_op {
103 const char *op_text;
104 short op_num, op_type;
105} const ops [] = {
106 {"-r", FILRD, UNOP},
107 {"-w", FILWR, UNOP},
108 {"-x", FILEX, UNOP},
109 {"-e", FILEXIST,UNOP},
110 {"-f", FILREG, UNOP},
111 {"-d", FILDIR, UNOP},
112 {"-c", FILCDEV,UNOP},
113 {"-b", FILBDEV,UNOP},
114 {"-p", FILFIFO,UNOP},
115 {"-u", FILSUID,UNOP},
116 {"-g", FILSGID,UNOP},
117 {"-k", FILSTCK,UNOP},
118 {"-s", FILGZ, UNOP},
119 {"-t", FILTT, UNOP},
120 {"-z", STREZ, UNOP},
121 {"-n", STRNZ, UNOP},
122 {"-h", FILSYM, UNOP}, /* for backwards compat */
123 {"-O", FILUID, UNOP},
124 {"-G", FILGID, UNOP},
125 {"-L", FILSYM, UNOP},
126 {"-S", FILSOCK,UNOP},
127 {"=", STREQ, BINOP},
128 {"!=", STRNE, BINOP},
129 {"<", STRLT, BINOP},
130 {">", STRGT, BINOP},
131 {"-eq", INTEQ, BINOP},
132 {"-ne", INTNE, BINOP},
133 {"-ge", INTGE, BINOP},
134 {"-gt", INTGT, BINOP},
135 {"-le", INTLE, BINOP},
136 {"-lt", INTLT, BINOP},
137 {"-nt", FILNT, BINOP},
138 {"-ot", FILOT, BINOP},
139 {"-ef", FILEQ, BINOP},
140 {"!", UNOT, BUNOP},
141 {"-a", BAND, BBINOP},
142 {"-o", BOR, BBINOP},
143 {"(", LPAREN, PAREN},
144 {")", RPAREN, PAREN},
145 {0, 0, 0}
146};
147
148static char **t_wp;
149static struct t_op const *t_wp_op;
150
151static void syntax(const char *, const char *);
152static int oexpr(enum token);
153static int aexpr(enum token);
154static int nexpr(enum token);
155static int primary(enum token);
156static int binop(void);
157static int filstat(char *, enum token);
158static enum token t_lex(char *);
159static int isoperand(void);
160static int getn(const char *);
161static int newerf(const char *, const char *);
162static int olderf(const char *, const char *);
163static int equalf(const char *, const char *);
164
165#if defined(SHELL)
166extern void error(const char *, ...) __attribute__((__noreturn__));
167#else
168static void error(const char *, ...) __attribute__((__noreturn__));
169
170static void
171error(const char *msg, ...)
172{
173 va_list ap;
174
175 va_start(ap, msg);
176 verrx(2, msg, ap);
177 /*NOTREACHED*/
178 va_end(ap);
179}
180#endif
181
182#ifdef SHELL
183int testcmd(int, char **);
184
185int
186testcmd(int argc, char **argv)
187#else
188int main(int, char *[]);
189
190int
191main(int argc, char *argv[])
192#endif
193{
194 int res;
195
196#ifdef HAVE_SETPROGNAME
197 setprogname(argv[0]);
198#endif
199 if (strcmp(argv[0], "[") == 0) {
200 if (strcmp(argv[--argc], "]"))
201 error("missing ]");
202 argv[argc] = NULL;
203 }
204
205 if (argc < 2)
206 return 1;
207
208 t_wp = &argv[1];
209 res = !oexpr(t_lex(*t_wp));
210
211 if (*t_wp != NULL && *++t_wp != NULL)
212 syntax(*t_wp, "unexpected operator");
213
214 return res;
215}
216
217static void
218syntax(const char *op, const char *msg)
219{
220
221 if (op && *op)
222 error("%s: %s", op, msg);
223 else
224 error("%s", msg);
225}
226
227static int
228oexpr(enum token n)
229{
230 int res;
231
232 res = aexpr(n);
233 if (t_lex(*++t_wp) == BOR)
234 return oexpr(t_lex(*++t_wp)) || res;
235 t_wp--;
236 return res;
237}
238
239static int
240aexpr(enum token n)
241{
242 int res;
243
244 res = nexpr(n);
245 if (t_lex(*++t_wp) == BAND)
246 return aexpr(t_lex(*++t_wp)) && res;
247 t_wp--;
248 return res;
249}
250
251static int
252nexpr(enum token n)
253{
254
255 if (n == UNOT)
256 return !nexpr(t_lex(*++t_wp));
257 return primary(n);
258}
259
260static int
261primary(enum token n)
262{
263 enum token nn;
264 int res;
265
266 if (n == EOI)
267 return 0; /* missing expression */
268 if (n == LPAREN) {
269 if ((nn = t_lex(*++t_wp)) == RPAREN)
270 return 0; /* missing expression */
271 res = oexpr(nn);
272 if (t_lex(*++t_wp) != RPAREN)
273 syntax(NULL, "closing paren expected");
274 return res;
275 }
276 if (t_wp_op && t_wp_op->op_type == UNOP) {
277 /* unary expression */
278 if (*++t_wp == NULL)
279 syntax(t_wp_op->op_text, "argument expected");
280 switch (n) {
281 case STREZ:
282 return strlen(*t_wp) == 0;
283 case STRNZ:
284 return strlen(*t_wp) != 0;
285 case FILTT:
286 return isatty(getn(*t_wp));
287 default:
288 return filstat(*t_wp, n);
289 }
290 }
291
292 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
293 return binop();
294 }
295
296 return strlen(*t_wp) > 0;
297}
298
299static int
300binop(void)
301{
302 const char *opnd1, *opnd2;
303 struct t_op const *op;
304
305 opnd1 = *t_wp;
306 (void) t_lex(*++t_wp);
307 op = t_wp_op;
308
309 if ((opnd2 = *++t_wp) == NULL)
310 syntax(op->op_text, "argument expected");
311
312 switch (op->op_num) {
313 case STREQ:
314 return strcmp(opnd1, opnd2) == 0;
315 case STRNE:
316 return strcmp(opnd1, opnd2) != 0;
317 case STRLT:
318 return strcmp(opnd1, opnd2) < 0;
319 case STRGT:
320 return strcmp(opnd1, opnd2) > 0;
321 case INTEQ:
322 return getn(opnd1) == getn(opnd2);
323 case INTNE:
324 return getn(opnd1) != getn(opnd2);
325 case INTGE:
326 return getn(opnd1) >= getn(opnd2);
327 case INTGT:
328 return getn(opnd1) > getn(opnd2);
329 case INTLE:
330 return getn(opnd1) <= getn(opnd2);
331 case INTLT:
332 return getn(opnd1) < getn(opnd2);
333 case FILNT:
334 return newerf(opnd1, opnd2);
335 case FILOT:
336 return olderf(opnd1, opnd2);
337 case FILEQ:
338 return equalf(opnd1, opnd2);
339 default:
340 abort();
341 /* NOTREACHED */
342 }
343}
344
345static int
346filstat(char *nm, enum token mode)
347{
348 struct stat s;
349
350 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
351 return 0;
352
353 switch (mode) {
354 case FILRD:
355 return access(nm, R_OK) == 0;
356 case FILWR:
357 return access(nm, W_OK) == 0;
358 case FILEX:
359 return access(nm, X_OK) == 0;
360 case FILEXIST:
361 return access(nm, F_OK) == 0;
362 case FILREG:
363 return S_ISREG(s.st_mode);
364 case FILDIR:
365 return S_ISDIR(s.st_mode);
366 case FILCDEV:
367#ifdef S_ISCHR
368 return S_ISCHR(s.st_mode);
369#else
370 return 0;
371#endif
372 case FILBDEV:
373#ifdef S_ISBLK
374 return S_ISBLK(s.st_mode);
375#else
376 return 0;
377#endif
378 case FILFIFO:
379#ifdef S_ISFIFO
380 return S_ISFIFO(s.st_mode);
381#else
382 return 0;
383#endif
384 case FILSOCK:
385#ifdef S_ISSOCK
386 return S_ISSOCK(s.st_mode);
387#else
388 return 0;
389#endif
390 case FILSYM:
391 return S_ISLNK(s.st_mode);
392 case FILSUID:
393 return (s.st_mode & S_ISUID) != 0;
394 case FILSGID:
395 return (s.st_mode & S_ISGID) != 0;
396 case FILSTCK:
397#ifdef S_ISVTX
398 return (s.st_mode & S_ISVTX) != 0;
399#else
400 return 0;
401#endif
402 case FILGZ:
403 return s.st_size > (off_t)0;
404 case FILUID:
405 return s.st_uid == geteuid();
406 case FILGID:
407 return s.st_gid == getegid();
408 default:
409 return 1;
410 }
411}
412
413static enum token
414t_lex(char *s)
415{
416 struct t_op const *op;
417
418 op = ops;
419
420 if (s == 0) {
421 t_wp_op = NULL;
422 return EOI;
423 }
424 while (op->op_text) {
425 if (strcmp(s, op->op_text) == 0) {
426 if ((op->op_type == UNOP && isoperand()) ||
427 (op->op_num == LPAREN && *(t_wp+1) == 0))
428 break;
429 t_wp_op = op;
430 return op->op_num;
431 }
432 op++;
433 }
434 t_wp_op = NULL;
435 return OPERAND;
436}
437
438static int
439isoperand(void)
440{
441 struct t_op const *op;
442 char *s, *t;
443
444 op = ops;
445 if ((s = *(t_wp+1)) == 0)
446 return 1;
447 if ((t = *(t_wp+2)) == 0)
448 return 0;
449 while (op->op_text) {
450 if (strcmp(s, op->op_text) == 0)
451 return op->op_type == BINOP &&
452 (t[0] != ')' || t[1] != '\0');
453 op++;
454 }
455 return 0;
456}
457
458/* atoi with error detection */
459static int
460getn(const char *s)
461{
462 char *p;
463 long r;
464
465 errno = 0;
466 r = strtol(s, &p, 10);
467
468 if (errno != 0)
469 error("%s: out of range", s);
470
471 while (isspace((unsigned char)*p))
472 p++;
473
474 if (*p)
475 error("%s: bad number", s);
476
477 return (int) r;
478}
479
480static int
481newerf(const char *f1, const char *f2)
482{
483 struct stat b1, b2;
484
485 return (stat(f1, &b1) == 0 &&
486 stat(f2, &b2) == 0 &&
487 b1.st_mtime > b2.st_mtime);
488}
489
490static int
491olderf(const char *f1, const char *f2)
492{
493 struct stat b1, b2;
494
495 return (stat(f1, &b1) == 0 &&
496 stat(f2, &b2) == 0 &&
497 b1.st_mtime < b2.st_mtime);
498}
499
500static int
501equalf(const char *f1, const char *f2)
502{
503 struct stat b1, b2;
504
505 return (stat(f1, &b1) == 0 &&
506 stat(f2, &b2) == 0 &&
507 b1.st_dev == b2.st_dev &&
508 b1.st_ino == b2.st_ino);
509}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette