add atof.c to the SRCS lists
[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
19f13f65 18static char sccsid[] = "@(#)test.c 5.2 (Berkeley) %G%";
66d28fed 19#endif /* not lint */
ab29fc7c 20
ab29fc7c
EA
21#include <sys/types.h>
22#include <sys/stat.h>
66d28fed
KB
23#include <errno.h>
24#include <stdlib.h>
2dbc5798 25#include <string.h>
66d28fed
KB
26#include <stdio.h>
27
ab29fc7c 28#include "operators.h"
ab29fc7c 29
66d28fed
KB
30#define STACKSIZE 12
31#define NESTINCR 16
ab29fc7c
EA
32
33/* data types */
66d28fed
KB
34#define STRING 0
35#define INTEGER 1
36#define BOOLEAN 2
ab29fc7c 37
66d28fed 38#define IS_BANG(s) (s[0] == '!' && s[1] == '\0')
ab29fc7c 39
ab29fc7c
EA
40/*
41 * This structure hold a value. The type keyword specifies the type of
42 * the value, and the union u holds the value. The value of a boolean
43 * is stored in u.num (1 = TRUE, 0 = FALSE).
44 */
ab29fc7c 45struct value {
66d28fed 46 int type;
bc156063 47 union {
66d28fed
KB
48 char *string;
49 long num;
50 } u;
ab29fc7c
EA
51};
52
53struct operator {
66d28fed
KB
54 short op; /* Which operator. */
55 short pri; /* Priority of operator. */
ab29fc7c
EA
56};
57
ab29fc7c 58struct filestat {
66d28fed
KB
59 char *name; /* Name of file. */
60 int rcode; /* Return code from stat. */
61 struct stat stat; /* Status info on file. */
ab29fc7c
EA
62};
63
66d28fed
KB
64static void err __P((const char *, ...));
65static int expr_is_false __P((struct value *));
66static void expr_operator __P((int, struct value *, struct filestat *));
67static int int_tcheck __P((char *));
68static int lookup_op __P((char *, char *const *));
69static void overflow __P((void));
70static int posix_binary_op __P((char **));
71static int posix_unary_op __P((char **));
72static void syntax __P((void));
ab29fc7c
EA
73
74int
bc156063 75main(argc, argv)
66d28fed
KB
76 int argc;
77 char *argv[];
ab29fc7c 78{
bc156063
EA
79 struct operator opstack[STACKSIZE];
80 struct operator *opsp;
81 struct value valstack[STACKSIZE + 1];
82 struct value *valsp;
83 struct filestat fs;
66d28fed
KB
84 char c, **ap, *opname, *p;
85 int binary, nest, op, pri, ret_val, skipping;
86
87 if (argv[0] == NULL) {
88 err("test: argc is zero.\n");
89 exit(2);
90 }
bc156063 91
bc156063 92 if (**argv == '[') {
66d28fed
KB
93 if (strcmp(argv[argc - 1], "]"))
94 err("missing ]");
bc156063
EA
95 argv[argc - 1] = NULL;
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 */
bc156063
EA
112 return (*argv[1] == '\0') ? 1 : 0;
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) {
206 if (valsp->type == STRING &&
207 int_tcheck(valsp->u.string))
208 valsp->u.num =
209 atol(valsp->u.string);
210 valsp->type = INTEGER;
211 } else if (c >= OP_STRING) {
212 /* OP_STRING or OP_FILE */
213 if (valsp->type == INTEGER) {
66d28fed
KB
214 if ((p = malloc(32)) == NULL)
215 err("%s",
216 strerror(errno));
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{
bc156063
EA
277 if (val->type == STRING) {
278 if (val->u.string[0] == '\0')
66d28fed 279 return (1);
bc156063
EA
280 } else { /* INTEGER or BOOLEAN */
281 if (val->u.num == 0)
66d28fed 282 return (1);
bc156063 283 }
66d28fed 284 return (0);
ab29fc7c
EA
285}
286
287
288/*
289 * Execute an operator. Op is the operator. Sp is the stack pointer;
290 * sp[0] refers to the first operand, sp[1] refers to the second operand
291 * (if any), and the result is placed in sp[0]. The operands are converted
292 * to the type expected by the operator before expr_operator is called.
293 * Fs is a pointer to a structure which holds the value of the last call
294 * to stat, to avoid repeated stat calls on the same file.
295 */
ab29fc7c
EA
296static void
297expr_operator(op, sp, fs)
66d28fed 298 int op;
ab29fc7c
EA
299 struct value *sp;
300 struct filestat *fs;
301{
66d28fed 302 int i;
ab29fc7c
EA
303
304 switch (op) {
305 case NOT:
bc156063
EA
306 sp->u.num = expr_is_false(sp);
307 sp->type = BOOLEAN;
308 break;
309 case ISEXIST:
310 if (fs == NULL || fs->rcode == -1)
311 goto false;
312 else
313 goto true;
314 case ISREAD:
66d28fed 315 i = S_IROTH;
bc156063
EA
316 goto permission;
317 case ISWRITE:
66d28fed 318 i = S_IWOTH;
bc156063
EA
319 goto permission;
320 case ISEXEC:
66d28fed
KB
321 i = S_IXOTH;
322permission: if (fs->stat.st_uid == geteuid())
bc156063
EA
323 i <<= 6;
324 else if (fs->stat.st_gid == getegid())
325 i <<= 3;
326 goto filebit; /* true if (stat.st_mode & i) != 0 */
327 case ISFILE:
328 i = S_IFREG;
329 goto filetype;
330 case ISDIR:
331 i = S_IFDIR;
332 goto filetype;
333 case ISCHAR:
334 i = S_IFCHR;
335 goto filetype;
336 case ISBLOCK:
337 i = S_IFBLK;
338 goto filetype;
339 case ISFIFO:
bc156063
EA
340 i = S_IFIFO;
341 goto filetype;
66d28fed
KB
342filetype: if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0)
343true: sp->u.num = 1;
344 else
345false: sp->u.num = 0;
bc156063
EA
346 sp->type = BOOLEAN;
347 break;
348 case ISSETUID:
349 i = S_ISUID;
350 goto filebit;
351 case ISSETGID:
352 i = S_ISGID;
353 goto filebit;
354 case ISSTICKY:
355 i = S_ISVTX;
66d28fed 356filebit: if (fs->stat.st_mode & i && fs->rcode >= 0)
bc156063
EA
357 goto true;
358 goto false;
359 case ISSIZE:
360 sp->u.num = fs->rcode >= 0 ? fs->stat.st_size : 0L;
361 sp->type = INTEGER;
362 break;
363 case ISTTY:
364 sp->u.num = isatty(sp->u.num);
365 sp->type = BOOLEAN;
366 break;
367 case NULSTR:
368 if (sp->u.string[0] == '\0')
369 goto true;
370 goto false;
371 case STRLEN:
372 sp->u.num = strlen(sp->u.string);
373 sp->type = INTEGER;
374 break;
375 case OR1:
376 case AND1:
66d28fed
KB
377 /*
378 * These operators are mostly handled by the parser. If we
bc156063 379 * get here it means that both operands were evaluated, so
66d28fed
KB
380 * the value is the value of the second operand.
381 */
bc156063
EA
382 *sp = *(sp + 1);
383 break;
384 case STREQ:
385 case STRNE:
386 i = 0;
66d28fed 387 if (!strcmp(sp->u.string, (sp + 1)->u.string))
bc156063
EA
388 i++;
389 if (op == STRNE)
390 i = 1 - i;
391 sp->u.num = i;
392 sp->type = BOOLEAN;
393 break;
394 case EQ:
395 if (sp->u.num == (sp + 1)->u.num)
396 goto true;
397 goto false;
398 case NE:
399 if (sp->u.num != (sp + 1)->u.num)
400 goto true;
401 goto false;
402 case GT:
403 if (sp->u.num > (sp + 1)->u.num)
404 goto true;
405 goto false;
406 case LT:
407 if (sp->u.num < (sp + 1)->u.num)
408 goto true;
409 goto false;
410 case LE:
411 if (sp->u.num <= (sp + 1)->u.num)
412 goto true;
413 goto false;
414 case GE:
415 if (sp->u.num >= (sp + 1)->u.num)
416 goto true;
417 goto false;
418
419 }
ab29fc7c
EA
420}
421
ab29fc7c
EA
422static int
423lookup_op(name, table)
bc156063
EA
424 char *name;
425 char *const * table;
ab29fc7c 426{
bc156063
EA
427 register char *const * tp;
428 register char const *p;
66d28fed 429 char c;
bc156063 430
66d28fed
KB
431 c = name[1];
432 for (tp = table; (p = *tp) != NULL; tp++)
433 if (p[1] == c && !strcmp(p, name))
434 return (tp - table);
435 return (-1);
ab29fc7c
EA
436}
437
438static int
439posix_unary_op(argv)
66d28fed 440 char **argv;
ab29fc7c 441{
ab29fc7c
EA
442 struct filestat fs;
443 struct value valp;
66d28fed
KB
444 int op, c;
445 char *opname;
ab29fc7c
EA
446
447 opname = *argv;
bc156063 448 if ((op = lookup_op(opname, unary_op)) < 0)
66d28fed 449 return (-1);
bc156063
EA
450 c = op_argflag[op];
451 opname = argv[1];
452 valp.u.string = opname;
453 if (c == OP_FILE) {
ab29fc7c
EA
454 fs.name = opname;
455 fs.rcode = stat(opname, &fs.stat);
456 } else if (c != OP_STRING)
66d28fed 457 return (-1);
ab29fc7c
EA
458
459 expr_operator(op, &valp, &fs);
460 return (valp.u.num == 0);
461}
462
ab29fc7c
EA
463static int
464posix_binary_op(argv)
bc156063 465 char **argv;
ab29fc7c 466{
ab29fc7c 467 struct value v[2];
66d28fed
KB
468 int op, c;
469 char *opname;
bc156063 470
ab29fc7c
EA
471 opname = argv[1];
472 if ((op = lookup_op(opname, binary_op)) < 0)
66d28fed 473 return (-1);
ab29fc7c
EA
474 op += FIRST_BINARY_OP;
475 c = op_argflag[op];
476
477 if (c == OP_INT && int_tcheck(argv[0]) && int_tcheck(argv[2])) {
478 v[0].u.num = atol(argv[0]);
479 v[1].u.num = atol(argv[2]);
2dbc5798
EA
480 } else {
481 v[0].u.string = argv[0];
482 v[1].u.string = argv[2];
ab29fc7c
EA
483 }
484 expr_operator(op, v, NULL);
485 return (v[0].u.num == 0);
486}
487
488/*
489 * Integer type checking.
490 */
491static int
492int_tcheck(v)
66d28fed 493 char *v;
ab29fc7c 494{
66d28fed 495 char *p;
ab29fc7c
EA
496
497 for (p = v; *p != '\0'; p++)
66d28fed
KB
498 if (!isdigit(*p))
499 err("illegal operand \"%s\" -- expected integer.", v);
500 return (1);
501}
502
503static void
504syntax()
505{
506 err("syntax error");
507}
508
509static void
510overflow()
511{
512 err("expression is too complex");
513}
514
515#if __STDC__
516#include <stdarg.h>
517#else
518#include <varargs.h>
519#endif
520
521void
522#if __STDC__
523err(const char *fmt, ...)
524#else
525err(fmt, va_alist)
526 char *fmt;
527 va_dcl
528#endif
529{
530 va_list ap;
531#if __STDC__
532 va_start(ap, fmt);
533#else
534 va_start(ap);
535#endif
536 (void)fprintf(stderr, "test: ");
537 (void)vfprintf(stderr, fmt, ap);
538 va_end(ap);
539 (void)fprintf(stderr, "\n");
540 exit(2);
541 /* NOTREACHED */
ab29fc7c 542}