]> git.cameronkatri.com Git - apple_cmds.git/blob - shell_cmds/test/test.c
system_cmds: Fix compilation for lower targets, downgrade lsmp
[apple_cmds.git] / shell_cmds / test / test.c
1 /* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink 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 * Important: This file is used both as a standalone program /bin/test and
14 * as a builtin for /bin/sh (#define SHELL).
15 */
16
17 #include <sys/cdefs.h>
18 __FBSDID("$FreeBSD$");
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22
23 #include <ctype.h>
24 #include <err.h>
25 #include <errno.h>
26 #ifdef __APPLE__
27 #include <fcntl.h>
28 #endif /* __APPLE__ */
29 #include <inttypes.h>
30 #include <limits.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #ifdef __APPLE__
38 #define eaccess(path, mode) faccessat(AT_FDCWD, path, mode, AT_EACCESS)
39 #define st_mtim st_mtimespec
40 #endif /* __APPLE__ */
41
42 #ifdef SHELL
43 #define main testcmd
44 #include "bltin/bltin.h"
45 #else
46 #include <locale.h>
47
48 static void error(const char *, ...) __dead2 __printf0like(1, 2);
49
50 static void
51 error(const char *msg, ...)
52 {
53 va_list ap;
54 va_start(ap, msg);
55 verrx(2, msg, ap);
56 /*NOTREACHED*/
57 va_end(ap);
58 }
59 #endif
60
61 /* test(1) accepts the following grammar:
62 oexpr ::= aexpr | aexpr "-o" oexpr ;
63 aexpr ::= nexpr | nexpr "-a" aexpr ;
64 nexpr ::= primary | "!" primary
65 primary ::= unary-operator operand
66 | operand binary-operator operand
67 | operand
68 | "(" oexpr ")"
69 ;
70 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
71 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
72
73 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
74 "-nt"|"-ot"|"-ef";
75 operand ::= <any legal UNIX file name>
76 */
77
78 enum token_types {
79 UNOP = 0x100,
80 BINOP = 0x200,
81 BUNOP = 0x300,
82 BBINOP = 0x400,
83 PAREN = 0x500
84 };
85
86 enum token {
87 EOI,
88 OPERAND,
89 FILRD = UNOP + 1,
90 FILWR,
91 FILEX,
92 FILEXIST,
93 FILREG,
94 FILDIR,
95 FILCDEV,
96 FILBDEV,
97 FILFIFO,
98 FILSOCK,
99 FILSYM,
100 FILGZ,
101 FILTT,
102 FILSUID,
103 FILSGID,
104 FILSTCK,
105 STREZ,
106 STRNZ,
107 FILUID,
108 FILGID,
109 FILNT = BINOP + 1,
110 FILOT,
111 FILEQ,
112 STREQ,
113 STRNE,
114 STRLT,
115 STRGT,
116 INTEQ,
117 INTNE,
118 INTGE,
119 INTGT,
120 INTLE,
121 INTLT,
122 UNOT = BUNOP + 1,
123 BAND = BBINOP + 1,
124 BOR,
125 LPAREN = PAREN + 1,
126 RPAREN
127 };
128
129 #define TOKEN_TYPE(token) ((token) & 0xff00)
130
131 static struct t_op {
132 char op_text[4];
133 short op_num;
134 } const ops [] = {
135 {"-r", FILRD},
136 {"-w", FILWR},
137 {"-x", FILEX},
138 {"-e", FILEXIST},
139 {"-f", FILREG},
140 {"-d", FILDIR},
141 {"-c", FILCDEV},
142 {"-b", FILBDEV},
143 {"-p", FILFIFO},
144 {"-u", FILSUID},
145 {"-g", FILSGID},
146 {"-k", FILSTCK},
147 {"-s", FILGZ},
148 {"-t", FILTT},
149 {"-z", STREZ},
150 {"-n", STRNZ},
151 {"-h", FILSYM}, /* for backwards compat */
152 {"-O", FILUID},
153 {"-G", FILGID},
154 {"-L", FILSYM},
155 {"-S", FILSOCK},
156 {"=", STREQ},
157 {"==", STREQ},
158 {"!=", STRNE},
159 {"<", STRLT},
160 {">", STRGT},
161 {"-eq", INTEQ},
162 {"-ne", INTNE},
163 {"-ge", INTGE},
164 {"-gt", INTGT},
165 {"-le", INTLE},
166 {"-lt", INTLT},
167 {"-nt", FILNT},
168 {"-ot", FILOT},
169 {"-ef", FILEQ},
170 {"!", UNOT},
171 {"-a", BAND},
172 {"-o", BOR},
173 {"(", LPAREN},
174 {")", RPAREN},
175 {"", 0}
176 };
177
178 static int nargc;
179 static char **t_wp;
180 static int parenlevel;
181
182 static int aexpr(enum token);
183 static int binop(enum token);
184 static int equalf(const char *, const char *);
185 static int filstat(char *, enum token);
186 static int getn(const char *);
187 static intmax_t getq(const char *);
188 static int intcmp(const char *, const char *);
189 static int isunopoperand(void);
190 static int islparenoperand(void);
191 static int isrparenoperand(void);
192 static int newerf(const char *, const char *);
193 static int nexpr(enum token);
194 static int oexpr(enum token);
195 static int olderf(const char *, const char *);
196 static int primary(enum token);
197 static void syntax(const char *, const char *);
198 static enum token t_lex(char *);
199
200 int
201 main(int argc, char **argv)
202 {
203 int res;
204 char *p;
205
206 /* radar:4689479 */
207 if (argc == 0)
208 return 1;
209
210 if ((p = strrchr(argv[0], '/')) == NULL)
211 p = argv[0];
212 else
213 p++;
214 if (strcmp(p, "[") == 0) {
215 if (strcmp(argv[--argc], "]") != 0)
216 error("missing ]");
217 argv[argc] = NULL;
218 }
219
220 /* no expression => false */
221 if (--argc <= 0)
222 return 1;
223
224 #ifndef SHELL
225 (void)setlocale(LC_CTYPE, "");
226 #endif
227 nargc = argc;
228 t_wp = &argv[1];
229 parenlevel = 0;
230 if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
231 /* Things like ! "" -o x do not fit in the normal grammar. */
232 --nargc;
233 ++t_wp;
234 res = oexpr(t_lex(*t_wp));
235 } else
236 res = !oexpr(t_lex(*t_wp));
237
238 if (--nargc > 0)
239 syntax(*t_wp, "unexpected operator");
240
241 return res;
242 }
243
244 static void
245 syntax(const char *op, const char *msg)
246 {
247
248 if (op && *op)
249 error("%s: %s", op, msg);
250 else
251 error("%s", msg);
252 }
253
254 static int
255 oexpr(enum token n)
256 {
257 int res;
258
259 res = aexpr(n);
260 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
261 return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
262 res;
263 t_wp--;
264 nargc++;
265 return res;
266 }
267
268 static int
269 aexpr(enum token n)
270 {
271 int res;
272
273 res = nexpr(n);
274 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
275 return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
276 res;
277 t_wp--;
278 nargc++;
279 return res;
280 }
281
282 static int
283 nexpr(enum token n)
284 {
285 if (n == UNOT)
286 return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
287 return primary(n);
288 }
289
290 static int
291 primary(enum token n)
292 {
293 enum token nn;
294 int res;
295
296 if (n == EOI)
297 return 0; /* missing expression */
298 if (n == LPAREN) {
299 parenlevel++;
300 if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
301 RPAREN) {
302 parenlevel--;
303 return 0; /* missing expression */
304 }
305 res = oexpr(nn);
306 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
307 syntax(NULL, "closing paren expected");
308 parenlevel--;
309 return res;
310 }
311 if (TOKEN_TYPE(n) == UNOP) {
312 /* unary expression */
313 if (--nargc == 0)
314 syntax(NULL, "argument expected"); /* impossible */
315 switch (n) {
316 case STREZ:
317 return strlen(*++t_wp) == 0;
318 case STRNZ:
319 return strlen(*++t_wp) != 0;
320 case FILTT:
321 return isatty(getn(*++t_wp));
322 default:
323 return filstat(*++t_wp, n);
324 }
325 }
326
327 nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
328 if (TOKEN_TYPE(nn) == BINOP)
329 return binop(nn);
330
331 return strlen(*t_wp) > 0;
332 }
333
334 static int
335 binop(enum token n)
336 {
337 const char *opnd1, *op, *opnd2;
338
339 opnd1 = *t_wp;
340 op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
341
342 if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
343 syntax(op, "argument expected");
344
345 switch (n) {
346 case STREQ:
347 return strcmp(opnd1, opnd2) == 0;
348 case STRNE:
349 return strcmp(opnd1, opnd2) != 0;
350 case STRLT:
351 return strcmp(opnd1, opnd2) < 0;
352 case STRGT:
353 return strcmp(opnd1, opnd2) > 0;
354 case INTEQ:
355 return intcmp(opnd1, opnd2) == 0;
356 case INTNE:
357 return intcmp(opnd1, opnd2) != 0;
358 case INTGE:
359 return intcmp(opnd1, opnd2) >= 0;
360 case INTGT:
361 return intcmp(opnd1, opnd2) > 0;
362 case INTLE:
363 return intcmp(opnd1, opnd2) <= 0;
364 case INTLT:
365 return intcmp(opnd1, opnd2) < 0;
366 case FILNT:
367 return newerf (opnd1, opnd2);
368 case FILOT:
369 return olderf (opnd1, opnd2);
370 case FILEQ:
371 return equalf (opnd1, opnd2);
372 default:
373 abort();
374 /* NOTREACHED */
375 }
376 }
377
378 static int
379 filstat(char *nm, enum token mode)
380 {
381 struct stat s;
382
383 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
384 return 0;
385
386 switch (mode) {
387 case FILRD:
388 return (eaccess(nm, R_OK) == 0);
389 case FILWR:
390 return (eaccess(nm, W_OK) == 0);
391 case FILEX:
392 /* XXX work around eaccess(2) false positives for superuser */
393 if (eaccess(nm, X_OK) != 0)
394 return 0;
395 if (S_ISDIR(s.st_mode) || geteuid() != 0)
396 return 1;
397 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0;
398 case FILEXIST:
399 return (eaccess(nm, F_OK) == 0);
400 case FILREG:
401 return S_ISREG(s.st_mode);
402 case FILDIR:
403 return S_ISDIR(s.st_mode);
404 case FILCDEV:
405 return S_ISCHR(s.st_mode);
406 case FILBDEV:
407 return S_ISBLK(s.st_mode);
408 case FILFIFO:
409 return S_ISFIFO(s.st_mode);
410 case FILSOCK:
411 return S_ISSOCK(s.st_mode);
412 case FILSYM:
413 return S_ISLNK(s.st_mode);
414 case FILSUID:
415 return (s.st_mode & S_ISUID) != 0;
416 case FILSGID:
417 return (s.st_mode & S_ISGID) != 0;
418 case FILSTCK:
419 return (s.st_mode & S_ISVTX) != 0;
420 case FILGZ:
421 return s.st_size > (off_t)0;
422 case FILUID:
423 return s.st_uid == geteuid();
424 case FILGID:
425 return s.st_gid == getegid();
426 default:
427 return 1;
428 }
429 }
430
431 static enum token
432 t_lex(char *s)
433 {
434 struct t_op const *op = ops;
435
436 if (s == 0) {
437 return EOI;
438 }
439 while (*op->op_text) {
440 if (strcmp(s, op->op_text) == 0) {
441 if (((TOKEN_TYPE(op->op_num) == UNOP ||
442 TOKEN_TYPE(op->op_num) == BUNOP)
443 && isunopoperand()) ||
444 (op->op_num == LPAREN && islparenoperand()) ||
445 (op->op_num == RPAREN && isrparenoperand()))
446 break;
447 return op->op_num;
448 }
449 op++;
450 }
451 return OPERAND;
452 }
453
454 static int
455 isunopoperand(void)
456 {
457 struct t_op const *op = ops;
458 char *s;
459 char *t;
460
461 if (nargc == 1)
462 return 1;
463 s = *(t_wp + 1);
464 if (nargc == 2)
465 return parenlevel == 1 && strcmp(s, ")") == 0;
466 t = *(t_wp + 2);
467 while (*op->op_text) {
468 if (strcmp(s, op->op_text) == 0)
469 return TOKEN_TYPE(op->op_num) == BINOP &&
470 (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
471 op++;
472 }
473 return 0;
474 }
475
476 static int
477 islparenoperand(void)
478 {
479 struct t_op const *op = ops;
480 char *s;
481
482 if (nargc == 1)
483 return 1;
484 s = *(t_wp + 1);
485 if (nargc == 2)
486 return parenlevel == 1 && strcmp(s, ")") == 0;
487 if (nargc != 3)
488 return 0;
489 while (*op->op_text) {
490 if (strcmp(s, op->op_text) == 0)
491 return TOKEN_TYPE(op->op_num) == BINOP;
492 op++;
493 }
494 return 0;
495 }
496
497 static int
498 isrparenoperand(void)
499 {
500 char *s;
501
502 if (nargc == 1)
503 return 0;
504 s = *(t_wp + 1);
505 if (nargc == 2)
506 return parenlevel == 1 && strcmp(s, ")") == 0;
507 return 0;
508 }
509
510 /* atoi with error detection */
511 static int
512 getn(const char *s)
513 {
514 char *p;
515 long r;
516
517 errno = 0;
518 r = strtol(s, &p, 10);
519
520 if (s == p)
521 error("%s: bad number", s);
522
523 if (errno != 0)
524 error((errno == EINVAL) ? "%s: bad number" :
525 "%s: out of range", s);
526
527 while (isspace((unsigned char)*p))
528 p++;
529
530 if (*p)
531 error("%s: bad number", s);
532
533 return (int) r;
534 }
535
536 /* atoi with error detection and 64 bit range */
537 static intmax_t
538 getq(const char *s)
539 {
540 char *p;
541 intmax_t r;
542
543 errno = 0;
544 r = strtoimax(s, &p, 10);
545
546 if (s == p)
547 error("%s: bad number", s);
548
549 if (errno != 0)
550 error((errno == EINVAL) ? "%s: bad number" :
551 "%s: out of range", s);
552
553 while (isspace((unsigned char)*p))
554 p++;
555
556 if (*p)
557 error("%s: bad number", s);
558
559 return r;
560 }
561
562 static int
563 intcmp (const char *s1, const char *s2)
564 {
565 intmax_t q1, q2;
566
567
568 q1 = getq(s1);
569 q2 = getq(s2);
570
571 if (q1 > q2)
572 return 1;
573
574 if (q1 < q2)
575 return -1;
576
577 return 0;
578 }
579
580 static int
581 newerf (const char *f1, const char *f2)
582 {
583 struct stat b1, b2;
584
585 if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
586 return 0;
587
588 if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
589 return 1;
590 if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
591 return 0;
592
593 return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
594 }
595
596 static int
597 olderf (const char *f1, const char *f2)
598 {
599 return (newerf(f2, f1));
600 }
601
602 static int
603 equalf (const char *f1, const char *f2)
604 {
605 struct stat b1, b2;
606
607 return (stat (f1, &b1) == 0 &&
608 stat (f2, &b2) == 0 &&
609 b1.st_dev == b2.st_dev &&
610 b1.st_ino == b2.st_ino);
611 }