dd: clarify meaning of multiplication factors; put xM in order
[platform/upstream/coreutils.git] / src / test.c
index c0edcd5..833b254 100644 (file)
@@ -2,23 +2,20 @@
 
 /* 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;
@@ -97,6 +83,7 @@ test_syntax_error (char const *format, char const *arg)
 {
   fprintf (stderr, "%s: ", argv[0]);
   fprintf (stderr, format, arg);
+  fputc ('\n', stderr);
   fflush (stderr);
   test_exit (TEST_FAILURE);
 }
@@ -131,92 +118,55 @@ beyond (void)
   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;
 }
 
@@ -282,10 +232,10 @@ term (void)
 
       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);
     }
 
@@ -301,7 +251,7 @@ term (void)
       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
     {
@@ -317,7 +267,6 @@ binary_operator (bool l_is_l)
 {
   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;
 
@@ -336,164 +285,55 @@ binary_operator (bool l_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], &lt);
-             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], &lt);
+             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
@@ -505,20 +345,20 @@ binary_operator (bool l_is_l)
          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], &lt);
-             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], &lt);
+             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])
@@ -643,11 +483,13 @@ unary_operator (void)
 
     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. */
@@ -754,7 +596,7 @@ two_arguments (void)
       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 ();
@@ -782,7 +624,7 @@ three_arguments (void)
   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);
 }
 
@@ -831,7 +673,7 @@ posixtest (int nargs)
   return (value);
 }
 
-#if defined (TEST_STANDALONE)
+#if defined TEST_STANDALONE
 # include "long-options.h"
 
 void
@@ -924,18 +766,25 @@ Except for -h and -L, all FILE-related tests dereference symbolic links.\n\
 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")
 
 /*
  * [:
@@ -948,7 +797,7 @@ main (int margc, char **margv)
 {
   bool value;
 
-#if !defined (TEST_STANDALONE)
+#if !defined TEST_STANDALONE
   int code;
 
   code = setjmp (test_exit_buf);
@@ -957,7 +806,7 @@ main (int margc, char **margv)
     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);
@@ -974,12 +823,12 @@ main (int margc, char **margv)
         "[" 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;