Statistics
| Branch: | Revision:

root / prex-0.9.0 / usr / bin / test / test.c @ 03e9c04a

History | View | Annotate | Download (8.15 KB)

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
#include <sys/cdefs.h>
14
#include <sys/stat.h>
15
#include <sys/types.h>
16

    
17
#include <ctype.h>
18
#include <err.h>
19
#include <errno.h>
20
#include <stdio.h>
21
#include <stdlib.h>
22
#include <string.h>
23
#include <unistd.h>
24
#include <stdarg.h>
25

    
26
#ifdef CMDBOX
27
#define main(argc, argv)        test_main(argc, argv)
28
#endif
29

    
30
/* test(1) accepts the following grammar:
31
        oexpr        ::= aexpr | aexpr "-o" oexpr ;
32
        aexpr        ::= nexpr | nexpr "-a" aexpr ;
33
        nexpr        ::= primary | "!" primary
34
        primary        ::= unary-operator operand
35
                | operand binary-operator operand
36
                | operand
37
                | "(" oexpr ")"
38
                ;
39
        unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
40
                "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
41

42
        binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
43
                        "-nt"|"-ot"|"-ef";
44
        operand ::= <any legal UNIX file name>
45
*/
46

    
47
enum token {
48
        EOI,
49
        FILRD,
50
        FILWR,
51
        FILEX,
52
        FILEXIST,
53
        FILREG,
54
        FILDIR,
55
        FILCDEV,
56
        FILBDEV,
57
        FILFIFO,
58
        FILSOCK,
59
        FILSYM,
60
        FILGZ,
61
        FILTT,
62
        FILSUID,
63
        FILSGID,
64
        FILSTCK,
65
        FILNT,
66
        FILOT,
67
        FILEQ,
68
        FILUID,
69
        FILGID,
70
        STREZ,
71
        STRNZ,
72
        STREQ,
73
        STRNE,
74
        STRLT,
75
        STRGT,
76
        INTEQ,
77
        INTNE,
78
        INTGE,
79
        INTGT,
80
        INTLE,
81
        INTLT,
82
        UNOT,
83
        BAND,
84
        BOR,
85
        LPAREN,
86
        RPAREN,
87
        OPERAND
88
};
89

    
90
enum token_types {
91
        UNOP,
92
        BINOP,
93
        BUNOP,
94
        BBINOP,
95
        PAREN
96
};
97

    
98
static struct t_op {
99
        const char *op_text;
100
        short op_num, op_type;
101
} const ops [] = {
102
        {"-r",        FILRD,        UNOP},
103
        {"-w",        FILWR,        UNOP},
104
        {"-x",        FILEX,        UNOP},
105
        {"-e",        FILEXIST,UNOP},
106
        {"-f",        FILREG,        UNOP},
107
        {"-d",        FILDIR,        UNOP},
108
        {"-c",        FILCDEV,UNOP},
109
        {"-b",        FILBDEV,UNOP},
110
        {"-p",        FILFIFO,UNOP},
111
        {"-u",        FILSUID,UNOP},
112
        {"-g",        FILSGID,UNOP},
113
        {"-k",        FILSTCK,UNOP},
114
        {"-s",        FILGZ,        UNOP},
115
        {"-t",        FILTT,        UNOP},
116
        {"-z",        STREZ,        UNOP},
117
        {"-n",        STRNZ,        UNOP},
118
        {"-h",        FILSYM,        UNOP},                /* for backwards compat */
119
        {"-O",        FILUID,        UNOP},
120
        {"-G",        FILGID,        UNOP},
121
        {"-L",        FILSYM,        UNOP},
122
        {"-S",        FILSOCK,UNOP},
123
        {"=",        STREQ,        BINOP},
124
        {"!=",        STRNE,        BINOP},
125
        {"<",        STRLT,        BINOP},
126
        {">",        STRGT,        BINOP},
127
        {"-eq",        INTEQ,        BINOP},
128
        {"-ne",        INTNE,        BINOP},
129
        {"-ge",        INTGE,        BINOP},
130
        {"-gt",        INTGT,        BINOP},
131
        {"-le",        INTLE,        BINOP},
132
        {"-lt",        INTLT,        BINOP},
133
        {"-nt",        FILNT,        BINOP},
134
        {"-ot",        FILOT,        BINOP},
135
        {"-ef",        FILEQ,        BINOP},
136
        {"!",        UNOT,        BUNOP},
137
        {"-a",        BAND,        BBINOP},
138
        {"-o",        BOR,        BBINOP},
139
        {"(",        LPAREN,        PAREN},
140
        {")",        RPAREN,        PAREN},
141
        {0,        0,        0}
142
};
143

    
144
static char **t_wp;
145
static struct t_op const *t_wp_op;
146

    
147
static void syntax(const char *, const char *);
148
static int oexpr(enum token);
149
static int aexpr(enum token);
150
static int nexpr(enum token);
151
static int primary(enum token);
152
static int binop(void);
153
static int filstat(char *, enum token);
154
static enum token t_lex(char *);
155
static int isoperand(void);
156
static int getn(const char *);
157
static int newerf(const char *, const char *);
158
static int olderf(const char *, const char *);
159
static int equalf(const char *, const char *);
160

    
161
static void error(const char *, ...);
162

    
163
static void
164
error(const char *msg, ...)
165
{
166
        va_list ap;
167

    
168
        va_start(ap, msg);
169
        verrx(2, msg, ap);
170
        va_end(ap);
171
        /*NOTREACHED*/
172
}
173

    
174
int
175
main(int argc, char *argv[])
176
{
177
        int res;
178

    
179
        if (strcmp(argv[0], "[") == 0) {
180
                if (strcmp(argv[--argc], "]"))
181
                        error("missing ]");
182
                argv[argc] = NULL;
183
        }
184

    
185
        if (argc < 2)
186
                return 1;
187

    
188
        t_wp = &argv[1];
189
        res = !oexpr(t_lex(*t_wp));
190

    
191
        if (*t_wp != NULL && *++t_wp != NULL)
192
                syntax(*t_wp, "unexpected operator");
193

    
194
        return res;
195
}
196

    
197
static void
198
syntax(const char *op, const char *msg)
199
{
200

    
201
        if (op && *op)
202
                error("%s: %s", op, msg);
203
        else
204
                error("%s", msg);
205
}
206

    
207
static int
208
oexpr(enum token n)
209
{
210
        int res;
211

    
212
        res = aexpr(n);
213
        if (t_lex(*++t_wp) == BOR)
214
                return oexpr(t_lex(*++t_wp)) || res;
215
        t_wp--;
216
        return res;
217
}
218

    
219
static int
220
aexpr(enum token n)
221
{
222
        int res;
223

    
224
        res = nexpr(n);
225
        if (t_lex(*++t_wp) == BAND)
226
                return aexpr(t_lex(*++t_wp)) && res;
227
        t_wp--;
228
        return res;
229
}
230

    
231
static int
232
nexpr(enum token n)
233
{
234

    
235
        if (n == UNOT)
236
                return !nexpr(t_lex(*++t_wp));
237
        return primary(n);
238
}
239

    
240
static int
241
primary(enum token n)
242
{
243
        enum token nn;
244
        int res;
245

    
246
        if (n == EOI)
247
                return 0;                /* missing expression */
248
        if (n == LPAREN) {
249
                if ((nn = t_lex(*++t_wp)) == RPAREN)
250
                        return 0;        /* missing expression */
251
                res = oexpr(nn);
252
                if (t_lex(*++t_wp) != RPAREN)
253
                        syntax(NULL, "closing paren expected");
254
                return res;
255
        }
256
        if (t_wp_op && t_wp_op->op_type == UNOP) {
257
                /* unary expression */
258
                if (*++t_wp == NULL)
259
                        syntax(t_wp_op->op_text, "argument expected");
260
                switch (n) {
261
                case STREZ:
262
                        return strlen(*t_wp) == 0;
263
                case STRNZ:
264
                        return strlen(*t_wp) != 0;
265
                case FILTT:
266
                        return isatty(getn(*t_wp));
267
                default:
268
                        return filstat(*t_wp, n);
269
                }
270
        }
271

    
272
        if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
273
                return binop();
274
        }
