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