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