Magellan Linux

Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/bltin/test.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 815 - (show annotations) (download)
Fri Apr 24 18:32:46 2009 UTC (15 years ago) by niro
File MIME type: text/plain
File size: 9338 byte(s)
-updated to klibc-1.5.15
1 /*
2 * test(1); version 7-like -- author Erik Baalbergen
3 * modified by Eric Gisin to be used as built-in.
4 * modified by Arnold Robbins to add SVR3 compatibility
5 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
6 * modified by J.T. Conklin for NetBSD.
7 *
8 * This program is in the Public Domain.
9 */
10
11 #include <sys/stat.h>
12 #include <sys/types.h>
13
14 #include <ctype.h>
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <stdarg.h>
20 #include "bltin.h"
21
22 /* test(1) accepts the following grammar:
23 oexpr ::= aexpr | aexpr "-o" oexpr ;
24 aexpr ::= nexpr | nexpr "-a" aexpr ;
25 nexpr ::= primary | "!" primary
26 primary ::= unary-operator operand
27 | operand binary-operator operand
28 | operand
29 | "(" oexpr ")"
30 ;
31 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
32 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
33
34 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
35 "-nt"|"-ot"|"-ef";
36 operand ::= <any legal UNIX file name>
37 */
38
39 enum token {
40 EOI,
41 FILRD,
42 FILWR,
43 FILEX,
44 FILEXIST,
45 FILREG,
46 FILDIR,
47 FILCDEV,
48 FILBDEV,
49 FILFIFO,
50 FILSOCK,
51 FILSYM,
52 FILGZ,
53 FILTT,
54 FILSUID,
55 FILSGID,
56 FILSTCK,
57 FILNT,
58 FILOT,
59 FILEQ,
60 FILUID,
61 FILGID,
62 STREZ,
63 STRNZ,
64 STREQ,
65 STRNE,
66 STRLT,
67 STRGT,
68 INTEQ,
69 INTNE,
70 INTGE,
71 INTGT,
72 INTLE,
73 INTLT,
74 UNOT,
75 BAND,
76 BOR,
77 LPAREN,
78 RPAREN,
79 OPERAND
80 };
81
82 enum token_types {
83 UNOP,
84 BINOP,
85 BUNOP,
86 BBINOP,
87 PAREN
88 };
89
90 static struct t_op {
91 const char *op_text;
92 short op_num, op_type;
93 } const ops [] = {
94 {"-r", FILRD, UNOP},
95 {"-w", FILWR, UNOP},
96 {"-x", FILEX, UNOP},
97 {"-e", FILEXIST,UNOP},
98 {"-f", FILREG, UNOP},
99 {"-d", FILDIR, UNOP},
100 {"-c", FILCDEV,UNOP},
101 {"-b", FILBDEV,UNOP},
102 {"-p", FILFIFO,UNOP},
103 {"-u", FILSUID,UNOP},
104 {"-g", FILSGID,UNOP},
105 {"-k", FILSTCK,UNOP},
106 {"-s", FILGZ, UNOP},
107 {"-t", FILTT, UNOP},
108 {"-z", STREZ, UNOP},
109 {"-n", STRNZ, UNOP},
110 {"-h", FILSYM, UNOP}, /* for backwards compat */
111 {"-O", FILUID, UNOP},
112 {"-G", FILGID, UNOP},
113 {"-L", FILSYM, UNOP},
114 {"-S", FILSOCK,UNOP},
115 {"=", STREQ, BINOP},
116 {"!=", STRNE, BINOP},
117 {"<", STRLT, BINOP},
118 {">", STRGT, BINOP},
119 {"-eq", INTEQ, BINOP},
120 {"-ne", INTNE, BINOP},
121 {"-ge", INTGE, BINOP},
122 {"-gt", INTGT, BINOP},
123 {"-le", INTLE, BINOP},
124 {"-lt", INTLT, BINOP},
125 {"-nt", FILNT, BINOP},
126 {"-ot", FILOT, BINOP},
127 {"-ef", FILEQ, BINOP},
128 {"!", UNOT, BUNOP},
129 {"-a", BAND, BBINOP},
130 {"-o", BOR, BBINOP},
131 {"(", LPAREN, PAREN},
132 {")", RPAREN, PAREN},
133 {0, 0, 0}
134 };
135
136 static char **t_wp;
137 static struct t_op const *t_wp_op;
138
139 static void syntax(const char *, const char *);
140 static int oexpr(enum token);
141 static int aexpr(enum token);
142 static int nexpr(enum token);
143 static int primary(enum token);
144 static int binop(void);
145 static int filstat(char *, enum token);
146 static enum token t_lex(char *);
147 static int isoperand(void);
148 static int getn(const char *);
149 static int newerf(const char *, const char *);
150 static int olderf(const char *, const char *);
151 static int equalf(const char *, const char *);
152 static int test_st_mode(const struct stat64 *, int);
153 static int bash_group_member(gid_t);
154
155 int
156 testcmd(int argc, char **argv)
157 {
158 int res;
159
160 if (strcmp(argv[0], "[") == 0) {
161 if (strcmp(argv[--argc], "]"))
162 error("missing ]");
163 argv[argc] = NULL;
164 }
165
166 if (argc < 2)
167 return 1;
168
169 t_wp = &argv[1];
170 res = !oexpr(t_lex(*t_wp));
171
172 if (*t_wp != NULL && *++t_wp != NULL)
173 syntax(*t_wp, "unexpected operator");
174
175 return res;
176 }
177
178 static void
179 syntax(const char *op, const char *msg)
180 {
181 if (op && *op)
182 error("%s: %s", op, msg);
183 else
184 error("%s", msg);
185 }
186
187 static int
188 oexpr(enum token n)
189 {
190 int res;
191
192 res = aexpr(n);
193 if (t_lex(*++t_wp) == BOR)
194 return oexpr(t_lex(*++t_wp)) || res;
195 t_wp--;
196 return res;
197 }
198
199 static int
200 aexpr(enum token n)
201 {
202 int res;
203
204 res = nexpr(n);
205 if (t_lex(*++t_wp) == BAND)
206 return aexpr(t_lex(*++t_wp)) && res;
207 t_wp--;
208 return res;
209 }
210
211 static int
212 nexpr(enum token n)
213 {
214 if (n == UNOT)
215 return !nexpr(t_lex(*++t_wp));
216 return primary(n);
217 }
218
219 static int
220 primary(enum token n)
221 {
222 enum token nn;
223 int res;
224
225 if (n == EOI)
226 return 0; /* missing expression */
227 if (n == LPAREN) {
228 if ((nn = t_lex(*++t_wp)) == RPAREN)
229 return 0; /* missing expression */
230 res = oexpr(nn);
231 if (t_lex(*++t_wp) != RPAREN)
232 syntax(NULL, "closing paren expected");
233 return res;
234 }
235 if (t_wp_op && t_wp_op->op_type == UNOP) {
236 /* unary expression */
237 if (*++t_wp == NULL)
238 syntax(t_wp_op->op_text, "argument expected");
239 switch (n) {
240 case STREZ:
241 return strlen(*t_wp) == 0;
242 case STRNZ:
243 return strlen(*t_wp) != 0;
244 case FILTT:
245 return isatty(getn(*t_wp));
246 default:
247 return filstat(*t_wp, n);
248 }
249 }
250
251 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
252 return binop();
253 }
254
255 return strlen(*t_wp) > 0;
256 }
257
258 static int
259 binop(void)
260 {
261 const char *opnd1, *opnd2;
262 struct t_op const *op;
263
264 opnd1 = *t_wp;
265 (void) t_lex(*++t_wp);
266 op = t_wp_op;
267
268 if ((opnd2 = *++t_wp) == (char *)0)
269 syntax(op->op_text, "argument expected");
270
271 switch (op->op_num) {
272 default:
273 #ifdef DEBUG
274 abort();
275 /* NOTREACHED */
276 #endif
277 case STREQ:
278 return strcmp(opnd1, opnd2) == 0;
279 case STRNE:
280 return strcmp(opnd1, opnd2) != 0;
281 case STRLT:
282 return strcmp(opnd1, opnd2) < 0;
283 case STRGT:
284 return strcmp(opnd1, opnd2) > 0;
285 case INTEQ:
286 return getn(opnd1) == getn(opnd2);
287 case INTNE:
288 return getn(opnd1) != getn(opnd2);
289 case INTGE:
290 return getn(opnd1) >= getn(opnd2);
291 case INTGT:
292 return getn(opnd1) > getn(opnd2);
293 case INTLE:
294 return getn(opnd1) <= getn(opnd2);
295 case INTLT:
296 return getn(opnd1) < getn(opnd2);
297 case FILNT:
298 return newerf (opnd1, opnd2);
299 case FILOT:
300 return olderf (opnd1, opnd2);
301 case FILEQ:
302 return equalf (opnd1, opnd2);
303 }
304 }
305
306 static int
307 filstat(char *nm, enum token mode)
308 {
309 struct stat64 s;
310
311 if (mode == FILSYM ? lstat64(nm, &s) : stat64(nm, &s))
312 return 0;
313
314 switch (mode) {
315 case FILRD:
316 return test_st_mode(&s, R_OK);
317 case FILWR:
318 return test_st_mode(&s, W_OK);
319 case FILEX:
320 return test_st_mode(&s, X_OK);
321 case FILEXIST:
322 return 1;
323 case FILREG:
324 return S_ISREG(s.st_mode);
325 case FILDIR:
326 return S_ISDIR(s.st_mode);
327 case FILCDEV:
328 return S_ISCHR(s.st_mode);
329 case FILBDEV:
330 return S_ISBLK(s.st_mode);
331 case FILFIFO:
332 return S_ISFIFO(s.st_mode);
333 case FILSOCK:
334 return S_ISSOCK(s.st_mode);
335 case FILSYM:
336 return S_ISLNK(s.st_mode);
337 case FILSUID:
338 return (s.st_mode & S_ISUID) != 0;
339 case FILSGID:
340 return (s.st_mode & S_ISGID) != 0;
341 case FILSTCK:
342 return (s.st_mode & S_ISVTX) != 0;
343 case FILGZ:
344 return !!s.st_size;
345 case FILUID:
346 return s.st_uid == geteuid();
347 case FILGID:
348 return s.st_gid == getegid();
349 default:
350 return 1;
351 }
352 }
353
354 static enum token
355 t_lex(char *s)
356 {
357 struct t_op const *op;
358
359 op = ops;
360
361 if (s == 0) {
362 t_wp_op = (struct t_op *)0;
363 return EOI;
364 }
365 while (op->op_text) {
366 if (strcmp(s, op->op_text) == 0) {
367 if ((op->op_type == UNOP && isoperand()) ||
368 (op->op_num == LPAREN && *(t_wp+1) == 0))
369 break;
370 t_wp_op = op;
371 return op->op_num;
372 }
373 op++;
374 }
375 t_wp_op = (struct t_op *)0;
376 return OPERAND;
377 }
378
379 static int
380 isoperand(void)
381 {
382 struct t_op const *op;
383 char *s, *t;
384
385 op = ops;
386 if ((s = *(t_wp+1)) == 0)
387 return 1;
388 if ((t = *(t_wp+2)) == 0)
389 return 0;
390 while (op->op_text) {
391 if (strcmp(s, op->op_text) == 0)
392 return op->op_type == BINOP &&
393 (t[0] != ')' || t[1] != '\0');
394 op++;
395 }
396 return 0;
397 }
398
399 /* atoi with error detection */
400 static int
401 getn(const char *s)
402 {
403 char *p;
404 long r;
405
406 errno = 0;
407 r = strtol(s, &p, 10);
408
409 if (errno != 0)
410 error("%s: out of range", s);
411
412 while (isspace((unsigned char)*p))
413 p++;
414
415 if (*p)
416 error("%s: bad number", s);
417
418 return (int) r;
419 }
420
421 static int
422 newerf (const char *f1, const char *f2)
423 {
424 struct stat b1, b2;
425
426 return (stat (f1, &b1) == 0 &&
427 stat (f2, &b2) == 0 &&
428 b1.st_mtime > b2.st_mtime);
429 }
430
431 static int
432 olderf (const char *f1, const char *f2)
433 {
434 struct stat b1, b2;
435
436 return (stat (f1, &b1) == 0 &&
437 stat (f2, &b2) == 0 &&
438 b1.st_mtime < b2.st_mtime);
439 }
440
441 static int
442 equalf (const char *f1, const char *f2)
443 {
444 struct stat b1, b2;
445
446 return (stat (f1, &b1) == 0 &&
447 stat (f2, &b2) == 0 &&
448 b1.st_dev == b2.st_dev &&
449 b1.st_ino == b2.st_ino);
450 }
451
452 /*
453 * Similar to what access(2) does, but uses the effective uid and gid.
454 * Doesn't make the mistake of telling root that any file is executable.
455 * Returns non-zero if the file is accessible.
456 */
457 static int
458 test_st_mode(const struct stat64 *st, int mode)
459 {
460 int euid = geteuid();
461
462 if (euid == 0) {
463 /* Root can read or write any file. */
464 if (mode != X_OK)
465 return 1;
466
467 /* Root can execute any file that has any one of the execute
468 bits set. */
469 mode = S_IXUSR | S_IXGRP | S_IXOTH;
470 } else if (st->st_uid == euid)
471 mode <<= 6;
472 else if (bash_group_member(st->st_gid))
473 mode <<= 3;
474
475 return st->st_mode & mode;
476 }
477
478 /* Return non-zero if GID is one that we have in our groups list. */
479 static int
480 bash_group_member(gid_t gid)
481 {
482 register int i;
483 gid_t *group_array;
484 int ngroups;
485
486 /* Short-circuit if possible, maybe saving a call to getgroups(). */
487 if (gid == getgid() || gid == getegid())
488 return (1);
489
490 ngroups = getgroups(0, NULL);
491 group_array = stalloc(ngroups * sizeof(gid_t));
492 if ((getgroups(ngroups, group_array)) != ngroups)
493 return (0);
494
495 /* Search through the list looking for GID. */
496 for (i = 0; i < ngroups; i++)
497 if (gid == group_array[i])
498 return (1);
499
500 return (0);
501 }