summaryrefslogtreecommitdiffstats
path: root/bin/test
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-26 06:18:55 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-26 06:18:55 +0000
commite3cfc8ce61f788739c66445d903f8beacb40c93d (patch)
treeba7beef9ee9289c4383bad976f88710e44c98b4d /bin/test
parent862fdf11a2ede45dec0da01ed575525d79468981 (diff)
downloadFreeBSD-src-e3cfc8ce61f788739c66445d903f8beacb40c93d.zip
FreeBSD-src-e3cfc8ce61f788739c66445d903f8beacb40c93d.tar.gz
BSD 4.4 Lite bin Sources
Diffstat (limited to 'bin/test')
-rw-r--r--bin/test/Makefile8
-rw-r--r--bin/test/TEST.csh137
-rw-r--r--bin/test/operators.c148
-rw-r--r--bin/test/operators.h77
-rw-r--r--bin/test/test.1255
-rw-r--r--bin/test/test.c560
6 files changed, 1185 insertions, 0 deletions
diff --git a/bin/test/Makefile b/bin/test/Makefile
new file mode 100644
index 0000000..ccd6089
--- /dev/null
+++ b/bin/test/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+
+PROG= test
+SRCS= test.c operators.c
+LINKS= ${BINDIR}/test ${BINDIR}/[
+MLINKS= test.1 '[.1'
+
+.include <bsd.prog.mk>
diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh
new file mode 100644
index 0000000..e5b9652
--- /dev/null
+++ b/bin/test/TEST.csh
@@ -0,0 +1,137 @@
+# @(#)TEST.csh 5.2 (Berkeley) 4/30/93
+
+#alias t '/usr/src/bin/test/obj/test \!*; echo $status'
+alias t '/bin/test \!*; echo $status'
+
+echo 't -b /dev/ttyp2'
+t -b /dev/ttyp2
+echo 't -b /dev/jb1a'
+t -b /dev/jb1a
+
+echo 't -c test.c'
+t -c test.c
+echo 't -c /dev/tty'
+t -c /dev/tty
+
+echo 't -d test.c'
+t -d test.c
+echo 't -d /etc'
+t -d /etc
+
+echo 't -e noexist'
+t -e noexist
+echo 't -e test.c'
+t -e test.c
+
+echo 't -f noexist'
+t -f noexist
+echo 't -f /dev/tty'
+t -f /dev/tty
+echo 't -f test.c'
+t -f test.c
+
+echo 't -g test.c'
+t -g test.c
+echo 't -g /bin/ps'
+t -g /bin/ps
+
+echo 't -n ""'
+t -n ""
+echo 't -n "hello"'
+t -n "hello"
+
+echo 't -p test.c'
+t -p test.c
+
+echo 't -r noexist'
+t -r noexist
+echo 't -r /etc/master.passwd'
+t -r /etc/master.passwd
+echo 't -r test.c'
+t -r test.c
+
+echo 't -s noexist'
+t -s noexist
+echo 't -s /dev/null'
+t -s /dev/null
+echo 't -s test.c'
+t -s test.c
+
+echo 't -t 20'
+t -t 20
+echo 't -t 0'
+t -t 0
+
+echo 't -u test.c'
+t -u test.c
+echo 't -u /bin/rcp'
+t -u /bin/rcp
+
+echo 't -w noexist'
+t -w noexist
+echo 't -w /etc/master.passwd'
+t -w /etc/master.passwd
+echo 't -w /dev/null'
+t -w /dev/null
+
+echo 't -x noexist'
+t -x noexist
+echo 't -x /bin/ps'
+t -x /bin/ps
+echo 't -x /etc/motd'
+t -x /etc/motd
+
+echo 't -z ""'
+t -z ""
+echo 't -z "foo"'
+t -z "foo"
+
+echo 't "foo"'
+t "foo"
+echo 't ""'
+t ""
+
+echo 't "hello" = "hello"'
+t "hello" = "hello"
+echo 't "hello" = "goodbye"'
+t "hello" = "goodbye"
+
+echo 't "hello" != "hello"'
+t "hello" != "hello"
+echo 't "hello" != "goodbye"'
+t "hello" != "goodbye"
+
+echo 't 200 -eq 200'
+t 200 -eq 200
+echo 't 34 -eq 222'
+t 34 -eq 222
+
+echo 't 200 -ne 200'
+t 200 -ne 200
+echo 't 34 -ne 222'
+t 34 -ne 222
+
+echo 't 200 -gt 200'
+t 200 -gt 200
+echo 't 340 -gt 222'
+t 340 -gt 222
+
+echo 't 200 -ge 200'
+t 200 -ge 200
+echo 't 34 -ge 222'
+t 34 -ge 222
+
+echo 't 200 -lt 200'
+t 200 -lt 200
+echo 't 34 -lt 222'
+t 34 -lt 222
+
+echo 't 200 -le 200'
+t 200 -le 200
+echo 't 340 -le 222'
+t 340 -le 222
+
+echo 't 700 -le 1000 -a -n "1" -a "20" = "20"'
+t 700 -le 1000 -a -n "1" -a "20" = "20"
+echo 't ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)'
+t ! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)
diff --git a/bin/test/operators.c b/bin/test/operators.c
new file mode 100644
index 0000000..335223b
--- /dev/null
+++ b/bin/test/operators.c
@@ -0,0 +1,148 @@
+/*-
+ * 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
+static char sccsid[] = "@(#)operators.c 8.3 (Berkeley) 4/2/94";
+#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",
+ NULL
+};
+
+const char *const binary_op[] = {
+ "-o",
+ "|",
+ "-a",
+ "&",
+ "=",
+ "!=",
+ "-eq",
+ "-ne",
+ "-gt",
+ "-lt",
+ "-le",
+ "-ge",
+ NULL
+};
+
+const char op_priority[] = {
+ 3,
+ 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,
+ 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
new file mode 100644
index 0000000..f8746df
--- /dev/null
+++ b/bin/test/operators.h
@@ -0,0 +1,77 @@
+/*-
+ * 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
+ */
+
+#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 FIRST_BINARY_OP 18
+#define OR1 18
+#define OR2 19
+#define AND1 20
+#define AND2 21
+#define STREQ 22
+#define STRNE 23
+#define EQ 24
+#define NE 25
+#define GT 26
+#define LT 27
+#define LE 28
+#define GE 29
+
+
+#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 op_priority[];
+extern const char op_argflag[];
diff --git a/bin/test/test.1 b/bin/test/test.1
new file mode 100644
index 0000000..b4eaf0d
--- /dev/null
+++ b/bin/test/test.1
@@ -0,0 +1,255 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" 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.
+.\"
+.\" @(#)test.1 8.1 (Berkeley) 5/31/93
+.\"
+.Dd May 31, 1993
+.Dt TEST 1
+.Os
+.Sh NAME
+.Nm test
+.Nd condition evaluation utility
+.Sh SYNOPSIS
+.Nm test
+.Ar expression
+.Sh DESCRIPTION
+The
+.Nm test
+utility evaluates the expression and, if it evaluates
+to true, returns a zero (true) exit status; otherwise
+it returns 1 (false).
+If there is no expression, test also
+returns 1 (false).
+.Pp
+All operators and flags are separate arguments to the
+.Nm test
+utility.
+.Pp
+The following primaries are used to construct expression:
+.Bl -tag -width Ar
+.It Fl b Ar file
+True if
+.Ar file
+exists and is a block special
+file.
+.It Fl c Ar file
+True if
+.Ar file
+exists and is a character
+special file.
+.It Fl d Ar file
+True if
+.Ar file
+exists and is a directory.
+.It Fl e Ar file
+True if
+.Ar file
+exists (regardless of type).
+.It Fl f Ar file
+True if
+.Ar file
+exists and is a regular file.
+.It Fl g Ar file
+True if
+.Ar file
+exists and its set group ID flag
+is set.
+.It Fl h Ar file
+True if
+.Ar file
+exists and is a symbolic link.
+.It Fl n Ar string
+True if the length of
+.Ar string
+is nonzero.
+.It Fl p Ar file
+True if
+.Ar file
+is a named pipe
+.Po Tn FIFO Pc .
+.It Fl r Ar file
+True if
+.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]
+True if the file whose file descriptor number
+is
+.Ar file_descriptor
+(default 1) is open and is
+associated with a terminal.
+.It Fl u Ar file
+True if
+.Ar file
+exists and its set user ID flag
+is set.
+.It Fl w Ar file
+True if
+.Ar file
+exists and is writable.
+True
+indicates only that the write flag is on.
+The file is not writable on a read-only file
+system even if this test indicates true.
+.It Fl x Ar file
+True if
+.Ar file
+exists and is executable.
+True
+indicates only that the execute flag is on.
+If
+.Ar file
+is a directory, true indicates that
+.Ar file
+can be searched.
+.It Fl z Ar string
+True if the length of
+.Ar string
+is zero.
+.It Ar string
+True if
+.Ar string
+is not the null
+string.
+.It Ar \&s\&1 Cm \&= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are identical.
+.It Ar \&s\&1 Cm \&!= Ar \&s\&2
+True if the strings
+.Ar \&s\&1
+and
+.Ar \&s\&2
+are not identical.
+.It Ar \&n\&1 Fl \&eq Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are algebraically
+equal.
+.It Ar \&n\&1 Fl \&ne Ar \&n\&2
+True if the integers
+.Ar \&n\&1
+and
+.Ar \&n\&2
+are not
+algebraically equal.
+.It Ar \&n\&1 Fl \&gt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&ge Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically
+greater than or equal to the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&lt Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than the integer
+.Ar \&n\&2 .
+.It Ar \&n\&1 Fl \&le Ar \&n\&2
+True if the integer
+.Ar \&n\&1
+is algebraically less
+than or equal to the integer
+.Ar \&n\&2 .
+.El
+.Pp
+These primaries can be combined with the following operators:
+.Bl -tag -width Ar
+.It Cm \&! Ar expression
+True if
+.Ar expression
+is false.
+.It Ar expression1 Fl a Ar expression2
+True if both
+.Ar expression1
+and
+.Ar expression2
+are true.
+.It Ar expression1 Fl o Ar expression2
+True if either
+.Ar expression1
+or
+.Ar expression2
+are true.
+.It Cm \&( Ns Ar expression Ns Cm \&)
+True if expression is true.
+.El
+.Pp
+The
+.Fl a
+operator has higher precedence than the
+.Fl o
+operator.
+.Sh GRAMMAR AMBIGUITY
+The
+.Nm test
+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
+command semantics.
+.Sh RETURN VALUES
+The
+.Nm test
+utility exits with one of the following values:
+.Bl -tag -width Ds
+.It 0
+expression evaluated to true.
+.It 1
+expression evaluated to false or expression was
+missing.
+.It >1
+An error occurred.
+.El
+.Sh STANDARDS
+The
+.Nm test
+function is expected to be
+.St -p1003.2
+compatible.
diff --git a/bin/test/test.c b/bin/test/test.c
new file mode 100644
index 0000000..b9a8ebf
--- /dev/null
+++ b/bin/test/test.c
@@ -0,0 +1,560 @@
+/*-
+ * 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.
+ *
+ * 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
+static char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)test.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#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;
+};
+
+struct operator {
+ short op; /* Which operator. */
+ short pri; /* Priority of operator. */
+};
+
+struct filestat {
+ char *name; /* Name of file. */
+ int rcode; /* Return code from stat. */
+ struct stat stat; /* Status info on file. */
+};
+
+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));
+
+int
+main(argc, argv)
+ int argc;
+ 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, pri, ret_val, skipping;
+
+ if ((p = argv[0]) == NULL)
+ errx(2, "test: argc is zero");
+
+ if (*p != '\0' && p[strlen(p) - 1] == '[') {
+ if (strcmp(argv[--argc], "]"))
+ errx(2, "missing ]");
+ argv[argc] = NULL;
+ }
+ ap = argv + 1;
+ fs.name = NULL;
+
+ /*
+ * Test(1) implements an inherently ambiguous grammer. 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 {
+ 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])) {
+ 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,
+ "%d", 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]));
+}
+
+static int
+expr_is_false(val)
+ struct value *val;
+{
+
+ 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);
+}
+
+
+/*
+ * 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;
+{
+ int i;
+
+ switch (op) {
+ case NOT:
+ sp->u.num = expr_is_false(sp);
+ sp->type = BOOLEAN;
+ break;
+ case ISEXIST:
+ if (fs == NULL || fs->rcode == -1)
+ goto false;
+ else
+ goto true;
+ case ISREAD:
+ i = S_IROTH;
+ goto permission;
+ case ISWRITE:
+ i = S_IWOTH;
+ goto permission;
+ case ISEXEC:
+ i = S_IXOTH;
+permission: if (fs->stat.st_uid == geteuid())
+ i <<= 6;
+ else if (fs->stat.st_gid == getegid())
+ i <<= 3;
+ 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;
+ (void)lstat(sp->u.string, &fs->stat);
+ goto filetype;
+ case ISFIFO:
+ i = S_IFIFO;
+ 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;
+
+ }
+}
+
+static int
+lookup_op(name, table)
+ char *name;
+ const char *const * table;
+{
+ 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);
+}
+
+static int
+posix_unary_op(argv)
+ char **argv;
+{
+ 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);
+}
+
+static int
+posix_binary_op(argv)
+ char **argv;
+{
+ 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];
+ }
+ expr_operator(op, v, NULL);
+ return (v[0].u.num == 0);
+}
+
+/*
+ * Integer type checking.
+ */
+static void
+get_int(v, lp)
+ char *v;
+ long *lp;
+{
+ long val;
+ char *ep;
+
+ for (; *v && isspace(*v); ++v);
+ if (isdigit(*v)) {
+ 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);
+}
+
+static void
+syntax()
+{
+
+ err(2, "syntax error");
+}
+
+static void
+overflow()
+{
+
+ err(2, "expression is too complex");
+}
OpenPOWER on IntegriCloud