source: trunk/src/kash/bltin/test.c@ 1214

Last change on this file since 1214 was 1214, checked in by bird, 18 years ago

some more cleanup.

  • Property svn:eol-style set to native
File size: 9.3 KB
Line 
1/* $NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $ */
2
3/*
4 * test(1); version 7-like -- author Erik Baalbergen
5 * modified by Eric Gisin to be used as built-in.
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).
8 * modified by J.T. Conklin for NetBSD.
9 *
10 * This program is in the Public Domain.
11 */
12
13#if 0
14#ifndef lint
15__RCSID("$NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $");
16#endif
17#endif
18
19#include <sys/types.h>
20#include <ctype.h>
21#include <errno.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <stdarg.h>
26
27#include "error.h"
28#include "shinstance.h"
29
30
31/* test(1) accepts the following grammar:
32 oexpr ::= aexpr | aexpr "-o" oexpr ;
33 aexpr ::= nexpr | nexpr "-a" aexpr ;
34 nexpr ::= primary | "!" primary
35 primary ::= unary-operator operand
36 | operand binary-operator operand
37 | operand
38 | "(" oexpr ")"
39 ;
40 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
41 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
42
43 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
44 "-nt"|"-ot"|"-ef";
45 operand ::= <any legal UNIX file name>
46*/
47
48enum token {
49 EOI,
50 FILRD,
51 FILWR,
52 FILEX,
53 FILEXIST,
54 FILREG,
55 FILDIR,
56 FILCDEV,
57 FILBDEV,
58 FILFIFO,
59 FILSOCK,
60 FILSYM,
61 FILGZ,
62 FILTT,
63 FILSUID,
64 FILSGID,
65 FILSTCK,
66 FILNT,
67 FILOT,
68 FILEQ,
69 FILUID,
70 FILGID,
71 STREZ,
72 STRNZ,
73 STREQ,
74 STRNE,
75 STRLT,
76 STRGT,
77 INTEQ,
78 INTNE,
79 INTGE,
80 INTGT,
81 INTLE,
82 INTLT,
83 UNOT,
84 BAND,
85 BOR,
86 LPAREN,
87 RPAREN,
88 OPERAND
89};
90
91enum token_types {
92 UNOP,
93 BINOP,
94 BUNOP,
95 BBINOP,
96 PAREN
97};
98
99static struct t_op {
100 const char *op_text;
101 short op_num, op_type;
102} const ops [] = {
103 {"-r", FILRD, UNOP},
104 {"-w", FILWR, UNOP},
105 {"-x", FILEX, UNOP},
106 {"-e", FILEXIST,UNOP},
107 {"-f", FILREG, UNOP},
108 {"-d", FILDIR, UNOP},
109 {"-c", FILCDEV,UNOP},
110 {"-b", FILBDEV,UNOP},
111 {"-p", FILFIFO,UNOP},
112 {"-u", FILSUID,UNOP},
113 {"-g", FILSGID,UNOP},
114 {"-k", FILSTCK,UNOP},
115 {"-s", FILGZ, UNOP},
116 {"-t", FILTT, UNOP},
117 {"-z", STREZ, UNOP},
118 {"-n", STRNZ, UNOP},
119 {"-h", FILSYM, UNOP}, /* for backwards compat */
120 {"-O", FILUID, UNOP},
121 {"-G", FILGID, UNOP},
122 {"-L", FILSYM, UNOP},
123 {"-S", FILSOCK,UNOP},
124 {"=", STREQ, BINOP},
125 {"!=", STRNE, BINOP},
126 {"<", STRLT, BINOP},
127 {">", STRGT, BINOP},
128 {"-eq", INTEQ, BINOP},
129 {"-ne", INTNE, BINOP},
130 {"-ge", INTGE, BINOP},
131 {"-gt", INTGT, BINOP},
132 {"-le", INTLE, BINOP},
133 {"-lt", INTLT, BINOP},
134 {"-nt", FILNT, BINOP},
135 {"-ot", FILOT, BINOP},
136 {"-ef", FILEQ, BINOP},
137 {"!", UNOT, BUNOP},
138 {"-a", BAND, BBINOP},
139 {"-o", BOR, BBINOP},
140 {"(", LPAREN, PAREN},
141 {")", RPAREN, PAREN},
142 {0, 0, 0}
143};
144
145//static char **t_wp;
146//static struct t_op const *t_wp_op;
147
148static void syntax(shinstance *, const char *, const char *);
149static int oexpr(shinstance *, enum token);
150static int aexpr(shinstance *, enum token);
151static int nexpr(shinstance *, enum token);
152static int primary(shinstance *, enum token);
153static int binop(shinstance *);
154static int filstat(shinstance *, char *, enum token);
155static enum token t_lex(shinstance *, char *);
156static int isoperand(shinstance *);
157static int getn(shinstance *, const char *);
158static int newerf(shinstance *, const char *, const char *);
159static int olderf(shinstance *, const char *, const char *);
160static int equalf(shinstance *, const char *, const char *);
161
162
163int
164testcmd(shinstance *psh, int argc, char **argv)
165{
166 int res;
167
168 if (strcmp(argv[0], "[") == 0) {
169 if (strcmp(argv[--argc], "]"))
170 error(psh, "missing ]");
171 argv[argc] = NULL;
172 }
173
174 if (argc < 2)
175 return 1;
176
177 psh->t_wp_op = NULL;
178 psh->t_wp = &argv[1];
179 res = !oexpr(psh, t_lex(psh, *psh->t_wp));
180
181 if (*psh->t_wp != NULL && *++psh->t_wp != NULL)
182 syntax(psh, *psh->t_wp, "unexpected operator");
183
184 return res;
185}
186
187static void
188syntax(shinstance *psh, const char *op, const char *msg)
189{
190
191 if (op && *op)
192 error(psh, "%s: %s", op, msg);
193 else
194 error(psh, "%s", msg);
195}
196
197static int
198oexpr(shinstance *psh, enum token n)
199{
200 int res;
201
202 res = aexpr(psh, n);
203 if (t_lex(psh, *++psh->t_wp) == BOR)
204 return oexpr(psh, t_lex(psh, *++psh->t_wp)) || res;
205 psh->t_wp--;
206 return res;
207}
208
209static int
210aexpr(shinstance *psh, enum token n)
211{
212 int res;
213
214 res = nexpr(psh, n);
215 if (t_lex(psh, *++psh->t_wp) == BAND)
216 return aexpr(psh, t_lex(psh, *++psh->t_wp)) && res;
217 psh->t_wp--;
218 return res;
219}
220
221static int
222nexpr(shinstance *psh, enum token n)
223{
224
225 if (n == UNOT)
226 return !nexpr(psh, t_lex(psh, *++psh->t_wp));
227 return primary(psh, n);
228}
229
230static int
231primary(shinstance *psh, enum token n)
232{
233 enum token nn;
234 int res;
235
236 if (n == EOI)
237 return 0; /* missing expression */
238 if (n == LPAREN) {
239 if ((nn = t_lex(psh, *++psh->t_wp)) == RPAREN)
240 return 0; /* missing expression */
241 res = oexpr(psh, nn);
242 if (t_lex(psh, *++psh->t_wp) != RPAREN)
243 syntax(psh, NULL, "closing paren expected");
244 return res;
245 }
246 if (psh->t_wp_op && psh->t_wp_op->op_type == UNOP) {
247 /* unary expression */
248 if (*++psh->t_wp == NULL)
249 syntax(psh, psh->t_wp_op->op_text, "argument expected");
250 switch (n) {
251 case STREZ:
252 return strlen(*psh->t_wp) == 0;
253 case STRNZ:
254 return strlen(*psh->t_wp) != 0;
255 case FILTT:
256 return shfile_isatty(&psh->fdtab, getn(psh, *psh->t_wp));
257 default:
258 return filstat(psh, *psh->t_wp, n);
259 }
260 }
261
262 if (t_lex(psh, psh->t_wp[1]), psh->t_wp_op && psh->t_wp_op->op_type == BINOP) {
263 return binop(psh);
264 }
265
266 return strlen(*psh->t_wp) > 0;
267}
268
269static int
270binop(shinstance *psh)
271{
272 const char *opnd1, *opnd2;
273 struct t_op const *op;
274
275 opnd1 = *psh->t_wp;
276 (void) t_lex(psh, *++psh->t_wp);
277 op = psh->t_wp_op;
278
279 if ((opnd2 = *++psh->t_wp) == NULL)
280 syntax(psh, op->op_text, "argument expected");
281
282 switch (op->op_num) {
283 case STREQ:
284 return strcmp(opnd1, opnd2) == 0;
285 case STRNE:
286 return strcmp(opnd1, opnd2) != 0;
287 case STRLT:
288 return strcmp(opnd1, opnd2) < 0;
289 case STRGT:
290 return strcmp(opnd1, opnd2) > 0;
291 case INTEQ:
292 return getn(psh, opnd1) == getn(psh, opnd2);
293 case INTNE:
294 return getn(psh, opnd1) != getn(psh, opnd2);
295 case INTGE:
296 return getn(psh, opnd1) >= getn(psh, opnd2);
297 case INTGT:
298 return getn(psh, opnd1) > getn(psh, opnd2);
299 case INTLE:
300 return getn(psh, opnd1) <= getn(psh, opnd2);
301 case INTLT:
302 return getn(psh, opnd1) < getn(psh, opnd2);
303 case FILNT:
304 return newerf(psh, opnd1, opnd2);
305 case FILOT:
306 return olderf(psh, opnd1, opnd2);
307 case FILEQ:
308 return equalf(psh, opnd1, opnd2);
309 default:
310 abort();
311 /* NOTREACHED */
312 return -1;
313 }
314}
315
316static int
317filstat(shinstance *psh, char *nm, enum token mode)
318{
319 struct stat s;
320
321 if (mode == FILSYM
322 ? shfile_lstat(&psh->fdtab, nm, &s)
323 : shfile_stat(&psh->fdtab, nm, &s))
324 return 0;
325
326 switch (mode) {
327 case FILRD:
328 return shfile_access(&psh->fdtab, nm, R_OK) == 0;
329 case FILWR:
330 return shfile_access(&psh->fdtab, nm, W_OK) == 0;
331 case FILEX:
332 return shfile_access(&psh->fdtab, nm, X_OK) == 0;
333 case FILEXIST:
334 return shfile_access(&psh->fdtab, nm, F_OK) == 0;
335 case FILREG:
336 return S_ISREG(s.st_mode);
337 case FILDIR:
338 return S_ISDIR(s.st_mode);
339 case FILCDEV:
340#ifdef S_ISCHR
341 return S_ISCHR(s.st_mode);
342#else
343 return 0;
344#endif
345 case FILBDEV:
346#ifdef S_ISBLK
347 return S_ISBLK(s.st_mode);
348#else
349 return 0;
350#endif
351 case FILFIFO:
352#ifdef S_ISFIFO
353 return S_ISFIFO(s.st_mode);
354#else
355 return 0;
356#endif
357 case FILSOCK:
358#ifdef S_ISSOCK
359 return S_ISSOCK(s.st_mode);
360#else
361 return 0;
362#endif
363 case FILSYM:
364 return S_ISLNK(s.st_mode);
365 case FILSUID:
366 return (s.st_mode & S_ISUID) != 0;
367 case FILSGID:
368 return (s.st_mode & S_ISGID) != 0;
369 case FILSTCK:
370#ifdef S_ISVTX
371 return (s.st_mode & S_ISVTX) != 0;
372#else
373 return 0;
374#endif
375 case FILGZ:
376 return s.st_size > (off_t)0;
377 case FILUID:
378 return s.st_uid == sh_geteuid(psh);
379 case FILGID:
380 return s.st_gid == sh_getegid(psh);
381 default:
382 return 1;
383 }
384}
385
386static enum token
387t_lex(shinstance *psh, char *s)
388{
389 struct t_op const *op;
390
391 op = ops;
392
393 if (s == 0) {
394 psh->t_wp_op = NULL;
395 return EOI;
396 }
397 while (op->op_text) {
398 if (strcmp(s, op->op_text) == 0) {
399 if ((op->op_type == UNOP && isoperand(psh)) ||
400 (op->op_num == LPAREN && *(psh->t_wp+1) == 0))
401 break;
402 psh->t_wp_op = op;
403 return op->op_num;
404 }
405 op++;
406 }
407 psh->t_wp_op = NULL;
408 return OPERAND;
409}
410
411static int
412isoperand(shinstance *psh)
413{
414 struct t_op const *op;
415 char *s, *t;
416
417 op = ops;
418 if ((s = *(psh->t_wp+1)) == 0)
419 return 1;
420 if ((t = *(psh->t_wp+2)) == 0)
421 return 0;
422 while (op->op_text) {
423 if (strcmp(s, op->op_text) == 0)
424 return op->op_type == BINOP &&
425 (t[0] != ')' || t[1] != '\0');
426 op++;
427 }
428 return 0;
429}
430
431/* atoi with error detection */
432static int
433getn(shinstance *psh, const char *s)
434{
435 char *p;
436 long r;
437
438 errno = 0;
439 r = strtol(s, &p, 10);
440
441 if (errno != 0)
442 error(psh, "%s: out of range", s);
443
444 while (isspace((unsigned char)*p))
445 p++;
446
447 if (*p)
448 error(psh, "%s: bad number", s);
449
450 return (int) r;
451}
452
453static int
454newerf(shinstance *psh, const char *f1, const char *f2)
455{
456 struct stat b1, b2;
457
458 return (shfile_stat(&psh->fdtab, f1, &b1) == 0 &&
459 shfile_stat(&psh->fdtab, f2, &b2) == 0 &&
460 b1.st_mtime > b2.st_mtime);
461}
462
463static int
464olderf(shinstance *psh, const char *f1, const char *f2)
465{
466 struct stat b1, b2;
467
468 return (shfile_stat(&psh->fdtab, f1, &b1) == 0 &&
469 shfile_stat(&psh->fdtab, f2, &b2) == 0 &&
470 b1.st_mtime < b2.st_mtime);
471}
472
473static int
474equalf(shinstance *psh, const char *f1, const char *f2)
475{
476 struct stat b1, b2;
477
478 return (shfile_stat(&psh->fdtab, f1, &b1) == 0 &&
479 shfile_stat(&psh->fdtab, f2, &b2) == 0 &&
480 b1.st_dev == b2.st_dev &&
481 b1.st_ino == b2.st_ino);
482}
Note: See TracBrowser for help on using the repository browser.