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