]> git.saurik.com Git - apple/shell_cmds.git/blame - test/test.c
shell_cmds-207.11.1.tar.gz
[apple/shell_cmds.git] / test / test.c
CommitLineData
71aad674 1/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
44bd5ea7 2
71aad674 3/*-
44bd5ea7
A
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 */
71aad674
A
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 */
44bd5ea7
A
16
17#include <sys/cdefs.h>
71aad674 18__FBSDID("$FreeBSD$");
44bd5ea7
A
19
20#include <sys/types.h>
21#include <sys/stat.h>
71aad674 22
44bd5ea7 23#include <ctype.h>
71aad674 24#include <err.h>
44bd5ea7 25#include <errno.h>
71aad674
A
26#ifdef __APPLE__
27#include <fcntl.h>
28#endif /* __APPLE__ */
29#include <inttypes.h>
30#include <limits.h>
31#include <stdarg.h>
44bd5ea7
A
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
71aad674
A
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
48static void error(const char *, ...) __dead2 __printf0like(1, 2);
49
50static void
51error(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
44bd5ea7
A
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
71aad674
A
78enum token_types {
79 UNOP = 0x100,
80 BINOP = 0x200,
81 BUNOP = 0x300,
82 BBINOP = 0x400,
83 PAREN = 0x500
84};
85
44bd5ea7
A
86enum token {
87 EOI,
71aad674
A
88 OPERAND,
89 FILRD = UNOP + 1,
44bd5ea7
A
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,
44bd5ea7
A
105 STREZ,
106 STRNZ,
71aad674
A
107 FILUID,
108 FILGID,
109 FILNT = BINOP + 1,
110 FILOT,
111 FILEQ,
44bd5ea7
A
112 STREQ,
113 STRNE,
114 STRLT,
115 STRGT,
116 INTEQ,
117 INTNE,
118 INTGE,
119 INTGT,
120 INTLE,
121 INTLT,
71aad674
A
122 UNOT = BUNOP + 1,
123 BAND = BBINOP + 1,
44bd5ea7 124 BOR,
71aad674
A
125 LPAREN = PAREN + 1,
126 RPAREN
44bd5ea7
A
127};
128
71aad674 129#define TOKEN_TYPE(token) ((token) & 0xff00)
44bd5ea7 130
71aad674
A
131static struct t_op {
132 char op_text[4];
133 short op_num;
44bd5ea7 134} const ops [] = {
71aad674
A
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}
44bd5ea7
A
176};
177
71aad674
A
178static int nargc;
179static char **t_wp;
180static int parenlevel;
181
182static int aexpr(enum token);
183static int binop(enum token);
184static int equalf(const char *, const char *);
185static int filstat(char *, enum token);
186static int getn(const char *);
187static intmax_t getq(const char *);
188static int intcmp(const char *, const char *);
189static int isunopoperand(void);
190static int islparenoperand(void);
191static int isrparenoperand(void);
192static int newerf(const char *, const char *);
193static int nexpr(enum token);
194static int oexpr(enum token);
195static int olderf(const char *, const char *);
196static int primary(enum token);
197static void syntax(const char *, const char *);
198static enum token t_lex(char *);
44bd5ea7
A
199
200int
71aad674 201main(int argc, char **argv)
44bd5ea7
A
202{
203 int res;
71aad674
A
204 char *p;
205
206 /* radar:4689479 */
207 if (argc == 0)
208 return 1;
44bd5ea7 209
71aad674
A
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 ]");
44bd5ea7
A
217 argv[argc] = NULL;
218 }
219
71aad674
A
220 /* no expression => false */
221 if (--argc <= 0)
44bd5ea7 222 return 1;
44bd5ea7 223
71aad674
A
224#ifndef SHELL
225 (void)setlocale(LC_CTYPE, "");
226#endif
227 nargc = argc;
44bd5ea7 228 t_wp = &argv[1];
71aad674
A
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");
44bd5ea7
A
240
241 return res;
242}
243
244static void
71aad674 245syntax(const char *op, const char *msg)
44bd5ea7 246{
71aad674 247
44bd5ea7 248 if (op && *op)
71aad674 249 error("%s: %s", op, msg);
44bd5ea7 250 else
71aad674 251 error("%s", msg);
44bd5ea7
A
252}
253
254static int
71aad674 255oexpr(enum token n)
44bd5ea7
A
256{
257 int res;
258
259 res = aexpr(n);
71aad674
A
260 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR)
261 return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ||
262 res;
44bd5ea7 263 t_wp--;
71aad674 264 nargc++;
44bd5ea7
A
265 return res;
266}
267
268static int
71aad674 269aexpr(enum token n)
44bd5ea7
A
270{
271 int res;
272
273 res = nexpr(n);
71aad674
A
274 if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND)
275 return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) &&
276 res;
44bd5ea7 277 t_wp--;
71aad674 278 nargc++;
44bd5ea7
A
279 return res;
280}
281
282static int
71aad674 283nexpr(enum token n)
44bd5ea7
A
284{
285 if (n == UNOT)
71aad674 286 return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL));
44bd5ea7
A
287 return primary(n);
288}
289
290static int
71aad674 291primary(enum token n)
44bd5ea7 292{
71aad674 293 enum token nn;
44bd5ea7
A
294 int res;
295
296 if (n == EOI)
71aad674 297 return 0; /* missing expression */
44bd5ea7 298 if (n == LPAREN) {
71aad674
A
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)
44bd5ea7 307 syntax(NULL, "closing paren expected");
71aad674 308 parenlevel--;
44bd5ea7
A
309 return res;
310 }
71aad674 311 if (TOKEN_TYPE(n) == UNOP) {
44bd5ea7 312 /* unary expression */
71aad674
A
313 if (--nargc == 0)
314 syntax(NULL, "argument expected"); /* impossible */
44bd5ea7
A
315 switch (n) {
316 case STREZ:
71aad674 317 return strlen(*++t_wp) == 0;
44bd5ea7 318 case STRNZ:
71aad674 319 return strlen(*++t_wp) != 0;
44bd5ea7 320 case FILTT:
71aad674 321 return isatty(getn(*++t_wp));
44bd5ea7 322 default:
71aad674 323 return filstat(*++t_wp, n);
44bd5ea7
A
324 }
325 }
326
71aad674
A
327 nn = t_lex(nargc > 0 ? t_wp[1] : NULL);
328 if (TOKEN_TYPE(nn) == BINOP)
329 return binop(nn);
44bd5ea7
A
330
331 return strlen(*t_wp) > 0;
332}
333
334static int
71aad674 335binop(enum token n)
44bd5ea7 336{
71aad674 337 const char *opnd1, *op, *opnd2;
44bd5ea7
A
338
339 opnd1 = *t_wp;
71aad674 340 op = nargc > 0 ? (--nargc, *++t_wp) : NULL;
44bd5ea7 341
71aad674
A
342 if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL)
343 syntax(op, "argument expected");
344
345 switch (n) {
44bd5ea7
A
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:
71aad674 355 return intcmp(opnd1, opnd2) == 0;
44bd5ea7 356 case INTNE:
71aad674 357 return intcmp(opnd1, opnd2) != 0;
44bd5ea7 358 case INTGE:
71aad674 359 return intcmp(opnd1, opnd2) >= 0;
44bd5ea7 360 case INTGT:
71aad674 361 return intcmp(opnd1, opnd2) > 0;
44bd5ea7 362 case INTLE:
71aad674 363 return intcmp(opnd1, opnd2) <= 0;
44bd5ea7 364 case INTLT:
71aad674 365 return intcmp(opnd1, opnd2) < 0;
44bd5ea7
A
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
378static int
71aad674 379filstat(char *nm, enum token mode)
44bd5ea7
A
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:
71aad674 388 return (eaccess(nm, R_OK) == 0);
44bd5ea7 389 case FILWR:
71aad674 390 return (eaccess(nm, W_OK) == 0);
44bd5ea7 391 case FILEX:
71aad674
A
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;
44bd5ea7 398 case FILEXIST:
71aad674 399 return (eaccess(nm, F_OK) == 0);
44bd5ea7
A
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
431static enum token
71aad674 432t_lex(char *s)
44bd5ea7
A
433{
434 struct t_op const *op = ops;
435
436 if (s == 0) {
44bd5ea7
A
437 return EOI;
438 }
71aad674 439 while (*op->op_text) {
44bd5ea7 440 if (strcmp(s, op->op_text) == 0) {
71aad674
A
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;
44bd5ea7
A
447 return op->op_num;
448 }
449 op++;
450 }
44bd5ea7
A
451 return OPERAND;
452}
453
71aad674
A
454static int
455isunopoperand(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
476static int
477islparenoperand(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
497static int
498isrparenoperand(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
44bd5ea7
A
510/* atoi with error detection */
511static int
71aad674 512getn(const char *s)
44bd5ea7
A
513{
514 char *p;
515 long r;
516
517 errno = 0;
518 r = strtol(s, &p, 10);
519
71aad674
A
520 if (s == p)
521 error("%s: bad number", s);
522
44bd5ea7 523 if (errno != 0)
71aad674
A
524 error((errno == EINVAL) ? "%s: bad number" :
525 "%s: out of range", s);
526
527 while (isspace((unsigned char)*p))
528 p++;
44bd5ea7 529
44bd5ea7 530 if (*p)
71aad674 531 error("%s: bad number", s);
44bd5ea7
A
532
533 return (int) r;
534}
535
71aad674
A
536/* atoi with error detection and 64 bit range */
537static intmax_t
538getq(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
44bd5ea7 562static int
71aad674 563intcmp (const char *s1, const char *s2)
44bd5ea7 564{
71aad674 565 intmax_t q1, q2;
44bd5ea7 566
71aad674
A
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;
44bd5ea7
A
578}
579
580static int
71aad674 581newerf (const char *f1, const char *f2)
44bd5ea7
A
582{
583 struct stat b1, b2;
584
71aad674
A
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
596static int
597olderf (const char *f1, const char *f2)
598{
599 return (newerf(f2, f1));
44bd5ea7
A
600}
601
602static int
71aad674 603equalf (const char *f1, const char *f2)
44bd5ea7
A
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}