bin/test: Sync with FreeBSD
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 13 Feb 2011 06:52:35 +0000 (20:52 -1000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 13 Feb 2011 07:06:05 +0000 (21:06 -1000)
-Help /bin/sh pass a few more regression tests.
-Convert to use st_mtim instead of st_mtimespec.
-Fix various cases with 3 or 4 parameters in test(1) to be POSIX
compliant.
-Use intmax_t as a quad replacement instead of long long.
-__printflike() should really be __printf0like().
-Localize (LC_CTYPE).
-Cleanup whitespace.
-Simplify markup in man page.
-Describe how test(1) will evaluate its expressions for a symlink.
-Document that both sides of -a or -o are always evaluated.

Obtained-from: FreeBSD

bin/test/test.1
bin/test/test.c

index b743068..cdee449 100644 (file)
@@ -1,3 +1,4 @@
+.\"-
 .\" Copyright (c) 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
 .\" SUCH DAMAGE.
 .\"
 .\"     @(#)test.1     8.1 (Berkeley) 5/31/93
-.\" $FreeBSD: src/bin/test/test.1,v 1.11.2.5 2001/12/14 14:22:09 ru Exp $
-.\" $DragonFly: src/bin/test/test.1,v 1.7 2007/10/20 17:56:46 swildner Exp $
+.\" $FreeBSD: src/bin/test/test.1,v 1.31 2010/09/11 10:49:56 brucec Exp $
 .\"
-.Dd May 31, 1993
+.Dd September 10, 2010
 .Dt TEST 1
 .Os
 .Sh NAME
@@ -198,75 +198,81 @@ True if
 .Ar string
 is not the null
 string.
-.It Ar \&s\&1 Cm \&= Ar \&s\&2
+.It Ar s1 Cm = Ar s2
 True if the strings
-.Ar \&s\&1
+.Ar s1
 and
-.Ar \&s\&2
+.Ar s2
 are identical.
-.It Ar \&s\&1 Cm \&!= Ar \&s\&2
+.It Ar s1 Cm != Ar s2
 True if the strings
-.Ar \&s\&1
+.Ar s1
 and
-.Ar \&s\&2
+.Ar s2
 are not identical.
-.It Ar \&s\&1 Cm \&< Ar \&s\&2
+.It Ar s1 Cm < Ar s2
 True if string
-.Ar \&s\&1
+.Ar s1
 comes before
-.Ar \&s\&2
-based on the ASCII value of their characters.
-.It Ar \&s\&1 Cm \&> Ar \&s\&2
+.Ar s2
+based on the binary value of their characters.
+.It Ar s1 Cm > Ar s2
 True if string
-.Ar \&s\&1
+.Ar s1
 comes after
-.Ar \&s\&2
-based on the ASCII value of their characters.
-.It Ar \&s\&1
-True if
-.Ar \&s\&1
-is not the null
-string.
-.It Ar \&n\&1 Fl \&eq Ar \&n\&2
+.Ar s2
+based on the binary value of their characters.
+.It Ar n1 Fl eq Ar n2
 True if the integers
-.Ar \&n\&1
+.Ar n1
 and
-.Ar \&n\&2
+.Ar n2
 are algebraically
 equal.
-.It Ar \&n\&1 Fl \&ne Ar \&n\&2
+.It Ar n1 Fl ne Ar n2
 True if the integers
-.Ar \&n\&1
+.Ar n1
 and
-.Ar \&n\&2
+.Ar n2
 are not
 algebraically equal.
-.It Ar \&n\&1 Fl \&gt Ar \&n\&2
+.It Ar n1 Fl gt Ar n2
 True if the integer
-.Ar \&n\&1
+.Ar n1
 is algebraically
 greater than the integer
-.Ar \&n\&2 .
-.It Ar \&n\&1 Fl \&ge Ar \&n\&2
+.Ar n2 .
+.It Ar n1 Fl ge Ar n2
 True if the integer
-.Ar \&n\&1
+.Ar n1
 is algebraically
 greater than or equal to the integer
-.Ar \&n\&2 .
-.It Ar \&n\&1 Fl \&lt Ar \&n\&2
+.Ar n2 .
+.It Ar n1 Fl lt Ar n2
 True if the integer
-.Ar \&n\&1
+.Ar n1
 is algebraically less
 than the integer
-.Ar \&n\&2 .
-.It Ar \&n\&1 Fl \&le Ar \&n\&2
+.Ar n2 .
+.It Ar n1 Fl le Ar n2
 True if the integer
-.Ar \&n\&1
+.Ar n1
 is algebraically less
 than or equal to the integer
-.Ar \&n\&2 .
+.Ar n2 .
 .El
 .Pp
+If
+.Ar file
+is a symbolic link,
+.Nm
+will fully dereference it and then evaluate the expression
+against the file referenced, except for the
+.Fl h
+and
+.Fl L
+primaries.
+.Pp
 These primaries can be combined with the following operators:
 .Bl -tag -width Ar
 .It Cm \&! Ar expression
@@ -285,7 +291,7 @@ True if either
 or
 .Ar expression2
 are true.
-.It Cm \&( Ns Ar expression Ns Cm \&)
+.It Cm \&( Ar expression Cm \&)
 True if expression is true.
 .El
 .Pp
@@ -304,18 +310,28 @@ manual page.
 .Sh GRAMMAR AMBIGUITY
 The
 .Nm
-grammar is inherently ambiguous.  In order to assure a degree of consistency,
+grammar is inherently ambiguous.
+In order to assure a degree of consistency,
 the cases described in the
 .St -p1003.2 ,
 section D11.2/4.62.4, standard
 are evaluated consistently according to the rules specified in the
-standards document.  All other cases are subject to the ambiguity in the
+standards document.
+All other cases are subject to the ambiguity in the
 command semantics.
-.Sh DIAGNOSTICS
+.Pp
+In particular, only expressions containing
+.Fl a ,
+.Fl o ,
+.Cm \&(
+or
+.Cm \&)
+can be ambiguous.
+.Sh EXIT STATUS
 The
 .Nm
 utility exits with one of the following values:
-.Bl -tag -width Ds
+.Bl -tag -width indent
 .It 0
 expression evaluated to true.
 .It 1
@@ -327,10 +343,23 @@ An error occurred.
 .Sh SEE ALSO
 .Xr builtin 1 ,
 .Xr expr 1 ,
-.Xr sh 1
+.Xr sh 1 ,
+.Xr symlink 7
 .Sh STANDARDS
 The
 .Nm
 utility implements a superset of the
 .St -p1003.2
 specification.
+.Sh BUGS
+Both sides are always evaluated in
+.Fl a
+and
+.Fl o .
+For instance, the writable status of
+.Pa file
+will be tested by the following command even though the former expression
+indicated false, which results in a gratuitous access to the file system:
+.Dl "[ -z abc -a -w file ]"
+To avoid this, write
+.Dl "[ -z abc ] && [ -w file ]"
index 533cf4b..6c01b39 100644 (file)
@@ -1,6 +1,6 @@
 /*     $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */
 
-/*
+/*-
  * test(1); version 7-like  --  author Erik Baalbergen
  * modified by Eric Gisin to be used as built-in.
  * modified by Arnold Robbins to add SVR3 compatibility
@@ -9,8 +9,7 @@
  *
  * This program is in the Public Domain.
  *
- * $FreeBSD: src/bin/test/test.c,v 1.29.2.7 2002/09/10 09:10:57 maxim Exp $
- * $DragonFly: src/bin/test/test.c,v 1.7 2004/11/07 19:42:16 eirikn Exp $
+ * $FreeBSD: src/bin/test/test.c,v 1.55 2010/03/28 13:16:08 ed Exp $
  */
 
 #include <sys/types.h>
@@ -19,6 +18,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -31,7 +31,9 @@
 #include "bltin/bltin.h"
 #include "error.h"
 #else
-static void error(const char *, ...) __dead2 __printflike(1, 2);
+#include <locale.h>
+
+static void error(const char *, ...) __dead2 __printf0like(1, 2);
 
 static void
 error(const char *msg, ...)
@@ -162,22 +164,25 @@ struct t_op {
 struct t_op const *t_wp_op;
 int nargc;
 char **t_wp;
-
-static int     aexpr (enum token);
-static int     binop (void);
-static int     equalf (const char *, const char *);
-static int     filstat (char *, enum token);
-static int     getn (const char *);
-static long long getll (const char *);
-static int     intcmp (const char *, const char *);
-static int     isoperand (void);
-static int     newerf (const char *, const char *);
-static int     nexpr (enum token);
-static int     oexpr (enum token);
-static int     olderf (const char *, const char *);
-static int     primary (enum token);
-static void    syntax (const char *, const char *);
-static enum    token t_lex (char *);
+int parenlevel;
+
+static int     aexpr(enum token);
+static int     binop(void);
+static int     equalf(const char *, const char *);
+static int     filstat(char *, enum token);
+static int     getn(const char *);
+static intmax_t        getq(const char *);
+static int     intcmp(const char *, const char *);
+static int     isunopoperand(void);
+static int     islparenoperand(void);
+static int     isrparenoperand(void);
+static int     newerf(const char *, const char *);
+static int     nexpr(enum token);
+static int     oexpr(enum token);
+static int     olderf(const char *, const char *);
+static int     primary(enum token);
+static void    syntax(const char *, const char *);
+static enum    token t_lex(char *);
 
 int
 main(int argc, char **argv)
@@ -201,6 +206,9 @@ main(int argc, char **argv)
        if (--argc <= 0)
                return 1;
 
+#ifndef SHELL
+       setlocale(LC_CTYPE, "");
+#endif
        /* XXX work around the absence of an eaccess(2) syscall */
        egid = getegid();
        euid = geteuid();
@@ -211,7 +219,14 @@ main(int argc, char **argv)
 
        nargc = argc;
        t_wp = &argv[1];
-       res = !oexpr(t_lex(*t_wp));
+       parenlevel = 0;
+       if (nargc == 4 && strcmp(*t_wp, "!") == 0) {
+               /* Things like ! "" -o x do not fit in the normal grammar. */
+               --nargc;
+               ++t_wp;
+               res = oexpr(t_lex(*t_wp));
+       } else
+               res = !oexpr(t_lex(*t_wp));
 
        if (--nargc > 0)
                syntax(*t_wp, "unexpected operator");
@@ -276,12 +291,16 @@ primary(enum token n)
        if (n == EOI)
                return 0;               /* missing expression */
        if (n == LPAREN) {
+               parenlevel++;
                if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) ==
-                   RPAREN)
+                   RPAREN) {
+                       parenlevel--;
                        return 0;       /* missing expression */
+               }
                res = oexpr(nn);
                if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN)
                        syntax(NULL, "closing paren expected");
