Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/coreutils/test.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/coreutils/test.c
r1765 r2725 13 13 * modified by Erik Andersen <andersen@codepoet.org> to be used 14 14 * in busybox. 15 * modified by Bernhard Fischer to be useable (i.e. a bit less bloaty).15 * modified by Bernhard Reutner-Fischer to be useable (i.e. a bit less bloaty). 16 16 * 17 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.17 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 18 18 * 19 19 * Original copyright notice states: … … 21 21 */ 22 22 23 //kbuild:lib-$(CONFIG_TEST) += test.o test_ptr_hack.o 24 //kbuild:lib-$(CONFIG_ASH) += test.o test_ptr_hack.o 25 //kbuild:lib-$(CONFIG_HUSH) += test.o test_ptr_hack.o 26 27 //config:config TEST 28 //config: bool "test" 29 //config: default y 30 //config: help 31 //config: test is used to check file types and compare values, 32 //config: returning an appropriate exit code. The bash shell 33 //config: has test built in, ash can build it in optionally. 34 //config: 35 //config:config FEATURE_TEST_64 36 //config: bool "Extend test to 64 bit" 37 //config: default y 38 //config: depends on TEST || ASH_BUILTIN_TEST || HUSH 39 //config: help 40 //config: Enable 64-bit support in test. 41 23 42 #include "libbb.h" 24 43 #include <setjmp.h> 25 44 26 /* This is a NOEXEC applet. Be very careful! */ 27 45 /* This is a NOFORK applet. Be very careful! */ 46 47 /* test_main() is called from shells, and we need to be extra careful here. 48 * This is true regardless of PREFER_APPLETS and STANDALONE_SHELL 49 * state. */ 28 50 29 51 /* test(1) accepts the following grammar: 30 oexpr 31 aexpr 32 nexpr 52 oexpr ::= aexpr | aexpr "-o" oexpr ; 53 aexpr ::= nexpr | nexpr "-a" aexpr ; 54 nexpr ::= primary | "!" primary 33 55 primary ::= unary-operator operand 34 56 | operand binary-operator operand … … 44 66 */ 45 67 68 /* TODO: handle [[ expr ]] bashism bash-compatibly. 69 * [[ ]] is meant to be a "better [ ]", with less weird syntax 70 * and without the risk of variables and quoted strings misinterpreted 71 * as operators. 72 * This will require support from shells - we need to know quote status 73 * of each parameter (see below). 74 * 75 * Word splitting and pathname expansion should NOT be performed: 76 * # a="a b"; [[ $a = "a b" ]] && echo YES 77 * YES 78 * # [[ /bin/m* ]] && echo YES 79 * YES 80 * 81 * =~ should do regexp match 82 * = and == should do pattern match against right side: 83 * # [[ *a* == bab ]] && echo YES 84 * # [[ bab == *a* ]] && echo YES 85 * YES 86 * != does the negated == (i.e., also with pattern matching). 87 * Pattern matching is quotation-sensitive: 88 * # [[ bab == "b"a* ]] && echo YES 89 * YES 90 * # [[ bab == b"a*" ]] && echo YES 91 * 92 * Conditional operators such as -f must be unquoted literals to be recognized: 93 * # [[ -e /bin ]] && echo YES 94 * YES 95 * # [[ '-e' /bin ]] && echo YES 96 * bash: conditional binary operator expected... 97 * # A='-e'; [[ $A /bin ]] && echo YES 98 * bash: conditional binary operator expected... 99 * 100 * || and && should work as -o and -a work in [ ] 101 * -a and -o aren't recognized (&& and || are to be used instead) 102 * ( and ) do not need to be quoted unlike in [ ]: 103 * # [[ ( abc ) && '' ]] && echo YES 104 * # [[ ( abc ) || '' ]] && echo YES 105 * YES 106 * # [[ ( abc ) -o '' ]] && echo YES 107 * bash: syntax error in conditional expression... 108 * 109 * Apart from the above, [[ expr ]] should work as [ expr ] 110 */ 111 112 #define TEST_DEBUG 0 113 46 114 enum token { 47 115 EOI, 48 FILRD, 116 117 FILRD, /* file access */ 49 118 FILWR, 50 119 FILEX, 120 51 121 FILEXIST, 52 FILREG, 122 123 FILREG, /* file type */ 53 124 FILDIR, 54 125 FILCDEV, … … 56 127 FILFIFO, 57 128 FILSOCK, 129 58 130 FILSYM, 59 131 FILGZ, 60 132 FILTT, 61 FILSUID, 133 134 FILSUID, /* file bit */ 62 135 FILSGID, 63 136 FILSTCK, 64 FILNT, 137 138 FILNT, /* file ops */ 65 139 FILOT, 66 140 FILEQ, 141 67 142 FILUID, 68 143 FILGID, 69 STREZ, 144 145 STREZ, /* str ops */ 70 146 STRNZ, 71 147 STREQ, … … 73 149 STRLT, 74 150 STRGT, 75 INTEQ, 151 152 INTEQ, /* int ops */ 76 153 INTNE, 77 154 INTGE, … … 79 156 INTLE, 80 157 INTLT, 158 81 159 UNOT, 82 160 BAND, … … 86 164 OPERAND 87 165 }; 88 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5)89 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5)90 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2)166 #define is_int_op(a) (((unsigned char)((a) - INTEQ)) <= 5) 167 #define is_str_op(a) (((unsigned char)((a) - STREZ)) <= 5) 168 #define is_file_op(a) (((unsigned char)((a) - FILNT)) <= 2) 91 169 #define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2) 92 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) 93 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) 94 enum token_types { 170 #define is_file_type(a) (((unsigned char)((a) - FILREG)) <= 5) 171 #define is_file_bit(a) (((unsigned char)((a) - FILSUID)) <= 2) 172 173 #if TEST_DEBUG 174 int depth; 175 #define nest_msg(...) do { \ 176 depth++; \ 177 fprintf(stderr, "%*s", depth*2, ""); \ 178 fprintf(stderr, __VA_ARGS__); \ 179 } while (0) 180 #define unnest_msg(...) do { \ 181 fprintf(stderr, "%*s", depth*2, ""); \ 182 fprintf(stderr, __VA_ARGS__); \ 183 depth--; \ 184 } while (0) 185 #define dbg_msg(...) do { \ 186 fprintf(stderr, "%*s", depth*2, ""); \ 187 fprintf(stderr, __VA_ARGS__); \ 188 } while (0) 189 #define unnest_msg_and_return(expr, ...) do { \ 190 number_t __res = (expr); \ 191 fprintf(stderr, "%*s", depth*2, ""); \ 192 fprintf(stderr, __VA_ARGS__, res); \ 193 depth--; \ 194 return __res; \ 195 } while (0) 196 static const char *const TOKSTR[] = { 197 "EOI", 198 "FILRD", 199 "FILWR", 200 "FILEX", 201 "FILEXIST", 202 "FILREG", 203 "FILDIR", 204 "FILCDEV", 205 "FILBDEV", 206 "FILFIFO", 207 "FILSOCK", 208 "FILSYM", 209 "FILGZ", 210 "FILTT", 211 "FILSUID", 212 "FILSGID", 213 "FILSTCK", 214 "FILNT", 215 "FILOT", 216 "FILEQ", 217 "FILUID", 218 "FILGID", 219 "STREZ", 220 "STRNZ", 221 "STREQ", 222 "STRNE", 223 "STRLT", 224 "STRGT", 225 "INTEQ", 226 "INTNE", 227 "INTGE", 228 "INTGT", 229 "INTLE", 230 "INTLT", 231 "UNOT", 232 "BAND", 233 "BOR", 234 "LPAREN", 235 "RPAREN", 236 "OPERAND" 237 }; 238 #else 239 #define nest_msg(...) ((void)0) 240 #define unnest_msg(...) ((void)0) 241 #define dbg_msg(...) ((void)0) 242 #define unnest_msg_and_return(expr, ...) return expr 243 #endif 244 245 enum { 95 246 UNOP, 96 247 BINOP, … … 100 251 }; 101 252 102 static const struct t_op { 103 char op_text[4]; 253 struct operator_t { 104 254 unsigned char op_num, op_type; 105 } ops[] = {106 { "-r", FILRD , UNOP },107 { "-w", FILWR , UNOP },108 { "-x", FILEX , UNOP },109 { "-e", FILEXIST, UNOP },110 { "-f", FILREG , UNOP },111 { "-d", FILDIR , UNOP },112 { "-c", FILCDEV , UNOP },113 { "-b", FILBDEV , UNOP },114 { "-p", FILFIFO , UNOP },115 { "-u", FILSUID , UNOP },116 { "-g", FILSGID , UNOP },117 { "-k", FILSTCK , UNOP },118 { "-s", FILGZ , UNOP },119 { "-t", FILTT , UNOP },120 { "-z", STREZ , UNOP },121 { "-n", STRNZ , UNOP },122 { "-h", FILSYM , UNOP }, /* for backwards compat */123 124 { "-O" , FILUID , UNOP },125 { "-G" , FILGID , UNOP },126 { "-L" , FILSYM , UNOP },127 { "-S" , FILSOCK, UNOP },128 { "=" , STREQ , BINOP },129 { "==" , STREQ , BINOP },130 { "!=" , STRNE , BINOP },131 { "<" , STRLT , BINOP },132 { ">" , STRGT , BINOP },133 { "-eq", INTEQ , BINOP },134 { "-ne", INTNE , BINOP },135 { "-ge", INTGE , BINOP },136 { "-gt", INTGT , BINOP },137 { "-le", INTLE , BINOP },138 { "-lt", INTLT , BINOP },139 { "-nt", FILNT , BINOP },140 { "-ot", FILOT , BINOP },141 { "-ef", FILEQ , BINOP },142 { "!" , UNOT , BUNOP },143 { "-a" , BAND , BBINOP },144 { "-o" , BOR , BBINOP },145 { "(" , LPAREN , PAREN },146 { ")" , RPAREN , PAREN },147 255 }; 148 256 257 static const struct operator_t ops_table[] = { 258 { /* "-r" */ FILRD , UNOP }, 259 { /* "-w" */ FILWR , UNOP }, 260 { /* "-x" */ FILEX , UNOP }, 261 { /* "-e" */ FILEXIST, UNOP }, 262 { /* "-f" */ FILREG , UNOP }, 263 { /* "-d" */ FILDIR , UNOP }, 264 { /* "-c" */ FILCDEV , UNOP }, 265 { /* "-b" */ FILBDEV , UNOP }, 266 { /* "-p" */ FILFIFO , UNOP }, 267 { /* "-u" */ FILSUID , UNOP }, 268 { /* "-g" */ FILSGID , UNOP }, 269 { /* "-k" */ FILSTCK , UNOP }, 270 { /* "-s" */ FILGZ , UNOP }, 271 { /* "-t" */ FILTT , UNOP }, 272 { /* "-z" */ STREZ , UNOP }, 273 { /* "-n" */ STRNZ , UNOP }, 274 { /* "-h" */ FILSYM , UNOP }, /* for backwards compat */ 275 276 { /* "-O" */ FILUID , UNOP }, 277 { /* "-G" */ FILGID , UNOP }, 278 { /* "-L" */ FILSYM , UNOP }, 279 { /* "-S" */ FILSOCK , UNOP }, 280 { /* "=" */ STREQ , BINOP }, 281 { /* "==" */ STREQ , BINOP }, 282 { /* "!=" */ STRNE , BINOP }, 283 { /* "<" */ STRLT , BINOP }, 284 { /* ">" */ STRGT , BINOP }, 285 { /* "-eq"*/ INTEQ , BINOP }, 286 { /* "-ne"*/ INTNE , BINOP }, 287 { /* "-ge"*/ INTGE , BINOP }, 288 { /* "-gt"*/ INTGT , BINOP }, 289 { /* "-le"*/ INTLE , BINOP }, 290 { /* "-lt"*/ INTLT , BINOP }, 291 { /* "-nt"*/ FILNT , BINOP }, 292 { /* "-ot"*/ FILOT , BINOP }, 293 { /* "-ef"*/ FILEQ , BINOP }, 294 { /* "!" */ UNOT , BUNOP }, 295 { /* "-a" */ BAND , BBINOP }, 296 { /* "-o" */ BOR , BBINOP }, 297 { /* "(" */ LPAREN , PAREN }, 298 { /* ")" */ RPAREN , PAREN }, 299 }; 300 /* Please keep these two tables in sync */ 301 static const char ops_texts[] ALIGN1 = 302 "-r" "\0" 303 "-w" "\0" 304 "-x" "\0" 305 "-e" "\0" 306 "-f" "\0" 307 "-d" "\0" 308 "-c" "\0" 309 "-b" "\0" 310 "-p" "\0" 311 "-u" "\0" 312 "-g" "\0" 313 "-k" "\0" 314 "-s" "\0" 315 "-t" "\0" 316 "-z" "\0" 317 "-n" "\0" 318 "-h" "\0" 319 320 "-O" "\0" 321 "-G" "\0" 322 "-L" "\0" 323 "-S" "\0" 324 "=" "\0" 325 "==" "\0" 326 "!=" "\0" 327 "<" "\0" 328 ">" "\0" 329 "-eq" "\0" 330 "-ne" "\0" 331 "-ge" "\0" 332 "-gt" "\0" 333 "-le" "\0" 334 "-lt" "\0" 335 "-nt" "\0" 336 "-ot" "\0" 337 "-ef" "\0" 338 "!" "\0" 339 "-a" "\0" 340 "-o" "\0" 341 "(" "\0" 342 ")" "\0" 343 ; 344 149 345 150 346 #if ENABLE_FEATURE_TEST_64 151 typedef int64_t arith_t;347 typedef int64_t number_t; 152 348 #else 153 typedef int arith_t;349 typedef int number_t; 154 350 #endif 155 351 156 /* Cannot eliminate these static data (do the G trick) 157 * because of test_main usage from other applets */ 158 static char **t_wp; 159 static struct t_op const *t_wp_op; 160 static gid_t *group_array; 161 static int ngroups; 162 static jmp_buf leaving; 163 164 static enum token t_lex(char *s); 165 static arith_t oexpr(enum token n); 166 static arith_t aexpr(enum token n); 167 static arith_t nexpr(enum token n); 168 static int binop(void); 169 static arith_t primary(enum token n); 170 static int filstat(char *nm, enum token mode); 171 static arith_t getn(const char *s); 172 /* UNUSED 173 static int newerf(const char *f1, const char *f2); 174 static int olderf(const char *f1, const char *f2); 175 static int equalf(const char *f1, const char *f2); 176 */ 177 static int test_eaccess(char *path, int mode); 178 static int is_a_group_member(gid_t gid); 179 static void initialize_group_array(void); 180 181 int 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 249 static void syntax(const char *op, const char *msg) ATTRIBUTE_NORETURN; 352 353 /* We try to minimize both static and stack usage. */ 354 struct test_statics { 355 char **args; 356 /* set only by check_operator(), either to bogus struct 357 * or points to matching operator_t struct. Never NULL. */ 358 const struct operator_t *last_operator; 359 gid_t *group_array; 360 int ngroups; 361 jmp_buf leaving; 362 }; 363 364 /* See test_ptr_hack.c */ 365 extern struct test_statics *const test_ptr_to_statics; 366 367 #define S (*test_ptr_to_statics) 368 #define args (S.args ) 369 #define last_operator (S.last_operator) 370 #define group_array (S.group_array ) 371 #define ngroups (S.ngroups ) 372 #define leaving (S.leaving ) 373 374 #define INIT_S() do { \ 375 (*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \ 376 barrier(); \ 377 } while (0) 378 #define DEINIT_S() do { \ 379 free(test_ptr_to_statics); \ 380 } while (0) 381 382 static number_t primary(enum token n); 383 384 static void syntax(const char *op, const char *msg) NORETURN; 250 385 static void syntax(const char *op, const char *msg) 251 386 { … … 258 393 } 259 394 260 static arith_t oexpr(enum token n) 261 { 262 arith_t res; 263 264 res = aexpr(n); 265 if (t_lex(*++t_wp) == BOR) { 266 return oexpr(t_lex(*++t_wp)) || res; 267 } 268 t_wp--; 269 return res; 270 } 271 272 static arith_t aexpr(enum token n) 273 { 274 arith_t res; 275 276 res = nexpr(n); 277 if (t_lex(*++t_wp) == BAND) 278 return aexpr(t_lex(*++t_wp)) && res; 279 t_wp--; 280 return res; 281 } 282 283 static arith_t nexpr(enum token n) 284 { 285 if (n == UNOT) 286 return !nexpr(t_lex(*++t_wp)); 287 return primary(n); 288 } 289 290 static arith_t primary(enum token n) 291 { 292 arith_t res; 293 294 if (n == EOI) { 295 syntax(NULL, "argument expected"); 296 } 297 if (n == LPAREN) { 298 res = oexpr(t_lex(*++t_wp)); 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 316 t_lex(t_wp[1]); 317 if (t_wp_op && t_wp_op->op_type == BINOP) { 318 return binop(); 319 } 320 321 return t_wp[0][0] != '\0'; 322 } 395 /* atoi with error detection */ 396 //XXX: FIXME: duplicate of existing libbb function? 397 static number_t getn(const char *s) 398 { 399 char *p; 400 #if ENABLE_FEATURE_TEST_64 401 long long r; 402 #else 403 long r; 404 #endif 405 406 errno = 0; 407 #if ENABLE_FEATURE_TEST_64 408 r = strtoll(s, &p, 10); 409 #else 410 r = strtol(s, &p, 10); 411 #endif 412 413 if (errno != 0) 414 syntax(s, "out of range"); 415 416 if (p == s || *(skip_whitespace(p)) != '\0') 417 syntax(s, "bad number"); 418 419 return r; 420 } 421 422 /* UNUSED 423 static int newerf(const char *f1, const char *f2) 424 { 425 struct stat b1, b2; 426 427 return (stat(f1, &b1) == 0 && 428 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); 429 } 430 431 static int olderf(const char *f1, const char *f2) 432 { 433 struct stat b1, b2; 434 435 return (stat(f1, &b1) == 0 && 436 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); 437 } 438 439 static int equalf(const char *f1, const char *f2) 440 { 441 struct stat b1, b2; 442 443 return (stat(f1, &b1) == 0 && 444 stat(f2, &b2) == 0 && 445 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); 446 } 447 */ 448 449 450 static enum token check_operator(const char *s) 451 { 452 static const struct operator_t no_op = { 453 .op_num = -1, 454 .op_type = -1 455 }; 456 int n; 457 458 last_operator = &no_op; 459 if (s == NULL) 460 return EOI; 461 n = index_in_strings(ops_texts, s); 462 if (n < 0) 463 return OPERAND; 464 last_operator = &ops_table[n]; 465 return ops_table[n].op_num; 466 } 467 323 468 324 469 static int binop(void) 325 470 { 326 471 const char *opnd1, *opnd2; 327 struct t_op const *op;328 arith_t val1, val2;329 330 opnd1 = * t_wp;331 (void) t_lex(*++t_wp);332 op = t_wp_op;333 334 opnd2 = *++ t_wp;472 const struct operator_t *op; 473 number_t val1, val2; 474 475 opnd1 = *args; 476 check_operator(*++args); 477 op = last_operator; 478 479 opnd2 = *++args; 335 480 if (opnd2 == NULL) 336 syntax( op->op_text, "argument expected");481 syntax(args[-1], "argument expected"); 337 482 338 483 if (is_int_op(op->op_num)) { … … 349 494 if (op->op_num == INTLE) 350 495 return val1 <= val2; 351 if (op->op_num == INTLT)352 496 /*if (op->op_num == INTLT)*/ 497 return val1 < val2; 353 498 } 354 499 if (is_str_op(op->op_num)) { … … 360 505 if (op->op_num == STRLT) 361 506 return val1 < 0; 362 if (op->op_num == STRGT)363 507 /*if (op->op_num == STRGT)*/ 508 return val1 > 0; 364 509 } 365 510 /* We are sure that these three are by now the only binops we didn't check … … 376 521 if (op->op_num == FILOT) 377 522 return b1.st_mtime < b2.st_mtime; 378 if (op->op_num == FILEQ) 379 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; 380 } 381 return 1; /* NOTREACHED */ 382 } 523 /*if (op->op_num == FILEQ)*/ 524 return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino; 525 } 526 /*return 1; - NOTREACHED */ 527 } 528 529 530 static void initialize_group_array(void) 531 { 532 int n; 533 534 /* getgroups may be expensive, try to use it only once */ 535 ngroups = 32; 536 do { 537 /* FIXME: ash tries so hard to not die on OOM, 538 * and we spoil it with just one xrealloc here */ 539 /* We realloc, because test_main can be entered repeatedly by shell. 540 * Testcase (ash): 'while true; do test -x some_file; done' 541 * and watch top. (some_file must have owner != you) */ 542 n = ngroups; 543 group_array = xrealloc(group_array, n * sizeof(gid_t)); 544 ngroups = getgroups(n, group_array); 545 } while (ngroups > n); 546 } 547 548 549 /* Return non-zero if GID is one that we have in our groups list. */ 550 //XXX: FIXME: duplicate of existing libbb function? 551 // see toplevel TODO file: 552 // possible code duplication ingroup() and is_a_group_member() 553 static int is_a_group_member(gid_t gid) 554 { 555 int i; 556 557 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 558 if (gid == getgid() || gid == getegid()) 559 return 1; 560 561 if (ngroups == 0) 562 initialize_group_array(); 563 564 /* Search through the list looking for GID. */ 565 for (i = 0; i < ngroups; i++) 566 if (gid == group_array[i]) 567 return 1; 568 569 return 0; 570 } 571 572 573 /* Do the same thing access(2) does, but use the effective uid and gid, 574 and don't make the mistake of telling root that any file is 575 executable. */ 576 static int test_eaccess(char *path, int mode) 577 { 578 struct stat st; 579 unsigned int euid = geteuid(); 580 581 if (stat(path, &st) < 0) 582 return -1; 583 584 if (euid == 0) { 585 /* Root can read or write any file. */ 586 if (mode != X_OK) 587 return 0; 588 589 /* Root can execute any file that has any one of the execute 590 bits set. */ 591 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 592 return 0; 593 } 594 595 if (st.st_uid == euid) /* owner */ 596 mode <<= 6; 597 else if (is_a_group_member(st.st_gid)) 598 mode <<= 3; 599 600 if (st.st_mode & mode) 601 return 0; 602 603 return -1; 604 } 605 383 606 384 607 static int filstat(char *nm, enum token mode) 385 608 { 386 609 struct stat s; 387 inti = i; /* gcc 3.x thinks it can be used uninitialized */610 unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */ 388 611 389 612 if (mode == FILSYM) { … … 454 677 } 455 678 456 static 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; 679 680 static number_t nexpr(enum token n) 681 { 682 number_t res; 683 684 nest_msg(">nexpr(%s)\n", TOKSTR[n]); 685 if (n == UNOT) { 686 n = check_operator(*++args); 687 if (n == EOI) { 688 /* special case: [ ! ], [ a -a ! ] are valid */ 689 /* IOW, "! ARG" may miss ARG */ 690 unnest_msg("<nexpr:1 (!EOI)\n"); 691 return 1; 470 692 } 471 op++; 472 } while (op < ops + ARRAY_SIZE(ops)); 473 474 return OPERAND; 475 } 476 477 /* atoi with error detection */ 478 //XXX: FIXME: duplicate of existing libbb function? 479 static arith_t getn(const char *s) 480 { 481 char *p; 482 #if ENABLE_FEATURE_TEST_64 483 long long r; 693 res = !nexpr(n); 694 unnest_msg("<nexpr:%lld\n", res); 695 return res; 696 } 697 res = primary(n); 698 unnest_msg("<nexpr:%lld\n", res); 699 return res; 700 } 701 702 703 static number_t aexpr(enum token n) 704 { 705 number_t res; 706 707 nest_msg(">aexpr(%s)\n", TOKSTR[n]); 708 res = nexpr(n); 709 dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]); 710 if (check_operator(*++args) == BAND) { 711 dbg_msg("aexpr: arg is AND, next args:%s\n", args[1]); 712 res = aexpr(check_operator(*++args)) && res; 713 unnest_msg("<aexpr:%lld\n", res); 714 return res; 715 } 716 args--; 717 unnest_msg("<aexpr:%lld, args:%s\n", res, args[0]); 718 return res; 719 } 720 721 722 static number_t oexpr(enum token n) 723 { 724 number_t res; 725 726 nest_msg(">oexpr(%s)\n", TOKSTR[n]); 727 res = aexpr(n); 728 dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]); 729 if (check_operator(*++args) == BOR) { 730 dbg_msg("oexpr: next arg is OR, next args:%s\n", args[1]); 731 res = oexpr(check_operator(*++args)) || res; 732 unnest_msg("<oexpr:%lld\n", res); 733 return res; 734 } 735 args--; 736 unnest_msg("<oexpr:%lld, args:%s\n", res, args[0]); 737 return res; 738 } 739 740 741 static number_t primary(enum token n) 742 { 743 #if TEST_DEBUG 744 number_t res = res; /* for compiler */ 484 745 #else 485 long r;746 number_t res; 486 747 #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); 748 const struct operator_t *args0_op; 749 750 nest_msg(">primary(%s)\n", TOKSTR[n]); 751 if (n == EOI) { 752 syntax(NULL, "argument expected"); 753 } 754 if (n == LPAREN) { 755 res = oexpr(check_operator(*++args)); 756 if (check_operator(*++args) != RPAREN) 757 syntax(NULL, "closing paren expected"); 758 unnest_msg("<primary:%lld\n", res); 759 return res; 760 } 761 762 /* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first, 763 * do the same */ 764 args0_op = last_operator; 765 /* last_operator = operator at args[1] */ 766 if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */ 767 if (args[2]) { 768 // coreutils also does this: 769 // if (args[3] && args[0]="-l" && args[2] is BINOP) 770 // return binop(1 /* prepended by -l */); 771 if (last_operator->op_type == BINOP) 772 unnest_msg_and_return(binop(), "<primary: binop:%lld\n"); 773 } 774 } 775 /* check "is args[0] unop?" second */ 776 if (args0_op->op_type == UNOP) { 777 /* unary expression */ 778 if (args[1] == NULL) 779 // syntax(args0_op->op_text, "argument expected"); 780 goto check_emptiness; 781 args++; 782 if (n == STREZ) 783 unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n"); 784 if (n == STRNZ) 785 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n"); 786 if (n == FILTT) 787 unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args); 788 unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args); 789 } 790 791 /*check_operator(args[1]); - already done */ 792 if (last_operator->op_type == BINOP) { 793 /* args[2] is known to be NULL, isn't it bound to fail? */ 794 unnest_msg_and_return(binop(), "<primary:%lld\n"); 795 } 796 check_emptiness: 797 unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n"); 798 } 799 800 801 int test_main(int argc, char **argv) 802 { 803 int res; 804 const char *arg0; 805 // bool negate = 0; 806 807 arg0 = bb_basename(argv[0]); 808 if (arg0[0] == '[') { 809 --argc; 810 if (!arg0[1]) { /* "[" ? */ 811 if (NOT_LONE_CHAR(argv[argc], ']')) { 812 bb_error_msg("missing ]"); 813 return 2; 814 } 815 } else { /* assuming "[[" */ 816 if (strcmp(argv[argc], "]]") != 0) { 817 bb_error_msg("missing ]]"); 818 return 2; 819 } 820 } 821 argv[argc] = NULL; 822 } 823 824 /* We must do DEINIT_S() prior to returning */ 825 INIT_S(); 826 827 res = setjmp(leaving); 828 if (res) 829 goto ret; 830 831 /* resetting ngroups is probably unnecessary. it will 832 * force a new call to getgroups(), which prevents using 833 * group data fetched during a previous call. but the 834 * only way the group data could be stale is if there's 835 * been an intervening call to setgroups(), and this 836 * isn't likely in the case of a shell. paranoia 837 * prevails... 838 */ 839 /*ngroups = 0; - done by INIT_S() */ 840 841 //argc--; 842 argv++; 843 844 /* Implement special cases from POSIX.2, section 4.62.4 */ 845 if (!argv[0]) { /* "test" */ 846 res = 1; 847 goto ret; 848 } 849 #if 0 850 // Now it's fixed in the parser and should not be needed 851 if (LONE_CHAR(argv[0], '!') && argv[1]) { 852 negate = 1; 853 //argc--; 854 argv++; 855 } 856 if (!argv[1]) { /* "test [!] arg" */ 857 res = (*argv[0] == '\0'); 858 goto ret; 859 } 860 if (argv[2] && !argv[3]) { 861 check_operator(argv[1]); 862 if (last_operator->op_type == BINOP) { 863 /* "test [!] arg1 <binary_op> arg2" */ 864 args = argv; 865 res = (binop() == 0); 866 goto ret; 867 } 868 } 869 870 /* Some complex expression. Undo '!' removal */ 871 if (negate) { 872 negate = 0; 873 //argc++; 874 argv--; 875 } 493 876 #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 } 503 504 /* UNUSED 505 static int newerf(const char *f1, const char *f2) 506 { 507 struct stat b1, b2; 508 509 return (stat(f1, &b1) == 0 && 510 stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime); 511 } 512 513 static int olderf(const char *f1, const char *f2) 514 { 515 struct stat b1, b2; 516 517 return (stat(f1, &b1) == 0 && 518 stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime); 519 } 520 521 static int equalf(const char *f1, const char *f2) 522 { 523 struct stat b1, b2; 524 525 return (stat(f1, &b1) == 0 && 526 stat(f2, &b2) == 0 && 527 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); 528 } 529 */ 530 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. */ 534 static int test_eaccess(char *path, int mode) 535 { 536 struct stat st; 537 unsigned int euid = geteuid(); 538 539 if (stat(path, &st) < 0) 540 return -1; 541 542 if (euid == 0) { 543 /* Root can read or write any file. */ 544 if (mode != X_OK) 545 return 0; 546 547 /* Root can execute any file that has any one of the execute 548 bits set. */ 549 if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) 550 return 0; 551 } 552 553 if (st.st_uid == euid) /* owner */ 554 mode <<= 6; 555 else if (is_a_group_member(st.st_gid)) 556 mode <<= 3; 557 558 if (st.st_mode & mode) 559 return 0; 560 561 return -1; 562 } 563 564 static void initialize_group_array(void) 565 { 566 ngroups = getgroups(0, NULL); 567 if (ngroups > 0) { 568 /* FIXME: ash tries so hard to not die on OOM, 569 * and we spoil it with just one xrealloc here */ 570 /* We realloc, because test_main can be entered repeatedly by shell. 571 * Testcase (ash): 'while true; do test -x some_file; done' 572 * and watch top. (some_file must have owner != you) */ 573 group_array = xrealloc(group_array, ngroups * sizeof(gid_t)); 574 getgroups(ngroups, group_array); 575 } 576 } 577 578 /* Return non-zero if GID is one that we have in our groups list. */ 579 //XXX: FIXME: duplicate of existing libbb function? 580 // see toplevel TODO file: 581 // possible code duplication ingroup() and is_a_group_member() 582 static int is_a_group_member(gid_t gid) 583 { 584 int i; 585 586 /* Short-circuit if possible, maybe saving a call to getgroups(). */ 587 if (gid == getgid() || gid == getegid()) 588 return 1; 589 590 if (ngroups == 0) 591 initialize_group_array(); 592 593 /* Search through the list looking for GID. */ 594 for (i = 0; i < ngroups; i++) 595 if (gid == group_array[i]) 596 return 1; 597 598 return 0; 599 } 877 args = argv; 878 res = !oexpr(check_operator(*args)); 879 880 if (*args != NULL && *++args != NULL) { 881 /* TODO: example when this happens? */ 882 bb_error_msg("%s: unknown operand", *args); 883 res = 2; 884 } 885 ret: 886 DEINIT_S(); 887 // return negate ? !res : res; 888 return res; 889 }
Note:
See TracChangeset
for help on using the changeset viewer.