find.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini find implementation for busybox
  4. *
  5. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  6. *
  7. * Reworked by David Douthitt <n9ubh@callsign.net> and
  8. * Matt Kraai <kraai@alumni.carnegiemellon.edu>.
  9. *
  10. * Licensed under GPLv2, see file LICENSE in this source tree.
  11. */
  12. /* findutils-4.1.20:
  13. *
  14. * # find file.txt -exec 'echo {}' '{} {}' ';'
  15. * find: echo file.txt: No such file or directory
  16. * # find file.txt -exec 'echo' '{} {}' '; '
  17. * find: missing argument to `-exec'
  18. * # find file.txt -exec 'echo {}' '{} {}' ';' junk
  19. * find: paths must precede expression
  20. * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';'
  21. * find: paths must precede expression
  22. * # find file.txt -exec 'echo' '{} {}' ';'
  23. * file.txt file.txt
  24. * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ]))
  25. * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';'
  26. * file.txt file.txt
  27. * file.txt
  28. * /tmp
  29. * # find -name '*.c' -o -name '*.h'
  30. * [shows files, *.c and *.h intermixed]
  31. * # find file.txt -name '*f*' -o -name '*t*'
  32. * file.txt
  33. * # find file.txt -name '*z*' -o -name '*t*'
  34. * file.txt
  35. * # find file.txt -name '*f*' -o -name '*z*'
  36. * file.txt
  37. *
  38. * # find t z -name '*t*' -print -o -name '*z*'
  39. * t
  40. * # find t z t z -name '*t*' -o -name '*z*' -print
  41. * z
  42. * z
  43. * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print
  44. * (no output)
  45. */
  46. /* Testing script
  47. * ./busybox find "$@" | tee /tmp/bb_find
  48. * echo ==================
  49. * /path/to/gnu/find "$@" | tee /tmp/std_find
  50. * echo ==================
  51. * diff -u /tmp/std_find /tmp/bb_find && echo Identical
  52. */
  53. //applet:IF_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_DROP, find))
  54. //kbuild:lib-$(CONFIG_FIND) += find.o
  55. //config:config FIND
  56. //config: bool "find"
  57. //config: default y
  58. //config: help
  59. //config: find is used to search your system to find specified files.
  60. //config:
  61. //config:config FEATURE_FIND_PRINT0
  62. //config: bool "Enable -print0: NUL-terminated output"
  63. //config: default y
  64. //config: depends on FIND
  65. //config: help
  66. //config: Causes output names to be separated by a NUL character
  67. //config: rather than a newline. This allows names that contain
  68. //config: newlines and other whitespace to be more easily
  69. //config: interpreted by other programs.
  70. //config:
  71. //config:config FEATURE_FIND_MTIME
  72. //config: bool "Enable -mtime: modified time matching"
  73. //config: default y
  74. //config: depends on FIND
  75. //config: help
  76. //config: Allow searching based on the modification time of
  77. //config: files, in days.
  78. //config:
  79. //config:config FEATURE_FIND_MMIN
  80. //config: bool "Enable -mmin: modified time matching by minutes"
  81. //config: default y
  82. //config: depends on FIND
  83. //config: help
  84. //config: Allow searching based on the modification time of
  85. //config: files, in minutes.
  86. //config:
  87. //config:config FEATURE_FIND_PERM
  88. //config: bool "Enable -perm: permissions matching"
  89. //config: default y
  90. //config: depends on FIND
  91. //config: help
  92. //config: Enable searching based on file permissions.
  93. //config:
  94. //config:config FEATURE_FIND_TYPE
  95. //config: bool "Enable -type: file type matching (file/dir/link/...)"
  96. //config: default y
  97. //config: depends on FIND
  98. //config: help
  99. //config: Enable searching based on file type (file,
  100. //config: directory, socket, device, etc.).
  101. //config:
  102. //config:config FEATURE_FIND_XDEV
  103. //config: bool "Enable -xdev: 'stay in filesystem'"
  104. //config: default y
  105. //config: depends on FIND
  106. //config: help
  107. //config: This option allows find to restrict searches to a single filesystem.
  108. //config:
  109. //config:config FEATURE_FIND_MAXDEPTH
  110. //config: bool "Enable -maxdepth N"
  111. //config: default y
  112. //config: depends on FIND
  113. //config: help
  114. //config: This option enables -maxdepth N option.
  115. //config:
  116. //config:config FEATURE_FIND_NEWER
  117. //config: bool "Enable -newer: compare file modification times"
  118. //config: default y
  119. //config: depends on FIND
  120. //config: help
  121. //config: Support the 'find -newer' option for finding any files which have
  122. //config: a modified time that is more recent than the specified FILE.
  123. //config:
  124. //config:config FEATURE_FIND_INUM
  125. //config: bool "Enable -inum: inode number matching"
  126. //config: default y
  127. //config: depends on FIND
  128. //config: help
  129. //config: Support the 'find -inum' option for searching by inode number.
  130. //config:
  131. //config:config FEATURE_FIND_EXEC
  132. //config: bool "Enable -exec: execute commands"
  133. //config: default y
  134. //config: depends on FIND
  135. //config: help
  136. //config: Support the 'find -exec' option for executing commands based upon
  137. //config: the files matched.
  138. //config:
  139. //config:config FEATURE_FIND_USER
  140. //config: bool "Enable -user: username/uid matching"
  141. //config: default y
  142. //config: depends on FIND
  143. //config: help
  144. //config: Support the 'find -user' option for searching by username or uid.
  145. //config:
  146. //config:config FEATURE_FIND_GROUP
  147. //config: bool "Enable -group: group/gid matching"
  148. //config: default y
  149. //config: depends on FIND
  150. //config: help
  151. //config: Support the 'find -group' option for searching by group name or gid.
  152. //config:
  153. //config:config FEATURE_FIND_NOT
  154. //config: bool "Enable the 'not' (!) operator"
  155. //config: default y
  156. //config: depends on FIND
  157. //config: help
  158. //config: Support the '!' operator to invert the test results.
  159. //config: If 'Enable full-blown desktop' is enabled, then will also support
  160. //config: the non-POSIX notation '-not'.
  161. //config:
  162. //config:config FEATURE_FIND_DEPTH
  163. //config: bool "Enable -depth"
  164. //config: default y
  165. //config: depends on FIND
  166. //config: help
  167. //config: Process each directory's contents before the directory itself.
  168. //config:
  169. //config:config FEATURE_FIND_PAREN
  170. //config: bool "Enable parens in options"
  171. //config: default y
  172. //config: depends on FIND
  173. //config: help
  174. //config: Enable usage of parens '(' to specify logical order of arguments.
  175. //config:
  176. //config:config FEATURE_FIND_SIZE
  177. //config: bool "Enable -size: file size matching"
  178. //config: default y
  179. //config: depends on FIND
  180. //config: help
  181. //config: Support the 'find -size' option for searching by file size.
  182. //config:
  183. //config:config FEATURE_FIND_PRUNE
  184. //config: bool "Enable -prune: exclude subdirectories"
  185. //config: default y
  186. //config: depends on FIND
  187. //config: help
  188. //config: If the file is a directory, dont descend into it. Useful for
  189. //config: exclusion .svn and CVS directories.
  190. //config:
  191. //config:config FEATURE_FIND_DELETE
  192. //config: bool "Enable -delete: delete files/dirs"
  193. //config: default y
  194. //config: depends on FIND && FEATURE_FIND_DEPTH
  195. //config: help
  196. //config: Support the 'find -delete' option for deleting files and directories.
  197. //config: WARNING: This option can do much harm if used wrong. Busybox will not
  198. //config: try to protect the user from doing stupid things. Use with care.
  199. //config:
  200. //config:config FEATURE_FIND_PATH
  201. //config: bool "Enable -path: match pathname with shell pattern"
  202. //config: default y
  203. //config: depends on FIND
  204. //config: help
  205. //config: The -path option matches whole pathname instead of just filename.
  206. //config:
  207. //config:config FEATURE_FIND_REGEX
  208. //config: bool "Enable -regex: match pathname with regex"
  209. //config: default y
  210. //config: depends on FIND
  211. //config: help
  212. //config: The -regex option matches whole pathname against regular expression.
  213. //config:
  214. //config:config FEATURE_FIND_CONTEXT
  215. //config: bool "Enable -context: security context matching"
  216. //config: default n
  217. //config: depends on FIND && SELINUX
  218. //config: help
  219. //config: Support the 'find -context' option for matching security context.
  220. //config:
  221. //config:config FEATURE_FIND_LINKS
  222. //config: bool "Enable -links: link count matching"
  223. //config: default y
  224. //config: depends on FIND
  225. //config: help
  226. //config: Support the 'find -links' option for matching number of links.
  227. #include <fnmatch.h>
  228. #include "libbb.h"
  229. #if ENABLE_FEATURE_FIND_REGEX
  230. #include "xregex.h"
  231. #endif
  232. /* This is a NOEXEC applet. Be very careful! */
  233. typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
  234. typedef struct {
  235. action_fp f;
  236. #if ENABLE_FEATURE_FIND_NOT
  237. bool invert;
  238. #endif
  239. } action;
  240. #define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
  241. #define ACTF(name) \
  242. static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
  243. const struct stat *statbuf UNUSED_PARAM, \
  244. action_##name* ap UNUSED_PARAM)
  245. ACTS(print)
  246. ACTS(name, const char *pattern; bool iname;)
  247. IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
  248. IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
  249. IF_FEATURE_FIND_PRINT0( ACTS(print0))
  250. IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
  251. IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
  252. IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
  253. IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
  254. IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
  255. IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
  256. IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;))
  257. IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;))
  258. IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
  259. IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
  260. IF_FEATURE_FIND_PRUNE( ACTS(prune))
  261. IF_FEATURE_FIND_DELETE( ACTS(delete))
  262. IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;))
  263. IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
  264. IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
  265. struct globals {
  266. IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
  267. IF_FEATURE_FIND_XDEV(int xdev_count;)
  268. action ***actions;
  269. bool need_print;
  270. recurse_flags_t recurse_flags;
  271. } FIX_ALIASING;
  272. #define G (*(struct globals*)&bb_common_bufsiz1)
  273. #define INIT_G() do { \
  274. struct G_sizecheck { \
  275. char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
  276. }; \
  277. /* we have to zero it out because of NOEXEC */ \
  278. memset(&G, 0, offsetof(struct globals, need_print)); \
  279. G.need_print = 1; \
  280. G.recurse_flags = ACTION_RECURSE; \
  281. } while (0)
  282. #if ENABLE_FEATURE_FIND_EXEC
  283. static unsigned count_subst(const char *str)
  284. {
  285. unsigned count = 0;
  286. while ((str = strstr(str, "{}")) != NULL) {
  287. count++;
  288. str++;
  289. }
  290. return count;
  291. }
  292. static char* subst(const char *src, unsigned count, const char* filename)
  293. {
  294. char *buf, *dst, *end;
  295. size_t flen = strlen(filename);
  296. /* we replace each '{}' with filename: growth by strlen-2 */
  297. buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
  298. while ((end = strstr(src, "{}"))) {
  299. memcpy(dst, src, end - src);
  300. dst += end - src;
  301. src = end + 2;
  302. memcpy(dst, filename, flen);
  303. dst += flen;
  304. }
  305. strcpy(dst, src);
  306. return buf;
  307. }
  308. #endif
  309. /* Return values of ACTFs ('action functions') are a bit mask:
  310. * bit 1=1: prune (use SKIP constant for setting it)
  311. * bit 0=1: matched successfully (TRUE)
  312. */
  313. static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
  314. {
  315. int cur_group;
  316. int cur_action;
  317. int rc = 0;
  318. action **app, *ap;
  319. /* "action group" is a set of actions ANDed together.
  320. * groups are ORed together.
  321. * We simply evaluate each group until we find one in which all actions
  322. * succeed. */
  323. /* -prune is special: if it is encountered, then we won't
  324. * descend into current directory. It doesn't matter whether
  325. * action group (in which -prune sits) will succeed or not:
  326. * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir
  327. * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs
  328. * not starting with 'f' */
  329. /* We invert TRUE bit (bit 0). Now 1 there means 'failure'.
  330. * and bitwise OR in "rc |= TRUE ^ ap->f()" will:
  331. * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'.
  332. * On return, bit is restored. */
  333. cur_group = -1;
  334. while ((app = appp[++cur_group]) != NULL) {
  335. rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
  336. cur_action = -1;
  337. while (1) {
  338. ap = app[++cur_action];
  339. if (!ap) /* all actions in group were successful */
  340. return rc ^ TRUE; /* restore TRUE bit */
  341. rc |= TRUE ^ ap->f(fileName, statbuf, ap);
  342. #if ENABLE_FEATURE_FIND_NOT
  343. if (ap->invert) rc ^= TRUE;
  344. #endif
  345. if (rc & TRUE) /* current group failed, try next */
  346. break;
  347. }
  348. }
  349. return rc ^ TRUE; /* restore TRUE bit */
  350. }
  351. ACTF(name)
  352. {
  353. const char *tmp = bb_basename(fileName);
  354. if (tmp != fileName && *tmp == '\0') {
  355. /* "foo/bar/". Oh no... go back to 'b' */
  356. tmp--;
  357. while (tmp != fileName && *--tmp != '/')
  358. continue;
  359. if (*tmp == '/')
  360. tmp++;
  361. }
  362. /* Was using FNM_PERIOD flag too,
  363. * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
  364. * find -name '*foo' should match .foo too:
  365. */
  366. return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
  367. }
  368. #if ENABLE_FEATURE_FIND_PATH
  369. ACTF(path)
  370. {
  371. return fnmatch(ap->pattern, fileName, 0) == 0;
  372. }
  373. #endif
  374. #if ENABLE_FEATURE_FIND_REGEX
  375. ACTF(regex)
  376. {
  377. regmatch_t match;
  378. if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
  379. return 0; /* no match */
  380. if (match.rm_so)
  381. return 0; /* match doesn't start at pos 0 */
  382. if (fileName[match.rm_eo])
  383. return 0; /* match doesn't end exactly at end of pathname */
  384. return 1;
  385. }
  386. #endif
  387. #if ENABLE_FEATURE_FIND_TYPE
  388. ACTF(type)
  389. {
  390. return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
  391. }
  392. #endif
  393. #if ENABLE_FEATURE_FIND_PERM
  394. ACTF(perm)
  395. {
  396. /* -perm +mode: at least one of perm_mask bits are set */
  397. if (ap->perm_char == '+')
  398. return (statbuf->st_mode & ap->perm_mask) != 0;
  399. /* -perm -mode: all of perm_mask are set */
  400. if (ap->perm_char == '-')
  401. return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
  402. /* -perm mode: file mode must match perm_mask */
  403. return (statbuf->st_mode & 07777) == ap->perm_mask;
  404. }
  405. #endif
  406. #if ENABLE_FEATURE_FIND_MTIME
  407. ACTF(mtime)
  408. {
  409. time_t file_age = time(NULL) - statbuf->st_mtime;
  410. time_t mtime_secs = ap->mtime_days * 24*60*60;
  411. if (ap->mtime_char == '+')
  412. return file_age >= mtime_secs + 24*60*60;
  413. if (ap->mtime_char == '-')
  414. return file_age < mtime_secs;
  415. /* just numeric mtime */
  416. return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
  417. }
  418. #endif
  419. #if ENABLE_FEATURE_FIND_MMIN
  420. ACTF(mmin)
  421. {
  422. time_t file_age = time(NULL) - statbuf->st_mtime;
  423. time_t mmin_secs = ap->mmin_mins * 60;
  424. if (ap->mmin_char == '+')
  425. return file_age >= mmin_secs + 60;
  426. if (ap->mmin_char == '-')
  427. return file_age < mmin_secs;
  428. /* just numeric mmin */
  429. return file_age >= mmin_secs && file_age < (mmin_secs + 60);
  430. }
  431. #endif
  432. #if ENABLE_FEATURE_FIND_NEWER
  433. ACTF(newer)
  434. {
  435. return (ap->newer_mtime < statbuf->st_mtime);
  436. }
  437. #endif
  438. #if ENABLE_FEATURE_FIND_INUM
  439. ACTF(inum)
  440. {
  441. return (statbuf->st_ino == ap->inode_num);
  442. }
  443. #endif
  444. #if ENABLE_FEATURE_FIND_EXEC
  445. ACTF(exec)
  446. {
  447. int i, rc;
  448. #if ENABLE_USE_PORTABLE_CODE
  449. char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
  450. #else /* gcc 4.3.1 generates smaller code: */
  451. char *argv[ap->exec_argc + 1];
  452. #endif
  453. for (i = 0; i < ap->exec_argc; i++)
  454. argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
  455. argv[i] = NULL; /* terminate the list */
  456. rc = spawn_and_wait(argv);
  457. if (rc < 0)
  458. bb_simple_perror_msg(argv[0]);
  459. i = 0;
  460. while (argv[i])
  461. free(argv[i++]);
  462. return rc == 0; /* return 1 if exitcode 0 */
  463. }
  464. #endif
  465. #if ENABLE_FEATURE_FIND_USER
  466. ACTF(user)
  467. {
  468. return (statbuf->st_uid == ap->uid);
  469. }
  470. #endif
  471. #if ENABLE_FEATURE_FIND_GROUP
  472. ACTF(group)
  473. {
  474. return (statbuf->st_gid == ap->gid);
  475. }
  476. #endif
  477. #if ENABLE_FEATURE_FIND_PRINT0
  478. ACTF(print0)
  479. {
  480. printf("%s%c", fileName, '\0');
  481. return TRUE;
  482. }
  483. #endif
  484. ACTF(print)
  485. {
  486. puts(fileName);
  487. return TRUE;
  488. }
  489. #if ENABLE_FEATURE_FIND_PAREN
  490. ACTF(paren)
  491. {
  492. return exec_actions(ap->subexpr, fileName, statbuf);
  493. }
  494. #endif
  495. #if ENABLE_FEATURE_FIND_SIZE
  496. ACTF(size)
  497. {
  498. if (ap->size_char == '+')
  499. return statbuf->st_size > ap->size;
  500. if (ap->size_char == '-')
  501. return statbuf->st_size < ap->size;
  502. return statbuf->st_size == ap->size;
  503. }
  504. #endif
  505. #if ENABLE_FEATURE_FIND_PRUNE
  506. /*
  507. * -prune: if -depth is not given, return true and do not descend
  508. * current dir; if -depth is given, return false with no effect.
  509. * Example:
  510. * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
  511. */
  512. ACTF(prune)
  513. {
  514. return SKIP + TRUE;
  515. }
  516. #endif
  517. #if ENABLE_FEATURE_FIND_DELETE
  518. ACTF(delete)
  519. {
  520. int rc;
  521. if (S_ISDIR(statbuf->st_mode)) {
  522. rc = rmdir(fileName);
  523. } else {
  524. rc = unlink(fileName);
  525. }
  526. if (rc < 0)
  527. bb_simple_perror_msg(fileName);
  528. return TRUE;
  529. }
  530. #endif
  531. #if ENABLE_FEATURE_FIND_CONTEXT
  532. ACTF(context)
  533. {
  534. security_context_t con;
  535. int rc;
  536. if (G.recurse_flags & ACTION_FOLLOWLINKS) {
  537. rc = getfilecon(fileName, &con);
  538. } else {
  539. rc = lgetfilecon(fileName, &con);
  540. }
  541. if (rc < 0)
  542. return FALSE;
  543. rc = strcmp(ap->context, con);
  544. freecon(con);
  545. return rc == 0;
  546. }
  547. #endif
  548. #if ENABLE_FEATURE_FIND_LINKS
  549. ACTF(links)
  550. {
  551. switch(ap->links_char) {
  552. case '-' : return (statbuf->st_nlink < ap->links_count);
  553. case '+' : return (statbuf->st_nlink > ap->links_count);
  554. default: return (statbuf->st_nlink == ap->links_count);
  555. }
  556. }
  557. #endif
  558. static int FAST_FUNC fileAction(const char *fileName,
  559. struct stat *statbuf,
  560. void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
  561. int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
  562. {
  563. int r;
  564. #if ENABLE_FEATURE_FIND_MAXDEPTH
  565. #define minmaxdepth ((int*)userData)
  566. if (depth < minmaxdepth[0])
  567. return TRUE; /* skip this, continue recursing */
  568. if (depth > minmaxdepth[1])
  569. return SKIP; /* stop recursing */
  570. #endif
  571. r = exec_actions(G.actions, fileName, statbuf);
  572. /* Had no explicit -print[0] or -exec? then print */
  573. if ((r & TRUE) && G.need_print)
  574. puts(fileName);
  575. #if ENABLE_FEATURE_FIND_MAXDEPTH
  576. if (S_ISDIR(statbuf->st_mode)) {
  577. if (depth == minmaxdepth[1])
  578. return SKIP;
  579. }
  580. #endif
  581. #if ENABLE_FEATURE_FIND_XDEV
  582. /* -xdev stops on mountpoints, but AFTER mountpoit itself
  583. * is processed as usual */
  584. if (S_ISDIR(statbuf->st_mode)) {
  585. if (G.xdev_count) {
  586. int i;
  587. for (i = 0; i < G.xdev_count; i++) {
  588. if (G.xdev_dev[i] == statbuf->st_dev)
  589. goto found;
  590. }
  591. return SKIP;
  592. found: ;
  593. }
  594. }
  595. #endif
  596. /* Cannot return 0: our caller, recursive_action(),
  597. * will perror() and skip dirs (if called on dir) */
  598. return (r & SKIP) ? SKIP : TRUE;
  599. #undef minmaxdepth
  600. }
  601. #if ENABLE_FEATURE_FIND_TYPE
  602. static int find_type(const char *type)
  603. {
  604. int mask = 0;
  605. if (*type == 'b')
  606. mask = S_IFBLK;
  607. else if (*type == 'c')
  608. mask = S_IFCHR;
  609. else if (*type == 'd')
  610. mask = S_IFDIR;
  611. else if (*type == 'p')
  612. mask = S_IFIFO;
  613. else if (*type == 'f')
  614. mask = S_IFREG;
  615. else if (*type == 'l')
  616. mask = S_IFLNK;
  617. else if (*type == 's')
  618. mask = S_IFSOCK;
  619. if (mask == 0 || type[1] != '\0')
  620. bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
  621. return mask;
  622. }
  623. #endif
  624. #if ENABLE_FEATURE_FIND_PERM \
  625. || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
  626. || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS
  627. static const char* plus_minus_num(const char* str)
  628. {
  629. if (*str == '-' || *str == '+')
  630. str++;
  631. return str;
  632. }
  633. #endif
  634. static action*** parse_params(char **argv)
  635. {
  636. enum {
  637. PARM_a ,
  638. PARM_o ,
  639. IF_FEATURE_FIND_NOT( PARM_char_not ,)
  640. #if ENABLE_DESKTOP
  641. PARM_and ,
  642. PARM_or ,
  643. IF_FEATURE_FIND_NOT( PARM_not ,)
  644. #endif
  645. PARM_print ,
  646. IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
  647. IF_FEATURE_FIND_DEPTH( PARM_depth ,)
  648. IF_FEATURE_FIND_PRUNE( PARM_prune ,)
  649. IF_FEATURE_FIND_DELETE( PARM_delete ,)
  650. IF_FEATURE_FIND_EXEC( PARM_exec ,)
  651. IF_FEATURE_FIND_PAREN( PARM_char_brace,)
  652. /* All options starting from here require argument */
  653. PARM_name ,
  654. PARM_iname ,
  655. IF_FEATURE_FIND_PATH( PARM_path ,)
  656. IF_FEATURE_FIND_REGEX( PARM_regex ,)
  657. IF_FEATURE_FIND_TYPE( PARM_type ,)
  658. IF_FEATURE_FIND_PERM( PARM_perm ,)
  659. IF_FEATURE_FIND_MTIME( PARM_mtime ,)
  660. IF_FEATURE_FIND_MMIN( PARM_mmin ,)
  661. IF_FEATURE_FIND_NEWER( PARM_newer ,)
  662. IF_FEATURE_FIND_INUM( PARM_inum ,)
  663. IF_FEATURE_FIND_USER( PARM_user ,)
  664. IF_FEATURE_FIND_GROUP( PARM_group ,)
  665. IF_FEATURE_FIND_SIZE( PARM_size ,)
  666. IF_FEATURE_FIND_CONTEXT(PARM_context ,)
  667. IF_FEATURE_FIND_LINKS( PARM_links ,)
  668. };
  669. static const char params[] ALIGN1 =
  670. "-a\0"
  671. "-o\0"
  672. IF_FEATURE_FIND_NOT( "!\0" )
  673. #if ENABLE_DESKTOP
  674. "-and\0"
  675. "-or\0"
  676. IF_FEATURE_FIND_NOT( "-not\0" )
  677. #endif
  678. "-print\0"
  679. IF_FEATURE_FIND_PRINT0( "-print0\0" )
  680. IF_FEATURE_FIND_DEPTH( "-depth\0" )
  681. IF_FEATURE_FIND_PRUNE( "-prune\0" )
  682. IF_FEATURE_FIND_DELETE( "-delete\0" )
  683. IF_FEATURE_FIND_EXEC( "-exec\0" )
  684. IF_FEATURE_FIND_PAREN( "(\0" )
  685. /* All options starting from here require argument */
  686. "-name\0"
  687. "-iname\0"
  688. IF_FEATURE_FIND_PATH( "-path\0" )
  689. IF_FEATURE_FIND_REGEX( "-regex\0" )
  690. IF_FEATURE_FIND_TYPE( "-type\0" )
  691. IF_FEATURE_FIND_PERM( "-perm\0" )
  692. IF_FEATURE_FIND_MTIME( "-mtime\0" )
  693. IF_FEATURE_FIND_MMIN( "-mmin\0" )
  694. IF_FEATURE_FIND_NEWER( "-newer\0" )
  695. IF_FEATURE_FIND_INUM( "-inum\0" )
  696. IF_FEATURE_FIND_USER( "-user\0" )
  697. IF_FEATURE_FIND_GROUP( "-group\0" )
  698. IF_FEATURE_FIND_SIZE( "-size\0" )
  699. IF_FEATURE_FIND_CONTEXT("-context\0")
  700. IF_FEATURE_FIND_LINKS( "-links\0" )
  701. ;
  702. action*** appp;
  703. unsigned cur_group = 0;
  704. unsigned cur_action = 0;
  705. IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
  706. /* This is the only place in busybox where we use nested function.
  707. * So far more standard alternatives were bigger. */
  708. /* Auto decl suppresses "func without a prototype" warning: */
  709. auto action* alloc_action(int sizeof_struct, action_fp f);
  710. action* alloc_action(int sizeof_struct, action_fp f)
  711. {
  712. action *ap;
  713. appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
  714. appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct);
  715. appp[cur_group][cur_action] = NULL;
  716. ap->f = f;
  717. IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
  718. IF_FEATURE_FIND_NOT( invert_flag = 0; )
  719. return ap;
  720. }
  721. #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
  722. appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
  723. /* Actions have side effects and return a true or false value
  724. * We implement: -print, -print0, -exec
  725. *
  726. * The rest are tests.
  727. *
  728. * Tests and actions are grouped by operators
  729. * ( expr ) Force precedence
  730. * ! expr True if expr is false
  731. * -not expr Same as ! expr
  732. * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
  733. * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
  734. * expr1 , expr2 List; both expr1 and expr2 are always evaluated
  735. * We implement: (), -a, -o
  736. */
  737. while (*argv) {
  738. const char *arg = argv[0];
  739. int parm = index_in_strings(params, arg);
  740. const char *arg1 = argv[1];
  741. if (parm >= PARM_name) {
  742. /* All options starting from -name require argument */
  743. if (!arg1)
  744. bb_error_msg_and_die(bb_msg_requires_arg, arg);
  745. argv++;
  746. }
  747. /* We can use big switch() here, but on i386
  748. * it doesn't give smaller code. Other arches? */
  749. /* --- Operators --- */
  750. if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
  751. /* no further special handling required */
  752. }
  753. else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
  754. /* start new OR group */
  755. cur_group++;
  756. appp = xrealloc(appp, (cur_group+2) * sizeof(*appp));
  757. /*appp[cur_group] = NULL; - already NULL */
  758. appp[cur_group+1] = NULL;
  759. cur_action = 0;
  760. }
  761. #if ENABLE_FEATURE_FIND_NOT
  762. else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
  763. /* also handles "find ! ! -name 'foo*'" */
  764. invert_flag ^= 1;
  765. }
  766. #endif
  767. /* --- Tests and actions --- */
  768. else if (parm == PARM_print) {
  769. G.need_print = 0;
  770. /* GNU find ignores '!' here: "find ! -print" */
  771. IF_FEATURE_FIND_NOT( invert_flag = 0; )
  772. (void) ALLOC_ACTION(print);
  773. }
  774. #if ENABLE_FEATURE_FIND_PRINT0
  775. else if (parm == PARM_print0) {
  776. G.need_print = 0;
  777. IF_FEATURE_FIND_NOT( invert_flag = 0; )
  778. (void) ALLOC_ACTION(print0);
  779. }
  780. #endif
  781. #if ENABLE_FEATURE_FIND_DEPTH
  782. else if (parm == PARM_depth) {
  783. G.recurse_flags |= ACTION_DEPTHFIRST;
  784. }
  785. #endif
  786. #if ENABLE_FEATURE_FIND_PRUNE
  787. else if (parm == PARM_prune) {
  788. IF_FEATURE_FIND_NOT( invert_flag = 0; )
  789. (void) ALLOC_ACTION(prune);
  790. }
  791. #endif
  792. #if ENABLE_FEATURE_FIND_DELETE
  793. else if (parm == PARM_delete) {
  794. G.need_print = 0;
  795. G.recurse_flags |= ACTION_DEPTHFIRST;
  796. (void) ALLOC_ACTION(delete);
  797. }
  798. #endif
  799. #if ENABLE_FEATURE_FIND_EXEC
  800. else if (parm == PARM_exec) {
  801. int i;
  802. action_exec *ap;
  803. G.need_print = 0;
  804. IF_FEATURE_FIND_NOT( invert_flag = 0; )
  805. ap = ALLOC_ACTION(exec);
  806. ap->exec_argv = ++argv; /* first arg after -exec */
  807. /*ap->exec_argc = 0; - ALLOC_ACTION did it */
  808. while (1) {
  809. if (!*argv) /* did not see ';' or '+' until end */
  810. bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
  811. // find -exec echo Foo ">{}<" ";"
  812. // executes "echo Foo <filename>",
  813. // find -exec echo Foo ">{}<" "+"
  814. // executes "echo Foo <filename1> <filename2> <filename3>...".
  815. // TODO (so far we treat "+" just like ";")
  816. if ((argv[0][0] == ';' || argv[0][0] == '+')
  817. && argv[0][1] == '\0'
  818. ) {
  819. break;
  820. }
  821. argv++;
  822. ap->exec_argc++;
  823. }
  824. if (ap->exec_argc == 0)
  825. bb_error_msg_and_die(bb_msg_requires_arg, arg);
  826. ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
  827. i = ap->exec_argc;
  828. while (i--)
  829. ap->subst_count[i] = count_subst(ap->exec_argv[i]);
  830. }
  831. #endif
  832. #if ENABLE_FEATURE_FIND_PAREN
  833. else if (parm == PARM_char_brace) {
  834. action_paren *ap;
  835. char **endarg;
  836. unsigned nested = 1;
  837. endarg = argv;
  838. while (1) {
  839. if (!*++endarg)
  840. bb_error_msg_and_die("unpaired '('");
  841. if (LONE_CHAR(*endarg, '('))
  842. nested++;
  843. else if (LONE_CHAR(*endarg, ')') && !--nested) {
  844. *endarg = NULL;
  845. break;
  846. }
  847. }
  848. ap = ALLOC_ACTION(paren);
  849. ap->subexpr = parse_params(argv + 1);
  850. *endarg = (char*) ")"; /* restore NULLed parameter */
  851. argv = endarg;
  852. }
  853. #endif
  854. else if (parm == PARM_name || parm == PARM_iname) {
  855. action_name *ap;
  856. ap = ALLOC_ACTION(name);
  857. ap->pattern = arg1;
  858. ap->iname = (parm == PARM_iname);
  859. }
  860. #if ENABLE_FEATURE_FIND_PATH
  861. else if (parm == PARM_path) {
  862. action_path *ap;
  863. ap = ALLOC_ACTION(path);
  864. ap->pattern = arg1;
  865. }
  866. #endif
  867. #if ENABLE_FEATURE_FIND_REGEX
  868. else if (parm == PARM_regex) {
  869. action_regex *ap;
  870. ap = ALLOC_ACTION(regex);
  871. xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
  872. }
  873. #endif
  874. #if ENABLE_FEATURE_FIND_TYPE
  875. else if (parm == PARM_type) {
  876. action_type *ap;
  877. ap = ALLOC_ACTION(type);
  878. ap->type_mask = find_type(arg1);
  879. }
  880. #endif
  881. #if ENABLE_FEATURE_FIND_PERM
  882. /* -perm mode File's permission bits are exactly mode (octal or symbolic).
  883. * Symbolic modes use mode 0 as a point of departure.
  884. * -perm -mode All of the permission bits mode are set for the file.
  885. * -perm +mode Any of the permission bits mode are set for the file.
  886. */
  887. else if (parm == PARM_perm) {
  888. action_perm *ap;
  889. ap = ALLOC_ACTION(perm);
  890. ap->perm_char = arg1[0];
  891. arg1 = plus_minus_num(arg1);
  892. /*ap->perm_mask = 0; - ALLOC_ACTION did it */
  893. if (!bb_parse_mode(arg1, &ap->perm_mask))
  894. bb_error_msg_and_die("invalid mode '%s'", arg1);
  895. }
  896. #endif
  897. #if ENABLE_FEATURE_FIND_MTIME
  898. else if (parm == PARM_mtime) {
  899. action_mtime *ap;
  900. ap = ALLOC_ACTION(mtime);
  901. ap->mtime_char = arg1[0];
  902. ap->mtime_days = xatoul(plus_minus_num(arg1));
  903. }
  904. #endif
  905. #if ENABLE_FEATURE_FIND_MMIN
  906. else if (parm == PARM_mmin) {
  907. action_mmin *ap;
  908. ap = ALLOC_ACTION(mmin);
  909. ap->mmin_char = arg1[0];
  910. ap->mmin_mins = xatoul(plus_minus_num(arg1));
  911. }
  912. #endif
  913. #if ENABLE_FEATURE_FIND_NEWER
  914. else if (parm == PARM_newer) {
  915. struct stat stat_newer;
  916. action_newer *ap;
  917. ap = ALLOC_ACTION(newer);
  918. xstat(arg1, &stat_newer);
  919. ap->newer_mtime = stat_newer.st_mtime;
  920. }
  921. #endif
  922. #if ENABLE_FEATURE_FIND_INUM
  923. else if (parm == PARM_inum) {
  924. action_inum *ap;
  925. ap = ALLOC_ACTION(inum);
  926. ap->inode_num = xatoul(arg1);
  927. }
  928. #endif
  929. #if ENABLE_FEATURE_FIND_USER
  930. else if (parm == PARM_user) {
  931. action_user *ap;
  932. ap = ALLOC_ACTION(user);
  933. ap->uid = bb_strtou(arg1, NULL, 10);
  934. if (errno)
  935. ap->uid = xuname2uid(arg1);
  936. }
  937. #endif
  938. #if ENABLE_FEATURE_FIND_GROUP
  939. else if (parm == PARM_group) {
  940. action_group *ap;
  941. ap = ALLOC_ACTION(group);
  942. ap->gid = bb_strtou(arg1, NULL, 10);
  943. if (errno)
  944. ap->gid = xgroup2gid(arg1);
  945. }
  946. #endif
  947. #if ENABLE_FEATURE_FIND_SIZE
  948. else if (parm == PARM_size) {
  949. /* -size n[bckw]: file uses n units of space
  950. * b (default): units are 512-byte blocks
  951. * c: 1 byte
  952. * k: kilobytes
  953. * w: 2-byte words
  954. */
  955. #if ENABLE_LFS
  956. #define XATOU_SFX xatoull_sfx
  957. #else
  958. #define XATOU_SFX xatoul_sfx
  959. #endif
  960. static const struct suffix_mult find_suffixes[] = {
  961. { "c", 1 },
  962. { "w", 2 },
  963. { "", 512 },
  964. { "b", 512 },
  965. { "k", 1024 },
  966. { "", 0 }
  967. };
  968. action_size *ap;
  969. ap = ALLOC_ACTION(size);
  970. ap->size_char = arg1[0];
  971. ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
  972. }
  973. #endif
  974. #if ENABLE_FEATURE_FIND_CONTEXT
  975. else if (parm == PARM_context) {
  976. action_context *ap;
  977. ap = ALLOC_ACTION(context);
  978. /*ap->context = NULL; - ALLOC_ACTION did it */
  979. /* SELinux headers erroneously declare non-const parameter */
  980. if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
  981. bb_simple_perror_msg(arg1);
  982. }
  983. #endif
  984. #if ENABLE_FEATURE_FIND_LINKS
  985. else if (parm == PARM_links) {
  986. action_links *ap;
  987. ap = ALLOC_ACTION(links);
  988. ap->links_char = arg1[0];
  989. ap->links_count = xatoul(plus_minus_num(arg1));
  990. }
  991. #endif
  992. else {
  993. bb_error_msg("unrecognized: %s", arg);
  994. bb_show_usage();
  995. }
  996. argv++;
  997. }
  998. return appp;
  999. #undef ALLOC_ACTION
  1000. }
  1001. //usage:#define find_trivial_usage
  1002. //usage: "[PATH]... [EXPRESSION]"
  1003. //usage:#define find_full_usage "\n\n"
  1004. //usage: "Search for files. The default PATH is the current directory,\n"
  1005. //usage: "default EXPRESSION is '-print'\n"
  1006. //usage: "\nEXPRESSION may consist of:"
  1007. //usage: "\n -follow Follow symlinks"
  1008. //usage: IF_FEATURE_FIND_XDEV(
  1009. //usage: "\n -xdev Don't descend directories on other filesystems"
  1010. //usage: )
  1011. //usage: IF_FEATURE_FIND_MAXDEPTH(
  1012. //usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
  1013. //usage: "\n tests/actions to command line arguments only"
  1014. //usage: )
  1015. //usage: "\n -mindepth N Don't act on first N levels"
  1016. //usage: "\n -name PATTERN File name (w/o directory name) matches PATTERN"
  1017. //usage: "\n -iname PATTERN Case insensitive -name"
  1018. //usage: IF_FEATURE_FIND_PATH(
  1019. //usage: "\n -path PATTERN Path matches PATTERN"
  1020. //usage: )
  1021. //usage: IF_FEATURE_FIND_REGEX(
  1022. //usage: "\n -regex PATTERN Path matches regex PATTERN"
  1023. //usage: )
  1024. //usage: IF_FEATURE_FIND_TYPE(
  1025. //usage: "\n -type X File type is X (X is one of: f,d,l,b,c,...)"
  1026. //usage: )
  1027. //usage: IF_FEATURE_FIND_PERM(
  1028. //usage: "\n -perm NNN Permissions match any of (+NNN), all of (-NNN),"
  1029. //usage: "\n or exactly NNN"
  1030. //usage: )
  1031. //usage: IF_FEATURE_FIND_MTIME(
  1032. //usage: "\n -mtime DAYS Modified time is greater than (+N), less than (-N),"
  1033. //usage: "\n or exactly N days"
  1034. //usage: )
  1035. //usage: IF_FEATURE_FIND_MMIN(
  1036. //usage: "\n -mmin MINS Modified time is greater than (+N), less than (-N),"
  1037. //usage: "\n or exactly N minutes"
  1038. //usage: )
  1039. //usage: IF_FEATURE_FIND_NEWER(
  1040. //usage: "\n -newer FILE Modified time is more recent than FILE's"
  1041. //usage: )
  1042. //usage: IF_FEATURE_FIND_INUM(
  1043. //usage: "\n -inum N File has inode number N"
  1044. //usage: )
  1045. //usage: IF_FEATURE_FIND_USER(
  1046. //usage: "\n -user NAME File is owned by user NAME (numeric user ID allowed)"
  1047. //usage: )
  1048. //usage: IF_FEATURE_FIND_GROUP(
  1049. //usage: "\n -group NAME File belongs to group NAME (numeric group ID allowed)"
  1050. //usage: )
  1051. //usage: IF_FEATURE_FIND_DEPTH(
  1052. //usage: "\n -depth Process directory name after traversing it"
  1053. //usage: )
  1054. //usage: IF_FEATURE_FIND_SIZE(
  1055. //usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
  1056. //usage: "\n +/-N: file size is bigger/smaller than N"
  1057. //usage: )
  1058. //usage: IF_FEATURE_FIND_LINKS(
  1059. //usage: "\n -links N Number of links is greater than (+N), less than (-N),"
  1060. //usage: "\n or exactly N"
  1061. //usage: )
  1062. //usage: "\n -print Print (default and assumed)"
  1063. //usage: IF_FEATURE_FIND_PRINT0(
  1064. //usage: "\n -print0 Delimit output with null characters rather than"
  1065. //usage: "\n newlines"
  1066. //usage: )
  1067. //usage: IF_FEATURE_FIND_CONTEXT(
  1068. //usage: "\n -context File has specified security context"
  1069. //usage: )
  1070. //usage: IF_FEATURE_FIND_EXEC(
  1071. //usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
  1072. //usage: "\n matching files"
  1073. //usage: )
  1074. //usage: IF_FEATURE_FIND_PRUNE(
  1075. //usage: "\n -prune Stop traversing current subtree"
  1076. //usage: )
  1077. //usage: IF_FEATURE_FIND_DELETE(
  1078. //usage: "\n -delete Delete files, turns on -depth option"
  1079. //usage: )
  1080. //usage: IF_FEATURE_FIND_PAREN(
  1081. //usage: "\n (EXPR) Group an expression"
  1082. //usage: )
  1083. //usage:
  1084. //usage:#define find_example_usage
  1085. //usage: "$ find / -name passwd\n"
  1086. //usage: "/etc/passwd\n"
  1087. int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  1088. int find_main(int argc UNUSED_PARAM, char **argv)
  1089. {
  1090. static const char options[] ALIGN1 =
  1091. "-follow\0"
  1092. IF_FEATURE_FIND_XDEV( "-xdev\0" )
  1093. IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
  1094. ;
  1095. enum {
  1096. OPT_FOLLOW,
  1097. IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
  1098. IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
  1099. };
  1100. char *arg;
  1101. char **argp;
  1102. int i, firstopt, status = EXIT_SUCCESS;
  1103. #if ENABLE_FEATURE_FIND_MAXDEPTH
  1104. int minmaxdepth[2] = { 0, INT_MAX };
  1105. #else
  1106. #define minmaxdepth NULL
  1107. #endif
  1108. INIT_G();
  1109. for (firstopt = 1; argv[firstopt]; firstopt++) {
  1110. if (argv[firstopt][0] == '-')
  1111. break;
  1112. if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
  1113. break;
  1114. #if ENABLE_FEATURE_FIND_PAREN
  1115. if (LONE_CHAR(argv[firstopt], '('))
  1116. break;
  1117. #endif
  1118. }
  1119. if (firstopt == 1) {
  1120. argv[0] = (char*)".";
  1121. argv--;
  1122. firstopt++;
  1123. }
  1124. /* All options always return true. They always take effect
  1125. * rather than being processed only when their place in the
  1126. * expression is reached.
  1127. * We implement: -follow, -xdev, -maxdepth
  1128. */
  1129. /* Process options, and replace then with -a */
  1130. /* (-a will be ignored by recursive parser later) */
  1131. argp = &argv[firstopt];
  1132. while ((arg = argp[0])) {
  1133. int opt = index_in_strings(options, arg);
  1134. if (opt == OPT_FOLLOW) {
  1135. G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
  1136. argp[0] = (char*)"-a";
  1137. }
  1138. #if ENABLE_FEATURE_FIND_XDEV
  1139. if (opt == OPT_XDEV) {
  1140. struct stat stbuf;
  1141. if (!G.xdev_count) {
  1142. G.xdev_count = firstopt - 1;
  1143. G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
  1144. for (i = 1; i < firstopt; i++) {
  1145. /* not xstat(): shouldn't bomb out on
  1146. * "find not_exist exist -xdev" */
  1147. if (stat(argv[i], &stbuf) == 0)
  1148. G.xdev_dev[i-1] = stbuf.st_dev;
  1149. /* else G.xdev_dev[i-1] stays 0 and
  1150. * won't match any real device dev_t */
  1151. }
  1152. }
  1153. argp[0] = (char*)"-a";
  1154. }
  1155. #endif
  1156. #if ENABLE_FEATURE_FIND_MAXDEPTH
  1157. if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
  1158. if (!argp[1])
  1159. bb_show_usage();
  1160. minmaxdepth[opt - OPT_MINDEPTH] = xatoi_positive(argp[1]);
  1161. argp[0] = (char*)"-a";
  1162. argp[1] = (char*)"-a";
  1163. argp++;
  1164. }
  1165. #endif
  1166. argp++;
  1167. }
  1168. G.actions = parse_params(&argv[firstopt]);
  1169. for (i = 1; i < firstopt; i++) {
  1170. if (!recursive_action(argv[i],
  1171. G.recurse_flags,/* flags */
  1172. fileAction, /* file action */
  1173. fileAction, /* dir action */
  1174. #if ENABLE_FEATURE_FIND_MAXDEPTH
  1175. minmaxdepth, /* user data */
  1176. #else
  1177. NULL, /* user data */
  1178. #endif
  1179. 0)) /* depth */
  1180. status = EXIT_FAILURE;
  1181. }
  1182. return status;
  1183. }