+               parenlevel--;
                return res;
        }
        if (t_wp_op && t_wp_op->op_type == UNOP) {
@@ -418,8 +437,10 @@ t_lex(char *s)
        }
        while (op->op_text) {
                if (strcmp(s, op->op_text) == 0) {
-                       if ((op->op_type == UNOP && isoperand()) ||
-                           (op->op_num == LPAREN && nargc == 1))
+                       if (((op->op_type == UNOP || op->op_type == BUNOP)
+                                               && isunopoperand()) ||
+                           (op->op_num == LPAREN && islparenoperand()) ||
+                           (op->op_num == RPAREN && isrparenoperand()))
                                break;
                        t_wp_op = op;
                        return op->op_num;
@@ -431,7 +452,7 @@ t_lex(char *s)
 }
 
 static int
-isoperand(void)
+isunopoperand(void)
 {
        struct t_op const *op = ops;
        char *s;
@@ -439,19 +460,53 @@ isoperand(void)
 
        if (nargc == 1)
                return 1;
-       if (nargc == 2)
-               return 0;
        s = *(t_wp + 1);
+       if (nargc == 2)
+               return parenlevel == 1 && strcmp(s, ")") == 0;
        t = *(t_wp + 2);
        while (op->op_text) {
                if (strcmp(s, op->op_text) == 0)
                        return op->op_type == BINOP &&
-                           (t[0] != ')' || t[1] != '\0');
+                           (parenlevel == 0 || t[0] != ')' || t[1] != '\0');
                op++;
        }
        return 0;
 }
 
+static int
+islparenoperand(void)
+{
+       struct t_op const *op = ops;
+       char *s;
+
+       if (nargc == 1)
+               return 1;
+       s = *(t_wp + 1);
+       if (nargc == 2)
+               return parenlevel == 1 && strcmp(s, ")") == 0;
+       if (nargc != 3)
+               return 0;
+       while (op->op_text) {
+               if (strcmp(s, op->op_text) == 0)
+                       return op->op_type == BINOP;
+               op++;
+       }
+       return 0;
+}
+
+static int
+isrparenoperand(void)
+{
+       char *s;
+
+       if (nargc == 1)
+               return 0;
+       s = *(t_wp + 1);
+       if (nargc == 2)
+               return parenlevel == 1 && strcmp(s, ")") == 0;
+       return 0;
+}
+
 /* atoi with error detection */
 static int
 getn(const char *s)
@@ -462,6 +517,9 @@ getn(const char *s)
        errno = 0;
        r = strtol(s, &p, 10);
 
+       if (s == p)
+               error("%s: bad number", s);
+
        if (errno != 0)
                error((errno == EINVAL) ? "%s: bad number" :
                                          "%s: out of range", s);
@@ -476,14 +534,17 @@ getn(const char *s)
 }
 
 /* atoi with error detection and 64 bit range */
