test.c revision 247274
1253883Ssjg/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 2236769Sobrien 3236769Sobrien/*- 4236769Sobrien * test(1); version 7-like -- author Erik Baalbergen 5236769Sobrien * modified by Eric Gisin to be used as built-in. 6236769Sobrien * modified by Arnold Robbins to add SVR3 compatibility 7236769Sobrien * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8236769Sobrien * modified by J.T. Conklin for NetBSD. 9236769Sobrien * 10236769Sobrien * This program is in the Public Domain. 11236769Sobrien */ 12236769Sobrien/* 13236769Sobrien * Important: This file is used both as a standalone program /bin/test and 14236769Sobrien * as a builtin for /bin/sh (#define SHELL). 15236769Sobrien */ 16236769Sobrien 17236769Sobrien#include <sys/cdefs.h> 18236769Sobrien__FBSDID("$FreeBSD: head/bin/test/test.c 247274 2013-02-25 19:05:40Z peterj $"); 19236769Sobrien 20236769Sobrien#include <sys/types.h> 21236769Sobrien#include <sys/stat.h> 22236769Sobrien 23236769Sobrien#include <ctype.h> 24236769Sobrien#include <err.h> 25236769Sobrien#include <errno.h> 26236769Sobrien#include <inttypes.h> 27236769Sobrien#include <limits.h> 28236769Sobrien#include <stdarg.h> 29236769Sobrien#include <stdio.h> 30236769Sobrien#include <stdlib.h> 31236769Sobrien#include <string.h> 32236769Sobrien#include <unistd.h> 33236769Sobrien 34236769Sobrien#ifdef SHELL 35236769Sobrien#define main testcmd 36236769Sobrien#include "bltin/bltin.h" 37236769Sobrien#else 38236769Sobrien#include <locale.h> 39236769Sobrien 40236769Sobrienstatic void error(const char *, ...) __dead2 __printf0like(1, 2); 41236769Sobrien 42236769Sobrienstatic void 43236769Sobrienerror(const char *msg, ...) 44236769Sobrien{ 45236769Sobrien va_list ap; 46236769Sobrien va_start(ap, msg); 47236769Sobrien verrx(2, msg, ap); 48236769Sobrien /*NOTREACHED*/ 49236769Sobrien va_end(ap); 50236769Sobrien} 51236769Sobrien#endif 52236769Sobrien 53236769Sobrien/* test(1) accepts the following grammar: 54236769Sobrien oexpr ::= aexpr | aexpr "-o" oexpr ; 55236769Sobrien aexpr ::= nexpr | nexpr "-a" aexpr ; 56236769Sobrien nexpr ::= primary | "!" primary 57236769Sobrien primary ::= unary-operator operand 58236769Sobrien | operand binary-operator operand 59236769Sobrien | operand 60236769Sobrien | "(" oexpr ")" 61236769Sobrien ; 62236769Sobrien unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 63236769Sobrien "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 64236769Sobrien 65236769Sobrien binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 66236769Sobrien "-nt"|"-nt[abcm][abcm]"|"-ot"|"-ot[abcm][abcm])"|"-ef"; 67236769Sobrien operand ::= <any legal UNIX file name> 68236769Sobrien*/ 69236769Sobrien 70236769Sobrienenum token { 71236769Sobrien EOI, 72236769Sobrien FILRD, 73236769Sobrien FILWR, 74236769Sobrien FILEX, 75236769Sobrien FILEXIST, 76236769Sobrien FILREG, 77236769Sobrien FILDIR, 78236769Sobrien FILCDEV, 79236769Sobrien FILBDEV, 80236769Sobrien FILFIFO, 81236769Sobrien FILSOCK, 82236769Sobrien FILSYM, 83236769Sobrien FILGZ, 84236769Sobrien FILTT, 85236769Sobrien FILSUID, 86236769Sobrien FILSGID, 87236769Sobrien FILSTCK, 88236769Sobrien FILNTAA, 89236769Sobrien FILNTAB, 90236769Sobrien FILNTAC, 91236769Sobrien FILNTAM, 92236769Sobrien FILNTBA, 93236769Sobrien FILNTBB, 94236769Sobrien FILNTBC, 95236769Sobrien FILNTBM, 96236769Sobrien FILNTCA, 97236769Sobrien FILNTCB, 98236769Sobrien FILNTCC, 99236769Sobrien FILNTCM, 100236769Sobrien FILNTMA, 101236769Sobrien FILNTMB, 102237578Sobrien FILNTMC, 103236769Sobrien FILNTMM, 104236769Sobrien FILOTAA, 105236769Sobrien FILOTAB, 106249033Ssjg FILOTAC, 107236769Sobrien FILOTAM, 108236769Sobrien FILOTBA, 109237578Sobrien FILOTBB, 110237578Sobrien FILOTBC, 111236769Sobrien FILOTBM, 112237578Sobrien FILOTCA, 113236769Sobrien FILOTCB, 114237578Sobrien FILOTCC, 115237578Sobrien FILOTCM, 116237578Sobrien FILOTMA, 117237578Sobrien FILOTMB, 118237578Sobrien FILOTMC, 119237578Sobrien FILOTMM, 120237578Sobrien FILEQ, 121236769Sobrien FILUID, 122236769Sobrien FILGID, 123237578Sobrien STREZ, 124237578Sobrien STRNZ, 125237578Sobrien STREQ, 126237578Sobrien STRNE, 127237578Sobrien STRLT, 128236769Sobrien STRGT, 129236769Sobrien INTEQ, 130236769Sobrien INTNE, 131236769Sobrien INTGE, 132236769Sobrien INTGT, 133236769Sobrien INTLE, 134236769Sobrien INTLT, 135236769Sobrien UNOT, 136236769Sobrien BAND, 137236769Sobrien BOR, 138236769Sobrien LPAREN, 139236769Sobrien RPAREN, 140236769Sobrien OPERAND 141236769Sobrien}; 142236769Sobrien 143236769Sobrienenum token_types { 144236769Sobrien UNOP, 145236769Sobrien BINOP, 146236769Sobrien BUNOP, 147236769Sobrien BBINOP, 148236769Sobrien PAREN 149236769Sobrien}; 150236769Sobrien 151236769Sobrienenum time_types { 152236769Sobrien ATIME, 153236769Sobrien BTIME, 154236769Sobrien CTIME, 155236769Sobrien MTIME 156236769Sobrien}; 157236769Sobrien 158236769Sobrienstatic struct t_op { 159236769Sobrien char op_text[6]; 160236769Sobrien char op_num, op_type; 161236769Sobrien} const ops [] = { 162236769Sobrien {"-r", FILRD, UNOP}, 163236769Sobrien {"-w", FILWR, UNOP}, 164236769Sobrien {"-x", FILEX, UNOP}, 165236769Sobrien {"-e", FILEXIST,UNOP}, 166236769Sobrien {"-f", FILREG, UNOP}, 167236769Sobrien {"-d", FILDIR, UNOP}, 168236769Sobrien {"-c", FILCDEV,UNOP}, 169236769Sobrien {"-b", FILBDEV,UNOP}, 170236769Sobrien {"-p", FILFIFO,UNOP}, 171236769Sobrien {"-u", FILSUID,UNOP}, 172236769Sobrien {"-g", FILSGID,UNOP}, 173236769Sobrien {"-k", FILSTCK,UNOP}, 174236769Sobrien {"-s", FILGZ, UNOP}, 175236769Sobrien {"-t", FILTT, UNOP}, 176236769Sobrien {"-z", STREZ, UNOP}, 177236769Sobrien {"-n", STRNZ, UNOP}, 178236769Sobrien {"-h", FILSYM, UNOP}, /* for backwards compat */ 179236769Sobrien {"-O", FILUID, UNOP}, 180236769Sobrien {"-G", FILGID, UNOP}, 181236769Sobrien {"-L", FILSYM, UNOP}, 182236769Sobrien {"-S", FILSOCK,UNOP}, 183236769Sobrien {"=", STREQ, BINOP}, 184236769Sobrien {"==", STREQ, BINOP}, 185236769Sobrien {"!=", STRNE, BINOP}, 186236769Sobrien {"<", STRLT, BINOP}, 187236769Sobrien {">", STRGT, BINOP}, 188236769Sobrien {"-eq", INTEQ, BINOP}, 189236769Sobrien {"-ne", INTNE, BINOP}, 190236769Sobrien {"-ge", INTGE, BINOP}, 191236769Sobrien {"-gt", INTGT, BINOP}, 192236769Sobrien {"-le", INTLE, BINOP}, 193236769Sobrien {"-lt", INTLT, BINOP}, 194236769Sobrien {"-nt", FILNTMM, BINOP}, 195236769Sobrien {"-ntaa", FILNTAA, BINOP}, 196236769Sobrien {"-ntab", FILNTAB, BINOP}, 197236769Sobrien {"-ntac", FILNTAC, BINOP}, 198236769Sobrien {"-ntam", FILNTAM, BINOP}, 199236769Sobrien {"-ntba", FILNTBA, BINOP}, 200236769Sobrien {"-ntbb", FILNTBB, BINOP}, 201236769Sobrien {"-ntbc", FILNTBC, BINOP}, 202236769Sobrien {"-ntbm", FILNTBM, BINOP}, 203236769Sobrien {"-ntca", FILNTCA, BINOP}, 204236769Sobrien {"-ntcb", FILNTCB, BINOP}, 205236769Sobrien {"-ntcc", FILNTCC, BINOP}, 206236769Sobrien {"-ntcm", FILNTCM, BINOP}, 207236769Sobrien {"-ntma", FILNTMA, BINOP}, 208236769Sobrien {"-ntmb", FILNTMB, BINOP}, 209236769Sobrien {"-ntmc", FILNTMC, BINOP}, 210236769Sobrien {"-ntmm", FILNTMM, BINOP}, 211236769Sobrien {"-ot", FILOTMM, BINOP}, 212236769Sobrien {"-otaa", FILOTAA, BINOP}, 213236769Sobrien {"-otab", FILOTBB, BINOP}, 214236769Sobrien {"-otac", FILOTAC, BINOP}, 215236769Sobrien {"-otam", FILOTAM, BINOP}, 216236769Sobrien {"-otba", FILOTBA, BINOP}, 217236769Sobrien {"-otbb", FILOTBB, BINOP}, 218236769Sobrien {"-otbc", FILOTBC, BINOP}, 219236769Sobrien {"-otbm", FILOTBM, BINOP}, 220236769Sobrien {"-otca", FILOTCA, BINOP}, 221236769Sobrien {"-otcb", FILOTCB, BINOP}, 222236769Sobrien {"-otcc", FILOTCC, BINOP}, 223236769Sobrien {"-otcm", FILOTCM, BINOP}, 224236769Sobrien {"-otma", FILOTMA, BINOP}, 225236769Sobrien {"-otmb", FILOTMB, BINOP}, 226236769Sobrien {"-otmc", FILOTMC, BINOP}, 227236769Sobrien {"-otmm", FILOTMM, BINOP}, 228236769Sobrien {"-ef", FILEQ, BINOP}, 229236769Sobrien {"!", UNOT, BUNOP}, 230236769Sobrien {"-a", BAND, BBINOP}, 231236769Sobrien {"-o", BOR, BBINOP}, 232236769Sobrien {"(", LPAREN, PAREN}, 233236769Sobrien {")", RPAREN, PAREN}, 234236769Sobrien {"", 0, 0} 235236769Sobrien}; 236236769Sobrien 237236769Sobrienstatic struct t_op const *t_wp_op; 238236769Sobrienstatic int nargc; 239236769Sobrienstatic char **t_wp; 240236769Sobrienstatic int parenlevel; 241236769Sobrien 242236769Sobrienstatic int aexpr(enum token); 243236769Sobrienstatic int binop(void); 244236769Sobrienstatic int equalf(const char *, const char *); 245236769Sobrienstatic int filstat(char *, enum token); 246236769Sobrienstatic int getn(const char *); 247236769Sobrienstatic intmax_t getq(const char *); 248236769Sobrienstatic int intcmp(const char *, const char *); 249236769Sobrienstatic int isunopoperand(void); 250236769Sobrienstatic int islparenoperand(void); 251236769Sobrienstatic int isrparenoperand(void); 252236769Sobrienstatic int newerf(const char *, const char *, enum time_types, 253236769Sobrien enum time_types); 254236769Sobrienstatic int nexpr(enum token); 255236769Sobrienstatic int oexpr(enum token); 256236769Sobrienstatic int primary(enum token); 257236769Sobrienstatic void syntax(const char *, const char *); 258236769Sobrienstatic enum token t_lex(char *); 259236769Sobrien 260236769Sobrienint 261236769Sobrienmain(int argc, char **argv) 262236769Sobrien{ 263236769Sobrien int res; 264236769Sobrien char *p; 265236769Sobrien 266236769Sobrien if ((p = strrchr(argv[0], '/')) == NULL) 267236769Sobrien p = argv[0]; 268236769Sobrien else 269236769Sobrien p++; 270236769Sobrien if (strcmp(p, "[") == 0) { 271236769Sobrien if (strcmp(argv[--argc], "]") != 0) 272236769Sobrien error("missing ]"); 273236769Sobrien argv[argc] = NULL; 274236769Sobrien } 275236769Sobrien 276236769Sobrien /* no expression => false */ 277236769Sobrien if (--argc <= 0) 278236769Sobrien return 1; 279236769Sobrien 280236769Sobrien#ifndef SHELL 281236769Sobrien (void)setlocale(LC_CTYPE, ""); 282236769Sobrien#endif 283236769Sobrien nargc = argc; 284236769Sobrien t_wp = &argv[1]; 285236769Sobrien parenlevel = 0; 286236769Sobrien if (nargc == 4 && strcmp(*t_wp, "!") == 0) { 287236769Sobrien /* Things like ! "" -o x do not fit in the normal grammar. */ 288236769Sobrien --nargc; 289236769Sobrien ++t_wp; 290236769Sobrien res = oexpr(t_lex(*t_wp)); 291236769Sobrien } else 292236769Sobrien res = !oexpr(t_lex(*t_wp)); 293236769Sobrien 294236769Sobrien if (--nargc > 0) 295236769Sobrien syntax(*t_wp, "unexpected operator"); 296236769Sobrien 297236769Sobrien return res; 298236769Sobrien} 299236769Sobrien 300236769Sobrienstatic void 301236769Sobriensyntax(const char *op, const char *msg) 302236769Sobrien{ 303236769Sobrien 304236769Sobrien if (op && *op) 305236769Sobrien error("%s: %s", op, msg); 306236769Sobrien else 307236769Sobrien error("%s", msg); 308236769Sobrien} 309236769Sobrien 310236769Sobrienstatic int 311236769Sobrienoexpr(enum token n) 312236769Sobrien{ 313236769Sobrien int res; 314236769Sobrien 315236769Sobrien res = aexpr(n); 316236769Sobrien if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) 317236769Sobrien return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || 318236769Sobrien res; 319236769Sobrien t_wp--; 320236769Sobrien nargc++; 321236769Sobrien return res; 322236769Sobrien} 323236769Sobrien 324236769Sobrienstatic int 325236769Sobrienaexpr(enum token n) 326236769Sobrien{ 327236769Sobrien int res; 328236769Sobrien 329236769Sobrien res = nexpr(n); 330236769Sobrien if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) 331236769Sobrien return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && 332236769Sobrien res; 333236769Sobrien t_wp--; 334236769Sobrien nargc++; 335236769Sobrien return res; 336236769Sobrien} 337236769Sobrien 338236769Sobrienstatic int 339236769Sobriennexpr(enum token n) 340236769Sobrien{ 341236769Sobrien if (n == UNOT) 342236769Sobrien return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); 343236769Sobrien return primary(n); 344236769Sobrien} 345236769Sobrien 346236769Sobrienstatic int 347236769Sobrienprimary(enum token n) 348236769Sobrien{ 349236769Sobrien enum token nn; 350236769Sobrien int res; 351236769Sobrien 352236769Sobrien if (n == EOI) 353236769Sobrien return 0; /* missing expression */ 354236769Sobrien if (n == LPAREN) { 355236769Sobrien parenlevel++; 356236769Sobrien if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == 357236769Sobrien RPAREN) { 358236769Sobrien parenlevel--; 359236769Sobrien return 0; /* missing expression */ 360236769Sobrien } 361236769Sobrien res = oexpr(nn); 362236769Sobrien if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) 363236769Sobrien syntax(NULL, "closing paren expected"); 364236769Sobrien parenlevel--; 365236769Sobrien return res; 366236769Sobrien } 367236769Sobrien if (t_wp_op && t_wp_op->op_type == UNOP) { 368236769Sobrien /* unary expression */ 369236769Sobrien if (--nargc == 0) 370236769Sobrien syntax(t_wp_op->op_text, "argument expected"); 371236769Sobrien switch (n) { 372236769Sobrien case STREZ: 373236769Sobrien return strlen(*++t_wp) == 0; 374236769Sobrien case STRNZ: 375236769Sobrien return strlen(*++t_wp) != 0; 376236769Sobrien case FILTT: 377236769Sobrien return isatty(getn(*++t_wp)); 378236769Sobrien default: 379236769Sobrien return filstat(*++t_wp, n); 380236769Sobrien } 381236769Sobrien } 382236769Sobrien 383236769Sobrien if (t_lex(nargc > 0 ? t_wp[1] : NULL), t_wp_op && t_wp_op->op_type == 384236769Sobrien BINOP) { 385236769Sobrien return binop(); 386236769Sobrien } 387236769Sobrien 388236769Sobrien return strlen(*t_wp) > 0; 389236769Sobrien} 390236769Sobrien 391236769Sobrienstatic int 392236769Sobrienbinop(void) 393236769Sobrien{ 394236769Sobrien const char *opnd1, *opnd2; 395236769Sobrien struct t_op const *op; 396236769Sobrien 397236769Sobrien opnd1 = *t_wp; 398236769Sobrien (void) t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL); 399236769Sobrien op = t_wp_op; 400236769Sobrien 401236769Sobrien if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) 402236769Sobrien syntax(op->op_text, "argument expected"); 403236769Sobrien 404236769Sobrien switch (op->op_num) { 405236769Sobrien case STREQ: 406236769Sobrien return strcmp(opnd1, opnd2) == 0; 407236769Sobrien case STRNE: 408236769Sobrien return strcmp(opnd1, opnd2) != 0; 409236769Sobrien case STRLT: 410236769Sobrien return strcmp(opnd1, opnd2) < 0; 411236769Sobrien case STRGT: 412236769Sobrien return strcmp(opnd1, opnd2) > 0; 413236769Sobrien case INTEQ: 414236769Sobrien return intcmp(opnd1, opnd2) == 0; 415236769Sobrien case INTNE: 416236769Sobrien return intcmp(opnd1, opnd2) != 0; 417236769Sobrien case INTGE: 418236769Sobrien return intcmp(opnd1, opnd2) >= 0; 419236769Sobrien case INTGT: 420236769Sobrien return intcmp(opnd1, opnd2) > 0; 421236769Sobrien case INTLE: 422236769Sobrien return intcmp(opnd1, opnd2) <= 0; 423236769Sobrien case INTLT: 424236769Sobrien return intcmp(opnd1, opnd2) < 0; 425236769Sobrien case FILNTAA: 426236769Sobrien return newerf(opnd1, opnd2, ATIME, ATIME); 427236769Sobrien case FILNTAB: 428236769Sobrien return newerf(opnd1, opnd2, ATIME, BTIME); 429236769Sobrien case FILNTAC: 430236769Sobrien return newerf(opnd1, opnd2, ATIME, CTIME); 431236769Sobrien case FILNTAM: 432236769Sobrien return newerf(opnd1, opnd2, ATIME, MTIME); 433236769Sobrien case FILNTBA: 434236769Sobrien return newerf(opnd1, opnd2, BTIME, ATIME); 435236769Sobrien case FILNTBB: 436236769Sobrien return newerf(opnd1, opnd2, BTIME, BTIME); 437236769Sobrien case FILNTBC: 438236769Sobrien return newerf(opnd1, opnd2, BTIME, CTIME); 439236769Sobrien case FILNTBM: 440236769Sobrien return newerf(opnd1, opnd2, BTIME, MTIME); 441236769Sobrien case FILNTCA: 442236769Sobrien return newerf(opnd1, opnd2, CTIME, ATIME); 443236769Sobrien case FILNTCB: 444236769Sobrien return newerf(opnd1, opnd2, CTIME, BTIME); 445253883Ssjg case FILNTCC: 446253883Ssjg return newerf(opnd1, opnd2, CTIME, CTIME); 447236769Sobrien case FILNTCM: 448236769Sobrien return newerf(opnd1, opnd2, CTIME, MTIME); 449236769Sobrien case FILNTMA: 450236769Sobrien return newerf(opnd1, opnd2, MTIME, ATIME); 451236769Sobrien case FILNTMB: 452236769Sobrien return newerf(opnd1, opnd2, MTIME, BTIME); 453236769Sobrien case FILNTMC: 454236769Sobrien return newerf(opnd1, opnd2, MTIME, CTIME); 455236769Sobrien case FILNTMM: 456236769Sobrien return newerf(opnd1, opnd2, MTIME, MTIME); 457236769Sobrien case FILOTAA: 458236769Sobrien return newerf(opnd2, opnd1, ATIME, ATIME); 459236769Sobrien case FILOTAB: 460236769Sobrien return newerf(opnd2, opnd1, BTIME, ATIME); 461236769Sobrien case FILOTAC: 462236769Sobrien return newerf(opnd2, opnd1, CTIME, ATIME); 463236769Sobrien case FILOTAM: 464236769Sobrien return newerf(opnd2, opnd1, MTIME, ATIME); 465236769Sobrien case FILOTBA: 466236769Sobrien return newerf(opnd2, opnd1, ATIME, BTIME); 467236769Sobrien case FILOTBB: 468236769Sobrien return newerf(opnd2, opnd1, BTIME, BTIME); 469236769Sobrien case FILOTBC: 470236769Sobrien return newerf(opnd2, opnd1, CTIME, BTIME); 471236769Sobrien case FILOTBM: 472236769Sobrien return newerf(opnd2, opnd1, MTIME, BTIME); 473236769Sobrien case FILOTCA: 474236769Sobrien return newerf(opnd2, opnd1, ATIME, CTIME); 475236769Sobrien case FILOTCB: 476236769Sobrien return newerf(opnd2, opnd1, BTIME, CTIME); 477236769Sobrien case FILOTCC: 478236769Sobrien return newerf(opnd2, opnd1, CTIME, CTIME); 479236769Sobrien case FILOTCM: 480236769Sobrien return newerf(opnd2, opnd1, MTIME, CTIME); 481236769Sobrien case FILOTMA: 482236769Sobrien return newerf(opnd2, opnd1, ATIME, MTIME); 483236769Sobrien case FILOTMB: 484236769Sobrien return newerf(opnd2, opnd1, BTIME, MTIME); 485236769Sobrien case FILOTMC: 486236769Sobrien return newerf(opnd2, opnd1, CTIME, MTIME); 487236769Sobrien case FILOTMM: 488236769Sobrien return newerf(opnd2, opnd1, MTIME, MTIME); 489236769Sobrien case FILEQ: 490236769Sobrien return equalf (opnd1, opnd2); 491236769Sobrien default: 492236769Sobrien abort(); 493236769Sobrien /* NOTREACHED */ 494236769Sobrien } 495236769Sobrien} 496236769Sobrien 497236769Sobrienstatic int 498236769Sobrienfilstat(char *nm, enum token mode) 499236769Sobrien{ 500236769Sobrien struct stat s; 501236769Sobrien 502236769Sobrien if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 503236769Sobrien return 0; 504236769Sobrien 505236769Sobrien switch (mode) { 506236769Sobrien case FILRD: 507236769Sobrien return (eaccess(nm, R_OK) == 0); 508236769Sobrien case FILWR: 509236769Sobrien return (eaccess(nm, W_OK) == 0); 510236769Sobrien case FILEX: 511236769Sobrien /* XXX work around eaccess(2) false positives for superuser */ 512236769Sobrien if (eaccess(nm, X_OK) != 0) 513236769Sobrien return 0; 514236769Sobrien if (S_ISDIR(s.st_mode) || geteuid() != 0) 515236769Sobrien return 1; 516236769Sobrien return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 517236769Sobrien case FILEXIST: 518 return (eaccess(nm, F_OK) == 0); 519 case FILREG: 520 return S_ISREG(s.st_mode); 521 case FILDIR: 522 return S_ISDIR(s.st_mode); 523 case FILCDEV: 524 return S_ISCHR(s.st_mode); 525 case FILBDEV: 526 return S_ISBLK(s.st_mode); 527 case FILFIFO: 528 return S_ISFIFO(s.st_mode); 529 case FILSOCK: 530 return S_ISSOCK(s.st_mode); 531 case FILSYM: 532 return S_ISLNK(s.st_mode); 533 case FILSUID: 534 return (s.st_mode & S_ISUID) != 0; 535 case FILSGID: 536 return (s.st_mode & S_ISGID) != 0; 537 case FILSTCK: 538 return (s.st_mode & S_ISVTX) != 0; 539 case FILGZ: 540 return s.st_size > (off_t)0; 541 case FILUID: 542 return s.st_uid == geteuid(); 543 case FILGID: 544 return s.st_gid == getegid(); 545 default: 546 return 1; 547 } 548} 549 550static enum token 551t_lex(char *s) 552{ 553 struct t_op const *op = ops; 554 555 if (s == 0) { 556 t_wp_op = NULL; 557 return EOI; 558 } 559 while (*op->op_text) { 560 if (strcmp(s, op->op_text) == 0) { 561 if (((op->op_type == UNOP || op->op_type == BUNOP) 562 && isunopoperand()) || 563 (op->op_num == LPAREN && islparenoperand()) || 564 (op->op_num == RPAREN && isrparenoperand())) 565 break; 566 t_wp_op = op; 567 return op->op_num; 568 } 569 op++; 570 } 571 t_wp_op = NULL; 572 return OPERAND; 573} 574 575static int 576isunopoperand(void) 577{ 578 struct t_op const *op = ops; 579 char *s; 580 char *t; 581 582 if (nargc == 1) 583 return 1; 584 s = *(t_wp + 1); 585 if (nargc == 2) 586 return parenlevel == 1 && strcmp(s, ")") == 0; 587 t = *(t_wp + 2); 588 while (*op->op_text) { 589 if (strcmp(s, op->op_text) == 0) 590 return op->op_type == BINOP && 591 (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); 592 op++; 593 } 594 return 0; 595} 596 597static int 598islparenoperand(void) 599{ 600 struct t_op const *op = ops; 601 char *s; 602 603 if (nargc == 1) 604 return 1; 605 s = *(t_wp + 1); 606 if (nargc == 2) 607 return parenlevel == 1 && strcmp(s, ")") == 0; 608 if (nargc != 3) 609 return 0; 610 while (*op->op_text) { 611 if (strcmp(s, op->op_text) == 0) 612 return op->op_type == BINOP; 613 op++; 614 } 615 return 0; 616} 617 618static int 619isrparenoperand(void) 620{ 621 char *s; 622 623 if (nargc == 1) 624 return 0; 625 s = *(t_wp + 1); 626 if (nargc == 2) 627 return parenlevel == 1 && strcmp(s, ")") == 0; 628 return 0; 629} 630 631/* atoi with error detection */ 632static int 633getn(const char *s) 634{ 635 char *p; 636 long r; 637 638 errno = 0; 639 r = strtol(s, &p, 10); 640 641 if (s == p) 642 error("%s: bad number", s); 643 644 if (errno != 0) 645 error((errno == EINVAL) ? "%s: bad number" : 646 "%s: out of range", s); 647 648 while (isspace((unsigned char)*p)) 649 p++; 650 651 if (*p) 652 error("%s: bad number", s); 653 654 return (int) r; 655} 656 657/* atoi with error detection and 64 bit range */ 658static intmax_t 659getq(const char *s) 660{ 661 char *p; 662 intmax_t r; 663 664 errno = 0; 665 r = strtoimax(s, &p, 10); 666 667 if (s == p) 668 error("%s: bad number", s); 669 670 if (errno != 0) 671 error((errno == EINVAL) ? "%s: bad number" : 672 "%s: out of range", s); 673 674 while (isspace((unsigned char)*p)) 675 p++; 676 677 if (*p) 678 error("%s: bad number", s); 679 680 return r; 681} 682 683static int 684intcmp (const char *s1, const char *s2) 685{ 686 intmax_t q1, q2; 687 688 689 q1 = getq(s1); 690 q2 = getq(s2); 691 692 if (q1 > q2) 693 return 1; 694 695 if (q1 < q2) 696 return -1; 697 698 return 0; 699} 700 701static int 702newerf (const char *f1, const char *f2, enum time_types t1, enum time_types t2) 703{ 704 struct stat b1, b2; 705 struct timespec *ts1, *ts2; 706 707 if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) 708 return 0; 709 710 switch (t1) { 711 case ATIME: ts1 = &b1.st_atim; break; 712 case BTIME: ts1 = &b1.st_birthtim; break; 713 case CTIME: ts1 = &b1.st_ctim; break; 714 default: ts1 = &b1.st_mtim; break; 715 } 716 717 switch (t2) { 718 case ATIME: ts2 = &b2.st_atim; break; 719 case BTIME: ts2 = &b2.st_birthtim; break; 720 case CTIME: ts2 = &b2.st_ctim; break; 721 default: ts2 = &b2.st_mtim; break; 722 } 723 724 if (ts1->tv_sec > ts2->tv_sec) 725 return 1; 726 if (ts1->tv_sec < ts2->tv_sec) 727 return 0; 728 729 return (ts1->tv_nsec > ts2->tv_nsec); 730} 731 732static int 733equalf (const char *f1, const char *f2) 734{ 735 struct stat b1, b2; 736 737 return (stat (f1, &b1) == 0 && 738 stat (f2, &b2) == 0 && 739 b1.st_dev == b2.st_dev && 740 b1.st_ino == b2.st_ino); 741} 742