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