home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / sh-utils-1.12-src.tgz / tar.out / fsf / sh-utils / src / test.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  26KB  |  1,114 lines

  1. /* GNU test program (ksb and mjb) */
  2.  
  3. /* Modified to run with the GNU shell by bfox. */
  4.  
  5. /* Copyright (C) 1987-1993, 1994 Free Software Foundation, Inc.
  6.  
  7.    This file is part of GNU Bash, the Bourne Again SHell.
  8.  
  9.    Bash is free software; you can redistribute it and/or modify it under
  10.    the terms of the GNU General Public License as published by the Free
  11.    Software Foundation; either version 2, or (at your option) any later
  12.    version.
  13.  
  14.    Bash is distributed in the hope that it will be useful, but WITHOUT ANY
  15.    WARRANTY; without even the implied warranty of MERCHANTABILITY or
  16.    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  17.    for more details.
  18.  
  19.    You should have received a copy of the GNU General Public License along
  20.    with Bash; see the file COPYING.  If not, write to the Free Software
  21.    Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  22.  
  23. /* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get 
  24.    the shell builtin version. */
  25. /* #define TEST_STANDALONE */
  26.  
  27. #include <config.h>
  28. #include <stdio.h>
  29. #include <sys/types.h>
  30.  
  31. #if !defined (TEST_STANDALONE)
  32. #  include "shell.h"
  33. #  include "posixstat.h"
  34. #  include "filecntl.h"
  35. #else /* TEST_STANDALONE */
  36. #  include "system.h"
  37. #  include "version.h"
  38. #  include "safe-stat.h"
  39. #  include "safe-lstat.h"
  40. #  include "group-member.h"
  41. #  if !defined (S_IXUGO)
  42. #    define S_IXUGO 0111
  43. #  endif /* S_IXUGO */
  44. #  if defined (_POSIX_VERSION)
  45. #    include <limits.h>
  46. #  else /* !_POSIX_VERSION */
  47. #    include <sys/param.h>
  48. #  endif /* _POSIX_VERSION */
  49. #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
  50. #define digit(c)  ((c) >= '0' && (c) <= '9')
  51. #define digit_value(c) ((c) - '0')
  52. char *program_name;
  53. #endif /* TEST_STANDALONE */
  54.  
  55. #if !defined (_POSIX_VERSION)
  56. #  include <sys/file.h>
  57. #endif /* !_POSIX_VERSION */
  58.  
  59. #include <errno.h>
  60. #ifndef errno
  61. extern int errno;
  62. #endif
  63.  
  64. #if !defined (STREQ)
  65. #  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
  66. #endif /* !STREQ */
  67.  
  68. #if !defined (member)
  69. #  define member(c, s) ((c) ? (index ((s), (c)) ? 1 : 0) : 0)
  70. #endif /* !member */
  71.  
  72. extern gid_t getgid (), getegid ();
  73. extern uid_t geteuid ();
  74.  
  75. #if !defined (R_OK)
  76. #define R_OK 4
  77. #define W_OK 2
  78. #define X_OK 1
  79. #define F_OK 0
  80. #endif /* R_OK */
  81.  
  82. /* This name is used solely when printing --version information.  */
  83. #define COMMAND_NAME "test"
  84.  
  85. /* The following few defines control the truth and false output of each stage.
  86.    TRUE and FALSE are what we use to compute the final output value.
  87.    SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
  88.    TRUTH_OR is how to do logical or with TRUE and FALSE.
  89.    TRUTH_AND is how to do logical and with TRUE and FALSE..
  90.    Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
  91.     SHELL_BOOLEAN = (!value). */
  92. #define TRUE 1
  93. #define FALSE 0
  94. #define SHELL_BOOLEAN(value) (!(value))
  95. #define TRUTH_OR(a, b) ((a) | (b))
  96. #define TRUTH_AND(a, b) ((a) & (b))
  97.  
  98. #if defined (TEST_STANDALONE)
  99. #  define test_exit(val) exit (val)
  100. #else
  101.    static jmp_buf test_exit_buf;
  102.    static int test_error_return = 0;
  103. #  define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
  104. #endif /* !TEST_STANDALONE */
  105.  
  106. char *xrealloc ();
  107.  
  108. static int pos;        /* The offset of the current argument in ARGV. */
  109. static int argc;    /* The number of arguments present in ARGV. */
  110. static char **argv;    /* The argument list. */
  111.  
  112. static int unop ();
  113. static int binop ();
  114. static int unary_operator ();
  115. static int binary_operator ();
  116. static int two_arguments ();
  117. static int three_arguments ();
  118. static int posixtest ();
  119.  
  120. static int expr ();
  121. static int term ();
  122. static int and ();
  123. static int or ();
  124.  
  125. #if __GNUC__ >= 2 && defined (__GNUC_MINOR__) \
  126.     && __GNUC_MINOR__ >= 5 && !defined (__STRICT_ANSI__)
  127. #define NO_RETURN_ATTRIBUTE __attribute__ ((noreturn))
  128. #else
  129. #define NO_RETURN_ATTRIBUTE /* empty */
  130. #endif
  131.  
  132. static void test_syntax_error () NO_RETURN_ATTRIBUTE;
  133. static void beyond () NO_RETURN_ATTRIBUTE;
  134.  
  135. static void
  136. test_syntax_error (format, arg)
  137.      char *format, *arg;
  138. {
  139.   fprintf (stderr, "%s: ", argv[0]);
  140.   fprintf (stderr, format, arg);
  141.   fflush (stderr);
  142.   test_exit (SHELL_BOOLEAN (FALSE));
  143. }
  144.  
  145. /* A wrapper for stat () which disallows pathnames that are empty strings. */
  146. static int
  147. test_stat (path, finfo)
  148.      char *path;
  149.      struct stat *finfo;
  150. {
  151.   if (*path == '\0')
  152.     {
  153.       errno = ENOENT;
  154.       return (-1);
  155.     }
  156.   return (SAFE_STAT (path, finfo));
  157. }
  158.  
  159. /* Do the same thing access(2) does, but use the effective uid and gid,
  160.    and don't make the mistake of telling root that any file is
  161.    executable. */
  162. static int
  163. eaccess (path, mode)
  164.      char *path;
  165.      int mode;
  166. {
  167.   struct stat st;
  168.   static int euid = -1;
  169.  
  170.   if (test_stat (path, &st) < 0)
  171.     return (-1);
  172.  
  173.   if (euid == -1)
  174.     euid = geteuid ();
  175.  
  176.   if (euid == 0)
  177.     {
  178.       /* Root can read or write any file. */
  179.       if (mode != X_OK)
  180.     return (0);
  181.  
  182.       /* Root can execute any file that has any one of the execute
  183.      bits set. */
  184.       if (st.st_mode & S_IXUGO)
  185.     return (0);
  186.     }
  187.  
  188.   if (st.st_uid == euid)        /* owner */
  189.     mode <<= 6;
  190.   else if (group_member (st.st_gid))
  191.     mode <<= 3;
  192.  
  193.   if (st.st_mode & mode)
  194.     return (0);
  195.  
  196.   return (-1);
  197. }
  198.  
  199. /* Increment our position in the argument list.  Check that we're not
  200.    past the end of the argument list.  This check is supressed if the
  201.    argument is FALSE.  Made a macro for efficiency. */
  202. #define advance(f)                            \
  203.   do                                    \
  204.     {                                    \
  205.       ++pos;                                \
  206.       if ((f) && pos >= argc)                        \
  207.     beyond ();                            \
  208.     }                                    \
  209.   while (0)
  210.  
  211. #if !defined (advance)
  212. static int
  213. advance (f)
  214.      int f;
  215. {
  216.   ++pos;
  217.  
  218.   if (f && pos >= argc)
  219.     beyond ();
  220. }
  221. #endif /* advance */
  222.  
  223. #define unary_advance()                         \
  224.   do                                    \
  225.     {                                    \
  226.       advance (1);                            \
  227.       ++pos;                                \
  228.     }                                    \
  229.   while (0)
  230.  
  231. /*
  232.  * beyond - call when we're beyond the end of the argument list (an
  233.  *    error condition)
  234.  */
  235. static void
  236. beyond ()
  237. {
  238.   test_syntax_error ("argument expected\n", (char *)NULL);
  239. }
  240.  
  241. /* Syntax error for when an integer argument was expected, but
  242.    something else was found. */
  243. static void
  244. integer_expected_error (pch)
  245.      char *pch;
  246. {
  247.   test_syntax_error ("integer expression expected %s\n", pch);
  248. }
  249.  
  250. /* Return non-zero if the characters pointed to by STRING constitute a
  251.    valid number.  Stuff the converted number into RESULT if RESULT is
  252.    a non-null pointer to a long. */
  253. static int
  254. isint (string, result)
  255.      register char *string;
  256.      long *result;
  257. {
  258.   int sign;
  259.   long value;
  260.  
  261.   sign = 1;
  262.   value = 0;
  263.  
  264.   if (result)
  265.     *result = 0;
  266.  
  267.   /* Skip leading whitespace characters. */
  268.   while (whitespace (*string))
  269.     string++;
  270.  
  271.   if (!*string)
  272.     return (0);
  273.  
  274.   /* We allow leading `-' or `+'. */
  275.   if (*string == '-' || *string == '+')
  276.     {
  277.       if (!digit (string[1]))
  278.     return (0);
  279.  
  280.       if (*string == '-')
  281.     sign = -1;
  282.  
  283.       string++;
  284.     }
  285.  
  286.   while (digit (*string))
  287.     {
  288.       if (result)
  289.     value = (value * 10) + digit_value (*string);
  290.       string++;
  291.     }
  292.  
  293.   /* Skip trailing whitespace, if any. */
  294.   while (whitespace (*string))
  295.     string++;
  296.  
  297.   /* Error if not at end of string. */
  298.   if (*string)
  299.     return (0);
  300.  
  301.   if (result)
  302.     {
  303.       value *= sign;
  304.       *result = value;
  305.     }
  306.  
  307.   return (1);
  308. }
  309.  
  310. /* Find the modification time of FILE, and stuff it into AGE, a pointer
  311.    to a long.  Return non-zero if successful, else zero. */
  312. static int
  313. age_of (filename, age)
  314.      char *filename;
  315.      long *age;
  316. {
  317.   struct stat finfo;
  318.  
  319.   if (test_stat (filename, &finfo) < 0)
  320.     return (0);
  321.  
  322.   if (age)
  323.     *age = finfo.st_mtime;
  324.  
  325.   return (1);
  326. }
  327.  
  328. /*
  329.  * term - parse a term and return 1 or 0 depending on whether the term
  330.  *    evaluates to true or false, respectively.
  331.  *
  332.  * term ::=
  333.  *    '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
  334.  *    '-'('L'|'x') filename
  335.  *     '-t' [ int ]
  336.  *    '-'('z'|'n') string
  337.  *    string
  338.  *    string ('!='|'=') string
  339.  *    <int> '-'(eq|ne|le|lt|ge|gt) <int>
  340.  *    file '-'(nt|ot|ef) file
  341.  *    '(' <expr> ')'
  342.  * int ::=
  343.  *    '-l' string
  344.  *    positive and negative integers
  345.  */
  346. static int
  347. term ()
  348. {
  349.   int value;
  350.  
  351.   if (pos >= argc)
  352.     beyond ();
  353.  
  354.   /* Deal with leading "not"'s. */
  355.   if ('!' == argv[pos][0] && '\000' == argv[pos][1])
  356.     {
  357.       value = FALSE;
  358.       while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
  359.     {
  360.       advance (1);
  361.       value ^= (TRUE);
  362.     }
  363.  
  364.       return (value ^ (term ()));
  365.     }
  366.  
  367.   /* A paren-bracketed argument. */  
  368.   if (argv[pos][0] == '(' && !argv[pos][1])
  369.     {
  370.       advance (1);
  371.       value = expr ();
  372.       if (argv[pos][0] != ')' || argv[pos][1])
  373.     test_syntax_error ("')' expected, found %s\n", argv[pos]);
  374.       advance (0);
  375.       return (TRUE == (value));
  376.     }
  377.  
  378.   /* are there enough arguments left that this could be dyadic? */
  379.   if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
  380.       ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
  381.     value = binary_operator ();
  382.  
  383.   /* Might be a switch type argument */
  384.   else if ('-' == argv[pos][0] && 0 == argv[pos][2])
  385.     {
  386.       if (unop (argv[pos][1]))
  387.     value = unary_operator ();
  388.       else
  389.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  390.     }
  391.   else
  392.     {
  393.       value = (argv[pos][0] != '\0');
  394.       advance (0);
  395.     }
  396.  
  397.   return (value);
  398. }
  399.  
  400. static int
  401. binary_operator ()
  402. {
  403.   register int op;
  404.   struct stat stat_buf, stat_spare;
  405.   long int l, r, value;
  406.   /* Are the left and right integer expressions of the form '-l string'? */
  407.   int l_is_l, r_is_l;
  408.  
  409.   if (strcmp (argv[pos], "-l") == 0)
  410.     {
  411.       l_is_l = 1;
  412.       op = pos + 2;
  413.  
  414.       /* Make sure that OP is still a valid binary operator. */
  415.       if ((op >= argc - 1) || (binop (argv[op]) == 0))
  416.     test_syntax_error ("%s: binary operator expected\n", argv[op]);
  417.  
  418.       advance (0);
  419.     }
  420.   else
  421.     {
  422.       l_is_l = 0;
  423.       op = pos + 1;
  424.     }
  425.  
  426.   if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
  427.     {
  428.       r_is_l = 1;
  429.       advance (0);
  430.     }
  431.   else
  432.     r_is_l = 0;
  433.  
  434.   if (argv[op][0] == '-')
  435.     {
  436.       /* check for eq, nt, and stuff */
  437.       switch (argv[op][1])
  438.     {
  439.     default:
  440.       break;
  441.  
  442.     case 'l':
  443.       if (argv[op][2] == 't' && !argv[op][3])
  444.         {
  445.           /* lt */
  446.           if (l_is_l)
  447.         l = strlen (argv[op - 1]);
  448.           else
  449.         {
  450.           if (!isint (argv[op - 1], &l))
  451.             integer_expected_error ("before -lt");
  452.         }
  453.  
  454.           if (r_is_l)
  455.         r = strlen (argv[op + 2]);
  456.           else
  457.         {
  458.           if (!isint (argv[op + 1], &r))
  459.             integer_expected_error ("after -lt");
  460.         }
  461.           pos += 3;
  462.           return (TRUE == (l < r));
  463.         }
  464.  
  465.       if (argv[op][2] == 'e' && !argv[op][3])
  466.         {
  467.           /* le */
  468.           if (l_is_l)
  469.         l = strlen (argv[op - 1]);
  470.           else
  471.         {
  472.           if (!isint (argv[op - 1], &l))
  473.             integer_expected_error ("before -le");
  474.         }
  475.           if (r_is_l)
  476.         r = strlen (argv[op + 2]);
  477.           else
  478.         {
  479.           if (!isint (argv[op + 1], &r))
  480.             integer_expected_error ("after -le");
  481.         }
  482.           pos += 3;
  483.           return (TRUE == (l <= r));
  484.         }
  485.       break;
  486.  
  487.     case 'g':
  488.       if (argv[op][2] == 't' && !argv[op][3])
  489.         {
  490.           /* gt integer greater than */
  491.           if (l_is_l)
  492.         l = strlen (argv[op - 1]);
  493.           else
  494.         {
  495.           if (!isint (argv[op - 1], &l))
  496.             integer_expected_error ("before -gt");
  497.         }
  498.           if (r_is_l)
  499.         r = strlen (argv[op + 2]);
  500.           else
  501.         {
  502.           if (!isint (argv[op + 1], &r))
  503.             integer_expected_error ("after -gt");
  504.         }
  505.           pos += 3;
  506.           return (TRUE == (l > r));
  507.         }
  508.  
  509.       if (argv[op][2] == 'e' && !argv[op][3])
  510.         {
  511.           /* ge - integer greater than or equal to */
  512.           if (l_is_l)
  513.         l = strlen (argv[op - 1]);
  514.           else
  515.         {
  516.           if (!isint (argv[op - 1], &l))
  517.             integer_expected_error ("before -ge");
  518.         }
  519.           if (r_is_l)
  520.         r = strlen (argv[op + 2]);
  521.           else
  522.         {
  523.           if (!isint (argv[op + 1], &r))
  524.             integer_expected_error ("after -ge");
  525.         }
  526.           pos += 3;
  527.           return (TRUE == (l >= r));
  528.         }
  529.       break;
  530.  
  531.     case 'n':
  532.       if (argv[op][2] == 't' && !argv[op][3])
  533.         {
  534.           /* nt - newer than */
  535.           pos += 3;
  536.           if (l_is_l || r_is_l)
  537.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  538.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  539.         return (TRUE == (l > r));
  540.           else
  541.         return (FALSE);
  542.         }
  543.  
  544.       if (argv[op][2] == 'e' && !argv[op][3])
  545.         {
  546.           /* ne - integer not equal */
  547.           if (l_is_l)
  548.         l = strlen (argv[op - 1]);
  549.           else
  550.         {
  551.           if (!isint (argv[op - 1], &l))
  552.             integer_expected_error ("before -ne");
  553.         }
  554.           if (r_is_l)
  555.         r = strlen (argv[op + 2]);
  556.           else
  557.         {
  558.           if (!isint (argv[op + 1], &r))
  559.             integer_expected_error ("after -ne");
  560.         }
  561.           pos += 3;
  562.           return (TRUE == (l != r));
  563.         }
  564.       break;
  565.  
  566.     case 'e':
  567.       if (argv[op][2] == 'q' && !argv[op][3])
  568.         {
  569.           /* eq - integer equal */
  570.           if (l_is_l)
  571.         l = strlen (argv[op - 1]);
  572.           else
  573.         {
  574.           if (!isint (argv[op - 1], &l))
  575.             integer_expected_error ("before -eq");
  576.         }
  577.           if (r_is_l)
  578.         r = strlen (argv[op + 2]);
  579.           else
  580.         {
  581.           if (!isint (argv[op + 1], &r))
  582.             integer_expected_error ("after -eq");
  583.         }
  584.           pos += 3;
  585.           return (TRUE == (l == r));
  586.         }
  587.  
  588.       if (argv[op][2] == 'f' && !argv[op][3])
  589.         {
  590.           /* ef - hard link? */
  591.           pos += 3;
  592.           if (l_is_l || r_is_l)
  593.         test_syntax_error ("-ef does not accept -l\n", (char *)NULL);
  594.           if (SAFE_STAT (argv[op - 1], &stat_buf) < 0)
  595.         return (FALSE);
  596.           if (SAFE_STAT (argv[op + 1], &stat_spare) < 0)
  597.         return (FALSE);
  598.           return (TRUE ==
  599.               (stat_buf.st_dev == stat_spare.st_dev &&
  600.                stat_buf.st_ino == stat_spare.st_ino));
  601.         }
  602.       break;
  603.  
  604.     case 'o':
  605.       if ('t' == argv[op][2] && '\000' == argv[op][3])
  606.         {
  607.           /* ot - older than */
  608.           pos += 3;
  609.           if (l_is_l || r_is_l)
  610.         test_syntax_error ("-nt does not accept -l\n", (char *)NULL);
  611.           if (age_of (argv[op - 1], &l) && age_of (argv[op + 1], &r))
  612.         return (TRUE == (l < r));
  613.           return (FALSE);
  614.         }
  615.       break;
  616.     }
  617.       test_syntax_error ("unknown binary operator", argv[op]);
  618.     }
  619.  
  620.   if (argv[op][0] == '=' && !argv[op][1])
  621.     {
  622.       value = (strcmp (argv[pos], argv[pos + 2]) == 0);
  623.       pos += 3;
  624.       return (TRUE == value);
  625.     }
  626.  
  627.   if (strcmp (argv[op], "!=") == 0)
  628.     {
  629.       value = (strcmp (argv[pos], argv[pos + 2]) != 0);
  630.       pos += 3;
  631.       return (TRUE == value);
  632.     }
  633.  
  634.   /* Not reached.  */
  635.   abort ();
  636. }
  637.  
  638. static int
  639. unary_operator ()
  640. {
  641.   long r, value;
  642.   struct stat stat_buf;
  643.  
  644.   switch (argv[pos][1])
  645.     {
  646.     default:
  647.       return (FALSE);
  648.  
  649.       /* All of the following unary operators use unary_advance (), which
  650.      checks to make sure that there is an argument, and then advances
  651.      pos right past it.  This means that pos - 1 is the location of the
  652.      argument. */
  653.  
  654.     case 'a':            /* file exists in the file system? */
  655.     case 'e':
  656.       unary_advance ();
  657.       value = -1 != test_stat (argv[pos - 1], &stat_buf);
  658.       return (TRUE == value);
  659.  
  660.     case 'r':            /* file is readable? */
  661.       unary_advance ();
  662.       value = -1 != eaccess (argv[pos - 1], R_OK);
  663.       return (TRUE == value);
  664.  
  665.     case 'w':            /* File is writeable? */
  666.       unary_advance ();
  667.       value = -1 != eaccess (argv[pos - 1], W_OK);
  668.       return (TRUE == value);
  669.  
  670.     case 'x':            /* File is executable? */
  671.       unary_advance ();
  672.       value = -1 != eaccess (argv[pos - 1], X_OK);
  673.       return (TRUE == value);
  674.  
  675.     case 'O':            /* File is owned by you? */
  676.       unary_advance ();
  677.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  678.     return (FALSE);
  679.  
  680.       return (TRUE == (geteuid () == stat_buf.st_uid));
  681.  
  682.     case 'G':            /* File is owned by your group? */
  683.       unary_advance ();
  684.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  685.     return (FALSE);
  686.  
  687.       return (TRUE == (getegid () == stat_buf.st_gid));
  688.  
  689.     case 'f':            /* File is a file? */
  690.       unary_advance ();
  691.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  692.     return (FALSE);
  693.  
  694.       /* Under POSIX, -f is true if the given file exists
  695.      and is a regular file. */
  696.       return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
  697.                (0 == (stat_buf.st_mode & S_IFMT))));
  698.  
  699.     case 'd':            /* File is a directory? */
  700.       unary_advance ();
  701.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  702.     return (FALSE);
  703.  
  704.       return (TRUE == (S_ISDIR (stat_buf.st_mode)));
  705.  
  706.     case 's':            /* File has something in it? */
  707.       unary_advance ();
  708.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  709.     return (FALSE);
  710.  
  711.       return (TRUE == (stat_buf.st_size > (off_t) 0));
  712.  
  713.     case 'S':            /* File is a socket? */
  714. #if !defined (S_ISSOCK)
  715.       return (FALSE);
  716. #else
  717.       unary_advance ();
  718.  
  719.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  720.     return (FALSE);
  721.  
  722.       return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
  723. #endif                /* S_ISSOCK */
  724.  
  725.     case 'c':            /* File is character special? */
  726.       unary_advance ();
  727.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  728.     return (FALSE);
  729.  
  730.       return (TRUE == (S_ISCHR (stat_buf.st_mode)));
  731.  
  732.     case 'b':            /* File is block special? */
  733.       unary_advance ();
  734.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  735.     return (FALSE);
  736.  
  737.       return (TRUE == (S_ISBLK (stat_buf.st_mode)));
  738.  
  739.     case 'p':            /* File is a named pipe? */
  740.       unary_advance ();
  741. #ifndef S_ISFIFO
  742.       return (FALSE);
  743. #else
  744.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  745.     return (FALSE);
  746.       return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
  747. #endif                /* S_ISFIFO */
  748.  
  749.     case 'L':            /* Same as -h  */
  750.       /*FALLTHROUGH*/
  751.  
  752.     case 'h':            /* File is a symbolic link? */
  753.       unary_advance ();
  754. #ifndef S_ISLNK
  755.       return (FALSE);
  756. #else
  757.       /* An empty filename is not a valid pathname. */
  758.       if ((argv[pos - 1][0] == '\0') ||
  759.       (SAFE_LSTAT (argv[pos - 1], &stat_buf) < 0))
  760.     return (FALSE);
  761.  
  762.       return (TRUE == (S_ISLNK (stat_buf.st_mode)));
  763. #endif                /* S_IFLNK */
  764.  
  765.     case 'u':            /* File is setuid? */
  766.       unary_advance ();
  767. #ifndef S_ISUID
  768.       return (FALSE);
  769. #else
  770.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  771.     return (FALSE);
  772.  
  773.       return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
  774. #endif
  775.  
  776.     case 'g':            /* File is setgid? */
  777.       unary_advance ();
  778. #ifndef S_ISGID
  779.       return (FALSE);
  780. #else
  781.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  782.     return (FALSE);
  783.  
  784.       return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
  785. #endif
  786.  
  787.     case 'k':            /* File has sticky bit set? */
  788.       unary_advance ();
  789.       if (test_stat (argv[pos - 1], &stat_buf) < 0)
  790.     return (FALSE);
  791. #ifndef S_ISVTX
  792.       /* This is not Posix, and is not defined on some Posix systems. */
  793.       return (FALSE);
  794. #else
  795.       return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
  796. #endif
  797.  
  798.     case 't':            /* File (fd) is a terminal?  (fd) defaults to stdout. */
  799.       advance (0);
  800.       if (pos < argc && isint (argv[pos], &r))
  801.     {
  802.       advance (0);
  803.       return (TRUE == (isatty ((int) r)));
  804.     }
  805.       return (TRUE == (isatty (1)));
  806.  
  807.     case 'n':            /* True if arg has some length. */
  808.       unary_advance ();
  809.       return (TRUE == (argv[pos - 1][0] != 0));
  810.  
  811.     case 'z':            /* True if arg has no length. */
  812.       unary_advance ();
  813.       return (TRUE == (argv[pos - 1][0] == '\0'));
  814.     }
  815. }
  816.     
  817. /*
  818.  * and:
  819.  *    term
  820.  *    term '-a' and
  821.  */
  822. static int
  823. and ()
  824. {
  825.   int value;
  826.  
  827.   value = term ();
  828.   while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
  829.     {
  830.       advance (0);
  831.       value = TRUTH_AND (value, and ());
  832.     }
  833.   return (TRUE == value);
  834. }
  835.  
  836. /*
  837.  * or:
  838.  *    and
  839.  *    and '-o' or
  840.  */
  841. static int
  842. or ()
  843. {
  844.   int value;
  845.  
  846.   value = and ();
  847.  
  848.   while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
  849.     {
  850.       advance (0);
  851.       value = TRUTH_OR (value, or ());
  852.     }
  853.  
  854.   return (TRUE == value);
  855. }
  856.  
  857. /*
  858.  * expr:
  859.  *    or
  860.  */
  861. static int
  862. expr ()
  863. {
  864.   if (pos >= argc)
  865.     beyond ();
  866.  
  867.   return (FALSE ^ (or ()));        /* Same with this. */
  868. }
  869.  
  870. /* Return TRUE if S is one of the test command's binary operators. */
  871. static int
  872. binop (s)
  873.      char *s;
  874. {
  875.   return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
  876.       (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
  877.       (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
  878.       (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
  879. }
  880.  
  881. /* Return non-zero if OP is one of the test command's unary operators. */
  882. static int
  883. unop (op)
  884.      int op;
  885. {
  886.   return (member (op, "abcdefgkLhprsStuwxOGnz"));
  887. }
  888.  
  889. static int
  890. two_arguments ()
  891. {
  892.   int value;
  893.  
  894.   if (STREQ (argv[pos], "!"))
  895.     value = strlen (argv[pos+1]) == 0;
  896.   else if ((argv[pos][0] == '-') && (argv[pos][2] == '\0'))
  897.     {
  898.       if (unop (argv[pos][1]))
  899.     value = unary_operator ();
  900.       else
  901.     test_syntax_error ("%s: unary operator expected\n", argv[pos]);
  902.     }
  903.   else
  904.     beyond ();
  905.   return (value);
  906. }
  907.  
  908. static int
  909. three_arguments ()
  910. {
  911.   int value;
  912.  
  913.   if (STREQ (argv[pos], "!"))
  914.     {
  915.       advance (1);
  916.       value = !two_arguments ();
  917.     }
  918.   else if (binop (argv[pos+1]))
  919.     {
  920.       value = binary_operator ();
  921.       pos = argc;
  922.     }
  923.   else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
  924.        (argv[pos][0] == '('))
  925.     value = expr ();
  926.   else
  927.     test_syntax_error ("%s: binary operator expected\n", argv[pos+1]);
  928.   return (value);
  929. }
  930.  
  931. /* This is an implementation of a Posix.2 proposal by David Korn. */
  932. static int
  933. posixtest ()
  934. {
  935.   int value;
  936.  
  937.   switch (argc - 1)    /* one extra passed in */
  938.     {
  939.       case 0:
  940.     value = FALSE;
  941.     pos = argc;
  942.     break;
  943.  
  944.       case 1:
  945.     value = strlen (argv[1]) != 0;
  946.     pos = argc;
  947.     break;
  948.  
  949.       case 2:
  950.     value = two_arguments ();
  951.     pos = argc;
  952.     break;
  953.  
  954.       case 3:
  955.     value = three_arguments ();
  956.     break;
  957.  
  958.       case 4:
  959.     if (STREQ (argv[pos], "!"))
  960.       {
  961.         advance (1);
  962.         value = !three_arguments ();
  963.         break;
  964.       }
  965.     /* FALLTHROUGH */
  966.       case 5:
  967.       default:
  968.     value = expr ();
  969.     }
  970.  
  971.   return (value);
  972. }
  973.  
  974. #if defined (TEST_STANDALONE)
  975. #include "long-options.h"
  976.  
  977. static void
  978. usage (status)
  979.      int status;
  980. {
  981.   if (status != 0)
  982.     fprintf (stderr, "Try `%s --help' for more information.\n",
  983.          program_name);
  984.   else
  985.     {
  986.       printf ("\
  987. Usage: %s EXPRESSION\n\
  988.   or:  [ EXPRESSION ]\n\
  989.   or:  %s OPTION\n\
  990. ",
  991.           program_name, program_name);
  992.       printf ("\
  993. \n\
  994.   --help      display this help and exit\n\
  995.   --version   output version information and exit\n\
  996. \n\
  997. EXPRESSION is true or false and sets exit status.  It is one of:\n\
  998. ");
  999.       printf ("\
  1000. \n\
  1001.   ( EXPRESSION )               EXPRESSION is true\n\
  1002.   ! EXPRESSION                 EXPRESSION is false\n\
  1003.   EXPRESSION1 -a EXPRESSION2   both EXPRESSION1 and EXPRESSION2 are true\n\
  1004.   EXPRESSION1 -o EXPRESSION2   either EXPRESSION1 or EXPRESSION2 is true\n\
  1005. \n\
  1006.   [-n] STRING          the length of STRING is non-zero\n\
  1007.   -z STRING            the length of STRING is zero\n\
  1008.   STRING1 = STRING2    the strings are equal\n\
  1009.   STRING1 != STRING2   the strings are not equal\n\
  1010. \n\
  1011.   INTEGER1 -eq INTEGER2   INTEGER1 is equal to INTEGER2\n\
  1012.   INTEGER1 -ge INTEGER2   INTEGER1 is greater than or equal to INTEGER2\n\
  1013.   INTEGER1 -gt INTEGER2   INTEGER1 is greater than INTEGER2\n\
  1014.   INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2\n\
  1015.   INTEGER1 -lt INTEGER2   INTEGER1 is less than INTEGER2\n\
  1016.   INTEGER1 -ne INTEGER2   INTEGER1 is not equal to INTEGER2\n\
  1017. ");
  1018.       printf ("\
  1019. \n\
  1020.   FILE1 -ef FILE2   FILE1 and FILE2 have the same device and inode numbers\n\
  1021.   FILE1 -nt FILE2   FILE1 is newer (modification date) than FILE2\n\
  1022.   FILE1 -ot FILE2   FILE1 is older than FILE2\n\
  1023. \n\
  1024.   -G FILE     FILE exists and is owned by the effective group ID\n\
  1025.   -L FILE     FILE exists and is a symbolic link\n\
  1026.   -O FILE     FILE exists and is owned by the effective user ID\n\
  1027.   -S FILE     FILE exists and is a socket\n\
  1028.   -b FILE     FILE exists and is block special\n\
  1029.   -c FILE     FILE exists and is character special\n\
  1030.   -d FILE     FILE exists and is a directory\n\
  1031.   -e FILE     FILE exists\n\
  1032.   -f FILE     FILE exists and is a regular file\n\
  1033.   -g FILE     FILE exists and is set-group-ID\n\
  1034.   -k FILE     FILE exists and has its sticky bit set\n\
  1035.   -p FILE     FILE exists and is a named pipe\n\
  1036.   -r FILE     FILE exists and is readable\n\
  1037.   -s FILE     FILE exists and has a size greater than zero\n\
  1038.   -t          standard output is opened on a terminal\n\
  1039.   -t FD       file descriptor FD is opened on a terminal\n\
  1040.   -u FILE     FILE exists and its set-user-ID bit is set\n\
  1041.   -w FILE     FILE exists and is writable\n\
  1042.   -x FILE     FILE exists and is executable\n\
  1043. ");
  1044.       printf ("\
  1045. \n\
  1046.   --help      display this help and exit\n\
  1047.   --version   output version information and exit\n\
  1048. \n\
  1049. Beware that parentheses need to be escaped by backslashes for shells.\n\
  1050. INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
  1051. ");
  1052.     }
  1053.   exit (status);
  1054. }
  1055. #endif /* TEST_STANDALONE */
  1056.  
  1057. /*
  1058.  * [:
  1059.  *    '[' expr ']'
  1060.  * test:
  1061.  *    test expr
  1062.  */
  1063. int
  1064. #if defined (TEST_STANDALONE)
  1065. main (margc, margv)
  1066. #else
  1067. test_command (margc, margv)
  1068. #endif /* !TEST_STANDALONE */
  1069.      int margc;
  1070.      char **margv;
  1071. {
  1072.   int value;
  1073.  
  1074. #if !defined (TEST_STANDALONE)
  1075.   int code;
  1076.  
  1077.   code = setjmp (test_exit_buf);
  1078.  
  1079.   if (code)
  1080.     return (test_error_return);
  1081. #else /* TEST_STANDALONE */
  1082.   program_name = margv[0];
  1083. #endif /* TEST_STANDALONE */
  1084.  
  1085.   argv = margv;
  1086.  
  1087.   if (margv[0] && strcmp (margv[0], "[") == 0)
  1088.     {
  1089.       parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
  1090.  
  1091.       --margc;
  1092.  
  1093.       if (margc < 2)
  1094.     test_exit (SHELL_BOOLEAN (FALSE));
  1095.  
  1096.       if (margv[margc] && strcmp (margv[margc], "]") != 0)
  1097.     test_syntax_error ("missing `]'\n", (char *)NULL);
  1098.     }
  1099.  
  1100.   argc = margc;
  1101.   pos = 1;
  1102.  
  1103.   if (pos >= argc)
  1104.     test_exit (SHELL_BOOLEAN (FALSE));
  1105.  
  1106.   parse_long_options (argc, argv, COMMAND_NAME, version_string, usage);
  1107.   value = posixtest ();
  1108.  
  1109.   if (pos != argc)
  1110.     test_syntax_error ("too many arguments\n", (char *)NULL);
  1111.  
  1112.   test_exit (SHELL_BOOLEAN (value));
  1113. }
  1114.