test.c revision 1.33 1 1.33 christos /* $NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $ */
2 1.15 cgd
3 1.13 jtc /*
4 1.13 jtc * test(1); version 7-like -- author Erik Baalbergen
5 1.13 jtc * modified by Eric Gisin to be used as built-in.
6 1.13 jtc * modified by Arnold Robbins to add SVR3 compatibility
7 1.13 jtc * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
8 1.13 jtc * modified by J.T. Conklin for NetBSD.
9 1.1 glass *
10 1.13 jtc * This program is in the Public Domain.
11 1.1 glass */
12 1.1 glass
13 1.17 christos #include <sys/cdefs.h>
14 1.1 glass #ifndef lint
15 1.33 christos __RCSID("$NetBSD: test.c,v 1.33 2007/06/24 18:54:58 christos Exp $");
16 1.13 jtc #endif
17 1.1 glass
18 1.23 wiz #include <sys/stat.h>
19 1.1 glass #include <sys/types.h>
20 1.23 wiz
21 1.13 jtc #include <ctype.h>
22 1.23 wiz #include <err.h>
23 1.1 glass #include <errno.h>
24 1.33 christos #include <limits.h>
25 1.13 jtc #include <stdio.h>
26 1.1 glass #include <stdlib.h>
27 1.1 glass #include <string.h>
28 1.23 wiz #include <unistd.h>
29 1.22 christos #include <stdarg.h>
30 1.1 glass
31 1.13 jtc /* test(1) accepts the following grammar:
32 1.13 jtc oexpr ::= aexpr | aexpr "-o" oexpr ;
33 1.13 jtc aexpr ::= nexpr | nexpr "-a" aexpr ;
34 1.14 cgd nexpr ::= primary | "!" primary
35 1.13 jtc primary ::= unary-operator operand
36 1.13 jtc | operand binary-operator operand
37 1.13 jtc | operand
38 1.13 jtc | "(" oexpr ")"
39 1.13 jtc ;
40 1.13 jtc unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
41 1.13 jtc "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
42 1.13 jtc
43 1.30 hubertf binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
44 1.30 hubertf "-nt"|"-ot"|"-ef";
45 1.13 jtc operand ::= <any legal UNIX file name>
46 1.13 jtc */
47 1.13 jtc
48 1.13 jtc enum token {
49 1.13 jtc EOI,
50 1.13 jtc FILRD,
51 1.13 jtc FILWR,
52 1.13 jtc FILEX,
53 1.13 jtc FILEXIST,
54 1.13 jtc FILREG,
55 1.13 jtc FILDIR,
56 1.13 jtc FILCDEV,
57 1.13 jtc FILBDEV,
58 1.13 jtc FILFIFO,
59 1.13 jtc FILSOCK,
60 1.13 jtc FILSYM,
61 1.13 jtc FILGZ,
62 1.13 jtc FILTT,
63 1.13 jtc FILSUID,
64 1.13 jtc FILSGID,
65 1.13 jtc FILSTCK,
66 1.13 jtc FILNT,
67 1.13 jtc FILOT,
68 1.13 jtc FILEQ,
69 1.13 jtc FILUID,
70 1.13 jtc FILGID,
71 1.13 jtc STREZ,
72 1.13 jtc STRNZ,
73 1.13 jtc STREQ,
74 1.13 jtc STRNE,
75 1.13 jtc STRLT,
76 1.13 jtc STRGT,
77 1.13 jtc INTEQ,
78 1.13 jtc INTNE,
79 1.13 jtc INTGE,
80 1.13 jtc INTGT,
81 1.13 jtc INTLE,
82 1.13 jtc INTLT,
83 1.13 jtc UNOT,
84 1.13 jtc BAND,
85 1.13 jtc BOR,
86 1.13 jtc LPAREN,
87 1.13 jtc RPAREN,
88 1.13 jtc OPERAND
89 1.13 jtc };
90 1.1 glass
91 1.13 jtc enum token_types {
92 1.13 jtc UNOP,
93 1.13 jtc BINOP,
94 1.13 jtc BUNOP,
95 1.13 jtc BBINOP,
96 1.13 jtc PAREN
97 1.1 glass };
98 1.1 glass
99 1.31 christos struct t_op {
100 1.13 jtc const char *op_text;
101 1.13 jtc short op_num, op_type;
102 1.31 christos };
103 1.31 christos
104 1.31 christos static const struct t_op cop[] = {
105 1.13 jtc {"!", UNOT, BUNOP},
106 1.13 jtc {"(", LPAREN, PAREN},
107 1.13 jtc {")", RPAREN, PAREN},
108 1.31 christos {"<", STRLT, BINOP},
109 1.31 christos {"=", STREQ, BINOP},
110 1.31 christos {">", STRGT, BINOP},
111 1.31 christos };
112 1.31 christos
113 1.31 christos static const struct t_op cop2[] = {
114 1.31 christos {"!=", STRNE, BINOP},
115 1.31 christos };
116 1.31 christos
117 1.31 christos static const struct t_op mop3[] = {
118 1.31 christos {"ef", FILEQ, BINOP},
119 1.31 christos {"eq", INTEQ, BINOP},
120 1.31 christos {"ge", INTGE, BINOP},
121 1.31 christos {"gt", INTGT, BINOP},
122 1.31 christos {"le", INTLE, BINOP},
123 1.31 christos {"lt", INTLT, BINOP},
124 1.31 christos {"ne", INTNE, BINOP},
125 1.31 christos {"nt", FILNT, BINOP},
126 1.31 christos {"ot", FILOT, BINOP},
127 1.31 christos };
128 1.31 christos
129 1.31 christos static const struct t_op mop2[] = {
130 1.31 christos {"G", FILGID, UNOP},
131 1.31 christos {"L", FILSYM, UNOP},
132 1.31 christos {"O", FILUID, UNOP},
133 1.31 christos {"S", FILSOCK,UNOP},
134 1.31 christos {"a", BAND, BBINOP},
135 1.31 christos {"b", FILBDEV,UNOP},
136 1.31 christos {"c", FILCDEV,UNOP},
137 1.31 christos {"d", FILDIR, UNOP},
138 1.31 christos {"e", FILEXIST,UNOP},
139 1.31 christos {"f", FILREG, UNOP},
140 1.31 christos {"g", FILSGID,UNOP},
141 1.31 christos {"h", FILSYM, UNOP}, /* for backwards compat */
142 1.31 christos {"k", FILSTCK,UNOP},
143 1.31 christos {"n", STRNZ, UNOP},
144 1.31 christos {"o", BOR, BBINOP},
145 1.31 christos {"p", FILFIFO,UNOP},
146 1.31 christos {"r", FILRD, UNOP},
147 1.31 christos {"s", FILGZ, UNOP},
148 1.31 christos {"t", FILTT, UNOP},
149 1.31 christos {"u", FILSUID,UNOP},
150 1.31 christos {"w", FILWR, UNOP},
151 1.31 christos {"x", FILEX, UNOP},
152 1.31 christos {"z", STREZ, UNOP},
153 1.1 glass };
154 1.1 glass
155 1.22 christos static char **t_wp;
156 1.22 christos static struct t_op const *t_wp_op;
157 1.1 glass
158 1.23 wiz static void syntax(const char *, const char *);
159 1.23 wiz static int oexpr(enum token);
160 1.23 wiz static int aexpr(enum token);
161 1.23 wiz static int nexpr(enum token);
162 1.23 wiz static int primary(enum token);
163 1.23 wiz static int binop(void);
164 1.33 christos static int test_access(struct stat *, mode_t);
165 1.23 wiz static int filstat(char *, enum token);
166 1.23 wiz static enum token t_lex(char *);
167 1.23 wiz static int isoperand(void);
168 1.23 wiz static int getn(const char *);
169 1.23 wiz static int newerf(const char *, const char *);
170 1.23 wiz static int olderf(const char *, const char *);
171 1.23 wiz static int equalf(const char *, const char *);
172 1.17 christos
173 1.22 christos #if defined(SHELL)
174 1.23 wiz extern void error(const char *, ...) __attribute__((__noreturn__));
175 1.33 christos extern void *ckmalloc(size_t);
176 1.22 christos #else
177 1.23 wiz static void error(const char *, ...) __attribute__((__noreturn__));
178 1.22 christos
179 1.22 christos static void
180 1.22 christos error(const char *msg, ...)
181 1.22 christos {
182 1.22 christos va_list ap;
183 1.22 christos
184 1.22 christos va_start(ap, msg);
185 1.22 christos verrx(2, msg, ap);
186 1.22 christos /*NOTREACHED*/
187 1.22 christos va_end(ap);
188 1.22 christos }
189 1.33 christos
190 1.33 christos static void *ckmalloc(size_t);
191 1.33 christos static void *
192 1.33 christos ckmalloc(size_t nbytes)
193 1.33 christos {
194 1.33 christos void *p = malloc(nbytes);
195 1.33 christos
196 1.33 christos if (!p)
197 1.33 christos error("Not enough memory!");
198 1.33 christos return p;
199 1.33 christos }
200 1.22 christos #endif
201 1.22 christos
202 1.22 christos #ifdef SHELL
203 1.23 wiz int testcmd(int, char **);
204 1.22 christos
205 1.22 christos int
206 1.23 wiz testcmd(int argc, char **argv)
207 1.22 christos #else
208 1.24 wiz int main(int, char *[]);
209 1.1 glass
210 1.1 glass int
211 1.24 wiz main(int argc, char *argv[])
212 1.22 christos #endif
213 1.1 glass {
214 1.24 wiz int res;
215 1.28 christos const char *argv0;
216 1.22 christos
217 1.28 christos #ifdef SHELL
218 1.28 christos argv0 = argv[0];
219 1.28 christos #else
220 1.24 wiz setprogname(argv[0]);
221 1.28 christos argv0 = getprogname();
222 1.28 christos #endif
223 1.28 christos if (strcmp(argv0, "[") == 0) {
224 1.1 glass if (strcmp(argv[--argc], "]"))
225 1.22 christos error("missing ]");
226 1.1 glass argv[argc] = NULL;
227 1.1 glass }
228 1.1 glass
229 1.22 christos if (argc < 2)
230 1.22 christos return 1;
231 1.22 christos
232 1.14 cgd t_wp = &argv[1];
233 1.13 jtc res = !oexpr(t_lex(*t_wp));
234 1.13 jtc
235 1.13 jtc if (*t_wp != NULL && *++t_wp != NULL)
236 1.21 kleink syntax(*t_wp, "unexpected operator");
237 1.13 jtc
238 1.13 jtc return res;
239 1.13 jtc }
240 1.13 jtc
241 1.13 jtc static void
242 1.23 wiz syntax(const char *op, const char *msg)
243 1.13 jtc {
244 1.26 simonb
245 1.13 jtc if (op && *op)
246 1.22 christos error("%s: %s", op, msg);
247 1.13 jtc else
248 1.22 christos error("%s", msg);
249 1.13 jtc }
250 1.13 jtc
251 1.13 jtc static int
252 1.23 wiz oexpr(enum token n)
253 1.13 jtc {
254 1.13 jtc int res;
255 1.13 jtc
256 1.13 jtc res = aexpr(n);
257 1.32 christos if (*t_wp == NULL)
258 1.32 christos return res;
259 1.13 jtc if (t_lex(*++t_wp) == BOR)
260 1.13 jtc return oexpr(t_lex(*++t_wp)) || res;
261 1.13 jtc t_wp--;
262 1.13 jtc return res;
263 1.13 jtc }
264 1.13 jtc
265 1.13 jtc static int
266 1.23 wiz aexpr(enum token n)
267 1.13 jtc {
268 1.13 jtc int res;
269 1.13 jtc
270 1.13 jtc res = nexpr(n);
271 1.32 christos if (*t_wp == NULL)
272 1.32 christos return res;
273 1.13 jtc if (t_lex(*++t_wp) == BAND)
274 1.13 jtc return aexpr(t_lex(*++t_wp)) && res;
275 1.13 jtc t_wp--;
276 1.13 jtc return res;
277 1.13 jtc }
278 1.13 jtc
279 1.13 jtc static int
280 1.23 wiz nexpr(enum token n)
281 1.13 jtc {
282 1.26 simonb
283 1.13 jtc if (n == UNOT)
284 1.13 jtc return !nexpr(t_lex(*++t_wp));
285 1.13 jtc return primary(n);
286 1.13 jtc }
287 1.13 jtc
288 1.13 jtc static int
289 1.23 wiz primary(enum token n)
290 1.13 jtc {
291 1.21 kleink enum token nn;
292 1.13 jtc int res;
293 1.13 jtc
294 1.13 jtc if (n == EOI)
295 1.21 kleink return 0; /* missing expression */
296 1.13 jtc if (n == LPAREN) {
297 1.21 kleink if ((nn = t_lex(*++t_wp)) == RPAREN)
298 1.21 kleink return 0; /* missing expression */
299 1.21 kleink res = oexpr(nn);
300 1.13 jtc if (t_lex(*++t_wp) != RPAREN)
301 1.13 jtc syntax(NULL, "closing paren expected");
302 1.13 jtc return res;
303 1.1 glass }
304 1.13 jtc if (t_wp_op && t_wp_op->op_type == UNOP) {
305 1.13 jtc /* unary expression */
306 1.13 jtc if (*++t_wp == NULL)
307 1.13 jtc syntax(t_wp_op->op_text, "argument expected");
308 1.13 jtc switch (n) {
309 1.13 jtc case STREZ:
310 1.13 jtc return strlen(*t_wp) == 0;
311 1.13 jtc case STRNZ:
312 1.13 jtc return strlen(*t_wp) != 0;
313 1.13 jtc case FILTT:
314 1.13 jtc return isatty(getn(*t_wp));
315 1.13 jtc default:
316 1.13 jtc return filstat(*t_wp, n);
317 1.1 glass }
318 1.13 jtc }
319 1.14 cgd
320 1.14 cgd if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
321 1.14 cgd return binop();
322 1.14 cgd }
323 1.14 cgd
324 1.14 cgd return strlen(*t_wp) > 0;
325 1.14 cgd }
326 1.14 cgd
327 1.14 cgd static int
328 1.23 wiz binop(void)
329 1.14 cgd {
330 1.16 tls const char *opnd1, *opnd2;
331 1.14 cgd struct t_op const *op;
332 1.14 cgd
333 1.13 jtc opnd1 = *t_wp;
334 1.13 jtc (void) t_lex(*++t_wp);
335 1.14 cgd op = t_wp_op;
336 1.13 jtc
337 1.26 simonb if ((opnd2 = *++t_wp) == NULL)
338 1.14 cgd syntax(op->op_text, "argument expected");
339 1.13 jtc
340 1.14 cgd switch (op->op_num) {
341 1.14 cgd case STREQ:
342 1.14 cgd return strcmp(opnd1, opnd2) == 0;
343 1.14 cgd case STRNE:
344 1.14 cgd return strcmp(opnd1, opnd2) != 0;
345 1.14 cgd case STRLT:
346 1.14 cgd return strcmp(opnd1, opnd2) < 0;
347 1.14 cgd case STRGT:
348 1.14 cgd return strcmp(opnd1, opnd2) > 0;
349 1.14 cgd case INTEQ:
350 1.14 cgd return getn(opnd1) == getn(opnd2);
351 1.14 cgd case INTNE:
352 1.14 cgd return getn(opnd1) != getn(opnd2);
353 1.14 cgd case INTGE:
354 1.14 cgd return getn(opnd1) >= getn(opnd2);
355 1.14 cgd case INTGT:
356 1.14 cgd return getn(opnd1) > getn(opnd2);
357 1.14 cgd case INTLE:
358 1.14 cgd return getn(opnd1) <= getn(opnd2);
359 1.14 cgd case INTLT:
360 1.14 cgd return getn(opnd1) < getn(opnd2);
361 1.14 cgd case FILNT:
362 1.26 simonb return newerf(opnd1, opnd2);
363 1.14 cgd case FILOT:
364 1.26 simonb return olderf(opnd1, opnd2);
365 1.14 cgd case FILEQ:
366 1.26 simonb return equalf(opnd1, opnd2);
367 1.17 christos default:
368 1.19 mycroft abort();
369 1.17 christos /* NOTREACHED */
370 1.1 glass }
371 1.1 glass }
372 1.1 glass
373 1.33 christos /*
374 1.33 christos * The manual, and IEEE POSIX 1003.2, suggests this should check the mode bits,
375 1.33 christos * not use access():
376 1.33 christos *
377 1.33 christos * True shall indicate only that the write flag is on. The file is not
378 1.33 christos * writable on a read-only file system even if this test indicates true.
379 1.33 christos *
380 1.33 christos * Unfortunately IEEE POSIX 1003.1-2001, as quoted in SuSv3, says only:
381 1.33 christos *
382 1.33 christos * True shall indicate that permission to read from file will be granted,
383 1.33 christos * as defined in "File Read, Write, and Creation".
384 1.33 christos *
385 1.33 christos * and that section says:
386 1.33 christos *
387 1.33 christos * When a file is to be read or written, the file shall be opened with an
388 1.33 christos * access mode corresponding to the operation to be performed. If file
389 1.33 christos * access permissions deny access, the requested operation shall fail.
390 1.33 christos *
391 1.33 christos * and of course access permissions are described as one might expect:
392 1.33 christos *
393 1.33 christos * * If a process has the appropriate privilege:
394 1.33 christos *
395 1.33 christos * * If read, write, or directory search permission is requested,
396 1.33 christos * access shall be granted.
397 1.33 christos *
398 1.33 christos * * If execute permission is requested, access shall be granted if
399 1.33 christos * execute permission is granted to at least one user by the file
400 1.33 christos * permission bits or by an alternate access control mechanism;
401 1.33 christos * otherwise, access shall be denied.
402 1.33 christos *
403 1.33 christos * * Otherwise:
404 1.33 christos *
405 1.33 christos * * The file permission bits of a file contain read, write, and
406 1.33 christos * execute/search permissions for the file owner class, file group
407 1.33 christos * class, and file other class.
408 1.33 christos *
409 1.33 christos * * Access shall be granted if an alternate access control mechanism
410 1.33 christos * is not enabled and the requested access permission bit is set for
411 1.33 christos * the class (file owner class, file group class, or file other class)
412 1.33 christos * to which the process belongs, or if an alternate access control
413 1.33 christos * mechanism is enabled and it allows the requested access; otherwise,
414 1.33 christos * access shall be denied.
415 1.33 christos *
416 1.33 christos * and when I first read this I thought: surely we can't go about using
417 1.33 christos * open(O_WRONLY) to try this test! However the POSIX 1003.1-2001 Rationale
418 1.33 christos * section for test does in fact say:
419 1.33 christos *
420 1.33 christos * On historical BSD systems, test -w directory always returned false
421 1.33 christos * because test tried to open the directory for writing, which always
422 1.33 christos * fails.
423 1.33 christos *
424 1.33 christos * and indeed this is in fact true for Seventh Edition UNIX, UNIX 32V, and UNIX
425 1.33 christos * System III, and thus presumably also for BSD up to and including 4.3.
426 1.33 christos *
427 1.33 christos * Secondly I remembered why using open() and/or access() are bogus. They
428 1.33 christos * don't work right for detecting read and write permissions bits when called
429 1.33 christos * by root.
430 1.33 christos *
431 1.33 christos * Interestingly the 'test' in 4.4BSD was closer to correct (as per
432 1.33 christos * 1003.2-1992) and it was implemented efficiently with stat() instead of
433 1.33 christos * open().
434 1.33 christos *
435 1.33 christos * This was apparently broken in NetBSD around about 1994/06/30 when the old
436 1.33 christos * 4.4BSD implementation was replaced with a (arguably much better coded)
437 1.33 christos * implementation derived from pdksh.
438 1.33 christos *
439 1.33 christos * Note that modern pdksh is yet different again, but still not correct, at
440 1.33 christos * least not w.r.t. 1003.2-1992.
441 1.33 christos *
442 1.33 christos * As I think more about it and read more of the related IEEE docs I don't like
443 1.33 christos * that wording about 'test -r' and 'test -w' in 1003.1-2001 at all. I very
444 1.33 christos * much prefer the original wording in 1003.2-1992. It is much more useful,
445 1.33 christos * and so that's what I've implemented.
446 1.33 christos *
447 1.33 christos * (Note that a strictly conforming implementation of 1003.1-2001 is in fact
448 1.33 christos * totally useless for the case in question since its 'test -w' and 'test -r'
449 1.33 christos * can never fail for root for any existing files, i.e. files for which 'test
450 1.33 christos * -e' succeeds.)
451 1.33 christos *
452 1.33 christos * The rationale for 1003.1-2001 suggests that the wording was "clarified" in
453 1.33 christos * 1003.1-2001 to align with the 1003.2b draft. 1003.2b Draft 12 (July 1999),
454 1.33 christos * which is the latest copy I have, does carry the same suggested wording as is
455 1.33 christos * in 1003.1-2001, with its rationale saying:
456 1.33 christos *
457 1.33 christos * This change is a clarification and is the result of interpretation
458 1.33 christos * request PASC 1003.2-92 #23 submitted for IEEE Std 1003.2-1992.
459 1.33 christos *
460 1.33 christos * That interpretation can be found here:
461 1.33 christos *
462 1.33 christos * http://www.pasc.org/interps/unofficial/db/p1003.2/pasc-1003.2-23.html
463 1.33 christos *
464 1.33 christos * Not terribly helpful, unfortunately. I wonder who that fence sitter was.
465 1.33 christos *
466 1.33 christos * Worse, IMVNSHO, I think the authors of 1003.2b-D12 have mis-interpreted the
467 1.33 christos * PASC interpretation and appear to be gone against at least one widely used
468 1.33 christos * implementation (namely 4.4BSD). The problem is that for file access by root
469 1.33 christos * this means that if test '-r' and '-w' are to behave as if open() were called
470 1.33 christos * then there's no way for a shell script running as root to check if a file
471 1.33 christos * has certain access bits set other than by the grotty means of interpreting
472 1.33 christos * the output of 'ls -l'. This was widely considered to be a bug in V7's
473 1.33 christos * "test" and is, I believe, one of the reasons why direct use of access() was
474 1.33 christos * avoided in some more recent implementations!
475 1.33 christos *
476 1.33 christos * I have always interpreted '-r' to match '-w' and '-x' as per the original
477 1.33 christos * wording in 1003.2-1992, not the other way around. I think 1003.2b goes much
478 1.33 christos * too far the wrong way without any valid rationale and that it's best if we
479 1.33 christos * stick with 1003.2-1992 and test the flags, and not mimic the behaviour of
480 1.33 christos * open() since we already know very well how it will work -- existance of the
481 1.33 christos * file is all that matters to open() for root.
482 1.33 christos *
483 1.33 christos * Unfortunately the SVID is no help at all (which is, I guess, partly why
484 1.33 christos * we're in this mess in the first place :-).
485 1.33 christos *
486 1.33 christos * The SysV implementation (at least in the 'test' builtin in /bin/sh) does use
487 1.33 christos * access(name, 2) even though it also goes to much greater lengths for '-x'
488 1.33 christos * matching the 1003.2-1992 definition (which is no doubt where that definition
489 1.33 christos * came from).
490 1.33 christos *
491 1.33 christos * The ksh93 implementation uses access() for '-r' and '-w' if
492 1.33 christos * (euid==uid&&egid==gid), but uses st_mode for '-x' iff running as root.
493 1.33 christos * i.e. it does strictly conform to 1003.1-2001 (and presumably 1003.2b).
494 1.33 christos */
495 1.33 christos static int
496 1.33 christos test_access(struct stat *sp, mode_t stmode)
497 1.33 christos {
498 1.33 christos gid_t *groups;
499 1.33 christos register int n;
500 1.33 christos uid_t euid;
501 1.33 christos int maxgroups;
502 1.33 christos
503 1.33 christos /*
504 1.33 christos * I suppose we could use access() if not running as root and if we are
505 1.33 christos * running with ((euid == uid) && (egid == gid)), but we've already
506 1.33 christos * done the stat() so we might as well just test the permissions
507 1.33 christos * directly instead of asking the kernel to do it....
508 1.33 christos */
509 1.33 christos euid = geteuid();
510 1.33 christos if (euid == 0) /* any bit is good enough */
511 1.33 christos stmode = (stmode << 6) | (stmode << 3) | stmode;
512 1.33 christos else if (sp->st_uid == euid)
513 1.33 christos stmode <<= 6;
514 1.33 christos else if (sp->st_gid == getegid())
515 1.33 christos stmode <<= 3;
516 1.33 christos else {
517 1.33 christos /* XXX stolen almost verbatim from ksh93.... */
518 1.33 christos /* on some systems you can be in several groups */
519 1.33 christos if ((maxgroups = getgroups(0, NULL)) <= 0)
520 1.33 christos maxgroups = NGROUPS_MAX; /* pre-POSIX system? */
521 1.33 christos groups = ckmalloc((maxgroups + 1) * sizeof(gid_t));
522 1.33 christos n = getgroups(maxgroups, groups);
523 1.33 christos while (--n >= 0) {
524 1.33 christos if (groups[n] == sp->st_gid) {
525 1.33 christos stmode <<= 3;
526 1.33 christos break;
527 1.33 christos }
528 1.33 christos }
529 1.33 christos free(groups);
530 1.33 christos }
531 1.33 christos
532 1.33 christos return sp->st_mode & stmode;
533 1.33 christos }
534 1.33 christos
535 1.1 glass static int
536 1.23 wiz filstat(char *nm, enum token mode)
537 1.1 glass {
538 1.13 jtc struct stat s;
539 1.13 jtc
540 1.18 mycroft if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
541 1.13 jtc return 0;
542 1.1 glass
543 1.13 jtc switch (mode) {
544 1.13 jtc case FILRD:
545 1.33 christos return test_access(&s, S_IROTH);
546 1.13 jtc case FILWR:
547 1.33 christos return test_access(&s, S_IWOTH);
548 1.13 jtc case FILEX:
549 1.33 christos return test_access(&s, S_IXOTH);
550 1.13 jtc case FILEXIST:
551 1.33 christos return 1; /* the successful lstat()/stat() is good enough */
552 1.13 jtc case FILREG:
553 1.18 mycroft return S_ISREG(s.st_mode);
554 1.13 jtc case FILDIR:
555 1.18 mycroft return S_ISDIR(s.st_mode);
556 1.13 jtc case FILCDEV:
557 1.18 mycroft return S_ISCHR(s.st_mode);
558 1.13 jtc case FILBDEV:
559 1.18 mycroft return S_ISBLK(s.st_mode);
560 1.13 jtc case FILFIFO:
561 1.18 mycroft return S_ISFIFO(s.st_mode);
562 1.13 jtc case FILSOCK:
563 1.18 mycroft return S_ISSOCK(s.st_mode);
564 1.18 mycroft case FILSYM:
565 1.18 mycroft return S_ISLNK(s.st_mode);
566 1.13 jtc case FILSUID:
567 1.18 mycroft return (s.st_mode & S_ISUID) != 0;
568 1.13 jtc case FILSGID:
569 1.18 mycroft return (s.st_mode & S_ISGID) != 0;
570 1.13 jtc case FILSTCK:
571 1.18 mycroft return (s.st_mode & S_ISVTX) != 0;
572 1.13 jtc case FILGZ:
573 1.18 mycroft return s.st_size > (off_t)0;
574 1.13 jtc case FILUID:
575 1.13 jtc return s.st_uid == geteuid();
576 1.13 jtc case FILGID:
577 1.13 jtc return s.st_gid == getegid();
578 1.13 jtc default:
579 1.13 jtc return 1;
580 1.13 jtc }
581 1.1 glass }
582 1.1 glass
583 1.31 christos #define VTOC(x) (const unsigned char *)((const struct t_op *)x)->op_text
584 1.31 christos
585 1.31 christos static int
586 1.31 christos compare1(const void *va, const void *vb)
587 1.31 christos {
588 1.31 christos const unsigned char *a = va;
589 1.31 christos const unsigned char *b = VTOC(vb);
590 1.31 christos
591 1.31 christos return a[0] - b[0];
592 1.31 christos }
593 1.31 christos
594 1.31 christos static int
595 1.31 christos compare2(const void *va, const void *vb)
596 1.31 christos {
597 1.31 christos const unsigned char *a = va;
598 1.31 christos const unsigned char *b = VTOC(vb);
599 1.31 christos int z = a[0] - b[0];
600 1.31 christos
601 1.31 christos return z ? z : (a[1] - b[1]);
602 1.31 christos }
603 1.31 christos
604 1.31 christos static struct t_op const *
605 1.31 christos findop(const char *s)
606 1.31 christos {
607 1.31 christos if (s[0] == '-') {
608 1.31 christos if (s[1] == '\0')
609 1.31 christos return NULL;
610 1.31 christos if (s[2] == '\0')
611 1.31 christos return bsearch(s + 1, mop2, __arraycount(mop2),
612 1.31 christos sizeof(*mop2), compare1);
613 1.31 christos else if (s[3] != '\0')
614 1.31 christos return NULL;
615 1.31 christos else
616 1.31 christos return bsearch(s + 1, mop3, __arraycount(mop3),
617 1.31 christos sizeof(*mop3), compare2);
618 1.31 christos } else {
619 1.31 christos if (s[1] == '\0')
620 1.31 christos return bsearch(s, cop, __arraycount(cop), sizeof(*cop),
621 1.31 christos compare1);
622 1.31 christos else if (strcmp(s, cop2[0].op_text) == 0)
623 1.31 christos return cop2;
624 1.31 christos else
625 1.31 christos return NULL;
626 1.31 christos }
627 1.31 christos }
628 1.31 christos
629 1.13 jtc static enum token
630 1.23 wiz t_lex(char *s)
631 1.1 glass {
632 1.24 wiz struct t_op const *op;
633 1.24 wiz
634 1.31 christos if (s == NULL) {
635 1.26 simonb t_wp_op = NULL;
636 1.13 jtc return EOI;
637 1.13 jtc }
638 1.31 christos
639 1.31 christos if ((op = findop(s)) != NULL) {
640 1.31 christos if (!((op->op_type == UNOP && isoperand()) ||
641 1.31 christos (op->op_num == LPAREN && *(t_wp+1) == 0))) {
642 1.13 jtc t_wp_op = op;
643 1.13 jtc return op->op_num;
644 1.13 jtc }
645 1.13 jtc }
646 1.26 simonb t_wp_op = NULL;
647 1.13 jtc return OPERAND;
648 1.21 kleink }
649 1.21 kleink
650 1.21 kleink static int
651 1.23 wiz isoperand(void)
652 1.21 kleink {
653 1.24 wiz struct t_op const *op;
654 1.24 wiz char *s, *t;
655 1.21 kleink
656 1.21 kleink if ((s = *(t_wp+1)) == 0)
657 1.21 kleink return 1;
658 1.21 kleink if ((t = *(t_wp+2)) == 0)
659 1.21 kleink return 0;
660 1.31 christos if ((op = findop(s)) != NULL)
661 1.31 christos return op->op_type == BINOP && (t[0] != ')' || t[1] != '\0');
662 1.21 kleink return 0;
663 1.1 glass }
664 1.1 glass
665 1.13 jtc /* atoi with error detection */
666 1.1 glass static int
667 1.23 wiz getn(const char *s)
668 1.1 glass {
669 1.6 alm char *p;
670 1.6 alm long r;
671 1.1 glass
672 1.6 alm errno = 0;
673 1.13 jtc r = strtol(s, &p, 10);
674 1.13 jtc
675 1.6 alm if (errno != 0)
676 1.22 christos error("%s: out of range", s);
677 1.13 jtc
678 1.20 christos while (isspace((unsigned char)*p))
679 1.22 christos p++;
680 1.13 jtc
681 1.13 jtc if (*p)
682 1.22 christos error("%s: bad number", s);
683 1.13 jtc
684 1.13 jtc return (int) r;
685 1.1 glass }
686 1.1 glass
687 1.13 jtc static int
688 1.26 simonb newerf(const char *f1, const char *f2)
689 1.1 glass {
690 1.13 jtc struct stat b1, b2;
691 1.13 jtc
692 1.26 simonb return (stat(f1, &b1) == 0 &&
693 1.26 simonb stat(f2, &b2) == 0 &&
694 1.13 jtc b1.st_mtime > b2.st_mtime);
695 1.1 glass }
696 1.1 glass
697 1.13 jtc static int
698 1.26 simonb olderf(const char *f1, const char *f2)
699 1.1 glass {
700 1.13 jtc struct stat b1, b2;
701 1.13 jtc
702 1.26 simonb return (stat(f1, &b1) == 0 &&
703 1.26 simonb stat(f2, &b2) == 0 &&
704 1.13 jtc b1.st_mtime < b2.st_mtime);
705 1.1 glass }
706 1.1 glass
707 1.13 jtc static int
708 1.26 simonb equalf(const char *f1, const char *f2)
709 1.13 jtc {
710 1.13 jtc struct stat b1, b2;
711 1.1 glass
712 1.26 simonb return (stat(f1, &b1) == 0 &&
713 1.26 simonb stat(f2, &b2) == 0 &&
714 1.13 jtc b1.st_dev == b2.st_dev &&
715 1.13 jtc b1.st_ino == b2.st_ino);
716 1.1 glass }
717