275

    
276
        return strlen(*t_wp) > 0;
277
}
278

    
279
static int
280
binop(void)
281
{
282
        const char *opnd1, *opnd2;
283
        struct t_op const *op;
284

    
285
        opnd1 = *t_wp;
286
        (void) t_lex(*++t_wp);
287
        op = t_wp_op;
288

    
289
        if ((opnd2 = *++t_wp) == NULL)
290
                syntax(op->op_text, "argument expected");
291

    
292
        switch (op->op_num) {
293
        case STREQ:
294
                return strcmp(opnd1, opnd2) == 0;
295
        case STRNE:
296
                return strcmp(opnd1, opnd2) != 0;
297
        case STRLT:
298
                return strcmp(opnd1, opnd2) < 0;
299
        case STRGT:
300
                return strcmp(opnd1, opnd2) > 0;
301
        case INTEQ:
302
                return getn(opnd1) == getn(opnd2);
303
        case INTNE:
304
                return getn(opnd1) != getn(opnd2);
305
        case INTGE:
306
                return getn(opnd1) >= getn(opnd2);
307
        case INTGT:
308
                return getn(opnd1) > getn(opnd2);
309
        case INTLE:
310
                return getn(opnd1) <= getn(opnd2);
311
        case INTLT:
312
                return getn(opnd1) < getn(opnd2);
313
        case FILNT:
314
                return newerf(opnd1, opnd2);
315
        case FILOT:
316
                return olderf(opnd1, opnd2);
317
        case FILEQ:
318
                return equalf(opnd1, opnd2);
319
        default:
320
                abort();
321
                /* NOTREACHED */
322
        }
323
}
324

    
325
static int
326
filstat(char *nm, enum token mode)
327
{
328
        struct stat s;
329

    
330
        if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
331
                return 0;
332

    
333
        switch (mode) {
334
        case FILRD:
335
                return access(nm, R_OK) == 0;
336
        case FILWR:
337
                return access(nm, W_OK) == 0;
338
        case FILEX:
339
                return access(nm, X_OK) == 0;
340
        case FILEXIST:
341
                return access(nm, F_OK) == 0;
342
        case FILREG:
343
                return S_ISREG(s.st_mode);
344
        case FILDIR:
345
                return S_ISDIR(s.st_mode);
346
        case FILCDEV:
347
                return S_ISCHR(s.st_mode);
348
        case FILBDEV:
349
                return S_ISBLK(s.st_mode);
350
        case FILFIFO:
351
                return S_ISFIFO(s.st_mode);
352
        case FILSOCK:
353
                return S_ISSOCK(s.st_mode);
354
        case FILSYM:
355
                return S_ISLNK(s.st_mode);
356
        case FILSUID:
357
                return (s.st_mode & S_ISUID) != 0;
358
        case FILSGID:
359
                return (s.st_mode & S_ISGID) != 0;
360
        case FILSTCK:
361
                return (s.st_mode & S_ISVTX) != 0;
362
        case FILGZ:
363
                return s.st_size > (off_t)0;
364
        case FILUID:
365
                return s.st_uid == geteuid();
366
        case FILGID:
367
                return s.st_gid == getegid();
368
        default:
369
                return 1;
370
        }
371
}
372

    
373
static enum token
374
t_lex(char *s)
375
{
376
        struct t_op const *op;
377

    
378
        op = ops;
379

    
380
        if (s == 0) {
381
                t_wp_op = NULL;
382
                return EOI;
383
        }
384
        while (op->op_text) {
385
                if (strcmp(s, op->op_text) == 0) {
386
                        if ((op->op_type == UNOP && isoperand()) ||
387
                            (op->op_num == LPAREN && *(t_wp+1) == 0))
388
                                break;
389
                        t_wp_op = op;
390
                        return op->op_num;
391
                }
392
                op++;
393
        }
394
        t_wp_op = NULL;
395
        return OPERAND;
396
}
397

    
398
static int
399
isoperand(void)
400
{
401
        struct t_op const *op;
402
        char *s, *t;
403

    
404
        op = ops;
405
        if ((s  = *(t_wp+1)) == 0)
406
                return 1;
407
        if ((t = *(t_wp+2)) == 0)
408
                return 0;
409
        while (op->op_text) {
410
                if (strcmp(s, op->op_text) == 0)
411
                            return op->op_type == BINOP &&
412
                                (t[0] != ')' || t[1] != '\0');
413
                op++;
414
        }
415
        return 0;
416
}
417

    
418
/* atoi with error detection */
419
static int
420
getn(const char *s)
421
{
422
        char *p;
423
        long r;
424

    
425
        errno = 0;
426
        r = strtol(s, &p, 10);
427

    
428
        if (errno != 0)
429
              error("%s: out of range", s);
430

    
431
        while (isspace((unsigned char)*p))
432
              p++;
433

    
434
        if (*p)
435
              error("%s: bad number", s);
436

    
437
        return (int) r;
438
}
439

    
440
static int
441
newerf(const char *f1, const char *f2)
442
{
443
        struct stat b1, b2;
444

    
445
        return (stat(f1, &b1) == 0 &&
446
                stat(f2, &b2) == 0 &&
447
                b1.st_mtime > b2.st_mtime);
448
}
449

    
450
static int
451
olderf(const char *f1, const char *f2)
452
{
453
        struct stat b1, b2;
454

    
455
        return (stat(f1, &b1) == 0 &&
456
                stat(f2, &b2) == 0 &&
457
                b1.st_mtime < b2.st_mtime);
458
}
459

    
460
static int
461
equalf(const char *f1, const char *f2)
462
{
463
        struct stat b1, b2;
464

    
465
        return (stat(f1, &b1) == 0 &&
466
                stat(f2, &b2) == 0 &&
467
                b1.st_dev == b2.st_dev &&
468
                b1.st_ino == b2.st_ino);
469
}