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

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

keywords.

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