11 |
#include <sys/stat.h> |
#include <sys/stat.h> |
12 |
#include <sys/types.h> |
#include <sys/types.h> |
13 |
|
|
14 |
#include <ctype.h> |
#include <fcntl.h> |
15 |
#include <errno.h> |
#include <stdint.h> |
16 |
#include <stdlib.h> |
#include <stdlib.h> |
17 |
#include <string.h> |
#include <string.h> |
18 |
#include <unistd.h> |
#include <unistd.h> |
143 |
static int primary(enum token); |
static int primary(enum token); |
144 |
static int binop(void); |
static int binop(void); |
145 |
static int filstat(char *, enum token); |
static int filstat(char *, enum token); |
146 |
static enum token t_lex(char *); |
static enum token t_lex(char **); |
147 |
static int isoperand(void); |
static int isoperand(char **); |
|
static int getn(const char *); |
|
148 |
static int newerf(const char *, const char *); |
static int newerf(const char *, const char *); |
149 |
static int olderf(const char *, const char *); |
static int olderf(const char *, const char *); |
150 |
static int equalf(const char *, const char *); |
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); |
static int test_st_mode(const struct stat64 *, int); |
155 |
static int bash_group_member(gid_t); |
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 |
int |
176 |
testcmd(int argc, char **argv) |
testcmd(int argc, char **argv) |
177 |
{ |
{ |
178 |
|
const struct t_op *op; |
179 |
|
enum token n; |
180 |
int res; |
int res; |
181 |
|
|
182 |
if (strcmp(argv[0], "[") == 0) { |
if (*argv[0] == '[') { |
183 |
if (strcmp(argv[--argc], "]")) |
if (*argv[--argc] != ']') |
184 |
error("missing ]"); |
error("missing ]"); |
185 |
argv[argc] = NULL; |
argv[argc] = NULL; |
186 |
} |
} |
187 |
|
|
188 |
if (argc < 2) |
argv++; |
189 |
|
argc--; |
190 |
|
|
191 |
|
if (argc < 1) |
192 |
return 1; |
return 1; |
193 |
|
|
194 |
t_wp = &argv[1]; |
/* |
195 |
res = !oexpr(t_lex(*t_wp)); |
* 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 (*t_wp != NULL && *++t_wp != NULL) |
if (argv[0] != NULL && argv[1] != NULL) |
223 |
syntax(*t_wp, "unexpected operator"); |
syntax(argv[0], "unexpected operator"); |
224 |
|
|
225 |
return res; |
return res; |
226 |
} |
} |
237 |
static int |
static int |
238 |
oexpr(enum token n) |
oexpr(enum token n) |
239 |
{ |
{ |
240 |
int res; |
int res = 0; |
241 |
|
|
242 |
res = aexpr(n); |
for (;;) { |
243 |
if (t_lex(*++t_wp) == BOR) |
res |= aexpr(n); |
244 |
return oexpr(t_lex(*++t_wp)) || res; |
n = t_lex(t_wp + 1); |
245 |
t_wp--; |
if (n != BOR) |
246 |
|
break; |
247 |
|
n = t_lex(t_wp += 2); |
248 |
|
} |
249 |
return res; |
return res; |
250 |
} |
} |
251 |
|
|
252 |
static int |
static int |
253 |
aexpr(enum token n) |
aexpr(enum token n) |
254 |
{ |
{ |
255 |
int res; |
int res = 1; |
256 |
|
|
257 |
res = nexpr(n); |
for (;;) { |
258 |
if (t_lex(*++t_wp) == BAND) |
if (!nexpr(n)) |
259 |
return aexpr(t_lex(*++t_wp)) && res; |
res = 0; |
260 |
t_wp--; |
n = t_lex(t_wp + 1); |
261 |
|
if (n != BAND) |
262 |
|
break; |
263 |
|
n = t_lex(t_wp += 2); |
264 |
|
} |
265 |
return res; |
return res; |
266 |
} |
} |
267 |
|
|
269 |
nexpr(enum token n) |
nexpr(enum token n) |
270 |
{ |
{ |
271 |
if (n == UNOT) |
if (n == UNOT) |
272 |
return !nexpr(t_lex(*++t_wp)); |
return !nexpr(t_lex(++t_wp)); |
273 |
return primary(n); |
return primary(n); |
274 |
} |
} |
275 |
|
|
282 |
if (n == EOI) |
if (n == EOI) |
283 |
return 0; /* missing expression */ |
return 0; /* missing expression */ |
284 |
if (n == LPAREN) { |
if (n == LPAREN) { |
285 |
if ((nn = t_lex(*++t_wp)) == RPAREN) |
if ((nn = t_lex(++t_wp)) == RPAREN) |
286 |
return 0; /* missing expression */ |
return 0; /* missing expression */ |
287 |
res = oexpr(nn); |
res = oexpr(nn); |
288 |
if (t_lex(*++t_wp) != RPAREN) |
if (t_lex(++t_wp) != RPAREN) |
289 |
syntax(NULL, "closing paren expected"); |
syntax(NULL, "closing paren expected"); |
290 |
return res; |
return res; |
291 |
} |
} |
300 |
return strlen(*t_wp) != 0; |
return strlen(*t_wp) != 0; |
301 |
case FILTT: |
case FILTT: |
302 |
return isatty(getn(*t_wp)); |
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: |
default: |
312 |
return filstat(*t_wp, n); |
return filstat(*t_wp, n); |
313 |
} |
} |
314 |
} |
} |
315 |
|
|
316 |
if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) { |
if (t_lex(t_wp + 1), t_wp_op && t_wp_op->op_type == BINOP) { |
317 |
return binop(); |
return binop(); |
318 |
} |
} |
319 |
|
|
327 |
struct t_op const *op; |
struct t_op const *op; |
328 |
|
|
329 |
opnd1 = *t_wp; |
opnd1 = *t_wp; |
330 |
(void) t_lex(*++t_wp); |
(void) t_lex(++t_wp); |
331 |
op = t_wp_op; |
op = t_wp_op; |
332 |
|
|
333 |
if ((opnd2 = *++t_wp) == (char *)0) |
if ((opnd2 = *++t_wp) == (char *)0) |
377 |
return 0; |
return 0; |
378 |
|
|
379 |
switch (mode) { |
switch (mode) { |
380 |
|
#ifndef HAVE_FACCESSAT |
381 |
case FILRD: |
case FILRD: |
382 |
return test_st_mode(&s, R_OK); |
return test_st_mode(&s, R_OK); |
383 |
case FILWR: |
case FILWR: |
384 |
return test_st_mode(&s, W_OK); |
return test_st_mode(&s, W_OK); |
385 |
case FILEX: |
case FILEX: |
386 |
return test_st_mode(&s, X_OK); |
return test_st_mode(&s, X_OK); |
387 |
|
#endif |
388 |
case FILEXIST: |
case FILEXIST: |
389 |
return 1; |
return 1; |
390 |
case FILREG: |
case FILREG: |
418 |
} |
} |
419 |
} |
} |
420 |
|
|
421 |
static enum token |
static enum token t_lex(char **tp) |
|
t_lex(char *s) |
|
422 |
{ |
{ |
423 |
struct t_op const *op; |
struct t_op const *op; |
424 |
|
char *s = *tp; |
|
op = ops; |
|
425 |
|
|
426 |
if (s == 0) { |
if (s == 0) { |
427 |
t_wp_op = (struct t_op *)0; |
t_wp_op = (struct t_op *)0; |
428 |
return EOI; |
return EOI; |
429 |
} |
} |
430 |
while (op->op_text) { |
|
431 |
if (strcmp(s, op->op_text) == 0) { |
op = getop(s); |
432 |
if ((op->op_type == UNOP && isoperand()) || |
if (op && !(op->op_type == UNOP && isoperand(tp)) && |
433 |
(op->op_num == LPAREN && *(t_wp+1) == 0)) |
!(op->op_num == LPAREN && !tp[1])) { |
434 |
break; |
t_wp_op = op; |
435 |
t_wp_op = op; |
return op->op_num; |
|
return op->op_num; |
|
|
} |
|
|
op++; |
|
436 |
} |
} |
437 |
|
|
438 |
t_wp_op = (struct t_op *)0; |
t_wp_op = (struct t_op *)0; |
439 |
return OPERAND; |
return OPERAND; |
440 |
} |
} |
441 |
|
|
442 |
static int |
static int isoperand(char **tp) |
|
isoperand(void) |
|
443 |
{ |
{ |
444 |
struct t_op const *op; |
struct t_op const *op; |
445 |
char *s, *t; |
char *s; |
446 |
|
|
447 |
op = ops; |
if (!(s = tp[1])) |
|
if ((s = *(t_wp+1)) == 0) |
|
448 |
return 1; |
return 1; |
449 |
if ((t = *(t_wp+2)) == 0) |
if (!tp[2]) |
450 |
return 0; |
return 0; |
|
while (op->op_text) { |
|
|
if (strcmp(s, op->op_text) == 0) |
|
|
return op->op_type == BINOP && |
|
|
(t[0] != ')' || t[1] != '\0'); |
|
|
op++; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* atoi with error detection */ |
|
|
static int |
|
|
getn(const char *s) |
|
|
{ |
|
|
char *p; |
|
|
long r; |
|
|
|
|
|
errno = 0; |
|
|
r = strtol(s, &p, 10); |
|
451 |
|
|
452 |
if (errno != 0) |
op = getop(s); |
453 |
error("%s: out of range", s); |
return op && op->op_type == BINOP; |
|
|
|
|
while (isspace((unsigned char)*p)) |
|
|
p++; |
|
|
|
|
|
if (*p) |
|
|
error("%s: bad number", s); |
|
|
|
|
|
return (int) r; |
|
454 |
} |
} |
455 |
|
|
456 |
static int |
static int |
484 |
b1.st_ino == b2.st_ino); |
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. |
* 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. |
* Doesn't make the mistake of telling root that any file is executable. |
540 |
|
|
541 |
return (0); |
return (0); |
542 |
} |
} |
543 |
|
#endif /* HAVE_FACCESSAT */ |