Prex Home / Browse Source - Prex Version: 0.9.0

root/usr/bin/test/test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. error
  2. main
  3. syntax
  4. oexpr
  5. aexpr
  6. nexpr
  7. primary
  8. binop
  9. filstat
  10. t_lex
  11. isoperand
  12. getn
  13. newerf
  14. olderf
  15. equalf

   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 }

/* [<][>][^][v][top][bottom][index][help] */