1 /* 2 * POSIX standard 3 * test expression 4 * [ expression ] 5 * 6 * Plan 9 additions: 7 * -A file exists and is append-only 8 * -L file exists and is exclusive-use 9 * -T file exists and is temporary 10 */ 11 12 #include <u.h> 13 #include <libc.h> 14 15 #define EQ(a,b) ((tmp=a)==0?0:(strcmp(tmp,b)==0)) 16 17 int ap; 18 int ac; 19 char **av; 20 char *tmp; 21 22 void synbad(char *, char *); 23 int fsizep(char *); 24 int isdir(char *); 25 int isreg(char *); 26 int isatty(int); 27 int isint(char *, int *); 28 int isolder(char *, char *); 29 int isolderthan(char *, char *); 30 int isnewerthan(char *, char *); 31 int hasmode(char *, ulong); 32 int tio(char *, int); 33 int e(void), e1(void), e2(void), e3(void); 34 char *nxtarg(int); 35 36 void 37 main(int argc, char *argv[]) 38 { 39 int r; 40 char *c; 41 42 ac = argc; av = argv; ap = 1; 43 if(EQ(argv[0],"[")) { 44 if(!EQ(argv[--ac],"]")) 45 synbad("] missing",""); 46 } 47 argv[ac] = 0; 48 if (ac<=1) 49 exits("usage"); 50 r = e(); 51 /* 52 * nice idea but short-circuit -o and -a operators may have 53 * not consumed their right-hand sides. 54 */ 55 if(0 && (c = nxtarg(1)) != nil) 56 synbad("unexpected operator/operand: ", c); 57 exits(r?0:"false"); 58 } 59 60 char * 61 nxtarg(int mt) 62 { 63 if(ap>=ac){ 64 if(mt){ 65 ap++; 66 return(0); 67 } 68 synbad("argument expected",""); 69 } 70 return(av[ap++]); 71 } 72 73 int 74 nxtintarg(int *pans) 75 { 76 if(ap<ac && isint(av[ap], pans)){ 77 ap++; 78 return 1; 79 } 80 return 0; 81 } 82 83 int 84 e(void) 85 { 86 int p1; 87 88 p1 = e1(); 89 if (EQ(nxtarg(1), "-o")) 90 return(p1 || e()); 91 ap--; 92 return(p1); 93 } 94 95 int 96 e1(void) 97 { 98 int p1; 99 100 p1 = e2(); 101 if (EQ(nxtarg(1), "-a")) 102 return (p1 && e1()); 103 ap--; 104 return(p1); 105 } 106 107 int 108 e2(void) 109 { 110 if (EQ(nxtarg(0), "!")) 111 return(!e2()); 112 ap--; 113 return(e3()); 114 } 115 116 int 117 e3(void) 118 { 119 int p1, int1, int2; 120 char *a, *p2; 121 122 a = nxtarg(0); 123 if(EQ(a, "(")) { 124 p1 = e(); 125 if(!EQ(nxtarg(0), ")")) 126 synbad(") expected",""); 127 return(p1); 128 } 129 130 if(EQ(a, "-A")) 131 return(hasmode(nxtarg(0), DMAPPEND)); 132 133 if(EQ(a, "-L")) 134 return(hasmode(nxtarg(0), DMEXCL)); 135 136 if(EQ(a, "-T")) 137 return(hasmode(nxtarg(0), DMTMP)); 138 139 if(EQ(a, "-f")) 140 return(isreg(nxtarg(0))); 141 142 if(EQ(a, "-d")) 143 return(isdir(nxtarg(0))); 144 145 if(EQ(a, "-r")) 146 return(tio(nxtarg(0), 4)); 147 148 if(EQ(a, "-w")) 149 return(tio(nxtarg(0), 2)); 150 151 if(EQ(a, "-x")) 152 return(tio(nxtarg(0), 1)); 153 154 if(EQ(a, "-e")) 155 return(tio(nxtarg(0), 0)); 156 157 if(EQ(a, "-c")) 158 return(0); 159 160 if(EQ(a, "-b")) 161 return(0); 162 163 if(EQ(a, "-u")) 164 return(0); 165 166 if(EQ(a, "-g")) 167 return(0); 168 169 if(EQ(a, "-s")) 170 return(fsizep(nxtarg(0))); 171 172 if(EQ(a, "-t")) 173 if(ap>=ac) 174 return(isatty(1)); 175 else if(nxtintarg(&int1)) 176 return(isatty(int1)); 177 else 178 synbad("not a valid file descriptor number ", ""); 179 180 if(EQ(a, "-n")) 181 return(!EQ(nxtarg(0), "")); 182 if(EQ(a, "-z")) 183 return(EQ(nxtarg(0), "")); 184 185 p2 = nxtarg(1); 186 if (p2==0) 187 return(!EQ(a,"")); 188 if(EQ(p2, "=")) 189 return(EQ(nxtarg(0), a)); 190 191 if(EQ(p2, "!=")) 192 return(!EQ(nxtarg(0), a)); 193 194 if(EQ(p2, "-older")) 195 return(isolder(nxtarg(0), a)); 196 197 if(EQ(p2, "-ot")) 198 return(isolderthan(nxtarg(0), a)); 199 200 if(EQ(p2, "-nt")) 201 return(isnewerthan(nxtarg(0), a)); 202 203 if(!isint(a, &int1)) 204 synbad("unexpected operator/operand: ", p2); 205 206 if(nxtintarg(&int2)){ 207 if(EQ(p2, "-eq")) 208 return(int1==int2); 209 if(EQ(p2, "-ne")) 210 return(int1!=int2); 211 if(EQ(p2, "-gt")) 212 return(int1>int2); 213 if(EQ(p2, "-lt")) 214 return(int1<int2); 215 if(EQ(p2, "-ge")) 216 return(int1>=int2); 217 if(EQ(p2, "-le")) 218 return(int1<=int2); 219 } 220 221 synbad("unknown operator ",p2); 222 return 0; /* to shut ken up */ 223 } 224 225 int 226 tio(char *a, int f) 227 { 228 return access (a, f) >= 0; 229 } 230 231 /* copy to local memory; clear names for safety */ 232 int 233 localstat(char *f, Dir *dir) 234 { 235 Dir *d; 236 237 d = dirstat(f); 238 if(d == nil) 239 return(-1); 240 *dir = *d; 241 free(d); 242 dir->name = 0; 243 dir->uid = 0; 244 dir->gid = 0; 245 dir->muid = 0; 246 return 0; 247 } 248 249 /* copy to local memory; clear names for safety */ 250 int 251 localfstat(int f, Dir *dir) 252 { 253 Dir *d; 254 255 d = dirfstat(f); 256 if(d == nil) 257 return(-1); 258 *dir = *d; 259 free(d); 260 dir->name = 0; 261 dir->uid = 0; 262 dir->gid = 0; 263 dir->muid = 0; 264 return 0; 265 } 266 267 int 268 hasmode(char *f, ulong m) 269 { 270 Dir dir; 271 272 if(localstat(f,&dir)<0) 273 return(0); 274 return(dir.mode&m); 275 } 276 277 int 278 isdir(char *f) 279 { 280 Dir dir; 281 282 if(localstat(f,&dir)<0) 283 return(0); 284 return(dir.mode&DMDIR); 285 } 286 287 int 288 isreg(char *f) 289 { 290 Dir dir; 291 292 if(localstat(f,&dir)<0) 293 return(0); 294 return(!(dir.mode&DMDIR)); 295 } 296 297 int 298 isatty(int fd) 299 { 300 Dir d1, d2; 301 302 if(localfstat(fd, &d1) < 0) 303 return 0; 304 if(localstat("/dev/cons", &d2) < 0) 305 return 0; 306 return d1.type==d2.type && d1.dev==d2.dev && d1.qid.path==d2.qid.path; 307 } 308 309 int 310 fsizep(char *f) 311 { 312 Dir dir; 313 314 if(localstat(f,&dir)<0) 315 return(0); 316 return(dir.length>0); 317 } 318 319 void 320 synbad(char *s1, char *s2) 321 { 322 int len; 323 324 write(2, "test: ", 6); 325 if ((len = strlen(s1)) != 0) 326 write(2, s1, len); 327 if ((len = strlen(s2)) != 0) 328 write(2, s2, len); 329 write(2, "\n", 1); 330 exits("bad syntax"); 331 } 332 333 int 334 isint(char *s, int *pans) 335 { 336 char *ep; 337 338 *pans = strtol(s, &ep, 0); 339 return (*ep == 0); 340 } 341 342 int 343 isolder(char *pin, char *f) 344 { 345 char *p = pin; 346 ulong n, m; 347 Dir dir; 348 349 if(localstat(f,&dir)<0) 350 return(0); 351 352 /* parse time */ 353 n = 0; 354 while(*p){ 355 m = strtoul(p, &p, 0); 356 switch(*p){ 357 case 0: 358 n = m; 359 break; 360 case 'y': 361 m *= 12; 362 /* fall through */ 363 case 'M': 364 m *= 30; 365 /* fall through */ 366 case 'd': 367 m *= 24; 368 /* fall through */ 369 case 'h': 370 m *= 60; 371 /* fall through */ 372 case 'm': 373 m *= 60; 374 /* fall through */ 375 case 's': 376 n += m; 377 p++; 378 break; 379 default: 380 synbad("bad time syntax, ", pin); 381 } 382 } 383 384 return(dir.mtime+n < time(0)); 385 } 386 387 int 388 isolderthan(char *a, char *b) 389 { 390 Dir ad, bd; 391 392 if(localstat(a, &ad)<0) 393 return(0); 394 if(localstat(b, &bd)<0) 395 return(0); 396 return ad.mtime > bd.mtime; 397 } 398 399 int 400 isnewerthan(char *a, char *b) 401 { 402 Dir ad, bd; 403 404 if(localstat(a, &ad)<0) 405 return(0); 406 if(localstat(b, &bd)<0) 407 return(0); 408 return ad.mtime < bd.mtime; 409 } 410