Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/findutils/find.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/findutils/find.c
r821 r1770 8 8 * Matt Kraai <kraai@alumni.carnegiemellon.edu>. 9 9 * 10 * Licensed under the GPL v 2 or later, see the file LICENSE in this tarball.10 * Licensed under the GPL version 2, see the file LICENSE in this tarball. 11 11 */ 12 12 13 #include "busybox.h" 14 #include <stdio.h> 15 #include <unistd.h> 16 #include <dirent.h> 17 #include <string.h> 18 #include <stdlib.h> 13 /* findutils-4.1.20: 14 * 15 * # find file.txt -exec 'echo {}' '{} {}' ';' 16 * find: echo file.txt: No such file or directory 17 * # find file.txt -exec 'echo' '{} {}' '; ' 18 * find: missing argument to `-exec' 19 * # find file.txt -exec 'echo {}' '{} {}' ';' junk 20 * find: paths must precede expression 21 * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';' 22 * find: paths must precede expression 23 * # find file.txt -exec 'echo' '{} {}' ';' 24 * file.txt file.txt 25 * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) 26 * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';' 27 * file.txt file.txt 28 * file.txt 29 * /tmp 30 * # find -name '*.c' -o -name '*.h' 31 * [shows files, *.c and *.h intermixed] 32 * # find file.txt -name '*f*' -o -name '*t*' 33 * file.txt 34 * # find file.txt -name '*z*' -o -name '*t*' 35 * file.txt 36 * # find file.txt -name '*f*' -o -name '*z*' 37 * file.txt 38 * 39 * # find t z -name '*t*' -print -o -name '*z*' 40 * t 41 * # find t z t z -name '*t*' -o -name '*z*' -print 42 * z 43 * z 44 * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print 45 * (no output) 46 */ 47 48 /* Testing script 49 * ./busybox find "$@" | tee /tmp/bb_find 50 * echo ================== 51 * /path/to/gnu/find "$@" | tee /tmp/std_find 52 * echo ================== 53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical 54 */ 55 19 56 #include <fnmatch.h> 20 #include <time.h> 21 #include <ctype.h> 22 23 static char *pattern; 24 #ifdef CONFIG_FEATURE_FIND_PRINT0 25 static char printsep = '\n'; 26 #endif 27 28 #ifdef CONFIG_FEATURE_FIND_TYPE 29 static int type_mask = 0; 30 #endif 31 32 #ifdef CONFIG_FEATURE_FIND_PERM 33 static char perm_char = 0; 34 static int perm_mask = 0; 35 #endif 36 37 #ifdef CONFIG_FEATURE_FIND_MTIME 38 static char mtime_char; 39 static int mtime_days; 40 #endif 41 42 #ifdef CONFIG_FEATURE_FIND_MMIN 43 static char mmin_char; 44 static int mmin_mins; 45 #endif 46 47 #ifdef CONFIG_FEATURE_FIND_XDEV 48 static dev_t *xdev_dev; 49 static int xdev_count = 0; 50 #endif 51 52 #ifdef CONFIG_FEATURE_FIND_NEWER 53 static time_t newer_mtime; 54 #endif 55 56 #ifdef CONFIG_FEATURE_FIND_INUM 57 static ino_t inode_num; 58 #endif 59 60 #ifdef CONFIG_FEATURE_FIND_EXEC 61 static char **exec_str; 62 static int num_matches; 63 static int exec_opt; 64 #endif 65 66 static int fileAction(const char *fileName, struct stat *statbuf, void* junk) 67 { 68 #ifdef CONFIG_FEATURE_FIND_XDEV 57 #include "libbb.h" 58 #if ENABLE_FEATURE_FIND_REGEX 59 #include "xregex.h" 60 #endif 61 62 /* This is a NOEXEC applet. Be very careful! */ 63 64 65 USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) 66 USE_FEATURE_FIND_XDEV(static int xdev_count;) 67 68 typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *); 69 70 typedef struct { 71 action_fp f; 72 #if ENABLE_FEATURE_FIND_NOT 73 bool invert; 74 #endif 75 } action; 76 #define ACTS(name, arg...) typedef struct { action a; arg; } action_##name; 77 #define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap) 78 ACTS(print) 79 ACTS(name, const char *pattern;) 80 USE_FEATURE_FIND_PATH( ACTS(path, const char *pattern;)) 81 USE_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;)) 82 USE_FEATURE_FIND_PRINT0( ACTS(print0)) 83 USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) 84 USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;)) 85 USE_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;)) 86 USE_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;)) 87 USE_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;)) 88 USE_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;)) 89 USE_FEATURE_FIND_USER( ACTS(user, uid_t uid;)) 90 USE_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;)) 91 USE_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;)) 92 USE_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;)) 93 USE_FEATURE_FIND_PRUNE( ACTS(prune)) 94 USE_FEATURE_FIND_DELETE( ACTS(delete)) 95 USE_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;)) 96 USE_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;)) 97 98 static action ***actions; 99 static bool need_print = 1; 100 static int recurse_flags = ACTION_RECURSE; 101 102 #if ENABLE_FEATURE_FIND_EXEC 103 static unsigned count_subst(const char *str) 104 { 105 unsigned count = 0; 106 while ((str = strstr(str, "{}")) != NULL) { 107 count++; 108 str++; 109 } 110 return count; 111 } 112 113 114 static char* subst(const char *src, unsigned count, const char* filename) 115 { 116 char *buf, *dst, *end; 117 size_t flen = strlen(filename); 118 /* we replace each '{}' with filename: growth by strlen-2 */ 119 buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1); 120 while ((end = strstr(src, "{}"))) { 121 memcpy(dst, src, end - src); 122 dst += end - src; 123 src = end + 2; 124 memcpy(dst, filename, flen); 125 dst += flen; 126 } 127 strcpy(dst, src); 128 return buf; 129 } 130 #endif 131 132 /* Return values of ACTFs ('action functions') are a bit mask: 133 * bit 1=1: prune (use SKIP constant for setting it) 134 * bit 0=1: matched successfully (TRUE) 135 */ 136 137 static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf) 138 { 139 int cur_group; 140 int cur_action; 141 int rc = 0; 142 action **app, *ap; 143 144 /* "action group" is a set of actions ANDed together. 145 * groups are ORed together. 146 * We simply evaluate each group until we find one in which all actions 147 * succeed. */ 148 149 /* -prune is special: if it is encountered, then we won't 150 * descend into current directory. It doesn't matter whether 151 * action group (in which -prune sits) will succeed or not: 152 * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir 153 * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs 154 * not starting with 'f' */ 155 156 /* We invert TRUE bit (bit 0). Now 1 there means 'failure'. 157 * and bitwise OR in "rc |= TRUE ^ ap->f()" will: 158 * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'. 159 * On return, bit is restored. */ 160 161 cur_group = -1; 162 while ((app = appp[++cur_group])) { 163 rc &= ~TRUE; /* 'success' so far, clear TRUE bit */ 164 cur_action = -1; 165 while (1) { 166 ap = app[++cur_action]; 167 if (!ap) /* all actions in group were successful */ 168 return rc ^ TRUE; /* restore TRUE bit */ 169 rc |= TRUE ^ ap->f(fileName, statbuf, ap); 170 #if ENABLE_FEATURE_FIND_NOT 171 if (ap->invert) rc ^= TRUE; 172 #endif 173 if (rc & TRUE) /* current group failed, try next */ 174 break; 175 } 176 } 177 return rc ^ TRUE; /* restore TRUE bit */ 178 } 179 180 181 ACTF(name) 182 { 183 const char *tmp = bb_basename(fileName); 184 if (tmp != fileName && !*tmp) { /* "foo/bar/". Oh no... go back to 'b' */ 185 tmp--; 186 while (tmp != fileName && *--tmp != '/') 187 continue; 188 if (*tmp == '/') 189 tmp++; 190 } 191 return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0; 192 } 193 #if ENABLE_FEATURE_FIND_PATH 194 ACTF(path) 195 { 196 return fnmatch(ap->pattern, fileName, 0) == 0; 197 } 198 #endif 199 #if ENABLE_FEATURE_FIND_REGEX 200 ACTF(regex) 201 { 202 regmatch_t match; 203 if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/)) 204 return 0; /* no match */ 205 if (match.rm_so) 206 return 0; /* match doesn't start at pos 0 */ 207 if (fileName[match.rm_eo]) 208 return 0; /* match doesn't end exactly at end of pathname */ 209 return 1; 210 } 211 #endif 212 #if ENABLE_FEATURE_FIND_TYPE 213 ACTF(type) 214 { 215 return ((statbuf->st_mode & S_IFMT) == ap->type_mask); 216 } 217 #endif 218 #if ENABLE_FEATURE_FIND_PERM 219 ACTF(perm) 220 { 221 /* -perm +mode: at least one of perm_mask bits are set */ 222 if (ap->perm_char == '+') 223 return (statbuf->st_mode & ap->perm_mask) != 0; 224 /* -perm -mode: all of perm_mask are set */ 225 if (ap->perm_char == '-') 226 return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask; 227 /* -perm mode: file mode must match perm_mask */ 228 return (statbuf->st_mode & 07777) == ap->perm_mask; 229 } 230 #endif 231 #if ENABLE_FEATURE_FIND_MTIME 232 ACTF(mtime) 233 { 234 time_t file_age = time(NULL) - statbuf->st_mtime; 235 time_t mtime_secs = ap->mtime_days * 24*60*60; 236 if (ap->mtime_char == '+') 237 return file_age >= mtime_secs + 24*60*60; 238 if (ap->mtime_char == '-') 239 return file_age < mtime_secs; 240 /* just numeric mtime */ 241 return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60); 242 } 243 #endif 244 #if ENABLE_FEATURE_FIND_MMIN 245 ACTF(mmin) 246 { 247 time_t file_age = time(NULL) - statbuf->st_mtime; 248 time_t mmin_secs = ap->mmin_mins * 60; 249 if (ap->mmin_char == '+') 250 return file_age >= mmin_secs + 60; 251 if (ap->mmin_char == '-') 252 return file_age < mmin_secs; 253 /* just numeric mmin */ 254 return file_age >= mmin_secs && file_age < (mmin_secs + 60); 255 } 256 #endif 257 #if ENABLE_FEATURE_FIND_NEWER 258 ACTF(newer) 259 { 260 return (ap->newer_mtime < statbuf->st_mtime); 261 } 262 #endif 263 #if ENABLE_FEATURE_FIND_INUM 264 ACTF(inum) 265 { 266 return (statbuf->st_ino == ap->inode_num); 267 } 268 #endif 269 #if ENABLE_FEATURE_FIND_EXEC 270 ACTF(exec) 271 { 272 int i, rc; 273 char *argv[ap->exec_argc + 1]; 274 for (i = 0; i < ap->exec_argc; i++) 275 argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); 276 argv[i] = NULL; /* terminate the list */ 277 278 rc = spawn_and_wait(argv); 279 if (rc < 0) 280 bb_perror_msg("%s", argv[0]); 281 282 i = 0; 283 while (argv[i]) 284 free(argv[i++]); 285 return rc == 0; /* return 1 if exitcode 0 */ 286 } 287 #endif 288 #if ENABLE_FEATURE_FIND_USER 289 ACTF(user) 290 { 291 return (statbuf->st_uid == ap->uid); 292 } 293 #endif 294 #if ENABLE_FEATURE_FIND_GROUP 295 ACTF(group) 296 { 297 return (statbuf->st_gid == ap->gid); 298 } 299 #endif 300 #if ENABLE_FEATURE_FIND_PRINT0 301 ACTF(print0) 302 { 303 printf("%s%c", fileName, '\0'); 304 return TRUE; 305 } 306 #endif 307 ACTF(print) 308 { 309 puts(fileName); 310 return TRUE; 311 } 312 #if ENABLE_FEATURE_FIND_PAREN 313 ACTF(paren) 314 { 315 return exec_actions(ap->subexpr, fileName, statbuf); 316 } 317 #endif 318 #if ENABLE_FEATURE_FIND_SIZE 319 ACTF(size) 320 { 321 if (ap->size_char == '+') 322 return statbuf->st_size > ap->size; 323 if (ap->size_char == '-') 324 return statbuf->st_size < ap->size; 325 return statbuf->st_size == ap->size; 326 } 327 #endif 328 #if ENABLE_FEATURE_FIND_PRUNE 329 /* 330 * -prune: if -depth is not given, return true and do not descend 331 * current dir; if -depth is given, return false with no effect. 332 * Example: 333 * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print 334 */ 335 ACTF(prune) 336 { 337 return SKIP + TRUE; 338 } 339 #endif 340 #if ENABLE_FEATURE_FIND_DELETE 341 ACTF(delete) 342 { 343 int rc; 344 if (S_ISDIR(statbuf->st_mode)) { 345 rc = rmdir(fileName); 346 } else { 347 rc = unlink(fileName); 348 } 349 if (rc < 0) 350 bb_perror_msg("%s", fileName); 351 return TRUE; 352 } 353 #endif 354 #if ENABLE_FEATURE_FIND_CONTEXT 355 ACTF(context) 356 { 357 security_context_t con; 358 int rc; 359 360 if (recurse_flags & ACTION_FOLLOWLINKS) { 361 rc = getfilecon(fileName, &con); 362 } else { 363 rc = lgetfilecon(fileName, &con); 364 } 365 if (rc < 0) 366 return FALSE; 367 rc = strcmp(ap->context, con); 368 freecon(con); 369 return rc == 0; 370 } 371 #endif 372 373 374 static int fileAction(const char *fileName, struct stat *statbuf, void *userData, int depth) 375 { 376 int i; 377 #if ENABLE_FEATURE_FIND_MAXDEPTH 378 int maxdepth = (int)(ptrdiff_t)userData; 379 380 if (depth > maxdepth) return SKIP; 381 #endif 382 383 #if ENABLE_FEATURE_FIND_XDEV 69 384 if (S_ISDIR(statbuf->st_mode) && xdev_count) { 70 int i; 71 for (i=0; i<xdev_count; i++) { 72 if (xdev_dev[i] != statbuf->st_dev) 73 return SKIP; 74 } 75 } 76 #endif 77 if (pattern != NULL) { 78 const char *tmp = strrchr(fileName, '/'); 79 80 if (tmp == NULL) 81 tmp = fileName; 82 else 83 tmp++; 84 if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0)) 85 goto no_match; 86 } 87 #ifdef CONFIG_FEATURE_FIND_TYPE 88 if (type_mask != 0) { 89 if (!((statbuf->st_mode & S_IFMT) == type_mask)) 90 goto no_match; 91 } 92 #endif 93 #ifdef CONFIG_FEATURE_FIND_PERM 94 if (perm_mask != 0) { 95 if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) || 96 (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) || 97 (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0))) 98 goto no_match; 99 } 100 #endif 101 #ifdef CONFIG_FEATURE_FIND_MTIME 102 if (mtime_char != 0) { 103 time_t file_age = time(NULL) - statbuf->st_mtime; 104 time_t mtime_secs = mtime_days * 24 * 60 * 60; 105 if (!((isdigit(mtime_char) && file_age >= mtime_secs && 106 file_age < mtime_secs + 24 * 60 * 60) || 107 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) || 108 (mtime_char == '-' && file_age < mtime_secs))) 109 goto no_match; 110 } 111 #endif 112 #ifdef CONFIG_FEATURE_FIND_MMIN 113 if (mmin_char != 0) { 114 time_t file_age = time(NULL) - statbuf->st_mtime; 115 time_t mmin_secs = mmin_mins * 60; 116 if (!((isdigit(mmin_char) && file_age >= mmin_secs && 117 file_age < mmin_secs + 60) || 118 (mmin_char == '+' && file_age >= mmin_secs + 60) || 119 (mmin_char == '-' && file_age < mmin_secs))) 120 goto no_match; 121 } 122 #endif 123 #ifdef CONFIG_FEATURE_FIND_NEWER 124 if (newer_mtime != 0) { 125 time_t file_age = newer_mtime - statbuf->st_mtime; 126 if (file_age >= 0) 127 goto no_match; 128 } 129 #endif 130 #ifdef CONFIG_FEATURE_FIND_INUM 131 if (inode_num != 0) { 132 if (!(statbuf->st_ino == inode_num)) 133 goto no_match; 134 } 135 #endif 136 #ifdef CONFIG_FEATURE_FIND_EXEC 137 if (exec_opt) { 138 int i; 139 char *cmd_string = ""; 140 for (i = 0; i < num_matches; i++) 141 cmd_string = bb_xasprintf("%s%s%s", cmd_string, exec_str[i], fileName); 142 cmd_string = bb_xasprintf("%s%s", cmd_string, exec_str[num_matches]); 143 system(cmd_string); 144 goto no_match; 145 } 146 #endif 147 148 #ifdef CONFIG_FEATURE_FIND_PRINT0 149 printf("%s%c", fileName, printsep); 385 for (i = 0; i < xdev_count; i++) { 386 if (xdev_dev[i] == statbuf->st_dev) 387 break; 388 } 389 if (i == xdev_count) 390 return SKIP; 391 } 392 #endif 393 i = exec_actions(actions, fileName, statbuf); 394 /* Had no explicit -print[0] or -exec? then print */ 395 if ((i & TRUE) && need_print) 396 puts(fileName); 397 /* Cannot return 0: our caller, recursive_action(), 398 * will perror() and skip dirs (if called on dir) */ 399 return (i & SKIP) ? SKIP : TRUE; 400 } 401 402 403 #if ENABLE_FEATURE_FIND_TYPE 404 static int find_type(const char *type) 405 { 406 int mask = 0; 407 408 if (*type == 'b') 409 mask = S_IFBLK; 410 else if (*type == 'c') 411 mask = S_IFCHR; 412 else if (*type == 'd') 413 mask = S_IFDIR; 414 else if (*type == 'p') 415 mask = S_IFIFO; 416 else if (*type == 'f') 417 mask = S_IFREG; 418 else if (*type == 'l') 419 mask = S_IFLNK; 420 else if (*type == 's') 421 mask = S_IFSOCK; 422 423 if (mask == 0 || *(type + 1) != '\0') 424 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); 425 426 return mask; 427 } 428 #endif 429 430 #if ENABLE_FEATURE_FIND_PERM \ 431 || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \ 432 || ENABLE_FEATURE_FIND_SIZE 433 static const char* plus_minus_num(const char* str) 434 { 435 if (*str == '-' || *str == '+') 436 str++; 437 return str; 438 } 439 #endif 440 441 static action*** parse_params(char **argv) 442 { 443 enum { 444 PARM_a , 445 PARM_o , 446 USE_FEATURE_FIND_NOT( PARM_char_not ,) 447 #if ENABLE_DESKTOP 448 PARM_and , 449 PARM_or , 450 USE_FEATURE_FIND_NOT( PARM_not ,) 451 #endif 452 PARM_print , 453 USE_FEATURE_FIND_PRINT0( PARM_print0 ,) 454 USE_FEATURE_FIND_DEPTH( PARM_depth ,) 455 USE_FEATURE_FIND_PRUNE( PARM_prune ,) 456 USE_FEATURE_FIND_DELETE( PARM_delete ,) 457 USE_FEATURE_FIND_EXEC( PARM_exec ,) 458 USE_FEATURE_FIND_PAREN( PARM_char_brace,) 459 /* All options starting from here require argument */ 460 PARM_name , 461 USE_FEATURE_FIND_PATH( PARM_path ,) 462 USE_FEATURE_FIND_REGEX( PARM_regex ,) 463 USE_FEATURE_FIND_TYPE( PARM_type ,) 464 USE_FEATURE_FIND_PERM( PARM_perm ,) 465 USE_FEATURE_FIND_MTIME( PARM_mtime ,) 466 USE_FEATURE_FIND_MMIN( PARM_mmin ,) 467 USE_FEATURE_FIND_NEWER( PARM_newer ,) 468 USE_FEATURE_FIND_INUM( PARM_inum ,) 469 USE_FEATURE_FIND_USER( PARM_user ,) 470 USE_FEATURE_FIND_GROUP( PARM_group ,) 471 USE_FEATURE_FIND_SIZE( PARM_size ,) 472 USE_FEATURE_FIND_CONTEXT(PARM_context ,) 473 }; 474 475 static const char params[] ALIGN1 = 476 "-a\0" 477 "-o\0" 478 USE_FEATURE_FIND_NOT( "!\0" ) 479 #if ENABLE_DESKTOP 480 "-and\0" 481 "-or\0" 482 USE_FEATURE_FIND_NOT( "-not\0" ) 483 #endif 484 "-print\0" 485 USE_FEATURE_FIND_PRINT0( "-print0\0" ) 486 USE_FEATURE_FIND_DEPTH( "-depth\0" ) 487 USE_FEATURE_FIND_PRUNE( "-prune\0" ) 488 USE_FEATURE_FIND_DELETE( "-delete\0" ) 489 USE_FEATURE_FIND_EXEC( "-exec\0" ) 490 USE_FEATURE_FIND_PAREN( "(\0" ) 491 /* All options starting from here require argument */ 492 "-name\0" 493 USE_FEATURE_FIND_PATH( "-path\0" ) 494 USE_FEATURE_FIND_REGEX( "-regex\0" ) 495 USE_FEATURE_FIND_TYPE( "-type\0" ) 496 USE_FEATURE_FIND_PERM( "-perm\0" ) 497 USE_FEATURE_FIND_MTIME( "-mtime\0" ) 498 USE_FEATURE_FIND_MMIN( "-mmin\0" ) 499 USE_FEATURE_FIND_NEWER( "-newer\0" ) 500 USE_FEATURE_FIND_INUM( "-inum\0" ) 501 USE_FEATURE_FIND_USER( "-user\0" ) 502 USE_FEATURE_FIND_GROUP( "-group\0" ) 503 USE_FEATURE_FIND_SIZE( "-size\0" ) 504 USE_FEATURE_FIND_CONTEXT("-context\0") 505 ; 506 507 action*** appp; 508 unsigned cur_group = 0; 509 unsigned cur_action = 0; 510 USE_FEATURE_FIND_NOT( bool invert_flag = 0; ) 511 512 /* 'static' doesn't work here! (gcc 4.1.2) */ 513 action* alloc_action(int sizeof_struct, action_fp f) 514 { 515 action *ap; 516 appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp)); 517 appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct); 518 appp[cur_group][cur_action] = NULL; 519 ap->f = f; 520 USE_FEATURE_FIND_NOT( ap->invert = invert_flag; ) 521 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 522 return ap; 523 } 524 525 #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name) 526 527 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */ 528 529 /* Actions have side effects and return a true or false value 530 * We implement: -print, -print0, -exec 531 * 532 * The rest are tests. 533 * 534 * Tests and actions are grouped by operators 535 * ( expr ) Force precedence 536 * ! expr True if expr is false 537 * -not expr Same as ! expr 538 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false 539 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true 540 * expr1 , expr2 List; both expr1 and expr2 are always evaluated 541 * We implement: (), -a, -o 542 */ 543 while (*argv) { 544 const char *arg = argv[0]; 545 int parm = index_in_strings(params, arg); 546 const char *arg1 = argv[1]; 547 548 if (parm >= PARM_name) { 549 /* All options starting from -name require argument */ 550 if (!arg1) 551 bb_error_msg_and_die(bb_msg_requires_arg, arg); 552 argv++; 553 } 554 555 /* We can use big switch() here, but on i386 556 * it doesn't give smaller code. Other arches? */ 557 558 /* --- Operators --- */ 559 if (parm == PARM_a USE_DESKTOP(|| parm == PARM_and)) { 560 /* no further special handling required */ 561 } 562 else if (parm == PARM_o USE_DESKTOP(|| parm == PARM_or)) { 563 /* start new OR group */ 564 cur_group++; 565 appp = xrealloc(appp, (cur_group+2) * sizeof(*appp)); 566 /*appp[cur_group] = NULL; - already NULL */ 567 appp[cur_group+1] = NULL; 568 cur_action = 0; 569 } 570 #if ENABLE_FEATURE_FIND_NOT 571 else if (parm == PARM_char_not USE_DESKTOP(|| parm == PARM_not)) { 572 /* also handles "find ! ! -name 'foo*'" */ 573 invert_flag ^= 1; 574 } 575 #endif 576 577 /* --- Tests and actions --- */ 578 else if (parm == PARM_print) { 579 need_print = 0; 580 /* GNU find ignores '!' here: "find ! -print" */ 581 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 582 (void) ALLOC_ACTION(print); 583 } 584 #if ENABLE_FEATURE_FIND_PRINT0 585 else if (parm == PARM_print0) { 586 need_print = 0; 587 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 588 (void) ALLOC_ACTION(print0); 589 } 590 #endif 591 #if ENABLE_FEATURE_FIND_DEPTH 592 else if (parm == PARM_depth) { 593 recurse_flags |= ACTION_DEPTHFIRST; 594 } 595 #endif 596 #if ENABLE_FEATURE_FIND_PRUNE 597 else if (parm == PARM_prune) { 598 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 599 (void) ALLOC_ACTION(prune); 600 } 601 #endif 602 #if ENABLE_FEATURE_FIND_DELETE 603 else if (parm == PARM_delete) { 604 need_print = 0; 605 recurse_flags |= ACTION_DEPTHFIRST; 606 (void) ALLOC_ACTION(delete); 607 } 608 #endif 609 #if ENABLE_FEATURE_FIND_EXEC 610 else if (parm == PARM_exec) { 611 int i; 612 action_exec *ap; 613 need_print = 0; 614 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 615 ap = ALLOC_ACTION(exec); 616 ap->exec_argv = ++argv; /* first arg after -exec */ 617 ap->exec_argc = 0; 618 while (1) { 619 if (!*argv) /* did not see ';' until end */ 620 bb_error_msg_and_die("-exec CMD must end by ';'"); 621 if (LONE_CHAR(argv[0], ';')) 622 break; 623 argv++; 624 ap->exec_argc++; 625 } 626 if (ap->exec_argc == 0) 627 bb_error_msg_and_die(bb_msg_requires_arg, arg); 628 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); 629 i = ap->exec_argc; 630 while (i--) 631 ap->subst_count[i] = count_subst(ap->exec_argv[i]); 632 } 633 #endif 634 #if ENABLE_FEATURE_FIND_PAREN 635 else if (parm == PARM_char_brace) { 636 action_paren *ap; 637 char **endarg; 638 unsigned nested = 1; 639 640 endarg = argv; 641 while (1) { 642 if (!*++endarg) 643 bb_error_msg_and_die("unpaired '('"); 644 if (LONE_CHAR(*endarg, '(')) 645 nested++; 646 else if (LONE_CHAR(*endarg, ')') && !--nested) { 647 *endarg = NULL; 648 break; 649 } 650 } 651 ap = ALLOC_ACTION(paren); 652 ap->subexpr = parse_params(argv + 1); 653 *endarg = (char*) ")"; /* restore NULLed parameter */ 654 argv = endarg; 655 } 656 #endif 657 else if (parm == PARM_name) { 658 action_name *ap; 659 ap = ALLOC_ACTION(name); 660 ap->pattern = arg1; 661 } 662 #if ENABLE_FEATURE_FIND_PATH 663 else if (parm == PARM_path) { 664 action_path *ap; 665 ap = ALLOC_ACTION(path); 666 ap->pattern = arg1; 667 } 668 #endif 669 #if ENABLE_FEATURE_FIND_REGEX 670 else if (parm == PARM_regex) { 671 action_regex *ap; 672 ap = ALLOC_ACTION(regex); 673 xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/); 674 } 675 #endif 676 #if ENABLE_FEATURE_FIND_TYPE 677 else if (parm == PARM_type) { 678 action_type *ap; 679 ap = ALLOC_ACTION(type); 680 ap->type_mask = find_type(arg1); 681 } 682 #endif 683 #if ENABLE_FEATURE_FIND_PERM 684 /* -perm mode File's permission bits are exactly mode (octal or symbolic). 685 * Symbolic modes use mode 0 as a point of departure. 686 * -perm -mode All of the permission bits mode are set for the file. 687 * -perm +mode Any of the permission bits mode are set for the file. 688 */ 689 else if (parm == PARM_perm) { 690 action_perm *ap; 691 ap = ALLOC_ACTION(perm); 692 ap->perm_char = arg1[0]; 693 arg1 = plus_minus_num(arg1); 694 ap->perm_mask = 0; 695 if (!bb_parse_mode(arg1, &ap->perm_mask)) 696 bb_error_msg_and_die("invalid mode: %s", arg1); 697 } 698 #endif 699 #if ENABLE_FEATURE_FIND_MTIME 700 else if (parm == PARM_mtime) { 701 action_mtime *ap; 702 ap = ALLOC_ACTION(mtime); 703 ap->mtime_char = arg1[0]; 704 ap->mtime_days = xatoul(plus_minus_num(arg1)); 705 } 706 #endif 707 #if ENABLE_FEATURE_FIND_MMIN 708 else if (parm == PARM_mmin) { 709 action_mmin *ap; 710 ap = ALLOC_ACTION(mmin); 711 ap->mmin_char = arg1[0]; 712 ap->mmin_mins = xatoul(plus_minus_num(arg1)); 713 } 714 #endif 715 #if ENABLE_FEATURE_FIND_NEWER 716 else if (parm == PARM_newer) { 717 struct stat stat_newer; 718 action_newer *ap; 719 ap = ALLOC_ACTION(newer); 720 xstat(arg1, &stat_newer); 721 ap->newer_mtime = stat_newer.st_mtime; 722 } 723 #endif 724 #if ENABLE_FEATURE_FIND_INUM 725 else if (parm == PARM_inum) { 726 action_inum *ap; 727 ap = ALLOC_ACTION(inum); 728 ap->inode_num = xatoul(arg1); 729 } 730 #endif 731 #if ENABLE_FEATURE_FIND_USER 732 else if (parm == PARM_user) { 733 action_user *ap; 734 ap = ALLOC_ACTION(user); 735 ap->uid = bb_strtou(arg1, NULL, 10); 736 if (errno) 737 ap->uid = xuname2uid(arg1); 738 } 739 #endif 740 #if ENABLE_FEATURE_FIND_GROUP 741 else if (parm == PARM_group) { 742 action_group *ap; 743 ap = ALLOC_ACTION(group); 744 ap->gid = bb_strtou(arg1, NULL, 10); 745 if (errno) 746 ap->gid = xgroup2gid(arg1); 747 } 748 #endif 749 #if ENABLE_FEATURE_FIND_SIZE 750 else if (parm == PARM_size) { 751 /* -size n[bckw]: file uses n units of space 752 * b (default): units are 512-byte blocks 753 * c: 1 byte 754 * k: kilobytes 755 * w: 2-byte words 756 */ 757 #if ENABLE_LFS 758 #define XATOU_SFX xatoull_sfx 150 759 #else 151 puts(fileName); 152 #endif 153 no_match: 154 return (TRUE); 155 } 156 157 #ifdef CONFIG_FEATURE_FIND_TYPE 158 static int find_type(char *type) 159 { 160 int mask = 0; 161 162 switch (type[0]) { 163 case 'b': 164 mask = S_IFBLK; 165 break; 166 case 'c': 167 mask = S_IFCHR; 168 break; 169 case 'd': 170 mask = S_IFDIR; 171 break; 172 case 'p': 173 mask = S_IFIFO; 174 break; 175 case 'f': 176 mask = S_IFREG; 177 break; 178 case 'l': 179 mask = S_IFLNK; 180 break; 181 case 's': 182 mask = S_IFSOCK; 183 break; 184 } 185 186 if (mask == 0 || type[1] != '\0') 187 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); 188 189 return mask; 190 } 191 #endif 192 760 #define XATOU_SFX xatoul_sfx 761 #endif 762 static const struct suffix_mult find_suffixes[] = { 763 { "c", 1 }, 764 { "w", 2 }, 765 { "", 512 }, 766 { "b", 512 }, 767 { "k", 1024 }, 768 { } 769 }; 770 action_size *ap; 771 ap = ALLOC_ACTION(size); 772 ap->size_char = arg1[0]; 773 ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes); 774 } 775 #endif 776 #if ENABLE_FEATURE_FIND_CONTEXT 777 else if (parm == PARM_context) { 778 action_context *ap; 779 ap = ALLOC_ACTION(context); 780 ap->context = NULL; 781 /* SELinux headers erroneously declare non-const parameter */ 782 if (selinux_raw_to_trans_context((char*)arg1, &ap->context)) 783 bb_perror_msg("%s", arg1); 784 } 785 #endif 786 else { 787 bb_error_msg("unrecognized: %s", arg); 788 bb_show_usage(); 789 } 790 argv++; 791 } 792 return appp; 793 #undef ALLOC_ACTION 794 } 795 796 797 int find_main(int argc, char **argv); 193 798 int find_main(int argc, char **argv) 194 799 { 195 int dereference = FALSE; 800 static const char options[] ALIGN1 = 801 "-follow\0" 802 USE_FEATURE_FIND_XDEV( "-xdev\0" ) 803 USE_FEATURE_FIND_MAXDEPTH("-maxdepth\0") 804 ; 805 enum { 806 OPT_FOLLOW, 807 USE_FEATURE_FIND_XDEV( OPT_XDEV ,) 808 USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,) 809 }; 810 811 char *arg; 812 char **argp; 196 813 int i, firstopt, status = EXIT_SUCCESS; 814 #if ENABLE_FEATURE_FIND_MAXDEPTH 815 int maxdepth = INT_MAX; 816 #endif 197 817 198 818 for (firstopt = 1; firstopt < argc; firstopt++) { 199 819 if (argv[firstopt][0] == '-') 200 820 break; 201 } 202 203 /* Parse any options */ 204 for (i = firstopt; i < argc; i++) { 205 if (strcmp(argv[i], "-follow") == 0) 206 dereference = TRUE; 207 else if (strcmp(argv[i], "-print") == 0) { 208 ; 209 } 210 #ifdef CONFIG_FEATURE_FIND_PRINT0 211 else if (strcmp(argv[i], "-print0") == 0) 212 printsep = '\0'; 213 #endif 214 else if (strcmp(argv[i], "-name") == 0) { 215 if (++i == argc) 216 bb_error_msg_and_die(bb_msg_requires_arg, "-name"); 217 pattern = argv[i]; 218 #ifdef CONFIG_FEATURE_FIND_TYPE 219 } else if (strcmp(argv[i], "-type") == 0) { 220 if (++i == argc) 221 bb_error_msg_and_die(bb_msg_requires_arg, "-type"); 222 type_mask = find_type(argv[i]); 223 #endif 224 #ifdef CONFIG_FEATURE_FIND_PERM 225 } else if (strcmp(argv[i], "-perm") == 0) { 226 char *end; 227 if (++i == argc) 228 bb_error_msg_and_die(bb_msg_requires_arg, "-perm"); 229 perm_mask = strtol(argv[i], &end, 8); 230 if ((end[0] != '\0') || (perm_mask > 07777)) 231 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-perm"); 232 if ((perm_char = argv[i][0]) == '-') 233 perm_mask = -perm_mask; 234 #endif 235 #ifdef CONFIG_FEATURE_FIND_MTIME 236 } else if (strcmp(argv[i], "-mtime") == 0) { 237 char *end; 238 if (++i == argc) 239 bb_error_msg_and_die(bb_msg_requires_arg, "-mtime"); 240 mtime_days = strtol(argv[i], &end, 10); 241 if (end[0] != '\0') 242 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-mtime"); 243 if ((mtime_char = argv[i][0]) == '-') 244 mtime_days = -mtime_days; 245 #endif 246 #ifdef CONFIG_FEATURE_FIND_MMIN 247 } else if (strcmp(argv[i], "-mmin") == 0) { 248 char *end; 249 if (++i == argc) 250 bb_error_msg_and_die(bb_msg_requires_arg, "-mmin"); 251 mmin_mins = strtol(argv[i], &end, 10); 252 if (end[0] != '\0') 253 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-mmin"); 254 if ((mmin_char = argv[i][0]) == '-') 255 mmin_mins = -mmin_mins; 256 #endif 257 #ifdef CONFIG_FEATURE_FIND_XDEV 258 } else if (strcmp(argv[i], "-xdev") == 0) { 821 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!')) 822 break; 823 #if ENABLE_FEATURE_FIND_PAREN 824 if (LONE_CHAR(argv[firstopt], '(')) 825 break; 826 #endif 827 } 828 if (firstopt == 1) { 829 argv[0] = (char*)"."; 830 argv--; 831 firstopt++; 832 } 833 834 /* All options always return true. They always take effect 835 * rather than being processed only when their place in the 836 * expression is reached. 837 * We implement: -follow, -xdev, -maxdepth 838 */ 839 /* Process options, and replace then with -a */ 840 /* (-a will be ignored by recursive parser later) */ 841 argp = &argv[firstopt]; 842 while ((arg = argp[0])) { 843 int opt = index_in_strings(options, arg); 844 if (opt == OPT_FOLLOW) { 845 recurse_flags |= ACTION_FOLLOWLINKS; 846 argp[0] = (char*)"-a"; 847 } 848 #if ENABLE_FEATURE_FIND_XDEV 849 if (opt == OPT_XDEV) { 259 850 struct stat stbuf; 260 261 xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1; 262 xdev_dev = xmalloc ( xdev_count * sizeof( dev_t )); 263 264 if ( firstopt == 1 ) { 265 xstat ( ".", &stbuf ); 266 xdev_dev [0] = stbuf. st_dev; 267 } 268 else { 269 851 if (!xdev_count) { 852 xdev_count = firstopt - 1; 853 xdev_dev = xmalloc(xdev_count * sizeof(dev_t)); 270 854 for (i = 1; i < firstopt; i++) { 271 xstat ( argv [i], &stbuf ); 272 xdev_dev [i-1] = stbuf. st_dev; 855 /* not xstat(): shouldn't bomb out on 856 * "find not_exist exist -xdev" */ 857 if (stat(argv[i], &stbuf)) 858 stbuf.st_dev = -1L; 859 xdev_dev[i-1] = stbuf.st_dev; 273 860 } 274 861 } 275 #endif 276 #ifdef CONFIG_FEATURE_FIND_NEWER 277 } else if (strcmp(argv[i], "-newer") == 0) { 278 struct stat stat_newer; 279 if (++i == argc) 280 bb_error_msg_and_die(bb_msg_requires_arg, "-newer"); 281 xstat (argv[i], &stat_newer); 282 newer_mtime = stat_newer.st_mtime; 283 #endif 284 #ifdef CONFIG_FEATURE_FIND_INUM 285 } else if (strcmp(argv[i], "-inum") == 0) { 286 char *end; 287 if (++i == argc) 288 bb_error_msg_and_die(bb_msg_requires_arg, "-inum"); 289 inode_num = strtol(argv[i], &end, 10); 290 if (end[0] != '\0') 291 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-inum"); 292 #endif 293 #ifdef CONFIG_FEATURE_FIND_EXEC 294 } else if (strcmp(argv[i], "-exec") == 0) { 295 int b_pos; 296 char *cmd_string = ""; 297 298 while (i++) { 299 if (i == argc) 300 bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); 301 if (*argv[i] == ';') 302 break; 303 cmd_string = bb_xasprintf("%s %s", cmd_string, argv[i]); 304 } 305 306 if (*cmd_string == 0) 307 bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); 308 cmd_string++; 309 exec_str = xmalloc(sizeof(char *)); 310 311 while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) { 312 num_matches++; 313 exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *)); 314 exec_str[num_matches - 1] = bb_xstrndup(cmd_string, b_pos); 315 cmd_string += b_pos + 2; 316 } 317 exec_str[num_matches] = bb_xstrdup(cmd_string); 318 exec_opt = 1; 319 #endif 320 } else 321 bb_show_usage(); 322 } 323 324 if (firstopt == 1) { 325 if (! recursive_action(".", TRUE, dereference, FALSE, fileAction, 326 fileAction, NULL)) 862 argp[0] = (char*)"-a"; 863 } 864 #endif 865 #if ENABLE_FEATURE_FIND_MAXDEPTH 866 if (opt == OPT_MAXDEPTH) { 867 if (!argp[1]) 868 bb_show_usage(); 869 maxdepth = xatoi_u(argp[1]); 870 argp[0] = (char*)"-a"; 871 argp[1] = (char*)"-a"; 872 argp++; 873 } 874 #endif 875 argp++; 876 } 877 878 actions = parse_params(&argv[firstopt]); 879 880 for (i = 1; i < firstopt; i++) { 881 if (!recursive_action(argv[i], 882 recurse_flags, /* flags */ 883 fileAction, /* file action */ 884 fileAction, /* dir action */ 885 #if ENABLE_FEATURE_FIND_MAXDEPTH 886 /* double cast suppresses 887 * "cast to ptr from int of different size" */ 888 (void*)(ptrdiff_t)maxdepth,/* user data */ 889 #else 890 NULL, /* user data */ 891 #endif 892 0)) /* depth */ 327 893 status = EXIT_FAILURE; 328 } else { 329 for (i = 1; i < firstopt; i++) { 330 if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, 331 fileAction, NULL)) 332 status = EXIT_FAILURE; 333 } 334 } 335 894 } 336 895 return status; 337 896 }
Note:
See TracChangeset
for help on using the changeset viewer.