|  | /* | 
|  | * POSIX standard | 
|  | *	test expression | 
|  | *	[ expression ] | 
|  | * | 
|  | * Plan 9 additions: | 
|  | *	-A file exists and is append-only | 
|  | *	-L file exists and is exclusive-use | 
|  | *	-T file exists and is temporary | 
|  | */ | 
|  |  | 
|  | #include <u.h> | 
|  | #include <libc.h> | 
|  |  | 
|  | #define isatty plan9_isatty | 
|  |  | 
|  | #define EQ(a,b)	((tmp=a)==0?0:(strcmp(tmp,b)==0)) | 
|  |  | 
|  | int	ap; | 
|  | int	ac; | 
|  | char	**av; | 
|  | char	*tmp; | 
|  |  | 
|  | void	synbad(char *, char *); | 
|  | int	fsizep(char *); | 
|  | int	isdir(char *); | 
|  | int	isreg(char *); | 
|  | int	isatty(int); | 
|  | int	isint(char *, int *); | 
|  | int	isolder(char *, char *); | 
|  | int	isolderthan(char *, char *); | 
|  | int	isnewerthan(char *, char *); | 
|  | int	hasmode(char *, ulong); | 
|  | int	tio(char *, int); | 
|  | int	e(void), e1(void), e2(void), e3(void); | 
|  | char	*nxtarg(int); | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int r; | 
|  | char *c; | 
|  |  | 
|  | ac = argc; av = argv; ap = 1; | 
|  | if(EQ(argv[0],"[")) { | 
|  | if(!EQ(argv[--ac],"]")) | 
|  | synbad("] missing",""); | 
|  | } | 
|  | argv[ac] = 0; | 
|  | if (ac<=1) | 
|  | exits("usage"); | 
|  | r = e(); | 
|  | /* | 
|  | * nice idea but short-circuit -o and -a operators may have | 
|  | * not consumed their right-hand sides. | 
|  | */ | 
|  | if(0 && (c = nxtarg(1)) != nil) | 
|  | synbad("unexpected operator/operand: ", c); | 
|  | exits(r?0:"false"); | 
|  | } | 
|  |  | 
|  | char * | 
|  | nxtarg(int mt) | 
|  | { | 
|  | if(ap>=ac){ | 
|  | if(mt){ | 
|  | ap++; | 
|  | return(0); | 
|  | } | 
|  | synbad("argument expected",""); | 
|  | } | 
|  | return(av[ap++]); | 
|  | } | 
|  |  | 
|  | int | 
|  | nxtintarg(int *pans) | 
|  | { | 
|  | if(ap<ac && isint(av[ap], pans)){ | 
|  | ap++; | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | e(void) | 
|  | { | 
|  | int p1; | 
|  |  | 
|  | p1 = e1(); | 
|  | if (EQ(nxtarg(1), "-o")) | 
|  | return(p1 || e()); | 
|  | ap--; | 
|  | return(p1); | 
|  | } | 
|  |  | 
|  | int | 
|  | e1(void) | 
|  | { | 
|  | int p1; | 
|  |  | 
|  | p1 = e2(); | 
|  | if (EQ(nxtarg(1), "-a")) | 
|  | return (p1 && e1()); | 
|  | ap--; | 
|  | return(p1); | 
|  | } | 
|  |  | 
|  | int | 
|  | e2(void) | 
|  | { | 
|  | if (EQ(nxtarg(0), "!")) | 
|  | return(!e2()); | 
|  | ap--; | 
|  | return(e3()); | 
|  | } | 
|  |  | 
|  | int | 
|  | e3(void) | 
|  | { | 
|  | int p1, int1, int2; | 
|  | char *a, *p2; | 
|  |  | 
|  | a = nxtarg(0); | 
|  | if(EQ(a, "(")) { | 
|  | p1 = e(); | 
|  | if(!EQ(nxtarg(0), ")")) | 
|  | synbad(") expected",""); | 
|  | return(p1); | 
|  | } | 
|  |  | 
|  | if(EQ(a, "-A")) | 
|  | return(hasmode(nxtarg(0), DMAPPEND)); | 
|  |  | 
|  | if(EQ(a, "-L")) | 
|  | return(hasmode(nxtarg(0), DMEXCL)); | 
|  |  | 
|  | if(EQ(a, "-T")) | 
|  | return(hasmode(nxtarg(0), DMTMP)); | 
|  |  | 
|  | if(EQ(a, "-f")) | 
|  | return(isreg(nxtarg(0))); | 
|  |  | 
|  | if(EQ(a, "-d")) | 
|  | return(isdir(nxtarg(0))); | 
|  |  | 
|  | if(EQ(a, "-r")) | 
|  | return(tio(nxtarg(0), 4)); | 
|  |  | 
|  | if(EQ(a, "-w")) | 
|  | return(tio(nxtarg(0), 2)); | 
|  |  | 
|  | if(EQ(a, "-x")) | 
|  | return(tio(nxtarg(0), 1)); | 
|  |  | 
|  | if(EQ(a, "-e")) | 
|  | return(tio(nxtarg(0), 0)); | 
|  |  | 
|  | if(EQ(a, "-c")) | 
|  | return(0); | 
|  |  | 
|  | if(EQ(a, "-b")) | 
|  | return(0); | 
|  |  | 
|  | if(EQ(a, "-u")) | 
|  | return(0); | 
|  |  | 
|  | if(EQ(a, "-g")) | 
|  | return(0); | 
|  |  | 
|  | if(EQ(a, "-s")) | 
|  | return(fsizep(nxtarg(0))); | 
|  |  | 
|  | if(EQ(a, "-t")) | 
|  | if(ap>=ac) | 
|  | return(isatty(1)); | 
|  | else if(nxtintarg(&int1)) | 
|  | return(isatty(int1)); | 
|  | else | 
|  | synbad("not a valid file descriptor number ", ""); | 
|  |  | 
|  | if(EQ(a, "-n")) | 
|  | return(!EQ(nxtarg(0), "")); | 
|  | if(EQ(a, "-z")) | 
|  | return(EQ(nxtarg(0), "")); | 
|  |  | 
|  | p2 = nxtarg(1); | 
|  | if (p2==0) | 
|  | return(!EQ(a,"")); | 
|  | if(EQ(p2, "=")) | 
|  | return(EQ(nxtarg(0), a)); | 
|  |  | 
|  | if(EQ(p2, "!=")) | 
|  | return(!EQ(nxtarg(0), a)); | 
|  |  | 
|  | if(EQ(p2, "-older")) | 
|  | return(isolder(nxtarg(0), a)); | 
|  |  | 
|  | if(EQ(p2, "-ot")) | 
|  | return(isolderthan(nxtarg(0), a)); | 
|  |  | 
|  | if(EQ(p2, "-nt")) | 
|  | return(isnewerthan(nxtarg(0), a)); | 
|  |  | 
|  | if(!isint(a, &int1)) | 
|  | synbad("unexpected operator/operand: ", p2); | 
|  |  | 
|  | if(nxtintarg(&int2)){ | 
|  | if(EQ(p2, "-eq")) | 
|  | return(int1==int2); | 
|  | if(EQ(p2, "-ne")) | 
|  | return(int1!=int2); | 
|  | if(EQ(p2, "-gt")) | 
|  | return(int1>int2); | 
|  | if(EQ(p2, "-lt")) | 
|  | return(int1<int2); | 
|  | if(EQ(p2, "-ge")) | 
|  | return(int1>=int2); | 
|  | if(EQ(p2, "-le")) | 
|  | return(int1<=int2); | 
|  | } | 
|  |  | 
|  | synbad("unknown operator ",p2); | 
|  | return 0;		/* to shut ken up */ | 
|  | } | 
|  |  | 
|  | int | 
|  | tio(char *a, int f) | 
|  | { | 
|  | return access (a, f) >= 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * note that the name strings pointed to by Dir members are | 
|  | * allocated with the Dir itself (by the same call to malloc), | 
|  | * but are not included in sizeof(Dir), so copying a Dir won't | 
|  | * copy the strings it points to. | 
|  | */ | 
|  |  | 
|  | int | 
|  | hasmode(char *f, ulong m) | 
|  | { | 
|  | int r; | 
|  | Dir *dir; | 
|  |  | 
|  | dir = dirstat(f); | 
|  | if (dir == nil) | 
|  | return 0; | 
|  | r = (dir->mode & m) != 0; | 
|  | free(dir); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | isdir(char *f) | 
|  | { | 
|  | return hasmode(f, DMDIR); | 
|  | } | 
|  |  | 
|  | int | 
|  | isreg(char *f) | 
|  | { | 
|  | int r; | 
|  | Dir *dir; | 
|  |  | 
|  | dir = dirstat(f); | 
|  | if (dir == nil) | 
|  | return 0; | 
|  | r = (dir->mode & DMDIR) == 0; | 
|  | free(dir); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | isatty(int fd) | 
|  | { | 
|  | int r; | 
|  | Dir *d1, *d2; | 
|  |  | 
|  | d1 = dirfstat(fd); | 
|  | d2 = dirstat("/dev/cons"); | 
|  | if (d1 == nil || d2 == nil) | 
|  | r = 0; | 
|  | else | 
|  | r = d1->type == d2->type && d1->dev == d2->dev && | 
|  | d1->qid.path == d2->qid.path; | 
|  | free(d1); | 
|  | free(d2); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | fsizep(char *f) | 
|  | { | 
|  | int r; | 
|  | Dir *dir; | 
|  |  | 
|  | dir = dirstat(f); | 
|  | if (dir == nil) | 
|  | return 0; | 
|  | r = dir->length > 0; | 
|  | free(dir); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | void | 
|  | synbad(char *s1, char *s2) | 
|  | { | 
|  | int len; | 
|  |  | 
|  | write(2, "test: ", 6); | 
|  | if ((len = strlen(s1)) != 0) | 
|  | write(2, s1, len); | 
|  | if ((len = strlen(s2)) != 0) | 
|  | write(2, s2, len); | 
|  | write(2, "\n", 1); | 
|  | exits("bad syntax"); | 
|  | } | 
|  |  | 
|  | int | 
|  | isint(char *s, int *pans) | 
|  | { | 
|  | char *ep; | 
|  |  | 
|  | *pans = strtol(s, &ep, 0); | 
|  | return (*ep == 0); | 
|  | } | 
|  |  | 
|  | int | 
|  | isolder(char *pin, char *f) | 
|  | { | 
|  | int r; | 
|  | ulong n, m; | 
|  | char *p = pin; | 
|  | Dir *dir; | 
|  |  | 
|  | dir = dirstat(f); | 
|  | if (dir == nil) | 
|  | return 0; | 
|  |  | 
|  | /* parse time */ | 
|  | n = 0; | 
|  | while(*p){ | 
|  | m = strtoul(p, &p, 0); | 
|  | switch(*p){ | 
|  | case 0: | 
|  | n = m; | 
|  | break; | 
|  | case 'y': | 
|  | m *= 12; | 
|  | /* fall through */ | 
|  | case 'M': | 
|  | m *= 30; | 
|  | /* fall through */ | 
|  | case 'd': | 
|  | m *= 24; | 
|  | /* fall through */ | 
|  | case 'h': | 
|  | m *= 60; | 
|  | /* fall through */ | 
|  | case 'm': | 
|  | m *= 60; | 
|  | /* fall through */ | 
|  | case 's': | 
|  | n += m; | 
|  | p++; | 
|  | break; | 
|  | default: | 
|  | synbad("bad time syntax, ", pin); | 
|  | } | 
|  | } | 
|  |  | 
|  | r = dir->mtime + n < time(0); | 
|  | free(dir); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | isolderthan(char *a, char *b) | 
|  | { | 
|  | int r; | 
|  | Dir *ad, *bd; | 
|  |  | 
|  | ad = dirstat(a); | 
|  | bd = dirstat(b); | 
|  | if (ad == nil || bd == nil) | 
|  | r = 0; | 
|  | else | 
|  | r = ad->mtime > bd->mtime; | 
|  | free(ad); | 
|  | free(bd); | 
|  | return r; | 
|  | } | 
|  |  | 
|  | int | 
|  | isnewerthan(char *a, char *b) | 
|  | { | 
|  | int r; | 
|  | Dir *ad, *bd; | 
|  |  | 
|  | ad = dirstat(a); | 
|  | bd = dirstat(b); | 
|  | if (ad == nil || bd == nil) | 
|  | r = 0; | 
|  | else | 
|  | r = ad->mtime < bd->mtime; | 
|  | free(ad); | 
|  | free(bd); | 
|  | return r; | 
|  | } |