Subversion Repositories HelenOS

Rev

Rev 2714 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2714 cejka 1
/*  $NetBSD: test.c,v 1.22 2000/04/09 23:24:59 christos 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
#ifndef lint
15
__RCSID("$NetBSD: test.c,v 1.22 2000/04/09 23:24:59 christos Exp $");
16
#endif
17
 
18
#include <sys/types.h>
19
#include <sys/stat.h>
20
#include <unistd.h>
21
#include <ctype.h>
22
#include <errno.h>
23
#include <stdio.h>
24
#include <stdlib.h>
25
#include <string.h>
26
#include <err.h>
27
#ifdef __STDC__
28
#include <stdarg.h>
29
#else
30
#include <varargs.h>
31
#endif
32
 
33
/* test(1) accepts the following grammar:
34
    oexpr   ::= aexpr | aexpr "-o" oexpr ;
35
    aexpr   ::= nexpr | nexpr "-a" aexpr ;
36
    nexpr   ::= primary | "!" primary
37
    primary ::= unary-operator operand
38
        | operand binary-operator operand
39
        | operand
40
        | "(" oexpr ")"
41
        ;
42
    unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
43
        "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
44
 
45
    binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
46
            "-nt"|"-ot"|"-ef";
47
    operand ::= <any legal UNIX file name>
48
*/
49
 
50
enum token {
51
    EOI,
52
    FILRD,
53
    FILWR,
54
    FILEX,
55
    FILEXIST,
56
    FILREG,
57
    FILDIR,
58
    FILCDEV,
59
    FILBDEV,
60
    FILFIFO,
61
    FILSOCK,
62
    FILSYM,
63
    FILGZ,
64
    FILTT,
65
    FILSUID,
66
    FILSGID,
67
    FILSTCK,
68
    FILNT,
69
    FILOT,
70
    FILEQ,
71
    FILUID,
72
    FILGID,
73
    STREZ,
74
    STRNZ,
75
    STREQ,
76
    STRNE,
77
    STRLT,
78
    STRGT,
79
    INTEQ,
80
    INTNE,
81
    INTGE,
82
    INTGT,
83
    INTLE,
84
    INTLT,
85
    UNOT,
86
    BAND,
87
    BOR,
88
    LPAREN,
89
    RPAREN,
90
    OPERAND
91
};
92
 
93
enum token_types {
94
    UNOP,
95
    BINOP,
96
    BUNOP,
97
    BBINOP,
98
    PAREN
99
};
100
 
101
static struct t_op {
102
    const char *op_text;
103
    short op_num, op_type;
104
} const ops [] = {
105
    {"-r",  FILRD,  UNOP},
106
    {"-w",  FILWR,  UNOP},
107
    {"-x",  FILEX,  UNOP},
108
    {"-e",  FILEXIST,UNOP},
109
    {"-f",  FILREG, UNOP},
110
    {"-d",  FILDIR, UNOP},
111
    {"-c",  FILCDEV,UNOP},
112
    {"-b",  FILBDEV,UNOP},
113
    {"-p",  FILFIFO,UNOP},
114
    {"-u",  FILSUID,UNOP},
115
    {"-g",  FILSGID,UNOP},
116
    {"-k",  FILSTCK,UNOP},
117
    {"-s",  FILGZ,  UNOP},
118
    {"-t",  FILTT,  UNOP},
119
    {"-z",  STREZ,  UNOP},
120
    {"-n",  STRNZ,  UNOP},
121
    {"-h",  FILSYM, UNOP},      /* for backwards compat */
122
    {"-O",  FILUID, UNOP},
123
    {"-G",  FILGID, UNOP},
124
    {"-L",  FILSYM, UNOP},
125
    {"-S",  FILSOCK,UNOP},
126
    {"=",   STREQ,  BINOP},
127
    {"!=",  STRNE,  BINOP},
128
    {"<",   STRLT,  BINOP},
129
    {">",   STRGT,  BINOP},
130
    {"-eq", INTEQ,  BINOP},
131
    {"-ne", INTNE,  BINOP},
132
    {"-ge", INTGE,  BINOP},
133
    {"-gt", INTGT,  BINOP},
134
    {"-le", INTLE,  BINOP},
135
    {"-lt", INTLT,  BINOP},
136
    {"-nt", FILNT,  BINOP},
137
    {"-ot", FILOT,  BINOP},
138
    {"-ef", FILEQ,  BINOP},
139
    {"!",   UNOT,   BUNOP},
140
    {"-a",  BAND,   BBINOP},
141
    {"-o",  BOR,    BBINOP},
142
    {"(",   LPAREN, PAREN},
143
    {")",   RPAREN, PAREN},
144
    {0, 0,  0}
145
};
146
 
