diff --git a/bin/test/README b/bin/test/README new file mode 100644 index 0000000..8159765 --- /dev/null +++ b/bin/test/README @@ -0,0 +1,22 @@ +This is test(1) for GNO/ME. For those of you familiar with it +from other platforms, it is a minimal BSD version, not a code-bloated +GNU version. For those not familiar with it, read the man page. + +I admit that this has limited functionality with gsh, but it will +become more useful when sh(1) comes out ... + +For copying info, see the legalese at the beginning of the file +test.c, or the man page (you have to view the file directly rather +than than via nroff(1) to see it). + +Also, regarding the GNO binary: + + This program contains material from the ORCA/C Run-Time + Libraries, copyright 1987-1996 by Byte Works, Inc. Used + with permission. + +If you have problems with this, let me know. It was a two-hour port. + +Devin Reade +February, 1996 + diff --git a/bin/test/TEST.csh b/bin/test/TEST.csh new file mode 100644 index 0000000..b3c8f1a --- /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/binaryop b/bin/test/binaryop new file mode 100644 index 0000000..d49b4bc --- /dev/null +++ b/bin/test/binaryop @@ -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/makefile.mk b/bin/test/makefile.mk new file mode 100644 index 0000000..707eeba --- /dev/null +++ b/bin/test/makefile.mk @@ -0,0 +1,52 @@ +# +# This file is intended for use with dmake +# +# $Id: makefile.mk,v 1.1 1996/02/10 08:27:30 gdr Exp $ +# +# VAFLAGS must use an optimization level of at least -O8, and no +# -g or -G* flag +# +# Include -DCHECK_STACK in DEFINES to get stack usage and debug info. +# If you use -DCHECK_STACK, you will also have to add the stack checking +# library. I keep mine in "/usr/lib/stack". +# + +DEFINES = -Dlint -DGNO +STACK = -s1280 +CFLAGS += $(DEFINES) $(STACK) -w -O +VAFLAGS += $(DEFINES) $(STACK) -w -O +LDFLAGS += -v +LDLIBS = +OBJS = test.o test2.o operators.o +ROOTS = test.root test2.root operators.root + +test: $(OBJS) test.r + @purge + $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ + copyfork test.r test -r + +test.o: test.c operators.h + @purge + $(CC) -c $(CFLAGS) -o $@ test.c + +test2.o: test2.c + @purge + $(CC) -c $(VAFLAGS) -o $@ test2.c + +operators.o: operators.c operators.h + @purge + $(CC) -c $(CFLAGS) -o $@ operators.c + +operators.c operators.h: unaryop binaryop + $(MAKE) make_op + +clean: + $(RM) -f $(OBJS) $(ROOTS) test.r + +clobber: clean + $(RM) -f test + +# use this rule to if you update binary_ops, or unary_ops +make_op: + @echo "$(MAKE) make_op invoked" +# sh ${.CURDIR}/mkops diff --git a/bin/test/mkops b/bin/test/mkops new file mode 100644 index 0000000..d37696f --- /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 +#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 0000000..155608a --- /dev/null +++ b/bin/test/operators.c @@ -0,0 +1,118 @@ +#line 1 ":trenco4:custom.src:test:operators.c" +/* + * Operators used in the test command. + * + * $Id: operators.c,v 1.1 1996/02/10 08:27:31 gdr Exp $ + */ + +#include +#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 0000000..4554417 --- /dev/null +++ b/bin/test/operators.h @@ -0,0 +1,56 @@ +/* + * $Id: operators.h,v 1.1 1996/02/10 08:27:31 gdr Exp $ + */ + +#ifndef __P +#ifdef __STDC__ +#define __P(a) a +#else +#define __P(a) () +#endif +#endif + +#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[]; + +extern void err __P((const char *, ...)); + diff --git a/bin/test/test.1 b/bin/test/test.1 new file mode 100644 index 0000000..fe59679 --- /dev/null +++ b/bin/test/test.1 @@ -0,0 +1,275 @@ +.\" 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 +.\" +.\" $Id: test.1,v 1.1 1996/02/10 08:27:32 gdr Exp $ +.\" +.TH TEST 1 "Commands and Applications" "9 February 1996" "Version 1.0" +.SH NAME +test \- Condition evaluation utility. +.SH SYNOPSIS +.B test +.I expression +.SH DESCRIPTION +The +.B 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). +.LP +All operators and flags are separate arguments to the +.B test +utility. +.LP +The following primaries are used to construct +.IR expression : +.IP "\fB-b\fR \fIfile\fR" +True if +.I file +exists and is a block special +file. +.IP "\fB-c\fR \fIfile\fR" +True if +.I file +exists and is a character +special file. +.IP "\fB-d\fR \fIfile\fR" +True if +.I file +exists and is a directory. +.IP "\fB-e\fR \fIfile\fR" +True if +.I file +exists (regardless of type). +.IP "\fB-f\fR \fIfile\fR" +True if +.I file +exists and is a regular file. +.IP "\fB-g\fR \fIfile\fR" +True if +.I file +exists and its set group ID flag +is set. +.IP "\fB-n\fR \fIstring\fR" +True if the length of +.I string +is nonzero. +.IP "\fB-p\fR \fIfile\fR" +True if +.I file +is a named pipe (FIFO). +.IP "\fB-r\fR \fIfile\fR" +True if +.I file +exists and is readable. +.IP "\fB-s\fR \fIfile\fR" +True if +.I file +exists and has a size greater +than zero. +.IP "\fB-t\fR [ \fIfile_descriptor\fR ]" +True if the file whose file descriptor number +is +.I file_descriptor +(default 1) is open and is +associated with a terminal. +.IP "\fB-u\fR \fIfile\fR" +True if +.I file +exists and its set user ID flag +is set. +.IP "\fB-w\fR \fIfile\fR" +True if +.I 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. +.IP "\fB-x\fR \fIfile\fR" +True if +.I file +exists and is executable. +True +indicates only that the execute flag is on. +If +.I file +is a directory, true indicates that +.I file +can be searched. +.IP "\fB-z\fR \fIstring\fR" +True if the length of +.I string +is zero. +.IP "\fIstring\fR" +True if +.I string +is not the null +string. +.IP "\fIs1\fR \fB=\fR \fIs2\fR" +True if the strings +.I s1 +and +.I s2 +are identical. +.IP "\fIs1\fR \fB!=\fR \fIs2\fR" +True if the strings +.I s1 +and +.I s2 +are not identical. +.IP "\fIn1\fR \fB-eq\fR \fIn2\fR" +True if the integers +.I n1 +and +.I n2 +are algebraically +equal. +.IP "\fIn1\fR \fB-ne\fR \fIn2\fR" +True if the integers +.I n1 +and +.I n2 +are not +algebraically equal. +.IP "\fIn1\fR \fB-gt\fR \fIn2\fR" +True if the integer +.I n1 +is algebraically +greater than the integer +.I n2 . +.IP "\fIn1\fR \fB-ge\fR \fIn2\fR" +True if the integer +.I n1 +is algebraically +greater than or equal to the integer +.I n2 . +.IP "\fIn1\fR \fB-lt\fR \fIn2\fR" +True if the integer +.I n1 +is algebraically less +than the integer +.I n2 . +.IP "\fIn1\fR \fB-le\fR \fIn2\fR" +True if the integer +.I n1 +is algebraically less +than or equal to the integer +.I n2 . +.LP +These primaries can be combined with the following operators: +.IP "\fB!\fR \fIexpression\fR" +True if +.I expression +is false. +.IP "\fIexpression1\fR \fB-a\fR \fIexpression2\fR" +True if both +.I expression1 +and +.I expression2 +are true. +.IP "\fIexpression1\fR \fB-o\fR \fIexpression2\fR" +True if either +.I expression1 +or +.I expression2 +are true. +.IP "\fB\&(\fR \fIexpression\fR \fB\&)\fR" +True if +.I expression +is true. +.LP +The +.B -a +operator has higher precedence than the +.B -o +operator. +.SH GRAMMAR AMBIGUITY +The +.B test +grammar is inherently ambiguous. In order to assure a degree of consistency, +the cases described in the +.IR "POSIX 1003.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 GNO CAVEATS +The +.BR \-b , +.BR \-c , +.BR \-g , +and +.BR \-u +operators always cause a false return value (as of GNO v2.0.4) +since block- and character-special files, and the setuid and setgid bits +are not currently available under GS/OS. +.LP +Because of the implementation of +.BR stat (2) +under GNO v2.0.4, the +.BR \-x +operator will return false if +.I file +is a directory. +.SH RETURN VALUES +The +.B test +utility exits with one of the following values: +.IP \fB0\fR +.I expression +evaluated to true. +.IP \fB1\fR +.I expression +evaluated to false or expression was missing. +.IP \fB>1\fR +An error occurred. +.SH AUTHOR +This code is derived from software contributed to Berkeley by +Kenneth Almquist, and is based on BSD +.BR test +version 5.4. +.LP +The GNO port of +.BR test +was done by Devin Reade, . +.SH STANDARDS +The +.B test +function is expected to be +.I "POSIX 1003.2" +compatible. diff --git a/bin/test/test.c b/bin/test/test.c new file mode 100644 index 0000000..6dfc44c --- /dev/null +++ b/bin/test/test.c @@ -0,0 +1,583 @@ +#line 1 ":trenco4:custom.src:test:test.c" +/* + * $Id: test.c,v 1.1 1996/02/10 08:27:32 gdr Exp $ + */ + +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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') + +#ifdef CHECK_STACK +void begin_stack_check(void); +int end_stack_check(void); +#endif + +/* + * 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 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)); + +#ifdef CHECK_STACK +#define RETURN(val) \ +{ \ + int i; \ + i = (val); \ + printf("DEBUG: test returning %d\n\n",i); \ + printf("stack usage: %d bytes\n",end_stack_check()); \ + return i; \ +} +#else +#define RETURN(val) return val +#endif + + +int +main(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; + +#ifdef CHECK_STACK + begin_stack_check(); +#endif + + 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] == 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) + 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 +#error "gdr: is this supposed to be %ld ? " + 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])); } +} + +static int +expr_is_false(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(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; +#ifdef GNO + case ISREAD: + i = S_IREAD; + goto filebit; /* true if (stat.st_mode & i) != 0 */ + case ISWRITE: + i = S_IWRITE; + goto filebit; /* true if (stat.st_mode & i) != 0 */ + case ISEXEC: + i = S_IEXEC; + goto filebit; /* true if (stat.st_mode & i) != 0 */ +#else + 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 */ +#endif /* not GNO */ + 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(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(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(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(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(void) +{ + err("syntax error"); +} + +static void +overflow(void) +{ + err("expression is too complex"); +} diff --git a/bin/test/test.desc b/bin/test/test.desc new file mode 100644 index 0000000..550462e --- /dev/null +++ b/bin/test/test.desc @@ -0,0 +1,11 @@ +Name: test +Version: 1.0 (9 Feb 96) +Shell: GNO/ME +Author: Devin Reade +Contact: gdr@myrias.com +Where: /bin +FTP: grind.isca.uiowa.edu + + The test utility evaluates string and integer expressions and, if +it evaluates to true, returns a zero (true) exit status; otherwise it +returns 1 (false). Test is used mainly in shell scripts. diff --git a/bin/test/test.rez b/bin/test/test.rez new file mode 100644 index 0000000..7d6dfef --- /dev/null +++ b/bin/test/test.rez @@ -0,0 +1,18 @@ +/* + * $Id: test.rez,v 1.1 1996/02/10 08:27:33 gdr Exp $ + */ + +#include "Types.Rez" + +resource rVersion (0x1, purgeable3, nocrossbank) { + + { 1, 0, 0, /* version 1.0.0 */ + release, /* development|alpha|beta|final|release */ + 0 /* non-final release number */ + }, + verBritain, /* close enough */ + "test", + "Condition evaluation utility\n" + "Devin Reade \n" + "Canada" +}; diff --git a/bin/test/test2.c b/bin/test/test2.c new file mode 100644 index 0000000..d89c946 --- /dev/null +++ b/bin/test/test2.c @@ -0,0 +1,37 @@ +#line 1 ":trenco4:custom.src:test:test2.c" +/* + * $Id: test2.c,v 1.1 1996/02/10 08:27:33 gdr Exp $ + */ + +#include +#include +#include "operators.h" + +#if __STDC__ +#include +#else +#include +#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/unaryop b/bin/test/unaryop new file mode 100644 index 0000000..0defa57 --- /dev/null +++ b/bin/test/unaryop @@ -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 +