Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 12 Aug 1999 13:45:56 +0200
From:      Sheldon Hearn <sheldonh@uunet.co.za>
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   bin/13091: [PATCH] pdksh-derived replacement for test(1)
Message-ID:  <3935.934458356@axl.noc.iafrica.com>

next in thread | raw e-mail | index | archive | help

>Number:         13091
>Category:       bin
>Synopsis:       [PATCH] pdksh-derived replacement for test(1)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Thu Aug 12 04:50:01 PDT 1999
>Closed-Date:
>Last-Modified:
>Originator:     Sheldon Hearn
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
UUNET South Africa
>Environment:

	4.0-CURRENT

>Description:

	A recent query on the freebsd-hackers list for new features in
	test brought up the issue of NetBSD's pdksh-derived test(1).
	Two noteworthy extensions are the -nt (newer than) and -ot
	(older than) binary file operators.

	NetBSD has had this test(1) since mid 1994, with no significant
	changes since since late 1997.

>How-To-Repeat:

	N/A

>Fix:

	Apply the following 27KB diff. Note that the operators.* files
	are obsoleted by this change.

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

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3935.934458356>