147
static char **t_wp;
148
static struct t_op const *t_wp_op;
149
static gid_t *group_array = NULL;
150
static int ngroups;
151
 
152
static void syntax __P((const char *, const char *));
153
static int oexpr __P((enum token));
154
static int aexpr __P((enum token));
155
static int nexpr __P((enum token));
156
static int primary __P((enum token));
157
static int binop __P((void));
158
static int filstat __P((char *, enum token));
159
static enum token t_lex __P((char *));
160
static int isoperand __P((void));
161
static int getn __P((const char *));
162
static int newerf __P((const char *, const char *));
163
static int olderf __P((const char *, const char *));
164
static int equalf __P((const char *, const char *));
165
static int test_eaccess();
166
static int bash_group_member();
167
static void initialize_group_array();
168
 
169
#if defined(SHELL)
170
extern void error __P((const char *, ...)) __attribute__((__noreturn__));
171
#else
172
static void error __P((const char *, ...)) __attribute__((__noreturn__));
173
 
174
static void
175
#ifdef __STDC__
176
error(const char *msg, ...)
177
#else
178
error(va_alist)
179
    va_dcl
180
#endif
181
{
182
    va_list ap;
183
#ifndef __STDC__
184
    const char *msg;
185
 
186
    va_start(ap);
187
    msg = va_arg(ap, const char *);
188
#else
189
    va_start(ap, msg);
190
#endif
191
    verrx(2, msg, ap);
192
    /*NOTREACHED*/
193
    va_end(ap);
194
}
195
#endif
196
 
197
#ifdef SHELL
198
int testcmd __P((int, char **));
199
 
200
int
201
testcmd(argc, argv)
202
    int argc;
203
    char **argv;
204
#else
205
int main __P((int, char **));
206
 
207
int
208
main(argc, argv)
209
    int argc;
210
    char **argv;
211
#endif
212
{
213
    int res;
214
 
215
 
216
    if (strcmp(argv[0], "[") == 0) {
217
        if (strcmp(argv[--argc], "]"))
218
            error("missing ]");
219
        argv[argc] = NULL;
220
    }
221
 
222
    if (argc < 2)
223
        return 1;
224
 
225
    t_wp = &argv[1];
226
    res = !oexpr(t_lex(*t_wp));
227
 
228
    if (*t_wp != NULL && *++t_wp != NULL)
229
        syntax(*t_wp, "unexpected operator");
230
 
231
    return res;
232
}
233
 
234
static void
235
syntax(op, msg)
236
    const char  *op;
237
    const char  *msg;
238
{
239
    if (op && *op)
240
        error("%s: %s", op, msg);
241
    else
242
        error("%s", msg);
243
}
244
 
245
static int
246
oexpr(n)
247
    enum token n;
248
{
249
    int res;
250
 
251
    res = aexpr(n);
252
    if (t_lex(*++t_wp) == BOR)
253
        return oexpr(t_lex(*++t_wp)) || res;
254
    t_wp--;
255
    return res;
256
}
257
 
258
static int
259
aexpr(n)
260
    enum token n;
261
{
262
    int res;
263
 
264
    res = nexpr(n);
265
    if (t_lex(*++t_wp) == BAND)
266
        return aexpr(t_lex(*++t_wp)) && res;
267
    t_wp--;
268
    return res;
269
}
270
 
271
static int
272
nexpr(n)
273
    enum token n;           /* token */
274
{
275
    if (n == UNOT)
276
        return !nexpr(t_lex(*++t_wp));
277
    return primary(n);
278
}
279
 
280
static int
281
primary(n)
282
    enum token n;
