test.c revision 50087
149884Ssheldonh/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ 249884Ssheldonh 349884Ssheldonh/* 449884Ssheldonh * test(1); version 7-like -- author Erik Baalbergen 549884Ssheldonh * modified by Eric Gisin to be used as built-in. 649884Ssheldonh * modified by Arnold Robbins to add SVR3 compatibility 749884Ssheldonh * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). 849884Ssheldonh * modified by J.T. Conklin for NetBSD. 91556Srgrimes * 1049884Ssheldonh * This program is in the Public Domain. 111556Srgrimes */ 121556Srgrimes 131556Srgrimes#ifndef lint 1436152Scharnierstatic const char rcsid[] = 1550087Sgreen "$Id: test.c,v 1.24 1999/08/18 00:18:52 green Exp $"; 161556Srgrimes#endif /* not lint */ 171556Srgrimes 1849884Ssheldonh#include <sys/types.h> 191556Srgrimes#include <sys/stat.h> 201556Srgrimes 211556Srgrimes#include <ctype.h> 221556Srgrimes#include <err.h> 231556Srgrimes#include <errno.h> 241556Srgrimes#include <stdio.h> 251556Srgrimes#include <stdlib.h> 261556Srgrimes#include <string.h> 271556Srgrimes#include <unistd.h> 281556Srgrimes 2949884Ssheldonh/* test(1) accepts the following grammar: 3049884Ssheldonh oexpr ::= aexpr | aexpr "-o" oexpr ; 3149884Ssheldonh aexpr ::= nexpr | nexpr "-a" aexpr ; 3249884Ssheldonh nexpr ::= primary | "!" primary 3349884Ssheldonh primary ::= unary-operator operand 3449884Ssheldonh | operand binary-operator operand 3549884Ssheldonh | operand 3649884Ssheldonh | "(" oexpr ")" 3749884Ssheldonh ; 3849884Ssheldonh unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 3949884Ssheldonh "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 401556Srgrimes 4149884Ssheldonh binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 4249884Ssheldonh "-nt"|"-ot"|"-ef"; 4349884Ssheldonh operand ::= <any legal UNIX file name> 4449884Ssheldonh*/ 451556Srgrimes 4649884Ssheldonhenum token { 4749884Ssheldonh EOI, 4849884Ssheldonh FILRD, 4949884Ssheldonh FILWR, 5049884Ssheldonh FILEX, 5149884Ssheldonh FILEXIST, 5249884Ssheldonh FILREG, 5349884Ssheldonh FILDIR, 5449884Ssheldonh FILCDEV, 5549884Ssheldonh FILBDEV, 5649884Ssheldonh FILFIFO, 5749884Ssheldonh FILSOCK, 5849884Ssheldonh FILSYM, 5949884Ssheldonh FILGZ, 6049884Ssheldonh FILTT, 6149884Ssheldonh FILSUID, 6249884Ssheldonh FILSGID, 6349884Ssheldonh FILSTCK, 6449884Ssheldonh FILNT, 6549884Ssheldonh FILOT, 6649884Ssheldonh FILEQ, 6749884Ssheldonh FILUID, 6849884Ssheldonh FILGID, 6949884Ssheldonh STREZ, 7049884Ssheldonh STRNZ, 7149884Ssheldonh STREQ, 7249884Ssheldonh STRNE, 7349884Ssheldonh STRLT, 7449884Ssheldonh STRGT, 7549884Ssheldonh INTEQ, 7649884Ssheldonh INTNE, 7749884Ssheldonh INTGE, 7849884Ssheldonh INTGT, 7949884Ssheldonh INTLE, 8049884Ssheldonh INTLT, 8149884Ssheldonh UNOT, 8249884Ssheldonh BAND, 8349884Ssheldonh BOR, 8449884Ssheldonh LPAREN, 8549884Ssheldonh RPAREN, 8649884Ssheldonh OPERAND 871556Srgrimes}; 881556Srgrimes 8949884Ssheldonhenum token_types { 9049884Ssheldonh UNOP, 9149884Ssheldonh BINOP, 9249884Ssheldonh BUNOP, 9349884Ssheldonh BBINOP, 9449884Ssheldonh PAREN 951556Srgrimes}; 961556Srgrimes 9749884Ssheldonhstruct t_op { 9849884Ssheldonh const char *op_text; 9949884Ssheldonh short op_num, op_type; 10049884Ssheldonh} const ops [] = { 10149884Ssheldonh {"-r", FILRD, UNOP}, 10249884Ssheldonh {"-w", FILWR, UNOP}, 10349884Ssheldonh {"-x", FILEX, UNOP}, 10449884Ssheldonh {"-e", FILEXIST,UNOP}, 10549884Ssheldonh {"-f", FILREG, UNOP}, 10649884Ssheldonh {"-d", FILDIR, UNOP}, 10749884Ssheldonh {"-c", FILCDEV,UNOP}, 10849884Ssheldonh {"-b", FILBDEV,UNOP}, 10949884Ssheldonh {"-p", FILFIFO,UNOP}, 11049884Ssheldonh {"-u", FILSUID,UNOP}, 11149884Ssheldonh {"-g", FILSGID,UNOP}, 11249884Ssheldonh {"-k", FILSTCK,UNOP}, 11349884Ssheldonh {"-s", FILGZ, UNOP}, 11449884Ssheldonh {"-t", FILTT, UNOP}, 11549884Ssheldonh {"-z", STREZ, UNOP}, 11649884Ssheldonh {"-n", STRNZ, UNOP}, 11749884Ssheldonh {"-h", FILSYM, UNOP}, /* for backwards compat */ 11849884Ssheldonh {"-O", FILUID, UNOP}, 11949884Ssheldonh {"-G", FILGID, UNOP}, 12049884Ssheldonh {"-L", FILSYM, UNOP}, 12149884Ssheldonh {"-S", FILSOCK,UNOP}, 12249884Ssheldonh {"=", STREQ, BINOP}, 12349884Ssheldonh {"!=", STRNE, BINOP}, 12449884Ssheldonh {"<", STRLT, BINOP}, 12549884Ssheldonh {">", STRGT, BINOP}, 12649884Ssheldonh {"-eq", INTEQ, BINOP}, 12749884Ssheldonh {"-ne", INTNE, BINOP}, 12849884Ssheldonh {"-ge", INTGE, BINOP}, 12949884Ssheldonh {"-gt", INTGT, BINOP}, 13049884Ssheldonh {"-le", INTLE, BINOP}, 13149884Ssheldonh {"-lt", INTLT, BINOP}, 13249884Ssheldonh {"-nt", FILNT, BINOP}, 13349884Ssheldonh {"-ot", FILOT, BINOP}, 13449884Ssheldonh {"-ef", FILEQ, BINOP}, 13549884Ssheldonh {"!", UNOT, BUNOP}, 13649884Ssheldonh {"-a", BAND, BBINOP}, 13749884Ssheldonh {"-o", BOR, BBINOP}, 13849884Ssheldonh {"(", LPAREN, PAREN}, 13949884Ssheldonh {")", RPAREN, PAREN}, 14049884Ssheldonh {0, 0, 0} 1411556Srgrimes}; 1421556Srgrimes 14349884Ssheldonhstruct t_op const *t_wp_op; 14449884Ssheldonhchar **t_wp; 1451556Srgrimes 14649884Ssheldonhstatic void syntax __P((const char *, const char *)); 14749884Ssheldonhstatic enum token t_lex __P((char *)); 14849884Ssheldonhstatic int oexpr __P((enum token)); 14949884Ssheldonhstatic int aexpr __P((enum token)); 15049884Ssheldonhstatic int nexpr __P((enum token)); 15149884Ssheldonhstatic int primary __P((enum token)); 15249884Ssheldonhstatic int binop __P((void)); 15349884Ssheldonhstatic int filstat __P((char *, enum token)); 15449884Ssheldonhstatic int isoperand __P((void)); 15549884Ssheldonhstatic int getn __P((const char *)); 15649884Ssheldonhstatic int newerf __P((const char *, const char *)); 15749884Ssheldonhstatic int olderf __P((const char *, const char *)); 15849884Ssheldonhstatic int equalf __P((const char *, const char *)); 15949884Ssheldonh 1601556Srgrimesint 1611556Srgrimesmain(argc, argv) 1621556Srgrimes int argc; 16349884Ssheldonh char **argv; 1641556Srgrimes{ 16549884Ssheldonh int res; 1661556Srgrimes 16749884Ssheldonh if (strcmp(argv[0], "[") == 0) { 1681556Srgrimes if (strcmp(argv[--argc], "]")) 1691556Srgrimes errx(2, "missing ]"); 1701556Srgrimes argv[argc] = NULL; 1711556Srgrimes } 1721556Srgrimes 17350087Sgreen /* 17450087Sgreen * We need to set our real user and group so that when we call 17550087Sgreen * access(2), it won't possibly return incorrect results. 17650087Sgreen */ 17750087Sgreen (void)setgid(getegid()); 17850087Sgreen (void)setuid(geteuid()); 17950087Sgreen 18049884Ssheldonh t_wp = &argv[1]; 18149884Ssheldonh res = !oexpr(t_lex(*t_wp)); 1821556Srgrimes 18349884Ssheldonh if (*t_wp != NULL && *++t_wp != NULL) 18449884Ssheldonh syntax(*t_wp, "unexpected operator"); 18549884Ssheldonh 18649884Ssheldonh return res; 1871556Srgrimes} 1881556Srgrimes 18949884Ssheldonhstatic void 19049884Ssheldonhsyntax(op, msg) 19149884Ssheldonh const char *op; 19249884Ssheldonh const char *msg; 1931556Srgrimes{ 1941556Srgrimes 19549884Ssheldonh if (op && *op) 19649884Ssheldonh errx(2, "%s: %s", op, msg); 19749884Ssheldonh else 19849884Ssheldonh errx(2, "%s", msg); 1991556Srgrimes} 2001556Srgrimes 20149884Ssheldonhstatic int 20249884Ssheldonhoexpr(n) 20349884Ssheldonh enum token n; 2041556Srgrimes{ 20549884Ssheldonh int res; 2061556Srgrimes 20749884Ssheldonh res = aexpr(n); 20849884Ssheldonh if (t_lex(*++t_wp) == BOR) 20949884Ssheldonh return oexpr(t_lex(*++t_wp)) || res; 21049884Ssheldonh t_wp--; 21149884Ssheldonh return res; 21249884Ssheldonh} 2134171Sache 21449884Ssheldonhstatic int 21549884Ssheldonhaexpr(n) 21649884Ssheldonh enum token n; 21749884Ssheldonh{ 21849884Ssheldonh int res; 2191556Srgrimes 22049884Ssheldonh res = nexpr(n); 22149884Ssheldonh if (t_lex(*++t_wp) == BAND) 22249884Ssheldonh return aexpr(t_lex(*++t_wp)) && res; 22349884Ssheldonh t_wp--; 22449884Ssheldonh return res; 2251556Srgrimes} 2261556Srgrimes 2271556Srgrimesstatic int 22849884Ssheldonhnexpr(n) 22949884Ssheldonh enum token n; /* token */ 2301556Srgrimes{ 23149884Ssheldonh if (n == UNOT) 23249884Ssheldonh return !nexpr(t_lex(*++t_wp)); 23349884Ssheldonh return primary(n); 2341556Srgrimes} 2351556Srgrimes 2361556Srgrimesstatic int 23749884Ssheldonhprimary(n) 23849884Ssheldonh enum token n; 2391556Srgrimes{ 24049884Ssheldonh enum token nn; 24149884Ssheldonh int res; 2421556Srgrimes 24349884Ssheldonh if (n == EOI) 24449884Ssheldonh return 0; /* missing expression */ 24549884Ssheldonh if (n == LPAREN) { 24649884Ssheldonh if ((nn = t_lex(*++t_wp)) == RPAREN) 24749884Ssheldonh return 0; /* missing expression */ 24849884Ssheldonh res = oexpr(nn); 24949884Ssheldonh if (t_lex(*++t_wp) != RPAREN) 25049884Ssheldonh syntax(NULL, "closing paren expected"); 25149884Ssheldonh return res; 25249884Ssheldonh } 25349884Ssheldonh if (t_wp_op && t_wp_op->op_type == UNOP) { 25449884Ssheldonh /* unary expression */ 25549884Ssheldonh if (*++t_wp == NULL) 25649884Ssheldonh syntax(t_wp_op->op_text, "argument expected"); 25749884Ssheldonh switch (n) { 25849884Ssheldonh case STREZ: 25949884Ssheldonh return strlen(*t_wp) == 0; 26049884Ssheldonh case STRNZ: 26149884Ssheldonh return strlen(*t_wp) != 0; 26249884Ssheldonh case FILTT: 26349884Ssheldonh return isatty(getn(*t_wp)); 26449884Ssheldonh default: 26549884Ssheldonh return filstat(*t_wp, n); 26649884Ssheldonh } 26749884Ssheldonh } 2681556Srgrimes 26949884Ssheldonh if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 27049884Ssheldonh return binop(); 27149884Ssheldonh } 27249884Ssheldonh 27349884Ssheldonh return strlen(*t_wp) > 0; 2741556Srgrimes} 2751556Srgrimes 2761556Srgrimesstatic int 27749884Ssheldonhbinop() 2781556Srgrimes{ 27949884Ssheldonh const char *opnd1, *opnd2; 28049884Ssheldonh struct t_op const *op; 2811556Srgrimes 28249884Ssheldonh opnd1 = *t_wp; 28349884Ssheldonh (void) t_lex(*++t_wp); 28449884Ssheldonh op = t_wp_op; 2851556Srgrimes 28649884Ssheldonh if ((opnd2 = *++t_wp) == NULL) 28749884Ssheldonh syntax(op->op_text, "argument expected"); 28849884Ssheldonh 28949884Ssheldonh switch (op->op_num) { 29049884Ssheldonh case STREQ: 29149884Ssheldonh return strcmp(opnd1, opnd2) == 0; 29249884Ssheldonh case STRNE: 29349884Ssheldonh return strcmp(opnd1, opnd2) != 0; 29449884Ssheldonh case STRLT: 29549884Ssheldonh return strcmp(opnd1, opnd2) < 0; 29649884Ssheldonh case STRGT: 29749884Ssheldonh return strcmp(opnd1, opnd2) > 0; 29849884Ssheldonh case INTEQ: 29949884Ssheldonh return getn(opnd1) == getn(opnd2); 30049884Ssheldonh case INTNE: 30149884Ssheldonh return getn(opnd1) != getn(opnd2); 30249884Ssheldonh case INTGE: 30349884Ssheldonh return getn(opnd1) >= getn(opnd2); 30449884Ssheldonh case INTGT: 30549884Ssheldonh return getn(opnd1) > getn(opnd2); 30649884Ssheldonh case INTLE: 30749884Ssheldonh return getn(opnd1) <= getn(opnd2); 30849884Ssheldonh case INTLT: 30949884Ssheldonh return getn(opnd1) < getn(opnd2); 31049884Ssheldonh case FILNT: 31149884Ssheldonh return newerf (opnd1, opnd2); 31249884Ssheldonh case FILOT: 31349884Ssheldonh return olderf (opnd1, opnd2); 31449884Ssheldonh case FILEQ: 31549884Ssheldonh return equalf (opnd1, opnd2); 31649884Ssheldonh default: 31749884Ssheldonh abort(); 31849884Ssheldonh /* NOTREACHED */ 3191556Srgrimes } 3201556Srgrimes} 3211556Srgrimes 32249884Ssheldonhstatic int 32349884Ssheldonhfilstat(nm, mode) 32449884Ssheldonh char *nm; 32549884Ssheldonh enum token mode; 3261556Srgrimes{ 32749884Ssheldonh struct stat s; 3281556Srgrimes 32949884Ssheldonh if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 33049884Ssheldonh return 0; 3312664Scsgr 33249884Ssheldonh switch (mode) { 33349884Ssheldonh case FILRD: 33449884Ssheldonh return access(nm, R_OK) == 0; 33549884Ssheldonh case FILWR: 33649884Ssheldonh return access(nm, W_OK) == 0; 33749884Ssheldonh case FILEX: 33850087Sgreen /* 33950087Sgreen * We cannot simply use access(2) for this specific case 34050087Sgreen * since it can always return false positives for root. 34150087Sgreen */ 34250087Sgreen if (access(nm, X_OK) != 0) 34350087Sgreen return 0; 34450087Sgreen if (S_ISDIR(s.st_mode) || getuid() != 0) 34550087Sgreen return 1; 34650087Sgreen return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 34749884Ssheldonh case FILEXIST: 34849884Ssheldonh return access(nm, F_OK) == 0; 34949884Ssheldonh case FILREG: 35049884Ssheldonh return S_ISREG(s.st_mode); 35149884Ssheldonh case FILDIR: 35249884Ssheldonh return S_ISDIR(s.st_mode); 35349884Ssheldonh case FILCDEV: 35449884Ssheldonh return S_ISCHR(s.st_mode); 35549884Ssheldonh case FILBDEV: 35649884Ssheldonh return S_ISBLK(s.st_mode); 35749884Ssheldonh case FILFIFO: 35849884Ssheldonh return S_ISFIFO(s.st_mode); 35949884Ssheldonh case FILSOCK: 36049884Ssheldonh return S_ISSOCK(s.st_mode); 36149884Ssheldonh case FILSYM: 36249884Ssheldonh return S_ISLNK(s.st_mode); 36349884Ssheldonh case FILSUID: 36449884Ssheldonh return (s.st_mode & S_ISUID) != 0; 36549884Ssheldonh case FILSGID: 36649884Ssheldonh return (s.st_mode & S_ISGID) != 0; 36749884Ssheldonh case FILSTCK: 36849884Ssheldonh return (s.st_mode & S_ISVTX) != 0; 36949884Ssheldonh case FILGZ: 37049884Ssheldonh return s.st_size > (off_t)0; 37149884Ssheldonh case FILUID: 37249884Ssheldonh return s.st_uid == geteuid(); 37349884Ssheldonh case FILGID: 37449884Ssheldonh return s.st_gid == getegid(); 37549884Ssheldonh default: 37649884Ssheldonh return 1; 3772675Scsgr } 37849884Ssheldonh} 3792675Scsgr 38049884Ssheldonhstatic enum token 38149884Ssheldonht_lex(s) 38249884Ssheldonh char *s; 38349884Ssheldonh{ 38449884Ssheldonh struct t_op const *op = ops; 38549884Ssheldonh 38649884Ssheldonh if (s == 0) { 38749884Ssheldonh t_wp_op = NULL; 38849884Ssheldonh return EOI; 38949884Ssheldonh } 39049884Ssheldonh while (op->op_text) { 39149884Ssheldonh if (strcmp(s, op->op_text) == 0) { 39249884Ssheldonh if ((op->op_type == UNOP && isoperand()) || 39349884Ssheldonh (op->op_num == LPAREN && *(t_wp+1) == 0)) 39449884Ssheldonh break; 39549884Ssheldonh t_wp_op = op; 39649884Ssheldonh return op->op_num; 3971556Srgrimes } 39849884Ssheldonh op++; 3991556Srgrimes } 40049884Ssheldonh t_wp_op = NULL; 40149884Ssheldonh return OPERAND; 4021556Srgrimes} 4031556Srgrimes 40449884Ssheldonhstatic int 40549884Ssheldonhisoperand() 4061556Srgrimes{ 40749884Ssheldonh struct t_op const *op = ops; 40849884Ssheldonh char *s; 40949884Ssheldonh char *t; 4101556Srgrimes 41149884Ssheldonh if ((s = *(t_wp+1)) == 0) 41249884Ssheldonh return 1; 41349884Ssheldonh if ((t = *(t_wp+2)) == 0) 41449884Ssheldonh return 0; 41549884Ssheldonh while (op->op_text) { 41649884Ssheldonh if (strcmp(s, op->op_text) == 0) 41749884Ssheldonh return op->op_type == BINOP && 41849884Ssheldonh (t[0] != ')' || t[1] != '\0'); 41949884Ssheldonh op++; 42049884Ssheldonh } 42149884Ssheldonh return 0; 4221556Srgrimes} 4231556Srgrimes 42449884Ssheldonh/* atoi with error detection */ 42549884Ssheldonhstatic int 42649884Ssheldonhgetn(s) 42749884Ssheldonh const char *s; 4281556Srgrimes{ 42949884Ssheldonh char *p; 43049884Ssheldonh long r; 4311556Srgrimes 43249884Ssheldonh errno = 0; 43349884Ssheldonh r = strtol(s, &p, 10); 43449884Ssheldonh 43549884Ssheldonh if (errno != 0) 43649884Ssheldonh errx(2, "%s: out of range", s); 43749884Ssheldonh 43849884Ssheldonh while (isspace((unsigned char)*p)) 43949884Ssheldonh p++; 44049884Ssheldonh 44149884Ssheldonh if (*p) 44249884Ssheldonh errx(2, "%s: bad number", s); 44349884Ssheldonh 44449884Ssheldonh return (int) r; 4451556Srgrimes} 44649884Ssheldonh 44749884Ssheldonhstatic int 44849884Ssheldonhnewerf (f1, f2) 44949884Ssheldonh const char *f1, *f2; 45049884Ssheldonh{ 45149884Ssheldonh struct stat b1, b2; 45249884Ssheldonh 45349884Ssheldonh return (stat (f1, &b1) == 0 && 45449884Ssheldonh stat (f2, &b2) == 0 && 45549884Ssheldonh b1.st_mtime > b2.st_mtime); 45649884Ssheldonh} 45749884Ssheldonh 45849884Ssheldonhstatic int 45949884Ssheldonholderf (f1, f2) 46049884Ssheldonh const char *f1, *f2; 46149884Ssheldonh{ 46249884Ssheldonh struct stat b1, b2; 46349884Ssheldonh 46449884Ssheldonh return (stat (f1, &b1) == 0 && 46549884Ssheldonh stat (f2, &b2) == 0 && 46649884Ssheldonh b1.st_mtime < b2.st_mtime); 46749884Ssheldonh} 46849884Ssheldonh 46949884Ssheldonhstatic int 47049884Ssheldonhequalf (f1, f2) 47149884Ssheldonh const char *f1, *f2; 47249884Ssheldonh{ 47349884Ssheldonh struct stat b1, b2; 47449884Ssheldonh 47549884Ssheldonh return (stat (f1, &b1) == 0 && 47649884Ssheldonh stat (f2, &b2) == 0 && 47749884Ssheldonh b1.st_dev == b2.st_dev && 47849884Ssheldonh b1.st_ino == b2.st_ino); 47949884Ssheldonh} 480