VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/test.c@ 3387

Last change on this file since 3387 was 3247, checked in by bird, 5 years ago

kmkbuiltin: warnings and build fixes

File size: 23.0 KB
Line 
1/* $NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $ */
2
3/*
4 * test(1); version 7-like -- author Erik Baalbergen
5 * modified by Eric Gisin to be used as built-in.
6 * modified by Arnold Robbins to add SVR3 compatibility
7 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 * modified by J.T. Conklin for NetBSD.
9 *
10 * This program is in the Public Domain.
11 */
12
13/*#include <sys/cdefs.h>
14#ifndef lint
15__RCSID("$NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $");
16#endif*/
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include "config.h"
23#include <sys/stat.h>
24#include <sys/types.h>
25
26#include <ctype.h>
27#include "err.h"
28#include <errno.h>
29#include <limits.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#ifdef _MSC_VER
34# include <direct.h>
35# include <io.h>
36# include <process.h>
37# include "mscfakes.h"
38# include "quote_argv.h"
39#else
40# include <unistd.h>
41#endif
42#include <stdarg.h>
43#include <sys/stat.h>
44
45#include "kmkbuiltin.h"
46
47
48/*********************************************************************************************************************************
49* Defined Constants And Macros *
50*********************************************************************************************************************************/
51#ifndef __arraycount
52# define __arraycount(a) ( sizeof(a) / sizeof(a[0]) )
53#endif
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59/* test(1) accepts the following grammar:
60 oexpr ::= aexpr | aexpr "-o" oexpr ;
61 aexpr ::= nexpr | nexpr "-a" aexpr ;
62 nexpr ::= primary | "!" primary
63 primary ::= unary-operator operand
64 | operand binary-operator operand
65 | operand
66 | "(" oexpr ")"
67 ;
68 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
69 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
70
71 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
72 "-nt"|"-ot"|"-ef";
73 operand ::= <any legal UNIX file name>
74*/
75
76enum token {
77 EOI,
78 FILRD,
79 FILWR,
80 FILEX,
81 FILEXIST,
82 FILREG,
83 FILDIR,
84 FILCDEV,
85 FILBDEV,
86 FILFIFO,
87 FILSOCK,
88 FILSYM,
89 FILGZ,
90 FILTT,
91 FILSUID,
92 FILSGID,
93 FILSTCK,
94 FILNT,
95 FILOT,
96 FILEQ,
97 FILUID,
98 FILGID,
99 STREZ,
100 STRNZ,
101 STREQ,
102 STRNE,
103 STRLT,
104 STRGT,
105 INTEQ,
106 INTNE,
107 INTGE,
108 INTGT,
109 INTLE,
110 INTLT,
111 UNOT,
112 BAND,
113 BOR,
114 LPAREN,
115 RPAREN,
116 OPERAND
117};
118
119enum token_types {
120 UNOP,
121 BINOP,
122 BUNOP,
123 BBINOP,
124 PAREN
125};
126
127struct t_op {
128 const char *op_text;
129 short op_num, op_type;
130};
131
132/** kmk_test instance data. */
133typedef struct TESTINSTANCE
134{
135 PKMKBUILTINCTX pCtx;
136 char **t_wp;
137 struct t_op const *t_wp_op;
138} TESTINSTANCE;
139typedef TESTINSTANCE *PTESTINSTANCE;
140
141
142/*********************************************************************************************************************************
143* Global Variables *
144*********************************************************************************************************************************/
145static const struct t_op cop[] = {
146 {"!", UNOT, BUNOP},
147 {"(", LPAREN, PAREN},
148 {")", RPAREN, PAREN},
149 {"<", STRLT, BINOP},
150 {"=", STREQ, BINOP},
151 {">", STRGT, BINOP},
152};
153
154static const struct t_op cop2[] = {
155 {"!=", STRNE, BINOP},
156};
157
158static const struct t_op mop3[] = {
159 {"ef", FILEQ, BINOP},
160 {"eq", INTEQ, BINOP},
161 {"ge", INTGE, BINOP},
162 {"gt", INTGT, BINOP},
163 {"le", INTLE, BINOP},
164 {"lt", INTLT, BINOP},
165 {"ne", INTNE, BINOP},
166 {"nt", FILNT, BINOP},
167 {"ot", FILOT, BINOP},
168};
169
170static const struct t_op mop2[] = {
171 {"G", FILGID, UNOP},
172 {"L", FILSYM, UNOP},
173 {"O", FILUID, UNOP},
174 {"S", FILSOCK,UNOP},
175 {"a", BAND, BBINOP},
176 {"b", FILBDEV,UNOP},
177 {"c", FILCDEV,UNOP},
178 {"d", FILDIR, UNOP},
179 {"e", FILEXIST,UNOP},
180 {"f", FILREG, UNOP},
181 {"g", FILSGID,UNOP},
182 {"h", FILSYM, UNOP}, /* for backwards compat */
183 {"k", FILSTCK,UNOP},
184 {"n", STRNZ, UNOP},
185 {"o", BOR, BBINOP},
186 {"p", FILFIFO,UNOP},
187 {"r", FILRD, UNOP},
188 {"s", FILGZ, UNOP},
189 {"t", FILTT, UNOP},
190 {"u", FILSUID,UNOP},
191 {"w", FILWR, UNOP},
192 {"x", FILEX, UNOP},
193 {"z", STREZ, UNOP},
194};
195
196
197/*********************************************************************************************************************************
198* Internal Functions *
199*********************************************************************************************************************************/
200static int syntax(PTESTINSTANCE, const char *, const char *);
201static int oexpr(PTESTINSTANCE, enum token);
202static int aexpr(PTESTINSTANCE, enum token);
203static int nexpr(PTESTINSTANCE, enum token);
204static int primary(PTESTINSTANCE, enum token);
205static int binop(PTESTINSTANCE);
206static int test_access(struct stat *, mode_t);
207static int filstat(char *, enum token);
208static enum token t_lex(PTESTINSTANCE, char *);
209static int isoperand(PTESTINSTANCE);
210static int getn(PTESTINSTANCE, const char *);
211static int newerf(const char *, const char *);
212static int olderf(const char *, const char *);
213static int equalf(const char *, const char *);
214static int usage(PKMKBUILTINCTX, int);
215
216#if !defined(KMK_BUILTIN_STANDALONE) || defined(ELECTRIC_HEAP)
217extern void *xmalloc(unsigned int);
218#else
219extern void *xmalloc(unsigned int sz)
220{
221 void *p = malloc(sz);
222 if (!p) {
223 fprintf(stderr, "kmk_test: malloc(%u) failed\n", sz);
224 exit(1);
225 }
226 return p;
227}
228#endif
229
230
231
232int kmk_builtin_test(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx, char ***ppapszArgvSpawn)
233{
234 TESTINSTANCE This;
235 int res;
236 char **argv_spawn;
237 int i;
238
239 This.pCtx = pCtx;
240 This.t_wp = NULL;
241 This.t_wp_op = NULL;
242
243 /* look for the '--', '--help' and '--version'. */
244 argv_spawn = NULL;
245 for (i = 1; i < argc; i++) {
246 if ( argv[i][0] == '-'
247 && argv[i][1] == '-') {
248 if (argv[i][2] == '\0') {
249 argc = i;
250 argv[i] = NULL;
251
252 /* skip blank arguments (happens inside kmk) */
253 while (argv[++i]) {
254 const char *psz = argv[i];
255 while (isspace(*psz))
256 psz++;
257 if (*psz)
258 break;
259 }
260 argv_spawn = &argv[i];
261 break;
262 }
263 if (!strcmp(argv[i], "--help"))
264 return usage(pCtx, 0);
265 if (!strcmp(argv[i], "--version"))
266 return kbuild_version(argv[0]);
267 }
268 }
269
270 /* are we '['? then check for ']'. */
271 if (strcmp(argv[0], "[") == 0) { /** @todo should skip the path in g_progname */
272 if (strcmp(argv[--argc], "]"))
273 return errx(pCtx, 1, "missing ]");
274 argv[argc] = NULL;
275 }
276
277 /* evaluate the expression */
278 if (argc < 2)
279 res = 1;
280 else {
281 This.t_wp = &argv[1];
282 res = oexpr(&This, t_lex(&This, *This.t_wp));
283 if (res != -42 && *This.t_wp != NULL && *++This.t_wp != NULL)
284 res = syntax(&This, *This.t_wp, "unexpected operator");
285 if (res == -42)
286 return 1; /* don't mix syntax errors with the argv_spawn ignore */
287 res = !res;
288 }
289
290 /* anything to execute on success? */
291 if (argv_spawn) {
292 if (res != 0 || !argv_spawn[0])
293 res = 0; /* ignored */
294 else {
295#ifdef KMK_BUILTIN_STANDALONE
296 /* try exec the specified process */
297# if defined(_MSC_VER)
298 int argc_spawn = 0;
299 while (argv_spawn[argc_spawn])
300 argc_spawn++;
301 if (quote_argv(argc, argv_spawn, 0 /*fWatcomBrainDamage*/, 0/*fFreeOrLeak*/) != -1)
302 {
303 res = _spawnvp(_P_WAIT, argv_spawn[0], argv_spawn);
304 if (res == -1)
305 res = err(pCtx, 1, "_spawnvp(_P_WAIT,%s,..)", argv_spawn[0]);
306 }
307 else
308 res = err(pCtx, 1, "quote_argv: out of memory");
309# else
310 execvp(argv_spawn[0], argv_spawn);
311 res = err(pCtx, 1, "execvp(%s,..)", argv_spawn[0]);
312# endif
313#else /* in kmk */
314 /* let job.c spawn the process, make a job.c style argv_spawn copy. */
315 char *cur, **argv_new;
316 size_t sz = 0;
317 int argc_new = 0;
318 while (argv_spawn[argc_new]) {
319 size_t len = strlen(argv_spawn[argc_new]) + 1;
320 sz += (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
321 argc_new++;
322 }
323
324 argv_new = xmalloc((argc_new + 1) * sizeof(char *));
325 cur = xmalloc(sz);
326 for (i = 0; i < argc_new; i++) {
327 size_t len = strlen(argv_spawn[i]) + 1;
328 argv_new[i] = memcpy(cur, argv_spawn[i], len);
329 cur += (len + sizeof(void *) - 1) & ~(sizeof(void *) - 1);
330 }
331 argv_new[i] = NULL;
332
333 *ppapszArgvSpawn = argv_new;
334 res = 0;
335#endif /* in kmk */
336 }
337 }
338
339 return res;
340}
341
342#ifdef KMK_BUILTIN_STANDALONE
343int main(int argc, char **argv, char **envp)
344{
345 KMKBUILTINCTX Ctx = { "kmk_test", NULL };
346 return kmk_builtin_test(argc, argv, envp, &Ctx, NULL);
347}
348#endif
349
350static int
351syntax(PTESTINSTANCE pThis, const char *op, const char *msg)
352{
353
354 if (op && *op)
355 errx(pThis->pCtx, 1, "%s: %s", op, msg);
356 else
357 errx(pThis->pCtx, 1, "%s", msg);
358 return -42;
359}
360
361static int
362oexpr(PTESTINSTANCE pThis, enum token n)
363{
364 int res;
365
366 res = aexpr(pThis, n);
367 if (res == -42 || *pThis->t_wp == NULL)
368 return res;
369 if (t_lex(pThis, *++(pThis->t_wp)) == BOR) {
370 int res2 = oexpr(pThis, t_lex(pThis, *++(pThis->t_wp)));
371 return res2 != -42 ? res2 || res : res2;
372 }
373 pThis->t_wp--;
374 return res;
375}
376
377static int
378aexpr(PTESTINSTANCE pThis, enum token n)
379{
380 int res;
381
382 res = nexpr(pThis, n);
383 if (res == -42 || *pThis->t_wp == NULL)
384 return res;
385 if (t_lex(pThis, *++(pThis->t_wp)) == BAND) {
386 int res2 = aexpr(pThis, t_lex(pThis, *++(pThis->t_wp)));
387 return res2 != -42 ? res2 && res : res2;
388 }
389 pThis->t_wp--;
390 return res;
391}
392
393static int
394nexpr(PTESTINSTANCE pThis, enum token n)
395{
396 if (n == UNOT) {
397 int res = nexpr(pThis, t_lex(pThis, *++(pThis->t_wp)));
398 return res != -42 ? !res : res;
399 }
400 return primary(pThis, n);
401}
402
403static int
404primary(PTESTINSTANCE pThis, enum token n)
405{
406 enum token nn;
407 int res;
408
409 if (n == EOI)
410 return 0; /* missing expression */
411 if (n == LPAREN) {
412 if ((nn = t_lex(pThis, *++(pThis->t_wp))) == RPAREN)
413 return 0; /* missing expression */
414 res = oexpr(pThis, nn);
415 if (res != -42 && t_lex(pThis, *++(pThis->t_wp)) != RPAREN)
416 return syntax(pThis, NULL, "closing paren expected");
417 return res;
418 }
419 if (pThis->t_wp_op && pThis->t_wp_op->op_type == UNOP) {
420 /* unary expression */
421 if (*++(pThis->t_wp) == NULL)
422 return syntax(pThis, pThis->t_wp_op->op_text, "argument expected");
423 switch (n) {
424 case STREZ:
425 return strlen(*pThis->t_wp) == 0;
426 case STRNZ:
427 return strlen(*pThis->t_wp) != 0;
428 case FILTT:
429 return isatty(getn(pThis, *pThis->t_wp));
430 default:
431 return filstat(*pThis->t_wp, n);
432 }
433 }
434
435 if (t_lex(pThis, pThis->t_wp[1]), pThis->t_wp_op && pThis->t_wp_op->op_type == BINOP) {
436 return binop(pThis);
437 }
438
439 return strlen(*pThis->t_wp) > 0;
440}
441
442static int
443binop(PTESTINSTANCE pThis)
444{
445 const char *opnd1, *opnd2;
446 struct t_op const *op;
447
448 opnd1 = *pThis->t_wp;
449 (void) t_lex(pThis, *++(pThis->t_wp));
450 op = pThis->t_wp_op;
451
452 if ((opnd2 = *++(pThis->t_wp)) == NULL)
453 return syntax(pThis, op->op_text, "argument expected");
454
455 switch (op->op_num) {
456 case STREQ:
457 return strcmp(opnd1, opnd2) == 0;
458 case STRNE:
459 return strcmp(opnd1, opnd2) != 0;
460 case STRLT:
461 return strcmp(opnd1, opnd2) < 0;
462 case STRGT:
463 return strcmp(opnd1, opnd2) > 0;
464 case INTEQ:
465 return getn(pThis, opnd1) == getn(pThis, opnd2);
466 case INTNE:
467 return getn(pThis, opnd1) != getn(pThis, opnd2);
468 case INTGE:
469 return getn(pThis, opnd1) >= getn(pThis, opnd2);
470 case INTGT:
471 return getn(pThis, opnd1) > getn(pThis, opnd2);
472 case INTLE:
473 return getn(pThis, opnd1) <= getn(pThis, opnd2);
474 case INTLT:
475 return getn(pThis, opnd1) < getn(pThis, opnd2);
476 case FILNT:
477 return newerf(opnd1, opnd2);
478 case FILOT:
479 return olderf(opnd1, opnd2);
480 case FILEQ:
481 return equalf(opnd1, opnd2);
482 default:
483 abort();
484 /* NOTREACHED */
485#ifdef _MSC_VER
486 return -42;
487#endif
488 }
489}
490
491/*
492 * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
493 * not use access():
494 *
495 * True shall indicate only that the write flag is on. The file is not
496 * writable on a read-only file system even if this test indicates true.
497 *
498 * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
499 *
500 * True shall indicate that permission to read from file will be granted,
501 * as defined in "File Read, Write, and Creation".
502 *
503 * and that section says:
504 *
505 * When a file is to be read or written, the file shall be opened with an
506 * access mode corresponding to the operation to be performed. If file
507 * access permissions deny access, the requested operation shall fail.
508 *
509 * and of course access permissions are described as one might expect:
510 *
511 * * If a process has the appropriate privilege:
512 *
513 * * If read, write, or directory search permission is requested,
514 * access shall be granted.
515 *
516 * * If execute permission is requested, access shall be granted if
517 * execute permission is granted to at least one user by the file
518 * permission bits or by an alternate access control mechanism;
519 * otherwise, access shall be denied.
520 *
521 * * Otherwise:
522 *
523 * * The file permission bits of a file contain read, write, and
524 * execute/search permissions for the file owner class, file group
525 * class, and file other class.
526 *
527 * * Access shall be granted if an alternate access control mechanism
528 * is not enabled and the requested access permission bit is set for
529 * the class (file owner class, file group class, or file other class)
530 * to which the process belongs, or if an alternate access control
531 * mechanism is enabled and it allows the requested access; otherwise,
532 * access shall be denied.
533 *
534 * and when I first read this I thought: surely we can't go about using
535 * open(O_WRONLY) to try this test! However the POSIX 1003.1-2001 Rationale
536 * section for test does in fact say:
537 *
538 * On historical BSD systems, test -w directory always returned false
539 * because test tried to open the directory for writing, which always
540 * fails.
541 *
542 * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
543 * System III, and thus presumably also for BSD up to and including 4.3.
544 *
545 * Secondly I remembered why using open() and/or access() are bogus. They
546 * don't work right for detecting read and write permissions bits when called
547 * by root.
548 *
549 * Interestingly the 'test' in 4.4BSD was closer to correct (as per
550 * 1003.2-1992) and it was implemented efficiently with stat() instead of
551 * open().
552 *
553 * This was apparently broken in NetBSD around about 1994/06/30 when the old
554 * 4.4BSD implementation was replaced with a (arguably much better coded)
555 * implementation derived from pdksh.
556 *
557 * Note that modern pdksh is yet different again, but still not correct, at
558 * least not w.r.t. 1003.2-1992.
559 *
560 * As I think more about it and read more of the related IEEE docs I don't like
561 * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all. I very
562 * much prefer the original wording in 1003.2-1992. It is much more useful,
563 * and so that's what I've implemented.
564 *
565 * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
566 * totally useless for the case in question since its 'test -w' and 'test -r'
567 * can never fail for root for any existing files, i.e. files for which 'test
568 * -e' succeeds.)
569 *
570 * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
571 * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999),
572 * which is the latest copy I have, does carry the same suggested wording as is
573 * in 1003.1-2001, with its rationale saying:
574 *
575 * This change is a clarification and is the result of interpretation
576 * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
577 *
578 * That interpretation can be found here:
579 *
580 * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
581 *
582 * Not terribly helpful, unfortunately. I wonder who that fence sitter was.
583 *
584 * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
585 * PASC interpretation and appear to be gone against at least one widely used
586 * implementation (namely 4.4BSD). The problem is that for file access by root
587 * this means that if test '-r' and '-w' are to behave as if open() were called
588 * then there's no way for a shell script running as root to check if a file
589 * has certain access bits set other than by the grotty means of interpreting
590 * the output of 'ls -l'. This was widely considered to be a bug in V7's
591 * "test" and is, I believe, one of the reasons why direct use of access() was
592 * avoided in some more recent implementations!
593 *
594 * I have always interpreted '-r' to match '-w' and '-x' as per the original
595 * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much
596 * too far the wrong way without any valid rationale and that it's best if we
597 * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
598 * open() since we already know very well how it will work -- existance of the
599 * file is all that matters to open() for root.
600 *
601 * Unfortunately the SVID is no help at all (which is, I guess, partly why
602 * we're in this mess in the first place :-).
603 *
604 * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
605 * access(name, 2) even though it also goes to much greater lengths for '-x'
606 * matching the 1003.2-1992 definition (which is no doubt where that definition
607 * came from).
608 *
609 * The ksh93 implementation uses access() for '-r' and '-w' if
610 * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
611 * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
612 */
613static int
614test_access(struct stat *sp, mode_t stmode)
615{
616#ifdef _MSC_VER
617 /* just pretend to be root for now. */
618 stmode = (stmode << 6) | (stmode << 3) | stmode;
619 return !!(sp->st_mode & stmode);
620#else
621 gid_t *groups;
622 register int n;
623 uid_t euid;
624 int maxgroups;
625
626 /*
627 * I suppose we could use access() if not running as root and if we are
628 * running with ((euid == uid) && (egid == gid)), but we've already
629 * done the stat() so we might as well just test the permissions
630 * directly instead of asking the kernel to do it....
631 */
632 euid = geteuid();
633 if (euid == 0) /* any bit is good enough */
634 stmode = (stmode << 6) | (stmode << 3) | stmode;
635 else if (sp->st_uid == euid)
636 stmode <<= 6;
637 else if (sp->st_gid == getegid())
638 stmode <<= 3;
639 else {
640 /* XXX stolen almost verbatim from ksh93.... */
641 /* on some systems you can be in several groups */
642 if ((maxgroups = getgroups(0, NULL)) <= 0)
643 maxgroups = NGROUPS_MAX; /* pre-POSIX system? */
644 groups = xmalloc((maxgroups + 1) * sizeof(gid_t));
645 n = getgroups(maxgroups, groups);
646 while (--n >= 0) {
647 if (groups[n] == sp->st_gid) {
648 stmode <<= 3;
649 break;
650 }
651 }
652 free(groups);
653 }
654
655 return !!(sp->st_mode & stmode);
656#endif
657}
658
659static int
660filstat(char *nm, enum token mode)
661{
662 struct stat s;
663
664 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
665 return 0;
666
667 switch (mode) {
668 case FILRD:
669 return test_access(&s, S_IROTH);
670 case FILWR:
671 return test_access(&s, S_IWOTH);
672 case FILEX:
673 return test_access(&s, S_IXOTH);
674 case FILEXIST:
675 return 1; /* the successful lstat()/stat() is good enough */
676 case FILREG:
677 return S_ISREG(s.st_mode);
678 case FILDIR:
679 return S_ISDIR(s.st_mode);
680 case FILCDEV:
681#ifdef S_ISCHR
682 return S_ISCHR(s.st_mode);
683#else
684 return 0;
685#endif
686 case FILBDEV:
687#ifdef S_ISBLK
688 return S_ISBLK(s.st_mode);
689#else
690 return 0;
691#endif
692 case FILFIFO:
693#ifdef S_ISFIFO
694 return S_ISFIFO(s.st_mode);
695#else
696 return 0;
697#endif
698 case FILSOCK:
699#ifdef S_ISSOCK
700 return S_ISSOCK(s.st_mode);
701#else
702 return 0;
703#endif
704 case FILSYM:
705#ifdef S_ISLNK
706 return S_ISLNK(s.st_mode);
707#else
708 return 0;
709#endif
710 case FILSUID:
711 return (s.st_mode & S_ISUID) != 0;
712 case FILSGID:
713 return (s.st_mode & S_ISGID) != 0;
714 case FILSTCK:
715#ifdef S_ISVTX
716 return (s.st_mode & S_ISVTX) != 0;
717#else
718 return 0;
719#endif
720 case FILGZ:
721 return s.st_size > (off_t)0;
722 case FILUID:
723 return s.st_uid == geteuid();
724 case FILGID:
725 return s.st_gid == getegid();
726 default:
727 return 1;
728 }
729}
730
731#define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text
732
733static int
734compare1(const void *va, const void *vb)
735{
736 const unsigned char *a = va;
737 const unsigned char *b = VTOC(vb);
738
739 return a[0] - b[0];
740}
741
742static int
743compare2(const void *va, const void *vb)
744{
745 const unsigned char *a = va;
746 const unsigned char *b = VTOC(vb);
747 int z = a[0] - b[0];
748
749 return z ? z : (a[1] - b[1]);
750}
751
752static struct t_op const *
753findop(const char *s)
754{
755 if (s[0] == '-') {
756 if (s[1] == '\0')
757 return NULL;
758 if (s[2] == '\0')
759 return bsearch(s + 1, mop2, __arraycount(mop2), sizeof(*mop2), compare1);
760 else if (s[3] != '\0')
761 return NULL;
762 else
763 return bsearch(s + 1, mop3, __arraycount(mop3), sizeof(*mop3), compare2);
764 } else {
765 if (s[1] == '\0')
766 return bsearch(s, cop, __arraycount(cop), sizeof(*cop), compare1);
767 else if (strcmp(s, cop2[0].op_text) == 0)
768 return cop2;
769 else
770 return NULL;
771 }
772}
773
774static enum token
775t_lex(PTESTINSTANCE pThis, char *s)
776{
777 struct t_op const *op;
778
779 if (s == NULL) {
780 pThis->t_wp_op = NULL;
781 return EOI;
782 }
783
784 if ((op = findop(s)) != NULL) {
785 if (!((op->op_type == UNOP && isoperand(pThis)) ||
786 (op->op_num == LPAREN && *(pThis->t_wp+1) == 0))) {
787 pThis->t_wp_op = op;
788 return op->op_num;
789 }
790 }
791 pThis->t_wp_op = NULL;
792 return OPERAND;
793}
794
795static int
796isoperand(PTESTINSTANCE pThis)
797{
798 struct t_op const *op;
799 char *s, *t;
800
801 if ((s = *(pThis->t_wp+1)) == 0)
802 return 1;
803 if ((t = *(pThis->t_wp+2)) == 0)
804 return 0;
805 if ((op = findop(s)) != NULL)
806 return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
807 return 0;
808}
809
810/* atoi with error detection */
811static int
812getn(PTESTINSTANCE pThis, const char *s)
813{
814 char *p;
815 long r;
816
817 errno = 0;
818 r = strtol(s, &p, 10);
819
820 if (errno != 0)
821 return errx(pThis->pCtx, -42, "%s: out of range", s);
822
823 while (isspace((unsigned char)*p))
824 p++;
825
826 if (*p)
827 return errx(pThis->pCtx, -42, "%s: bad number", s);
828
829 return (int) r;
830}
831
832static int
833newerf(const char *f1, const char *f2)
834{
835 struct stat b1, b2;
836
837 return (stat(f1, &b1) == 0 &&
838 stat(f2, &b2) == 0 &&
839 b1.st_mtime > b2.st_mtime);
840}
841
842static int
843olderf(const char *f1, const char *f2)
844{
845 struct stat b1, b2;
846
847 return (stat(f1, &b1) == 0 &&
848 stat(f2, &b2) == 0 &&
849 b1.st_mtime < b2.st_mtime);
850}
851
852static int
853equalf(const char *f1, const char *f2)
854{
855 struct stat b1, b2;
856
857 return (stat(f1, &b1) == 0 &&
858 stat(f2, &b2) == 0 &&
859 b1.st_dev == b2.st_dev &&
860 b1.st_ino == b2.st_ino);
861}
862
863static int
864usage(PKMKBUILTINCTX pCtx, int fIsErr)
865{
866 kmk_builtin_ctx_printf(pCtx, fIsErr, "usage: %s expression [-- <prog> [args]]\n", pCtx->pszProgName);
867 return 0; /* only used in --help. */
868}
869
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use