summaryrefslogtreecommitdiffstats
path: root/bin/test
diff options
context:
space:
mode:
authorsheldonh <sheldonh@FreeBSD.org>1999-08-16 09:44:09 +0000
committersheldonh <sheldonh@FreeBSD.org>1999-08-16 09:44:09 +0000
commit90a5109c16c32f86f9a2c8968a2e4abf85d71ed3 (patch)
tree22bc48aad6566d71b90235fa534be590d589165f /bin/test
parent122d640213334cd6872cf7247331d2c89b463b7a (diff)
downloadFreeBSD-src-90a5109c16c32f86f9a2c8968a2e4abf85d71ed3.zip
FreeBSD-src-90a5109c16c32f86f9a2c8968a2e4abf85d71ed3.tar.gz
Replace our test(1) with NetBSD's pdksh-derived version. The code is
significantly easier to read and extend and offers a few new tests. A few style changes taken from style(9) and OpenBSD, as well as whitespace cleanups. This change was discussed on freebsd-committers and freebsd-hackers and met with approval from at least des, eivind and brian. PR: 13091 Obtained from: NetBSD
Diffstat (limited to 'bin/test')
-rw-r--r--bin/test/Makefile3
-rw-r--r--bin/test/operators.c165
-rw-r--r--bin/test/operators.h80
-rw-r--r--bin/test/test.191
-rw-r--r--bin/test/test.c911
5 files changed, 465 insertions, 785 deletions
diff --git a/bin/test/Makefile b/bin/test/Makefile
index a3a8126..5b1f2e1 100644
--- a/bin/test/Makefile
+++ b/bin/test/Makefile
@@ -1,8 +1,7 @@
# @(#)Makefile 8.1 (Berkeley) 5/31/93
-# $Id$
+# $Id: Makefile,v 1.4 1997/02/22 14:06:17 peter Exp $
PROG= test
-SRCS= test.c operators.c
LINKS= ${BINDIR}/test ${BINDIR}/[
MLINKS= test.1 '[.1'
diff --git a/bin/test/operators.c b/bin/test/operators.c
deleted file mode 100644
index 9ebce4a..0000000
--- a/bin/test/operators.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/*-
- * Copyright (c) 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)operators.c 8.3 (Berkeley) 4/2/94";
-#endif
-static const char rcsid[] =
- "$Id: operators.c,v 1.7 1998/05/18 06:51:53 charnier Exp $";
-#endif /* not lint */
-
-/*
- * Operators used in the test command.
- */
-
-#include <stdio.h>
-
-#include "operators.h"
-
-const char *const unary_op[] = {
- "!",
- "-b",
- "-c",
- "-d",
- "-e",
- "-f",
- "-g",
- "-h",
- "-k",
- "-n",
- "-p",
- "-r",
- "-s",
- "-t",
- "-u",
- "-w",
- "-x",
- "-z",
- "-S",
- NULL
-};
-
-const char *const binary_op[] = {
- "-o",
- "|",
- "-a",
- "&",
- "=",
- "!=",
- "-eq",
- "-ne",
- "-gt",
- "-lt",
- "-le",
- "-ge",
- NULL
-};
-
-const char *const andor_op[] = {
- "-o",
- "|",
- "-a",
- "&",
- NULL
-};
-
-
-
-const char op_priority[] = {
- 3,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 12,
- 1,
- 1,
- 2,
- 2,
- 4,
- 4,
- 4,
- 4,
- 4,
- 4,
- 4,
- 4,
-};
-
-const char op_argflag[] = {
- 0,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_STRING,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_INT,
- OP_FILE,
- OP_FILE,
- OP_FILE,
- OP_STRING,
- OP_FILE,
- 0,
- 0,
- 0,
- 0,
- OP_STRING,
- OP_STRING,
- OP_INT,
- OP_INT,
- OP_INT,
- OP_INT,
- OP_INT,
- OP_INT,
-};
diff --git a/bin/test/operators.h b/bin/test/operators.h
deleted file mode 100644
index be8620e..0000000
--- a/bin/test/operators.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*-
- * Copyright (c) 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * @(#)operators.h 8.3 (Berkeley) 4/2/94
- * $Id: operators.h,v 1.5 1997/02/22 14:06:22 peter Exp $
- */
-
-#define NOT 0
-#define ISBLOCK 1
-#define ISCHAR 2
-#define ISDIR 3
-#define ISEXIST 4
-#define ISFILE 5
-#define ISSETGID 6
-#define ISSYMLINK 7
-#define ISSTICKY 8
-#define STRLEN 9
-#define ISFIFO 10
-#define ISREAD 11
-#define ISSIZE 12
-#define ISTTY 13
-#define ISSETUID 14
-#define ISWRITE 15
-#define ISEXEC 16
-#define NULSTR 17
-#define ISSOCK 18
-
-#define FIRST_BINARY_OP 19
-#define OR1 FIRST_BINARY_OP
-#define OR2 (FIRST_BINARY_OP + 1)
-#define AND1 (FIRST_BINARY_OP + 2)
-#define AND2 (FIRST_BINARY_OP + 3)
-#define STREQ (FIRST_BINARY_OP + 4)
-#define STRNE (FIRST_BINARY_OP + 5)
-#define EQ (FIRST_BINARY_OP + 6)
-#define NE (FIRST_BINARY_OP + 7)
-#define GT (FIRST_BINARY_OP + 8)
-#define LT (FIRST_BINARY_OP + 9)
-#define LE (FIRST_BINARY_OP + 10)
-#define GE (FIRST_BINARY_OP + 11)
-
-
-#define OP_INT 1 /* arguments to operator are integer */
-#define OP_STRING 2 /* arguments to operator are string */
-#define OP_FILE 3 /* argument is a file name */
-
-extern const char *const unary_op[];
-extern const char *const binary_op[];
-extern const char *const andor_op[];
-extern const char op_priority[];
-extern const char op_argflag[];
diff --git a/bin/test/test.1 b/bin/test/test.1
index 45a84e3..bfe342b 100644
--- a/bin/test/test.1
+++ b/bin/test/test.1
@@ -33,20 +33,23 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)test.1 8.1 (Berkeley) 5/31/93
-.\" $Id: test.1,v 1.7 1998/09/07 16:59:05 cracauer Exp $
+.\" $Id: test.1,v 1.8 1998/12/18 03:16:47 jkoshy Exp $
.\"
.Dd May 31, 1993
.Dt TEST 1
.Os
.Sh NAME
-.Nm test
+.Nm test ,
+.Nm \&[
.Nd condition evaluation utility
.Sh SYNOPSIS
.Nm test
.Ar expression
+.Nm [
+.Ar expression Cm ]
.Sh DESCRIPTION
The
-.Nm
+.Nm test
utility evaluates the expression and, if it evaluates
to true, returns a zero (true) exit status; otherwise
it returns 1 (false).
@@ -54,11 +57,11 @@ If there is no expression, test also
returns 1 (false).
.Pp
All operators and flags are separate arguments to the
-.Nm
+.Nm test
utility.
.Pp
The following primaries are used to construct expression:
-.Bl -tag -width indent
+.Bl -tag -width Ar
.It Fl b Ar file
True if
.Ar file
@@ -90,6 +93,14 @@ is set.
True if
.Ar file
exists and is a symbolic link.
+This operator is retained for compatibility with previous versions of
+this program. Do not rely on its existence; use
+.Fl L
+instead.
+.It Fl k Ar file
+True if
+.Ar file
+exists and its sticky bit is set.
.It Fl n Ar string
True if the length of
.Ar string
@@ -99,25 +110,20 @@ True if
.Ar file
is a named pipe
.Po Tn FIFO Pc .
-.It Fl S Ar file
-True if
-.Ar file
-is a socket.
.It Fl r Ar file
True if
-.Ar file
+.Ar file
exists and is readable.
.It Fl s Ar file
True if
.Ar file
exists and has a size greater
than zero.
-.It Fl t Ar [file_descriptor]
+.It Fl t Op Ar file_descriptor
True if the file whose file descriptor number
is
.Ar file_descriptor
-(default 1) is open and is
-associated with a terminal.
+is open and is associated with a terminal.
.It Fl u Ar file
True if
.Ar file
@@ -146,6 +152,38 @@ can be searched.
True if the length of
.Ar string
is zero.
+.It Fl L Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+.It Fl O Ar file
+True if
+.Ar file
+exists and its owner matches the effective user id of this process.
+.It Fl G Ar file
+True if
+.Ar file
+exists and its group matches the effective group id of this process.
+.It Fl S Ar file
+True if
+.Ar file
+exists and is a socket.
+.It Ar file1 Fl nt Ar file2
+True if
+.Ar file1
+exists and is newer than
+.Ar file2 .
+.It Ar file1 Fl ot Ar file2
+True if
+.Ar file1
+exists and is older than
+.Ar file2 .
+.It Ar file1 Fl ef Ar file2
+True if
+.Ar file1
+and
+.Ar file2
+exist and refer to the same file.
.It Ar string
True if
.Ar string
@@ -163,6 +201,23 @@ True if the strings
and
.Ar \&s\&2
are not identical.
+.It Ar \&s\&1 Cm \&< Ar \&s\&2
+True if string
+.Ar \&s\&1
+comes before
+.Ar \&s\&2
+based on the ASCII value of their characters.
+.It Ar \&s\&1 Cm \&> Ar \&s\&2
+True if string
+.Ar \&s\&1
+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
True if the integers
.Ar \&n\&1
@@ -232,7 +287,7 @@ operator has higher precedence than the
operator.
.Sh GRAMMAR AMBIGUITY
The
-.Nm
+.Nm test
grammar is inherently ambiguous. In order to assure a degree of consistency,
the cases described in the
.St -p1003.2 ,
@@ -242,7 +297,7 @@ standards document. All other cases are subject to the ambiguity in the
command semantics.
.Sh RETURN VALUES
The
-.Nm
+.Nm test
utility exits with one of the following values:
.Bl -tag -width Ds
.It 0
@@ -258,7 +313,7 @@ An error occurred.
.Xr sh 1
.Sh STANDARDS
The
-.Nm
-function is expected to be
+.Nm test
+utility implements a superset of the
.St -p1003.2
-compatible.
+specification.
diff --git a/bin/test/test.c b/bin/test/test.c
index e6cdf55..a4ae0cd 100644
--- a/bin/test/test.c
+++ b/bin/test/test.c
@@ -1,593 +1,464 @@
-/*-
- * Copyright (c) 1992, 1993, 1994
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
+/* $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
+ * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
+ * modified by J.T. Conklin for NetBSD.
*
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * This program is in the Public Domain.
*/
#ifndef lint
-static char const copyright[] =
-"@(#) Copyright (c) 1992, 1993, 1994\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94";
-#endif
static const char rcsid[] =
- "$Id: test.c,v 1.21 1999/05/08 10:22:15 kris Exp $";
+ "$Id: test.c,v 1.22 1999/08/14 05:38:04 chris Exp $";
#endif /* not lint */
-#include <sys/param.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
-#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "operators.h"
-
-#define STACKSIZE 12
-#define NESTINCR 16
-
-/* data types */
-#define STRING 0
-#define INTEGER 1
-#define BOOLEAN 2
-
-#define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
-
-/*
- * This structure hold a value. The type keyword specifies the type of
- * the value, and the union u holds the value. The value of a boolean
- * is stored in u.num (1 = TRUE, 0 = FALSE).
- */
-struct value {
- int type;
- union {
- char *string;
- long num;
- } u;
+/* test(1) accepts the following grammar:
+ oexpr ::= aexpr | aexpr "-o" oexpr ;
+ aexpr ::= nexpr | nexpr "-a" aexpr ;
+ nexpr ::= primary | "!" primary
+ primary ::= unary-operator operand
+ | operand binary-operator operand
+ | operand
+ | "(" oexpr ")"
+ ;
+ unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
+ "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
+
+ binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+ "-nt"|"-ot"|"-ef";
+ operand ::= <any legal UNIX file name>
+*/
+
+enum token {
+ EOI,
+ FILRD,
+ FILWR,
+ FILEX,
+ FILEXIST,
+ FILREG,
+ FILDIR,
+ FILCDEV,
+ FILBDEV,
+ FILFIFO,
+ FILSOCK,
+ FILSYM,
+ FILGZ,
+ FILTT,
+ FILSUID,
+ FILSGID,
+ FILSTCK,
+ FILNT,
+ FILOT,
+ FILEQ,
+ FILUID,
+ FILGID,
+ STREZ,
+ STRNZ,
+ STREQ,
+ STRNE,
+ STRLT,
+ STRGT,
+ INTEQ,
+ INTNE,
+ INTGE,
+ INTGT,
+ INTLE,
+ INTLT,
+ UNOT,
+ BAND,
+ BOR,
+ LPAREN,
+ RPAREN,
+ OPERAND
};
-struct operator {
- short op; /* Which operator. */
- short pri; /* Priority of operator. */
+enum token_types {
+ UNOP,
+ BINOP,
+ BUNOP,
+ BBINOP,
+ PAREN
};
-struct filestat {
- char *name; /* Name of file. */
- int rcode; /* Return code from stat. */
- struct stat stat; /* Status info on file. */
+struct t_op {
+ const char *op_text;
+ short op_num, op_type;
+} const ops [] = {
+ {"-r", FILRD, UNOP},
+ {"-w", FILWR, UNOP},
+ {"-x", FILEX, UNOP},
+ {"-e", FILEXIST,UNOP},
+ {"-f", FILREG, UNOP},
+ {"-d", FILDIR, UNOP},
+ {"-c", FILCDEV,UNOP},
+ {"-b", FILBDEV,UNOP},
+ {"-p", FILFIFO,UNOP},
+ {"-u", FILSUID,UNOP},
+ {"-g", FILSGID,UNOP},
+ {"-k", FILSTCK,UNOP},
+ {"-s", FILGZ, UNOP},
+ {"-t", FILTT, UNOP},
+ {"-z", STREZ, UNOP},
+ {"-n", STRNZ, UNOP},
+ {"-h", FILSYM, UNOP}, /* for backwards compat */
+ {"-O", FILUID, UNOP},
+ {"-G", FILGID, UNOP},
+ {"-L", FILSYM, UNOP},
+ {"-S", FILSOCK,UNOP},
+ {"=", STREQ, BINOP},
+ {"!=", STRNE, BINOP},
+ {"<", STRLT, BINOP},
+ {">", STRGT, BINOP},
+ {"-eq", INTEQ, BINOP},
+ {"-ne", INTNE, BINOP},
+ {"-ge", INTGE, BINOP},
+ {"-gt", INTGT, BINOP},
+ {"-le", INTLE, BINOP},
+ {"-lt", INTLT, BINOP},
+ {"-nt", FILNT, BINOP},
+ {"-ot", FILOT, BINOP},
+ {"-ef", FILEQ, BINOP},
+ {"!", UNOT, BUNOP},
+ {"-a", BAND, BBINOP},
+ {"-o", BOR, BBINOP},
+ {"(", LPAREN, PAREN},
+ {")", RPAREN, PAREN},
+ {0, 0, 0}
};
-static int expr_is_false __P((struct value *));
-static void expr_operator __P((int, struct value *, struct filestat *));
-static void get_int __P((char *, long *));
-static int lookup_op __P((char *, const char *const *));
-static void overflow __P((void));
-static int posix_binary_op __P((char **));
-static int posix_unary_op __P((char **));
-static void syntax __P((void));
+struct t_op const *t_wp_op;
+char **t_wp;
+
+static void syntax __P((const char *, const char *));
+static enum token t_lex __P((char *));
+static int oexpr __P((enum token));
+static int aexpr __P((enum token));
+static int nexpr __P((enum token));
+static int primary __P((enum token));
+static int binop __P((void));
+static int filstat __P((char *, enum token));
+static int isoperand __P((void));
+static int getn __P((const char *));
+static int newerf __P((const char *, const char *));
+static int olderf __P((const char *, const char *));
+static int equalf __P((const char *, const char *));
int
main(argc, argv)
int argc;
- char *argv[];
+ char **argv;
{
- struct operator opstack[STACKSIZE];
- struct operator *opsp;
- struct value valstack[STACKSIZE + 1];
- struct value *valsp;
- struct filestat fs;
- char c, **ap, *opname, *p;
- int binary, nest, op = 0, pri, ret_val, skipping;
-
- if ((p = argv[0]) == NULL)
- errx(2, "test: argc is zero");
-
- if (*p != '\0' && p[strlen(p) - 1] == '[') {
+ int res;
+
+ if (strcmp(argv[0], "[") == 0) {
if (strcmp(argv[--argc], "]"))
errx(2, "missing ]");
argv[argc] = NULL;
}
- ap = argv + 1;
- fs.name = NULL;
-
- /*
- * Test(1) implements an inherently ambiguous grammar. In order to
- * assure some degree of consistency, we special case the POSIX 1003.2
- * requirements to assure correct evaluation for POSIX scripts. The
- * following special cases comply with POSIX P1003.2/D11.2 Section
- * 4.62.4.
- */
- switch(argc - 1) {
- case 0: /* % test */
- return (1);
- break;
- case 1: /* % test arg */
- return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
- break;
- case 2: /* % test op arg */
- opname = argv[1];
- if (IS_BANG(opname))
- return (*argv[2] == '\0') ? 0 : 1;
- else {
- ret_val = posix_unary_op(&argv[1]);
- if (ret_val >= 0)
- return (ret_val);
- }
- break;
- case 3: /* % test arg1 op arg2 */
- if (IS_BANG(argv[1])) {
- ret_val = posix_unary_op(&argv[1]);
- if (ret_val >= 0)
- return (!ret_val);
- } else if (lookup_op(argv[2], andor_op) < 0) {
- ret_val = posix_binary_op(&argv[1]);
- if (ret_val >= 0)
- return (ret_val);
- }
- break;
- case 4: /* % test ! arg1 op arg2 */
- if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) {
- ret_val = posix_binary_op(&argv[2]);
- if (ret_val >= 0)
- return (!ret_val);
- }
- break;
- default:
- break;
- }
- /*
- * We use operator precedence parsing, evaluating the expression as
- * we parse it. Parentheses are handled by bumping up the priority
- * of operators using the variable "nest." We use the variable
- * "skipping" to turn off evaluation temporarily for the short
- * circuit boolean operators. (It is important do the short circuit
- * evaluation because under NFS a stat operation can take infinitely
- * long.)
- */
- opsp = opstack + STACKSIZE;
- valsp = valstack;
- nest = skipping = 0;
- if (*ap == NULL) {
- valstack[0].type = BOOLEAN;
- valstack[0].u.num = 0;
- goto done;
- }
- for (;;) {
- opname = *ap++;
- if (opname == NULL)
- syntax();
- if (opname[0] == '(' && opname[1] == '\0') {
- nest += NESTINCR;
- continue;
- } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
- if (opsp == &opstack[0])
- overflow();
- --opsp;
- opsp->op = op;
- opsp->pri = op_priority[op] + nest;
- continue;
- } else {
- valsp->type = STRING;
- valsp->u.string = opname;
- valsp++;
- }
- for (;;) {
- opname = *ap++;
- if (opname == NULL) {
- if (nest != 0)
- syntax();
- pri = 0;
- break;
- }
- if (opname[0] != ')' || opname[1] != '\0') {
- if ((op = lookup_op(opname, binary_op)) < 0)
- syntax();
- op += FIRST_BINARY_OP;
- pri = op_priority[op] + nest;
- break;
- }
- if ((nest -= NESTINCR) < 0)
- syntax();
- }
- while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
- binary = opsp->op;
- for (;;) {
- valsp--;
- c = op_argflag[opsp->op];
- if (c == OP_INT) {
- if (valsp->type == STRING)
- get_int(valsp->u.string,
- &valsp->u.num);
- valsp->type = INTEGER;
- } else if (c >= OP_STRING) {
- /* OP_STRING or OP_FILE */
- if (valsp->type == INTEGER) {
- if ((p = malloc(32)) == NULL)
- err(2, NULL);
-#ifdef SHELL
- fmtstr(p, 32, "%d",
- valsp->u.num);
-#else
- (void)sprintf(p,
- "%ld", valsp->u.num);
-#endif
- valsp->u.string = p;
- } else if (valsp->type == BOOLEAN) {
- if (valsp->u.num)
- valsp->u.string =
- "true";
- else
- valsp->u.string = "";
- }
- valsp->type = STRING;
- if (c == OP_FILE && (fs.name == NULL ||
- strcmp(fs.name, valsp->u.string))) {
- fs.name = valsp->u.string;
- fs.rcode =
- stat(valsp->u.string,
- &fs.stat);
- }
- }
- if (binary < FIRST_BINARY_OP)
- break;
- binary = 0;
- }
- if (!skipping)
- expr_operator(opsp->op, valsp, &fs);
- else if (opsp->op == AND1 || opsp->op == OR1)
- skipping--;
- valsp++; /* push value */
- opsp++; /* pop operator */
- }
- if (opname == NULL)
- break;
- if (opsp == &opstack[0])
- overflow();
- if (op == AND1 || op == AND2) {
- op = AND1;
- if (skipping || expr_is_false(valsp - 1))
- skipping++;
- }
- if (op == OR1 || op == OR2) {
- op = OR1;
- if (skipping || !expr_is_false(valsp - 1))
- skipping++;
- }
- opsp--;
- opsp->op = op;
- opsp->pri = pri;
- }
-done: return (expr_is_false(&valstack[0]));
+ t_wp = &argv[1];
+ res = !oexpr(t_lex(*t_wp));
+
+ if (*t_wp != NULL && *++t_wp != NULL)
+ syntax(*t_wp, "unexpected operator");
+
+ return res;
+}
+
+static void
+syntax(op, msg)
+ const char *op;
+ const char *msg;
+{
+
+ if (op && *op)
+ errx(2, "%s: %s", op, msg);
+ else
+ errx(2, "%s", msg);
}
static int
-expr_is_false(val)
- struct value *val;
+oexpr(n)
+ enum token n;
{
+ int res;
- if (val->type == STRING) {
- if (val->u.string[0] == '\0')
- return (1);
- } else { /* INTEGER or BOOLEAN */
- if (val->u.num == 0)
- return (1);
- }
- return (0);
+ res = aexpr(n);
+ if (t_lex(*++t_wp) == BOR)
+ return oexpr(t_lex(*++t_wp)) || res;
+ t_wp--;
+ return res;
}
+static int
+aexpr(n)
+ enum token n;
+{
+ int res;
-/*
- * Execute an operator. Op is the operator. Sp is the stack pointer;
- * sp[0] refers to the first operand, sp[1] refers to the second operand
- * (if any), and the result is placed in sp[0]. The operands are converted
- * to the type expected by the operator before expr_operator is called.
- * Fs is a pointer to a structure which holds the value of the last call
- * to stat, to avoid repeated stat calls on the same file.
- */
-static void
-expr_operator(op, sp, fs)
- int op;
- struct value *sp;
- struct filestat *fs;
+ res = nexpr(n);
+ if (t_lex(*++t_wp) == BAND)
+ return aexpr(t_lex(*++t_wp)) && res;
+ t_wp--;
+ return res;
+}
+
+static int
+nexpr(n)
+ enum token n; /* token */
{
- int i;
-
- switch (op) {
- case NOT:
- sp->u.num = expr_is_false(sp);
- sp->type = BOOLEAN;
- break;
- case ISEXIST:
-exist:
- if (fs == NULL || fs->rcode == -1)
- goto false;
- else
- goto true;
- case ISREAD:
- if (geteuid() == 0)
- goto exist;
- i = S_IROTH;
- goto permission;
- case ISWRITE:
- if (geteuid() != 0)
- i = S_IWOTH;
- else {
- i = S_IWOTH|S_IWGRP|S_IWUSR;
- goto filebit;
+ if (n == UNOT)
+ return !nexpr(t_lex(*++t_wp));
+ return primary(n);
+}
+
+static int
+primary(n)
+ enum token n;
+{
+ enum token nn;
+ int res;
+
+ if (n == EOI)
+ return 0; /* missing expression */
+ if (n == LPAREN) {
+ if ((nn = t_lex(*++t_wp)) == RPAREN)
+ return 0; /* missing expression */
+ res = oexpr(nn);
+ if (t_lex(*++t_wp) != RPAREN)
+ syntax(NULL, "closing paren expected");
+ return res;
+ }
+ if (t_wp_op && t_wp_op->op_type == UNOP) {
+ /* unary expression */
+ if (*++t_wp == NULL)
+ syntax(t_wp_op->op_text, "argument expected");
+ switch (n) {
+ case STREZ:
+ return strlen(*t_wp) == 0;
+ case STRNZ:
+ return strlen(*t_wp) != 0;
+ case FILTT:
+ return isatty(getn(*t_wp));
+ default:
+ return filstat(*t_wp, n);
}
- goto permission;
- case ISEXEC:
- if (geteuid() != 0) {
- i = S_IXOTH;
-permission: if (fs->stat.st_uid == geteuid())
- i <<= 6;
- else {
- gid_t grlist[NGROUPS];
- int ngroups, j;
-
- ngroups = getgroups(NGROUPS, grlist);
- for (j = 0; j < ngroups; j++)
- if (fs->stat.st_gid == grlist[j]) {
- i <<= 3;
- goto filebit;
- }
- }
- } else
- i = S_IXOTH|S_IXGRP|S_IXUSR;
- goto filebit; /* true if (stat.st_mode & i) != 0 */
- case ISFILE:
- i = S_IFREG;
- goto filetype;
- case ISDIR:
- i = S_IFDIR;
- goto filetype;
- case ISCHAR:
- i = S_IFCHR;
- goto filetype;
- case ISBLOCK:
- i = S_IFBLK;
- goto filetype;
- case ISSYMLINK:
- i = S_IFLNK;
- fs->rcode = lstat(sp->u.string, &fs->stat);
- goto filetype;
- case ISFIFO:
- i = S_IFIFO;
- goto filetype;
- case ISSOCK:
- i = S_IFSOCK;
- goto filetype;
-filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
-true: sp->u.num = 1;
- else
-false: sp->u.num = 0;
- sp->type = BOOLEAN;
- break;
- case ISSETUID:
- i = S_ISUID;
- goto filebit;
- case ISSETGID:
- i = S_ISGID;
- goto filebit;
- case ISSTICKY:
- i = S_ISVTX;
-filebit: if (fs->stat.st_mode & i && fs->rcode >= 0)
- goto true;
- goto false;
- case ISSIZE:
- sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
- sp->type = INTEGER;
- break;
- case ISTTY:
- sp->u.num = isatty(sp->u.num);
- sp->type = BOOLEAN;
- break;
- case NULSTR:
- if (sp->u.string[0] == '\0')
- goto true;
- goto false;
- case STRLEN:
- sp->u.num = strlen(sp->u.string);
- sp->type = INTEGER;
- break;
- case OR1:
- case AND1:
- /*
- * These operators are mostly handled by the parser. If we
- * get here it means that both operands were evaluated, so
- * the value is the value of the second operand.
- */
- *sp = *(sp + 1);
- break;
- case STREQ:
- case STRNE:
- i = 0;
- if (!strcmp(sp->u.string, (sp + 1)->u.string))
- i++;
- if (op == STRNE)
- i = 1 - i;
- sp->u.num = i;
- sp->type = BOOLEAN;
- break;
- case EQ:
- if (sp->u.num == (sp + 1)->u.num)
- goto true;
- goto false;
- case NE:
- if (sp->u.num != (sp + 1)->u.num)
- goto true;
- goto false;
- case GT:
- if (sp->u.num > (sp + 1)->u.num)
- goto true;
- goto false;
- case LT:
- if (sp->u.num < (sp + 1)->u.num)
- goto true;
- goto false;
- case LE:
- if (sp->u.num <= (sp + 1)->u.num)
- goto true;
- goto false;
- case GE:
- if (sp->u.num >= (sp + 1)->u.num)
- goto true;
- goto false;
+ }
+ if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
+ return binop();
}
+
+ return strlen(*t_wp) > 0;
}
static int
-lookup_op(name, table)
- char *name;
- const char *const * table;
+binop()
{
- const char *const * tp;
- const char *p;
- char c;
-
- c = name[1];
- for (tp = table; (p = *tp) != NULL; tp++)
- if (p[1] == c && !strcmp(p, name))
- return (tp - table);
- return (-1);
+ const char *opnd1, *opnd2;
+ struct t_op const *op;
+
+ opnd1 = *t_wp;
+ (void) t_lex(*++t_wp);
+ op = t_wp_op;
+
+ if ((opnd2 = *++t_wp) == NULL)
+ syntax(op->op_text, "argument expected");
+
+ switch (op->op_num) {
+ case STREQ:
+ return strcmp(opnd1, opnd2) == 0;
+ case STRNE:
+ return strcmp(opnd1, opnd2) != 0;
+ case STRLT:
+ return strcmp(opnd1, opnd2) < 0;
+ case STRGT:
+ return strcmp(opnd1, opnd2) > 0;
+ case INTEQ:
+ return getn(opnd1) == getn(opnd2);
+ case INTNE:
+ return getn(opnd1) != getn(opnd2);
+ case INTGE:
+ return getn(opnd1) >= getn(opnd2);
+ case INTGT:
+ return getn(opnd1) > getn(opnd2);
+ case INTLE:
+ return getn(opnd1) <= getn(opnd2);
+ case INTLT:
+ return getn(opnd1) < getn(opnd2);
+ case FILNT:
+ return newerf (opnd1, opnd2);
+ case FILOT:
+ return olderf (opnd1, opnd2);
+ case FILEQ:
+ return equalf (opnd1, opnd2);
+ default:
+ abort();
+ /* NOTREACHED */
+ }
}
static int
-posix_unary_op(argv)
- char **argv;
+filstat(nm, mode)
+ char *nm;
+ enum token mode;
{
- struct filestat fs;
- struct value valp;
- int op, c;
- char *opname;
-
- opname = *argv;
- if ((op = lookup_op(opname, unary_op)) < 0)
- return (-1);
- c = op_argflag[op];
- opname = argv[1];
- valp.u.string = opname;
- if (c == OP_FILE) {
- fs.name = opname;
- fs.rcode = stat(opname, &fs.stat);
- } else if (c != OP_STRING)
- return (-1);
-
- expr_operator(op, &valp, &fs);
- return (valp.u.num == 0);
+ struct stat s;
+
+ if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
+ return 0;
+
+ switch (mode) {
+ case FILRD:
+ return access(nm, R_OK) == 0;
+ case FILWR:
+ return access(nm, W_OK) == 0;
+ case FILEX:
+ return access(nm, X_OK) == 0;
+ case FILEXIST:
+ return access(nm, F_OK) == 0;
+ case FILREG:
+ return S_ISREG(s.st_mode);
+ case FILDIR:
+ return S_ISDIR(s.st_mode);
+ case FILCDEV:
+ return S_ISCHR(s.st_mode);
+ case FILBDEV:
+ return S_ISBLK(s.st_mode);
+ case FILFIFO:
+ return S_ISFIFO(s.st_mode);
+ case FILSOCK:
+ return S_ISSOCK(s.st_mode);
+ case FILSYM:
+ return S_ISLNK(s.st_mode);
+ case FILSUID:
+ return (s.st_mode & S_ISUID) != 0;
+ case FILSGID:
+ return (s.st_mode & S_ISGID) != 0;
+ case FILSTCK:
+ return (s.st_mode & S_ISVTX) != 0;
+ case FILGZ:
+ return s.st_size > (off_t)0;
+ case FILUID:
+ return s.st_uid == geteuid();
+ case FILGID:
+ return s.st_gid == getegid();
+ default:
+ return 1;
+ }
+}
+
+static enum token
+t_lex(s)
+ char *s;
+{
+ struct t_op const *op = ops;
+
+ if (s == 0) {
+ t_wp_op = NULL;
+ return EOI;
+ }
+ while (op->op_text) {
+ if (strcmp(s, op->op_text) == 0) {
+ if ((op->op_type == UNOP && isoperand()) ||
+ (op->op_num == LPAREN && *(t_wp+1) == 0))
+ break;
+ t_wp_op = op;
+ return op->op_num;
+ }
+ op++;
+ }
+ t_wp_op = NULL;
+ return OPERAND;
}
static int
-posix_binary_op(argv)
- char **argv;
+isoperand()
{
- struct value v[2];
- int op, c;
- char *opname;
-
- opname = argv[1];
- if ((op = lookup_op(opname, binary_op)) < 0)
- return (-1);
- op += FIRST_BINARY_OP;
- c = op_argflag[op];
-
- if (c == OP_INT) {
- get_int(argv[0], &v[0].u.num);
- get_int(argv[2], &v[1].u.num);
- } else {
- v[0].u.string = argv[0];
- v[1].u.string = argv[2];
+ struct t_op const *op = ops;
+ char *s;
+ char *t;
+
+ if ((s = *(t_wp+1)) == 0)
+ return 1;
+ if ((t = *(t_wp+2)) == 0)
+ return 0;
+ while (op->op_text) {
+ if (strcmp(s, op->op_text) == 0)
+ return op->op_type == BINOP &&
+ (t[0] != ')' || t[1] != '\0');
+ op++;
}
- expr_operator(op, v, NULL);
- return (v[0].u.num == 0);
+ return 0;
}
-/*
- * Integer type checking.
- */
-static void
-get_int(v, lp)
- char *v;
- long *lp;
+/* atoi with error detection */
+static int
+getn(s)
+ const char *s;
{
- long val;
- char *ep;
+ char *p;
+ long r;
- for (; *v && isspace(*v); ++v);
+ errno = 0;
+ r = strtol(s, &p, 10);
- if (!*v) {
- *lp = 0;
- return;
- }
+ if (errno != 0)
+ errx(2, "%s: out of range", s);
- if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) {
- errno = 0;
- val = strtol(v, &ep, 10);
- if (*ep != '\0')
- errx(2, "%s: trailing non-numeric characters", v);
- if (errno == ERANGE) {
- if (val == LONG_MIN)
- errx(2, "%s: underflow", v);
- if (val == LONG_MAX)
- errx(2, "%s: overflow", v);
- }
- *lp = val;
- return;
- }
- errx(2, "%s: expected integer", v);
+ while (isspace((unsigned char)*p))
+ p++;
+
+ if (*p)
+ errx(2, "%s: bad number", s);
+
+ return (int) r;
}
-static void
-syntax()
+static int
+newerf (f1, f2)
+ const char *f1, *f2;
{
+ struct stat b1, b2;
- errx(2, "syntax error");
+ return (stat (f1, &b1) == 0 &&
+ stat (f2, &b2) == 0 &&
+ b1.st_mtime > b2.st_mtime);
}
-static void
-overflow()
+static int
+olderf (f1, f2)
+ const char *f1, *f2;
+{
+ struct stat b1, b2;
+
+ return (stat (f1, &b1) == 0 &&
+ stat (f2, &b2) == 0 &&
+ b1.st_mtime < b2.st_mtime);
+}
+
+static int
+equalf (f1, f2)
+ const char *f1, *f2;
{
+ struct stat b1, b2;
- errx(2, "expression is too complex");
+ return (stat (f1, &b1) == 0 &&
+ stat (f2, &b2) == 0 &&
+ b1.st_dev == b2.st_dev &&
+ b1.st_ino == b2.st_ino);
}
OpenPOWER on IntegriCloud