283
{
284
    enum token nn;
285
    int res;
286
 
287
    if (n == EOI)
288
        return 0;       /* missing expression */
289
    if (n == LPAREN) {
290
        if ((nn = t_lex(*++t_wp)) == RPAREN)
291
            return 0;   /* missing expression */
292
        res = oexpr(nn);
293
        if (t_lex(*++t_wp) != RPAREN)
294
            syntax(NULL, "closing paren expected");
295
        return res;
296
    }
297
    if (t_wp_op && t_wp_op->op_type == UNOP) {
298
        /* unary expression */
299
        if (*++t_wp == NULL)
300
            syntax(t_wp_op->op_text, "argument expected");
301
        switch (n) {
302
        case STREZ:
303
            return strlen(*t_wp) == 0;
304
        case STRNZ:
305
            return strlen(*t_wp) != 0;
306
        case FILTT:
307
            return isatty(getn(*t_wp));
308
        default:
309
            return filstat(*t_wp, n);
310
        }
311
    }
312
 
313
    if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
314
        return binop();
315
    }    
316
 
317
    return strlen(*t_wp) > 0;
318
}
319
 
320
static int
321
binop()
322
{
323
    const char *opnd1, *opnd2;
324
    struct t_op const *op;
325
 
326
    opnd1 = *t_wp;
327
    (void) t_lex(*++t_wp);
328
    op = t_wp_op;
329
 
330
    if ((opnd2 = *++t_wp) == (char *)0)
331
        syntax(op->op_text, "argument expected");
332
 
333
    switch (op->op_num) {
334
    case STREQ:
335
        return strcmp(opnd1, opnd2) == 0;
336
    case STRNE:
337
        return strcmp(opnd1, opnd2) != 0;
338
    case STRLT:
339
        return strcmp(opnd1, opnd2) < 0;
340
    case STRGT:
341
        return strcmp(opnd1, opnd2) > 0;
342
    case INTEQ:
343
        return getn(opnd1) == getn(opnd2);
344
    case INTNE:
345
        return getn(opnd1) != getn(opnd2);
346
    case INTGE:
347
        return getn(opnd1) >= getn(opnd2);
348
    case INTGT:
349
        return getn(opnd1) > getn(opnd2);
350
    case INTLE:
351
        return getn(opnd1) <= getn(opnd2);
352
    case INTLT:
353
        return getn(opnd1) < getn(opnd2);
354
    case FILNT:
355
        return newerf (opnd1, opnd2);
356
    case FILOT:
357
        return olderf (opnd1, opnd2);
358
    case FILEQ:
359
        return equalf (opnd1, opnd2);
360
    default:
361
        abort();
362
        /* NOTREACHED */
363
    }
364
}
365
 
366
static int
367
filstat(nm, mode)
368
    char *nm;
369
    enum token mode;
370
{
371
    struct stat s;
372
 
373
    if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s))
374
        return 0;
375
 
376
    switch (mode) {
377
    case FILRD:
378
        return test_eaccess(nm, R_OK) == 0;
379
    case FILWR:
380
        return test_eaccess(nm, W_OK) == 0;
381
    case FILEX:
382
        return test_eaccess(nm, X_OK) == 0;
383
    case FILEXIST:
384
        return 1;
385
    case FILREG:
386
        return S_ISREG(s.st_mode);
387
    case FILDIR:
388
        return S_ISDIR(s.st_mode);
389
    case FILCDEV:
390
        return S_ISCHR(s.st_mode);
391
    case FILBDEV:
392
        return S_ISBLK(s.st_mode);
393
    case FILFIFO:
394
        return S_ISFIFO(s.st_mode);
395
    case FILSOCK:
396
        return S_ISSOCK(s.st_mode);
397
    case FILSYM:
398
        return S_ISLNK(s.st_mode);
399
    case FILSUID:
400
        return (s.st_mode & S_ISUID) != 0;
401
    case FILSGID:
402
        return (s.st_mode & S_ISGID) != 0;
403
    case FILSTCK:
404
        return (s.st_mode & S_ISVTX) != 0;
405
    case FILGZ:
406
        return s.st_size > (off_t)0;
407
    case FILUID:
408
        return s.st_uid == geteuid();
409
    case FILGID:
410
        return s.st_gid == getegid();
411
    default:
412
        return 1;
413
    }
414
}
415
 
416
static enum token
417
t_lex(s)
418
    char *s;
