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); 1755134c3f7SWarner Losh static int binop(void); 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 315577ad9b2SJilles Tjoelker if (TOKEN_TYPE(t_lex(nargc > 0 ? t_wp[1] : NULL)) == BINOP) 316717a08b6SSheldon Hearn return binop(); 317717a08b6SSheldon Hearn 318717a08b6SSheldon Hearn return strlen(*t_wp) > 0; 3194b88c807SRodney W. Grimes } 3204b88c807SRodney W. Grimes 3214b88c807SRodney W. Grimes static int 3225134c3f7SWarner Losh binop(void) 3234b88c807SRodney W. Grimes { 324577ad9b2SJilles Tjoelker const char *opnd1, *op, *opnd2; 325577ad9b2SJilles Tjoelker enum token n; 3264b88c807SRodney W. Grimes 327717a08b6SSheldon Hearn opnd1 = *t_wp; 328577ad9b2SJilles Tjoelker op = nargc > 0 ? t_wp[1] : NULL; 329577ad9b2SJilles Tjoelker n = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL); 3304b88c807SRodney W. Grimes 3316c62b047SMaxim Konovalov if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) 332577ad9b2SJilles Tjoelker syntax(op, "argument expected"); 3334b88c807SRodney W. Grimes 334577ad9b2SJilles Tjoelker switch (n) { 3354b88c807SRodney W. Grimes case STREQ: 336717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) == 0; 3374b88c807SRodney W. Grimes case STRNE: 338717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) != 0; 339717a08b6SSheldon Hearn case STRLT: 340717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) < 0; 341717a08b6SSheldon Hearn case STRGT: 342717a08b6SSheldon Hearn return strcmp(opnd1, opnd2) > 0; 343717a08b6SSheldon Hearn case INTEQ: 344de96f240SStefan Eßer return intcmp(opnd1, opnd2) == 0; 345717a08b6SSheldon Hearn case INTNE: 346de96f240SStefan Eßer return intcmp(opnd1, opnd2) != 0; 347717a08b6SSheldon Hearn case INTGE: 348de96f240SStefan Eßer return intcmp(opnd1, opnd2) >= 0; 349717a08b6SSheldon Hearn case INTGT: 350de96f240SStefan Eßer return intcmp(opnd1, opnd2) > 0; 351717a08b6SSheldon Hearn case INTLE: 352de96f240SStefan Eßer return intcmp(opnd1, opnd2) <= 0; 353717a08b6SSheldon Hearn case INTLT: 354de96f240SStefan Eßer return intcmp(opnd1, opnd2) < 0; 355f19825afSJilles Tjoelker case FILNT: 356f19825afSJilles Tjoelker return newerf (opnd1, opnd2); 357f19825afSJilles Tjoelker case FILOT: 358f19825afSJilles Tjoelker return olderf (opnd1, opnd2); 359717a08b6SSheldon Hearn case FILEQ: 360717a08b6SSheldon Hearn return equalf (opnd1, opnd2); 361717a08b6SSheldon Hearn default: 362717a08b6SSheldon Hearn abort(); 363717a08b6SSheldon Hearn /* NOTREACHED */ 364717a08b6SSheldon Hearn } 365717a08b6SSheldon Hearn } 366717a08b6SSheldon Hearn 367717a08b6SSheldon Hearn static int 3685134c3f7SWarner Losh filstat(char *nm, enum token mode) 369717a08b6SSheldon Hearn { 370717a08b6SSheldon Hearn struct stat s; 371717a08b6SSheldon Hearn 372717a08b6SSheldon Hearn if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 373717a08b6SSheldon Hearn return 0; 374717a08b6SSheldon Hearn 375717a08b6SSheldon Hearn switch (mode) { 376717a08b6SSheldon Hearn case FILRD: 37789a3a364SMaxim Konovalov return (eaccess(nm, R_OK) == 0); 378717a08b6SSheldon Hearn case FILWR: 37989a3a364SMaxim Konovalov return (eaccess(nm, W_OK) == 0); 380717a08b6SSheldon Hearn case FILEX: 38189a3a364SMaxim Konovalov /* XXX work around eaccess(2) false positives for superuser */ 38289a3a364SMaxim Konovalov if (eaccess(nm, X_OK) != 0) 383eb5e5558SBrian Feldman return 0; 38489a3a364SMaxim Konovalov if (S_ISDIR(s.st_mode) || geteuid() != 0) 385eb5e5558SBrian Feldman return 1; 386d2fed466SBrian Feldman return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 387717a08b6SSheldon Hearn case FILEXIST: 38889a3a364SMaxim Konovalov return (eaccess(nm, F_OK) == 0); 389717a08b6SSheldon Hearn case FILREG: 390717a08b6SSheldon Hearn return S_ISREG(s.st_mode); 391717a08b6SSheldon Hearn case FILDIR: 392717a08b6SSheldon Hearn return S_ISDIR(s.st_mode); 393717a08b6SSheldon Hearn case FILCDEV: 394717a08b6SSheldon Hearn return S_ISCHR(s.st_mode); 395717a08b6SSheldon Hearn case FILBDEV: 396717a08b6SSheldon Hearn return S_ISBLK(s.st_mode); 397717a08b6SSheldon Hearn case FILFIFO: 398717a08b6SSheldon Hearn return S_ISFIFO(s.st_mode); 399717a08b6SSheldon Hearn case FILSOCK: 400717a08b6SSheldon Hearn return S_ISSOCK(s.st_mode); 401717a08b6SSheldon Hearn case FILSYM: 402717a08b6SSheldon Hearn return S_ISLNK(s.st_mode); 403717a08b6SSheldon Hearn case FILSUID: 404717a08b6SSheldon Hearn return (s.st_mode & S_ISUID) != 0; 405717a08b6SSheldon Hearn case FILSGID: 406717a08b6SSheldon Hearn return (s.st_mode & S_ISGID) != 0; 407717a08b6SSheldon Hearn case FILSTCK: 408717a08b6SSheldon Hearn return (s.st_mode & S_ISVTX) != 0; 409717a08b6SSheldon Hearn case FILGZ: 410717a08b6SSheldon Hearn return s.st_size > (off_t)0; 411717a08b6SSheldon Hearn case FILUID: 412717a08b6SSheldon Hearn return s.st_uid == geteuid(); 413717a08b6SSheldon Hearn case FILGID: 414717a08b6SSheldon Hearn return s.st_gid == getegid(); 415717a08b6SSheldon Hearn default: 416717a08b6SSheldon Hearn return 1; 417717a08b6SSheldon Hearn } 418717a08b6SSheldon Hearn } 419717a08b6SSheldon Hearn 420717a08b6SSheldon Hearn static enum token 4215134c3f7SWarner Losh t_lex(char *s) 422717a08b6SSheldon Hearn { 423717a08b6SSheldon Hearn struct t_op const *op = ops; 424717a08b6SSheldon Hearn 425717a08b6SSheldon Hearn if (s == 0) { 426717a08b6SSheldon Hearn return EOI; 427717a08b6SSheldon Hearn } 428ba22f6c6SJilles Tjoelker while (*op->op_text) { 429717a08b6SSheldon Hearn if (strcmp(s, op->op_text) == 0) { 430577ad9b2SJilles Tjoelker if (((TOKEN_TYPE(op->op_num) == UNOP || 431577ad9b2SJilles Tjoelker TOKEN_TYPE(op->op_num) == BUNOP) 43200e8c94fSJilles Tjoelker && isunopoperand()) || 43300e8c94fSJilles Tjoelker (op->op_num == LPAREN && islparenoperand()) || 43400e8c94fSJilles Tjoelker (op->op_num == RPAREN && isrparenoperand())) 4354b88c807SRodney W. Grimes break; 436717a08b6SSheldon Hearn return op->op_num; 4374b88c807SRodney W. Grimes } 438717a08b6SSheldon Hearn op++; 439717a08b6SSheldon Hearn } 440717a08b6SSheldon Hearn return OPERAND; 4414b88c807SRodney W. Grimes } 4424b88c807SRodney W. Grimes 4434b88c807SRodney W. Grimes static int 44400e8c94fSJilles Tjoelker isunopoperand(void) 4454b88c807SRodney W. Grimes { 446717a08b6SSheldon Hearn struct t_op const *op = ops; 447717a08b6SSheldon Hearn char *s; 448717a08b6SSheldon Hearn char *t; 4494b88c807SRodney W. Grimes 4506c62b047SMaxim Konovalov if (nargc == 1) 451717a08b6SSheldon Hearn return 1; 4526c62b047SMaxim Konovalov s = *(t_wp + 1); 45300e8c94fSJilles Tjoelker if (nargc == 2) 45400e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 4556c62b047SMaxim Konovalov t = *(t_wp + 2); 456ba22f6c6SJilles Tjoelker while (*op->op_text) { 457717a08b6SSheldon Hearn if (strcmp(s, op->op_text) == 0) 458577ad9b2SJilles Tjoelker return TOKEN_TYPE(op->op_num) == BINOP && 45900e8c94fSJilles Tjoelker (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); 460717a08b6SSheldon Hearn op++; 461717a08b6SSheldon Hearn } 462717a08b6SSheldon Hearn return 0; 4634b88c807SRodney W. Grimes } 4644b88c807SRodney W. Grimes 46500e8c94fSJilles Tjoelker static int 46600e8c94fSJilles Tjoelker islparenoperand(void) 46700e8c94fSJilles Tjoelker { 46800e8c94fSJilles Tjoelker struct t_op const *op = ops; 46900e8c94fSJilles Tjoelker char *s; 47000e8c94fSJilles Tjoelker 47100e8c94fSJilles Tjoelker if (nargc == 1) 47200e8c94fSJilles Tjoelker return 1; 47300e8c94fSJilles Tjoelker s = *(t_wp + 1); 47400e8c94fSJilles Tjoelker if (nargc == 2) 47500e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 47600e8c94fSJilles Tjoelker if (nargc != 3) 47700e8c94fSJilles Tjoelker return 0; 478ba22f6c6SJilles Tjoelker while (*op->op_text) { 47900e8c94fSJilles Tjoelker if (strcmp(s, op->op_text) == 0) 480577ad9b2SJilles Tjoelker return TOKEN_TYPE(op->op_num) == BINOP; 48100e8c94fSJilles Tjoelker op++; 48200e8c94fSJilles Tjoelker } 48300e8c94fSJilles Tjoelker return 0; 48400e8c94fSJilles Tjoelker } 48500e8c94fSJilles Tjoelker 48600e8c94fSJilles Tjoelker static int 48700e8c94fSJilles Tjoelker isrparenoperand(void) 48800e8c94fSJilles Tjoelker { 48900e8c94fSJilles Tjoelker char *s; 49000e8c94fSJilles Tjoelker 49100e8c94fSJilles Tjoelker if (nargc == 1) 49200e8c94fSJilles Tjoelker return 0; 49300e8c94fSJilles Tjoelker s = *(t_wp + 1); 49400e8c94fSJilles Tjoelker if (nargc == 2) 49500e8c94fSJilles Tjoelker return parenlevel == 1 && strcmp(s, ")") == 0; 49600e8c94fSJilles Tjoelker return 0; 49700e8c94fSJilles Tjoelker } 49800e8c94fSJilles Tjoelker 499717a08b6SSheldon Hearn /* atoi with error detection */ 5004b88c807SRodney W. Grimes static int 5015134c3f7SWarner Losh getn(const char *s) 5024b88c807SRodney W. Grimes { 503717a08b6SSheldon Hearn char *p; 504717a08b6SSheldon Hearn long r; 5054b88c807SRodney W. Grimes 5064b88c807SRodney W. Grimes errno = 0; 507717a08b6SSheldon Hearn r = strtol(s, &p, 10); 508717a08b6SSheldon Hearn 5093d09cebfSAndrey A. Chernov if (s == p) 5103d09cebfSAndrey A. Chernov error("%s: bad number", s); 5113d09cebfSAndrey A. Chernov 512717a08b6SSheldon Hearn if (errno != 0) 5139ea42c8eSAndrey A. Chernov error((errno == EINVAL) ? "%s: bad number" : 5149ea42c8eSAndrey A. Chernov "%s: out of range", s); 515717a08b6SSheldon Hearn 516717a08b6SSheldon Hearn while (isspace((unsigned char)*p)) 517717a08b6SSheldon Hearn p++; 518717a08b6SSheldon Hearn 519717a08b6SSheldon Hearn if (*p) 520d919a882SAkinori MUSHA error("%s: bad number", s); 521717a08b6SSheldon Hearn 522717a08b6SSheldon Hearn return (int) r; 5234b88c807SRodney W. Grimes } 5244b88c807SRodney W. Grimes 525de96f240SStefan Eßer /* atoi with error detection and 64 bit range */ 52682ea3997SAndrey A. Chernov static intmax_t 5275134c3f7SWarner Losh getq(const char *s) 528de96f240SStefan Eßer { 529de96f240SStefan Eßer char *p; 53082ea3997SAndrey A. Chernov intmax_t r; 531de96f240SStefan Eßer 532de96f240SStefan Eßer errno = 0; 53382ea3997SAndrey A. Chernov r = strtoimax(s, &p, 10); 534de96f240SStefan Eßer 5353d09cebfSAndrey A. Chernov if (s == p) 5363d09cebfSAndrey A. Chernov error("%s: bad number", s); 5373d09cebfSAndrey A. Chernov 538de96f240SStefan Eßer if (errno != 0) 5399ea42c8eSAndrey A. Chernov error((errno == EINVAL) ? "%s: bad number" : 5409ea42c8eSAndrey A. Chernov "%s: out of range", s); 541de96f240SStefan Eßer 542de96f240SStefan Eßer while (isspace((unsigned char)*p)) 543de96f240SStefan Eßer p++; 544de96f240SStefan Eßer 545de96f240SStefan Eßer if (*p) 546d919a882SAkinori MUSHA error("%s: bad number", s); 547de96f240SStefan Eßer 548de96f240SStefan Eßer return r; 549de96f240SStefan Eßer } 550de96f240SStefan Eßer 551de96f240SStefan Eßer static int 5525134c3f7SWarner Losh intcmp (const char *s1, const char *s2) 553de96f240SStefan Eßer { 55482ea3997SAndrey A. Chernov intmax_t q1, q2; 555de96f240SStefan Eßer 556de96f240SStefan Eßer 557de96f240SStefan Eßer q1 = getq(s1); 558de96f240SStefan Eßer q2 = getq(s2); 559de96f240SStefan Eßer 560de96f240SStefan Eßer if (q1 > q2) 561de96f240SStefan Eßer return 1; 562de96f240SStefan Eßer 563de96f240SStefan Eßer if (q1 < q2) 564de96f240SStefan Eßer return -1; 565de96f240SStefan Eßer 566de96f240SStefan Eßer return 0; 567de96f240SStefan Eßer } 568de96f240SStefan Eßer 569717a08b6SSheldon Hearn static int 570f19825afSJilles Tjoelker newerf (const char *f1, const char *f2) 5714b88c807SRodney W. Grimes { 572717a08b6SSheldon Hearn struct stat b1, b2; 5734b88c807SRodney W. Grimes 5746576952cSDavid Malone if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) 5756576952cSDavid Malone return 0; 5766576952cSDavid Malone 577f19825afSJilles Tjoelker if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) 578293beebcSPeter Jeremy return 1; 579f19825afSJilles Tjoelker if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) 580293beebcSPeter Jeremy return 0; 581293beebcSPeter Jeremy 582f19825afSJilles Tjoelker return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); 583f19825afSJilles Tjoelker } 584f19825afSJilles Tjoelker 585f19825afSJilles Tjoelker static int 586f19825afSJilles Tjoelker olderf (const char *f1, const char *f2) 587f19825afSJilles Tjoelker { 588f19825afSJilles Tjoelker return (newerf(f2, f1)); 589717a08b6SSheldon Hearn } 590717a08b6SSheldon Hearn 591717a08b6SSheldon Hearn static int 5925134c3f7SWarner Losh equalf (const char *f1, const char *f2) 593717a08b6SSheldon Hearn { 594717a08b6SSheldon Hearn struct stat b1, b2; 595717a08b6SSheldon Hearn 596717a08b6SSheldon Hearn return (stat (f1, &b1) == 0 && 597717a08b6SSheldon Hearn stat (f2, &b2) == 0 && 598717a08b6SSheldon Hearn b1.st_dev == b2.st_dev && 599717a08b6SSheldon Hearn b1.st_ino == b2.st_ino); 6004b88c807SRodney W. Grimes } 601