View | Details | Raw Unified | Return to bug 13091
Collapse All | Expand All

(-)Makefile (-1 lines)
Lines 2-8 Link Here
2
#	$Id: Makefile,v 1.4 1997/02/22 14:06:17 peter Exp $
2
#	$Id: Makefile,v 1.4 1997/02/22 14:06:17 peter Exp $
3
3
4
PROG=	test
4
PROG=	test
5
SRCS=   test.c operators.c
6
LINKS=	${BINDIR}/test ${BINDIR}/[
5
LINKS=	${BINDIR}/test ${BINDIR}/[
7
MLINKS=	test.1 '[.1'
6
MLINKS=	test.1 '[.1'
8
7
(-)test.1 (-17 / +72 lines)
Lines 39-52 Link Here
39
.Dt TEST 1
39
.Dt TEST 1
40
.Os
40
.Os
41
.Sh NAME
41
.Sh NAME
42
.Nm test
42
.Nm test ,
43
.Nm \&[
43
.Nd condition evaluation utility
44
.Nd condition evaluation utility
44
.Sh SYNOPSIS
45
.Sh SYNOPSIS
45
.Nm test
46
.Nm test
46
.Ar expression
47
.Ar expression
48
.Nm [
49
.Ar expression Cm ]
47
.Sh DESCRIPTION
50
.Sh DESCRIPTION
48
The
51
The
49
.Nm
52
.Nm test
50
utility evaluates the expression and, if it evaluates
53
utility evaluates the expression and, if it evaluates
51
to true, returns a zero (true) exit status; otherwise
54
to true, returns a zero (true) exit status; otherwise
52
it returns 1 (false).
55
it returns 1 (false).
Lines 54-64 Link Here
54
returns 1 (false).
57
returns 1 (false).
55
.Pp
58
.Pp
56
All operators and flags are separate arguments to the
59
All operators and flags are separate arguments to the
57
.Nm
60
.Nm test
58
utility.
61
utility.
59
.Pp
62
.Pp
60
The following primaries are used to construct expression:
63
The following primaries are used to construct expression:
61
.Bl -tag -width indent
64
.Bl -tag -width Ar
62
.It Fl b Ar file
65
.It Fl b Ar file
63
True if
66
True if
64
.Ar file
67
.Ar file
Lines 90-95 Link Here
90
True if
93
True if
91
.Ar file
94
.Ar file
92
exists and is a symbolic link.
95
exists and is a symbolic link.
96
This operator is retained for compatibility with previous versions of
97
this program. Do not rely on its existence; use
98
.Fl L
99
instead.
100
.It Fl k Ar file
101
True if
102
.Ar file
103
exists and its sticky bit is set.
93
.It Fl n Ar string
104
.It Fl n Ar string
94
True if the length of
105
True if the length of
95
.Ar string
106
.Ar string
Lines 99-123 Link Here
99
.Ar file
110
.Ar file
100
is a named pipe
111
is a named pipe
101
.Po Tn FIFO Pc .
112
.Po Tn FIFO Pc .
102
.It Fl S Ar file
103
True if
104
.Ar file
105
is a socket. 
106
.It Fl r Ar file
113
.It Fl r Ar file
107
True if
114
True if
108
.Ar file
115
.Ar file 
109
exists and is readable.
116
exists and is readable.
110
.It Fl s Ar file
117
.It Fl s Ar file
111
True if
118
True if
112
.Ar file
119
.Ar file
113
exists and has a size greater
120
exists and has a size greater
114
than zero.
121
than zero.
115
.It Fl t Ar [file_descriptor]
122
.It Fl t Op Ar file_descriptor
116
True if the file whose file descriptor number
123
True if the file whose file descriptor number
117
is
124
is
118
.Ar file_descriptor
125
.Ar file_descriptor
119
(default 1) is open and is
126
is open and is associated with a terminal.
120
associated with a terminal.
121
.It Fl u Ar file
127
.It Fl u Ar file
122
True if
128
True if
123
.Ar file
129
.Ar file
Lines 146-151 Link Here
146
True if the length of
152
True if the length of
147
.Ar string
153
.Ar string
148
is zero.
154
is zero.
155
.It Fl L Ar file
156
True if 
157
.Ar file
158
exists and is a symbolic link.
159
.It Fl O Ar file
160
True if
161
.Ar file
162
exists and its owner matches the effective user id of this process.
163
.It Fl G Ar file
164
True if
165
.Ar file
166
exists and its group matches the effective group id of this process.
167
.It Fl S Ar file
168
True if
169
.Ar file
170
exists and is a socket.
171
.It Ar file1 Fl nt Ar file2
172
True if
173
.Ar file1
174
exists and is newer than
175
.Ar file2 .
176
.It Ar file1 Fl ot Ar file2
177
True if
178
.Ar file1
179
exists and is older than
180
.Ar file2 .
181
.It Ar file1 Fl ef Ar file2
182
True if
183
.Ar file1
184
and
185
.Ar file2
186
exist and refer to the same file.
149
.It Ar string
187
.It Ar string
150
True if
188
True if
151
.Ar string
189
.Ar string
Lines 163-168 Link Here
163
and
201
and
164
.Ar \&s\&2
202
.Ar \&s\&2
165
are not identical.
203
are not identical.
204
.It Ar \&s\&1 Cm \&< Ar \&s\&2
205
True if string
206
.Ar \&s\&1
207
comes before
208
.Ar \&s\&2
209
based on the ASCII value of their characters.
210
.It Ar \&s\&1 Cm \&> Ar \&s\&2
211
True if string
212
.Ar \&s\&1
213
comes after
214
.Ar \&s\&2
215
based on the ASCII value of their characters.
216
.It Ar \&s\&1
217
True if
218
.Ar \&s\&1
219
is not the null
220
string.
166
.It Ar \&n\&1 Fl \&eq Ar \&n\&2 
221
.It Ar \&n\&1 Fl \&eq Ar \&n\&2 
167
True if the integers
222
True if the integers
168
.Ar \&n\&1
223
.Ar \&n\&1
Lines 232-238 Link Here
232
operator.
287
operator.
233
.Sh GRAMMAR AMBIGUITY
288
.Sh GRAMMAR AMBIGUITY
234
The 
289
The 
235
.Nm
290
.Nm test
236
grammar is inherently ambiguous.  In order to assure a degree of consistency,
291
grammar is inherently ambiguous.  In order to assure a degree of consistency,
237
the cases described in the 
292
the cases described in the 
238
.St -p1003.2 , 
293
.St -p1003.2 , 
Lines 242-248 Link Here
242
command semantics.
297
command semantics.
243
.Sh RETURN VALUES
298
.Sh RETURN VALUES
244
The
299
The
245
.Nm
300
.Nm test
246
utility exits with one of the following values:
301
utility exits with one of the following values:
247
.Bl -tag -width Ds
302
.Bl -tag -width Ds
248
.It 0
303
.It 0
Lines 258-264 Link Here
258
.Xr sh 1
313
.Xr sh 1
259
.Sh STANDARDS
314
.Sh STANDARDS
260
The
315
The
261
.Nm
316
.Nm test
262
function is expected to be
317
utility implements a superset of the
263
.St -p1003.2
318
.St -p1003.2
264
compatible.
319
specification.
(-)test.c (-509 / +379 lines)
Lines 1-593 Link Here
1
/*-
1
/*	$NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $	*/
2
 * Copyright (c) 1992, 1993, 1994
2
3
 *	The Regents of the University of California.  All rights reserved.
3
/*
4
 *
4
 * test(1); version 7-like  --  author Erik Baalbergen
5
 * This code is derived from software contributed to Berkeley by
5
 * modified by Eric Gisin to be used as built-in.
6
 * Kenneth Almquist.
6
 * modified by Arnold Robbins to add SVR3 compatibility
7
 *
7
 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8
 * Redistribution and use in source and binary forms, with or without
8
 * modified by J.T. Conklin for NetBSD.
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. All advertising materials mentioning features or use of this software
17
 *    must display the following acknowledgement:
18
 *	This product includes software developed by the University of
19
 *	California, Berkeley and its contributors.
20
 * 4. Neither the name of the University nor the names of its contributors
21
 *    may be used to endorse or promote products derived from this software
22
 *    without specific prior written permission.
23
 *
9
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
10
 * This program is in the Public Domain.
25
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34
 * SUCH DAMAGE.
35
 */
11
 */
36
12
37
#ifndef lint
13
#ifndef lint
38
static char const copyright[] =
39
"@(#) Copyright (c) 1992, 1993, 1994\n\
40
	The Regents of the University of California.  All rights reserved.\n";
41
#endif /* not lint */
42
43
#ifndef lint
44
#if 0
45
static char sccsid[] = "@(#)test.c	8.3 (Berkeley) 4/2/94";
46
#endif
47
static const char rcsid[] =
14
static const char rcsid[] =
48
	"$Id: test.c,v 1.21 1999/05/08 10:22:15 kris Exp $";
15
	"$Id: test.c,v 1.21 1999/05/08 10:22:15 kris Exp $";
49
#endif /* not lint */
16
#endif /* not lint */
50
17
51
#include <sys/param.h>
18
#include <sys/types.h>
52
#include <sys/stat.h>
19
#include <sys/stat.h>
53
20
54
#include <ctype.h>
21
#include <ctype.h>
55
#include <err.h>
22
#include <err.h>
56
#include <errno.h>
23
#include <errno.h>
57
#include <limits.h>
58
#include <stdio.h>
24
#include <stdio.h>
59
#include <stdlib.h>
25
#include <stdlib.h>
60
#include <string.h>
26
#include <string.h>
61
#include <unistd.h>
27
#include <unistd.h>
62
63
#include "operators.h"
64
28
65
#define	STACKSIZE	12
29
/* test(1) accepts the following grammar:
66
#define	NESTINCR	16
30
	oexpr	::= aexpr | aexpr "-o" oexpr ;
67
31
	aexpr	::= nexpr | nexpr "-a" aexpr ;
68
/* data types */
32
	nexpr	::= primary | "!" primary
69
#define	STRING	0
33
	primary	::= unary-operator operand
70
#define	INTEGER	1
34
		| operand binary-operator operand
71
#define	BOOLEAN	2
35
		| operand
36
		| "(" oexpr ")"
37
		;
38
	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
39
		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
72
40
73
#define	IS_BANG(s) (s[0] == '!' && s[1] == '\0')
41
	binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
42
			"-nt"|"-ot"|"-ef";
43
	operand ::= <any legal UNIX file name>
44
*/
74
45
75
/*
46
enum token {
76
 * This structure hold a value.  The type keyword specifies the type of
47
	EOI,
77
 * the value, and the union u holds the value.  The value of a boolean
48
	FILRD,
78
 * is stored in u.num (1 = TRUE, 0 = FALSE).
49
	FILWR,
79
 */
50
	FILEX,
80
struct value {
51
	FILEXIST,
81
	int type;
52
	FILREG,
82
	union {
53
	FILDIR,
83
		char *string;
54
	FILCDEV,
84
		long num;
55
	FILBDEV,
85
	} u;
56
	FILFIFO,
57
	FILSOCK,
58
	FILSYM,
59
	FILGZ,
60
	FILTT,
61
	FILSUID,
62
	FILSGID,
63
	FILSTCK,
64
	FILNT,
65
	FILOT,
66
	FILEQ,
67
	FILUID,
68
	FILGID,
69
	STREZ,
70
	STRNZ,
71
	STREQ,
72
	STRNE,
73
	STRLT,
74
	STRGT,
75
	INTEQ,
76
	INTNE,
77
	INTGE,
78
	INTGT,
79
	INTLE,
80
	INTLT,
81
	UNOT,
82
	BAND,
83
	BOR,
84
	LPAREN,
85
	RPAREN,
86
	OPERAND
86
};
87
};
87
88
88
struct operator {
89
enum token_types {
89
	short op;		/* Which operator. */
90
	UNOP,
90
	short pri;		/* Priority of operator. */
91
	BINOP,
92
	BUNOP,
93
	BBINOP,
94
	PAREN
91
};
95
};
92
96
93
struct filestat {
97
struct t_op {
94
	char *name;		/* Name of file. */
98
	const char *op_text;
95
	int rcode;		/* Return code from stat. */
99
	short op_num, op_type;
96
	struct stat stat;	/* Status info on file. */
100
} const ops [] = {
101
	{"-r",	FILRD,	UNOP},
102
	{"-w",	FILWR,	UNOP},
103
	{"-x",	FILEX,	UNOP},
104
	{"-e",	FILEXIST,UNOP},
105
	{"-f",	FILREG,	UNOP},
106
	{"-d",	FILDIR,	UNOP},
107
	{"-c",	FILCDEV,UNOP},
108
	{"-b",	FILBDEV,UNOP},
109
	{"-p",	FILFIFO,UNOP},
110
	{"-u",	FILSUID,UNOP},
111
	{"-g",	FILSGID,UNOP},
112
	{"-k",	FILSTCK,UNOP},
113
	{"-s",	FILGZ,	UNOP},
114
	{"-t",	FILTT,	UNOP},
115
	{"-z",	STREZ,	UNOP},
116
	{"-n",	STRNZ,	UNOP},
117
	{"-h",	FILSYM,	UNOP},		/* for backwards compat */
118
	{"-O",	FILUID,	UNOP},
119
	{"-G",	FILGID,	UNOP},
120
	{"-L",	FILSYM,	UNOP},
121
	{"-S",	FILSOCK,UNOP},
122
	{"=",	STREQ,	BINOP},
123
	{"!=",	STRNE,	BINOP},
124
	{"<",	STRLT,	BINOP},
125
	{">",	STRGT,	BINOP},
126
	{"-eq",	INTEQ,	BINOP},
127
	{"-ne",	INTNE,	BINOP},
128
	{"-ge",	INTGE,	BINOP},
129
	{"-gt",	INTGT,	BINOP},
130
	{"-le",	INTLE,	BINOP},
131
	{"-lt",	INTLT,	BINOP},
132
	{"-nt",	FILNT,	BINOP},
133
	{"-ot",	FILOT,	BINOP},
134
	{"-ef",	FILEQ,	BINOP},
135
	{"!",	UNOT,	BUNOP},
136
	{"-a",	BAND,	BBINOP},
137
	{"-o",	BOR,	BBINOP},
138
	{"(",	LPAREN,	PAREN},
139
	{")",	RPAREN,	PAREN},
140
	{0,	0,	0}
97
};
141
};
98
142
99
static int	expr_is_false __P((struct value *));
143
struct t_op const *t_wp_op;
100
static void	expr_operator __P((int, struct value *, struct filestat *));
144
char **t_wp;
101
static void	get_int __P((char *, long *));
102
static int	lookup_op __P((char *, const char *const *));
103
static void	overflow __P((void));
104
static int	posix_binary_op __P((char **));
105
static int	posix_unary_op __P((char **));
106
static void	syntax __P((void));
107
145
146
static void syntax __P((const char *, const char *));
147
static enum token t_lex __P((char *));
148
static int oexpr __P((enum token));
149
static int aexpr __P((enum token));
150
static int nexpr __P((enum token));
151
static int primary __P((enum token));
152
static int binop __P((void));
153
static int filstat __P((char *, enum token));
154
static int isoperand __P((void));
155
static int getn __P((const char *));
156
static int newerf __P((const char *, const char *));
157
static int olderf __P((const char *, const char *));
158
static int equalf __P((const char *, const char *));
159
108
int
160
int
109
main(argc, argv)
161
main(argc, argv)
110
	int argc;
162
	int argc;
111
	char *argv[];
163
	char *argv[];
112
{
164
{
113
	struct operator opstack[STACKSIZE];
165
	int	res;
114
	struct operator *opsp;
115
	struct value valstack[STACKSIZE + 1];
116
	struct value *valsp;
117
	struct filestat fs;
118
	char  c, **ap, *opname, *p;
119
	int binary, nest, op = 0, pri, ret_val, skipping;
120
121
	if ((p = argv[0]) == NULL)
122
		errx(2, "test: argc is zero");
123
166
124
	if (*p != '\0' && p[strlen(p) - 1] == '[') {
167
	if (strcmp(argv[0], "[") == 0) {
125
		if (strcmp(argv[--argc], "]"))
168
		if (strcmp(argv[--argc], "]"))
126
			errx(2, "missing ]");
169
			errx(2, "missing ]");
127
		argv[argc] = NULL;
170
		argv[argc] = NULL;
128
	}
171
	}
129
	ap = argv + 1;
130
	fs.name = NULL;
131
172
132
	/*
173
	t_wp = &argv[1];
133
	 * Test(1) implements an inherently ambiguous grammar.  In order to
174
	res = !oexpr(t_lex(*t_wp));
134
	 * assure some degree of consistency, we special case the POSIX 1003.2
135
	 * requirements to assure correct evaluation for POSIX scripts.  The
136
	 * following special cases comply with POSIX P1003.2/D11.2 Section
137
	 * 4.62.4.
138
	 */
139
	switch(argc - 1) {
140
	case 0:				/* % test */
141
		return (1);
142
		break;
143
	case 1:				/* % test arg */
144
		return (argv[1] == NULL || *argv[1] == '\0') ? 1 : 0;
145
		break;
146
	case 2:				/* % test op arg */
147
		opname = argv[1];
148
		if (IS_BANG(opname))
149
			return (*argv[2] == '\0') ? 0 : 1;
150
		else {
151
			ret_val = posix_unary_op(&argv[1]);
152
			if (ret_val >= 0)
153
				return (ret_val);
154
		}
155
		break;
156
	case 3:				/* % test arg1 op arg2 */
157
		if (IS_BANG(argv[1])) {
158
			ret_val = posix_unary_op(&argv[1]);
159
			if (ret_val >= 0)
160
				return (!ret_val);
161
		} else if (lookup_op(argv[2], andor_op) < 0) {
162
			ret_val = posix_binary_op(&argv[1]);
163
			if (ret_val >= 0)
164
				return (ret_val);
165
		}
166
		break;
167
	case 4:				/* % test ! arg1 op arg2 */
168
		if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0 ) {
169
			ret_val = posix_binary_op(&argv[2]);
170
			if (ret_val >= 0)
171
				return (!ret_val);
172
		}
173
		break;
174
	default:
175
		break;
176
	}
177
175
178
	/*
176
	if (*t_wp != NULL && *++t_wp != NULL)
179
	 * We use operator precedence parsing, evaluating the expression as
177
		syntax(*t_wp, "unexpected operator");
180
	 * we parse it.  Parentheses are handled by bumping up the priority
178
181
	 * of operators using the variable "nest."  We use the variable
179
	return res;
182
	 * "skipping" to turn off evaluation temporarily for the short
180
}
183
	 * circuit boolean operators.  (It is important do the short circuit
181
184
	 * evaluation because under NFS a stat operation can take infinitely
182
static void
185
	 * long.)
183
syntax(op, msg)
186
	 */
184
	const char	*op;
187
	opsp = opstack + STACKSIZE;
185
	const char	*msg;
188
	valsp = valstack;
186
{
189
	nest = skipping = 0;
187
	if (op && *op)
190
	if (*ap == NULL) {
188
		errx(2, "%s: %s", op, msg);
191
		valstack[0].type = BOOLEAN;
189
	else
192
		valstack[0].u.num = 0;
190
		errx(2, "%s", msg);
193
		goto done;
194
	}
195
	for (;;) {
196
		opname = *ap++;
197
		if (opname == NULL)
198
			syntax();
199
		if (opname[0] == '(' && opname[1] == '\0') {
200
			nest += NESTINCR;
201
			continue;
202
		} else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
203
			if (opsp == &opstack[0])
204
				overflow();
205
			--opsp;
206
			opsp->op = op;
207
			opsp->pri = op_priority[op] + nest;
208
			continue;
209
		} else {
210
			valsp->type = STRING;
211
			valsp->u.string = opname;
212
			valsp++;
213
		}
214
		for (;;) {
215
			opname = *ap++;
216
			if (opname == NULL) {
217
				if (nest != 0)
218
					syntax();
219
				pri = 0;
220
				break;
221
			}
222
			if (opname[0] != ')' || opname[1] != '\0') {
223
				if ((op = lookup_op(opname, binary_op)) < 0)
224
					syntax();
225
				op += FIRST_BINARY_OP;
226
				pri = op_priority[op] + nest;
227
				break;
228
			}
229
			if ((nest -= NESTINCR) < 0)
230
				syntax();
231
		}
232
		while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
233
			binary = opsp->op;
234
			for (;;) {
235
				valsp--;
236
				c = op_argflag[opsp->op];
237
				if (c == OP_INT) {
238
					if (valsp->type == STRING)
239
						get_int(valsp->u.string,
240
						    &valsp->u.num);
241
					valsp->type = INTEGER;
242
				} else if (c >= OP_STRING) {
243
					            /* OP_STRING or OP_FILE */
244
					if (valsp->type == INTEGER) {
245
						if ((p = malloc(32)) == NULL)
246
							err(2, NULL);
247
#ifdef SHELL
248
						fmtstr(p, 32, "%d",
249
						    valsp->u.num);
250
#else
251
						(void)sprintf(p,
252
						    "%ld", valsp->u.num);
253
#endif
254
						valsp->u.string = p;
255
					} else if (valsp->type == BOOLEAN) {
256
						if (valsp->u.num)
257
							valsp->u.string =
258
						            "true";
259
						else
260
							valsp->u.string = "";
261
					}
262
					valsp->type = STRING;
263
					if (c == OP_FILE && (fs.name == NULL ||
264
					    strcmp(fs.name, valsp->u.string))) {
265
						fs.name = valsp->u.string;
266
						fs.rcode =
267
						    stat(valsp->u.string,
268
                                                    &fs.stat);
269
					}
270
				}
271
				if (binary < FIRST_BINARY_OP)
272
					break;
273
				binary = 0;
274
			}
275
			if (!skipping)
276
				expr_operator(opsp->op, valsp, &fs);
277
			else if (opsp->op == AND1 || opsp->op == OR1)
278
				skipping--;
279
			valsp++;		/* push value */
280
			opsp++;			/* pop operator */
281
		}
282
		if (opname == NULL)
283
			break;
284
		if (opsp == &opstack[0])
285
			overflow();
286
		if (op == AND1 || op == AND2) {
287
			op = AND1;
288
			if (skipping || expr_is_false(valsp - 1))
289
				skipping++;
290
		}
291
		if (op == OR1 || op == OR2) {
292
			op = OR1;
293
			if (skipping || !expr_is_false(valsp - 1))
294
				skipping++;
295
		}
296
		opsp--;
297
		opsp->op = op;
298
		opsp->pri = pri;
299
	}
300
done:	return (expr_is_false(&valstack[0]));
301
}
191
}
302
192
303
static int
193
static int
304
expr_is_false(val)
194
oexpr(n)
305
	struct value *val;
