aboutsummaryrefslogtreecommitdiff
path: root/coreutils
diff options
context:
space:
mode:
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2007-11-16 12:20:30 +0000
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>2007-11-16 12:20:30 +0000
commita702457eac1d2b014f108f78605d7fb6424b5844 (patch)
tree00467733cd82e82073657f53a8a0aceb659cc2b2 /coreutils
parente8979889b4dc2a995c4136820952a8468a09f066 (diff)
downloadbusybox-w32-a702457eac1d2b014f108f78605d7fb6424b5844.tar.gz
busybox-w32-a702457eac1d2b014f108f78605d7fb6424b5844.tar.bz2
busybox-w32-a702457eac1d2b014f108f78605d7fb6424b5844.zip
- remove most of the forward declarations. No obj-code changes.
Diffstat (limited to 'coreutils')
-rw-r--r--coreutils/test.c485
1 files changed, 241 insertions, 244 deletions
diff --git a/coreutils/test.c b/coreutils/test.c
index 3c57cf418..0b94100c1 100644
--- a/coreutils/test.c
+++ b/coreutils/test.c
@@ -161,90 +161,7 @@ static gid_t *group_array;
161static int ngroups; 161static int ngroups;
162static jmp_buf leaving; 162static jmp_buf leaving;
163 163
164static enum token t_lex(char *s);
165static arith_t oexpr(enum token n);
166static arith_t aexpr(enum token n);
167static arith_t nexpr(enum token n);
168static int binop(void);
169static arith_t primary(enum token n); 164static arith_t primary(enum token n);
170static int filstat(char *nm, enum token mode);
171static arith_t getn(const char *s);
172/* UNUSED
173static int newerf(const char *f1, const char *f2);
174static int olderf(const char *f1, const char *f2);
175static int equalf(const char *f1, const char *f2);
176*/
177static int test_eaccess(char *path, int mode);
178static int is_a_group_member(gid_t gid);
179static void initialize_group_array(void);
180
181int test_main(int argc, char **argv)
182{
183 int res;
184 const char *arg0;
185 bool _off;
186
187 arg0 = bb_basename(argv[0]);
188 if (arg0[0] == '[') {
189 --argc;
190 if (!arg0[1]) { /* "[" ? */
191 if (NOT_LONE_CHAR(argv[argc], ']')) {
192 bb_error_msg("missing ]");
193 return 2;
194 }
195 } else { /* assuming "[[" */
196 if (strcmp(argv[argc], "]]") != 0) {
197 bb_error_msg("missing ]]");
198 return 2;
199 }
200 }
201 argv[argc] = NULL;
202 }
203
204 res = setjmp(leaving);
205 if (res)
206 return res;
207
208 /* resetting ngroups is probably unnecessary. it will
209 * force a new call to getgroups(), which prevents using
210 * group data fetched during a previous call. but the
211 * only way the group data could be stale is if there's
212 * been an intervening call to setgroups(), and this
213 * isn't likely in the case of a shell. paranoia
214 * prevails...
215 */
216 ngroups = 0;
217
218 /* Implement special cases from POSIX.2, section 4.62.4 */
219 if (argc == 1)
220 return 1;
221 if (argc == 2)
222 return *argv[1] == '\0';
223//assert(argc);
224 /* remember if we saw argc==4 which wants *no* '!' test */
225 _off = argc - 4;
226 if (_off ?
227 (LONE_CHAR(argv[1], '!'))
228 : (argv[1][0] != '!' || argv[1][1] != '\0'))
229 {
230 if (argc == 3)
231 return *argv[2] != '\0';
232
233 t_lex(argv[2 + _off]);
234 if (t_wp_op && t_wp_op->op_type == BINOP) {
235 t_wp = &argv[1 + _off];
236 return binop() == _off;
237 }
238 }
239 t_wp = &argv[1];
240 res = !oexpr(t_lex(*t_wp));
241
242 if (*t_wp != NULL && *++t_wp != NULL) {
243 bb_error_msg("%s: unknown operand", *t_wp);
244 return 2;
245 }
246 return res;
247}
248 165
249static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN; 166static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN;
250static void syntax(const char *op, const char *msg) 167static void syntax(const char *op, const char *msg)
@@ -257,70 +174,83 @@ static void syntax(const char *op, const char *msg)
257 longjmp(leaving, 2); 174 longjmp(leaving, 2);
258} 175}
259 176
260static arith_t oexpr(enum token n) 177/* atoi with error detection */
178//XXX: FIXME: duplicate of existing libbb function?
179static arith_t getn(const char *s)
261{ 180{
262 arith_t res; 181 char *p;
182#if ENABLE_FEATURE_TEST_64
183 long long r;
184#else
185 long r;
186#endif
263 187
264 res = aexpr(n); 188 errno = 0;
265 if (t_lex(*++t_wp) == BOR) { 189#if ENABLE_FEATURE_TEST_64
266 return oexpr(t_lex(*++t_wp)) || res; 190 r = strtoll(s, &p, 10);
267 } 191#else
268 t_wp--; 192 r = strtol(s, &p, 10);
269 return res; 193#endif
194
195 if (errno != 0)
196 syntax(s, "out of range");
197
198 if (*(skip_whitespace(p)))
199 syntax(s, "bad number");
200
201 return r;
270} 202}
271 203
272static arith_t aexpr(enum token n) 204/* UNUSED
205static int newerf(const char *f1, const char *f2)
273{ 206{
274 arith_t res; 207 struct stat b1, b2;
275 208
276 res = nexpr(n); 209 return (stat(f1, &b1) == 0 &&
277 if (t_lex(*++t_wp) == BAND) 210 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
278 return aexpr(t_lex(*++t_wp)) && res;
279 t_wp--;
280 return res;
281} 211}
282 212
283static arith_t nexpr(enum token n) 213static int olderf(const char *f1, const char *f2)
284{ 214{
285 if (n == UNOT) 215 struct stat b1, b2;
286 return !nexpr(t_lex(*++t_wp)); 216
287 return primary(n); 217 return (stat(f1, &b1) == 0 &&
218 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
288} 219}
289 220
290static arith_t primary(enum token n) 221static int equalf(const char *f1, const char *f2)
291{ 222{
292 arith_t res; 223 struct stat b1, b2;
293 224
294 if (n == EOI) { 225 return (stat(f1, &b1) == 0 &&
295 syntax(NULL, "argument expected"); 226 stat(f2, &b2) == 0 &&
296 } 227 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
297 if (n == LPAREN) { 228}
298 res = oexpr(t_lex(*++t_wp)); 229*/
299 if (t_lex(*++t_wp) != RPAREN)
300 syntax(NULL, "closing paren expected");
301 return res;
302 }
303 if (t_wp_op && t_wp_op->op_type == UNOP) {
304 /* unary expression */
305 if (*++t_wp == NULL)
306 syntax(t_wp_op->op_text, "argument expected");
307 if (n == STREZ)
308 return t_wp[0][0] == '\0';
309 if (n == STRNZ)
310 return t_wp[0][0] != '\0';
311 if (n == FILTT)
312 return isatty(getn(*t_wp));
313 return filstat(*t_wp, n);
314 }
315 230
316 t_lex(t_wp[1]); 231
317 if (t_wp_op && t_wp_op->op_type == BINOP) { 232static enum token t_lex(char *s)
318 return binop(); 233{
234 const struct t_op *op;
235
236 t_wp_op = NULL;
237 if (s == NULL) {
238 return EOI;
319 } 239 }
320 240
321 return t_wp[0][0] != '\0'; 241 op = ops;
242 do {
243 if (strcmp(s, op->op_text) == 0) {
244 t_wp_op = op;
245 return op->op_num;
246 }
247 op++;
248 } while (op < ops + ARRAY_SIZE(ops));
249
250 return OPERAND;
322} 251}
323 252
253
324static int binop(void) 254static int binop(void)
325{ 255{
326 const char *opnd1, *opnd2; 256 const char *opnd1, *opnd2;
@@ -381,6 +311,80 @@ static int binop(void)
381 return 1; /* NOTREACHED */ 311 return 1; /* NOTREACHED */
382} 312}
383 313
314
315static void initialize_group_array(void)
316{
317 ngroups = getgroups(0, NULL);
318 if (ngroups > 0) {
319 /* FIXME: ash tries so hard to not die on OOM,
320 * and we spoil it with just one xrealloc here */
321 /* We realloc, because test_main can be entered repeatedly by shell.
322 * Testcase (ash): 'while true; do test -x some_file; done'
323 * and watch top. (some_file must have owner != you) */
324 group_array = xrealloc(group_array, ngroups * sizeof(gid_t));
325 getgroups(ngroups, group_array);
326 }
327}
328
329
330/* Return non-zero if GID is one that we have in our groups list. */
331//XXX: FIXME: duplicate of existing libbb function?
332// see toplevel TODO file:
333// possible code duplication ingroup() and is_a_group_member()
334static int is_a_group_member(gid_t gid)
335{
336 int i;
337
338 /* Short-circuit if possible, maybe saving a call to getgroups(). */
339 if (gid == getgid() || gid == getegid())
340 return 1;
341
342 if (ngroups == 0)
343 initialize_group_array();
344
345 /* Search through the list looking for GID. */
346 for (i = 0; i < ngroups; i++)
347 if (gid == group_array[i])
348 return 1;
349
350 return 0;
351}
352
353
354/* Do the same thing access(2) does, but use the effective uid and gid,
355 and don't make the mistake of telling root that any file is
356 executable. */
357static int test_eaccess(char *path, int mode)
358{
359 struct stat st;
360 unsigned int euid = geteuid();
361
362 if (stat(path, &st) < 0)
363 return -1;
364
365 if (euid == 0) {
366 /* Root can read or write any file. */
367 if (mode != X_OK)
368 return 0;
369
370 /* Root can execute any file that has any one of the execute
371 bits set. */
372 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
373 return 0;
374 }
375
376 if (st.st_uid == euid) /* owner */
377 mode <<= 6;
378 else if (is_a_group_member(st.st_gid))
379 mode <<= 3;
380
381 if (st.st_mode & mode)
382 return 0;
383
384 return -1;
385}
386
387
384static int filstat(char *nm, enum token mode) 388static int filstat(char *nm, enum token mode)
385{ 389{
386 struct stat s; 390 struct stat s;
@@ -453,147 +457,140 @@ static int filstat(char *nm, enum token mode)
453 return 1; /* NOTREACHED */ 457 return 1; /* NOTREACHED */
454} 458}
455 459
456static enum token t_lex(char *s)
457{
458 const struct t_op *op;
459
460 t_wp_op = NULL;
461 if (s == NULL) {
462 return EOI;
463 }
464
465 op = ops;
466 do {
467 if (strcmp(s, op->op_text) == 0) {
468 t_wp_op = op;
469 return op->op_num;
470 }
471 op++;
472 } while (op < ops + ARRAY_SIZE(ops));
473 460
474 return OPERAND; 461static arith_t nexpr(enum token n)
475}
476
477/* atoi with error detection */
478//XXX: FIXME: duplicate of existing libbb function?
479static arith_t getn(const char *s)
480{ 462{
481 char *p; 463 if (n == UNOT)
482#if ENABLE_FEATURE_TEST_64 464 return !nexpr(t_lex(*++t_wp));
483 long long r; 465 return primary(n);
484#else
485 long r;
486#endif
487
488 errno = 0;
489#if ENABLE_FEATURE_TEST_64
490 r = strtoll(s, &p, 10);
491#else
492 r = strtol(s, &p, 10);
493#endif
494
495 if (errno != 0)
496 syntax(s, "out of range");
497
498 if (*(skip_whitespace(p)))
499 syntax(s, "bad number");
500
501 return r;
502} 466}
503 467
504/* UNUSED
505static int newerf(const char *f1, const char *f2)
506{
507 struct stat b1, b2;
508 468
509 return (stat(f1, &b1) == 0 && 469static arith_t aexpr(enum token n)
510 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
511}
512
513static int olderf(const char *f1, const char *f2)
514{ 470{
515 struct stat b1, b2; 471 arith_t res;
516 472
517 return (stat(f1, &b1) == 0 && 473 res = nexpr(n);
518 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); 474 if (t_lex(*++t_wp) == BAND)
475 return aexpr(t_lex(*++t_wp)) && res;
476 t_wp--;
477 return res;
519} 478}
520 479
521static int equalf(const char *f1, const char *f2) 480
481static arith_t oexpr(enum token n)
522{ 482{
523 struct stat b1, b2; 483 arith_t res;
524 484
525 return (stat(f1, &b1) == 0 && 485 res = aexpr(n);
526 stat(f2, &b2) == 0 && 486 if (t_lex(*++t_wp) == BOR) {
527 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); 487 return oexpr(t_lex(*++t_wp)) || res;
488 }
489 t_wp--;
490 return res;
528} 491}
529*/
530 492
531/* Do the same thing access(2) does, but use the effective uid and gid,
532 and don't make the mistake of telling root that any file is
533 executable. */
534static int test_eaccess(char *path, int mode)
535{
536 struct stat st;
537 unsigned int euid = geteuid();
538 493
539 if (stat(path, &st) < 0)
540 return -1;
541 494
542 if (euid == 0) { 495static arith_t primary(enum token n)
543 /* Root can read or write any file. */ 496{
544 if (mode != X_OK) 497 arith_t res;
545 return 0;
546 498
547 /* Root can execute any file that has any one of the execute 499 if (n == EOI) {
548 bits set. */ 500 syntax(NULL, "argument expected");
549 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 501 }
550 return 0; 502 if (n == LPAREN) {
503 res = oexpr(t_lex(*++t_wp));
504 if (t_lex(*++t_wp) != RPAREN)
505 syntax(NULL, "closing paren expected");
506 return res;
507 }
508 if (t_wp_op && t_wp_op->op_type == UNOP) {
509 /* unary expression */
510 if (*++t_wp == NULL)
511 syntax(t_wp_op->op_text, "argument expected");
512 if (n == STREZ)
513 return t_wp[0][0] == '\0';
514 if (n == STRNZ)
515 return t_wp[0][0] != '\0';
516 if (n == FILTT)
517 return isatty(getn(*t_wp));
518 return filstat(*t_wp, n);
551 } 519 }
552 520
553 if (st.st_uid == euid) /* owner */ 521 t_lex(t_wp[1]);
554 mode <<= 6; 522 if (t_wp_op && t_wp_op->op_type == BINOP) {
555 else if (is_a_group_member(st.st_gid)) 523 return binop();
556 mode <<= 3; 524 }
557
558 if (st.st_mode & mode)
559 return 0;
560 525
561 return -1; 526 return t_wp[0][0] != '\0';
562} 527}
563 528
564static void initialize_group_array(void) 529
530int test_main(int argc, char **argv)
565{ 531{
566 ngroups = getgroups(0, NULL); 532 int res;
567 if (ngroups > 0) { 533 const char *arg0;
568 /* FIXME: ash tries so hard to not die on OOM, 534 bool _off;
569 * and we spoil it with just one xrealloc here */ 535
570 /* We realloc, because test_main can be entered repeatedly by shell. 536 arg0 = bb_basename(argv[0]);
571 * Testcase (ash): 'while true; do test -x some_file; done' 537 if (arg0[0] == '[') {
572 * and watch top. (some_file must have owner != you) */ 538 --argc;
573 group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); 539 if (!arg0[1]) { /* "[" ? */
574 getgroups(ngroups, group_array); 540 if (NOT_LONE_CHAR(argv[argc], ']')) {
541 bb_error_msg("missing ]");
542 return 2;
543 }
544 } else { /* assuming "[[" */
545 if (strcmp(argv[argc], "]]") != 0) {
546 bb_error_msg("missing ]]");
547 return 2;
548 }
549 }
550 argv[argc] = NULL;
575 } 551 }
576}
577 552
578/* Return non-zero if GID is one that we have in our groups list. */ 553 res = setjmp(leaving);
579//XXX: FIXME: duplicate of existing libbb function? 554 if (res)
580// see toplevel TODO file: 555 return res;
581// possible code duplication ingroup() and is_a_group_member()
582static int is_a_group_member(gid_t gid)
583{
584 int i;
585 556
586 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 557 /* resetting ngroups is probably unnecessary. it will
587 if (gid == getgid() || gid == getegid()) 558 * force a new call to getgroups(), which prevents using
588 return 1; 559 * group data fetched during a previous call. but the
560 * only way the group data could be stale is if there's
561 * been an intervening call to setgroups(), and this
562 * isn't likely in the case of a shell. paranoia
563 * prevails...
564 */
565 ngroups = 0;
589 566
590 if (ngroups == 0) 567 /* Implement special cases from POSIX.2, section 4.62.4 */
591 initialize_group_array(); 568 if (argc == 1)
569 return 1;
570 if (argc == 2)
571 return *argv[1] == '\0';
572//assert(argc);
573 /* remember if we saw argc==4 which wants *no* '!' test */
574 _off = argc - 4;
575 if (_off ?
576 (LONE_CHAR(argv[1], '!'))
577 : (argv[1][0] != '!' || argv[1][1] != '\0'))
578 {
579 if (argc == 3)
580 return *argv[2] != '\0';
592 581
593 /* Search through the list looking for GID. */ 582 t_lex(argv[2 + _off]);
594 for (i = 0; i < ngroups; i++) 583 if (t_wp_op && t_wp_op->op_type == BINOP) {
595 if (gid == group_array[i]) 584 t_wp = &argv[1 + _off];
596 return 1; 585 return binop() == _off;
586 }
587 }
588 t_wp = &argv[1];
589 res = !oexpr(t_lex(*t_wp));
597 590
598 return 0; 591 if (*t_wp != NULL && *++t_wp != NULL) {
592 bb_error_msg("%s: unknown operand", *t_wp);
593 return 2;
594 }
595 return res;
599} 596}