1717a08b6SSheldon Hearn /* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 2717a08b6SSheldon Hearn 39ddb49cbSWarner Losh /*- 4717a08b6SSheldon Hearn * test(1); version 7-like -- author Erik Baalbergen 5717a08b6SSheldon Hearn * modified by Eric Gisin to be used as built-in. 6717a08b6SSheldon Hearn * modified by Arnold Robbins to add SVR3 compatibility 7717a08b6SSheldon Hearn * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 8717a08b6SSheldon Hearn * modified by J.T. Conklin for NetBSD. 94b88c807SRodney W. Grimes * 10717a08b6SSheldon Hearn * This program is in the Public Domain. 114b88c807SRodney W. Grimes */ 122fd7d6aaSJilles Tjoelker /* 132fd7d6aaSJilles Tjoelker * Important: This file is used both as a standalone program /bin/test and 142fd7d6aaSJilles Tjoelker * as a builtin for /bin/sh (#define SHELL). 152fd7d6aaSJilles Tjoelker */ 164b88c807SRodney W. Grimes 172749b141SDavid E. O'Brien #include <sys/cdefs.h> 182749b141SDavid E. O'Brien __FBSDID("$FreeBSD$"); 194b88c807SRodney W. Grimes 20717a08b6SSheldon Hearn #include <sys/types.h> 21007d3350SEivind Eklund #include <sys/stat.h> 224b88c807SRodney W. Grimes 234b88c807SRodney W. Grimes #include <ctype.h> 244b88c807SRodney W. Grimes #include <err.h> 254b88c807SRodney W. Grimes #include <errno.h> 2682ea3997SAndrey A. Chernov #include <inttypes.h> 276dca6515SKris Kennaway #include <limits.h> 2825e04004SAkinori MUSHA #include <stdarg.h> 294b88c807SRodney W. Grimes #include <stdio.h> 304b88c807SRodney W. Grimes #include <stdlib.h> 314b88c807SRodney W. Grimes #include <string.h> 324b88c807SRodney W. Grimes #include <unistd.h> 334b88c807SRodney W. Grimes 34d90c5c4aSAkinori MUSHA #ifdef SHELL 35d90c5c4aSAkinori MUSHA #define main testcmd 36d90c5c4aSAkinori MUSHA #include "bltin/bltin.h" 37d919a882SAkinori MUSHA #else 383d09cebfSAndrey A. Chernov #include <locale.h> 393d09cebfSAndrey A. Chernov 40033be9aeSAlfred Perlstein static void error(const char *, ...) __dead2 __printf0like(1, 2); 41d919a882SAkinori MUSHA 42d919a882SAkinori MUSHA static void 43d919a882SAkinori MUSHA error(const char *msg, ...) 44d919a882SAkinori MUSHA { 45d919a882SAkinori MUSHA va_list ap; 46d919a882SAkinori MUSHA va_start(ap, msg); 47d919a882SAkinori MUSHA verrx(2, msg, ap); 48d919a882SAkinori MUSHA /*NOTREACHED*/ 49d919a882SAkinori MUSHA va_end(ap); 50d919a882SAkinori MUSHA } 51d90c5c4aSAkinori MUSHA #endif 52d90c5c4aSAkinori MUSHA 53717a08b6SSheldon Hearn /* test(1) accepts the following grammar: 54717a08b6SSheldon Hearn oexpr ::= aexpr | aexpr "-o" oexpr ; 55717a08b6SSheldon Hearn aexpr ::= nexpr | nexpr "-a" aexpr ; 56717a08b6SSheldon Hearn nexpr ::= primary | "!" primary 57717a08b6SSheldon Hearn primary ::= unary-operator operand 58717a08b6SSheldon Hearn | operand binary-operator operand 59717a08b6SSheldon Hearn | operand 60717a08b6SSheldon Hearn | "(" oexpr ")" 61717a08b6SSheldon Hearn ; 62717a08b6SSheldon Hearn unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 63717a08b6SSheldon Hearn "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 644b88c807SRodney W. Grimes 65717a08b6SSheldon Hearn binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 66f19825afSJilles Tjoelker "-nt"|"-ot"|"-ef"; 67717a08b6SSheldon Hearn operand ::= <any legal UNIX file name> 684b88c807SRodney W. Grimes */ 69717a08b6SSheldon Hearn 70577ad9b2SJilles Tjoelker enum token_types { 71577ad9b2SJilles Tjoelker UNOP = 0x100, 72577ad9b2SJilles Tjoelker BINOP = 0x200, 73577ad9b2SJilles Tjoelker BUNOP = 0x300, 74577ad9b2SJilles Tjoelker BBINOP = 0x400, 75577ad9b2SJilles Tjoelker PAREN = 0x500 76577ad9b2SJilles Tjoelker }; 77577ad9b2SJilles Tjoelker 78717a08b6SSheldon Hearn enum token { 79717a08b6SSheldon Hearn EOI, 80577ad9b2SJilles Tjoelker OPERAND, 81577ad9b2SJilles Tjoelker FILRD = UNOP + 1, 82717a08b6SSheldon Hearn FILWR, 83717a08b6SSheldon Hearn FILEX, 84717a08b6SSheldon Hearn FILEXIST, 85717a08b6SSheldon Hearn FILREG, 86717a08b6SSheldon Hearn FILDIR, 87717a08b6SSheldon Hearn FILCDEV, 88717a08b6SSheldon Hearn FILBDEV, 89717a08b6SSheldon Hearn FILFIFO, 90717a08b6SSheldon Hearn FILSOCK, 91717a08b6SSheldon Hearn FILSYM, 92717a08b6SSheldon Hearn FILGZ, 93717a08b6SSheldon Hearn FILTT, 94717a08b6SSheldon Hearn FILSUID, 95717a08b6SSheldon Hearn FILSGID, 96717a08b6SSheldon Hearn FILSTCK, 97717a08b6SSheldon Hearn STREZ, 98717a08b6SSheldon Hearn STRNZ, 99577ad9b2SJilles Tjoelker FILUID, 100577ad9b2SJilles Tjoelker FILGID, 101577ad9b2SJilles Tjoelker FILNT = BINOP + 1, 102577ad9b2SJilles Tjoelker FILOT, 103577ad9b2SJilles Tjoelker FILEQ, 104717a08b6SSheldon Hearn STREQ, 105717a08b6SSheldon Hearn STRNE, 106717a08b6SSheldon Hearn STRLT, 107717a08b6SSheldon Hearn STRGT, 108717a08b6SSheldon Hearn INTEQ, 109717a08b6SSheldon Hearn INTNE, 110717a08b6SSheldon Hearn INTGE, 111717a08b6SSheldon Hearn INTGT, 112717a08b6SSheldon Hearn INTLE, 113717a08b6SSheldon Hearn INTLT, 114577ad9b2SJilles Tjoelker UNOT = BUNOP + 1, 115577ad9b2SJilles Tjoelker BAND = BBINOP + 1, 116717a08b6SSheldon Hearn BOR, 117577ad9b2SJilles Tjoelker LPAREN = PAREN + 1, 118577ad9b2SJilles Tjoelker RPAREN 1194b88c807SRodney W. Grimes }; 1204b88c807SRodney W. Grimes 121577ad9b2SJilles Tjoelker #define TOKEN_TYPE(token) ((token) & 0xff00) 1224b88c807SRodney W. Grimes 123f9d4afb4SEd Schouten static struct t_op { 124f19825afSJilles Tjoelker char op_text[4]; 125577ad9b2SJilles Tjoelker short op_num; 126717a08b6SSheldon Hearn } const ops [] = { 127577ad9b2SJilles Tjoelker {"-r", FILRD}, 128577ad9b2SJilles Tjoelker {"-w", FILWR}, 129577ad9b2SJilles Tjoelker {"-x", FILEX}, 130577ad9b2SJilles Tjoelker {"-e", FILEXIST}, 131577ad9b2SJilles Tjoelker {"-f", FILREG}, 132577ad9b2SJilles Tjoelker {"-d", FILDIR}, 133577ad9b2SJilles Tjoelker {"-c", FILCDEV}, 134577ad9b2SJilles Tjoelker {"-b", FILBDEV}, 135577ad9b2SJilles Tjoelker {"-p", FILFIFO}, 136577ad9b2SJilles Tjoelker {"-u", FILSUID}, 137577ad9b2SJilles Tjoelker {"-g", FILSGID}, 138577ad9b2SJilles Tjoelker {"-k", FILSTCK}, 139577ad9b2SJilles Tjoelker {"-s", FILGZ}, 140577ad9b2SJilles Tjoelker {"-t", FILTT}, 141577ad9b2SJilles Tjoelker {"-z", STREZ}, 142577ad9b2SJilles Tjoelker {"-n", STRNZ}, 143577ad9b2SJilles Tjoelker {"-h", FILSYM}, /* for backwards compat */ 144577ad9b2SJilles Tjoelker {"-O", FILUID}, 145577ad9b2SJilles Tjoelker {"-G", FILGID}, 146577ad9b2SJilles Tjoelker {"-L", FILSYM}, 147577ad9b2SJilles Tjoelker {"-S", FILSOCK}, 148577ad9b2SJilles Tjoelker {"=", STREQ}, 149577ad9b2SJilles Tjoelker {"==", STREQ}, 150577ad9b2SJilles Tjoelker {"!=", STRNE}, 151577ad9b2SJilles Tjoelker {"<", STRLT}, 152577ad9b2SJilles Tjoelker {">", STRGT}, 153577ad9b2SJilles Tjoelker {"-eq", INTEQ}, 154577ad9b2SJilles Tjoelker {"-ne", INTNE}, 155577ad9b2SJilles Tjoelker {"-ge", INTGE}, 156577ad9b2SJilles Tjoelker {"-gt", INTGT}, 157577ad9b2SJilles Tjoelker {"-le", INTLE}, 158577ad9b2SJilles Tjoelker {"-lt", INTLT}, 159577ad9b2SJilles Tjoelker {"-nt", FILNT}, 160577ad9b2SJilles Tjoelker {"-ot", FILOT}, 161577ad9b2SJilles Tjoelker {"-ef", FILEQ}, 162577ad9b2SJilles Tjoelker {"!", UNOT}, 163577ad9b2SJilles Tjoelker {"-a", BAND}, 164577ad9b2SJilles Tjoelker {"-o", BOR}, 165577ad9b2SJilles Tjoelker {"(", LPAREN}, 166577ad9b2SJilles Tjoelker {")", RPAREN}, 167577ad9b2SJilles Tjoelker {"", 0} 1684b88c807SRodney W. Grimes }; 1694b88c807SRodney W. Grimes 170f9d4afb4SEd Schouten static int nargc; 171f9d4afb4SEd Schouten static char **t_wp; 172f9d4afb4SEd Schouten static int parenlevel; 173717a08b6SSheldon Hearn 1745134c3f7SWarner Losh static int aexpr(enum token); 175c80ad859SJilles Tjoelker static int binop(enum token); 1765134c3f7SWarner Losh static int equalf(const char *, const char *); 1775134c3f7SWarner Losh static int filstat(char *, enum token); 1785134c3f7SWarner Losh static int getn(const char *); 17982ea3997SAndrey A. Chernov static intmax_t getq(const char *); 1805134c3f7SWarner Losh static int intcmp(const char *, const char *); 18100e8c94fSJilles Tjoelker static int isunopoperand(void); 18200e8c94fSJilles Tjoelker static int islparenoperand(void); 18300e8c94fSJilles Tjoelker static int isrparenoperand(void); 184f19825afSJilles Tjoelker static int newerf(const char *, const char *); 1855134c3f7SWarner Losh static int nexpr(enum token); 1865134c3f7SWarner Losh static int oexpr(enum token); 187f19825afSJilles Tjoelker static int olderf(const char *, const char *); 1885134c3f7SWarner Losh static int primary(enum token); 1895134c3f7SWarner Losh static void syntax(const char *, const char *); 1905134c3f7SWarner Losh static enum token t_lex(char *); 1914b88c807SRodney W. Grimes 1924b88c807SRodney W. Grimes int 1935134c3f7SWarner Losh main(int argc, char **argv) 1944b88c807SRodney W. Grimes { 1956c62b047SMaxim Konovalov int res; 1969abf3043SSheldon Hearn char *p; 1974b88c807SRodney W. Grimes 1980cf90cd1SJilles Tjoelker if ((p = strrchr(argv[0], '/')) == NULL) 1999abf3043SSheldon Hearn p = argv[0]; 2009abf3043SSheldon Hearn else 2019abf3043SSheldon Hearn p++; 2029abf3043SSheldon Hearn if (strcmp(p, "[") == 0) { 203bd90b9c7SAkinori MUSHA if (strcmp(argv[--argc], "]") != 0) 204d919a882SAkinori MUSHA error("missing ]"); 2054b88c807SRodney W. Grimes argv[argc] = NULL; 2064b88c807SRodney W. Grimes } 2074b88c807SRodney W. Grimes 2086c62b047SMaxim Konovalov /* no expression => false */ 2096c62b047SMaxim Konovalov if (--argc <= 0) 2106c62b047SMaxim Konovalov return 1; 2116c62b047SMaxim Konovalov 2123d09cebfSAndrey A. Chernov #ifndef SHELL 2133d09cebfSAndrey A. Chernov (void)setlocale(LC_CTYPE, ""); 2143d09cebfSAndrey A. Chernov #endif 2156c62b047SMaxim Konovalov nargc = argc; 216717a08b6SSheldon Hearn t_wp = &argv[1]; 21700e8c94fSJilles Tjoelker parenlevel = 0; 21800e8c94fSJilles Tjoelker if (nargc == 4 && strcmp(*t_wp, "!") == 0) { 21900e8c94fSJilles Tjoelker /* Things like ! "" -o x do not fit in the normal grammar. */ 22000e8c94fSJilles Tjoelker --nargc; 22100e8c94fSJilles Tjoelker ++t_wp; 22200e8c94fSJilles Tjoelker res = oexpr(t_lex(*t_wp)); 22300e8c94fSJilles Tjoelker } else 224717a08b6SSheldon Hearn res = !oexpr(t_lex(*t_wp)); 225717a08b6SSheldon Hearn 2266c62b047SMaxim Konovalov if (--nargc > 0) 227717a08b6SSheldon Hearn syntax(*t_wp, "unexpected operator"); 228717a08b6SSheldon Hearn 229717a08b6SSheldon Hearn return res; 2304b88c807SRodney W. Grimes } 231717a08b6SSheldon Hearn 232717a08b6SSheldon Hearn static void 2335134c3f7SWarner Losh syntax(const char *op, const char *msg) 234717a08b6SSheldon Hearn { 235717a08b6SSheldon Hearn 236717a08b6SSheldon Hearn if (op && *op) 237d919a882SAkinori MUSHA error("%s: %s", op, msg); 238717a08b6SSheldon Hearn else 239d919a882SAkinori MUSHA error("%s", msg); 2404b88c807SRodney W. Grimes } 241717a08b6SSheldon Hearn 242717a08b6SSheldon Hearn static int 2435134c3f7SWarner Losh oexpr(enum token n) 244717a08b6SSheldon Hearn { 245717a08b6SSheldon Hearn int res; 246717a08b6SSheldon Hearn 247717a08b6SSheldon Hearn res = aexpr(n); 2486c62b047SMaxim Konovalov if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) 2496c62b047SMaxim Konovalov return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || 2506c62b047SMaxim Konovalov res; 251717a08b6SSheldon Hearn t_wp--; 2526c62b047SMaxim Konovalov nargc++; 253717a08b6SSheldon Hearn return res; 2544b88c807SRodney W. Grimes } 255717a08b6SSheldon Hearn 256717a08b6SSheldon Hearn static int 2575134c3f7SWarner Losh aexpr(enum token n) 258717a08b6SSheldon Hearn { 259717a08b6SSheldon Hearn int res; 260717a08b6SSheldon Hearn 261717a08b6SSheldon Hearn res = nexpr(n); 2626c62b047SMaxim Konovalov if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) 2636c62b047SMaxim Konovalov return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && 2646c62b047SMaxim Konovalov res; 265717a08b6SSheldon Hearn t_wp--; 2666c62b047SMaxim Konovalov nargc++; 267717a08b6SSheldon Hearn return res; 268717a08b6SSheldon Hearn } 269717a08b6SSheldon Hearn 270717a08b6SSheldon Hearn static int 2715134c3f7SWarner Losh nexpr(enum token n) 272717a08b6SSheldon Hearn { 273717a08b6SSheldon Hearn if (n == UNOT) 2746c62b047SMaxim Konovalov return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); 275717a08b6SSheldon Hearn return primary(n); 276717a08b6SSheldon Hearn } 277717a08b6SSheldon Hearn 278717a08b6SSheldon Hearn static int 2795134c3f7SWarner Losh primary(enum token n) 280717a08b6SSheldon Hearn { 281717a08b6SSheldon Hearn enum token nn; 282717a08b6SSheldon Hearn int res; 283717a08b6SSheldon Hearn 284717a08b6SSheldon Hearn if (n == EOI) 285717a08b6SSheldon Hearn return 0; /* missing expression */ 286717a08b6SSheldon Hearn if (n == LPAREN) { 28700e8c94fSJilles Tjoelker parenlevel++; 2886c62b047SMaxim Konovalov if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == 28900e8c94fSJilles Tjoelker RPAREN) { 29000e8c94fSJilles Tjoelker parenlevel--; 291717a08b6SSheldon Hearn return 0; /* missing expression */ 29200e8c94fSJilles Tjoelker } 293717a08b6SSheldon Hearn res = oexpr(nn); 2946c62b047SMaxim Konovalov if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) 295717a08b6SSheldon Hearn syntax(NULL, "closing paren expected"); 29600e8c94fSJilles Tjoelker parenlevel--; 297717a08b6SSheldon Hearn return res; 298717a08b6SSheldon Hearn } 299577ad9b2SJilles Tjoelker if (TOKEN_TYPE(n) == UNOP) { 300717a08b6SSheldon Hearn /* unary expression */ 3016c62b047SMaxim Konovalov if (--nargc == 0) 302577ad9b2SJilles Tjoelker syntax(NULL, "argument expected"); /* impossible */ 303717a08b6SSheldon Hearn switch (n) { 304717a08b6SSheldon Hearn case STREZ: 3056c62b047SMaxim Konovalov return strlen(*++t_wp) == 0; 306717a08b6SSheldon Hearn case STRNZ: 3076c62b047SMaxim Konovalov return strlen(*++t_wp) != 0; 308717a08b6SSheldon Hearn case FILTT: 3096c62b047SMaxim Konovalov return isatty(getn(*++t_wp)); 3104b88c807SRodney W. Grimes default: 3116c62b047SMaxim Konovalov return filstat(*++t_wp, n); 312717a08b6SSheldon Hearn } 3134b88c807SRodney W. Grimes } 3144b88c807SRodney W. Grimes 315c80ad859SJilles Tjoelker nn = t_lex(nargc > 0 ? t_wp[1] : NULL); 316c80ad859SJilles Tjoelker if (TOKEN_TYPE(nn) == BINOP) 317c80ad859SJilles Tjoelker return binop(nn); 318717a08b6SSheldon Hearn 319717a08b6SSheldon Hearn return strlen(*t_wp) > 0; 3204b88c807SRodney W. Grimes } 3214b88c807SRodney W. Grimes 3224b88c807SRodney W. Grimes static int 323c80ad859SJilles Tjoelker binop(enum token n) 3244b88c807SRodney W. Grimes { 325577ad9b2SJilles Tjoelker const char *opnd1, *op, *opnd2; 3264b88c807SRodney W. Grimes 327717a08b6SSheldon Hearn opnd1 = *t_wp; 328c80ad859SJilles Tjoelker op = nargc > 0 ? (--nargc, *++t_wp) : NULL; 3294b88c807SRodney W. Grimes 3306c62b047SMaxim Konovalov if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) 331577ad9b2SJilles Tjoelker syntax(op, "argument expected"); 3324b88c807SRodney W. Grimes 333577ad9b2SJilles Tjoelker switch (n) { 3344b88c807SRodney W. Grimes case STREQ: 335717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) == 0; 3364b88c807SRodney W. Grimes case STRNE: 337717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) != 0; 338717a08b6SSheldon Hearn case STRLT: 339717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) < 0; 340717a08b6SSheldon Hearn case STRGT: 341717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) > 0; 342717a08b6SSheldon Hearn case INTEQ: 343de96f240SStefan Eßer return intcmp(opnd1, opnd2) == 0; 344717a08b6SSheldon Hearn case INTNE: 345de96f240SStefan Eßer return intcmp(opnd1, opnd2) != 0; 346717a08b6SSheldon Hearn case INTGE: 347de96f240SStefan Eßer return intcmp(opnd1, opnd2) >= 0; 348717a08b6SSheldon Hearn case INTGT: 349de96f240SStefan Eßer return intcmp(opnd1, opnd2) > 0; 350717a08b6SSheldon Hearn case INTLE: 351de96f240SStefan Eßer return intcmp(opnd1, opnd2) <= 0; 352717a08b6SSheldon Hearn case INTLT: 353de96f240SStefan Eßer return intcmp(opnd1, opnd2) < 0; 354f19825afSJilles Tjoelker case FILNT: 355f19825afSJilles Tjoelker return newerf (opnd1, opnd2); 356f19825afSJilles Tjoelker case FILOT: 357f19825afSJilles Tjoelker return olderf (opnd1, opnd2); 358717a08b6SSheldon Hearn case FILEQ: 359717a08b6SSheldon Hearn return equalf (opnd1, opnd2); 360717a08b6SSheldon Hearn default: 361717a08b6SSheldon Hearn abort(); 362717a08b6SSheldon Hearn /* NOTREACHED */ 363717a08b6SSheldon Hearn } 364717a08b6SSheldon Hearn } 365717a08b6SSheldon Hearn 366717a08b6SSheldon Hearn static int 3675134c3f7SWarner Losh filstat(char *nm, enum token mode) 368717a08b6SSheldon Hearn { 369717a08b6SSheldon Hearn struct stat s; 370717a08b6SSheldon Hearn 371717a08b6SSheldon Hearn if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 372717a08b6SSheldon Hearn return 0; 373717a08b6SSheldon Hearn 374717a08b6SSheldon Hearn switch (mode) { 375717a08b6SSheldon Hearn case FILRD: 37689a3a364SMaxim Konovalov return (eaccess(nm, R_OK) == 0); 377717a08b6SSheldon Hearn case FILWR: 37889a3a364SMaxim Konovalov return (eaccess(nm, W_OK) == 0); 379717a08b6SSheldon Hearn case FILEX: 38089a3a364SMaxim Konovalov /* XXX work around eaccess(2) false positives for superuser */ 38189a3a364SMaxim Konovalov if (eaccess(nm, X_OK) != 0) 382eb5e5558SBrian Feldman return 0; 38389a3a364SMaxim Konovalov if (S_ISDIR(s.st_mode) || geteuid() != 0) 384eb5e5558SBrian Feldman return 1; 385d2fed466SBrian Feldman return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 386717a08b6SSheldon Hearn case FILEXIST: 38789a3a364SMaxim Konovalov return (eaccess(nm, F_OK) == 0); 388717a08b6SSheldon Hearn case FILREG: 389717a08b6SSheldon Hearn return S_ISREG(s.st_mode); 390717a08b6SSheldon Hearn case FILDIR: 391717a08b6SSheldon Hearn return S_ISDIR(s.st_mode); 392717a08b6SSheldon Hearn case FILCDEV: 393717a08b6SSheldon Hearn return S_ISCHR(s.st_mode); 394717a08b6SSheldon Hearn case FILBDEV: 395717a08b6SSheldon Hearn return S_ISBLK(s.st_mode); 396717a08b6SSheldon Hearn case FILFIFO: 397717a08b6SSheldon Hearn return S_ISFIFO(s.st_mode); 398717a08b6SSheldon Hearn case FILSOCK: 399717a08b6SSheldon Hearn return S_ISSOCK(s.st_mode); 400717a08b6SSheldon Hearn case FILSYM: 401717a08b6SSheldon Hearn return S_ISLNK(s.st_mode); 402717a08b6SSheldon Hearn case FILSUID: 403717a08b6SSheldon Hearn return (s.st_mode & S_ISUID) != 0; 404717a08b6SSheldon Hearn case FILSGID: 405717a08b6SSheldon Hearn return (s.st_mode & S_ISGID) != 0; 406717a08b6SSheldon Hearn case FILSTCK: 407717a08b6SSheldon Hearn return (s.st_mode & S_ISVTX) != 0; 408717a08b6SSheldon Hearn case FILGZ: 409717a08b6SSheldon Hearn return s.st_size > (off_t)0; 410717a08b6SSheldon Hearn case FILUID: 411717a08b6SSheldon Hearn return s.st_uid == geteuid(); 412717a08b6SSheldon Hearn case FILGID: 413717a08b6SSheldon Hearn return s.st_gid == getegid(); 414717a08b6SSheldon Hearn default: 415717a08b6SSheldon Hearn return 1; 416717a08b6SSheldon Hearn } 417717a08b6SSheldon Hearn } 418717a08b6SSheldon Hearn 419717a08b6SSheldon Hearn static enum token 4205134c3f7SWarner Losh t_lex(char *s) 421717a08b6SSheldon Hearn { 422717a08b6SSheldon Hearn struct t_op const *op = ops; 423717a08b6SSheldon Hearn 424717a08b6SSheldon Hearn if (s == 0) { 425717a08b6SSheldon Hearn return EOI; 426717a08b6SSheldon Hearn } 427ba22f6c6SJilles Tjoelker while (*op->op_text) { 428717a08b6SSheldon Hearn if (strcmp(s, op->op_text) == 0) { 429577ad9b2SJilles Tjoelker if (((TOKEN_TYPE(op->op_num) == UNOP || 430577ad9b2SJilles Tjoelker TOKEN_TYPE(op->op_num) == BUNOP) 43100e8c94fSJilles Tjoelker && isunopoperand()) || 43200e8c94fSJilles Tjoelker (op->op_num == LPAREN && islparenoperand()) || 43300e8c94fSJilles Tjoelker (op->op_num == RPAREN && isrparenoperand())) 4344b88c807SRodney W. Grimes break; 435717a08b6SSheldon Hearn return op->op_num; 4364b88c807SRodney W. Grimes } 437717a08b6SSheldon Hearn op++; 438717a08b6SSheldon Hearn } 439717a08b6SSheldon Hearn return OPERAND; 4404b88c807SRodney W. Grimes } 4414b88c807SRodney W. Grimes 4424b88c807SRodney W. Grimes static int 44300e8c94fSJilles Tjoelker isunopoperand(void) 4444b88c807SRodney W. Grimes { 445717a08b6SSheldon Hearn struct t_op const *op = ops; 446717a08b6SSheldon Hearn char *s; 447717a08b6SSheldon Hearn char *t; 4484b88c807SRodney W. Grimes 4496c62b047SMaxim Konovalov if (nargc == 1) 450717a08b6SSheldon Hearn return 1; 4516c62b047SMaxim Konovalov s = *(t_wp + 1); 45200e8c94fSJilles Tjoelker if (nargc == 2) 45300e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 4546c62b047SMaxim Konovalov t = *(t_wp + 2); 455ba22f6c6SJilles Tjoelker while (*op->op_text) { 456717a08b6SSheldon Hearn if (strcmp(s, op->op_text) == 0) 457577ad9b2SJilles Tjoelker return TOKEN_TYPE(op->op_num) == BINOP && 45800e8c94fSJilles Tjoelker (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); 459717a08b6SSheldon Hearn op++; 460717a08b6SSheldon Hearn } 461717a08b6SSheldon Hearn return 0; 4624b88c807SRodney W. Grimes } 4634b88c807SRodney W. Grimes 46400e8c94fSJilles Tjoelker static int 46500e8c94fSJilles Tjoelker islparenoperand(void) 46600e8c94fSJilles Tjoelker { 46700e8c94fSJilles Tjoelker struct t_op const *op = ops; 46800e8c94fSJilles Tjoelker char *s; 46900e8c94fSJilles Tjoelker 47000e8c94fSJilles Tjoelker if (nargc == 1) 47100e8c94fSJilles Tjoelker return 1; 47200e8c94fSJilles Tjoelker s = *(t_wp + 1); 47300e8c94fSJilles Tjoelker if (nargc == 2) 47400e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 47500e8c94fSJilles Tjoelker if (nargc != 3) 47600e8c94fSJilles Tjoelker return 0; 477ba22f6c6SJilles Tjoelker while (*op->op_text) { 47800e8c94fSJilles Tjoelker if (strcmp(s, op->op_text) == 0) 479577ad9b2SJilles Tjoelker return TOKEN_TYPE(op->op_num) == BINOP; 48000e8c94fSJilles Tjoelker op++; 48100e8c94fSJilles Tjoelker } 48200e8c94fSJilles Tjoelker return 0; 48300e8c94fSJilles Tjoelker } 48400e8c94fSJilles Tjoelker 48500e8c94fSJilles Tjoelker static int 48600e8c94fSJilles Tjoelker isrparenoperand(void) 48700e8c94fSJilles Tjoelker { 48800e8c94fSJilles Tjoelker char *s; 48900e8c94fSJilles Tjoelker 49000e8c94fSJilles Tjoelker if (nargc == 1) 49100e8c94fSJilles Tjoelker return 0; 49200e8c94fSJilles Tjoelker s = *(t_wp + 1); 49300e8c94fSJilles Tjoelker if (nargc == 2) 49400e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 49500e8c94fSJilles Tjoelker return 0; 49600e8c94fSJilles Tjoelker } 49700e8c94fSJilles Tjoelker 498717a08b6SSheldon Hearn /* atoi with error detection */ 4994b88c807SRodney W. Grimes static int 5005134c3f7SWarner Losh getn(const char *s) 5014b88c807SRodney W. Grimes { 502717a08b6SSheldon Hearn char *p; 503717a08b6SSheldon Hearn long r; 5044b88c807SRodney W. Grimes 5054b88c807SRodney W. Grimes errno = 0; 506717a08b6SSheldon Hearn r = strtol(s, &p, 10); 507717a08b6SSheldon Hearn 5083d09cebfSAndrey A. Chernov if (s == p) 5093d09cebfSAndrey A. Chernov error("%s: bad number", s); 5103d09cebfSAndrey A. Chernov 511717a08b6SSheldon Hearn if (errno != 0) 5129ea42c8eSAndrey A. Chernov error((errno == EINVAL) ? "%s: bad number" : 5139ea42c8eSAndrey A. Chernov "%s: out of range", s); 514717a08b6SSheldon Hearn 515717a08b6SSheldon Hearn while (isspace((unsigned char)*p)) 516717a08b6SSheldon Hearn p++; 517717a08b6SSheldon Hearn 518717a08b6SSheldon Hearn if (*p) 519d919a882SAkinori MUSHA error("%s: bad number", s); 520717a08b6SSheldon Hearn 521717a08b6SSheldon Hearn return (int) r; 5224b88c807SRodney W. Grimes } 5234b88c807SRodney W. Grimes 524de96f240SStefan Eßer /* atoi with error detection and 64 bit range */ 52582ea3997SAndrey A. Chernov static intmax_t 5265134c3f7SWarner Losh getq(const char *s) 527de96f240SStefan Eßer { 528de96f240SStefan Eßer char *p; 52982ea3997SAndrey A. Chernov intmax_t r; 530de96f240SStefan Eßer 531de96f240SStefan Eßer errno = 0; 53282ea3997SAndrey A. Chernov r = strtoimax(s, &p, 10); 533de96f240SStefan Eßer 5343d09cebfSAndrey A. Chernov if (s == p) 5353d09cebfSAndrey A. Chernov error("%s: bad number", s); 5363d09cebfSAndrey A. Chernov 537de96f240SStefan Eßer if (errno != 0) 5389ea42c8eSAndrey A. Chernov error((errno == EINVAL) ? "%s: bad number" : 5399ea42c8eSAndrey A. Chernov "%s: out of range", s); 540de96f240SStefan Eßer 541de96f240SStefan Eßer while (isspace((unsigned char)*p)) 542de96f240SStefan Eßer p++; 543de96f240SStefan Eßer 544de96f240SStefan Eßer if (*p) 545d919a882SAkinori MUSHA error("%s: bad number", s); 546de96f240SStefan Eßer 547de96f240SStefan Eßer return r; 548de96f240SStefan Eßer } 549de96f240SStefan Eßer 550de96f240SStefan Eßer static int 5515134c3f7SWarner Losh intcmp (const char *s1, const char *s2) 552de96f240SStefan Eßer { 55382ea3997SAndrey A. Chernov intmax_t q1, q2; 554de96f240SStefan Eßer 555de96f240SStefan Eßer 556de96f240SStefan Eßer q1 = getq(s1); 557de96f240SStefan Eßer q2 = getq(s2); 558de96f240SStefan Eßer 559de96f240SStefan Eßer if (q1 > q2) 560de96f240SStefan Eßer return 1; 561de96f240SStefan Eßer 562de96f240SStefan Eßer if (q1 < q2) 563de96f240SStefan Eßer return -1; 564de96f240SStefan Eßer 565de96f240SStefan Eßer return 0; 566de96f240SStefan Eßer } 567de96f240SStefan Eßer 568717a08b6SSheldon Hearn static int 569f19825afSJilles Tjoelker newerf (const char *f1, const char *f2) 5704b88c807SRodney W. Grimes { 571717a08b6SSheldon Hearn struct stat b1, b2; 5724b88c807SRodney W. Grimes 5736576952cSDavid Malone if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) 5746576952cSDavid Malone return 0; 5756576952cSDavid Malone 576f19825afSJilles Tjoelker if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) 577293beebcSPeter Jeremy return 1; 578f19825afSJilles Tjoelker if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) 579293beebcSPeter Jeremy return 0; 580293beebcSPeter Jeremy 581f19825afSJilles Tjoelker return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); 582f19825afSJilles Tjoelker } 583f19825afSJilles Tjoelker 584f19825afSJilles Tjoelker static int 585f19825afSJilles Tjoelker olderf (const char *f1, const char *f2) 586f19825afSJilles Tjoelker { 587f19825afSJilles Tjoelker return (newerf(f2, f1)); 588717a08b6SSheldon Hearn } 589717a08b6SSheldon Hearn 590717a08b6SSheldon Hearn static int 5915134c3f7SWarner Losh equalf (const char *f1, const char *f2) 592717a08b6SSheldon Hearn { 593717a08b6SSheldon Hearn struct stat b1, b2; 594717a08b6SSheldon Hearn 595717a08b6SSheldon Hearn return (stat (f1, &b1) == 0 && 596717a08b6SSheldon Hearn stat (f2, &b2) == 0 && 597717a08b6SSheldon Hearn b1.st_dev == b2.st_dev && 598717a08b6SSheldon Hearn b1.st_ino == b2.st_ino); 5994b88c807SRodney W. Grimes } 600