Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 815 - (hide 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 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     #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 niro 815 if ((getgroups(ngroups, group_array)) != ngroups)
493     return (0);
494 niro 532
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     }