test.c revision 50471
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[] = 1550471Speter "$FreeBSD: head/bin/test/test.c 50471 1999-08-27 23:15:48Z peter $"; 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> 2476883Skris#include <stdio.h> 2586619Sknu#include <stdlib.h> 261556Srgrimes#include <string.h> 271556Srgrimes#include <unistd.h> 281556Srgrimes 291556Srgrimes/* test(1) accepts the following grammar: 301556Srgrimes oexpr ::= aexpr | aexpr "-o" oexpr ; 3186505Sknu aexpr ::= nexpr | nexpr "-a" aexpr ; 3286505Sknu nexpr ::= primary | "!" primary 3386505Sknu primary ::= unary-operator operand 3486618Sknu | operand binary-operator operand 3588084Sache | operand 3688084Sache | "(" oexpr ")" 3786618Sknu ; 3886618Sknu unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| 3986618Sknu "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; 4086618Sknu 4186618Sknu binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| 4286618Sknu "-nt"|"-ot"|"-ef"; 4386618Sknu operand ::= <any legal UNIX file name> 4486618Sknu*/ 4586505Sknu 4686618Sknuenum token { 4786618Sknu EOI, 4886618Sknu FILRD, 4986618Sknu FILWR, 5086505Sknu FILEX, 5186618Sknu FILEXIST, 5286618Sknu FILREG, 5386618Sknu FILDIR, 5486618Sknu FILCDEV, 5586618Sknu FILBDEV, 5686618Sknu FILFIFO, 5786618Sknu FILSOCK, 5886618Sknu FILSYM, 5986618Sknu FILGZ, 6086618Sknu FILTT, 6186618Sknu FILSUID, 6249884Ssheldonh FILSGID, 6349884Ssheldonh FILSTCK, 6449884Ssheldonh FILNT, 6549884Ssheldonh FILOT, 6649884Ssheldonh FILEQ, 6749884Ssheldonh FILUID, 6849884Ssheldonh FILGID, 6949884Ssheldonh STREZ, 7049884Ssheldonh STRNZ, 7149884Ssheldonh STREQ, 7249884Ssheldonh STRNE, 731556Srgrimes STRLT, 7449884Ssheldonh STRGT, 7549884Ssheldonh INTEQ, 7649884Ssheldonh INTNE, 7749884Ssheldonh INTGE, 781556Srgrimes INTGT, 7949884Ssheldonh INTLE, 8049884Ssheldonh INTLT, 8149884Ssheldonh UNOT, 8249884Ssheldonh BAND, 8349884Ssheldonh BOR, 8449884Ssheldonh LPAREN, 8549884Ssheldonh RPAREN, 8649884Ssheldonh OPERAND 8749884Ssheldonh}; 8849884Ssheldonh 8949884Ssheldonhenum token_types { 9049884Ssheldonh UNOP, 9149884Ssheldonh BINOP, 9249884Ssheldonh BUNOP, 9349884Ssheldonh BBINOP, 9449884Ssheldonh PAREN 9549884Ssheldonh}; 9649884Ssheldonh 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}, 1201556Srgrimes {"-L", FILSYM, UNOP}, 1211556Srgrimes {"-S", FILSOCK,UNOP}, 12249884Ssheldonh {"=", STREQ, BINOP}, 12349884Ssheldonh {"!=", STRNE, BINOP}, 12449884Ssheldonh {"<", STRLT, BINOP}, 12549884Ssheldonh {">", STRGT, BINOP}, 12649884Ssheldonh {"-eq", INTEQ, BINOP}, 12749884Ssheldonh {"-ne", INTNE, BINOP}, 1281556Srgrimes {"-ge", INTGE, BINOP}, 1291556Srgrimes {"-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} 14149884Ssheldonh}; 14249884Ssheldonh 14349884Ssheldonhstruct t_op const *t_wp_op; 14449884Ssheldonhchar **t_wp; 14549884Ssheldonh 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 16049884Ssheldonhint 16149884Ssheldonhmain(argc, argv) 16249884Ssheldonh int argc; 16349884Ssheldonh char **argv; 16449884Ssheldonh{ 16549884Ssheldonh int res; 16649884Ssheldonh 16749884Ssheldonh if (strcmp(argv[0], "[") == 0) { 16849884Ssheldonh if (strcmp(argv[--argc], "]")) 16949884Ssheldonh errx(2, "missing ]"); 17049884Ssheldonh argv[argc] = NULL; 17149884Ssheldonh } 17249884Ssheldonh 17349884Ssheldonh /* XXX work around the absence of an eaccess(2) syscall */ 1741556Srgrimes (void)setgid(getegid()); 1751556Srgrimes (void)setuid(geteuid()); 17649884Ssheldonh 17749884Ssheldonh t_wp = &argv[1]; 1781556Srgrimes res = !oexpr(t_lex(*t_wp)); 17976883Skris 18076883Skris if (*t_wp != NULL && *++t_wp != NULL) 18176883Skris syntax(*t_wp, "unexpected operator"); 18276883Skris 18376883Skris return res; 18488471Sache} 18576883Skris 18676883Skrisstatic void 18776883Skrissyntax(op, msg) 18876883Skris const char *op; 18976883Skris const char *msg; 19076883Skris{ 19176883Skris 19276883Skris if (op && *op) 19376883Skris errx(2, "%s: %s", op, msg); 19476883Skris else 19549884Ssheldonh errx(2, "%s", msg); 1961556Srgrimes} 1971556Srgrimes 1981556Srgrimesstatic int 19949884Ssheldonhoexpr(n) 2001556Srgrimes enum token n; 20149884Ssheldonh{ 20255179Ssheldonh int res; 2031556Srgrimes 20455179Ssheldonh res = aexpr(n); 20555179Ssheldonh if (t_lex(*++t_wp) == BOR) 20655179Ssheldonh return oexpr(t_lex(*++t_wp)) || res; 20755179Ssheldonh t_wp--; 20855179Ssheldonh return res; 20986622Sknu} 21086618Sknu 2111556Srgrimesstatic int 2121556Srgrimesaexpr(n) 2131556Srgrimes enum token n; 21486622Sknu{ 21586622Sknu int res; 21686622Sknu 21786622Sknu res = nexpr(n); 21888084Sache if (t_lex(*++t_wp) == BAND) 21988084Sache return aexpr(t_lex(*++t_wp)) && res; 22088084Sache t_wp--; 22150302Sgreen return res; 22250087Sgreen} 22350087Sgreen 22450087Sgreenstatic int 22549884Ssheldonhnexpr(n) 22649884Ssheldonh enum token n; /* token */ 2271556Srgrimes{ 22849884Ssheldonh if (n == UNOT) 22949884Ssheldonh return !nexpr(t_lex(*++t_wp)); 23049884Ssheldonh return primary(n); 23149884Ssheldonh} 2321556Srgrimes 2331556Srgrimesstatic int 23449884Ssheldonhprimary(n) 23549884Ssheldonh enum token n; 23649884Ssheldonh{ 23749884Ssheldonh enum token nn; 2381556Srgrimes int res; 2391556Srgrimes 24049884Ssheldonh if (n == EOI) 24186618Sknu return 0; /* missing expression */ 24249884Ssheldonh if (n == LPAREN) { 24386618Sknu if ((nn = t_lex(*++t_wp)) == RPAREN) 2441556Srgrimes return 0; /* missing expression */ 2451556Srgrimes res = oexpr(nn); 24649884Ssheldonh if (t_lex(*++t_wp) != RPAREN) 24749884Ssheldonh syntax(NULL, "closing paren expected"); 24849884Ssheldonh return res; 2491556Srgrimes } 25049884Ssheldonh if (t_wp_op && t_wp_op->op_type == UNOP) { 2511556Srgrimes /* unary expression */ 25249884Ssheldonh if (*++t_wp == NULL) 25349884Ssheldonh syntax(t_wp_op->op_text, "argument expected"); 25449884Ssheldonh switch (n) { 25549884Ssheldonh case STREZ: 25649884Ssheldonh return strlen(*t_wp) == 0; 25749884Ssheldonh case STRNZ: 2584171Sache return strlen(*t_wp) != 0; 25949884Ssheldonh case FILTT: 26049884Ssheldonh return isatty(getn(*t_wp)); 26149884Ssheldonh default: 26249884Ssheldonh return filstat(*t_wp, n); 26349884Ssheldonh } 2641556Srgrimes } 26549884Ssheldonh 26649884Ssheldonh if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { 26749884Ssheldonh return binop(); 26849884Ssheldonh } 26949884Ssheldonh 2701556Srgrimes return strlen(*t_wp) > 0; 2711556Srgrimes} 2721556Srgrimes 27349884Ssheldonhstatic int 27449884Ssheldonhbinop() 2751556Srgrimes{ 27649884Ssheldonh const char *opnd1, *opnd2; 27749884Ssheldonh struct t_op const *op; 27849884Ssheldonh 2791556Srgrimes opnd1 = *t_wp; 2801556Srgrimes (void) t_lex(*++t_wp); 2811556Srgrimes op = t_wp_op; 28249884Ssheldonh 28349884Ssheldonh if ((opnd2 = *++t_wp) == NULL) 2841556Srgrimes syntax(op->op_text, "argument expected"); 28549884Ssheldonh 28649884Ssheldonh switch (op->op_num) { 2871556Srgrimes case STREQ: 28849884Ssheldonh return strcmp(opnd1, opnd2) == 0; 28949884Ssheldonh case STRNE: 29049884Ssheldonh return strcmp(opnd1, opnd2) != 0; 29149884Ssheldonh case STRLT: 29249884Ssheldonh return strcmp(opnd1, opnd2) < 0; 29349884Ssheldonh case STRGT: 29449884Ssheldonh return strcmp(opnd1, opnd2) > 0; 29549884Ssheldonh case INTEQ: 29649884Ssheldonh return getn(opnd1) == getn(opnd2); 29749884Ssheldonh case INTNE: 29849884Ssheldonh return getn(opnd1) != getn(opnd2); 29949884Ssheldonh case INTGE: 30049884Ssheldonh return getn(opnd1) >= getn(opnd2); 30149884Ssheldonh case INTGT: 30249884Ssheldonh return getn(opnd1) > getn(opnd2); 30349884Ssheldonh case INTLE: 30449884Ssheldonh return getn(opnd1) <= getn(opnd2); 30549884Ssheldonh case INTLT: 30649884Ssheldonh return getn(opnd1) < getn(opnd2); 30749884Ssheldonh case FILNT: 30849884Ssheldonh return newerf (opnd1, opnd2); 30949884Ssheldonh case FILOT: 31049884Ssheldonh return olderf (opnd1, opnd2); 31149884Ssheldonh case FILEQ: 31249884Ssheldonh return equalf (opnd1, opnd2); 3131556Srgrimes default: 31449884Ssheldonh abort(); 31549884Ssheldonh /* NOTREACHED */ 31649884Ssheldonh } 31749884Ssheldonh} 31849884Ssheldonh 3191556Srgrimesstatic int 3201556Srgrimesfilstat(nm, mode) 3211556Srgrimes char *nm; 32249884Ssheldonh enum token mode; 3231556Srgrimes{ 32449884Ssheldonh struct stat s; 32549884Ssheldonh 3261556Srgrimes if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) 32749884Ssheldonh return 0; 32849884Ssheldonh 32949884Ssheldonh switch (mode) { 3301556Srgrimes case FILRD: 33149884Ssheldonh return access(nm, R_OK) == 0; 33249884Ssheldonh case FILWR: 33349884Ssheldonh return access(nm, W_OK) == 0; 33449884Ssheldonh case FILEX: 33549884Ssheldonh /* XXX work around access(2) false positives for superuser */ 33649884Ssheldonh if (access(nm, X_OK) != 0) 33749884Ssheldonh return 0; 33849884Ssheldonh if (S_ISDIR(s.st_mode) || getuid() != 0) 33949884Ssheldonh return 1; 34049884Ssheldonh return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; 34149884Ssheldonh case FILEXIST: 34249884Ssheldonh return access(nm, F_OK) == 0; 34349884Ssheldonh case FILREG: 34462925Sse return S_ISREG(s.st_mode); 34549884Ssheldonh case FILDIR: 34662925Sse return S_ISDIR(s.st_mode); 34749884Ssheldonh case FILCDEV: 34862925Sse return S_ISCHR(s.st_mode); 34949884Ssheldonh case FILBDEV: 35062925Sse return S_ISBLK(s.st_mode); 35149884Ssheldonh case FILFIFO: 35262925Sse return S_ISFIFO(s.st_mode); 35349884Ssheldonh case FILSOCK: 35462925Sse return S_ISSOCK(s.st_mode); 35549884Ssheldonh case FILSYM: 35649884Ssheldonh return S_ISLNK(s.st_mode); 35749884Ssheldonh case FILSUID: 35849884Ssheldonh return (s.st_mode & S_ISUID) != 0; 35949884Ssheldonh case FILSGID: 36049884Ssheldonh return (s.st_mode & S_ISGID) != 0; 36149884Ssheldonh case FILSTCK: 36249884Ssheldonh return (s.st_mode & S_ISVTX) != 0; 36349884Ssheldonh case FILGZ: 3641556Srgrimes return s.st_size > (off_t)0; 3651556Srgrimes case FILUID: 3661556Srgrimes return s.st_uid == geteuid(); 36749884Ssheldonh case FILGID: 36849884Ssheldonh return s.st_gid == getegid(); 36949884Ssheldonh default: 37049884Ssheldonh return 1; 3711556Srgrimes } 37249884Ssheldonh} 3731556Srgrimes 37449884Ssheldonhstatic enum token 37549884Ssheldonht_lex(s) 3762664Scsgr char *s; 37749884Ssheldonh{ 37849884Ssheldonh struct t_op const *op = ops; 37949884Ssheldonh 38049884Ssheldonh if (s == 0) { 38149884Ssheldonh t_wp_op = NULL; 38249884Ssheldonh return EOI; 38350302Sgreen } 38450087Sgreen while (op->op_text) { 38550087Sgreen if (strcmp(s, op->op_text) == 0) { 38650087Sgreen if ((op->op_type == UNOP && isoperand()) || 38750087Sgreen (op->op_num == LPAREN && *(t_wp+1) == 0)) 38850087Sgreen break; 38949884Ssheldonh t_wp_op = op; 39049884Ssheldonh return op->op_num; 39149884Ssheldonh } 39249884Ssheldonh op++; 39349884Ssheldonh } 39449884Ssheldonh t_wp_op = NULL; 39549884Ssheldonh return OPERAND; 39649884Ssheldonh} 39749884Ssheldonh 39849884Ssheldonhstatic int 39949884Ssheldonhisoperand() 40049884Ssheldonh{ 40149884Ssheldonh struct t_op const *op = ops; 40249884Ssheldonh char *s; 40349884Ssheldonh char *t; 40449884Ssheldonh 40549884Ssheldonh if ((s = *(t_wp+1)) == 0) 40649884Ssheldonh return 1; 40749884Ssheldonh if ((t = *(t_wp+2)) == 0) 40849884Ssheldonh return 0; 40949884Ssheldonh while (op->op_text) { 41049884Ssheldonh if (strcmp(s, op->op_text) == 0) 41149884Ssheldonh return op->op_type == BINOP && 41249884Ssheldonh (t[0] != ')' || t[1] != '\0'); 41349884Ssheldonh op++; 41449884Ssheldonh } 41549884Ssheldonh return 0; 41649884Ssheldonh} 41749884Ssheldonh 41849884Ssheldonh/* atoi with error detection */ 4192675Scsgrstatic int 42049884Ssheldonhgetn(s) 4212675Scsgr const char *s; 42249884Ssheldonh{ 42349884Ssheldonh char *p; 42449884Ssheldonh long r; 42549884Ssheldonh 42649884Ssheldonh errno = 0; 42749884Ssheldonh r = strtol(s, &p, 10); 42849884Ssheldonh 42949884Ssheldonh if (errno != 0) 43049884Ssheldonh errx(2, "%s: out of range", s); 43149884Ssheldonh 43249884Ssheldonh while (isspace((unsigned char)*p)) 43349884Ssheldonh p++; 43449884Ssheldonh 43549884Ssheldonh if (*p) 43649884Ssheldonh errx(2, "%s: bad number", s); 43749884Ssheldonh 43849884Ssheldonh return (int) r; 4391556Srgrimes} 44049884Ssheldonh 4411556Srgrimesstatic int 44249884Ssheldonhnewerf (f1, f2) 44349884Ssheldonh const char *f1, *f2; 4441556Srgrimes{ 4451556Srgrimes struct stat b1, b2; 44649884Ssheldonh 44749884Ssheldonh return (stat (f1, &b1) == 0 && 4481556Srgrimes stat (f2, &b2) == 0 && 44949884Ssheldonh b1.st_mtime > b2.st_mtime); 45049884Ssheldonh} 45149884Ssheldonh 4521556Srgrimesstatic int 45349884Ssheldonholderf (f1, f2) 45449884Ssheldonh const char *f1, *f2; 45549884Ssheldonh{ 45649884Ssheldonh struct stat b1, b2; 45749884Ssheldonh 45849884Ssheldonh return (stat (f1, &b1) == 0 && 45949884Ssheldonh stat (f2, &b2) == 0 && 46049884Ssheldonh b1.st_mtime < b2.st_mtime); 46149884Ssheldonh} 46249884Ssheldonh 46349884Ssheldonhstatic int 4641556Srgrimesequalf (f1, f2) 4651556Srgrimes const char *f1, *f2; 46649884Ssheldonh{ 46749884Ssheldonh struct stat b1, b2; 46849884Ssheldonh 46949884Ssheldonh return (stat (f1, &b1) == 0 && 4701556Srgrimes stat (f2, &b2) == 0 && 47149884Ssheldonh b1.st_dev == b2.st_dev && 47249884Ssheldonh b1.st_ino == b2.st_ino); 4731556Srgrimes} 47449884Ssheldonh