home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD2.bin / bbs / gnu / sh-utils-1.12-src.lha / sh-utils-1.12 / src / test.c < prev    next >
C/C++ Source or Header  |  1994-10-01  |  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]