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