Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1122 - (show annotations) (download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 10037 byte(s)
-updated to klibc-1.5.19
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 <fcntl.h>
15 #include <stdint.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(char **);
148 static int newerf(const char *, const char *);
149 static int olderf(const char *, const char *);
150 static int equalf(const char *, const char *);
151 #ifdef HAVE_FACCESSAT
152 static int test_file_access(const char *, int);
153 #else
154 static int test_st_mode(const struct stat64 *, int);
155 static int bash_group_member(gid_t);
156 #endif
157
158 static inline intmax_t getn(const char *s)
159 {
160 return atomax10(s);
161 }
162
163 static const struct t_op *getop(const char *s)
164 {
165 const struct t_op *op;
166
167 for (op = ops; op->op_text; op++) {
168 if (strcmp(s, op->op_text) == 0)
169 return op;
170 }
171
172 return NULL;
173 }
174
175 int
176 testcmd(int argc, char **argv)
177 {
178 const struct t_op *op;
179 enum token n;
180 int res;
181
182 if (*argv[0] == '[') {
183 if (*argv[--argc] != ']')
184 error("missing ]");
185 argv[argc] = NULL;
186 }
187
188 argv++;
189 argc--;
190
191 if (argc < 1)
192 return 1;
193
194 /*
195 * POSIX prescriptions: he who wrote this deserves the Nobel
196 * peace prize.
197 */
198 switch (argc) {
199 case 3:
200 op = getop(argv[1]);
201 if (op && op->op_type == BINOP) {
202 n = OPERAND;
203 goto eval;
204 }
205 /* fall through */
206
207 case 4:
208 if (!strcmp(argv[0], "(") && !strcmp(argv[argc - 1], ")")) {
209 argv[--argc] = NULL;
210 argv++;
211 argc--;
212 }
213 }
214
215 n = t_lex(argv);
216
217 eval:
218 t_wp = argv;
219 res = !oexpr(n);
220 argv = t_wp;
221
222 if (argv[0] != NULL && argv[1] != NULL)
223 syntax(argv[0], "unexpected operator");
224
225 return res;
226 }
227
228 static void
229 syntax(const char *op, const char *msg)
230 {
231 if (op && *op)
232 error("%s: %s", op, msg);
233 else
234 error("%s", msg);
235 }
236
237 static int
238 oexpr(enum token n)
239 {
240 int res = 0;
241
242 for (;;) {
243 res |= aexpr(n);
244 n = t_lex(t_wp + 1);
245 if (n != BOR)
246 break;
247 n = t_lex(t_wp += 2);
248 }
249 return res;
250 }
251
252 static int
253 aexpr(enum token n)
254 {
255 int res = 1;
256
257 for (;;) {
258 if (!nexpr(n))
259 res = 0;
260 n = t_lex(t_wp + 1);
261 if (n != BAND)
262 break;
263 n = t_lex(t_wp += 2);
264 }
265 return res;
266 }
267
268 static int
269 nexpr(enum token n)
270 {
271 if (n == UNOT)
272 return !nexpr(t_lex(++t_wp));
273 return primary(n);
274 }
275
276 static int
277 primary(enum token n)
278 {
279 enum token nn;
280 int res;
281
282 if (n == EOI)
283 return 0; /* missing expression */
284 if (n == LPAREN) {
285 if ((nn = t_lex(++t_wp)) == RPAREN)
286 return 0; /* missing expression */
287 res = oexpr(nn);
288 if (t_lex(++t_wp) != RPAREN)
289 syntax(NULL, "closing paren expected");
290 return res;
291 }
292 if (t_wp_op && t_wp_op->op_type == UNOP) {
293 /* unary expression */
294 if (*++t_wp == NULL)
295 syntax(t_wp_op->op_text, "argument expected");
296 switch (n) {
297 case STREZ:
298 return strlen(*t_wp) == 0;
299 case STRNZ:
300 return strlen(*t_wp) != 0;
301 case FILTT:
302 return isatty(getn(*t_wp));
303 #ifdef HAVE_FACCESSAT
304 case FILRD:
305 return test_file_access(*t_wp, R_OK);
306 case FILWR:
307 return test_file_access(*t_wp, W_OK);
308 case FILEX:
309 return test_file_access(*t_wp, X_OK);
310 #endif
311 default:
312 return filstat(*t_wp, n);
313 }
314 }
315
316 if (t_lex(t_wp + 1), t_wp_op && t_wp_op->op_type == BINOP) {
317 return binop();
318 }
319
320 return strlen(*t_wp) > 0;
321 }
322
323 static int
324 binop(void)
325 {
326 const char *opnd1, *opnd2;
327 struct t_op const *op;
328
329 opnd1 = *t_wp;
330 (void) t_lex(++t_wp);
331 op = t_wp_op;
332
333 if ((opnd2 = *++t_wp) == (char *)0)
334 syntax(op->op_text, "argument expected");
335
336 switch (op->op_num) {
337 default:
338 #ifdef DEBUG
339 abort();
340 /* NOTREACHED */
341 #endif
342 case STREQ:
343 return strcmp(opnd1, opnd2) == 0;
344 case STRNE:
345 return strcmp(opnd1, opnd2) != 0;
346 case STRLT:
347 return strcmp(opnd1, opnd2) < 0;
348 case STRGT:
349 return strcmp(opnd1, opnd2) > 0;
350 case INTEQ:
351 return getn(opnd1) == getn(opnd2);
352 case INTNE:
353 return getn(opnd1) != getn(opnd2);
354 case INTGE:
355 return getn(opnd1) >= getn(opnd2);
356 case INTGT:
357 return getn(opnd1) > getn(opnd2);
358 case INTLE:
359 return getn(opnd1) <= getn(opnd2);
360 case INTLT:
361 return getn(opnd1) < getn(opnd2);
362 case FILNT:
363 return newerf (opnd1, opnd2);
364 case FILOT:
365 return olderf (opnd1, opnd2);
366 case FILEQ:
367 return equalf (opnd1, opnd2);
368 }
369 }
370
371 static int
372 filstat(char *nm, enum token mode)
373 {
374 struct stat64 s;
375
376 if (mode == FILSYM ? lstat64(nm, &s) : stat64(nm, &s))
377 return 0;
378
379 switch (mode) {
380 #ifndef HAVE_FACCESSAT
381 case FILRD:
382 return test_st_mode(&s, R_OK);
383 case FILWR:
384 return test_st_mode(&s, W_OK);
385 case FILEX:
386 return test_st_mode(&s, X_OK);
387 #endif
388 case FILEXIST:
389 return 1;
390 case FILREG:
391 return S_ISREG(s.st_mode);
392 case FILDIR:
393 return S_ISDIR(s.st_mode);
394 case FILCDEV:
395 return S_ISCHR(s.st_mode);
396 case FILBDEV:
397 return S_ISBLK(s.st_mode);
398 case FILFIFO:
399 return S_ISFIFO(s.st_mode);
400 case FILSOCK:
401 return S_ISSOCK(s.st_mode);
402 case FILSYM:
403 return S_ISLNK(s.st_mode);
404 case FILSUID:
405 return (s.st_mode & S_ISUID) != 0;
406 case FILSGID:
407 return (s.st_mode & S_ISGID) != 0;
408 case FILSTCK:
409 return (s.st_mode & S_ISVTX) != 0;
410 case FILGZ:
411 return !!s.st_size;
412 case FILUID:
413 return s.st_uid == geteuid();
414 case FILGID:
415 return s.st_gid == getegid();
416 default:
417 return 1;
418 }
419 }
420
421 static enum token t_lex(char **tp)
422 {
423 struct t_op const *op;
424 char *s = *tp;
425
426 if (s == 0) {
427 t_wp_op = (struct t_op *)0;
428 return EOI;
429 }
430
431 op = getop(s);
432 if (op && !(op->op_type == UNOP && isoperand(tp)) &&
433 !(op->op_num == LPAREN && !tp[1])) {
434 t_wp_op = op;
435 return op->op_num;
436 }
437
438 t_wp_op = (struct t_op *)0;
439 return OPERAND;
440 }
441
442 static int isoperand(char **tp)
443 {
444 struct t_op const *op;
445 char *s;
446
447 if (!(s = tp[1]))
448 return 1;
449 if (!tp[2])
450 return 0;
451
452 op = getop(s);
453 return op && op->op_type == BINOP;
454 }
455
456 static int
457 newerf (const char *f1, const char *f2)
458 {
459 struct stat b1, b2;
460
461 return (stat (f1, &b1) == 0 &&
462 stat (f2, &b2) == 0 &&
463 b1.st_mtime > b2.st_mtime);
464 }
465
466 static int
467 olderf (const char *f1, const char *f2)
468 {
469 struct stat b1, b2;
470
471 return (stat (f1, &b1) == 0 &&
472 stat (f2, &b2) == 0 &&
473 b1.st_mtime < b2.st_mtime);
474 }
475
476 static int
477 equalf (const char *f1, const char *f2)
478 {
479 struct stat b1, b2;
480
481 return (stat (f1, &b1) == 0 &&
482 stat (f2, &b2) == 0 &&
483 b1.st_dev == b2.st_dev &&
484 b1.st_ino == b2.st_ino);
485 }
486
487 #ifdef HAVE_FACCESSAT
488 static int test_file_access(const char *path, int mode)
489 {
490 return !faccessat(AT_FDCWD, path, mode, AT_EACCESS);
491 }
492 #else /* HAVE_FACCESSAT */
493 /*
494 * Similar to what access(2) does, but uses the effective uid and gid.
495 * Doesn't make the mistake of telling root that any file is executable.
496 * Returns non-zero if the file is accessible.
497 */
498 static int
499 test_st_mode(const struct stat64 *st, int mode)
500 {
501 int euid = geteuid();
502
503 if (euid == 0) {
504 /* Root can read or write any file. */
505 if (mode != X_OK)
506 return 1;
507
508 /* Root can execute any file that has any one of the execute
509 bits set. */
510 mode = S_IXUSR | S_IXGRP | S_IXOTH;
511 } else if (st->st_uid == euid)
512 mode <<= 6;
513 else if (bash_group_member(st->st_gid))
514 mode <<= 3;
515
516 return st->st_mode & mode;
517 }
518
519 /* Return non-zero if GID is one that we have in our groups list. */
520 static int
521 bash_group_member(gid_t gid)
522 {
523 register int i;
524 gid_t *group_array;
525 int ngroups;
526
527 /* Short-circuit if possible, maybe saving a call to getgroups(). */
528 if (gid == getgid() || gid == getegid())
529 return (1);
530
531 ngroups = getgroups(0, NULL);
532 group_array = stalloc(ngroups * sizeof(gid_t));
533 if ((getgroups(ngroups, group_array)) != ngroups)
534 return (0);
535
536 /* Search through the list looking for GID. */
537 for (i = 0; i < ngroups; i++)
538 if (gid == group_array[i])
539 return (1);
540
541 return (0);
542 }
543 #endif /* HAVE_FACCESSAT */