Annotation of prex-old/usr/bin/test/test.c, Revision 1.1
1.1 ! nbrk 1: /* $NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $ */
! 2:
! 3: /*
! 4: * test(1); version 7-like -- author Erik Baalbergen
! 5: * modified by Eric Gisin to be used as built-in.
! 6: * modified by Arnold Robbins to add SVR3 compatibility
! 7: * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
! 8: * modified by J.T. Conklin for NetBSD.
! 9: *
! 10: * This program is in the Public Domain.
! 11: */
! 12:
! 13: #include <sys/cdefs.h>
! 14: #include <sys/stat.h>
! 15: #include <sys/types.h>
! 16:
! 17: #include <ctype.h>
! 18: #include <err.h>
! 19: #include <errno.h>
! 20: #include <stdio.h>
! 21: #include <stdlib.h>
! 22: #include <string.h>
! 23: #include <unistd.h>
! 24: #include <stdarg.h>
! 25:
! 26: #ifdef CMDBOX
! 27: #define main(argc, argv) test_main(argc, argv)
! 28: #endif
! 29:
! 30: /* test(1) accepts the following grammar:
! 31: oexpr ::= aexpr | aexpr "-o" oexpr ;
! 32: aexpr ::= nexpr | nexpr "-a" aexpr ;
! 33: nexpr ::= primary | "!" primary
! 34: primary ::= unary-operator operand
! 35: | operand binary-operator operand
! 36: | operand
! 37: | "(" oexpr ")"
! 38: ;
! 39: unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
! 40: "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
! 41:
! 42: binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
! 43: "-nt"|"-ot"|"-ef";
! 44: operand ::= <any legal UNIX file name>
! 45: */
! 46:
! 47: enum token {
! 48: EOI,
! 49: FILRD,
! 50: FILWR,
! 51: FILEX,
! 52: FILEXIST,
! 53: FILREG,
! 54: FILDIR,
! 55: FILCDEV,
! 56: FILBDEV,
! 57: FILFIFO,
! 58: FILSOCK,
! 59: FILSYM,
! 60: FILGZ,
! 61: FILTT,
! 62: FILSUID,
! 63: FILSGID,
! 64: FILSTCK,
! 65: FILNT,
! 66: FILOT,
! 67: FILEQ,
! 68: FILUID,
! 69: FILGID,
! 70: STREZ,
! 71: STRNZ,
! 72: STREQ,
! 73: STRNE,
! 74: STRLT,
! 75: STRGT,
! 76: INTEQ,
! 77: INTNE,
! 78: INTGE,
! 79: INTGT,
! 80: INTLE,
! 81: INTLT,
! 82: UNOT,
! 83: BAND,
! 84: BOR,
! 85: LPAREN,
! 86: RPAREN,
! 87: OPERAND
! 88: };
! 89:
! 90: enum token_types {
! 91: UNOP,
! 92: BINOP,
! 93: BUNOP,
! 94: BBINOP,
! 95: PAREN
! 96: };
! 97:
! 98: static struct t_op {
! 99: const char *op_text;
! 100: short op_num, op_type;
! 101: } const ops [] = {
! 102: {"-r", FILRD, UNOP},
! 103: {"-w", FILWR, UNOP},
! 104: {"-x", FILEX, UNOP},
! 105: {"-e", FILEXIST,UNOP},
! 106: {"-f", FILREG, UNOP},
! 107: {"-d", FILDIR, UNOP},
! 108: {"-c", FILCDEV,UNOP},
! 109: {"-b", FILBDEV,UNOP},
! 110: {"-p", FILFIFO,UNOP},
! 111: {"-u", FILSUID,UNOP},
! 112: {"-g", FILSGID,UNOP},
! 113: {"-k", FILSTCK,UNOP},
! 114: {"-s", FILGZ, UNOP},
! 115: {"-t", FILTT, UNOP},
! 116: {"-z", STREZ, UNOP},
! 117: {"-n", STRNZ, UNOP},
! 118: {"-h", FILSYM, UNOP}, /* for backwards compat */
! 119: {"-O", FILUID, UNOP},
! 120: {"-G", FILGID, UNOP},
! 121: {"-L", FILSYM, UNOP},
! 122: {"-S", FILSOCK,UNOP},
! 123: {"=", STREQ, BINOP},
! 124: {"!=", STRNE, BINOP},
! 125: {"<", STRLT, BINOP},
! 126: {">", STRGT, BINOP},
! 127: {"-eq", INTEQ, BINOP},
! 128: {"-ne", INTNE, BINOP},
! 129: {"-ge", INTGE, BINOP},
! 130: {"-gt", INTGT, BINOP},
! 131: {"-le", INTLE, BINOP},
! 132: {"-lt", INTLT, BINOP},
! 133: {"-nt", FILNT, BINOP},
! 134: {"-ot", FILOT, BINOP},
! 135: {"-ef", FILEQ, BINOP},
! 136: {"!", UNOT, BUNOP},
! 137: {"-a", BAND, BBINOP},
! 138: {"-o", BOR, BBINOP},
! 139: {"(", LPAREN, PAREN},
! 140: {")", RPAREN, PAREN},
! 141: {0, 0, 0}
! 142: };
! 143:
! 144: static char **t_wp;
! 145: static struct t_op const *t_wp_op;
! 146:
! 147: static void syntax(const char *, const char *);
! 148: static int oexpr(enum token);
! 149: static int aexpr(enum token);
! 150: static int nexpr(enum token);
! 151: static int primary(enum token);
! 152: static int binop(void);
! 153: static int filstat(char *, enum token);
! 154: static enum token t_lex(char *);
! 155: static int isoperand(void);
! 156: static int getn(const char *);
! 157: static int newerf(const char *, const char *);
! 158: static int olderf(const char *, const char *);
! 159: static int equalf(const char *, const char *);
! 160:
! 161: static void error(const char *, ...);
! 162:
! 163: static void
! 164: error(const char *msg, ...)
! 165: {
! 166: va_list ap;
! 167:
! 168: va_start(ap, msg);
! 169: verrx(2, msg, ap);
! 170: va_end(ap);
! 171: /*NOTREACHED*/
! 172: }
! 173:
! 174: int
! 175: main(int argc, char *argv[])
! 176: {
! 177: int res;
! 178:
! 179: if (strcmp(argv[0], "[") == 0) {
! 180: if (strcmp(argv[--argc], "]"))
! 181: error("missing ]");
! 182: argv[argc] = NULL;
! 183: }
! 184:
! 185: if (argc < 2)
! 186: return 1;
! 187:
! 188: t_wp = &argv[1];
! 189: res = !oexpr(t_lex(*t_wp));
! 190:
! 191: if (*t_wp != NULL && *++t_wp != NULL)
! 192: syntax(*t_wp, "unexpected operator");
! 193:
! 194: return res;
! 195: }
! 196:
! 197: static void
! 198: syntax(const char *op, const char *msg)
! 199: {
! 200:
! 201: if (op && *op)
! 202: error("%s: %s", op, msg);
! 203: else
! 204: error("%s", msg);
! 205: }
! 206:
! 207: static int
! 208: oexpr(enum token n)
! 209: {
! 210: int res;
! 211:
! 212: res = aexpr(n);
! 213: if (t_lex(*++t_wp) == BOR)
! 214: return oexpr(t_lex(*++t_wp)) || res;
! 215: t_wp--;
! 216: return res;
! 217: }
! 218:
! 219: static int
! 220: aexpr(enum token n)
! 221: {
! 222: int res;
! 223:
! 224: res = nexpr(n);
! 225: if (t_lex(*++t_wp) == BAND)
! 226: return aexpr(t_lex(*++t_wp)) && res;
! 227: t_wp--;
! 228: return res;
! 229: }
! 230:
! 231: static int
! 232: nexpr(enum token n)
! 233: {
! 234:
! 235: if (n == UNOT)
! 236: return !nexpr(t_lex(*++t_wp));
! 237: return primary(n);
! 238: }
! 239:
! 240: static int
! 241: primary(enum token n)
! 242: {
! 243: enum token nn;
! 244: int res;
! 245:
! 246: if (n == EOI)
! 247: return 0; /* missing expression */
! 248: if (n == LPAREN) {
! 249: if ((nn = t_lex(*++t_wp)) == RPAREN)
! 250: return 0; /* missing expression */
! 251: res = oexpr(nn);
! 252: if (t_lex(*++t_wp) != RPAREN)
! 253: syntax(NULL, "closing paren expected");
! 254: return res;
! 255: }
! 256: if (t_wp_op && t_wp_op->op_type == UNOP) {
! 257: /* unary expression */
! 258: if (*++t_wp == NULL)
! 259: syntax(t_wp_op->op_text, "argument expected");
! 260: switch (n) {
! 261: case STREZ:
! 262: return strlen(*t_wp) == 0;
! 263: case STRNZ:
! 264: return strlen(*t_wp) != 0;
! 265: case FILTT:
! 266: return isatty(getn(*t_wp));
! 267: default:
! 268: return filstat(*t_wp, n);
! 269: }
! 270: }
! 271:
! 272: if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
! 273: return binop();
! 274: }
! 275:
! 276: return strlen(*t_wp) > 0;
! 277: }
! 278:
! 279: static int
! 280: binop(void)
! 281: {
! 282: const char *opnd1, *opnd2;
! 283: struct t_op const *op;
! 284:
! 285: opnd1 = *t_wp;
! 286: (void) t_lex(*++t_wp);
! 287: op = t_wp_op;
! 288:
! 289: if ((opnd2 = *++t_wp) == NULL)
! 290: syntax(op->op_text, "argument expected");
! 291:
! 292: switch (op->op_num) {
! 293: case STREQ:
! 294: return strcmp(opnd1, opnd2) == 0;
! 295: case STRNE:
! 296: return strcmp(opnd1, opnd2) != 0;
! 297: case STRLT:
! 298: return strcmp(opnd1, opnd2) < 0;
! 299: case STRGT:
! 300: return strcmp(opnd1, opnd2) > 0;
! 301: case INTEQ:
! 302: return getn(opnd1) == getn(opnd2);
! 303: case INTNE:
! 304: return getn(opnd1) != getn(opnd2);
! 305: case INTGE:
! 306: return getn(opnd1) >= getn(opnd2);
! 307: case INTGT:
! 308: return getn(opnd1) > getn(opnd2);
! 309: case INTLE:
! 310: return getn(opnd1) <= getn(opnd2);
! 311: case INTLT:
! 312: return getn(opnd1) < getn(opnd2);
! 313: case FILNT:
! 314: return newerf(opnd1, opnd2);
! 315: case FILOT:
! 316: return olderf(opnd1, opnd2);
! 317: case FILEQ:
! 318: return equalf(opnd1, opnd2);
! 319: default:
! 320: abort();
! 321: /* NOTREACHED */
! 322: }
! 323: }
! 324:
! 325: static int
! 326: filstat(char *nm, enum token mode)
! 327: {
! 328: struct stat s;
! 329:
! 330: if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
! 331: return 0;
! 332:
! 333: switch (mode) {
! 334: case FILRD:
! 335: return access(nm, R_OK) == 0;
! 336: case FILWR:
! 337: return access(nm, W_OK) == 0;
! 338: case FILEX:
! 339: return access(nm, X_OK) == 0;
! 340: case FILEXIST:
! 341: return access(nm, F_OK) == 0;
! 342: case FILREG:
! 343: return S_ISREG(s.st_mode);
! 344: case FILDIR:
! 345: return S_ISDIR(s.st_mode);
! 346: case FILCDEV:
! 347: return S_ISCHR(s.st_mode);
! 348: case FILBDEV:
! 349: return S_ISBLK(s.st_mode);
! 350: case FILFIFO:
! 351: return S_ISFIFO(s.st_mode);
! 352: case FILSOCK:
! 353: return S_ISSOCK(s.st_mode);
! 354: case FILSYM:
! 355: return S_ISLNK(s.st_mode);
! 356: case FILSUID:
! 357: return (s.st_mode & S_ISUID) != 0;
! 358: case FILSGID:
! 359: return (s.st_mode & S_ISGID) != 0;
! 360: case FILSTCK:
! 361: return (s.st_mode & S_ISVTX) != 0;
! 362: case FILGZ:
! 363: return s.st_size > (off_t)0;
! 364: case FILUID:
! 365: return s.st_uid == geteuid();
! 366: case FILGID:
! 367: return s.st_gid == getegid();
! 368: default:
! 369: return 1;
! 370: }
! 371: }
! 372:
! 373: static enum token
! 374: t_lex(char *s)
! 375: {
! 376: struct t_op const *op;
! 377:
! 378: op = ops;
! 379:
! 380: if (s == 0) {
! 381: t_wp_op = NULL;
! 382: return EOI;
! 383: }
! 384: while (op->op_text) {
! 385: if (strcmp(s, op->op_text) == 0) {
! 386: if ((op->op_type == UNOP && isoperand()) ||
! 387: (op->op_num == LPAREN && *(t_wp+1) == 0))
! 388: break;
! 389: t_wp_op = op;
! 390: return op->op_num;
! 391: }
! 392: op++;
! 393: }
! 394: t_wp_op = NULL;
! 395: return OPERAND;
! 396: }
! 397:
! 398: static int
! 399: isoperand(void)
! 400: {
! 401: struct t_op const *op;
! 402: char *s, *t;
! 403:
! 404: op = ops;
! 405: if ((s = *(t_wp+1)) == 0)
! 406: return 1;
! 407: if ((t = *(t_wp+2)) == 0)
! 408: return 0;
! 409: while (op->op_text) {
! 410: if (strcmp(s, op->op_text) == 0)
! 411: return op->op_type == BINOP &&
! 412: (t[0] != ')' || t[1] != '\0');
! 413: op++;
! 414: }
! 415: return 0;
! 416: }
! 417:
! 418: /* atoi with error detection */
! 419: static int
! 420: getn(const char *s)
! 421: {
! 422: char *p;
! 423: long r;
! 424:
! 425: errno = 0;
! 426: r = strtol(s, &p, 10);
! 427:
! 428: if (errno != 0)
! 429: error("%s: out of range", s);
! 430:
! 431: while (isspace((unsigned char)*p))
! 432: p++;
! 433:
! 434: if (*p)
! 435: error("%s: bad number", s);
! 436:
! 437: return (int) r;
! 438: }
! 439:
! 440: static int
! 441: newerf(const char *f1, const char *f2)
! 442: {
! 443: struct stat b1, b2;
! 444:
! 445: return (stat(f1, &b1) == 0 &&
! 446: stat(f2, &b2) == 0 &&
! 447: b1.st_mtime > b2.st_mtime);
! 448: }
! 449:
! 450: static int
! 451: olderf(const char *f1, const char *f2)
! 452: {
! 453: struct stat b1, b2;
! 454:
! 455: return (stat(f1, &b1) == 0 &&
! 456: stat(f2, &b2) == 0 &&
! 457: b1.st_mtime < b2.st_mtime);
! 458: }
! 459:
! 460: static int
! 461: equalf(const char *f1, const char *f2)
! 462: {
! 463: struct stat b1, b2;
! 464:
! 465: return (stat(f1, &b1) == 0 &&
! 466: stat(f2, &b2) == 0 &&
! 467: b1.st_dev == b2.st_dev &&
! 468: b1.st_ino == b2.st_ino);
! 469: }
CVSweb