195
	enum token n;
306
{
196
{
197
	int res;
307
198
308
	if (val->type == STRING) {
199
	res = aexpr(n);
309
		if (val->u.string[0] == '\0')
200
	if (t_lex(*++t_wp) == BOR)
310
			return (1);
201
		return oexpr(t_lex(*++t_wp)) || res;
311
	} else {		/* INTEGER or BOOLEAN */
202
	t_wp--;
312
		if (val->u.num == 0)
203
	return res;
313
			return (1);
314
	}
315
	return (0);
316
}
204
}
317
205
206
static int
207
aexpr(n)
208
	enum token n;
209
{
210
	int res;
318
211
319
/*
212
	res = nexpr(n);
320
 * Execute an operator.  Op is the operator.  Sp is the stack pointer;
213
	if (t_lex(*++t_wp) == BAND)
321
 * sp[0] refers to the first operand, sp[1] refers to the second operand
214
		return aexpr(t_lex(*++t_wp)) && res;
322
 * (if any), and the result is placed in sp[0].  The operands are converted
215
	t_wp--;
323
 * to the type expected by the operator before expr_operator is called.
216
	return res;
324
 * Fs is a pointer to a structure which holds the value of the last call
217
}
325
 * to stat, to avoid repeated stat calls on the same file.
218
326
 */
219
static int
327
static void
220
nexpr(n)
328
expr_operator(op, sp, fs)
221
	enum token n;			/* token */
329
	int op;
330
	struct value *sp;
331
	struct filestat *fs;
332
{
222
{
333
	int i;
223
	if (n == UNOT)
224
		return !nexpr(t_lex(*++t_wp));
225
	return primary(n);
226
}
334
227
335
	switch (op) {
228
static int
336
	case NOT:
229
primary(n)
337
		sp->u.num = expr_is_false(sp);
230
	enum token n;
338
		sp->type = BOOLEAN;
231
{
339
		break;
232
	enum token nn;
340
	case ISEXIST:
233
	int res;
341
exist:
234
342
		if (fs == NULL || fs->rcode == -1)
235
	if (n == EOI)
343
			goto false;
236
		return 0;		/* missing expression */
344
		else
237
	if (n == LPAREN) {
345
			goto true;
238
		if ((nn = t_lex(*++t_wp)) == RPAREN)
346
	case ISREAD:
239
			return 0;	/* missing expression */
347
		if (geteuid() == 0)
240
		res = oexpr(nn);
348
			goto exist;
241
		if (t_lex(*++t_wp) != RPAREN)
349
		i = S_IROTH;
242
			syntax(NULL, "closing paren expected");
350
		goto permission;
243
		return res;
351
	case ISWRITE:
244
	}
352
		if (geteuid() != 0)
245
	if (t_wp_op && t_wp_op->op_type == UNOP) {
353
			i = S_IWOTH;
246
		/* unary expression */
354
		else {
247
		if (*++t_wp == NULL)
355
			i = S_IWOTH|S_IWGRP|S_IWUSR;
248
			syntax(t_wp_op->op_text, "argument expected");
356
			goto filebit;
249
		switch (n) {
250
		case STREZ:
251
			return strlen(*t_wp) == 0;
252
		case STRNZ:
253
			return strlen(*t_wp) != 0;
254
		case FILTT:
255
			return isatty(getn(*t_wp));
256
		default:
257
			return filstat(*t_wp, n);
357
		}
258
		}
358
		goto permission;
259
	}
359
	case ISEXEC:
360
		if (geteuid() != 0) {
361
			i = S_IXOTH;
362
permission:		if (fs->stat.st_uid == geteuid())
363
				i <<= 6;
364
			else {
365
				gid_t grlist[NGROUPS];
366
				int ngroups, j;
367
260
368
				ngroups = getgroups(NGROUPS, grlist);
261
	if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
369
				for (j = 0; j < ngroups; j++)
262
		return binop();
370
					if (fs->stat.st_gid == grlist[j]) {
263
	}	  
371
						i <<= 3;
372
						goto filebit;
373
					}
374
			}
375
		} else
376
			i = S_IXOTH|S_IXGRP|S_IXUSR;
377
		goto filebit;	/* true if (stat.st_mode & i) != 0 */
378
	case ISFILE:
379
		i = S_IFREG;
380
		goto filetype;
381
	case ISDIR:
382
		i = S_IFDIR;
383
		goto filetype;
384
	case ISCHAR:
385
		i = S_IFCHR;
386
		goto filetype;
387
	case ISBLOCK:
388
		i = S_IFBLK;
389
		goto filetype;
390
	case ISSYMLINK:
391
		i = S_IFLNK;
392
		fs->rcode = lstat(sp->u.string, &fs->stat);
393
		goto filetype;
394
	case ISFIFO:
395
		i = S_IFIFO;
396
		goto filetype;
397
	case ISSOCK:
398
		i = S_IFSOCK;
399
		goto filetype;
400
filetype:	if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
401
true:			sp->u.num = 1;
402
		else
403
false:			sp->u.num = 0;
404
		sp->type = BOOLEAN;
405
		break;
406
	case ISSETUID:
407
		i = S_ISUID;
408
		goto filebit;
409
	case ISSETGID:
410
		i = S_ISGID;
411
		goto filebit;
412
	case ISSTICKY:
413
		i = S_ISVTX;
414
filebit:	if (fs->stat.st_mode & i && fs->rcode >= 0)
415
			goto true;
416
		goto false;
417
	case ISSIZE:
418
		sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
419
		sp->type = INTEGER;
420
		break;
421
	case ISTTY:
422
		sp->u.num = isatty(sp->u.num);
423
		sp->type = BOOLEAN;
424
		break;
425
	case NULSTR:
426
		if (sp->u.string[0] == '\0')
427
			goto true;
428
		goto false;
429
	case STRLEN:
430
		sp->u.num = strlen(sp->u.string);
431
		sp->type = INTEGER;
432
		break;
433
	case OR1:
434
	case AND1:
435
		/*
436
		 * These operators are mostly handled by the parser.  If we
437
		 * get here it means that both operands were evaluated, so
438
		 * the value is the value of the second operand.
439
		 */
440
		*sp = *(sp + 1);
441
		break;
442
	case STREQ:
443
	case STRNE:
444
		i = 0;
445
		if (!strcmp(sp->u.string, (sp + 1)->u.string))
446
			i++;
447
		if (op == STRNE)
448
			i = 1 - i;
449
		sp->u.num = i;
450
		sp->type = BOOLEAN;
451
		break;
452
	case EQ:
453
		if (sp->u.num == (sp + 1)->u.num)
454
			goto true;
455
		goto false;
456
	case NE:
457
		if (sp->u.num != (sp + 1)->u.num)
458
			goto true;
459
		goto false;
460
	case GT:
461
		if (sp->u.num > (sp + 1)->u.num)
462
			goto true;
463
		goto false;
464
	case LT:
465
		if (sp->u.num < (sp + 1)->u.num)
466
			goto true;
467
		goto false;
468
	case LE:
469
		if (sp->u.num <= (sp + 1)->u.num)
470
			goto true;
471
		goto false;
472
	case GE:
473
		if (sp->u.num >= (sp + 1)->u.num)
474
			goto true;
475
		goto false;
476
264
477
	}
265
	return strlen(*t_wp) > 0;
478
}
266
}
479
267
480
static int
268
static int
481
lookup_op(name, table)
269
binop()
482
	char *name;