419
{
420
    struct t_op const *op = ops;
421
 
422
    if (s == 0) {
423
        t_wp_op = (struct t_op *)0;
424
        return EOI;
425
    }
426
    while (op->op_text) {
427
        if (strcmp(s, op->op_text) == 0) {
428
            if ((op->op_type == UNOP && isoperand()) ||
429
                (op->op_num == LPAREN && *(t_wp+1) == 0))
430
                break;
431
            t_wp_op = op;
432
            return op->op_num;
433
        }
434
        op++;
435
    }
436
    t_wp_op = (struct t_op *)0;
437
    return OPERAND;
438
}
439
 
440
static int
441
isoperand()
442
{
443
    struct t_op const *op = ops;
444
    char *s;
445
    char *t;
446
 
447
    if ((s  = *(t_wp+1)) == 0)
448
        return 1;
449
    if ((t = *(t_wp+2)) == 0)
450
        return 0;
451
    while (op->op_text) {
452
        if (strcmp(s, op->op_text) == 0)
453
                return op->op_type == BINOP &&
454
                    (t[0] != ')' || t[1] != '\0');
455
        op++;
456
    }
457
    return 0;
458
}
459
 
460
/* atoi with error detection */
461
static int
462
getn(s)
463
    const char *s;
464
{
465
    char *p;
466
    long r;
467
 
468
    errno = 0;
469
    r = strtol(s, &p, 10);
470
 
471
    if (errno != 0)
472
          error("%s: out of range", s);
473
 
474
    while (isspace((unsigned char)*p))
475
          p++;
476
 
477
    if (*p)
478
          error("%s: bad number", s);
479
 
480
    return (int) r;
481
}
482
 
483
static int
484
newerf (f1, f2)
485
const char *f1, *f2;
486
{
487
    struct stat b1, b2;
488
 
489
    return (stat (f1, &b1) == 0 &&
490
        stat (f2, &b2) == 0 &&
491
        b1.st_mtime > b2.st_mtime);
492
}
493
 
494
static int
495
olderf (f1, f2)
496
const char *f1, *f2;
497
{
498
    struct stat b1, b2;
499
 
500
    return (stat (f1, &b1) == 0 &&
501
        stat (f2, &b2) == 0 &&
502
        b1.st_mtime < b2.st_mtime);
503
}
504
 
505
static int
506
equalf (f1, f2)
507
const char *f1, *f2;
508
{
509
    struct stat b1, b2;
510
 
511
    return (stat (f1, &b1) == 0 &&
512
        stat (f2, &b2) == 0 &&
513
        b1.st_dev == b2.st_dev &&
514
        b1.st_ino == b2.st_ino);
515
}
516
 
517
/* Do the same thing access(2) does, but use the effective uid and gid,
518
   and don't make the mistake of telling root that any file is
519
   executable. */
520
static int
521
test_eaccess (path, mode)
522
char *path;
523
int mode;
524
{
525
    struct stat st;
526
    int euid = geteuid();
527
 
528
    if (stat (path, &st) < 0)
529
        return (-1);
530
 
531
    if (euid == 0) {
532
        /* Root can read or write any file. */
533
        if (mode != X_OK)
534
        return (0);
535
 
536
        /* Root can execute any file that has any one of the execute
537
           bits set. */
538
        if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
539
            return (0);
540
    }
541
 
542
    if (st.st_uid == euid)      /* owner */
543
        mode <<= 6;
544
    else if (bash_group_member (st.st_gid))
545
        mode <<= 3;
546
 
547
    if (st.st_mode & mode)
548
        return (0);
549
 
550
    return (-1);
551
}
552
 
553
static void
554
initialize_group_array ()
555
{
556
    ngroups = getgroups(0, NULL);
557
    group_array = malloc(ngroups * sizeof(gid_t));
558
    if (!group_array)
559
        error(strerror(ENOMEM));
560
    getgroups(ngroups, group_array);
561
}
562
 
563
/* Return non-zero if GID is one that we have in our groups list. */
564
static int
565
bash_group_member (gid)
566
gid_t gid;
567
{
568
    register int i;
569
 
570
    /* Short-circuit if possible, maybe saving a call to getgroups(). */
571
    if (gid == getgid() || gid == getegid())
572
        return (1);
573
 
574
    if (ngroups == 0)
575
        initialize_group_array ();
576
 
577
    /* Search through the list looking for GID. */
578
    for (i = 0; i < ngroups; i++)
579
        if (gid == group_array[i])
580
            return (1);
581
 
582
    return (0);
583
}