Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/bltins/test.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* David Korn <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* test expression
23
* [ expression ]
24
*
25
* David Korn
26
* AT&T Labs
27
*
28
*/
29
30
31
#include "defs.h"
32
#include <error.h>
33
#include <ls.h>
34
#include <regex.h>
35
#include "io.h"
36
#include "terminal.h"
37
#include "test.h"
38
#include "builtins.h"
39
#include "FEATURE/externs"
40
#include "FEATURE/poll"
41
#include <tmx.h>
42
43
#if !_lib_setregid
44
# undef _lib_setreuid
45
#endif /* _lib_setregid */
46
47
#ifdef S_ISSOCK
48
# if _pipe_socketpair
49
# if _socketpair_shutdown_mode
50
# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino&&((p)->st_mode&(S_IRUSR|S_IWUSR))!=(S_IRUSR|S_IWUSR))
51
# else
52
# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
53
# endif
54
# else
55
# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode)||S_ISSOCK((p)->st_mode)&&(p)->st_ino)
56
# endif
57
# define isasock(f,p) (test_stat(f,p)>=0&&S_ISSOCK((p)->st_mode))
58
#else
59
# define isapipe(f,p) (test_stat(f,p)>=0&&S_ISFIFO((p)->st_mode))
60
# define isasock(f,p) (0)
61
#endif
62
63
#define permission(a,f) (sh_access(a,f)==0)
64
static time_t test_time(const char*, const char*);
65
static int test_stat(const char*, struct stat*);
66
static int test_mode(const char*);
67
68
/* single char string compare */
69
#define c_eq(a,c) (*a==c && *(a+1)==0)
70
/* two character string compare */
71
#define c2_eq(a,c1,c2) (*a==c1 && *(a+1)==c2 && *(a+2)==0)
72
73
struct test
74
{
75
Shell_t *sh;
76
int ap;
77
int ac;
78
char **av;
79
};
80
81
static char *nxtarg(struct test*,int);
82
static int expr(struct test*,int);
83
static int e3(struct test*);
84
85
static int test_strmatch(Shell_t *shp,const char *str, const char *pat)
86
{
87
regoff_t match[2*(MATCH_MAX+1)],n;
88
register int c, m=0;
89
register const char *cp=pat;
90
while(c = *cp++)
91
{
92
if(c=='(')
93
m++;
94
if(c=='\\' && *cp)
95
cp++;
96
}
97
if(m)
98
m++;
99
else
100
match[0] = 0;
101
if(m > elementsof(match)/2)
102
m = elementsof(match)/2;
103
n = strgrpmatch(str, pat, match, m, STR_GROUP|STR_MAXIMAL|STR_LEFT|STR_RIGHT);
104
if(m==0 && n==1)
105
match[1] = strlen(str);
106
if(n)
107
sh_setmatch(shp, str, -1, n, match, 0);
108
return(n);
109
}
110
111
int b_test(int argc, char *argv[],Shbltin_t *context)
112
{
113
struct test tdata;
114
register char *cp = argv[0];
115
register int not;
116
tdata.sh = context->shp;
117
tdata.av = argv;
118
tdata.ap = 1;
119
if(c_eq(cp,'['))
120
{
121
cp = argv[--argc];
122
if(!c_eq(cp, ']'))
123
errormsg(SH_DICT,ERROR_exit(2),e_missing,"']'");
124
}
125
if(argc <= 1)
126
return(1);
127
cp = argv[1];
128
if(c_eq(cp,'(') && argc<=6 && c_eq(argv[argc-1],')'))
129
{
130
/* special case ( binop ) to conform with standard */
131
if(!(argc==4 && (not=sh_lookup(cp=argv[2],shtab_testops))))
132
{
133
cp = (++argv)[1];
134
argc -= 2;
135
}
136
}
137
not = c_eq(cp,'!');
138
/* posix portion for test */
139
switch(argc)
140
{
141
case 5:
142
if(!not)
143
break;
144
argv++;
145
/* fall through */
146
case 4:
147
{
148
register int op = sh_lookup(cp=argv[2],shtab_testops);
149
if(op&TEST_BINOP)
150
break;
151
if(!op)
152
{
153
if(argc==5)
154
break;
155
if(not && cp[0]=='-' && cp[2]==0)
156
return(test_unop(tdata.sh,cp[1],argv[3])!=0);
157
else if(argv[1][0]=='-' && argv[1][2]==0)
158
return(!test_unop(tdata.sh,argv[1][1],cp));
159
else if(not && c_eq(argv[2],'!'))
160
return(*argv[3]==0);
161
errormsg(SH_DICT,ERROR_exit(2),e_badop,cp);
162
}
163
return(test_binop(tdata.sh,op,argv[1],argv[3])^(argc!=5));
164
}
165
case 3:
166
if(not)
167
return(*argv[2]!=0);
168
if(cp[0] != '-' || cp[2] || cp[1]=='?')
169
{
170
if(cp[0]=='-' && (cp[1]=='-' || cp[1]=='?') &&
171
strcmp(argv[2],"--")==0)
172
{
173
char *av[3];
174
av[0] = argv[0];
175
av[1] = argv[1];
176
av[2] = 0;
177
optget(av,sh_opttest);
178
errormsg(SH_DICT,ERROR_usage(2), "%s",opt_info.arg);
179
return(2);
180
}
181
break;
182
}
183
return(!test_unop(tdata.sh,cp[1],argv[2]));
184
case 2:
185
return(*cp==0);
186
}
187
tdata.ac = argc;
188
return(!expr(&tdata,0));
189
}
190
191
/*
192
* evaluate a test expression.
193
* flag is 0 on outer level
194
* flag is 1 when in parenthesis
195
* flag is 2 when evaluating -a
196
*/
197
static int expr(struct test *tp,register int flag)
198
{
199
register int r;
200
register char *p;
201
r = e3(tp);
202
while(tp->ap < tp->ac)
203
{
204
p = nxtarg(tp,0);
205
/* check for -o and -a */
206
if(flag && c_eq(p,')'))
207
{
208
tp->ap--;
209
break;
210
}
211
if(*p=='-' && *(p+2)==0)
212
{
213
if(*++p == 'o')
214
{
215
if(flag==2)
216
{
217
tp->ap--;
218
break;
219
}
220
r |= expr(tp,3);
221
continue;
222
}
223
else if(*p == 'a')
224
{
225
r &= expr(tp,2);
226
continue;
227
}
228
}
229
if(flag==0)
230
break;
231
errormsg(SH_DICT,ERROR_exit(2),e_badsyntax);
232
}
233
return(r);
234
}
235
236
static char *nxtarg(struct test *tp,int mt)
237
{
238
if(tp->ap >= tp->ac)
239
{
240
if(mt)
241
{
242
tp->ap++;
243
return(0);
244
}
245
errormsg(SH_DICT,ERROR_exit(2),e_argument);
246
}
247
return(tp->av[tp->ap++]);
248
}
249
250
251
static int e3(struct test *tp)
252
{
253
register char *arg, *cp;
254
register int op;
255
char *binop;
256
arg=nxtarg(tp,0);
257
if(arg && c_eq(arg, '!'))
258
return(!e3(tp));
259
if(c_eq(arg, '('))
260
{
261
op = expr(tp,1);
262
cp = nxtarg(tp,0);
263
if(!cp || !c_eq(cp, ')'))
264
errormsg(SH_DICT,ERROR_exit(2),e_missing,"')'");
265
return(op);
266
}
267
cp = nxtarg(tp,1);
268
if(cp!=0 && (c_eq(cp,'=') || c2_eq(cp,'!','=')))
269
goto skip;
270
if(c2_eq(arg,'-','t'))
271
{
272
if(cp)
273
{
274
op = strtol(cp,&binop, 10);
275
return(*binop?0:tty_check(op));
276
}
277
else
278
{
279
/* test -t with no arguments */
280
tp->ap--;
281
return(tty_check(1));
282
}
283
}
284
if(*arg=='-' && arg[2]==0)
285
{
286
op = arg[1];
287
if(!cp)
288
{
289
/* for backward compatibility with new flags */
290
if(op==0 || !strchr(test_opchars+10,op))
291
return(1);
292
errormsg(SH_DICT,ERROR_exit(2),e_argument);
293
}
294
if(strchr(test_opchars,op))
295
return(test_unop(tp->sh,op,cp));
296
}
297
if(!cp)
298
{
299
tp->ap--;
300
return(*arg!=0);
301
}
302
skip:
303
op = sh_lookup(binop=cp,shtab_testops);
304
if(!(op&TEST_BINOP))
305
cp = nxtarg(tp,0);
306
if(!op)
307
errormsg(SH_DICT,ERROR_exit(2),e_badop,binop);
308
if(op==TEST_AND || op==TEST_OR)
309
tp->ap--;
310
return(test_binop(tp->sh,op,arg,cp));
311
}
312
313
int test_unop(Shell_t *shp,register int op,register const char *arg)
314
{
315
struct stat statb;
316
int f;
317
switch(op)
318
{
319
case 'r':
320
return(permission(arg, R_OK));
321
case 'w':
322
return(permission(arg, W_OK));
323
case 'x':
324
return(permission(arg, X_OK));
325
case 'V':
326
#if SHOPT_FS_3D
327
{
328
register int offset = staktell();
329
if(stat(arg,&statb)<0 || !S_ISREG(statb.st_mode))
330
return(0);
331
/* add trailing / */
332
stakputs(arg);
333
stakputc('/');
334
stakputc(0);
335
arg = (const char*)stakptr(offset);
336
stakseek(offset);
337
/* FALL THRU */
338
}
339
#else
340
return(0);
341
#endif /* SHOPT_FS_3D */
342
case 'd':
343
return(test_stat(arg,&statb)>=0 && S_ISDIR(statb.st_mode));
344
case 'c':
345
return(test_stat(arg,&statb)>=0 && S_ISCHR(statb.st_mode));
346
case 'b':
347
return(test_stat(arg,&statb)>=0 && S_ISBLK(statb.st_mode));
348
case 'f':
349
return(test_stat(arg,&statb)>=0 && S_ISREG(statb.st_mode));
350
case 'u':
351
return(test_mode(arg)&S_ISUID);
352
case 'g':
353
return(test_mode(arg)&S_ISGID);
354
case 'k':
355
#ifdef S_ISVTX
356
return(test_mode(arg)&S_ISVTX);
357
#else
358
return(0);
359
#endif /* S_ISVTX */
360
#if SHOPT_TEST_L
361
case 'l':
362
#endif
363
case 'L':
364
case 'h': /* undocumented, and hopefully will disappear */
365
if(*arg==0 || arg[strlen(arg)-1]=='/' || lstat(arg,&statb)<0)
366
return(0);
367
return(S_ISLNK(statb.st_mode));
368
369
case 'C':
370
#ifdef S_ISCTG
371
return(test_stat(arg,&statb)>=0 && S_ISCTG(statb.st_mode));
372
#else
373
return(0);
374
#endif /* S_ISCTG */
375
case 'H':
376
#ifdef S_ISCDF
377
{
378
register int offset = staktell();
379
if(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode))
380
return(1);
381
stakputs(arg);
382
stakputc('+');
383
stakputc(0);
384
arg = (const char*)stakptr(offset);
385
stakseek(offset);
386
return(test_stat(arg,&statb)>=0 && S_ISCDF(statb.st_mode));
387
}
388
#else
389
return(0);
390
#endif /* S_ISCDF */
391
392
case 'S':
393
return(isasock(arg,&statb));
394
case 'N':
395
return(test_stat(arg,&statb)>=0 && tmxgetmtime(&statb) > tmxgetatime(&statb));
396
case 'p':
397
return(isapipe(arg,&statb));
398
case 'n':
399
return(*arg != 0);
400
case 'z':
401
return(*arg == 0);
402
case 's':
403
sfsync(sfstdout);
404
case 'O':
405
case 'G':
406
if(*arg==0 || test_stat(arg,&statb)<0)
407
return(0);
408
if(op=='s')
409
return(statb.st_size>0);
410
else if(op=='O')
411
return(statb.st_uid==shp->gd->userid);
412
return(statb.st_gid==shp->gd->groupid);
413
case 'a':
414
case 'e':
415
if(memcmp(arg,"/dev/",5)==0 && sh_open(arg,O_NONBLOCK))
416
return(1);
417
return(permission(arg, F_OK));
418
case 'o':
419
f=1;
420
if(*arg=='?')
421
return(sh_lookopt(arg+1,&f)>0);
422
op = sh_lookopt(arg,&f);
423
return(op && (f==(sh_isoption(op)!=0)));
424
case 't':
425
{
426
char *last;
427
op = strtol(arg,&last, 10);
428
return(*last?0:tty_check(op));
429
}
430
case 'v':
431
case 'R':
432
{
433
Namval_t *np;
434
Namarr_t *ap;
435
int isref;
436
if(!(np = nv_open(arg,shp->var_tree,NV_VARNAME|NV_NOFAIL|NV_NOADD|NV_NOREF)))
437
return(0);
438
isref = nv_isref(np);
439
if(op=='R')
440
return(isref);
441
if(isref)
442
{
443
if(np->nvalue.cp)
444
np = nv_refnode(np);
445
else
446
return(0);
447
448
}
449
if(ap = nv_arrayptr(np))
450
return(nv_arrayisset(np,ap));
451
return(!nv_isnull(np) || nv_isattr(np,NV_INTEGER));
452
}
453
default:
454
{
455
static char a[3] = "-?";
456
a[1]= op;
457
errormsg(SH_DICT,ERROR_exit(2),e_badop,a);
458
/* NOTREACHED */
459
return(0);
460
}
461
}
462
}
463
464
int test_binop(Shell_t *shp,register int op,const char *left,const char *right)
465
{
466
register double lnum,rnum;
467
if(op&TEST_ARITH)
468
{
469
while(*left=='0')
470
left++;
471
while(*right=='0')
472
right++;
473
lnum = sh_arith(shp,left);
474
rnum = sh_arith(shp,right);
475
}
476
switch(op)
477
{
478
/* op must be one of the following values */
479
case TEST_AND:
480
case TEST_OR:
481
return(*left!=0);
482
case TEST_PEQ:
483
return(test_strmatch(shp, left, right));
484
case TEST_PNE:
485
return(!test_strmatch(shp, left, right));
486
case TEST_SGT:
487
return(strcoll(left, right)>0);
488
case TEST_SLT:
489
return(strcoll(left, right)<0);
490
case TEST_SEQ:
491
return(strcmp(left, right)==0);
492
case TEST_SNE:
493
return(strcmp(left, right)!=0);
494
case TEST_EF:
495
return(test_inode(left,right));
496
case TEST_NT:
497
return(test_time(left,right)>0);
498
case TEST_OT:
499
return(test_time(left,right)<0);
500
case TEST_EQ:
501
return(lnum==rnum);
502
case TEST_NE:
503
return(lnum!=rnum);
504
case TEST_GT:
505
return(lnum>rnum);
506
case TEST_LT:
507
return(lnum<rnum);
508
case TEST_GE:
509
return(lnum>=rnum);
510
case TEST_LE:
511
return(lnum<=rnum);
512
}
513
/* NOTREACHED */
514
return(0);
515
}
516
517
/*
518
* returns the modification time of f1 - modification time of f2
519
*/
520
521
static time_t test_time(const char *file1,const char *file2)
522
{
523
Time_t t1, t2;
524
struct stat statb1,statb2;
525
int r=test_stat(file2,&statb2);
526
if(test_stat(file1,&statb1)<0)
527
return(r<0?0:-1);
528
if(r<0)
529
return(1);
530
t1 = tmxgetmtime(&statb1);
531
t2 = tmxgetmtime(&statb2);
532
if (t1 > t2)
533
return(1);
534
if (t1 < t2)
535
return(-1);
536
return(0);
537
}
538
539
/*
540
* return true if inode of two files are the same
541
*/
542
543
int test_inode(const char *file1,const char *file2)
544
{
545
struct stat stat1,stat2;
546
if(test_stat(file1,&stat1)>=0 && test_stat(file2,&stat2)>=0)
547
if(stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino)
548
return(1);
549
return(0);
550
}
551
552
553
/*
554
* This version of access checks against effective uid/gid
555
* The static buffer statb is shared with test_mode.
556
*/
557
558
int sh_access(register const char *name, register int mode)
559
{
560
Shell_t *shp = sh_getinterp();
561
struct stat statb;
562
if(*name==0)
563
return(-1);
564
if(sh_isdevfd(name))
565
return(sh_ioaccess((int)strtol(name+8, (char**)0, 10),mode));
566
/* can't use access function for execute permission with root */
567
if(mode==X_OK && shp->gd->euserid==0)
568
goto skip;
569
if(shp->gd->userid==shp->gd->euserid && shp->gd->groupid==shp->gd->egroupid)
570
return(access(name,mode));
571
#ifdef _lib_setreuid
572
/* swap the real uid to effective, check access then restore */
573
/* first swap real and effective gid, if different */
574
if(shp->gd->groupid==shp->gd->euserid || setregid(shp->gd->egroupid,shp->gd->groupid)==0)
575
{
576
/* next swap real and effective uid, if needed */
577
if(shp->gd->userid==shp->gd->euserid || setreuid(shp->gd->euserid,shp->gd->userid)==0)
578
{
579
mode = access(name,mode);
580
/* restore ids */
581
if(shp->gd->userid!=shp->gd->euserid)
582
setreuid(shp->gd->userid,shp->gd->euserid);
583
if(shp->gd->groupid!=shp->gd->egroupid)
584
setregid(shp->gd->groupid,shp->gd->egroupid);
585
return(mode);
586
}
587
else if(shp->gd->groupid!=shp->gd->egroupid)
588
setregid(shp->gd->groupid,shp->gd->egroupid);
589
}
590
#endif /* _lib_setreuid */
591
skip:
592
if(test_stat(name, &statb) == 0)
593
{
594
if(mode == F_OK)
595
return(mode);
596
else if(shp->gd->euserid == 0)
597
{
598
if(!S_ISREG(statb.st_mode) || mode!=X_OK)
599
return(0);
600
/* root needs execute permission for someone */
601
mode = (S_IXUSR|S_IXGRP|S_IXOTH);
602
}
603
else if(shp->gd->euserid == statb.st_uid)
604
mode <<= 6;
605
else if(shp->gd->egroupid == statb.st_gid)
606
mode <<= 3;
607
#ifdef _lib_getgroups
608
/* on some systems you can be in several groups */
609
else
610
{
611
static int maxgroups;
612
gid_t *groups;
613
register int n;
614
if(maxgroups==0)
615
{
616
/* first time */
617
if((maxgroups=getgroups(0,(gid_t*)0)) <= 0)
618
{
619
/* pre-POSIX system */
620
maxgroups=NGROUPS_MAX;
621
}
622
}
623
groups = (gid_t*)stakalloc((maxgroups+1)*sizeof(gid_t));
624
n = getgroups(maxgroups,groups);
625
while(--n >= 0)
626
{
627
if(groups[n] == statb.st_gid)
628
{
629
mode <<= 3;
630
break;
631
}
632
}
633
}
634
# endif /* _lib_getgroups */
635
if(statb.st_mode & mode)
636
return(0);
637
}
638
return(-1);
639
}
640
641
/*
642
* Return the mode bits of file <file>
643
* If <file> is null, then the previous stat buffer is used.
644
* The mode bits are zero if the file doesn't exist.
645
*/
646
647
static int test_mode(register const char *file)
648
{
649
struct stat statb;
650
statb.st_mode = 0;
651
if(file && (*file==0 || test_stat(file,&statb)<0))
652
return(0);
653
return(statb.st_mode);
654
}
655
656
/*
657
* do an fstat() for /dev/fd/n, otherwise stat()
658
*/
659
static int test_stat(const char *name,struct stat *buff)
660
{
661
if(*name==0)
662
{
663
errno = ENOENT;
664
return(-1);
665
}
666
if(sh_isdevfd(name))
667
return(fstat((int)strtol(name+8, (char**)0, 10),buff));
668
else
669
return(stat(name,buff));
670
}
671
672