Subversion Repositories HelenOS

Rev

Rev 2714 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $NetBSD: test.c,v 1.22 2000/04/09 23:24:59 christos 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. #ifndef lint
  15. __RCSID("$NetBSD: test.c,v 1.22 2000/04/09 23:24:59 christos Exp $");
  16. #endif
  17.  
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <unistd.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <err.h>
  27. #ifdef __STDC__
  28. #include <stdarg.h>
  29. #else
  30. #include <varargs.h>
  31. #endif
  32.  
  33. /* test(1) accepts the following grammar:
  34.     oexpr   ::= aexpr | aexpr "-o" oexpr ;
  35.     aexpr   ::= nexpr | nexpr "-a" aexpr ;
  36.     nexpr   ::= primary | "!" primary
  37.     primary ::= unary-operator operand
  38.         | operand binary-operator operand
  39.         | operand
  40.         | "(" oexpr ")"
  41.         ;
  42.     unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
  43.         "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
  44.  
  45.     binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
  46.             "-nt"|"-ot"|"-ef";
  47.     operand ::= <any legal UNIX file name>
  48. */
  49.  
  50. enum token {
  51.     EOI,
  52.     FILRD,
  53.     FILWR,
  54.     FILEX,
  55.     FILEXIST,
  56.     FILREG,
  57.     FILDIR,
  58.     FILCDEV,
  59.     FILBDEV,
  60.     FILFIFO,
  61.     FILSOCK,
  62.     FILSYM,
  63.     FILGZ,
  64.     FILTT,
  65.     FILSUID,
  66.     FILSGID,
  67.     FILSTCK,
  68.     FILNT,
  69.     FILOT,
  70.     FILEQ,
  71.     FILUID,
  72.     FILGID,
  73.     STREZ,
  74.     STRNZ,
  75.     STREQ,
  76.     STRNE,
  77.     STRLT,
  78.     STRGT,
  79.     INTEQ,
  80.     INTNE,
  81.     INTGE,
  82.     INTGT,
  83.     INTLE,
  84.     INTLT,
  85.     UNOT,
  86.     BAND,
  87.     BOR,
  88.     LPAREN,
  89.     RPAREN,
  90.     OPERAND
  91. };
  92.  
  93. enum token_types {
  94.     UNOP,
  95.     BINOP,
  96.     BUNOP,
  97.     BBINOP,
  98.     PAREN
  99. };
  100.  
  101. static struct t_op {
  102.     const char *op_text;
  103.     short op_num, op_type;
  104. } const ops [] = {
  105.     {"-r",  FILRD,  UNOP},
  106.     {"-w",  FILWR,  UNOP},
  107.     {"-x",  FILEX,  UNOP},
  108.     {"-e",  FILEXIST,UNOP},
  109.     {"-f",  FILREG, UNOP},
  110.     {"-d",  FILDIR, UNOP},
  111.     {"-c",  FILCDEV,UNOP},
  112.     {"-b",  FILBDEV,UNOP},
  113.     {"-p",  FILFIFO,UNOP},
  114.     {"-u",  FILSUID,UNOP},
  115.     {"-g",  FILSGID,UNOP},
  116.     {"-k",  FILSTCK,UNOP},
  117.     {"-s",  FILGZ,  UNOP},
  118.     {"-t",  FILTT,  UNOP},
  119.     {"-z",  STREZ,  UNOP},
  120.     {"-n",  STRNZ,  UNOP},
  121.     {"-h",  FILSYM, UNOP},      /* for backwards compat */
  122.     {"-O",  FILUID, UNOP},
  123.     {"-G",  FILGID, UNOP},
  124.     {"-L",  FILSYM, UNOP},
  125.     {"-S",  FILSOCK,UNOP},
  126.     {"=",   STREQ,  BINOP},
  127.     {"!=",  STRNE,  BINOP},
  128.     {"<",   STRLT,  BINOP},
  129.     {">",   STRGT,  BINOP},
  130.     {"-eq", INTEQ,  BINOP},
  131.     {"-ne", INTNE,  BINOP},
  132.     {"-ge", INTGE,  BINOP},
  133.     {"-gt", INTGT,  BINOP},
  134.     {"-le", INTLE,  BINOP},
  135.     {"-lt", INTLT,  BINOP},
  136.     {"-nt", FILNT,  BINOP},
  137.     {"-ot", FILOT,  BINOP},
  138.     {"-ef", FILEQ,  BINOP},
  139.     {"!",   UNOT,   BUNOP},
  140.     {"-a",  BAND,   BBINOP},
  141.     {"-o",  BOR,    BBINOP},
  142.     {"(",   LPAREN, PAREN},
  143.     {")",   RPAREN, PAREN},
  144.     {0, 0,  0}
  145. };
  146.  
  147. static char **t_wp;
  148. static struct t_op const *t_wp_op;
  149. static gid_t *group_array = NULL;
  150. static int ngroups;
  151.  
  152. static void syntax __P((const char *, const char *));
  153. static int oexpr __P((enum token));
  154. static int aexpr __P((enum token));
  155. static int nexpr __P((enum token));
  156. static int primary __P((enum token));
  157. static int binop __P((void));
  158. static int filstat __P((char *, enum token));
  159. static enum token t_lex __P((char *));
  160. static int isoperand __P((void));
  161. static int getn __P((const char *));
  162. static int newerf __P((const char *, const char *));
  163. static int olderf __P((const char *, const char *));
  164. static int equalf __P((const char *, const char *));
  165. static int test_eaccess();
  166. static int bash_group_member();
  167. static void initialize_group_array();
  168.  
  169. #if defined(SHELL)
  170. extern void error __P((const char *, ...)) __attribute__((__noreturn__));
  171. #else
  172. static void error __P((const char *, ...)) __attribute__((__noreturn__));
  173.  
  174. static void
  175. #ifdef __STDC__
  176. error(const char *msg, ...)
  177. #else
  178. error(va_alist)
  179.     va_dcl
  180. #endif
  181. {
  182.     va_list ap;
  183. #ifndef __STDC__
  184.     const char *msg;
  185.  
  186.     va_start(ap);
  187.     msg = va_arg(ap, const char *);
  188. #else
  189.     va_start(ap, msg);
  190. #endif
  191.     verrx(2, msg, ap);
  192.     /*NOTREACHED*/
  193.     va_end(ap);
  194. }
  195. #endif
  196.  
  197. #ifdef SHELL
  198. int testcmd __P((int, char **));
  199.  
  200. int
  201. testcmd(argc, argv)
  202.     int argc;
  203.     char **argv;
  204. #else
  205. int main __P((int, char **));
  206.  
  207. int
  208. main(argc, argv)
  209.     int argc;
  210.     char **argv;
  211. #endif
  212. {
  213.     int res;
  214.  
  215.  
  216.     if (strcmp(argv[0], "[") == 0) {
  217.         if (strcmp(argv[--argc], "]"))
  218.             error("missing ]");
  219.         argv[argc] = NULL;
  220.     }
  221.  
  222.     if (argc < 2)
  223.         return 1;
  224.  
  225.     t_wp = &argv[1];
  226.     res = !oexpr(t_lex(*t_wp));
  227.  
  228.     if (*t_wp != NULL && *++t_wp != NULL)
  229.         syntax(*t_wp, "unexpected operator");
  230.  
  231.     return res;
  232. }
  233.  
  234. static void
  235. syntax(op, msg)
  236.     const char  *op;
  237.     const char  *msg;
  238. {
  239.     if (op && *op)
  240.         error("%s: %s", op, msg);
  241.     else
  242.         error("%s", msg);
  243. }
  244.  
  245. static int
  246. oexpr(n)
  247.     enum token n;
  248. {
  249.     int res;
  250.  
  251.     res = aexpr(n);
  252.     if (t_lex(*++t_wp) == BOR)
  253.         return oexpr(t_lex(*++t_wp)) || res;
  254.     t_wp--;
  255.     return res;
  256. }
  257.  
  258. static int
  259. aexpr(n)
  260.     enum token n;
  261. {
  262.     int res;
  263.  
  264.     res = nexpr(n);
  265.     if (t_lex(*++t_wp) == BAND)
  266.         return aexpr(t_lex(*++t_wp)) && res;
  267.     t_wp--;
  268.     return res;
  269. }
  270.  
  271. static int
  272. nexpr(n)
  273.     enum token n;           /* token */
  274. {
  275.     if (n == UNOT)
  276.         return !nexpr(t_lex(*++t_wp));
  277.     return primary(n);
  278. }
  279.  
  280. static int
  281. primary(n)
  282.     enum token n;
  283. {
  284.     enum token nn;
  285.     int res;
  286.  
  287.     if (n == EOI)
  288.         return 0;       /* missing expression */
  289.     if (n == LPAREN) {
  290.         if ((nn = t_lex(*++t_wp)) == RPAREN)
  291.             return 0;   /* missing expression */
  292.         res = oexpr(nn);
  293.         if (t_lex(*++t_wp) != RPAREN)
  294.             syntax(NULL, "closing paren expected");
  295.         return res;
  296.     }
  297.     if (t_wp_op && t_wp_op->op_type == UNOP) {
  298.         /* unary expression */
  299.         if (*++t_wp == NULL)
  300.             syntax(t_wp_op->op_text, "argument expected");
  301.         switch (n) {
  302.         case STREZ:
  303.             return strlen(*t_wp) == 0;
  304.         case STRNZ:
  305.             return strlen(*t_wp) != 0;
  306.         case FILTT:
  307.             return isatty(getn(*t_wp));
  308.         default:
  309.             return filstat(*t_wp, n);
  310.         }
  311.     }
  312.  
  313.     if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
  314.         return binop();
  315.     }    
  316.  
  317.     return strlen(*t_wp) > 0;
  318. }
  319.  
  320. static int
  321. binop()
  322. {
  323.     const char *opnd1, *opnd2;
  324.     struct t_op const *op;
  325.  
  326.     opnd1 = *t_wp;
  327.     (void) t_lex(*++t_wp);
  328.     op = t_wp_op;
  329.  
  330.     if ((opnd2 = *++t_wp) == (char *)0)
  331.         syntax(op->op_text, "argument expected");
  332.        
  333.     switch (op->op_num) {
  334.     case STREQ:
  335.         return strcmp(opnd1, opnd2) == 0;
  336.     case STRNE:
  337.         return strcmp(opnd1, opnd2) != 0;
  338.     case STRLT:
  339.         return strcmp(opnd1, opnd2) < 0;
  340.     case STRGT:
  341.         return strcmp(opnd1, opnd2) > 0;
  342.     case INTEQ:
  343.         return getn(opnd1) == getn(opnd2);
  344.     case INTNE:
  345.         return getn(opnd1) != getn(opnd2);
  346.     case INTGE:
  347.         return getn(opnd1) >= getn(opnd2);
  348.     case INTGT:
  349.         return getn(opnd1) > getn(opnd2);
  350.     case INTLE:
  351.         return getn(opnd1) <= getn(opnd2);
  352.     case INTLT:
  353.         return getn(opnd1) < getn(opnd2);
  354.     case FILNT:
  355.         return newerf (opnd1, opnd2);
  356.     case FILOT:
  357.         return olderf (opnd1, opnd2);
  358.     case FILEQ:
  359.         return equalf (opnd1, opnd2);
  360.     default:
  361.         abort();
  362.         /* NOTREACHED */
  363.     }
  364. }
  365.  
  366. static int
  367. filstat(nm, mode)
  368.     char *nm;
  369.     enum token mode;
  370. {
  371.     struct stat s;
  372.  
  373.     if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
  374.         return 0;
  375.  
  376.     switch (mode) {
  377.     case FILRD:
  378.         return test_eaccess(nm, R_OK) == 0;
  379.     case FILWR:
  380.         return test_eaccess(nm, W_OK) == 0;
  381.     case FILEX:
  382.         return test_eaccess(nm, X_OK) == 0;
  383.     case FILEXIST:
  384.         return 1;
  385.     case FILREG:
  386.         return S_ISREG(s.st_mode);
  387.     case FILDIR:
  388.         return S_ISDIR(s.st_mode);
  389.     case FILCDEV:
  390.         return S_ISCHR(s.st_mode);
  391.     case FILBDEV:
  392.         return S_ISBLK(s.st_mode);
  393.     case FILFIFO:
  394.         return S_ISFIFO(s.st_mode);
  395.     case FILSOCK:
  396.         return S_ISSOCK(s.st_mode);
  397.     case FILSYM:
  398.         return S_ISLNK(s.st_mode);
  399.     case FILSUID:
  400.         return (s.st_mode & S_ISUID) != 0;
  401.     case FILSGID:
  402.         return (s.st_mode & S_ISGID) != 0;
  403.     case FILSTCK:
  404.         return (s.st_mode & S_ISVTX) != 0;
  405.     case FILGZ:
  406.         return s.st_size > (off_t)0;
  407.     case FILUID:
  408.         return s.st_uid == geteuid();
  409.     case FILGID:
  410.         return s.st_gid == getegid();
  411.     default:
  412.         return 1;
  413.     }
  414. }
  415.  
  416. static enum token
  417. t_lex(s)
  418.     char *s;
  419. {
  420.     struct t_op const *op = ops;
  421.  
  422.     if (s == 0) {
  423.         t_wp_op = (struct t_op *)0;
  424.         return EOI;
  425.     }
  426.     while (op->op_text) {
  427.         if (strcmp(s, op->op_text) == 0) {
  428.             if ((op->op_type == UNOP && isoperand()) ||
  429.                 (op->op_num == LPAREN && *(t_wp+1) == 0))
  430.                 break;
  431.             t_wp_op = op;
  432.             return op->op_num;
  433.         }
  434.         op++;
  435.     }
  436.     t_wp_op = (struct t_op *)0;
  437.     return OPERAND;
  438. }
  439.  
  440. static int
  441. isoperand()
  442. {
  443.     struct t_op const *op = ops;
  444.     char *s;
  445.     char *t;
  446.  
  447.     if ((s  = *(t_wp+1)) == 0)
  448.         return 1;
  449.     if ((t = *(t_wp+2)) == 0)
  450.         return 0;
  451.     while (op->op_text) {
  452.         if (strcmp(s, op->op_text) == 0)
  453.                 return op->op_type == BINOP &&
  454.                     (t[0] != ')' || t[1] != '\0');
  455.         op++;
  456.     }
  457.     return 0;
  458. }
  459.  
  460. /* atoi with error detection */
  461. static int
  462. getn(s)
  463.     const char *s;
  464. {
  465.     char *p;
  466.     long r;
  467.  
  468.     errno = 0;
  469.     r = strtol(s, &p, 10);
  470.  
  471.     if (errno != 0)
  472.           error("%s: out of range", s);
  473.  
  474.     while (isspace((unsigned char)*p))
  475.           p++;
  476.    
  477.     if (*p)
  478.           error("%s: bad number", s);
  479.  
  480.     return (int) r;
  481. }
  482.  
  483. static int
  484. newerf (f1, f2)
  485. const char *f1, *f2;
  486. {
  487.     struct stat b1, b2;
  488.  
  489.     return (stat (f1, &b1) == 0 &&
  490.         stat (f2, &b2) == 0 &&
  491.         b1.st_mtime > b2.st_mtime);
  492. }
  493.  
  494. static int
  495. olderf (f1, f2)
  496. const char *f1, *f2;
  497. {
  498.     struct stat b1, b2;
  499.  
  500.     return (stat (f1, &b1) == 0 &&
  501.         stat (f2, &b2) == 0 &&
  502.         b1.st_mtime < b2.st_mtime);
  503. }
  504.  
  505. static int
  506. equalf (f1, f2)
  507. const char *f1, *f2;
  508. {
  509.     struct stat b1, b2;
  510.  
  511.     return (stat (f1, &b1) == 0 &&
  512.         stat (f2, &b2) == 0 &&
  513.         b1.st_dev == b2.st_dev &&
  514.         b1.st_ino == b2.st_ino);
  515. }
  516.  
  517. /* Do the same thing access(2) does, but use the effective uid and gid,
  518.    and don't make the mistake of telling root that any file is
  519.    executable. */
  520. static int
  521. test_eaccess (path, mode)
  522. char *path;
  523. int mode;
  524. {
  525.     struct stat st;
  526.     int euid = geteuid();
  527.  
  528.     if (stat (path, &st) < 0)
  529.         return (-1);
  530.  
  531.     if (euid == 0) {
  532.         /* Root can read or write any file. */
  533.         if (mode != X_OK)
  534.         return (0);
  535.  
  536.         /* Root can execute any file that has any one of the execute
  537.            bits set. */
  538.         if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
  539.             return (0);
  540.     }
  541.  
  542.     if (st.st_uid == euid)      /* owner */
  543.         mode <<= 6;
  544.     else if (bash_group_member (st.st_gid))
  545.         mode <<= 3;
  546.  
  547.     if (st.st_mode & mode)
  548.         return (0);
  549.  
  550.     return (-1);
  551. }
  552.  
  553. static void
  554. initialize_group_array ()
  555. {
  556.     ngroups = getgroups(0, NULL);
  557.     group_array = malloc(ngroups * sizeof(gid_t));
  558.     if (!group_array)
  559.         error(strerror(ENOMEM));
  560.     getgroups(ngroups, group_array);
  561. }
  562.  
  563. /* Return non-zero if GID is one that we have in our groups list. */
  564. static int
  565. bash_group_member (gid)
  566. gid_t gid;
  567. {
  568.     register int i;
  569.  
  570.     /* Short-circuit if possible, maybe saving a call to getgroups(). */
  571.     if (gid == getgid() || gid == getegid())
  572.         return (1);
  573.  
  574.     if (ngroups == 0)
  575.         initialize_group_array ();
  576.  
  577.     /* Search through the list looking for GID. */
  578.     for (i = 0; i < ngroups; i++)
  579.         if (gid == group_array[i])
  580.             return (1);
  581.  
  582.     return (0);
  583. }
  584.