source: mainline/uspace/app/ash/bltin/test.c@ 8ccd2ea

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8ccd2ea was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

  • Property mode set to 100644
File size: 10.3 KB
Line 
1/* $NetBSD: test.c,v 1.22 2000/04/09 23:24:59 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.22 2000/04/09 23:24:59 christos Exp $");
16#endif
17
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <unistd.h>
21#include <ctype.h>
22#include <errno.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <err.h>
27#ifdef __STDC__
28#include <stdarg.h>
29#else
30#include <varargs.h>
31#endif
32
33/* test(1) accepts the following grammar:
34 oexpr ::= aexpr | aexpr "-o" oexpr ;
35 aexpr ::= nexpr | nexpr "-a" aexpr ;
36 nexpr ::= primary | "!" primary
37 primary ::= unary-operator operand
38 | operand binary-operator operand
39 | operand
40 | "(" oexpr ")"
41 ;
42 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
43 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
44
45 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
46 "-nt"|"-ot"|"-ef";
47 operand ::= <any legal UNIX file name>
48*/
49
50enum token {
51 EOI,
52 FILRD,
53 FILWR,
54 FILEX,
55 FILEXIST,
56 FILREG,
57 FILDIR,
58 FILCDEV,
59 FILBDEV,
60 FILFIFO,
61 FILSOCK,
62 FILSYM,
63 FILGZ,
64 FILTT,
65 FILSUID,
66 FILSGID,
67 FILSTCK,
68 FILNT,
69 FILOT,
70 FILEQ,
71 FILUID,
72 FILGID,
73 STREZ,
74 STRNZ,
75 STREQ,
76 STRNE,
77 STRLT,
78 STRGT,
79 INTEQ,
80 INTNE,
81 INTGE,
82 INTGT,
83 INTLE,
84 INTLT,
85 UNOT,
86 BAND,
87 BOR,
88 LPAREN,
89 RPAREN,
90 OPERAND
91};
92
93enum token_types {
94 UNOP,
95 BINOP,
96 BUNOP,
97 BBINOP,
98 PAREN
99};
100
101static struct t_op {
102 const char *op_text;
103 short op_num, op_type;
104} const ops [] = {
105 {"-r", FILRD, UNOP},
106 {"-w", FILWR, UNOP},
107 {"-x", FILEX, UNOP},
108 {"-e", FILEXIST,UNOP},
109 {"-f", FILREG, UNOP},
110 {"-d", FILDIR, UNOP},
111 {"-c", FILCDEV,UNOP},
112 {"-b", FILBDEV,UNOP},
113 {"-p", FILFIFO,UNOP},
114 {"-u", FILSUID,UNOP},
115 {"-g", FILSGID,UNOP},
116 {"-k", FILSTCK,UNOP},
117 {"-s", FILGZ, UNOP},
118 {"-t", FILTT, UNOP},
119 {"-z", STREZ, UNOP},
120 {"-n", STRNZ, UNOP},
121 {"-h", FILSYM, UNOP}, /* for backwards compat */
122 {"-O", FILUID, UNOP},
123 {"-G", FILGID, UNOP},
124 {"-L", FILSYM, UNOP},
125 {"-S", FILSOCK,UNOP},
126 {"=", STREQ, BINOP},
127 {"!=", STRNE, BINOP},
128 {"<", STRLT, BINOP},
129 {">", STRGT, BINOP},
130 {"-eq", INTEQ, BINOP},
131 {"-ne", INTNE, BINOP},
132 {"-ge", INTGE, BINOP},
133 {"-gt", INTGT, BINOP},
134 {"-le", INTLE, BINOP},
135 {"-lt", INTLT, BINOP},
136 {"-nt", FILNT, BINOP},
137 {"-ot", FILOT, BINOP},
138 {"-ef", FILEQ, BINOP},
139 {"!", UNOT, BUNOP},
140 {"-a", BAND, BBINOP},
141 {"-o", BOR, BBINOP},
142 {"(", LPAREN, PAREN},
143 {")", RPAREN, PAREN},
144 {0, 0, 0}
145};
146
147static char **t_wp;
148static struct t_op const *t_wp_op;
149static gid_t *group_array = NULL;
150static int ngroups;
151
152static void syntax __P((const char *, const char *));
153static int oexpr __P((enum token));
154static int aexpr __P((enum token));
155static int nexpr __P((enum token));
156static int primary __P((enum token));
157static int binop __P((void));
158static int filstat __P((char *, enum token));
159static enum token t_lex __P((char *));
160static int isoperand __P((void));
161static int getn __P((const char *));
162static int newerf __P((const char *, const char *));
163static int olderf __P((const char *, const char *));
164static int equalf __P((const char *, const char *));
165static int test_eaccess();
166static int bash_group_member();
167static void initialize_group_array();
168
169#if defined(SHELL)
170extern void error __P((const char *, ...)) __attribute__((__noreturn__));
171#else
172static void error __P((const char *, ...)) __attribute__((__noreturn__));
173
174static void
175#ifdef __STDC__
176error(const char *msg, ...)
177#else
178error(va_alist)
179 va_dcl
180#endif
181{
182 va_list ap;
183#ifndef __STDC__
184 const char *msg;
185
186 va_start(ap);
187 msg = va_arg(ap, const char *);
188#else
189 va_start(ap, msg);
190#endif
191 verrx(2, msg, ap);
192 /*NOTREACHED*/
193 va_end(ap);
194}
195#endif
196
197#ifdef SHELL
198int testcmd __P((int, char **));
199
200int
201testcmd(argc, argv)
202 int argc;
203 char **argv;
204#else
205int main __P((int, char **));
206
207int
208main(argc, argv)
209 int argc;
210 char **argv;
211#endif
212{
213 int res;
214
215
216 if (strcmp(argv[0], "[") == 0) {
217 if (strcmp(argv[--argc], "]"))
218 error("missing ]");
219 argv[argc] = NULL;
220 }
221
222 if (argc < 2)
223 return 1;
224
225 t_wp = &argv[1];
226 res = !oexpr(t_lex(*t_wp));
227
228 if (*t_wp != NULL && *++t_wp != NULL)
229 syntax(*t_wp, "unexpected operator");
230
231 return res;
232}
233
234static void
235syntax(op, msg)
236 const char *op;
237 const char *msg;
238{
239 if (op && *op)
240 error("%s: %s", op, msg);
241 else
242 error("%s", msg);
243}
244
245static int
246oexpr(n)
247 enum token n;
248{
249 int res;
250
251 res = aexpr(n);
252 if (t_lex(*++t_wp) == BOR)
253 return oexpr(t_lex(*++t_wp)) || res;
254 t_wp--;
255 return res;
256}
257
258static int
259aexpr(n)
260 enum token n;
261{
262 int res;
263
264 res = nexpr(n);
265 if (t_lex(*++t_wp) == BAND)
266 return aexpr(t_lex(*++t_wp)) && res;
267 t_wp--;
268 return res;
269}
270
271static int
272nexpr(n)
273 enum token n; /* token */
274{
275 if (n == UNOT)
276 return !nexpr(t_lex(*++t_wp));
277 return primary(n);
278}
279
280static int
281primary(n)
282 enum token n;
283{
284 enum token nn;
285 int res;
286
287 if (n == EOI)
288 return 0; /* missing expression */
289 if (n == LPAREN) {
290 if ((nn = t_lex(*++t_wp)) == RPAREN)
291 return 0; /* missing expression */
292 res = oexpr(nn);
293 if (t_lex(*++t_wp) != RPAREN)
294 syntax(NULL, "closing paren expected");
295 return res;
296 }
297 if (t_wp_op && t_wp_op->op_type == UNOP) {
298 /* unary expression */
299 if (*++t_wp == NULL)
300 syntax(t_wp_op->op_text, "argument expected");
301 switch (n) {
302 case STREZ:
303 return strlen(*t_wp) == 0;
304 case STRNZ:
305 return strlen(*t_wp) != 0;
306 case FILTT:
307 return isatty(getn(*t_wp));
308 default:
309 return filstat(*t_wp, n);
310 }
311 }
312
313 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
314 return binop();
315 }
316
317 return strlen(*t_wp) > 0;
318}
319
320static int
321binop()
322{
323 const char *opnd1, *opnd2;
324 struct t_op const *op;
325
326 opnd1 = *t_wp;
327 (void) t_lex(*++t_wp);
328 op = t_wp_op;
329
330 if ((opnd2 = *++t_wp) == (char *)0)
331 syntax(op->op_text, "argument expected");
332
333 switch (op->op_num) {
334 case STREQ:
335 return strcmp(opnd1, opnd2) == 0;
336 case STRNE:
337 return strcmp(opnd1, opnd2) != 0;
338 case STRLT:
339 return strcmp(opnd1, opnd2) < 0;
340 case STRGT:
341 return strcmp(opnd1, opnd2) > 0;
342 case INTEQ:
343 return getn(opnd1) == getn(opnd2);
344 case INTNE:
345 return getn(opnd1) != getn(opnd2);
346 case INTGE:
347 return getn(opnd1) >= getn(opnd2);
348 case INTGT:
349 return getn(opnd1) > getn(opnd2);
350 case INTLE:
351 return getn(opnd1) <= getn(opnd2);
352 case INTLT:
353 return getn(opnd1) < getn(opnd2);
354 case FILNT:
355 return newerf (opnd1, opnd2);
356 case FILOT:
357 return olderf (opnd1, opnd2);
358 case FILEQ:
359 return equalf (opnd1, opnd2);
360 default:
361 abort();
362 /* NOTREACHED */
363 }
364}
365
366static int
367filstat(nm, mode)
368 char *nm;
369 enum token mode;
370{
371 struct stat s;
372
373 if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
374 return 0;
375
376 switch (mode) {
377 case FILRD:
378 return test_eaccess(nm, R_OK) == 0;
379 case FILWR:
380 return test_eaccess(nm, W_OK) == 0;
381 case FILEX:
382 return test_eaccess(nm, X_OK) == 0;
383 case FILEXIST:
384 return 1;
385 case FILREG:
386 return S_ISREG(s.st_mode);
387 case FILDIR:
388 return S_ISDIR(s.st_mode);
389 case FILCDEV:
390 return S_ISCHR(s.st_mode);
391 case FILBDEV:
392 return S_ISBLK(s.st_mode);
393 case FILFIFO:
394 return S_ISFIFO(s.st_mode);
395 case FILSOCK:
396 return S_ISSOCK(s.st_mode);
397 case FILSYM:
398 return S_ISLNK(s.st_mode);
399 case FILSUID:
400 return (s.st_mode & S_ISUID) != 0;
401 case FILSGID:
402 return (s.st_mode & S_ISGID) != 0;
403 case FILSTCK:
404 return (s.st_mode & S_ISVTX) != 0;
405 case FILGZ:
406 return s.st_size > (off_t)0;
407 case FILUID:
408 return s.st_uid == geteuid();
409 case FILGID:
410 return s.st_gid == getegid();
411 default:
412 return 1;
413 }
414}
415
416static enum token
417t_lex(s)
418 char *s;
419{
420 struct t_op const *op = ops;
421
422 if (s == 0) {
423 t_wp_op = (struct t_op *)0;
424 return EOI;
425 }
426 while (op->op_text) {
427 if (strcmp(s, op->op_text) == 0) {
428 if ((op->op_type == UNOP && isoperand()) ||
429 (op->op_num == LPAREN && *(t_wp+1) == 0))
430 break;
431 t_wp_op = op;
432 return op->op_num;
433 }
434 op++;
435 }
436 t_wp_op = (struct t_op *)0;
437 return OPERAND;
438}
439
440static int
441isoperand()
442{
443 struct t_op const *op = ops;
444 char *s;
445 char *t;
446
447 if ((s = *(t_wp+1)) == 0)
448 return 1;
449 if ((t = *(t_wp+2)) == 0)
450 return 0;
451 while (op->op_text) {
452 if (strcmp(s, op->op_text) == 0)
453 return op->op_type == BINOP &&
454 (t[0] != ')' || t[1] != '\0');
455 op++;
456 }
457 return 0;
458}
459
460/* atoi with error detection */
461static int
462getn(s)
463 const char *s;
464{
465 char *p;
466 long r;
467
468 errno = 0;
469 r = strtol(s, &p, 10);
470
471 if (errno != 0)
472 error("%s: out of range", s);
473
474 while (isspace((unsigned char)*p))
475 p++;
476
477 if (*p)
478 error("%s: bad number", s);
479
480 return (int) r;
481}
482
483static int
484newerf (f1, f2)
485const char *f1, *f2;
486{
487 struct stat b1, b2;
488
489 return (stat (f1, &b1) == 0 &&
490 stat (f2, &b2) == 0 &&
491 b1.st_mtime > b2.st_mtime);
492}
493
494static int
495olderf (f1, f2)
496const char *f1, *f2;
497{
498 struct stat b1, b2;
499
500 return (stat (f1, &b1) == 0 &&
501 stat (f2, &b2) == 0 &&
502 b1.st_mtime < b2.st_mtime);
503}
504
505static int
506equalf (f1, f2)
507const char *f1, *f2;
508{
509 struct stat b1, b2;
510
511 return (stat (f1, &b1) == 0 &&
512 stat (f2, &b2) == 0 &&
513 b1.st_dev == b2.st_dev &&
514 b1.st_ino == b2.st_ino);
515}
516
517/* Do the same thing access(2) does, but use the effective uid and gid,
518 and don't make the mistake of telling root that any file is
519 executable. */
520static int
521test_eaccess (path, mode)
522char *path;
523int mode;
524{
525 struct stat st;
526 int euid = geteuid();
527
528 if (stat (path, &st) < 0)
529 return (-1);
530
531 if (euid == 0) {
532 /* Root can read or write any file. */
533 if (mode != X_OK)
534 return (0);
535
536 /* Root can execute any file that has any one of the execute
537 bits set. */
538 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
539 return (0);
540 }
541
542 if (st.st_uid == euid) /* owner */
543 mode <<= 6;
544 else if (bash_group_member (st.st_gid))
545 mode <<= 3;
546
547 if (st.st_mode & mode)
548 return (0);
549
550 return (-1);
551}
552
553static void
554initialize_group_array ()
555{
556 ngroups = getgroups(0, NULL);
557 group_array = malloc(ngroups * sizeof(gid_t));
558 if (!group_array)
559 error(strerror(ENOMEM));
560 getgroups(ngroups, group_array);
561}
562
563/* Return non-zero if GID is one that we have in our groups list. */
564static int
565bash_group_member (gid)
566gid_t gid;
567{
568 register int i;
569
570 /* Short-circuit if possible, maybe saving a call to getgroups(). */
571 if (gid == getgid() || gid == getegid())
572 return (1);
573
574 if (ngroups == 0)
575 initialize_group_array ();
576
577 /* Search through the list looking for GID. */
578 for (i = 0; i < ngroups; i++)
579 if (gid == group_array[i])
580 return (1);
581
582 return (0);
583}
Note: See TracBrowser for help on using the repository browser.