aboutsummaryrefslogtreecommitdiff
path: root/bin/test
diff options
context:
space:
mode:
Diffstat (limited to 'bin/test')
-rw-r--r--bin/test/Makefile14
-rw-r--r--bin/test/TEST.csh147
-rw-r--r--bin/test/binary_op56
-rw-r--r--bin/test/mkops84
-rw-r--r--bin/test/operators.c115
-rw-r--r--bin/test/operators.h41
-rw-r--r--bin/test/test.1251
-rw-r--r--bin/test/test.c574
-rw-r--r--bin/test/unary_op59
9 files changed, 1341 insertions, 0 deletions
diff --git a/bin/test/Makefile b/bin/test/Makefile
new file mode 100644
index 000000000000..34d81d2a88ab
--- /dev/null
+++ b/bin/test/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 5.1 (Berkeley) 6/8/92
+
+PROG= test
+SRCS= test.c operators.c
+CFLAGS+=-I.
+LINKS= ${BINDIR}/test ${BINDIR}/[
+MLINKS= test.1 '[.1'
+
+
+# use this rule to if you update binary_ops, or unary_ops
+make_op:
+ sh ${.CURDIR}/mkops
+
+.include <bsd.prog.mk>
diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh
new file mode 100644
index 000000000000..b3c8f1a32de6
--- /dev/null
+++ b/bin/test/TEST.csh
@@ -0,0 +1,147 @@
+# @(#)TEST.csh 5.1 (Berkeley) 6/8/92
+
+alias t './test \!*; echo $status'
+#alias t '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" \)
+
+echo 't foo -a ""'
+t foo -a ""
+echo 't "" -a foo'
+t "" -a foo
+echo 't "" -a ""'
+t "" -a ""
+echo 't "" -o ""'
+t "" -o ""
+
diff --git a/bin/test/binary_op b/bin/test/binary_op
new file mode 100644
index 000000000000..d49b4bc10f6e
--- /dev/null
+++ b/bin/test/binary_op
@@ -0,0 +1,56 @@
+# Copyright (c) 1988 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.
+#
+# @(#)binary_op 1.2 (Berkeley) 6/3/92
+#
+
+
+#
+# List of binary operators used by test
+#
+
+
+OR1 -o 1
+OR2 | 1
+AND1 -a 2
+AND2 & 2
+STREQ = 4 OP_STRING
+STRNE != 4 OP_STRING
+EQ -eq 4 OP_INT
+NE -ne 4 OP_INT
+GT -gt 4 OP_INT
+LT -lt 4 OP_INT
+LE -le 4 OP_INT
+GE -ge 4 OP_INT
+
diff --git a/bin/test/mkops b/bin/test/mkops
new file mode 100644
index 000000000000..d37696f828c1
--- /dev/null
+++ b/bin/test/mkops
@@ -0,0 +1,84 @@
+# Copyright (c) 1988 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.
+#
+# @(#)mkops 1.3 (Berkeley) 6/3/92
+#
+
+
+#exec > operators.h
+(
+awk '/^[^#]/ {printf "#define %s %d\n", $1, n++}' unary_op binary_op
+awk '/^[^#]/ {n++}
+END {printf "\n#define FIRST_BINARY_OP %d\n", n}
+' unary_op
+echo '
+#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 char *const unary_op[];
+extern char *const binary_op[];
+extern const char op_priority[];
+extern const char op_argflag[];'
+) >operators.h
+
+#exec > operators.c
+(
+echo '/*
+ * Operators used in the test command.
+ */
+
+#include <stdio.h>
+#include "operators.h"
+
+char *const unary_op[] = {'
+awk '/^[^#]/ {printf " \"%s\",\n", $2}' unary_op
+echo ' NULL
+};
+
+char *const binary_op[] = {'
+awk '/^[^#]/ {printf " \"%s\",\n", $2}' binary_op
+echo ' NULL
+};
+
+const char op_priority[] = {'
+awk '/^[^#]/ {printf " %s,\n", $3}' unary_op binary_op
+echo '};
+
+const char op_argflag[] = {'
+awk '/^[^#]/ {if (length($4) > 0) printf " %s,\n", $4
+ else printf " 0,\n"}
+' unary_op binary_op
+echo '};'
+) >operators.c
diff --git a/bin/test/operators.c b/bin/test/operators.c
new file mode 100644
index 000000000000..6700770fac2d
--- /dev/null
+++ b/bin/test/operators.c
@@ -0,0 +1,115 @@
+/*
+ * Operators used in the test command.
+ */
+
+#include <stdio.h>
+#include "operators.h"
+
+char *const unary_op[] = {
+ "!",
+ "-b",
+ "-c",
+ "-d",
+ "-e",
+ "-f",
+ "-g",
+ "-k",
+ "-n",
+ "-p",
+ "-r",
+ "-s",
+ "-t",
+ "-u",
+ "-w",
+ "-x",
+ "-z",
+ NULL
+};
+
+char *const binary_op[] = {
+ "-o",
+ "|",
+ "-a",
+ "&",
+ "=",
+ "!=",
+ "-eq",
+ "-ne",
+ "-gt",
+ "-lt",
+ "-le",
+ "-ge",
+ NULL
+};
+
+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,
+ 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_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 000000000000..d49e7c25f09a
--- /dev/null
+++ b/bin/test/operators.h
@@ -0,0 +1,41 @@
+#define NOT 0
+#define ISBLOCK 1
+#define ISCHAR 2
+#define ISDIR 3
+#define ISEXIST 4
+#define ISFILE 5
+#define ISSETGID 6
+#define ISSTICKY 7
+#define STRLEN 8
+#define ISFIFO 9
+#define ISREAD 10
+#define ISSIZE 11
+#define ISTTY 12
+#define ISSETUID 13
+#define ISWRITE 14
+#define ISEXEC 15
+#define NULSTR 16
+#define OR1 17
+#define OR2 18
+#define AND1 19
+#define AND2 20
+#define STREQ 21
+#define STRNE 22
+#define EQ 23
+#define NE 24
+#define GT 25
+#define LT 26
+#define LE 27
+#define GE 28
+
+#define FIRST_BINARY_OP 17
+
+#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 char *const unary_op[];
+extern char *const binary_op[];
+extern 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
new file mode 100644
index 000000000000..3fd02d4bac2b
--- /dev/null
+++ b/bin/test/test.1
@@ -0,0 +1,251 @@
+.\" Copyright (c) 1991 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 6.6 (Berkeley) 6/8/92
+.\"
+.Dd June 8, 1992
+.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 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 000000000000..66d8638130a2
--- /dev/null
+++ b/bin/test/test.c
@@ -0,0 +1,574 @@
+/*-
+ * Copyright (c) 1992 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
+char copyright[] =
+"@(#) Copyright (c) 1992 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)test.c 5.4 (Berkeley) 2/12/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.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 void err __P((const char *, ...));
+static int expr_is_false __P((struct value *));
+static void expr_operator __P((int, struct value *, struct filestat *));
+static long chk_atol __P((char *));
+static int lookup_op __P((char *, 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) {
+ err("test: argc is zero.\n");
+ exit(2);
+ }
+
+ if (*p != '\0' && p[strlen(p) - 1] == '[') {
+ if (strcmp(argv[--argc], "]"))
+ err("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 */
+ /* MIPS machine returns NULL of '[ ]' is called. */
+ return (argv[1] == 0 || *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)
+ valsp->u.num =
+ chk_atol(valsp->u.string);
+ valsp->type = INTEGER;
+ } else if (c >= OP_STRING) {
+ /* OP_STRING or OP_FILE */
+ if (valsp->type == INTEGER) {
+ if ((p = malloc(32)) == NULL)
+ err("%s",
+ strerror(errno));
+#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 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;
+ char *const * table;
+{
+ register char *const * tp;
+ register char const *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) {
+ v[0].u.num = chk_atol(argv[0]);
+ v[1].u.num = chk_atol(argv[2]);
+ } 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 long
+chk_atol(v)
+ char *v;
+{
+ char *p;
+ long r;
+
+ errno = 0;
+ r = strtol(v, &p, 10);
+ if (errno != 0)
+ err("\"%s\" -- out of range.", v);
+ while (isspace(*p))
+ p++;
+ if (*p != '\0')
+ err("illegal operand \"%s\" -- expected integer.", v);
+ return (r);
+}
+
+static void
+syntax()
+{
+ err("syntax error");
+}
+
+static void
+overflow()
+{
+ err("expression is too complex");
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "test: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ exit(2);
+ /* NOTREACHED */
+}
diff --git a/bin/test/unary_op b/bin/test/unary_op
new file mode 100644
index 000000000000..0defa57ad23f
--- /dev/null
+++ b/bin/test/unary_op
@@ -0,0 +1,59 @@
+# Copyright (c) 1988 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.
+#
+# @(#)unary_op 1.2 (Berkeley) 6/3/92
+#
+
+#
+# List of unary operators used by test.
+#
+
+NOT ! 3
+ISBLOCK -b 12 OP_FILE
+ISCHAR -c 12 OP_FILE
+ISDIR -d 12 OP_FILE
+ISEXIST -e 12 OP_FILE
+ISFILE -f 12 OP_FILE
+ISSETGID -g 12 OP_FILE
+ISSTICKY -k 12 OP_FILE
+STRLEN -n 12 OP_STRING
+ISFIFO -p 12 OP_FILE
+ISREAD -r 12 OP_FILE
+ISSIZE -s 12 OP_FILE
+ISTTY -t 12 OP_INT
+ISSETUID -u 12 OP_FILE
+ISWRITE -w 12 OP_FILE
+ISEXEC -x 12 OP_FILE
+NULSTR -z 12 OP_STRING
+