483
	const char *const * table;
484
{
270
{
485
	const char *const * tp;
271
	const char *opnd1, *opnd2;
486
	const char *p;
272
	struct t_op const *op;
487
	char c;
488
273
489
	c = name[1];
274
	opnd1 = *t_wp;
490
	for (tp = table; (p = *tp) != NULL; tp++)
275
	(void) t_lex(*++t_wp);
491
		if (p[1] == c && !strcmp(p, name))
276
	op = t_wp_op;
492
			return (tp - table);
277
493
	return (-1);
278
	if ((opnd2 = *++t_wp) == (char *)0)
279
		syntax(op->op_text, "argument expected");
280
		
281
	switch (op->op_num) {
282
	case STREQ:
283
		return strcmp(opnd1, opnd2) == 0;
284
	case STRNE:
285
		return strcmp(opnd1, opnd2) != 0;
286
	case STRLT:
287
		return strcmp(opnd1, opnd2) < 0;
288
	case STRGT:
289
		return strcmp(opnd1, opnd2) > 0;
290
	case INTEQ:
291
		return getn(opnd1) == getn(opnd2);
292
	case INTNE:
293
		return getn(opnd1) != getn(opnd2);
294
	case INTGE:
295
		return getn(opnd1) >= getn(opnd2);
296
	case INTGT:
297
		return getn(opnd1) > getn(opnd2);
298
	case INTLE:
299
		return getn(opnd1) <= getn(opnd2);
300
	case INTLT:
301
		return getn(opnd1) < getn(opnd2);
302
	case FILNT:
303
		return newerf (opnd1, opnd2);
304
	case FILOT:
305
		return olderf (opnd1, opnd2);
306
	case FILEQ:
307
		return equalf (opnd1, opnd2);
308
	default:
309
		abort();
310
		/* NOTREACHED */
311
	}
494
}
312
}
495
313
496
static int
314
static int
497
posix_unary_op(argv)
315
filstat(nm, mode)
498
	char **argv;