-static long long
-getll(const char *s)
+static intmax_t
+getq(const char *s)
 {
        char *p;
-       long long r;
+       intmax_t r;
 
        errno = 0;
-       r = strtoll(s, &p, 10);
+       r = strtoimax(s, &p, 10);
+
+       if (s == p)
+               error("%s: bad number", s);
 
        if (errno != 0)
                error((errno == EINVAL) ? "%s: bad number" :
@@ -501,11 +562,11 @@ getll(const char *s)
 static int
 intcmp (const char *s1, const char *s2)
 {
-       long long q1, q2;
+       intmax_t q1, q2;
 
 
-       q1 = getll(s1);
-       q2 = getll(s2);
+       q1 = getq(s1);
+       q2 = getq(s2);
 
        if (q1 > q2)
                return 1;
@@ -524,12 +585,12 @@ newerf (const char *f1, const char *f2)
        if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0)
                return 0;
 
-       if (b1.st_mtimespec.tv_sec > b2.st_mtimespec.tv_sec)
+       if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec)
                return 1;
-       if (b1.st_mtimespec.tv_sec < b2.st_mtimespec.tv_sec)
+       if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec)
                return 0;
 
-       return (b1.st_mtimespec.tv_nsec > b2.st_mtimespec.tv_nsec);
+       return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec);
 }
 
 static int