test.c revision 96375
1169691Skan/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 2169691Skan 3169691Skan/* 4169691Skan * test(1); version 7-like -- author Erik Baalbergen 5169691Skan * modified by Eric Gisin to be used as built-in. 6169691Skan * modified by Arnold Robbins to add SVR3 compatibility 7169691Skan * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8169691Skan * modified by J.T. Conklin for NetBSD. 9169691Skan * 10169691Skan * This program is in the Public Domain. 11169691Skan */ 12169691Skan 13169691Skan#ifndef lint 14169691Skanstatic const char rcsid[] = 15169691Skan "$FreeBSD: head/bin/test/test.c 96375 2002-05-11 01:24:39Z alfred $"; 16169691Skan#endif /* not lint */ 17169691Skan 18169691Skan#include <sys/types.h> 19169691Skan#include <sys/stat.h> 20169691Skan 21169691Skan#include <ctype.h> 22169691Skan#include <err.h> 23169691Skan#include <errno.h> 24169691Skan#include <inttypes.h> 25169691Skan#include <limits.h> 26169691Skan#include <stdarg.h> 27169691Skan#include <stdio.h> 28169691Skan#include <stdlib.h> 29169691Skan#include <string.h> 30169691Skan#include <unistd.h> 31169691Skan 32169691Skan#ifdef SHELL 33169691Skan#define main testcmd 34169691Skan#include "bltin/bltin.h" 35169691Skan#else 36169691Skan#include <locale.h> 37169691Skan 38169691Skanstatic void error(const char *, ...) __attribute__((__noreturn__)) 39169691Skan __printf0like(1, 2); 40169691Skan 41169691Skanstatic void 42169691Skanerror(const char *msg, ...) 43169691Skan{ 44169691Skan va_list ap; 45169691Skan va_start(ap, msg); 46169691Skan verrx(2, msg, ap); 47169691Skan /*NOTREACHED*/ 48169691Skan va_end(ap); 49169691Skan} 50169691Skan#endif 51169691Skan 52169691Skan/* test(1) accepts the following grammar: 53169691Skan oexpr ::= aexpr | aexpr "-o" oexpr ; 54169691Skan aexpr ::= nexpr | nexpr "-a" aexpr ; 55169691Skan nexpr ::= primary | "!" primary 56169691Skan primary ::= unary-operator operand 57169691Skan | operand binary-operator operand 58169691Skan | operand 59169691Skan | "(" oexpr ")" 60169691Skan ; 61169691Skan unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 62169691Skan "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 63169691Skan 64169691Skan binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 65169691Skan "-nt"|"-ot"|"-ef"; 66169691Skan operand ::= <any legal UNIX file name> 67169691Skan*/ 68169691Skan 69169691Skanenum token { 70169691Skan EOI, 71169691Skan FILRD, 72169691Skan FILWR, 73169691Skan FILEX, 74169691Skan FILEXIST, 75169691Skan FILREG, 76169691Skan FILDIR, 77169691Skan FILCDEV, 78169691Skan FILBDEV, 79169691Skan FILFIFO, 80169691Skan FILSOCK, 81169691Skan FILSYM, 82169691Skan FILGZ, 83169691Skan FILTT, 84169691Skan FILSUID, 85169691Skan FILSGID, 86169691Skan FILSTCK, 87169691Skan FILNT, 88169691Skan FILOT, 89169691Skan FILEQ, 90169691Skan FILUID, 91169691Skan FILGID, 92169691Skan STREZ, 93169691Skan STRNZ, 94169691Skan STREQ, 95169691Skan STRNE, 96169691Skan STRLT, 97169691Skan STRGT, 98169691Skan INTEQ, 99169691Skan INTNE, 100169691Skan INTGE, 101169691Skan INTGT, 102169691Skan INTLE, 103169691Skan INTLT, 104169691Skan UNOT, 105169691Skan BAND, 106169691Skan BOR, 107169691Skan LPAREN, 108169691Skan RPAREN, 109169691Skan OPERAND 110169691Skan}; 111169691Skan 112169691Skanenum token_types { 113169691Skan UNOP, 114169691Skan BINOP, 115169691Skan BUNOP, 116169691Skan BBINOP, 117169691Skan PAREN 118169691Skan}; 119169691Skan 120169691Skanstruct t_op { 121169691Skan const char *op_text; 122169691Skan short op_num, op_type; 123169691Skan} const ops [] = { 124169691Skan {"-r", FILRD, UNOP}, 125169691Skan {"-w", FILWR, UNOP}, 126169691Skan {"-x", FILEX, UNOP}, 127169691Skan {"-e", FILEXIST,UNOP}, 128169691Skan {"-f", FILREG, UNOP}, 129169691Skan {"-d", FILDIR, UNOP}, 130169691Skan {"-c", FILCDEV,UNOP}, 131169691Skan {"-b", FILBDEV,UNOP}, 132169691Skan {"-p", FILFIFO,UNOP}, 133169691Skan {"-u", FILSUID,UNOP}, 134169691Skan {"-g", FILSGID,UNOP}, 135169691Skan {"-k", FILSTCK,UNOP}, 136169691Skan {"-s", FILGZ, UNOP}, 137169691Skan {"-t", FILTT, UNOP}, 138169691Skan {"-z", STREZ, UNOP}, 139169691Skan {"-n", STRNZ, UNOP}, 140169691Skan {"-h", FILSYM, UNOP}, /* for backwards compat */ 141169691Skan {"-O", FILUID, UNOP}, 142169691Skan {"-G", FILGID, UNOP}, 143169691Skan {"-L", FILSYM, UNOP}, 144169691Skan {"-S", FILSOCK,UNOP}, 145169691Skan {"=", STREQ, BINOP}, 146169691Skan {"!=", STRNE, BINOP}, 147169691Skan {"<", STRLT, BINOP}, 148169691Skan {">", STRGT, BINOP}, 149169691Skan {"-eq", INTEQ, BINOP}, 150169691Skan {"-ne", INTNE, BINOP}, 151169691Skan {"-ge", INTGE, BINOP}, 152169691Skan {"-gt", INTGT, BINOP}, 153169691Skan {"-le", INTLE, BINOP}, 154169691Skan {"-lt", INTLT, BINOP}, 155169691Skan {"-nt", FILNT, BINOP}, 156169691Skan {"-ot", FILOT, BINOP}, 157 {"-ef", FILEQ, BINOP}, 158 {"!", UNOT, BUNOP}, 159 {"-a", BAND, BBINOP}, 160 {"-o", BOR, BBINOP}, 161 {"(", LPAREN, PAREN}, 162 {")", RPAREN, PAREN}, 163 {0, 0, 0} 164}; 165 166struct t_op const *t_wp_op; 167char **t_wp; 168 169static int aexpr(enum token); 170static int binop(void); 171static int equalf(const char *, const char *); 172static int filstat(char *, enum token); 173static int getn(const char *); 174static intmax_t getq(const char *); 175static int intcmp(const char *, const char *); 176static int isoperand(void); 177static int newerf(const char *, const char *); 178static int nexpr(enum token); 179static int oexpr(enum token); 180static int olderf(const char *, const char *); 181static int primary(enum token); 182static void syntax(const char *, const char *); 183static enum token t_lex(char *); 184 185int 186main(int argc, char **argv) 187{ 188 int i, res; 189 char *p; 190 char **nargv; 191 192 /* 193 * XXX copy the whole contents of argv to a newly allocated 194 * space with two extra cells filled with NULL's - this source 195 * code totally depends on their presence. 196 */ 197 if ((nargv = (char **)malloc((argc + 2) * sizeof(char *))) == NULL) 198 error("Out of space"); 199 200 for (i = 0; i < argc; i++) 201 nargv[i] = argv[i]; 202 203 nargv[i] = nargv[i + 1] = NULL; 204 argv = nargv; 205 206 if ((p = rindex(argv[0], '/')) == NULL) 207 p = argv[0]; 208 else 209 p++; 210 if (strcmp(p, "[") == 0) { 211 if (strcmp(argv[--argc], "]") != 0) 212 error("missing ]"); 213 argv[argc] = NULL; 214 } 215 216#ifndef SHELL 217 (void)setlocale(LC_CTYPE, ""); 218#endif 219 t_wp = &argv[1]; 220 res = !oexpr(t_lex(*t_wp)); 221 222 if (*t_wp != NULL && *++t_wp != NULL) 223 syntax(*t_wp, "unexpected operator"); 224 225 return res; 226} 227 228static void 229syntax(const char *op, const char *msg) 230{ 231 232 if (op && *op) 233 error("%s: %s", op, msg); 234 else 235 error("%s", msg); 236} 237 238static int 239oexpr(enum token n) 240{ 241 int res; 242 243 res = aexpr(n); 244 if (t_lex(*++t_wp) == BOR) 245 return oexpr(t_lex(*++t_wp)) || res; 246 t_wp--; 247 return res; 248} 249 250static int 251aexpr(enum token n) 252{ 253 int res; 254 255 res = nexpr(n); 256 if (t_lex(*++t_wp) == BAND) 257 return aexpr(t_lex(*++t_wp)) && res; 258 t_wp--; 259 return res; 260} 261 262static int 263nexpr(enum token n) 264{ 265 if (n == UNOT) 266 return !nexpr(t_lex(*++t_wp)); 267 return primary(n); 268} 269 270static int 271primary(enum token n) 272{ 273 enum token nn; 274 int res; 275 276 if (n == EOI) 277 return 0; /* missing expression */ 278 if (n == LPAREN) { 279 if ((nn = t_lex(*++t_wp)) == RPAREN) 280 return 0; /* missing expression */ 281 res = oexpr(nn); 282 if (t_lex(*++t_wp) != RPAREN) 283 syntax(NULL, "closing paren expected"); 284 return res; 285 } 286 if (t_wp_op && t_wp_op->op_type == UNOP) { 287 /* unary expression */ 288 if (*++t_wp == NULL) 289 syntax(t_wp_op->op_text, "argument expected"); 290 switch (n) { 291 case STREZ: 292 return strlen(*t_wp) == 0; 293 case STRNZ: 294 return strlen(*t_wp) != 0; 295 case FILTT: 296 return isatty(getn(*t_wp)); 297 default: 298 return filstat(*t_wp, n); 299 } 300 } 301 302 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 303 return binop(); 304 } 305 306 return strlen(*t_wp) > 0; 307} 308 309static int 310binop(void) 311{ 312 const char *opnd1, *opnd2; 313 struct t_op const *op; 314 315 opnd1 = *t_wp; 316 (void) t_lex(*++t_wp); 317 op = t_wp_op; 318 319 if ((opnd2 = *++t_wp) == NULL) 320 syntax(op->op_text, "argument expected"); 321 322 switch (op->op_num) { 323 case STREQ: 324 return strcmp(opnd1, opnd2) == 0; 325 case STRNE: 326 return strcmp(opnd1, opnd2) != 0; 327 case STRLT: 328 return strcmp(opnd1, opnd2) < 0; 329 case STRGT: 330 return strcmp(opnd1, opnd2) > 0; 331 case INTEQ: 332 return intcmp(opnd1, opnd2) == 0; 333 case INTNE: 334 return intcmp(opnd1, opnd2) != 0; 335 case INTGE: 336 return intcmp(opnd1, opnd2) >= 0; 337 case INTGT: 338 return intcmp(opnd1, opnd2) > 0; 339 case INTLE: 340 return intcmp(opnd1, opnd2) <= 0; 341 case INTLT: 342 return intcmp(opnd1, opnd2) < 0; 343 case FILNT: 344 return newerf (opnd1, opnd2); 345 case FILOT: 346 return olderf (opnd1, opnd2); 347 case FILEQ: 348 return equalf (opnd1, opnd2); 349 default: 350 abort(); 351 /* NOTREACHED */ 352 } 353} 354 355static int 356filstat(char *nm, enum token mode) 357{ 358 struct stat s; 359 360 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 361 return 0; 362 363 switch (mode) { 364 case FILRD: 365 return (eaccess(nm, R_OK) == 0); 366 case FILWR: 367 return (eaccess(nm, W_OK) == 0); 368 case FILEX: 369 /* XXX work around eaccess(2) false positives for superuser */ 370 if (eaccess(nm, X_OK) != 0) 371 return 0; 372 if (S_ISDIR(s.st_mode) || geteuid() != 0) 373 return 1; 374 return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 375 case FILEXIST: 376 return (eaccess(nm, F_OK) == 0); 377 case FILREG: 378 return S_ISREG(s.st_mode); 379 case FILDIR: 380 return S_ISDIR(s.st_mode); 381 case FILCDEV: 382 return S_ISCHR(s.st_mode); 383 case FILBDEV: 384 return S_ISBLK(s.st_mode); 385 case FILFIFO: 386 return S_ISFIFO(s.st_mode); 387 case FILSOCK: 388 return S_ISSOCK(s.st_mode); 389 case FILSYM: 390 return S_ISLNK(s.st_mode); 391 case FILSUID: 392 return (s.st_mode & S_ISUID) != 0; 393 case FILSGID: 394 return (s.st_mode & S_ISGID) != 0; 395 case FILSTCK: 396 return (s.st_mode & S_ISVTX) != 0; 397 case FILGZ: 398 return s.st_size > (off_t)0; 399 case FILUID: 400 return s.st_uid == geteuid(); 401 case FILGID: 402 return s.st_gid == getegid(); 403 default: 404 return 1; 405 } 406} 407 408static enum token 409t_lex(char *s) 410{ 411 struct t_op const *op = ops; 412 413 if (s == 0) { 414 t_wp_op = NULL; 415 return EOI; 416 } 417 while (op->op_text) { 418 if (strcmp(s, op->op_text) == 0) { 419 if ((op->op_type == UNOP && isoperand()) || 420 (op->op_num == LPAREN && *(t_wp+1) == 0)) 421 break; 422 t_wp_op = op; 423 return op->op_num; 424 } 425 op++; 426 } 427 t_wp_op = NULL; 428 return OPERAND; 429} 430 431static int 432isoperand(void) 433{ 434 struct t_op const *op = ops; 435 char *s; 436 char *t; 437 438 if ((s = *(t_wp+1)) == 0) 439 return 1; 440 if ((t = *(t_wp+2)) == 0) 441 return 0; 442 while (op->op_text) { 443 if (strcmp(s, op->op_text) == 0) 444 return op->op_type == BINOP && 445 (t[0] != ')' || t[1] != '\0'); 446 op++; 447 } 448 return 0; 449} 450 451/* atoi with error detection */ 452static int 453getn(const char *s) 454{ 455 char *p; 456 long r; 457 458 errno = 0; 459 r = strtol(s, &p, 10); 460 461 if (s == p) 462 error("%s: bad number", s); 463 464 if (errno != 0) 465 error((errno == EINVAL) ? "%s: bad number" : 466 "%s: out of range", s); 467 468 while (isspace((unsigned char)*p)) 469 p++; 470 471 if (*p) 472 error("%s: bad number", s); 473 474 return (int) r; 475} 476 477/* atoi with error detection and 64 bit range */ 478static intmax_t 479getq(const char *s) 480{ 481 char *p; 482 intmax_t r; 483 484 errno = 0; 485 r = strtoimax(s, &p, 10); 486 487 if (s == p) 488 error("%s: bad number", s); 489 490 if (errno != 0) 491 error((errno == EINVAL) ? "%s: bad number" : 492 "%s: out of range", s); 493 494 while (isspace((unsigned char)*p)) 495 p++; 496 497 if (*p) 498 error("%s: bad number", s); 499 500 return r; 501} 502 503static int 504intcmp (const char *s1, const char *s2) 505{ 506 intmax_t q1, q2; 507 508 509 q1 = getq(s1); 510 q2 = getq(s2); 511 512 if (q1 > q2) 513 return 1; 514 515 if (q1 < q2) 516 return -1; 517 518 return 0; 519} 520 521static int 522newerf (const char *f1, const char *f2) 523{ 524 struct stat b1, b2; 525 526 return (stat (f1, &b1) == 0 && 527 stat (f2, &b2) == 0 && 528 b1.st_mtime > b2.st_mtime); 529} 530 531static int 532olderf (const char *f1, const char *f2) 533{ 534 struct stat b1, b2; 535 536 return (stat (f1, &b1) == 0 && 537 stat (f2, &b2) == 0 && 538 b1.st_mtime < b2.st_mtime); 539} 540 541static int 542equalf (const char *f1, const char *f2) 543{ 544 struct stat b1, b2; 545 546 return (stat (f1, &b1) == 0 && 547 stat (f2, &b2) == 0 && 548 b1.st_dev == b2.st_dev && 549 b1.st_ino == b2.st_ino); 550} 551