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