* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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
static char copyright
[] =
"@(#) Copyright (c) 1992, 1993\n\
The Regents of the University of California. All rights reserved.\n";
static char sccsid
[] = "@(#)test.c 8.1 (Berkeley) 5/31/93";
#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).
short op
; /* Which operator. */
short pri
; /* Priority of operator. */
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 *, 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 operator opstack
[STACKSIZE
];
struct value valstack
[STACKSIZE
+ 1];
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
], "]"))
* 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
return (argv
[1] == NULL
|| *argv
[1] == '\0') ? 1 : 0;
case 2: /* % test op arg */
return (*argv
[2] == '\0') ? 0 : 1;
ret_val
= posix_unary_op(&argv
[1]);
case 3: /* % test arg1 op arg2 */
ret_val
= posix_unary_op(&argv
[1]);
ret_val
= posix_binary_op(&argv
[1]);
case 4: /* % test ! arg1 op arg2 */
ret_val
= posix_binary_op(&argv
[2]);
* 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
opsp
= opstack
+ STACKSIZE
;
valstack
[0].type
= BOOLEAN
;
if (opname
[0] == '(' && opname
[1] == '\0') {
} else if (*ap
&& (op
= lookup_op(opname
, unary_op
)) >= 0) {
opsp
->pri
= op_priority
[op
] + nest
;
valsp
->u
.string
= opname
;
if (opname
[0] != ')' || opname
[1] != '\0') {
if ((op
= lookup_op(opname
, binary_op
)) < 0)
pri
= op_priority
[op
] + nest
;
if ((nest
-= NESTINCR
) < 0)
while (opsp
< &opstack
[STACKSIZE
] && opsp
->pri
>= pri
) {
c
= op_argflag
[opsp
->op
];
if (valsp
->type
== STRING
)
} else if (c
>= OP_STRING
) {
/* OP_STRING or OP_FILE */
if (valsp
->type
== INTEGER
) {
if ((p
= malloc(32)) == NULL
)
} else if (valsp
->type
== BOOLEAN
) {
if (c
== OP_FILE
&& (fs
.name
== NULL
||
strcmp(fs
.name
, valsp
->u
.string
))) {
fs
.name
= valsp
->u
.string
;
if (binary
< FIRST_BINARY_OP
)
expr_operator(opsp
->op
, valsp
, &fs
);
else if (opsp
->op
== AND1
|| opsp
->op
== OR1
)
valsp
++; /* push value */
opsp
++; /* pop operator */
if (op
== AND1
|| op
== AND2
) {
if (skipping
|| expr_is_false(valsp
- 1))
if (op
== OR1
|| op
== OR2
) {
if (skipping
|| !expr_is_false(valsp
- 1))
done
: return (expr_is_false(&valstack
[0]));
if (val
->type
== STRING
) {
if (val
->u
.string
[0] == '\0')
} else { /* INTEGER or BOOLEAN */
* 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.
expr_operator(op
, sp
, fs
)
sp
->u
.num
= expr_is_false(sp
);
if (fs
== NULL
|| fs
->rcode
== -1)
permission
: if (fs
->stat
.st_uid
== geteuid())
else if (fs
->stat
.st_gid
== getegid())
goto filebit
; /* true if (stat.st_mode & i) != 0 */
(void)lstat(sp
->u
.string
, &fs
->stat
);
filetype
: if ((fs
->stat
.st_mode
& S_IFMT
) == i
&& fs
->rcode
>= 0)
filebit
: if (fs
->stat
.st_mode
& i
&& fs
->rcode
>= 0)
sp
->u
.num
= fs
->rcode
>= 0 ? fs
->stat
.st_size
: 0L;
sp
->u
.num
= isatty(sp
->u
.num
);
if (sp
->u
.string
[0] == '\0')
sp
->u
.num
= strlen(sp
->u
.string
);
* 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.
if (!strcmp(sp
->u
.string
, (sp
+ 1)->u
.string
))
if (sp
->u
.num
== (sp
+ 1)->u
.num
)
if (sp
->u
.num
!= (sp
+ 1)->u
.num
)
if (sp
->u
.num
> (sp
+ 1)->u
.num
)
if (sp
->u
.num
< (sp
+ 1)->u
.num
)
if (sp
->u
.num
<= (sp
+ 1)->u
.num
)
if (sp
->u
.num
>= (sp
+ 1)->u
.num
)
register char *const * tp
;
for (tp
= table
; (p
= *tp
) != NULL
; tp
++)
if (p
[1] == c
&& !strcmp(p
, name
))
if ((op
= lookup_op(opname
, unary_op
)) < 0)
fs
.rcode
= stat(opname
, &fs
.stat
);
} else if (c
!= OP_STRING
)
expr_operator(op
, &valp
, &fs
);
return (valp
.u
.num
== 0);
if ((op
= lookup_op(opname
, binary_op
)) < 0)
get_int(argv
[0], &v
[0].u
.num
);
get_int(argv
[2], &v
[1].u
.num
);
expr_operator(op
, v
, NULL
);
return (v
[0].u
.num
== 0);
for (; *v
&& isspace(*v
); ++v
);
val
= strtol(v
, &ep
, 10);
errx(2, "%s: trailing non-numeric characters", v
);
errx(2, "%s: underflow", v
);
errx(2, "%s: overflow", v
);
errx(2, "%s: expected integer", v
);
err(2, "expression is too complex");