Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1122 - (hide 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 niro 532 /*
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 niro 1122 #include <fcntl.h>
15     #include <stdint.h>
16 niro 532 #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 niro 1122 static enum token t_lex(char **);
147     static int isoperand(char **);
148 niro 532 static int newerf(const char *, const char *);
149     static int olderf(const char *, const char *);
150     static int equalf(const char *, const char *);
151 niro 1122 #ifdef HAVE_FACCESSAT
152     static int test_file_access(const char *, int);
153     #else
154 niro 532 static int test_st_mode(const struct stat64 *, int);
155     static int bash_group_member(gid_t);
156 niro 1122 #endif
157 niro 532
158 niro 1122 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 niro 532 int
176     testcmd(int argc, char **argv)
177     {
178 niro 1122 const struct t_op *op;
179     enum token n;
180 niro 532 int res;
181    
182 niro 1122 if (*argv[0] == '[') {
183     if (*argv[--argc] != ']')
184 niro 532 error("missing ]");
185     argv[argc] = NULL;
186     }
187    
188 niro 1122 argv++;
189     argc--;
190    
191     if (argc < 1)
192 niro 532 return 1;
193    
194 niro 1122 /*
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 niro 532
207 niro 1122 case 4:
208     if (!strcmp(argv[0], "(") && !strcmp(argv[argc - 1], ")")) {
209     argv[--argc] = NULL;
210     argv++;
211     argc--;
212     }
213     }
214 niro 532
215 niro 1122 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 niro 532 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 niro 1122 int res = 0;
241 niro 532
242 niro 1122 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 niro 532 return res;
250     }
251    
252     static int
253     aexpr(enum token n)
254     {
255 niro 1122 int res = 1;
256 niro 532
257 niro 1122 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 niro 532 return res;
266     }
267    
268     static int
269     nexpr(enum token n)
270     {
271     if (n == UNOT)
272 niro 1122 return !nexpr(t_lex(++t_wp));
273 niro 532 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 niro 1122 if ((nn = t_lex(++t_wp)) == RPAREN)
286 niro 532 return 0; /* missing expression */
287     res = oexpr(nn);
288 niro 1122 if (t_lex(++t_wp) != RPAREN)
289 niro 532 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 niro 1122 #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 niro 532 default:
312     return filstat(*t_wp, n);
313     }
314     }
315    
316 niro 1122 if (t_lex(t_wp + 1), t_wp_op && t_wp_op->op_type == BINOP) {
317 niro 532 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 niro 1122 (void) t_lex(++t_wp);
331 niro 532 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 niro 1122 #ifndef HAVE_FACCESSAT
381 niro 532 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 niro 1122 #endif
388 niro 532 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 niro 1122 static enum token t_lex(char **tp)
422 niro 532 {
423     struct t_op const *op;
424 niro 1122 char *s = *tp;
425 niro 532
426     if (s == 0) {
427     t_wp_op = (struct t_op *)0;
428     return EOI;
429     }
430 niro 1122
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 niro 532 }
437 niro 1122
438 niro 532 t_wp_op = (struct t_op *)0;
439     return OPERAND;
440     }
441    
442 niro 1122 static int isoperand(char **tp)
443 niro 532 {
444     struct t_op const *op;
445 niro 1122 char *s;
446 niro 532
447 niro 1122 if (!(s = tp[1]))
448 niro 532 return 1;
449 niro 1122 if (!tp[2])
450 niro 532 return 0;
451    
452 niro 1122 op = getop(s);
453     return op && op->op_type == BINOP;
454 niro 532 }
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 niro 1122 #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 niro 532 /*
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 niro 815 if ((getgroups(ngroups, group_array)) != ngroups)
534     return (0);
535 niro 532
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 niro 1122 #endif /* HAVE_FACCESSAT */