316
	char *nm;
317
	enum token mode;
499
{
318
{
500
	struct filestat fs;
319
	struct stat s;
501
	struct value valp;
502
	int op, c;
503
	char *opname;
504
320
505
	opname = *argv;
321
	if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
506
	if ((op = lookup_op(opname, unary_op)) < 0)
322
		return 0;
507
		return (-1);
508
	c = op_argflag[op];
509
	opname = argv[1];
510
	valp.u.string = opname;
511
	if (c == OP_FILE) {
512
		fs.name = opname;
513
		fs.rcode = stat(opname, &fs.stat);
514
	} else if (c != OP_STRING)
515
		return (-1);
516
323
517
	expr_operator(op, &valp, &fs);
324
	switch (mode) {
518
	return (valp.u.num == 0);
325
	case FILRD:
326
		return access(nm, R_OK) == 0;
327
	case FILWR:
328
		return access(nm, W_OK) == 0;
329
	case FILEX:
330
		return access(nm, X_OK) == 0;
331
	case FILEXIST:
332
		return access(nm, F_OK) == 0;
333
	case FILREG:
334
		return S_ISREG(s.st_mode);
335
	case FILDIR:
336
		return S_ISDIR(s.st_mode);
337
	case FILCDEV:
338
		return S_ISCHR(s.st_mode);
339
	case FILBDEV:
340
		return S_ISBLK(s.st_mode);
341
	case FILFIFO:
342
		return S_ISFIFO(s.st_mode);
343
	case FILSOCK:
344
		return S_ISSOCK(s.st_mode);
345
	case FILSYM:
346
		return S_ISLNK(s.st_mode);
347
	case FILSUID:
348
		return (s.st_mode & S_ISUID) != 0;
349
	case FILSGID:
350
		return (s.st_mode & S_ISGID) != 0;
351
	case FILSTCK:
352
		return (s.st_mode & S_ISVTX) != 0;
353
	case FILGZ:
354
		return s.st_size > (off_t)0;
355
	case FILUID:
356
		return s.st_uid == geteuid();
357
	case FILGID:
358
		return s.st_gid == getegid();
359
	default:
360
		return 1;
361
	}
519
}
362
}
520
363
521
static int
364
static enum token
522
posix_binary_op(argv)
365
t_lex(s)
523
	char  **argv;
