/* Modified to run with the GNU shell by bfox. */
-/* Copyright (C) 1987-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2005, 2007-2008 Free Software Foundation, Inc.
- This file is part of GNU Bash, the Bourne Again SHell.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- Bash is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2, or (at your option) any later
- version.
-
- Bash is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Define TEST_STANDALONE to get the /bin/test version. Otherwise, you get
the shell builtin version. */
#endif
#include "system.h"
-#include "error.h"
#include "euidaccess.h"
#include "quote.h"
+#include "stat-time.h"
+#include "strnumcmp.h"
-#ifndef _POSIX_VERSION
+#if HAVE_SYS_PARAM_H
# include <sys/param.h>
-#endif /* _POSIX_VERSION */
-#define whitespace(c) (((c) == ' ') || ((c) == '\t'))
-#define digit(c) ((c) >= '0' && (c) <= '9')
-#define digit_value(c) ((c) - '0')
-
-char *program_name;
-
-#if !defined (_POSIX_VERSION)
-# include <sys/file.h>
-#endif /* !_POSIX_VERSION */
-
-extern gid_t getegid ();
-extern uid_t geteuid ();
+#endif
/* Exit status for syntax errors, etc. */
enum { TEST_TRUE, TEST_FALSE, TEST_FAILURE };
-#if defined (TEST_STANDALONE)
+#if defined TEST_STANDALONE
# define test_exit(val) exit (val)
#else
static jmp_buf test_exit_buf;
{
fprintf (stderr, "%s: ", argv[0]);
fprintf (stderr, format, arg);
+ fputc ('\n', stderr);
fflush (stderr);
test_exit (TEST_FAILURE);
}
test_syntax_error (_("missing argument after %s"), quote (argv[argc - 1]));
}
-/* Syntax error for when an integer argument was expected, but
- something else was found. */
-static void
-integer_expected_error (char const *pch)
-{
- test_syntax_error (_("%s: integer expression expected\n"), pch);
-}
-
-/* Return true if the characters pointed to by STRING constitute a
- valid number. Stuff the converted number into RESULT if RESULT is
- not null. */
-static bool
-is_int (char const *string, intmax_t *result)
+/* If the characters pointed to by STRING constitute a valid number,
+ return a pointer to the start of the number, skipping any blanks or
+ leading '+'. Otherwise, report an error and exit. */
+static char const *
+find_int (char const *string)
{
- int sign;
- intmax_t value;
- char const *orig_string;
-
- sign = 1;
- value = 0;
-
- if (result)
- *result = 0;
+ char const *p;
+ char const *number_start;
- /* Skip leading whitespace characters. */
- while (whitespace (*string))
- string++;
+ for (p = string; isblank (to_uchar (*p)); p++)
+ continue;
- if (!*string)
- return false;
-
- /* Save a pointer to the start, for diagnostics. */
- orig_string = string;
-
- /* We allow leading `-' or `+'. */
- if (*string == '-' || *string == '+')
+ if (*p == '+')
{
- if (!digit (string[1]))
- return false;
-
- if (*string == '-')
- sign = -1;
-
- string++;
+ p++;
+ number_start = p;
}
-
- while (digit (*string))
+ else
{
- if (result)
- {
- intmax_t new_v = 10 * value + sign * (*string - '0');
- if (0 < sign
- ? (INTMAX_MAX / 10 < value || new_v < 0)
- : (value < INTMAX_MIN / 10 || 0 < new_v))
- test_syntax_error ((0 < sign
- ? _("integer is too large: %s\n")
- : _("integer is too small: %s\n")),
- orig_string);
- value = new_v;
- }
- string++;
+ number_start = p;
+ p += (*p == '-');
}
- /* Skip trailing whitespace, if any. */
- while (whitespace (*string))
- string++;
-
- /* Error if not at end of string. */
- if (*string)
- return false;
-
- if (result)
- *result = value;
+ if (ISDIGIT (*p++))
+ {
+ while (ISDIGIT (*p))
+ p++;
+ while (isblank (to_uchar (*p)))
+ p++;
+ if (!*p)
+ return number_start;
+ }
- return true;
+ test_syntax_error (_("invalid integer %s"), quote (string));
}
-/* Find the modification time of FILE, and stuff it into *AGE.
+/* Find the modification time of FILE, and stuff it into *MTIME.
Return true if successful. */
static bool
-age_of (char const *filename, time_t *age)
+get_mtime (char const *filename, struct timespec *mtime)
{
struct stat finfo;
bool ok = (stat (filename, &finfo) == 0);
+#ifdef lint
+ static struct timespec const zero;
+ *mtime = zero;
+#endif
if (ok)
- *age = finfo.st_mtime;
+ *mtime = get_stat_mtime (&finfo);
return ok;
}
value = posixtest (nargs);
if (argv[pos] == 0)
- test_syntax_error (_("')' expected\n"), NULL);
+ test_syntax_error (_("')' expected"), NULL);
else
if (argv[pos][0] != ')' || argv[pos][1])
- test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
+ test_syntax_error (_("')' expected, found %s"), argv[pos]);
advance (false);
}
if (test_unop (argv[pos]))
value = unary_operator ();
else
- test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
+ test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
{
{
int op;
struct stat stat_buf, stat_spare;
- intmax_t l, r;
/* Is the right integer expression of the form '-l string'? */
bool r_is_l;
if (argv[op][0] == '-')
{
/* check for eq, nt, and stuff */
+ if ((((argv[op][1] == 'l' || argv[op][1] == 'g')
+ && (argv[op][2] == 'e' || argv[op][2] == 't'))
+ || (argv[op][1] == 'e' && argv[op][2] == 'q')
+ || (argv[op][1] == 'n' && argv[op][2] == 'e'))
+ && !argv[op][3])
+ {
+ char lbuf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char rbuf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char const *l = (l_is_l
+ ? umaxtostr (strlen (argv[op - 1]), lbuf)
+ : find_int (argv[op - 1]));
+ char const *r = (r_is_l
+ ? umaxtostr (strlen (argv[op + 2]), rbuf)
+ : find_int (argv[op + 1]));
+ int cmp = strintcmp (l, r);
+ bool xe_operator = (argv[op][2] == 'e');
+ pos += 3;
+ return (argv[op][1] == 'l' ? cmp < xe_operator
+ : argv[op][1] == 'g' ? cmp > - xe_operator
+ : (cmp != 0) == xe_operator);
+ }
+
switch (argv[op][1])
{
default:
break;
- case 'l':
- if (argv[op][2] == 't' && !argv[op][3])
- {
- /* lt */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -lt"));
- }
-
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -lt"));
- }
- pos += 3;
- return l < r;
- }
-
- if (argv[op][2] == 'e' && !argv[op][3])
- {
- /* le */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -le"));
- }
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -le"));
- }
- pos += 3;
- return l <= r;
- }
- break;
-
- case 'g':
- if (argv[op][2] == 't' && !argv[op][3])
- {
- /* gt integer greater than */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -gt"));
- }
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -gt"));
- }
- pos += 3;
- return l > r;
- }
-
- if (argv[op][2] == 'e' && !argv[op][3])
- {
- /* ge - integer greater than or equal to */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -ge"));
- }
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -ge"));
- }
- pos += 3;
- return l >= r;
- }
- break;
-
case 'n':
if (argv[op][2] == 't' && !argv[op][3])
{
/* nt - newer than */
- time_t lt, rt;
+ struct timespec lt, rt;
bool le, re;
pos += 3;
if (l_is_l | r_is_l)
- test_syntax_error (_("-nt does not accept -l\n"), NULL);
- le = age_of (argv[op - 1], <);
- re = age_of (argv[op + 1], &rt);
- return le > re || (le && lt > rt);
- }
-
- if (argv[op][2] == 'e' && !argv[op][3])
- {
- /* ne - integer not equal */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -ne"));
- }
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -ne"));
- }
- pos += 3;
- return l != r;
+ test_syntax_error (_("-nt does not accept -l"), NULL);
+ le = get_mtime (argv[op - 1], <);
+ re = get_mtime (argv[op + 1], &rt);
+ return le && (!re || timespec_cmp (lt, rt) > 0);
}
break;
case 'e':
- if (argv[op][2] == 'q' && !argv[op][3])
- {
- /* eq - integer equal */
- if (l_is_l)
- l = strlen (argv[op - 1]);
- else
- {
- if (!is_int (argv[op - 1], &l))
- integer_expected_error (_("before -eq"));
- }
- if (r_is_l)
- r = strlen (argv[op + 2]);
- else
- {
- if (!is_int (argv[op + 1], &r))
- integer_expected_error (_("after -eq"));
- }
- pos += 3;
- return l == r;
- }
-
if (argv[op][2] == 'f' && !argv[op][3])
{
/* ef - hard link? */
pos += 3;
if (l_is_l | r_is_l)
- test_syntax_error (_("-ef does not accept -l\n"), NULL);
+ test_syntax_error (_("-ef does not accept -l"), NULL);
return (stat (argv[op - 1], &stat_buf) == 0
&& stat (argv[op + 1], &stat_spare) == 0
&& stat_buf.st_dev == stat_spare.st_dev
if ('t' == argv[op][2] && '\000' == argv[op][3])
{
/* ot - older than */
- time_t lt, rt;
+ struct timespec lt, rt;
bool le, re;
pos += 3;
if (l_is_l | r_is_l)
- test_syntax_error (_("-ot does not accept -l\n"), NULL);
- le = age_of (argv[op - 1], <);
- re = age_of (argv[op + 1], &rt);
- return le < re || (re && lt < rt);
+ test_syntax_error (_("-ot does not accept -l"), NULL);
+ le = get_mtime (argv[op - 1], <);
+ re = get_mtime (argv[op + 1], &rt);
+ return re && (!le || timespec_cmp (lt, rt) < 0);
}
break;
}
/* FIXME: is this dead code? */
- test_syntax_error (_("unknown binary operator\n"), argv[op]);
+ test_syntax_error (_("unknown binary operator"), argv[op]);
}
if (argv[op][0] == '=' && !argv[op][1])
case 't': /* File (fd) is a terminal? */
{
- intmax_t fd;
+ long int fd;
+ char const *arg;
unary_advance ();
- if (!is_int (argv[pos - 1], &fd))
- integer_expected_error (_("after -t"));
- return INT_MIN <= fd && fd <= INT_MAX && isatty (fd);
+ arg = find_int (argv[pos - 1]);
+ errno = 0;
+ fd = strtol (arg, NULL, 10);
+ return (errno != ERANGE && 0 <= fd && fd <= INT_MAX && isatty (fd));
}
case 'n': /* True if arg has some length. */
if (test_unop (argv[pos]))
value = unary_operator ();
else
- test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
+ test_syntax_error (_("%s: unary operator expected"), argv[pos]);
}
else
beyond ();
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]);
+ test_syntax_error (_("%s: binary operator expected"), argv[pos+1]);
return (value);
}
return (value);
}
-#if defined (TEST_STANDALONE)
+#if defined TEST_STANDALONE
# include "long-options.h"
void
Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
"), stdout);
+ fputs (_("\
+\n\
+NOTE: [ honors the --help and --version options, but test does not.\n\
+test treats each of those as it treats any other nonempty STRING.\n\
+"), stdout);
printf (USAGE_BUILTIN_WARNING, _("test and/or ["));
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_bug_reporting_address ();
}
exit (status);
}
#endif /* TEST_STANDALONE */
-#if !defined (TEST_STANDALONE)
+#if !defined TEST_STANDALONE
# define main test_command
#endif
-#define AUTHORS "Kevin Braunsdorf", "Matthew Bradburn"
+#define AUTHORS \
+ proper_name ("Kevin Braunsdorf"), \
+ proper_name ("Matthew Bradburn")
/*
* [:
{
bool value;
-#if !defined (TEST_STANDALONE)
+#if !defined TEST_STANDALONE
int code;
code = setjmp (test_exit_buf);
return (test_error_return);
#else /* TEST_STANDALONE */
initialize_main (&margc, &margv);
- program_name = margv[0];
+ set_program_name (margv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
"[" 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. */
+ to exit silently with status 0. */
if (margc < 2 || !STREQ (margv[margc - 1], "]"))
{
- parse_long_options (margc, margv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
+ parse_long_options (margc, margv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
usage, AUTHORS, (char const *) NULL);
- test_syntax_error (_("missing `]'\n"), NULL);
+ test_syntax_error (_("missing `]'"), NULL);
}
--margc;