#include <fnmatch.h>
#include "libbb.h"
+#if ENABLE_FEATURE_FIND_REGEX
+#include "xregex.h"
+#endif
/* This is a NOEXEC applet. Be very careful! */
#define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap)
ACTS(print)
ACTS(name, const char *pattern;)
+USE_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
+USE_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
USE_FEATURE_FIND_PRINT0(ACTS(print0))
USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
USE_FEATURE_FIND_SIZE( ACTS(size, off_t size;))
USE_FEATURE_FIND_PRUNE( ACTS(prune))
USE_FEATURE_FIND_DELETE(ACTS(delete))
-USE_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
static action ***actions;
static bool need_print = 1;
}
return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0;
}
+#if ENABLE_FEATURE_FIND_PATH
+ACTF(path)
+{
+ return fnmatch(ap->pattern, fileName, 0) == 0;
+}
+#endif
+#if ENABLE_FEATURE_FIND_REGEX
+ACTF(regex)
+{
+ regmatch_t match;
+ if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
+ return 0; /* no match */
+ if (match.rm_so)
+ return 0; /* match doesn't start at pos 0 */
+ if (fileName[match.rm_eo])
+ return 0; /* match doesn't end exactly at end of pathname */
+ return 1;
+}
+#endif
#if ENABLE_FEATURE_FIND_TYPE
ACTF(type)
{
return rc == 0; /* return 1 if exitcode 0 */
}
#endif
-
#if ENABLE_FEATURE_FIND_USER
ACTF(user)
{
return (statbuf->st_uid == ap->uid);
}
#endif
-
#if ENABLE_FEATURE_FIND_GROUP
ACTF(group)
{
return (statbuf->st_gid == ap->gid);
}
#endif
-
#if ENABLE_FEATURE_FIND_PRINT0
ACTF(print0)
{
return TRUE;
}
#endif
-
ACTF(print)
{
puts(fileName);
return TRUE;
}
-
#if ENABLE_FEATURE_FIND_PAREN
ACTF(paren)
{
return exec_actions(ap->subexpr, fileName, statbuf);
}
#endif
-
+#if ENABLE_FEATURE_FIND_SIZE
+ACTF(size)
+{
+ return statbuf->st_size == ap->size;
+}
+#endif
#if ENABLE_FEATURE_FIND_PRUNE
/*
* -prune: if -depth is not given, return true and do not descend
return SKIP + TRUE;
}
#endif
-
#if ENABLE_FEATURE_FIND_DELETE
ACTF(delete)
{
}
#endif
-#if ENABLE_FEATURE_FIND_PATH
-ACTF(path)
-{
- return fnmatch(ap->pattern, fileName, 0) == 0;
-}
-#endif
-#if ENABLE_FEATURE_FIND_SIZE
-ACTF(size)
+static int fileAction(const char *fileName, struct stat *statbuf, void *userData, int depth)
{
- return statbuf->st_size == ap->size;
-}
-#endif
+ int i;
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+ int maxdepth = (int)(ptrdiff_t)userData;
+ if (depth > maxdepth) return SKIP;
+#endif
-static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth)
-{
- int i;
#if ENABLE_FEATURE_FIND_XDEV
if (S_ISDIR(statbuf->st_mode) && xdev_count) {
for (i = 0; i < xdev_count; i++) {
PARM_print ,
USE_FEATURE_FIND_PRINT0(PARM_print0 ,)
PARM_name ,
+ USE_FEATURE_FIND_PATH( PARM_path ,)
+ USE_FEATURE_FIND_REGEX( PARM_regex ,)
USE_FEATURE_FIND_TYPE( PARM_type ,)
USE_FEATURE_FIND_PERM( PARM_perm ,)
USE_FEATURE_FIND_MTIME( PARM_mtime ,)
USE_FEATURE_FIND_SIZE( PARM_size ,)
USE_FEATURE_FIND_PRUNE( PARM_prune ,)
USE_FEATURE_FIND_DELETE(PARM_delete ,)
- USE_FEATURE_FIND_PATH( PARM_path ,)
#if ENABLE_DESKTOP
PARM_and ,
PARM_or ,
"-print" ,
USE_FEATURE_FIND_PRINT0("-print0",)
"-name" ,
+ USE_FEATURE_FIND_PATH( "-path" ,)
+ USE_FEATURE_FIND_REGEX( "-regex" ,)
USE_FEATURE_FIND_TYPE( "-type" ,)
USE_FEATURE_FIND_PERM( "-perm" ,)
USE_FEATURE_FIND_MTIME( "-mtime" ,)
USE_FEATURE_FIND_SIZE( "-size" ,)
USE_FEATURE_FIND_PRUNE( "-prune" ,)
USE_FEATURE_FIND_DELETE("-delete",)
- USE_FEATURE_FIND_PATH( "-path" ,)
#if ENABLE_DESKTOP
"-and" ,
"-or" ,
ap = ALLOC_ACTION(name);
ap->pattern = arg1;
}
+#if ENABLE_FEATURE_FIND_PATH
+ else if (parm == PARM_path) {
+ action_path *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(path);
+ ap->pattern = arg1;
+ }
+#endif
+#if ENABLE_FEATURE_FIND_REGEX
+ else if (parm == PARM_regex) {
+ action_regex *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(regex);
+ xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
+ }
+#endif
#if ENABLE_FEATURE_FIND_TYPE
else if (parm == PARM_type) {
action_type *ap;
argv = endarg;
}
#endif
+#if ENABLE_FEATURE_FIND_SIZE
+ else if (parm == PARM_size) {
+ action_size *ap;
+ if (!*++argv)
+ bb_error_msg_and_die(bb_msg_requires_arg, arg);
+ ap = ALLOC_ACTION(size);
+ ap->size = XATOOFF(arg1);
+ }
+#endif
#if ENABLE_FEATURE_FIND_PRUNE
else if (parm == PARM_prune) {
USE_FEATURE_FIND_NOT( invert_flag = 0; )
(void) ALLOC_ACTION(delete);
}
#endif
-#if ENABLE_FEATURE_FIND_PATH
- else if (parm == PARM_path) {
- action_path *ap;
- if (!*++argv)
- bb_error_msg_and_die(bb_msg_requires_arg, arg);
- ap = ALLOC_ACTION(path);
- ap->pattern = arg1;
- }
-#endif
-#if ENABLE_FEATURE_FIND_SIZE
- else if (parm == PARM_size) {
- action_size *ap;
- if (!*++argv)
- bb_error_msg_and_die(bb_msg_requires_arg, arg);
- ap = ALLOC_ACTION(size);
- ap->size = XATOOFF(arg1);
- }
-#endif
- else
+ else {
+ bb_error_msg("unrecognized: %s", arg);
bb_show_usage();
+ }
argv++;
}
return appp;
int find_main(int argc, char **argv);
int find_main(int argc, char **argv)
{
- static const char * const options[] = {
+ static const char *const options[] = {
"-follow",
-USE_FEATURE_FIND_XDEV( "-xdev", )
+USE_FEATURE_FIND_XDEV( "-xdev" ,)
+USE_FEATURE_FIND_MAXDEPTH("-maxdepth",)
NULL
};
+ enum {
+ OPT_FOLLOW,
+USE_FEATURE_FIND_XDEV( OPT_XDEV ,)
+USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,)
+ };
char *arg;
char **argp;
int i, firstopt, status = EXIT_SUCCESS;
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+ int maxdepth = INT_MAX;
+#endif
for (firstopt = 1; firstopt < argc; firstopt++) {
if (argv[firstopt][0] == '-')
/* All options always return true. They always take effect
* rather than being processed only when their place in the
* expression is reached.
- * We implement: -follow, -xdev
+ * We implement: -follow, -xdev, -maxdepth
*/
/* Process options, and replace then with -a */
/* (-a will be ignored by recursive parser later) */
argp = &argv[firstopt];
while ((arg = argp[0])) {
- i = index_in_str_array(options, arg);
- if (i == 0) { /* -follow */
+ int opt = index_in_str_array(options, arg);
+ if (opt == OPT_FOLLOW) {
recurse_flags |= ACTION_FOLLOWLINKS;
argp[0] = (char*)"-a";
}
#if ENABLE_FEATURE_FIND_XDEV
- else if (i == 1) { /* -xdev */
+ if (opt == OPT_XDEV) {
struct stat stbuf;
if (!xdev_count) {
xdev_count = firstopt - 1;
}
argp[0] = (char*)"-a";
}
+#endif
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+ if (opt == OPT_MAXDEPTH) {
+ if (!argp[1])
+ bb_show_usage();
+ maxdepth = xatoi_u(argp[1]);
+ argp[0] = (char*)"-a";
+ argp[1] = (char*)"-a";
+ argp++;
+ }
#endif
argp++;
}
recurse_flags, /* flags */
fileAction, /* file action */
fileAction, /* dir action */
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+ (void*)maxdepth,/* user data */
+#else
NULL, /* user data */
+#endif
0)) /* depth */
status = EXIT_FAILURE;
}
"the current directory, default EXPRESSION is '-print'\n" \
"\nEXPRESSION may consist of:" \
"\n -follow Dereference symlinks" \
+ USE_FEATURE_FIND_XDEV( \
+ "\n -xdev Don't descend directories on other filesystems") \
+ USE_FEATURE_FIND_MAXDEPTH( \
+ "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies" \
+ "\n tests/actions to command line arguments only") \
"\n -name PATTERN File name (leading directories removed) matches PATTERN" \
"\n -print Print (default and assumed)" \
USE_FEATURE_FIND_PRINT0( \
"\n -print0 Delimit output with null characters rather than" \
- "\n newlines" \
- ) USE_FEATURE_FIND_TYPE( \
- "\n -type X Filetype matches X (where X is one of: f,d,l,b,c,...)" \
- ) USE_FEATURE_FIND_PERM( \
+ "\n newlines") \
+ USE_FEATURE_FIND_TYPE( \
+ "\n -type X Filetype matches X (where X is one of: f,d,l,b,c,...)") \
+ USE_FEATURE_FIND_PERM( \
"\n -perm PERMS Permissions match any of (+NNN), all of (-NNN)," \
- "\n or exactly (NNN)" \
- ) USE_FEATURE_FIND_MTIME( \
+ "\n or exactly (NNN)") \
+ USE_FEATURE_FIND_MTIME( \
"\n -mtime DAYS Modified time is greater than (+N), less than (-N)," \
- "\n or exactly (N) days" \
- ) USE_FEATURE_FIND_MMIN( \
+ "\n or exactly (N) days") \
+ USE_FEATURE_FIND_MMIN( \
"\n -mmin MINS Modified time is greater than (+N), less than (-N)," \
- "\n or exactly (N) minutes" \
- ) USE_FEATURE_FIND_NEWER( \
- "\n -newer FILE Modified time is more recent than FILE's" \
- ) USE_FEATURE_FIND_INUM( \
- "\n -inum N File has inode number N" \
- ) USE_FEATURE_FIND_EXEC( \
+ "\n or exactly (N) minutes") \
+ USE_FEATURE_FIND_NEWER( \
+ "\n -newer FILE Modified time is more recent than FILE's") \
+ USE_FEATURE_FIND_INUM( \
+ "\n -inum N File has inode number N") \
+ USE_FEATURE_FIND_EXEC( \
"\n -exec CMD Execute CMD with all instances of {} replaced by the" \
- "\n files matching EXPRESSION" \
- ) USE_FEATURE_FIND_USER( \
- "\n -user NAME File is owned by user NAME (numeric user ID allowed)" \
- ) USE_FEATURE_FIND_GROUP( \
- "\n -group NAME File belongs to group NAME (numeric group ID allowed)" \
- ) USE_FEATURE_FIND_DEPTH( \
- "\n -depth Process directory after traversing it" \
- ) USE_FEATURE_FIND_SIZE( \
- "\n -size N File size is N" \
- ) USE_FEATURE_FIND_PRUNE( \
- "\n -prune Stop traversing current subtree" \
- ) USE_FEATURE_FIND_DELETE( \
- "\n -delete Delete files, turns on -depth option" \
- ) USE_FEATURE_FIND_PATH( \
- "\n -path Path matches PATTERN" \
- ) USE_FEATURE_FIND_PAREN( \
- "\n (EXPR) Group an expression" \
- )
+ "\n files matching EXPRESSION") \
+ USE_FEATURE_FIND_USER( \
+ "\n -user NAME File is owned by user NAME (numeric user ID allowed)") \
+ USE_FEATURE_FIND_GROUP( \
+ "\n -group NAME File belongs to group NAME (numeric group ID allowed)") \
+ USE_FEATURE_FIND_DEPTH( \
+ "\n -depth Process directory after traversing it") \
+ USE_FEATURE_FIND_SIZE( \
+ "\n -size N File size is N") \
+ USE_FEATURE_FIND_PRUNE( \
+ "\n -prune Stop traversing current subtree") \
+ USE_FEATURE_FIND_DELETE( \
+ "\n -delete Delete files, turns on -depth option") \
+ USE_FEATURE_FIND_PATH( \
+ "\n -path Path matches PATTERN") \
+ USE_FEATURE_FIND_PAREN( \
+ "\n (EXPR) Group an expression") \
#define find_example_usage \
"$ find / -name passwd\n" \