blob: 0bce66e6fa720c24d710c8d27fec3432dad1969c [file] [log] [blame]
Erik Andersen13456d12000-03-16 08:09:57 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen5e1189e2000-04-15 16:34:54 +00003 * test implementation for busybox
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
5 * Copyright (c) by a whole pile of folks:
6 *
7 * test(1); version 7-like -- author Erik Baalbergen
8 * modified by Eric Gisin to be used as built-in.
9 * modified by Arnold Robbins to add SVR3 compatibility
10 * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
11 * modified by J.T. Conklin for NetBSD.
12 * modified by Herbert Xu to be used as built-in in ash.
13 * modified by Erik Andersen <andersee@debian.org> to be used
14 * in busybox.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 * Original copyright notice states:
31 * "This program is in the Public Domain."
32 */
33
Erik Andersen13456d12000-03-16 08:09:57 +000034#include <sys/types.h>
Erik Andersen13456d12000-03-16 08:09:57 +000035#include <unistd.h>
36#include <ctype.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <string.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000040#include "busybox.h"
Erik Andersen13456d12000-03-16 08:09:57 +000041
42/* test(1) accepts the following grammar:
43 oexpr ::= aexpr | aexpr "-o" oexpr ;
44 aexpr ::= nexpr | nexpr "-a" aexpr ;
45 nexpr ::= primary | "!" primary
46 primary ::= unary-operator operand
47 | operand binary-operator operand
48 | operand
49 | "(" oexpr ")"
50 ;
51 unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
52 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
53
54 binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
55 "-nt"|"-ot"|"-ef";
56 operand ::= <any legal UNIX file name>
57*/
58
59enum token {
60 EOI,
61 FILRD,
62 FILWR,
63 FILEX,
64 FILEXIST,
65 FILREG,
66 FILDIR,
67 FILCDEV,
68 FILBDEV,
69 FILFIFO,
70 FILSOCK,
71 FILSYM,
72 FILGZ,
73 FILTT,
74 FILSUID,
75 FILSGID,
76 FILSTCK,
77 FILNT,
78 FILOT,
79 FILEQ,
80 FILUID,
81 FILGID,
82 STREZ,
83 STRNZ,
84 STREQ,
85 STRNE,
86 STRLT,
87 STRGT,
88 INTEQ,
89 INTNE,
90 INTGE,
91 INTGT,
92 INTLE,
93 INTLT,
94 UNOT,
95 BAND,
96 BOR,
97 LPAREN,
98 RPAREN,
99 OPERAND
100};
101
102enum token_types {
103 UNOP,
104 BINOP,
105 BUNOP,
106 BBINOP,
107 PAREN
108};
109
Eric Andersen92d23242001-03-19 23:49:41 +0000110static const struct t_op {
Erik Andersen13456d12000-03-16 08:09:57 +0000111 const char *op_text;
112 short op_num, op_type;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000113} ops[] = {
114 {
115 "-r", FILRD, UNOP}, {
116 "-w", FILWR, UNOP}, {
117 "-x", FILEX, UNOP}, {
118 "-e", FILEXIST, UNOP}, {
119 "-f", FILREG, UNOP}, {
120 "-d", FILDIR, UNOP}, {
121 "-c", FILCDEV, UNOP}, {
122 "-b", FILBDEV, UNOP}, {
123 "-p", FILFIFO, UNOP}, {
124 "-u", FILSUID, UNOP}, {
125 "-g", FILSGID, UNOP}, {
126 "-k", FILSTCK, UNOP}, {
127 "-s", FILGZ, UNOP}, {
128 "-t", FILTT, UNOP}, {
129 "-z", STREZ, UNOP}, {
130 "-n", STRNZ, UNOP}, {
131 "-h", FILSYM, UNOP}, /* for backwards compat */
132 {
133 "-O", FILUID, UNOP}, {
134 "-G", FILGID, UNOP}, {
135 "-L", FILSYM, UNOP}, {
136 "-S", FILSOCK, UNOP}, {
137 "=", STREQ, BINOP}, {
138 "!=", STRNE, BINOP}, {
139 "<", STRLT, BINOP}, {
140 ">", STRGT, BINOP}, {
141 "-eq", INTEQ, BINOP}, {
142 "-ne", INTNE, BINOP}, {
143 "-ge", INTGE, BINOP}, {
144 "-gt", INTGT, BINOP}, {
145 "-le", INTLE, BINOP}, {
146 "-lt", INTLT, BINOP}, {
147 "-nt", FILNT, BINOP}, {
148 "-ot", FILOT, BINOP}, {
149 "-ef", FILEQ, BINOP}, {
150 "!", UNOT, BUNOP}, {
151 "-a", BAND, BBINOP}, {
152 "-o", BOR, BBINOP}, {
153 "(", LPAREN, PAREN}, {
154 ")", RPAREN, PAREN}, {
155 0, 0, 0}
Erik Andersen13456d12000-03-16 08:09:57 +0000156};
157
Matt Kraaie6e81832000-12-30 07:46:23 +0000158static char **t_wp;
159static struct t_op const *t_wp_op;
Erik Andersen13456d12000-03-16 08:09:57 +0000160static gid_t *group_array = NULL;
161static int ngroups;
162
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000163static enum token t_lex(char *s);
Eric Andersen74400cc2001-10-18 04:11:39 +0000164static int oexpr(enum token n);
165static int aexpr(enum token n);
166static int nexpr(enum token n);
167static int binop(void);
168static int primary(enum token n);
169static int filstat(char *nm, enum token mode);
170static int getn(const char *s);
171static int newerf(const char *f1, const char *f2);
172static int olderf(const char *f1, const char *f2);
173static int equalf(const char *f1, const char *f2);
174static void syntax(const char *op, const char *msg);
175static int test_eaccess(char *path, int mode);
176static int is_a_group_member(gid_t gid);
177static void initialize_group_array(void);
Erik Andersen13456d12000-03-16 08:09:57 +0000178
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000179extern int test_main(int argc, char **argv)
Erik Andersen13456d12000-03-16 08:09:57 +0000180{
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000181 int res;
Erik Andersen13456d12000-03-16 08:09:57 +0000182
Manuel Novoa III cad53642003-03-19 09:13:01 +0000183 if (strcmp(bb_applet_name, "[") == 0) {
Erik Andersen13456d12000-03-16 08:09:57 +0000184 if (strcmp(argv[--argc], "]"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000185 bb_error_msg_and_die("missing ]");
Erik Andersen13456d12000-03-16 08:09:57 +0000186 argv[argc] = NULL;
187 }
Erik Andersen13456d12000-03-16 08:09:57 +0000188 /* Implement special cases from POSIX.2, section 4.62.4 */
189 switch (argc) {
190 case 1:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000191 exit(1);
Erik Andersen13456d12000-03-16 08:09:57 +0000192 case 2:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000193 exit(*argv[1] == '\0');
Erik Andersen13456d12000-03-16 08:09:57 +0000194 case 3:
195 if (argv[1][0] == '!' && argv[1][1] == '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000196 exit(!(*argv[2] == '\0'));
Erik Andersen13456d12000-03-16 08:09:57 +0000197 }
198 break;
199 case 4:
200 if (argv[1][0] != '!' || argv[1][1] != '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000201 if (t_lex(argv[2]), t_wp_op && t_wp_op->op_type == BINOP) {
Erik Andersen13456d12000-03-16 08:09:57 +0000202 t_wp = &argv[1];
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000203 exit(binop() == 0);
Erik Andersen13456d12000-03-16 08:09:57 +0000204 }
205 }
206 break;
207 case 5:
208 if (argv[1][0] == '!' && argv[1][1] == '\0') {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000209 if (t_lex(argv[3]), t_wp_op && t_wp_op->op_type == BINOP) {
Erik Andersen13456d12000-03-16 08:09:57 +0000210 t_wp = &argv[2];
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000211 exit(!(binop() == 0));
Erik Andersen13456d12000-03-16 08:09:57 +0000212 }
213 }
214 break;
215 }
216
217 t_wp = &argv[1];
218 res = !oexpr(t_lex(*t_wp));
219
220 if (*t_wp != NULL && *++t_wp != NULL)
221 syntax(*t_wp, "unknown operand");
222
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000223 return (res);
Erik Andersen13456d12000-03-16 08:09:57 +0000224}
225
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000226static void syntax(const char *op, const char *msg)
Erik Andersen13456d12000-03-16 08:09:57 +0000227{
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000228 if (op && *op) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000229 bb_error_msg_and_die("%s: %s", op, msg);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000230 } else {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000231 bb_error_msg_and_die("%s", msg);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000232 }
Erik Andersen13456d12000-03-16 08:09:57 +0000233}
234
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000235static int oexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000236{
237 int res;
238
239 res = aexpr(n);
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000240 if (t_lex(*++t_wp) == BOR) {
Erik Andersen13456d12000-03-16 08:09:57 +0000241 return oexpr(t_lex(*++t_wp)) || res;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000242 }
Erik Andersen13456d12000-03-16 08:09:57 +0000243 t_wp--;
244 return res;
245}
246
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000247static int aexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000248{
249 int res;
250
251 res = nexpr(n);
252 if (t_lex(*++t_wp) == BAND)
253 return aexpr(t_lex(*++t_wp)) && res;
254 t_wp--;
255 return res;
256}
257
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000258static int nexpr(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000259{
260 if (n == UNOT)
261 return !nexpr(t_lex(*++t_wp));
262 return primary(n);
263}
264
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000265static int primary(enum token n)
Erik Andersen13456d12000-03-16 08:09:57 +0000266{
267 int res;
268
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000269 if (n == EOI) {
Erik Andersen13456d12000-03-16 08:09:57 +0000270 syntax(NULL, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000271 }
Erik Andersen13456d12000-03-16 08:09:57 +0000272 if (n == LPAREN) {
273 res = oexpr(t_lex(*++t_wp));
274 if (t_lex(*++t_wp) != RPAREN)
275 syntax(NULL, "closing paren expected");
276 return res;
277 }
278 if (t_wp_op && t_wp_op->op_type == UNOP) {
279 /* unary expression */
280 if (*++t_wp == NULL)
281 syntax(t_wp_op->op_text, "argument expected");
282 switch (n) {
283 case STREZ:
284 return strlen(*t_wp) == 0;
285 case STRNZ:
286 return strlen(*t_wp) != 0;
287 case FILTT:
288 return isatty(getn(*t_wp));
289 default:
290 return filstat(*t_wp, n);
291 }
292 }
293
294 if (t_lex(t_wp[1]), t_wp_op && t_wp_op->op_type == BINOP) {
295 return binop();
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000296 }
Erik Andersen13456d12000-03-16 08:09:57 +0000297
298 return strlen(*t_wp) > 0;
299}
300
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000301static int binop()
Erik Andersen13456d12000-03-16 08:09:57 +0000302{
303 const char *opnd1, *opnd2;
304 struct t_op const *op;
305
306 opnd1 = *t_wp;
307 (void) t_lex(*++t_wp);
308 op = t_wp_op;
309
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000310 if ((opnd2 = *++t_wp) == (char *) 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000311 syntax(op->op_text, "argument expected");
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000312
Erik Andersen13456d12000-03-16 08:09:57 +0000313 switch (op->op_num) {
314 case STREQ:
315 return strcmp(opnd1, opnd2) == 0;
316 case STRNE:
317 return strcmp(opnd1, opnd2) != 0;
318 case STRLT:
319 return strcmp(opnd1, opnd2) < 0;
320 case STRGT:
321 return strcmp(opnd1, opnd2) > 0;
322 case INTEQ:
323 return getn(opnd1) == getn(opnd2);
324 case INTNE:
325 return getn(opnd1) != getn(opnd2);
326 case INTGE:
327 return getn(opnd1) >= getn(opnd2);
328 case INTGT:
329 return getn(opnd1) > getn(opnd2);
330 case INTLE:
331 return getn(opnd1) <= getn(opnd2);
332 case INTLT:
333 return getn(opnd1) < getn(opnd2);
334 case FILNT:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000335 return newerf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000336 case FILOT:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000337 return olderf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000338 case FILEQ:
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000339 return equalf(opnd1, opnd2);
Erik Andersen13456d12000-03-16 08:09:57 +0000340 }
341 /* NOTREACHED */
342 return 1;
343}
344
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000345static int filstat(char *nm, enum token mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000346{
347 struct stat s;
Eric Andersenfad04fd2000-07-14 06:49:52 +0000348 unsigned int i;
Erik Andersen13456d12000-03-16 08:09:57 +0000349
350 if (mode == FILSYM) {
351#ifdef S_IFLNK
352 if (lstat(nm, &s) == 0) {
353 i = S_IFLNK;
354 goto filetype;
355 }
356#endif
357 return 0;
358 }
359
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000360 if (stat(nm, &s) != 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000361 return 0;
362
363 switch (mode) {
364 case FILRD:
365 return test_eaccess(nm, R_OK) == 0;
366 case FILWR:
367 return test_eaccess(nm, W_OK) == 0;
368 case FILEX:
369 return test_eaccess(nm, X_OK) == 0;
370 case FILEXIST:
371 return 1;
372 case FILREG:
373 i = S_IFREG;
374 goto filetype;
375 case FILDIR:
376 i = S_IFDIR;
377 goto filetype;
378 case FILCDEV:
379 i = S_IFCHR;
380 goto filetype;
381 case FILBDEV:
382 i = S_IFBLK;
383 goto filetype;
384 case FILFIFO:
385#ifdef S_IFIFO
386 i = S_IFIFO;
387 goto filetype;
388#else
389 return 0;
390#endif
391 case FILSOCK:
392#ifdef S_IFSOCK
393 i = S_IFSOCK;
394 goto filetype;
395#else
396 return 0;
397#endif
398 case FILSUID:
399 i = S_ISUID;
400 goto filebit;
401 case FILSGID:
402 i = S_ISGID;
403 goto filebit;
404 case FILSTCK:
405 i = S_ISVTX;
406 goto filebit;
407 case FILGZ:
408 return s.st_size > 0L;
409 case FILUID:
410 return s.st_uid == geteuid();
411 case FILGID:
412 return s.st_gid == getegid();
413 default:
414 return 1;
415 }
416
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000417 filetype:
Erik Andersen13456d12000-03-16 08:09:57 +0000418 return ((s.st_mode & S_IFMT) == i);
419
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000420 filebit:
Erik Andersen13456d12000-03-16 08:09:57 +0000421 return ((s.st_mode & i) != 0);
422}
423
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000424static enum token t_lex(char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000425{
426 struct t_op const *op = ops;
427
428 if (s == 0) {
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000429 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000430 return EOI;
431 }
432 while (op->op_text) {
433 if (strcmp(s, op->op_text) == 0) {
434 t_wp_op = op;
435 return op->op_num;
436 }
437 op++;
438 }
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000439 t_wp_op = (struct t_op *) 0;
Erik Andersen13456d12000-03-16 08:09:57 +0000440 return OPERAND;
441}
442
443/* atoi with error detection */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000444static int getn(const char *s)
Erik Andersen13456d12000-03-16 08:09:57 +0000445{
446 char *p;
447 long r;
448
449 errno = 0;
450 r = strtol(s, &p, 10);
451
452 if (errno != 0)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000453 bb_error_msg_and_die("%s: out of range", s);
Erik Andersen13456d12000-03-16 08:09:57 +0000454
455 while (isspace(*p))
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000456 p++;
457
Erik Andersen13456d12000-03-16 08:09:57 +0000458 if (*p)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000459 bb_error_msg_and_die("%s: bad number", s);
Erik Andersen13456d12000-03-16 08:09:57 +0000460
461 return (int) r;
462}
463
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000464static int newerf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000465{
466 struct stat b1, b2;
467
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000468 return (stat(f1, &b1) == 0 &&
469 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000470}
471
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000472static int olderf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000473{
474 struct stat b1, b2;
475
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000476 return (stat(f1, &b1) == 0 &&
477 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
Erik Andersen13456d12000-03-16 08:09:57 +0000478}
479
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000480static int equalf(const char *f1, const char *f2)
Erik Andersen13456d12000-03-16 08:09:57 +0000481{
482 struct stat b1, b2;
483
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000484 return (stat(f1, &b1) == 0 &&
485 stat(f2, &b2) == 0 &&
486 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
Erik Andersen13456d12000-03-16 08:09:57 +0000487}
488
489/* Do the same thing access(2) does, but use the effective uid and gid,
490 and don't make the mistake of telling root that any file is
491 executable. */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000492static int test_eaccess(char *path, int mode)
Erik Andersen13456d12000-03-16 08:09:57 +0000493{
494 struct stat st;
Eric Andersenfad04fd2000-07-14 06:49:52 +0000495 unsigned int euid = geteuid();
Erik Andersen13456d12000-03-16 08:09:57 +0000496
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000497 if (stat(path, &st) < 0)
Erik Andersen13456d12000-03-16 08:09:57 +0000498 return (-1);
499
500 if (euid == 0) {
501 /* Root can read or write any file. */
502 if (mode != X_OK)
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000503 return (0);
Erik Andersen13456d12000-03-16 08:09:57 +0000504
505 /* Root can execute any file that has any one of the execute
506 bits set. */
507 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
508 return (0);
509 }
510
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000511 if (st.st_uid == euid) /* owner */
Erik Andersen13456d12000-03-16 08:09:57 +0000512 mode <<= 6;
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000513 else if (is_a_group_member(st.st_gid))
Erik Andersen13456d12000-03-16 08:09:57 +0000514 mode <<= 3;
515
516 if (st.st_mode & mode)
517 return (0);
518
519 return (-1);
520}
521
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000522static void initialize_group_array()
Erik Andersen13456d12000-03-16 08:09:57 +0000523{
524 ngroups = getgroups(0, NULL);
Matt Kraai322ae932000-09-13 02:46:14 +0000525 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
Erik Andersen13456d12000-03-16 08:09:57 +0000526 getgroups(ngroups, group_array);
527}
528
529/* Return non-zero if GID is one that we have in our groups list. */
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000530static int is_a_group_member(gid_t gid)
Erik Andersen13456d12000-03-16 08:09:57 +0000531{
532 register int i;
533
534 /* Short-circuit if possible, maybe saving a call to getgroups(). */
535 if (gid == getgid() || gid == getegid())
536 return (1);
537
538 if (ngroups == 0)
Glenn L McGrathacfc0d82002-08-23 06:05:11 +0000539 initialize_group_array();
Erik Andersen13456d12000-03-16 08:09:57 +0000540
541 /* Search through the list looking for GID. */
542 for (i = 0; i < ngroups; i++)
543 if (gid == group_array[i])
544 return (1);
545
546 return (0);
547}