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