366
	char *s;
524
{
367
{
525
	struct value v[2];
368
	struct t_op const *op = ops;
526
	int op, c;
527
	char *opname;
528
369
529
	opname = argv[1];
370
	if (s == 0) {
530
	if ((op = lookup_op(opname, binary_op)) < 0)
371
		t_wp_op = (struct t_op *)0;
531
		return (-1);
372
		return EOI;
532
	op += FIRST_BINARY_OP;
373
	}
533
	c = op_argflag[op];
374
	while (op->op_text) {
375
		if (strcmp(s, op->op_text) == 0) {
376
			if ((op->op_type == UNOP && isoperand()) ||
377
			    (op->op_num == LPAREN && *(t_wp+1) == 0))
378
				break;
379
			t_wp_op = op;
380
			return op->op_num;
381
		}
382
		op++;
383
	}
384
	t_wp_op = (struct t_op *)0;
385
	return OPERAND;
386
}
534
387
535
	if (c == OP_INT) {
388
static int
536
		get_int(argv[0], &v[0].u.num);
389
isoperand()
537
		get_int(argv[2], &v[1].u.num);
390
{
538
	} else {
391
	struct t_op const *op = ops;
539
		v[0].u.string = argv[0];
392
	char *s;
540
		v[1].u.string = argv[2];
393
	char *t;
394
395
	if ((s  = *(t_wp+1)) == 0)
396
		return 1;
397
	if ((t = *(t_wp+2)) == 0)
398
		return 0;
399
	while (op->op_text) {
400
		if (strcmp(s, op->op_text) == 0)
401
	    		return op->op_type == BINOP &&
402
	    		    (t[0] != ')' || t[1] != '\0'); 
403
		op++;
541
	}
404
	}
542
	expr_operator(op, v, NULL);
405
	return 0;
543
	return (v[0].u.num == 0);
544
}
406
}
545
407
546
/*
408
/* atoi with error detection */
547
 * Integer type checking.
