This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / bin / test / test.c
CommitLineData
78ed81a3 1/*-
2 * Copyright (c) 1992 The Regents of the University of California.
3 * All rights reserved.
15637ed4 4 *
78ed81a3 5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
15637ed4 7 *
78ed81a3 8 * Redistribution and use in source and binary forms, with or without
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.
15637ed4 23 *
78ed81a3 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
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.
15637ed4
RG
35 */
36
78ed81a3 37#ifndef lint
38char copyright[] =
39"@(#) Copyright (c) 1992 The Regents of the University of California.\n\
40 All rights reserved.\n";
41#endif /* not lint */
15637ed4 42
78ed81a3 43#ifndef lint
44static char sccsid[] = "@(#)test.c 5.4 (Berkeley) 2/12/93";
45#endif /* not lint */
15637ed4
RG
46
47#include <sys/types.h>
48#include <sys/stat.h>
78ed81a3 49#include <errno.h>
50#include <stdlib.h>
51#include <string.h>
52#include <stdio.h>
15637ed4 53
78ed81a3 54#include "operators.h"
15637ed4 55
78ed81a3 56#define STACKSIZE 12
57#define NESTINCR 16
15637ed4 58
78ed81a3 59/* data types */
60#define STRING 0
61#define INTEGER 1
62#define BOOLEAN 2
15637ed4 63
78ed81a3 64#define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
15637ed4
RG
65
66/*
78ed81a3 67 * This structure hold a value. The type keyword specifies the type of
68 * the value, and the union u holds the value. The value of a boolean
69 * is stored in u.num (1 = TRUE, 0 = FALSE).
15637ed4 70 */
78ed81a3 71struct value {
72 int type;
73 union {
74 char *string;
75 long num;
76 } u;
77};
78
79struct operator {
80 short op; /* Which operator. */
81 short pri; /* Priority of operator. */
82};
83
84struct filestat {
85 char *name; /* Name of file. */
86 int rcode; /* Return code from stat. */
87 struct stat stat; /* Status info on file. */
88};
89
90static void err __P((const char *, ...));
91static int expr_is_false __P((struct value *));
92static void expr_operator __P((int, struct value *, struct filestat *));
93static long chk_atol __P((char *));
94static int lookup_op __P((char *, char *const *));
95static void overflow __P((void));
96static int posix_binary_op __P((char **));
97static int posix_unary_op __P((char **));
98static void syntax __P((void));
15637ed4 99
78ed81a3 100int
101main(argc, argv)
102 int argc;
103 char *argv[];
15637ed4 104{
78ed81a3 105 struct operator opstack[STACKSIZE];
106 struct operator *opsp;
107 struct value valstack[STACKSIZE + 1];
108 struct value *valsp;
109 struct filestat fs;
110 char c, **ap, *opname, *p;
111 int binary, nest, op, pri, ret_val, skipping;
112
113 if ((p = argv[0]) == NULL) {
114 err("test: argc is zero.\n");
115 exit(2);
15637ed4 116 }
15637ed4 117
78ed81a3 118 if (*p != '\0' && p[strlen(p) - 1] == '[') {
119 if (strcmp(argv[--argc], "]"))
120 err("missing ]");
121 argv[argc] = NULL;
15637ed4 122 }
78ed81a3 123 ap = argv + 1;
124 fs.name = NULL;
125
126 /*
127 * Test(1) implements an inherently ambiguous grammer. In order to
128 * assure some degree of consistency, we special case the POSIX 1003.2
129 * requirements to assure correct evaluation for POSIX scripts. The
130 * following special cases comply with POSIX P1003.2/D11.2 Section
131 * 4.62.4.
132 */
133 switch(argc - 1) {
134 case 0: /* % test */
135 return (1);
136 break;
137 case 1: /* % test arg */
138 /* MIPS machine returns NULL of '[ ]' is called. */
139 return (argv[1] == 0 || *argv[1] == '\0') ? 1 : 0;
140 break;
141 case 2: /* % test op arg */
142 opname = argv[1];
143 if (IS_BANG(opname))
144 return (*argv[2] == '\0') ? 0 : 1;
145 else {
146 ret_val = posix_unary_op(&argv[1]);
147 if (ret_val >= 0)
148 return (ret_val);
15637ed4 149 }
78ed81a3 150 break;
151 case 3: /* % test arg1 op arg2 */
152 if (IS_BANG(argv[1])) {
153 ret_val = posix_unary_op(&argv[1]);
154 if (ret_val >= 0)
155 return (!ret_val);
156 } else if (lookup_op(argv[2], andor_op) < 0) {
157 ret_val = posix_binary_op(&argv[1]);
158 if (ret_val >= 0)
159 return (ret_val);
15637ed4 160 }
78ed81a3 161 break;
162 case 4: /* % test ! arg1 op arg2 */
163 if (IS_BANG(argv[1]) && lookup_op(argv[3], andor_op) < 0) {
164 ret_val = posix_binary_op(&argv[2]);
165 if (ret_val >= 0)
166 return (!ret_val);
15637ed4 167 }
78ed81a3 168 break;
169 default:
170 break;
171 }
15637ed4 172
78ed81a3 173 /*
174 * We use operator precedence parsing, evaluating the expression as
175 * we parse it. Parentheses are handled by bumping up the priority
176 * of operators using the variable "nest." We use the variable
177 * "skipping" to turn off evaluation temporarily for the short
178 * circuit boolean operators. (It is important do the short circuit
179 * evaluation because under NFS a stat operation can take infinitely
180 * long.)
181 */
182 opsp = opstack + STACKSIZE;
183 valsp = valstack;
184 nest = skipping = 0;
185 if (*ap == NULL) {
186 valstack[0].type = BOOLEAN;
187 valstack[0].u.num = 0;
188 goto done;
189 }
190 for (;;) {
191 opname = *ap++;
192 if (opname == NULL)
193 syntax();
194 if (opname[0] == '(' && opname[1] == '\0') {
195 nest += NESTINCR;
196 continue;
197 } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) {
198 if (opsp == &opstack[0])
199 overflow();
200 --opsp;
201 opsp->op = op;
202 opsp->pri = op_priority[op] + nest;
203 continue;
204 } else {
205 valsp->type = STRING;
206 valsp->u.string = opname;
207 valsp++;
15637ed4 208 }
78ed81a3 209 for (;;) {
210 opname = *ap++;
211 if (opname == NULL) {
212 if (nest != 0)
213 syntax();
214 pri = 0;
215 break;
216 }
217 if (opname[0] != ')' || opname[1] != '\0') {
218 if ((op = lookup_op(opname, binary_op)) < 0)
219 syntax();
220 op += FIRST_BINARY_OP;
221 pri = op_priority[op] + nest;
222 break;
223 }
224 if ((nest -= NESTINCR) < 0)
225 syntax();
15637ed4 226 }
78ed81a3 227 while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) {
228 binary = opsp->op;
229 for (;;) {
230 valsp--;
231 c = op_argflag[opsp->op];
232 if (c == OP_INT) {
233 if (valsp->type == STRING)
234 valsp->u.num =
235 chk_atol(valsp->u.string);
236 valsp->type = INTEGER;
237 } else if (c >= OP_STRING) {
238 /* OP_STRING or OP_FILE */
239 if (valsp->type == INTEGER) {
240 if ((p = malloc(32)) == NULL)
241 err("%s",
242 strerror(errno));
243#ifdef SHELL
244 fmtstr(p, 32, "%d",
245 valsp->u.num);
246#else
247 (void)sprintf(p,
248 "%d", valsp->u.num);
249#endif
250 valsp->u.string = p;
251 } else if (valsp->type == BOOLEAN) {
252 if (valsp->u.num)
253 valsp->u.string =
254 "true";
255 else
256 valsp->u.string = "";
257 }
258 valsp->type = STRING;
259 if (c == OP_FILE && (fs.name == NULL ||
260 strcmp(fs.name, valsp->u.string))) {
261 fs.name = valsp->u.string;
262 fs.rcode =
263 stat(valsp->u.string,
264 &fs.stat);
265 }
266 }
267 if (binary < FIRST_BINARY_OP)
268 break;
269 binary = 0;
270 }
271 if (!skipping)
272 expr_operator(opsp->op, valsp, &fs);
273 else if (opsp->op == AND1 || opsp->op == OR1)
274 skipping--;
275 valsp++; /* push value */
276 opsp++; /* pop operator */
15637ed4 277 }
78ed81a3 278 if (opname == NULL)
279 break;
280 if (opsp == &opstack[0])
281 overflow();
282 if (op == AND1 || op == AND2) {
283 op = AND1;
284 if (skipping || expr_is_false(valsp - 1))
285 skipping++;
15637ed4 286 }
78ed81a3 287 if (op == OR1 || op == OR2) {
288 op = OR1;
289 if (skipping || !expr_is_false(valsp - 1))
290 skipping++;
15637ed4 291 }
78ed81a3 292 opsp--;
293 opsp->op = op;
294 opsp->pri = pri;
15637ed4 295 }
78ed81a3 296done: return (expr_is_false(&valstack[0]));
297}
15637ed4 298
78ed81a3 299static int
300expr_is_false(val)
301 struct value *val;
302{
303 if (val->type == STRING) {
304 if (val->u.string[0] == '\0')
305 return (1);
306 } else { /* INTEGER or BOOLEAN */
307 if (val->u.num == 0)
308 return (1);
15637ed4 309 }
78ed81a3 310 return (0);
311}
15637ed4 312
15637ed4 313
78ed81a3 314/*
315 * Execute an operator. Op is the operator. Sp is the stack pointer;
316 * sp[0] refers to the first operand, sp[1] refers to the second operand
317 * (if any), and the result is placed in sp[0]. The operands are converted
318 * to the type expected by the operator before expr_operator is called.
319 * Fs is a pointer to a structure which holds the value of the last call
320 * to stat, to avoid repeated stat calls on the same file.
321 */
322static void
323expr_operator(op, sp, fs)
324 int op;
325 struct value *sp;
326 struct filestat *fs;
327{
328 int i;
329
330 switch (op) {
331 case NOT:
332 sp->u.num = expr_is_false(sp);
333 sp->type = BOOLEAN;
334 break;
335 case ISEXIST:
336 if (fs == NULL || fs->rcode == -1)
337 goto false;
338 else
339 goto true;
340 case ISREAD:
341 i = S_IROTH;
342 goto permission;
343 case ISWRITE:
344 i = S_IWOTH;
345 goto permission;
346 case ISEXEC:
347 i = S_IXOTH;
348permission: if (fs->stat.st_uid == geteuid())
349 i <<= 6;
350 else if (fs->stat.st_gid == getegid())
351 i <<= 3;
352 goto filebit; /* true if (stat.st_mode & i) != 0 */
353 case ISFILE:
354 i = S_IFREG;
355 goto filetype;
356 case ISDIR:
357 i = S_IFDIR;
358 goto filetype;
359 case ISCHAR:
360 i = S_IFCHR;
361 goto filetype;
362 case ISBLOCK:
363 i = S_IFBLK;
364 goto filetype;
365 case ISFIFO:
366 i = S_IFIFO;
367 goto filetype;
368filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
369true: sp->u.num = 1;
370 else
371false: sp->u.num = 0;
372 sp->type = BOOLEAN;
373 break;
374 case ISSETUID:
375 i = S_ISUID;
376 goto filebit;
377 case ISSETGID:
378 i = S_ISGID;
379 goto filebit;
380 case ISSTICKY:
381 i = S_ISVTX;
382filebit: if (fs->stat.st_mode & i && fs->rcode >= 0)
383 goto true;
384 goto false;
385 case ISSIZE:
386 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
387 sp->type = INTEGER;
388 break;
389 case ISTTY:
390 sp->u.num = isatty(sp->u.num);
391 sp->type = BOOLEAN;
392 break;
393 case NULSTR:
394 if (sp->u.string[0] == '\0')
395 goto true;
396 goto false;
397 case STRLEN:
398 sp->u.num = strlen(sp->u.string);
399 sp->type = INTEGER;
400 break;
401 case OR1:
402 case AND1:
403 /*
404 * These operators are mostly handled by the parser. If we
405 * get here it means that both operands were evaluated, so
406 * the value is the value of the second operand.
407 */
408 *sp = *(sp + 1);
409 break;
410 case STREQ:
411 case STRNE:
412 i = 0;
413 if (!strcmp(sp->u.string, (sp + 1)->u.string))
414 i++;
415 if (op == STRNE)
416 i = 1 - i;
417 sp->u.num = i;
418 sp->type = BOOLEAN;
419 break;
420 case EQ:
421 if (sp->u.num == (sp + 1)->u.num)
422 goto true;
423 goto false;
424 case NE:
425 if (sp->u.num != (sp + 1)->u.num)
426 goto true;
427 goto false;
428 case GT:
429 if (sp->u.num > (sp + 1)->u.num)
430 goto true;
431 goto false;
432 case LT:
433 if (sp->u.num < (sp + 1)->u.num)
434 goto true;
435 goto false;
436 case LE:
437 if (sp->u.num <= (sp + 1)->u.num)
438 goto true;
439 goto false;
440 case GE:
441 if (sp->u.num >= (sp + 1)->u.num)
442 goto true;
443 goto false;
15637ed4 444
15637ed4 445 }
15637ed4
RG
446}
447
15637ed4 448static int
78ed81a3 449lookup_op(name, table)
450 char *name;
451 char *const * table;
15637ed4 452{
78ed81a3 453 register char *const * tp;
454 register char const *p;
455 char c;
456
457 c = name[1];
458 for (tp = table; (p = *tp) != NULL; tp++)
459 if (p[1] == c && !strcmp(p, name))
460 return (tp - table);
461 return (-1);
15637ed4
RG
462}
463
15637ed4 464static int
78ed81a3 465posix_unary_op(argv)
466 char **argv;
15637ed4 467{
78ed81a3 468 struct filestat fs;
469 struct value valp;
470 int op, c;
471 char *opname;
472
473 opname = *argv;
474 if ((op = lookup_op(opname, unary_op)) < 0)
475 return (-1);
476 c = op_argflag[op];
477 opname = argv[1];
478 valp.u.string = opname;
479 if (c == OP_FILE) {
480 fs.name = opname;
481 fs.rcode = stat(opname, &fs.stat);
482 } else if (c != OP_STRING)
483 return (-1);
484
485 expr_operator(op, &valp, &fs);
486 return (valp.u.num == 0);
15637ed4
RG
487}
488
78ed81a3 489static int
490posix_binary_op(argv)
491 char **argv;
15637ed4 492{
78ed81a3 493 struct value v[2];
494 int op, c;
495 char *opname;
496
497 opname = argv[1];
498 if ((op = lookup_op(opname, binary_op)) < 0)
499 return (-1);
500 op += FIRST_BINARY_OP;
501 c = op_argflag[op];
502
503 if (c == OP_INT) {
504 v[0].u.num = chk_atol(argv[0]);
505 v[1].u.num = chk_atol(argv[2]);
506 } else {
507 v[0].u.string = argv[0];
508 v[1].u.string = argv[2];
509 }
510 expr_operator(op, v, NULL);
511 return (v[0].u.num == 0);
15637ed4
RG
512}
513
514/*
78ed81a3 515 * Integer type checking.
15637ed4 516 */
78ed81a3 517static long
518chk_atol(v)
519 char *v;
15637ed4 520{
78ed81a3 521 char *p;
522 long r;
523
524 errno = 0;
525 r = strtol(v, &p, 10);
526 if (errno != 0)
527 err("\"%s\" -- out of range.", v);
528 while (isspace(*p))
529 p++;
530 if (*p != '\0')
531 err("illegal operand \"%s\" -- expected integer.", v);
532 return (r);
533}
15637ed4 534
78ed81a3 535static void
536syntax()
537{
538 err("syntax error");
539}
15637ed4 540
78ed81a3 541static void
542overflow()
543{
544 err("expression is too complex");
545}
15637ed4 546
78ed81a3 547#if __STDC__
548#include <stdarg.h>
549#else
550#include <varargs.h>
551#endif
15637ed4 552
78ed81a3 553void
554#if __STDC__
555err(const char *fmt, ...)
556#else
557err(fmt, va_alist)
558 char *fmt;
559 va_dcl
560#endif
561{
562 va_list ap;
563#if __STDC__
564 va_start(ap, fmt);
565#else
566 va_start(ap);
567#endif
568 (void)fprintf(stderr, "test: ");
569 (void)vfprintf(stderr, fmt, ap);
570 va_end(ap);
571 (void)fprintf(stderr, "\n");
572 exit(2);
573 /* NOTREACHED */
15637ed4 574}