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