From b3967821f8b89d00e3f7b6c54c1003976d3202bf Mon Sep 17 00:00:00 2001 From: Peter Avalos Date: Sat, 12 Feb 2011 20:52:35 -1000 Subject: [PATCH] bin/test: Sync with FreeBSD -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 | 121 ++++++++++++++++++++++++++++++------------------ bin/test/test.c | 137 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 174 insertions(+), 84 deletions(-) diff --git a/bin/test/test.1 b/bin/test/test.1 index b743068..cdee449 100644 --- a/bin/test/test.1 +++ b/bin/test/test.1 @@ -1,3 +1,4 @@ +.\"- .\" Copyright (c) 1991, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -33,10 +34,9 @@ .\" 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 \> 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 \< 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 ]" diff --git a/bin/test/test.c b/bin/test/test.c index 533cf4b..6c01b39 100644 --- a/bin/test/test.c +++ b/bin/test/test.c @@ -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 @@ -19,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +31,9 @@ #include "bltin/bltin.h" #include "error.h" #else -static void error(const char *, ...) __dead2 __printflike(1, 2); +#include + +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 -- 1.7.7.2