aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--findutils/Config.in19
-rw-r--r--findutils/find.c148
-rw-r--r--include/usage.h68
3 files changed, 151 insertions, 84 deletions
diff --git a/findutils/Config.in b/findutils/Config.in
index a32e98922..bcdc02446 100644
--- a/findutils/Config.in
+++ b/findutils/Config.in
@@ -57,8 +57,14 @@ config FEATURE_FIND_XDEV
57 default y 57 default y
58 depends on FIND 58 depends on FIND
59 help 59 help
60 This option will allow find to restrict searches to a single 60 This option allows find to restrict searches to a single filesystem.
61 filesystem. 61
62config FEATURE_FIND_MAXDEPTH
63 bool "Enable -maxdepth N option"
64 default y
65 depends on FIND
66 help
67 This option enables -maxdepth N option.
62 68
63config FEATURE_FIND_NEWER 69config FEATURE_FIND_NEWER
64 bool "Enable -newer option for comparing file mtimes" 70 bool "Enable -newer option for comparing file mtimes"
@@ -149,7 +155,14 @@ config FEATURE_FIND_PATH
149 default y 155 default y
150 depends on FIND 156 depends on FIND
151 help 157 help
152 The -path option matches whole pathnames instead of just filenames. 158 The -path option matches whole pathname instead of just filename.
159
160config FEATURE_FIND_REGEX
161 bool "Enable -regex: match pathname to regex"
162 default y
163 depends on FIND
164 help
165 The -regex option matches whole pathname against regular expression.
153 166
154config GREP 167config GREP
155 bool "grep" 168 bool "grep"
diff --git a/findutils/find.c b/findutils/find.c
index 2d271d27e..386bc54ad 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -47,6 +47,9 @@
47 47
48#include <fnmatch.h> 48#include <fnmatch.h>
49#include "libbb.h" 49#include "libbb.h"
50#if ENABLE_FEATURE_FIND_REGEX
51#include "xregex.h"
52#endif
50 53
51/* This is a NOEXEC applet. Be very careful! */ 54/* This is a NOEXEC applet. Be very careful! */
52 55
@@ -66,6 +69,8 @@ typedef struct {
66#define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap) 69#define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap)
67 ACTS(print) 70 ACTS(print)
68 ACTS(name, const char *pattern;) 71 ACTS(name, const char *pattern;)
72USE_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
73USE_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
69USE_FEATURE_FIND_PRINT0(ACTS(print0)) 74USE_FEATURE_FIND_PRINT0(ACTS(print0))
70USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) 75USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
71USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;)) 76USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
@@ -80,7 +85,6 @@ USE_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
80USE_FEATURE_FIND_SIZE( ACTS(size, off_t size;)) 85USE_FEATURE_FIND_SIZE( ACTS(size, off_t size;))
81USE_FEATURE_FIND_PRUNE( ACTS(prune)) 86USE_FEATURE_FIND_PRUNE( ACTS(prune))
82USE_FEATURE_FIND_DELETE(ACTS(delete)) 87USE_FEATURE_FIND_DELETE(ACTS(delete))
83USE_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
84 88
85static action ***actions; 89static action ***actions;
86static bool need_print = 1; 90static bool need_print = 1;
@@ -182,6 +186,25 @@ ACTF(name)
182 } 186 }
183 return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0; 187 return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0;
184} 188}
189#if ENABLE_FEATURE_FIND_PATH
190ACTF(path)
191{
192 return fnmatch(ap->pattern, fileName, 0) == 0;
193}
194#endif
195#if ENABLE_FEATURE_FIND_REGEX
196ACTF(regex)
197{
198 regmatch_t match;
199 if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
200 return 0; /* no match */
201 if (match.rm_so)
202 return 0; /* match doesn't start at pos 0 */
203 if (fileName[match.rm_eo])
204 return 0; /* match doesn't end exactly at end of pathname */
205 return 1;
206}
207#endif
185#if ENABLE_FEATURE_FIND_TYPE 208#if ENABLE_FEATURE_FIND_TYPE
186ACTF(type) 209ACTF(type)
187{ 210{
@@ -258,21 +281,18 @@ ACTF(exec)
258 return rc == 0; /* return 1 if exitcode 0 */ 281 return rc == 0; /* return 1 if exitcode 0 */
259} 282}
260#endif 283#endif
261
262#if ENABLE_FEATURE_FIND_USER 284#if ENABLE_FEATURE_FIND_USER
263ACTF(user) 285ACTF(user)
264{ 286{
265 return (statbuf->st_uid == ap->uid); 287 return (statbuf->st_uid == ap->uid);
266} 288}
267#endif 289#endif
268
269#if ENABLE_FEATURE_FIND_GROUP 290#if ENABLE_FEATURE_FIND_GROUP
270ACTF(group) 291ACTF(group)
271{ 292{
272 return (statbuf->st_gid == ap->gid); 293 return (statbuf->st_gid == ap->gid);
273} 294}
274#endif 295#endif
275
276#if ENABLE_FEATURE_FIND_PRINT0 296#if ENABLE_FEATURE_FIND_PRINT0
277ACTF(print0) 297ACTF(print0)
278{ 298{
@@ -280,20 +300,23 @@ ACTF(print0)
280 return TRUE; 300 return TRUE;
281} 301}
282#endif 302#endif
283
284ACTF(print) 303ACTF(print)
285{ 304{
286 puts(fileName); 305 puts(fileName);
287 return TRUE; 306 return TRUE;
288} 307}
289
290#if ENABLE_FEATURE_FIND_PAREN 308#if ENABLE_FEATURE_FIND_PAREN
291ACTF(paren) 309ACTF(paren)
292{ 310{
293 return exec_actions(ap->subexpr, fileName, statbuf); 311 return exec_actions(ap->subexpr, fileName, statbuf);
294} 312}
295#endif 313#endif
296 314#if ENABLE_FEATURE_FIND_SIZE
315ACTF(size)
316{
317 return statbuf->st_size == ap->size;
318}
319#endif
297#if ENABLE_FEATURE_FIND_PRUNE 320#if ENABLE_FEATURE_FIND_PRUNE
298/* 321/*
299 * -prune: if -depth is not given, return true and do not descend 322 * -prune: if -depth is not given, return true and do not descend
@@ -306,7 +329,6 @@ ACTF(prune)
306 return SKIP + TRUE; 329 return SKIP + TRUE;
307} 330}
308#endif 331#endif
309
310#if ENABLE_FEATURE_FIND_DELETE 332#if ENABLE_FEATURE_FIND_DELETE
311ACTF(delete) 333ACTF(delete)
312{ 334{
@@ -322,24 +344,16 @@ ACTF(delete)
322} 344}
323#endif 345#endif
324 346
325#if ENABLE_FEATURE_FIND_PATH
326ACTF(path)
327{
328 return fnmatch(ap->pattern, fileName, 0) == 0;
329}
330#endif
331 347
332#if ENABLE_FEATURE_FIND_SIZE 348static int fileAction(const char *fileName, struct stat *statbuf, void *userData, int depth)
333ACTF(size)
334{ 349{
335 return statbuf->st_size == ap->size; 350 int i;
336} 351#if ENABLE_FEATURE_FIND_MAXDEPTH
337#endif 352 int maxdepth = (int)(ptrdiff_t)userData;
338 353
354 if (depth > maxdepth) return SKIP;
355#endif
339 356
340static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth)
341{
342 int i;
343#if ENABLE_FEATURE_FIND_XDEV 357#if ENABLE_FEATURE_FIND_XDEV
344 if (S_ISDIR(statbuf->st_mode) && xdev_count) { 358 if (S_ISDIR(statbuf->st_mode) && xdev_count) {
345 for (i = 0; i < xdev_count; i++) { 359 for (i = 0; i < xdev_count; i++) {
@@ -404,6 +418,8 @@ static action*** parse_params(char **argv)
404 PARM_print , 418 PARM_print ,
405 USE_FEATURE_FIND_PRINT0(PARM_print0 ,) 419 USE_FEATURE_FIND_PRINT0(PARM_print0 ,)
406 PARM_name , 420 PARM_name ,
421 USE_FEATURE_FIND_PATH( PARM_path ,)
422 USE_FEATURE_FIND_REGEX( PARM_regex ,)
407 USE_FEATURE_FIND_TYPE( PARM_type ,) 423 USE_FEATURE_FIND_TYPE( PARM_type ,)
408 USE_FEATURE_FIND_PERM( PARM_perm ,) 424 USE_FEATURE_FIND_PERM( PARM_perm ,)
409 USE_FEATURE_FIND_MTIME( PARM_mtime ,) 425 USE_FEATURE_FIND_MTIME( PARM_mtime ,)
@@ -418,7 +434,6 @@ static action*** parse_params(char **argv)
418 USE_FEATURE_FIND_SIZE( PARM_size ,) 434 USE_FEATURE_FIND_SIZE( PARM_size ,)
419 USE_FEATURE_FIND_PRUNE( PARM_prune ,) 435 USE_FEATURE_FIND_PRUNE( PARM_prune ,)
420 USE_FEATURE_FIND_DELETE(PARM_delete ,) 436 USE_FEATURE_FIND_DELETE(PARM_delete ,)
421 USE_FEATURE_FIND_PATH( PARM_path ,)
422#if ENABLE_DESKTOP 437#if ENABLE_DESKTOP
423 PARM_and , 438 PARM_and ,
424 PARM_or , 439 PARM_or ,
@@ -433,6 +448,8 @@ static action*** parse_params(char **argv)
433 "-print" , 448 "-print" ,
434 USE_FEATURE_FIND_PRINT0("-print0",) 449 USE_FEATURE_FIND_PRINT0("-print0",)
435 "-name" , 450 "-name" ,
451 USE_FEATURE_FIND_PATH( "-path" ,)
452 USE_FEATURE_FIND_REGEX( "-regex" ,)
436 USE_FEATURE_FIND_TYPE( "-type" ,) 453 USE_FEATURE_FIND_TYPE( "-type" ,)
437 USE_FEATURE_FIND_PERM( "-perm" ,) 454 USE_FEATURE_FIND_PERM( "-perm" ,)
438 USE_FEATURE_FIND_MTIME( "-mtime" ,) 455 USE_FEATURE_FIND_MTIME( "-mtime" ,)
@@ -447,7 +464,6 @@ static action*** parse_params(char **argv)
447 USE_FEATURE_FIND_SIZE( "-size" ,) 464 USE_FEATURE_FIND_SIZE( "-size" ,)
448 USE_FEATURE_FIND_PRUNE( "-prune" ,) 465 USE_FEATURE_FIND_PRUNE( "-prune" ,)
449 USE_FEATURE_FIND_DELETE("-delete",) 466 USE_FEATURE_FIND_DELETE("-delete",)
450 USE_FEATURE_FIND_PATH( "-path" ,)
451#if ENABLE_DESKTOP 467#if ENABLE_DESKTOP
452 "-and" , 468 "-and" ,
453 "-or" , 469 "-or" ,
@@ -536,6 +552,24 @@ static action*** parse_params(char **argv)
536 ap = ALLOC_ACTION(name); 552 ap = ALLOC_ACTION(name);
537 ap->pattern = arg1; 553 ap->pattern = arg1;
538 } 554 }
555#if ENABLE_FEATURE_FIND_PATH
556 else if (parm == PARM_path) {
557 action_path *ap;
558 if (!*++argv)
559 bb_error_msg_and_die(bb_msg_requires_arg, arg);
560 ap = ALLOC_ACTION(path);
561 ap->pattern = arg1;
562 }
563#endif
564#if ENABLE_FEATURE_FIND_REGEX
565 else if (parm == PARM_regex) {
566 action_regex *ap;
567 if (!*++argv)
568 bb_error_msg_and_die(bb_msg_requires_arg, arg);
569 ap = ALLOC_ACTION(regex);
570 xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
571 }
572#endif
539#if ENABLE_FEATURE_FIND_TYPE 573#if ENABLE_FEATURE_FIND_TYPE
540 else if (parm == PARM_type) { 574 else if (parm == PARM_type) {
541 action_type *ap; 575 action_type *ap;
@@ -678,6 +712,15 @@ static action*** parse_params(char **argv)
678 argv = endarg; 712 argv = endarg;
679 } 713 }
680#endif 714#endif
715#if ENABLE_FEATURE_FIND_SIZE
716 else if (parm == PARM_size) {
717 action_size *ap;
718 if (!*++argv)
719 bb_error_msg_and_die(bb_msg_requires_arg, arg);
720 ap = ALLOC_ACTION(size);
721 ap->size = XATOOFF(arg1);
722 }
723#endif
681#if ENABLE_FEATURE_FIND_PRUNE 724#if ENABLE_FEATURE_FIND_PRUNE
682 else if (parm == PARM_prune) { 725 else if (parm == PARM_prune) {
683 USE_FEATURE_FIND_NOT( invert_flag = 0; ) 726 USE_FEATURE_FIND_NOT( invert_flag = 0; )
@@ -691,26 +734,10 @@ static action*** parse_params(char **argv)
691 (void) ALLOC_ACTION(delete); 734 (void) ALLOC_ACTION(delete);
692 } 735 }
693#endif 736#endif
694#if ENABLE_FEATURE_FIND_PATH 737 else {
695 else if (parm == PARM_path) { 738 bb_error_msg("unrecognized: %s", arg);
696 action_path *ap;
697 if (!*++argv)
698 bb_error_msg_and_die(bb_msg_requires_arg, arg);
699 ap = ALLOC_ACTION(path);
700 ap->pattern = arg1;
701 }
702#endif
703#if ENABLE_FEATURE_FIND_SIZE
704 else if (parm == PARM_size) {
705 action_size *ap;
706 if (!*++argv)
707 bb_error_msg_and_die(bb_msg_requires_arg, arg);
708 ap = ALLOC_ACTION(size);
709 ap->size = XATOOFF(arg1);
710 }
711#endif
712 else
713 bb_show_usage(); 739 bb_show_usage();
740 }
714 argv++; 741 argv++;
715 } 742 }
716 return appp; 743 return appp;
@@ -721,15 +748,24 @@ static action*** parse_params(char **argv)
721int find_main(int argc, char **argv); 748int find_main(int argc, char **argv);
722int find_main(int argc, char **argv) 749int find_main(int argc, char **argv)
723{ 750{
724 static const char * const options[] = { 751 static const char *const options[] = {
725 "-follow", 752 "-follow",
726USE_FEATURE_FIND_XDEV( "-xdev", ) 753USE_FEATURE_FIND_XDEV( "-xdev" ,)
754USE_FEATURE_FIND_MAXDEPTH("-maxdepth",)
727 NULL 755 NULL
728 }; 756 };
757 enum {
758 OPT_FOLLOW,
759USE_FEATURE_FIND_XDEV( OPT_XDEV ,)
760USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,)
761 };
729 762
730 char *arg; 763 char *arg;
731 char **argp; 764 char **argp;
732 int i, firstopt, status = EXIT_SUCCESS; 765 int i, firstopt, status = EXIT_SUCCESS;
766#if ENABLE_FEATURE_FIND_MAXDEPTH
767 int maxdepth = INT_MAX;
768#endif
733 769
734 for (firstopt = 1; firstopt < argc; firstopt++) { 770 for (firstopt = 1; firstopt < argc; firstopt++) {
735 if (argv[firstopt][0] == '-') 771 if (argv[firstopt][0] == '-')
@@ -750,19 +786,19 @@ USE_FEATURE_FIND_XDEV( "-xdev", )
750/* All options always return true. They always take effect 786/* All options always return true. They always take effect
751 * rather than being processed only when their place in the 787 * rather than being processed only when their place in the
752 * expression is reached. 788 * expression is reached.
753 * We implement: -follow, -xdev 789 * We implement: -follow, -xdev, -maxdepth
754 */ 790 */
755 /* Process options, and replace then with -a */ 791 /* Process options, and replace then with -a */
756 /* (-a will be ignored by recursive parser later) */ 792 /* (-a will be ignored by recursive parser later) */
757 argp = &argv[firstopt]; 793 argp = &argv[firstopt];
758 while ((arg = argp[0])) { 794 while ((arg = argp[0])) {
759 i = index_in_str_array(options, arg); 795 int opt = index_in_str_array(options, arg);
760 if (i == 0) { /* -follow */ 796 if (opt == OPT_FOLLOW) {
761 recurse_flags |= ACTION_FOLLOWLINKS; 797 recurse_flags |= ACTION_FOLLOWLINKS;
762 argp[0] = (char*)"-a"; 798 argp[0] = (char*)"-a";
763 } 799 }
764#if ENABLE_FEATURE_FIND_XDEV 800#if ENABLE_FEATURE_FIND_XDEV
765 else if (i == 1) { /* -xdev */ 801 if (opt == OPT_XDEV) {
766 struct stat stbuf; 802 struct stat stbuf;
767 if (!xdev_count) { 803 if (!xdev_count) {
768 xdev_count = firstopt - 1; 804 xdev_count = firstopt - 1;
@@ -778,6 +814,16 @@ USE_FEATURE_FIND_XDEV( "-xdev", )
778 argp[0] = (char*)"-a"; 814 argp[0] = (char*)"-a";
779 } 815 }
780#endif 816#endif
817#if ENABLE_FEATURE_FIND_MAXDEPTH
818 if (opt == OPT_MAXDEPTH) {
819 if (!argp[1])
820 bb_show_usage();
821 maxdepth = xatoi_u(argp[1]);
822 argp[0] = (char*)"-a";
823 argp[1] = (char*)"-a";
824 argp++;
825 }
826#endif
781 argp++; 827 argp++;
782 } 828 }
783 829
@@ -788,7 +834,11 @@ USE_FEATURE_FIND_XDEV( "-xdev", )
788 recurse_flags, /* flags */ 834 recurse_flags, /* flags */
789 fileAction, /* file action */ 835 fileAction, /* file action */
790 fileAction, /* dir action */ 836 fileAction, /* dir action */
837#if ENABLE_FEATURE_FIND_MAXDEPTH
838 (void*)maxdepth,/* user data */
839#else
791 NULL, /* user data */ 840 NULL, /* user data */
841#endif
792 0)) /* depth */ 842 0)) /* depth */
793 status = EXIT_FAILURE; 843 status = EXIT_FAILURE;
794 } 844 }
diff --git a/include/usage.h b/include/usage.h
index 0c02d2501..8c438e285 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -943,46 +943,50 @@
943 "the current directory, default EXPRESSION is '-print'\n" \ 943 "the current directory, default EXPRESSION is '-print'\n" \
944 "\nEXPRESSION may consist of:" \ 944 "\nEXPRESSION may consist of:" \
945 "\n -follow Dereference symlinks" \ 945 "\n -follow Dereference symlinks" \
946 USE_FEATURE_FIND_XDEV( \
947 "\n -xdev Don't descend directories on other filesystems") \
948 USE_FEATURE_FIND_MAXDEPTH( \
949 "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies" \
950 "\n tests/actions to command line arguments only") \
946 "\n -name PATTERN File name (leading directories removed) matches PATTERN" \ 951 "\n -name PATTERN File name (leading directories removed) matches PATTERN" \
947 "\n -print Print (default and assumed)" \ 952 "\n -print Print (default and assumed)" \
948 USE_FEATURE_FIND_PRINT0( \ 953 USE_FEATURE_FIND_PRINT0( \
949 "\n -print0 Delimit output with null characters rather than" \ 954 "\n -print0 Delimit output with null characters rather than" \
950 "\n newlines" \ 955 "\n newlines") \
951 ) USE_FEATURE_FIND_TYPE( \ 956 USE_FEATURE_FIND_TYPE( \
952 "\n -type X Filetype matches X (where X is one of: f,d,l,b,c,...)" \ 957 "\n -type X Filetype matches X (where X is one of: f,d,l,b,c,...)") \
953 ) USE_FEATURE_FIND_PERM( \ 958 USE_FEATURE_FIND_PERM( \
954 "\n -perm PERMS Permissions match any of (+NNN), all of (-NNN)," \ 959 "\n -perm PERMS Permissions match any of (+NNN), all of (-NNN)," \
955 "\n or exactly (NNN)" \ 960 "\n or exactly (NNN)") \
956 ) USE_FEATURE_FIND_MTIME( \ 961 USE_FEATURE_FIND_MTIME( \
957 "\n -mtime DAYS Modified time is greater than (+N), less than (-N)," \ 962 "\n -mtime DAYS Modified time is greater than (+N), less than (-N)," \
958 "\n or exactly (N) days" \ 963 "\n or exactly (N) days") \
959 ) USE_FEATURE_FIND_MMIN( \ 964 USE_FEATURE_FIND_MMIN( \
960 "\n -mmin MINS Modified time is greater than (+N), less than (-N)," \ 965 "\n -mmin MINS Modified time is greater than (+N), less than (-N)," \
961 "\n or exactly (N) minutes" \ 966 "\n or exactly (N) minutes") \
962 ) USE_FEATURE_FIND_NEWER( \ 967 USE_FEATURE_FIND_NEWER( \
963 "\n -newer FILE Modified time is more recent than FILE's" \ 968 "\n -newer FILE Modified time is more recent than FILE's") \
964 ) USE_FEATURE_FIND_INUM( \ 969 USE_FEATURE_FIND_INUM( \
965 "\n -inum N File has inode number N" \ 970 "\n -inum N File has inode number N") \
966 ) USE_FEATURE_FIND_EXEC( \ 971 USE_FEATURE_FIND_EXEC( \
967 "\n -exec CMD Execute CMD with all instances of {} replaced by the" \ 972 "\n -exec CMD Execute CMD with all instances of {} replaced by the" \
968 "\n files matching EXPRESSION" \ 973 "\n files matching EXPRESSION") \
969 ) USE_FEATURE_FIND_USER( \ 974 USE_FEATURE_FIND_USER( \
970 "\n -user NAME File is owned by user NAME (numeric user ID allowed)" \ 975 "\n -user NAME File is owned by user NAME (numeric user ID allowed)") \
971 ) USE_FEATURE_FIND_GROUP( \ 976 USE_FEATURE_FIND_GROUP( \
972 "\n -group NAME File belongs to group NAME (numeric group ID allowed)" \ 977 "\n -group NAME File belongs to group NAME (numeric group ID allowed)") \
973 ) USE_FEATURE_FIND_DEPTH( \ 978 USE_FEATURE_FIND_DEPTH( \
974 "\n -depth Process directory after traversing it" \ 979 "\n -depth Process directory after traversing it") \
975 ) USE_FEATURE_FIND_SIZE( \ 980 USE_FEATURE_FIND_SIZE( \
976 "\n -size N File size is N" \ 981 "\n -size N File size is N") \
977 ) USE_FEATURE_FIND_PRUNE( \ 982 USE_FEATURE_FIND_PRUNE( \
978 "\n -prune Stop traversing current subtree" \ 983 "\n -prune Stop traversing current subtree") \
979 ) USE_FEATURE_FIND_DELETE( \ 984 USE_FEATURE_FIND_DELETE( \
980 "\n -delete Delete files, turns on -depth option" \ 985 "\n -delete Delete files, turns on -depth option") \
981 ) USE_FEATURE_FIND_PATH( \ 986 USE_FEATURE_FIND_PATH( \
982 "\n -path Path matches PATTERN" \ 987 "\n -path Path matches PATTERN") \
983 ) USE_FEATURE_FIND_PAREN( \ 988 USE_FEATURE_FIND_PAREN( \
984 "\n (EXPR) Group an expression" \ 989 "\n (EXPR) Group an expression") \
985 )
986 990
987#define find_example_usage \ 991#define find_example_usage \
988 "$ find / -name passwd\n" \ 992 "$ find / -name passwd\n" \