#include <stdio.h>
#include <sys/types.h>
-/* The official name of this program (e.g., no `g' prefix). */
-#define PROGRAM_NAME "test"
-
#define TEST_STANDALONE 1
#ifndef LBRACKET
# define LBRACKET 0
#endif
+/* The official name of this program (e.g., no `g' prefix). */
+#if LBRACKET
+# define PROGRAM_NAME "["
+#else
+# define PROGRAM_NAME "test"
+#endif
+
#include "system.h"
#include "error.h"
+#include "exitfail.h"
#include "euidaccess.h"
#ifndef _POSIX_VERSION
# define F_OK 0
#endif /* R_OK */
-/* This name is used solely when printing --version information. */
-#define PROGRAM_NAME "test"
-
/* The following few defines control the truth and false output of each stage.
TRUE and FALSE are what we use to compute the final output value.
SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
#define TRUTH_OR(a, b) ((a) | (b))
#define TRUTH_AND(a, b) ((a) & (b))
+/* Exit status for syntax errors, etc. */
+enum { TEST_FAILURE = 2 };
+
#if defined (TEST_STANDALONE)
# define test_exit(val) exit (val)
#else
static int test_unop (char const *s);
static int binop (char *s);
static int unary_operator (void);
-static int binary_operator (void);
+static int binary_operator (bool);
static int two_arguments (void);
static int three_arguments (void);
-static int posixtest (void);
+static int posixtest (int);
static int expr (void);
static int term (void);
fprintf (stderr, "%s: ", argv[0]);
fprintf (stderr, format, arg);
fflush (stderr);
- test_exit (SHELL_BOOLEAN (FALSE));
+ test_exit (TEST_FAILURE);
}
#if HAVE_SETREUID && HAVE_SETREGID
/* A paren-bracketed argument. */
if (argv[pos][0] == '(' && argv[pos][1] == '\0')
{
+ int nargs;
+
advance (1);
- value = expr ();
+
+ for (nargs = 1;
+ pos + nargs < argc && ! STREQ (argv[pos + nargs], ")");
+ nargs++)
+ if (nargs == 4)
+ {
+ nargs = argc - pos;
+ break;
+ }
+
+ value = posixtest (nargs);
if (argv[pos] == 0)
test_syntax_error (_("')' expected\n"), NULL);
else
}
/* are there enough arguments left that this could be dyadic? */
- if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
- ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
- value = binary_operator ();
+ if (pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))
+ value = binary_operator (true);
+ else if (pos + 3 <= argc && binop (argv[pos + 1]))
+ value = binary_operator (false);
/* Might be a switch type argument */
else if (argv[pos][0] == '-' && argv[pos][1] && argv[pos][2] == '\0')
}
static int
-binary_operator (void)
+binary_operator (bool l_is_l)
{
register int op;
struct stat stat_buf, stat_spare;
intmax_t l, r;
int value;
- /* Are the left and right integer expressions of the form '-l string'? */
- int l_is_l, r_is_l;
-
- if (strcmp (argv[pos], "-l") == 0)
- {
- l_is_l = 1;
- op = pos + 2;
-
- /* Make sure that OP is still a valid binary operator. */
- if ((op >= argc - 1) || (binop (argv[op]) == 0))
- test_syntax_error (_("%s: binary operator expected\n"), argv[op]);
+ /* Is the right integer expression of the form '-l string'? */
+ int r_is_l;
- advance (0);
- }
- else
- {
- l_is_l = 0;
- op = pos + 1;
- }
+ if (l_is_l)
+ advance (0);
+ op = pos + 1;
if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
{
case 't': /* File (fd) is a terminal? */
{
intmax_t fd;
- advance (0);
- if (pos < argc)
- {
- if (!isint (argv[pos], &fd))
- integer_expected_error (_("after -t"));
- advance (0);
- }
- else
- {
- fd = 1;
- }
+ unary_advance ();
+ if (!isint (argv[pos - 1], &fd))
+ integer_expected_error (_("after -t"));
return (TRUE == (fd == (int) fd && isatty (fd)));
}
}
static int
-one_argument (const char *s)
+one_argument (void)
{
- if (! getenv ("POSIXLY_CORRECT") && STREQ (s, "-t"))
- return (TRUE == (isatty (1)));
-
- return strlen (s) != 0;
+ return argv[pos++][0] != '\0';
}
static int
int value;
if (STREQ (argv[pos], "!"))
- value = ! one_argument (argv[pos+1]);
+ {
+ advance (0);
+ value = ! one_argument ();
+ }
else if (argv[pos][0] == '-'
&& argv[pos][1] != '\0'
&& argv[pos][2] == '\0')
{
int value;
- if (STREQ (argv[pos], "!"))
+ if (binop (argv[pos + 1]))
+ value = binary_operator (false);
+ else if (STREQ (argv[pos], "!"))
{
advance (1);
value = !two_arguments ();
}
- else if (binop (argv[pos+1]))
+ else if (STREQ (argv[pos], "(") && STREQ (argv[pos + 2], ")"))
{
- value = binary_operator ();
- pos = argc;
+ advance (0);
+ value = one_argument ();
+ advance (0);
}
- else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
- (argv[pos][0] == '('))
+ else if (STREQ (argv[pos + 1], "-a") || STREQ (argv[pos + 1], "-o"))
value = expr ();
else
test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
-posixtest (void)
+posixtest (int nargs)
{
int value;
- switch (argc - 1) /* one extra passed in */
+ switch (nargs)
{
- case 0:
- value = FALSE;
- pos = argc;
- break;
-
case 1:
- value = one_argument (argv[1]);
- pos = argc;
+ value = one_argument ();
break;
case 2:
value = two_arguments ();
- pos = argc;
break;
case 3:
value = !three_arguments ();
break;
}
+ if (STREQ (argv[pos], "(") && STREQ (argv[pos + 3], ")"))
+ {
+ advance (0);
+ value = two_arguments ();
+ advance (0);
+ break;
+ }
/* FALLTHROUGH */
case 5:
default:
+ if (nargs <= 0)
+ abort ();
value = expr ();
}
program_name);
else
{
- printf (_("\
-Usage: %s EXPRESSION\n\
- or: [ EXPRESSION ]\n\
- or: %s OPTION\n\
-"),
- program_name, program_name);
fputs (_("\
+Usage: test EXPRESSION\n\
+ or: [ EXPRESSION ]\n\
+ or: [ OPTION\n\
Exit with the status determined by EXPRESSION.\n\
\n\
"), stdout);
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
+ exit_failure = TEST_FAILURE;
atexit (close_stdout);
#endif /* TEST_STANDALONE */
- /* Recognize --help or --version unless POSIXLY_CORRECT is set. */
- if (! getenv ("POSIXLY_CORRECT"))
- parse_long_options (margc, margv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
- AUTHORS, usage);
-
argv = margv;
if (LBRACKET)
{
- --margc;
+ /* Recognize --help or --version, but only when invoked in the
+ "[" form, and when the last argument is not "]". POSIX
+ allows "[ --help" and "[ --version" to have the usual GNU
+ behavior, but it requires "test --help" and "test --version"
+ to exit silently with status 1. */
+ if (margc < 2 || strcmp (margv[margc - 1], "]") != 0)
+ {
+ parse_long_options (margc, margv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
+ AUTHORS, usage);
+ test_syntax_error (_("missing `]'\n"), NULL);
+ }
- if (margc < 1 || strcmp (margv[margc], "]") != 0)
- test_syntax_error (_("missing `]'\n"), NULL);
+ --margc;
}
argc = margc;
if (pos >= argc)
test_exit (SHELL_BOOLEAN (FALSE));
- value = posixtest ();
+ value = posixtest (argc - 1);
if (pos != argc)
test_syntax_error (_("too many arguments\n"), NULL);