From e3cfc8ce61f788739c66445d903f8beacb40c93d Mon Sep 17 00:00:00 2001 From: rgrimes Date: Thu, 26 May 1994 06:18:55 +0000 Subject: BSD 4.4 Lite bin Sources --- bin/test/Makefile | 8 + bin/test/TEST.csh | 137 +++++++++++++ bin/test/operators.c | 148 ++++++++++++++ bin/test/operators.h | 77 +++++++ bin/test/test.1 | 255 +++++++++++++++++++++++ bin/test/test.c | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1185 insertions(+) create mode 100644 bin/test/Makefile create mode 100644 bin/test/TEST.csh create mode 100644 bin/test/operators.c create mode 100644 bin/test/operators.h create mode 100644 bin/test/test.1 create mode 100644 bin/test/test.c (limited to 'bin/test') 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 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 + +#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 \> 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 \< 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 +#include + +#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') + +/* + * 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"); +} -- cgit v1.1