* Copyright (c) 1988 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
static char copyright
[] =
"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)test.c 1.3 (Berkeley) %G%";
#define INITARGS(argv) if (argv[0] == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
#define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
#define equal(s1, s2) (strcmp(s1, s2) == 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 int lookup_op
__P((char *, char *const *));
static long atol
__P((const char *));
static int posix_binary_op
__P((char **));
static int posix_unary_op
__P((char **));
static int int_tcheck
__P((char *));
int nest
; /* parentheses nesting */
struct operator opstack
[STACKSIZE
];
struct value valstack
[STACKSIZE
+ 1];
if (!equal(argv
[argc
- 1], "]"))
/* Test(1) implements an inherently ambiguous grammer. In order to
* assure some degree of consistency, we special case the POSIX
* requirements to assure correct evaluation for POSIX following
* scripts. The following special cases comply with POSIX
* P1003.2/D11.2 Section 4.62.4. */
return (*argv
[1] == '\0') ? 1 : 0;
case 2: /* % test op arg */
return (*argv
[2] == '\0') ? 1 : 0;
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
&&
int_tcheck(valsp
->u
.string
))
} else if (c
>= OP_STRING
) {
/* OP_STRING or OP_FILE */
if (valsp
->type
== INTEGER
) {
sprintf(p
, "%d", valsp
->u
.num
);
} else if (valsp
->type
== BOOLEAN
) {
fs
.name
= valsp
->u
.string
;
if (binary
< FIRST_BINARY_OP
)
expr_operator(opsp
->op
, valsp
, &fs
);
else if (opsp
->op
== AND1
|| opsp
->op
== OR1
)
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))
return expr_is_false(&valstack
[0]);
syntax
: error("syntax error");
overflow
: error("Expression too complex");
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)
if (fs
->stat
.st_uid
== geteuid())
else if (fs
->stat
.st_gid
== getegid())
goto filebit
; /* true if (stat.st_mode & i) != 0 */
if ((fs
->stat
.st_mode
& S_IFMT
) == i
&& fs
->rcode
>= 0) {
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 (equal(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
&& equal(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)
if (c
== OP_INT
&& int_tcheck(argv
[0]) && int_tcheck(argv
[2])) {
v
[0].u
.num
= atol(argv
[0]);
v
[1].u
.num
= atol(argv
[2]);
expr_operator(op
, v
, NULL
);
return (v
[0].u
.num
== 0);
for (p
= v
; *p
!= '\0'; p
++)
if (*p
< '0' || *p
> '9') {
snprintf(outbuf
, sizeof(outbuf
),
"Illegal operand \"%s\" -- expected integer.", v
);