home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR24 / BASH_112.ZIP / BASH-112.TAR / bash-1.12 / test.c < prev    next >
C/C++ Source or Header  |  1993-02-14  |  24KB  |  1,055 lines

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