1 /* @(#)test.c 1.41 19/09/16 Copyright 1986,1995-2019 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static UConst char sccsid[] =
5 "@(#)test.c 1.41 19/09/16 Copyright 1986,1995-2019 J. Schilling";
6 #endif
7 /*
8 * Test routine (the test builtin command)
9 *
10 * Copyright (c) 1986,1995-2019 J. Schilling
11 */
12 /*
13 * The contents of this file are subject to the terms of the
14 * Common Development and Distribution License, Version 1.0 only
15 * (the "License"). You may not use this file except in compliance
16 * with the License.
17 *
18 * See the file CDDL.Schily.txt in this distribution for details.
19 * A copy of the CDDL is also available via the Internet at
20 * http://www.opensource.org/licenses/cddl1.txt
21 *
22 * When distributing Covered Code, include this CDDL HEADER in each
23 * file and include the License file CDDL.Schily.txt from this distribution.
24 */
25
26 #include <schily/fcntl.h>
27 #include <schily/stdio.h>
28 #include <schily/unistd.h>
29 #include <schily/setjmp.h>
30 #include <schily/jmpdefs.h>
31 #include <schily/varargs.h>
32 #include <schily/stat.h>
33 #include "bsh.h"
34 #include "str.h"
35 #include "strsubs.h"
36 #include <schily/string.h>
37
38 #ifndef R_OK
39 #define R_OK 4
40 #endif
41 #ifndef W_OK
42 #define W_OK 2
43 #endif
44 #ifndef X_OK
45 #define X_OK 1
46 #endif
47 #ifndef F_OK
48 #define F_OK 0
49 #endif
50
51 #ifndef HAVE_LSTAT
52 # define lstat stat
53 #undef AT_SYMLINK_NOFOLLOW
54 #define AT_SYMLINK_NOFOLLOW 0
55 #endif
56
57 /* Kostet mehr Code den ganzen Kram auf den Stack zu tun */
58
59 LOCAL jmps_t *tjmp = 0; /* jmp_buf steht auf dem Stack */
60 LOCAL int tac = 0;
61 LOCAL char **tav = 0;
62 LOCAL FILE **tstd = 0;
63 LOCAL Argvec *tvp = 0;
64
65 #define exp2 _exp2 /* Some compilers do not like exp2() */
66
67 EXPORT void bcompute __PR((Argvec * vp, FILE ** std, int flag));
68 EXPORT void btest __PR((Argvec * vp, FILE ** std, int flag));
69 EXPORT void bexpr __PR((Argvec * vp, FILE ** std, int flag));
70 EXPORT BOOL test __PR((Argvec * vp, FILE ** std));
71 LOCAL char *getarg __PR((BOOL flg));
72 LOCAL int getiarg __PR((BOOL flg));
73 LOCAL void ungetarg __PR((void));
74 LOCAL int expr __PR((void));
75 LOCAL int exp0 __PR((void));
76 LOCAL int exp1 __PR((void));
77 LOCAL int exp2 __PR((void));
78 LOCAL int expn __PR((void));
79 LOCAL int ass_expr __PR((char *name, char *op, int y));
80 LOCAL BOOL access_ok __PR((char *name, int mode));
81 LOCAL BOOL fattr __PR((char *name, int type));
82 LOCAL BOOL ftype __PR((char *name, int type));
83 EXPORT BOOL is_dir __PR((char *name));
84 LOCAL BOOL fowner __PR((char *name, int owner));
85 LOCAL BOOL fgroup __PR((char *name, int group));
86 LOCAL off_t fsize __PR((char *name));
87 LOCAL BOOL isttyf __PR((int i));
88 LOCAL int lstatat __PR((char *name, struct stat *buf, int flag));
89 LOCAL void expr_syntax __PR((char *fmt, ...));
90
91 /* ARGSUSED */
92 EXPORT void
bcompute(vp,std,flag)93 bcompute(vp, std, flag)
94 Argvec *vp;
95 FILE *std[];
96 int flag;
97 {
98 fprintf(std[2], "compute obsolete use 'test' or '@'.\n");
99 test(vp, std);
100 }
101
102 /* ARGSUSED */
103 EXPORT void
btest(vp,std,flag)104 btest(vp, std, flag)
105 Argvec *vp;
106 FILE *std[];
107 int flag;
108 {
109 test(vp, std);
110 }
111
112 /* ARGSUSED */
113 EXPORT void
bexpr(vp,std,flag)114 bexpr(vp, std, flag)
115 Argvec *vp;
116 FILE *std[];
117 int flag;
118 {
119 char buf[12];
120 jmps_t exprjmp;
121 register int ac;
122 register char **av;
123
124 tjmp = &exprjmp;
125 if (!setjmp(exprjmp.jb)) {
126 ac = vp->av_ac;
127 av = vp->av_av;
128 tvp = vp;
129 tstd = std;
130 if ((tac = ac - 3) <= 0)
131 expr_syntax(expected, argument);
132 tav = &av[3];
133 sprintf(buf, "%d", ass_expr(av[1], av[2], expr()));
134 ev_insert(concat(av[1], eql, buf, (char *)NULL));
135 }
136 }
137
138 EXPORT BOOL
test(vp,std)139 test(vp, std)
140 Argvec *vp;
141 FILE *std[];
142 {
143 jmps_t testjmp;
144 register int ac;
145 register char **av;
146
147 tjmp = &testjmp;
148 if (!setjmp(testjmp.jb)) {
149 ac = vp->av_ac;
150 av = vp->av_av;
151 tvp = vp;
152 tstd = std;
153 if (streql(av[0], "[") && !streql(av[--ac], "]"))
154 expr_syntax("%s ']'", emissing);
155 tac = --ac;
156 tav = &av[1];
157 ex_status = expr() ? 0 : 1;
158 return (TRUE);
159 }
160 return (FALSE); /* fuer bif() */
161 }
162
163 LOCAL char *
getarg(flg)164 getarg(flg)
165 BOOL flg;
166 {
167 if (tac-- <= 0) {
168 if (flg) {
169 tac++;
170 return (NULL);
171 }
172 expr_syntax(expected, argument);
173 }
174 return (*tav++);
175 }
176
177 /* ARGSUSED */
178 LOCAL int
getiarg(flg)179 getiarg(flg)
180 BOOL flg;
181 {
182 int i;
183
184 if (!toint(tstd, getarg(0), &i))
185 expr_syntax(expected, number);
186 return (i);
187 }
188
189
190 LOCAL void
ungetarg()191 ungetarg()
192 {
193 tac++, tav--;
194 }
195
196 LOCAL int
expr()197 expr()
198 {
199 int x;
200 char *op;
201
202 x = exp0();
203 if ((op = getarg(1)) != NULL)
204 expr_syntax(unexpected, op);
205 return (x);
206 }
207
208 LOCAL int
exp0()209 exp0()
210 {
211 int x;
212 char *op;
213
214 x = exp1();
215 if ((op = getarg(1)) != NULL) {
216 if (streql(op, "-o"))
217 return (x | exp0());
218 if (streql(op, "-or"))
219 return (x || exp0());
220 ungetarg();
221 }
222 return (x);
223 }
224
225 LOCAL int
exp1()226 exp1()
227 {
228 int x;
229 char *op;
230
231 x = exp2();
232 if ((op = getarg(1)) != NULL) {
233 if (streql(op, "-a"))
234 return (x & exp1());
235 if (streql(op, "-and"))
236 return (x && exp1());
237 ungetarg();
238 }
239 return (x);
240 }
241
242 LOCAL int
exp2()243 exp2()
244 {
245
246 if (streql(getarg(0), "!"))
247 return (!expn());
248 ungetarg();
249 return (expn());
250 }
251
252 LOCAL int
expn()253 expn()
254 {
255 register char *a;
256 register char *op;
257 int x;
258 int y;
259
260 a = getarg(0);
261 if (streql(a, lpar)) {
262 x = exp0();
263 if (!(a = getarg(1)) || !streql(a, rpar))
264 expr_syntax("%s %s", emissing, rpar);
265 return (x);
266 }
267 if (streql(a, "-r"))
268 return (access_ok(getarg(0), R_OK));
269 if (streql(a, "-w"))
270 return (access_ok(getarg(0), W_OK));
271 if (streql(a, "-x"))
272 return (access_ok(getarg(0), X_OK));
273 if (streql(a, "-e")) /* ksh uses -a */
274 return (access_ok(getarg(0), F_OK));
275 if (streql(a, "-O")) /* non-POSIX */
276 return (fowner(getarg(0), geteuid()));
277 if (streql(a, "-G")) /* non-POSIX */
278 return (fgroup(getarg(0), getegid()));
279 if (streql(a, "-s"))
280 return (fsize(getarg(0)) > (off_t)0);
281 if (streql(a, "-S"))
282 #ifdef S_IFSOCK
283 return (ftype(getarg(0), S_IFSOCK));
284 #else
285 return (FALSE);
286 #endif
287 if (streql(a, "-d"))
288 return (ftype(getarg(0), S_IFDIR));
289 if (streql(a, "-D"))
290 #ifdef S_IFDOOR
291 return (ftype(getarg(0), S_IFDOOR));
292 #else
293 return (FALSE);
294 #endif
295 if (streql(a, "-c"))
296 #ifdef S_IFCHR
297 return (ftype(getarg(0), S_IFCHR));
298 #else
299 return (FALSE);
300 #endif
301 if (streql(a, "-b"))
302 #ifdef S_IFBLK
303 return (ftype(getarg(0), S_IFBLK));
304 #else
305 return (FALSE);
306 #endif
307 if (streql(a, "-f"))
308 return (ftype(getarg(0), S_IFREG));
309 if (streql(a, "-h") || streql(a, "-L")) /* -L is for ksh compat */
310 #ifdef S_IFLNK
311 return (ftype(getarg(0), S_IFLNK));
312 #else
313 return (FALSE);
314 #endif
315 if (streql(a, "-C")) /* non-POSIX aber genannt */
316 #ifdef S_IFCTG
317 return (ftype(getarg(0), S_IFCTG));
318 #else
319 return (FALSE);
320 #endif
321 if (streql(a, "-p"))
322 #ifdef S_IFIFO
323 return (ftype(getarg(0), S_IFIFO));
324 #else
325 return (FALSE);
326 #endif
327 if (streql(a, "-P"))
328 #if defined(S_IFPORT) && S_IFPORT != S_IFIFO /* Do not use it on Ultrix */
329 return (ftype(getarg(0), S_IFPORT));
330 #else
331 return (FALSE);
332 #endif
333 if (streql(a, "-u"))
334 return (fattr(getarg(0), S_ISUID));
335 if (streql(a, "-g"))
336 return (fattr(getarg(0), S_ISGID));
337 if (streql(a, "-k")) /* non-POSIX aber genannt */
338 return (fattr(getarg(0), S_ISVTX));
339 if (streql(a, "-t"))
340 return (isatty(getiarg(0)));
341 if (streql(a, "-T")) /* non-POSIX */
342 return (isttyf(getiarg(0)));
343 if (streql(a, "-l")) /* non-POSIX aber genannt */
344 return (strlen(getarg(0)));
345 if (streql(a, "-n"))
346 return (!streql(getarg(0), nullstr));
347 if (streql(a, "-z"))
348 return (streql(getarg(0), nullstr));
349 op = getarg(1);
350 if (op == NULL) {
351 if (*astoi(a, &x) == '\0')
352 return (x);
353 return (!streql(a, nullstr));
354 }
355 if (streql(op, "-a") || streql(op, "-o") ||
356 streql(op, "-and") || streql(op, "-or")) {
357 ungetarg();
358 return (!streql(a, nullstr)); /* TRUE if not "" */
359 }
360 if (streql(op, eql) || streql(op, "=="))
361 return (streql(getarg(0), a));
362 if (streql(op, "!="))
363 return (!streql(getarg(0), a));
364 if (!toint(tstd, a, &x) || !toint(tstd, getarg(0), &y))
365 expr_syntax(expected, number);
366 if (streql(op, "+"))
367 return (x + y);
368 if (streql(op, "-"))
369 return (x - y);
370 if (streql(op, "*"))
371 return (x * y);
372 if (streql(op, slash)) {
373 if (y == 0)
374 expr_syntax(divzero); /* never returns */
375 return (x / y);
376 }
377 if (streql(op, "%")) {
378 if (y == 0)
379 expr_syntax(divzero); /* never returns */
380 return (x % y);
381 }
382 if (streql(op, "&"))
383 return (x & y);
384 if (streql(op, "|"))
385 return (x | y);
386 if (streql(op, "&&"))
387 return (x && y);
388 if (streql(op, "||"))
389 return (x || y);
390 if (streql(op, "-eq"))
391 return (x == y);
392 if (streql(op, "-ne"))
393 return (x != y);
394 if (streql(op, ">") || streql(op, "-gt"))
395 return (x > y);
396 if (streql(op, "<") || streql(op, "-lt"))
397 return (x < y);
398 if (streql(op, ">=") || streql(op, "-ge"))
399 return (x >= y);
400 if (streql(op, "<=") || streql(op, "-le"))
401 return (x <= y);
402 if (streql(op, "<<"))
403 return (x << y);
404 if (streql(op, ">>"))
405 return (x >> y);
406 expr_syntax(ebadop, op); /* never returns */
407 return (0); /* Keep lint happy */
408 }
409
410 LOCAL int
ass_expr(name,op,y)411 ass_expr(name, op, y)
412 char *name;
413 register char *op;
414 int y;
415 {
416 char *val;
417 int x;
418
419 if (streql(op, eql))
420 return (y);
421 if (strlen(op) != 2 || op[1] != '=')
422 expr_syntax(ebadop, op);
423 if (!(val = getcurenv(name)))
424 expr_syntax("Undefined Variable '%s'", name);
425 else if (!toint(tstd, val, &x))
426 expr_syntax(expected, number); /* never returns */
427
428 switch (op[0]) {
429
430 case '+' : return (x + y);
431 case '-' : return (x - y);
432 case '/' : if (y == 0)
433 expr_syntax(divzero); /* never returns */
434 return (x / y);
435 case '%' : if (y == 0)
436 expr_syntax(divzero); /* never returns */
437 return (x % y);
438 case '*' : return (x * y);
439
440 default : expr_syntax(ebadop, op); /* never returns */
441
442 }
443 return (0); /* Keep lint happy */
444 }
445
446 LOCAL BOOL
access_ok(name,mode)447 access_ok(name, mode)
448 char *name;
449 int mode;
450 {
451 return (access(name, mode) == 0 ? TRUE : FALSE);
452 }
453
454 LOCAL BOOL
fattr(name,type)455 fattr(name, type)
456 char *name;
457 int type;
458 {
459 struct stat buf;
460
461 if (lstatat(name, &buf, AT_SYMLINK_NOFOLLOW) < 0)
462 return (FALSE);
463 return ((buf.st_mode & type) == type);
464 }
465
466 LOCAL BOOL
ftype(name,type)467 ftype(name, type)
468 char *name;
469 int type;
470 {
471 struct stat buf;
472
473 if (lstatat(name, &buf, AT_SYMLINK_NOFOLLOW) < 0)
474 return (FALSE);
475 return ((buf.st_mode & S_IFMT) == type);
476 }
477
478 EXPORT BOOL
is_dir(name)479 is_dir(name)
480 char *name;
481 {
482 /* return (ftype(name, S_IFDIR));*/
483
484 struct stat buf;
485
486 if (lstatat(name, &buf, 0) < 0)
487 return (FALSE);
488 return ((buf.st_mode & S_IFMT) == S_IFDIR);
489 }
490
491 LOCAL BOOL
fowner(name,owner)492 fowner(name, owner)
493 char *name;
494 int owner;
495 {
496 struct stat buf;
497
498 if (lstatat(name, &buf, AT_SYMLINK_NOFOLLOW) < 0)
499 return (FALSE);
500 return (buf.st_uid == owner);
501 }
502
503 LOCAL BOOL
fgroup(name,group)504 fgroup(name, group)
505 char *name;
506 int group;
507 {
508 struct stat buf;
509
510 if (lstatat(name, &buf, AT_SYMLINK_NOFOLLOW) < 0)
511 return (FALSE);
512 return (buf.st_gid == group);
513 }
514
515 LOCAL off_t
fsize(name)516 fsize(name)
517 char *name;
518 {
519 struct stat buf;
520
521 if (lstatat(name, &buf, AT_SYMLINK_NOFOLLOW) < 0)
522 return (-1);
523 return (buf.st_size);
524 }
525
526 LOCAL BOOL
isttyf(i)527 isttyf(i)
528 int i;
529 {
530 return ((i < 0 || i > 2) ? FALSE : isatty(fdown(tstd[i])));
531 }
532
533 LOCAL int
lstatat(name,buf,flag)534 lstatat(name, buf, flag)
535 char *name;
536 struct stat *buf;
537 int flag;
538 {
539 #ifdef HAVE_FCHDIR
540 char *p;
541 int fd;
542 int err;
543 #endif
544 int ret;
545
546 if ((ret = fstatat(AT_FDCWD, name, buf, flag)) < 0 &&
547 geterrno() != ENAMETOOLONG) {
548 return (ret);
549 }
550
551 #ifdef HAVE_FCHDIR
552 if (ret >= 0)
553 return (ret);
554
555 fd = bsh_hop_dirs(name, &p);
556 ret = fstatat(fd, p, buf, flag);
557 err = geterrno();
558 close(fd);
559 seterrno(err);
560 #endif
561 return (ret);
562 }
563
564 /* VARARGS1 */
565 #ifdef PROTOTYPES
566 LOCAL void
expr_syntax(char * fmt,...)567 expr_syntax(char *fmt, ...)
568 #else
569 LOCAL void
570 expr_syntax(fmt, va_alist)
571 char *fmt;
572 va_dcl
573 #endif
574 {
575 va_list args;
576
577 #ifdef PROTOTYPES
578 va_start(args, fmt);
579 #else
580 va_start(args);
581 #endif
582 fprintf(tstd[2], "%s: %r\n", tvp->av_av[0], fmt, args);
583 va_end(args);
584 busage(tvp, tstd);
585 ex_status = -1;
586 longjmp(tjmp->jb, TRUE);
587 }
588