409
static int
548
 */
410
getn(s)
549
static void
411
	const char *s;
550
get_int(v, lp)
551
	char *v;
552
	long *lp;
553
{
412
{
554
	long val;
413
	char *p;
555
	char *ep;
414
	long r;
556
415
557
	for (; *v && isspace(*v); ++v);
416
	errno = 0;
417
	r = strtol(s, &p, 10);
558
418
559
	if(!*v) {
419
	if (errno != 0)
560
		*lp = 0;
420
	  errx(2, "%s: out of range", s);
561
		return;
562
	}
563
421
564
	if (isdigit(*v) || ((*v == '-' || *v == '+') && isdigit(*(v+1)))) {
422
	while (isspace((unsigned char)*p))
565
		errno = 0;
423
	  p++;
566
		val = strtol(v, &ep, 10);
424
	
567
		if (*ep != '\0')
425
	if (*p)
568
			errx(2, "%s: trailing non-numeric characters", v);
426
	  errx(2, "%s: bad number", s);
569
		if (errno == ERANGE) {
427
570
			if (val == LONG_MIN)
428
	return (int) r;
571
				errx(2, "%s: underflow", v);
572
			if (val == LONG_MAX)
573
				errx(2, "%s: overflow", v);
574
		}
575
		*lp = val;
576
		return;
577
	}
578
	errx(2, "%s: expected integer", v);
579
}
429
}
580
430
581
static void
431
static int
582
syntax()
432
newerf (f1, f2)
433
	const char *f1, *f2;
583
{
434
{
435
	struct stat b1, b2;
584
436
585
	errx(2, "syntax error");
437
	return (stat (f1, &b1) == 0 &&
438
		stat (f2, &b2) == 0 &&
439
		b1.st_mtime > b2.st_mtime);
586
}
440
}
587
441
588
static void
442
static int
589
overflow()
443
olderf (f1, f2)
444
	const char *f1, *f2;
590
{
445
{
446
	struct stat b1, b2;
591
447
592
	errx(2, "expression is too complex");
448
	return (stat (f1, &b1) == 0 &&
449
		stat (f2, &b2) == 0 &&
450
		b1.st_mtime < b2.st_mtime);
451
}
452
453
static int
454
equalf (f1, f2)
455
	const char *f1, *f2;
456
{
457
	struct stat b1, b2;
458
459
	return (stat (f1, &b1) == 0 &&
460
		stat (f2, &b2) == 0 &&
461
		b1.st_dev == b2.st_dev &&
462
		b1.st_ino == b2.st_ino);
593
}
463
}

Return to bug 13091