1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355#include <fnmatch.h>
356#include "libbb.h"
357#include "common_bufsiz.h"
358#if ENABLE_FEATURE_FIND_REGEX
359# include "xregex.h"
360#endif
361
362#ifndef FNM_CASEFOLD
363# define FNM_CASEFOLD 0
364#endif
365
366#if 1
367# define dbg(...) ((void)0)
368#else
369# define dbg(...) bb_error_msg(__VA_ARGS__)
370#endif
371
372
373
374
375
376typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
377
378typedef struct {
379 action_fp f;
380#if ENABLE_FEATURE_FIND_NOT
381 bool invert;
382#endif
383} action;
384
385#define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
386#define ACTF(name) \
387 static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
388 const struct stat *statbuf UNUSED_PARAM, \
389 action_##name* ap UNUSED_PARAM)
390
391 ACTS(print)
392 ACTS(name, const char *pattern; bool iname;)
393IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern; bool ipath;))
394IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
395IF_FEATURE_FIND_PRINT0( ACTS(print0))
396IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
397IF_FEATURE_FIND_EXECUTABLE(ACTS(executable))
398IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
399IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
400IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
401IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
402IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
403IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;))
404IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;))
405IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
406IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
407IF_FEATURE_FIND_PRUNE( ACTS(prune))
408IF_FEATURE_FIND_QUIT( ACTS(quit))
409IF_FEATURE_FIND_DELETE( ACTS(delete))
410IF_FEATURE_FIND_EMPTY( ACTS(empty))
411IF_FEATURE_FIND_EXEC( ACTS(exec,
412 char **exec_argv;
413 unsigned *subst_count;
414 int exec_argc;
415 IF_FEATURE_FIND_EXEC_PLUS(
416
417
418
419
420 char **filelist;
421 int filelist_idx;
422 int file_len;
423 )
424 ))
425IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
426IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
427
428struct globals {
429 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
430 IF_FEATURE_FIND_XDEV(int xdev_count;)
431#if ENABLE_FEATURE_FIND_MAXDEPTH
432 int minmaxdepth[2];
433#endif
434 action ***actions;
435 smallint need_print;
436 smallint xdev_on;
437 smalluint exitstatus;
438 recurse_flags_t recurse_flags;
439 IF_FEATURE_FIND_EXEC_PLUS(unsigned max_argv_len;)
440} FIX_ALIASING;
441#define G (*(struct globals*)bb_common_bufsiz1)
442#define INIT_G() do { \
443 setup_common_bufsiz(); \
444 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
445 \
446 memset(&G, 0, sizeof(G)); \
447 IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
448 IF_FEATURE_FIND_EXEC_PLUS(G.max_argv_len = bb_arg_max() - 2048;) \
449 G.need_print = 1; \
450 G.recurse_flags = ACTION_RECURSE; \
451} while (0)
452
453
454
455
456
457
458static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
459{
460 int cur_group;
461 int cur_action;
462 int rc = 0;
463 action **app, *ap;
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482 cur_group = -1;
483 while ((app = appp[++cur_group]) != NULL) {
484 rc &= ~TRUE;
485 cur_action = -1;
486 while (1) {
487 ap = app[++cur_action];
488 if (!ap)
489 return rc ^ TRUE;
490 rc |= TRUE ^ ap->f(fileName, statbuf, ap);
491#if ENABLE_FEATURE_FIND_NOT
492 if (ap->invert) rc ^= TRUE;
493#endif
494 dbg("grp %d action %d rc:0x%x", cur_group, cur_action, rc);
495 if (rc & TRUE)
496 break;
497 }
498 }
499 dbg("returning:0x%x", rc ^ TRUE);
500 return rc ^ TRUE;
501}
502
503#if !FNM_CASEFOLD
504static char *strcpy_upcase(char *dst, const char *src)
505{
506 char *d = dst;
507 while (1) {
508 unsigned char ch = *src++;
509 if (ch >= 'a' && ch <= 'z')
510 ch -= ('a' - 'A');
511 *d++ = ch;
512 if (ch == '\0')
513 break;
514 }
515 return dst;
516}
517#endif
518
519ACTF(name)
520{
521 int r;
522 const char *tmp = bb_basename(fileName);
523
524
525
526
527
528
529
530
531 char *trunc_slash = NULL;
532
533 if (*tmp == '\0') {
534
535 while (tmp != fileName && tmp[-1] == '/')
536 tmp--;
537 if (tmp == fileName) {
538
539
540
541
542
543
544
545 if (tmp[1])
546 trunc_slash = (char*)tmp + 1;
547 } else {
548
549 trunc_slash = (char*)tmp;
550 while (tmp != fileName && tmp[-1] != '/')
551 tmp--;
552 }
553 }
554
555
556
557
558
559 if (trunc_slash) *trunc_slash = '\0';
560#if FNM_CASEFOLD
561 r = fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0));
562#else
563 if (ap->iname)
564 tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
565 r = fnmatch(ap->pattern, tmp, 0);
566#endif
567 if (trunc_slash) *trunc_slash = '/';
568 return r == 0;
569}
570
571#if ENABLE_FEATURE_FIND_PATH
572ACTF(path)
573{
574# if FNM_CASEFOLD
575 return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0;
576# else
577 if (ap->ipath)
578 fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName);
579 return fnmatch(ap->pattern, fileName, 0) == 0;
580# endif
581}
582#endif
583#if ENABLE_FEATURE_FIND_REGEX
584ACTF(regex)
585{
586 regmatch_t match;
587 if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 ))
588 return 0;
589 if (match.rm_so)
590 return 0;
591 if (fileName[match.rm_eo])
592 return 0;
593 return 1;
594}
595#endif
596#if ENABLE_FEATURE_FIND_TYPE
597ACTF(type)
598{
599 return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
600}
601#endif
602#if ENABLE_FEATURE_FIND_EXECUTABLE
603ACTF(executable)
604{
605 return access(fileName, X_OK) == 0;
606}
607#endif
608#if ENABLE_FEATURE_FIND_PERM
609ACTF(perm)
610{
611
612 if (ap->perm_char == '+' || ap->perm_char == '/')
613 return (statbuf->st_mode & ap->perm_mask) != 0;
614
615 if (ap->perm_char == '-')
616 return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
617
618 return (statbuf->st_mode & 07777) == ap->perm_mask;
619}
620#endif
621#if ENABLE_FEATURE_FIND_MTIME
622ACTF(mtime)
623{
624 time_t file_age = time(NULL) - statbuf->st_mtime;
625 time_t mtime_secs = ap->mtime_days * 24*60*60;
626 if (ap->mtime_char == '+')
627 return file_age >= mtime_secs + 24*60*60;
628 if (ap->mtime_char == '-')
629 return file_age < mtime_secs;
630
631 return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
632}
633#endif
634#if ENABLE_FEATURE_FIND_MMIN
635ACTF(mmin)
636{
637 time_t file_age = time(NULL) - statbuf->st_mtime;
638 time_t mmin_secs = ap->mmin_mins * 60;
639 if (ap->mmin_char == '+')
640 return file_age >= mmin_secs + 60;
641 if (ap->mmin_char == '-')
642 return file_age < mmin_secs;
643
644 return file_age >= mmin_secs && file_age < (mmin_secs + 60);
645}
646#endif
647#if ENABLE_FEATURE_FIND_NEWER
648ACTF(newer)
649{
650 return (ap->newer_mtime < statbuf->st_mtime);
651}
652#endif
653#if ENABLE_FEATURE_FIND_INUM
654ACTF(inum)
655{
656 return (statbuf->st_ino == ap->inode_num);
657}
658#endif
659#if ENABLE_FEATURE_FIND_EXEC
660static int do_exec(action_exec *ap, const char *fileName)
661{
662 int i, rc;
663# if ENABLE_FEATURE_FIND_EXEC_PLUS
664 int size = ap->exec_argc + ap->filelist_idx + 1;
665# else
666 int size = ap->exec_argc + 1;
667# endif
668# if ENABLE_USE_PORTABLE_CODE
669 char **argv = alloca(sizeof(char*) * size);
670# else
671 char *argv[size];
672# endif
673 char **pp = argv;
674
675 for (i = 0; i < ap->exec_argc; i++) {
676 const char *arg = ap->exec_argv[i];
677
678# if ENABLE_FEATURE_FIND_EXEC_PLUS
679 if (ap->filelist) {
680
681
682
683
684 if (ap->subst_count[i] == 0) {
685 *pp++ = xstrdup(arg);
686 } else {
687 int j = 0;
688 while (ap->filelist[j]) {
689
690 *pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]);
691 free(ap->filelist[j]);
692 j++;
693 }
694 }
695 } else
696# endif
697 {
698
699 *pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName);
700 }
701 }
702 *pp = NULL;
703
704# if ENABLE_FEATURE_FIND_EXEC_PLUS
705 if (ap->filelist) {
706 ap->filelist[0] = NULL;
707 ap->filelist_idx = 0;
708 ap->file_len = 0;
709 }
710# endif
711
712 rc = spawn_and_wait(argv);
713 if (rc < 0)
714 bb_simple_perror_msg(argv[0]);
715
716 i = 0;
717 while (argv[i])
718 free(argv[i++]);
719 return rc == 0;
720}
721ACTF(exec)
722{
723# if ENABLE_FEATURE_FIND_EXEC_PLUS
724 if (ap->filelist) {
725 int rc;
726
727 ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx);
728 ap->filelist[ap->filelist_idx++] = xstrdup(fileName);
729 ap->file_len += strlen(fileName) + sizeof(char*) + 1;
730
731 rc = 1;
732 if (ap->file_len >= G.max_argv_len)
733 rc = do_exec(ap, NULL);
734 return rc;
735 }
736# endif
737 return do_exec(ap, fileName);
738}
739# if ENABLE_FEATURE_FIND_EXEC_PLUS
740static int flush_exec_plus(void)
741{
742 action *ap;
743 action **app;
744 action ***appp = G.actions;
745 while ((app = *appp++) != NULL) {
746 while ((ap = *app++) != NULL) {
747 if (ap->f == (action_fp)func_exec) {
748 action_exec *ae = (void*)ap;
749 if (ae->filelist_idx != 0) {
750 int rc = do_exec(ae, NULL);
751# if ENABLE_FEATURE_FIND_NOT
752 if (ap->invert) rc = !rc;
753# endif
754 if (rc == 0)
755 return 1;
756 }
757 }
758 }
759 }
760 return 0;
761}
762# endif
763#endif
764#if ENABLE_FEATURE_FIND_USER
765ACTF(user)
766{
767 return (statbuf->st_uid == ap->uid);
768}
769#endif
770#if ENABLE_FEATURE_FIND_GROUP
771ACTF(group)
772{
773 return (statbuf->st_gid == ap->gid);
774}
775#endif
776#if ENABLE_FEATURE_FIND_PRINT0
777ACTF(print0)
778{
779 printf("%s%c", fileName, '\0');
780 return TRUE;
781}
782#endif
783ACTF(print)
784{
785 puts(fileName);
786 return TRUE;
787}
788#if ENABLE_FEATURE_FIND_PAREN
789ACTF(paren)
790{
791 return exec_actions(ap->subexpr, fileName, statbuf);
792}
793#endif
794#if ENABLE_FEATURE_FIND_SIZE
795ACTF(size)
796{
797 if (ap->size_char == '+')
798 return statbuf->st_size > ap->size;
799 if (ap->size_char == '-')
800 return statbuf->st_size < ap->size;
801 return statbuf->st_size == ap->size;
802}
803#endif
804#if ENABLE_FEATURE_FIND_PRUNE
805
806
807
808
809
810
811ACTF(prune)
812{
813 return SKIP + TRUE;
814}
815#endif
816#if ENABLE_FEATURE_FIND_QUIT
817ACTF(quit)
818{
819 exit(G.exitstatus);
820}
821#endif
822#if ENABLE_FEATURE_FIND_DELETE
823ACTF(delete)
824{
825 int rc;
826 if (S_ISDIR(statbuf->st_mode)) {
827
828 rc = 0;
829 if (NOT_LONE_CHAR(fileName, '.'))
830 rc = rmdir(fileName);
831 } else {
832 rc = unlink(fileName);
833 }
834 if (rc < 0)
835 bb_simple_perror_msg(fileName);
836 return TRUE;
837}
838#endif
839#if ENABLE_FEATURE_FIND_EMPTY
840ACTF(empty)
841{
842 if (S_ISDIR(statbuf->st_mode)) {
843 DIR *dir;
844 struct dirent *dent;
845
846 dir = opendir(fileName);
847 if (!dir) {
848 bb_simple_perror_msg(fileName);
849 return FALSE;
850 }
851
852 while ((dent = readdir(dir)) != NULL
853 && DOT_OR_DOTDOT(dent->d_name)
854 ) {
855 continue;
856 }
857 closedir(dir);
858 return dent == NULL;
859 }
860 return S_ISREG(statbuf->st_mode) && statbuf->st_size == 0;
861}
862#endif
863#if ENABLE_FEATURE_FIND_CONTEXT
864ACTF(context)
865{
866 security_context_t con;
867 int rc;
868
869 if (G.recurse_flags & ACTION_FOLLOWLINKS) {
870 rc = getfilecon(fileName, &con);
871 } else {
872 rc = lgetfilecon(fileName, &con);
873 }
874 if (rc < 0)
875 return FALSE;
876 rc = strcmp(ap->context, con);
877 freecon(con);
878 return rc == 0;
879}
880#endif
881#if ENABLE_FEATURE_FIND_LINKS
882ACTF(links)
883{
884 switch(ap->links_char) {
885 case '-' : return (statbuf->st_nlink < ap->links_count);
886 case '+' : return (statbuf->st_nlink > ap->links_count);
887 default: return (statbuf->st_nlink == ap->links_count);
888 }
889}
890#endif
891
892static int FAST_FUNC fileAction(
893 struct recursive_state *state IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
894 const char *fileName,
895 struct stat *statbuf)
896{
897 int r;
898 int same_fs = 1;
899
900#if ENABLE_FEATURE_FIND_XDEV
901 if (S_ISDIR(statbuf->st_mode) && G.xdev_count) {
902 int i;
903 for (i = 0; i < G.xdev_count; i++) {
904 if (G.xdev_dev[i] == statbuf->st_dev)
905 goto found;
906 }
907
908 same_fs = 0;
909 found: ;
910 }
911#endif
912
913#if ENABLE_FEATURE_FIND_MAXDEPTH
914 if (state->depth < G.minmaxdepth[0]) {
915 if (same_fs)
916 return TRUE;
917 return SKIP;
918 }
919 if (state->depth > G.minmaxdepth[1])
920 return SKIP;
921#endif
922
923 r = exec_actions(G.actions, fileName, statbuf);
924
925 if ((r & TRUE) && G.need_print)
926 puts(fileName);
927
928#if ENABLE_FEATURE_FIND_MAXDEPTH
929 if (S_ISDIR(statbuf->st_mode)) {
930 if (state->depth == G.minmaxdepth[1])
931 return SKIP;
932 }
933#endif
934
935
936 if (!same_fs) {
937 return SKIP;
938 }
939
940
941
942 return (r & SKIP) ? SKIP : TRUE;
943}
944
945
946#if ENABLE_FEATURE_FIND_TYPE
947static int find_type(const char *type)
948{
949 int mask = 0;
950
951 if (*type == 'b')
952 mask = S_IFBLK;
953 else if (*type == 'c')
954 mask = S_IFCHR;
955 else if (*type == 'd')
956 mask = S_IFDIR;
957 else if (*type == 'p')
958 mask = S_IFIFO;
959 else if (*type == 'f')
960 mask = S_IFREG;
961 else if (*type == 'l')
962 mask = S_IFLNK;
963 else if (*type == 's')
964 mask = S_IFSOCK;
965
966 if (mask == 0 || type[1] != '\0')
967 bb_error_msg_and_die(bb_msg_invalid_arg_to, type, "-type");
968
969 return mask;
970}
971#endif
972
973#if ENABLE_FEATURE_FIND_PERM \
974 || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
975 || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS
976static const char* plus_minus_num(const char* str)
977{
978 if (*str == '-' || *str == '+')
979 str++;
980 return str;
981}
982#endif
983
984
985#define USE_NESTED_FUNCTION 0
986
987#if !USE_NESTED_FUNCTION
988struct pp_locals {
989 action*** appp;
990 unsigned cur_group;
991 unsigned cur_action;
992 IF_FEATURE_FIND_NOT( bool invert_flag; )
993};
994static action* alloc_action(struct pp_locals *ppl, int sizeof_struct, action_fp f)
995{
996 action *ap = xzalloc(sizeof_struct);
997 action **app;
998 action ***group = &ppl->appp[ppl->cur_group];
999 *group = app = xrealloc(*group, (ppl->cur_action+2) * sizeof(ppl->appp[0][0]));
1000 app[ppl->cur_action++] = ap;
1001 app[ppl->cur_action] = NULL;
1002 ap->f = f;
1003 IF_FEATURE_FIND_NOT( ap->invert = ppl->invert_flag; )
1004 IF_FEATURE_FIND_NOT( ppl->invert_flag = 0; )
1005 return ap;
1006}
1007#endif
1008
1009static action*** parse_params(char **argv)
1010{
1011 enum {
1012 OPT_FOLLOW ,
1013 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
1014 IF_FEATURE_FIND_DEPTH( OPT_DEPTH ,)
1015 PARM_a ,
1016 PARM_o ,
1017 IF_FEATURE_FIND_NOT( PARM_char_not ,)
1018#if ENABLE_DESKTOP
1019 PARM_and ,
1020 PARM_or ,
1021 IF_FEATURE_FIND_NOT( PARM_not ,)
1022#endif
1023 PARM_print ,
1024 IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
1025 IF_FEATURE_FIND_PRUNE( PARM_prune ,)
1026 IF_FEATURE_FIND_QUIT( PARM_quit ,)
1027 IF_FEATURE_FIND_DELETE( PARM_delete ,)
1028 IF_FEATURE_FIND_EMPTY( PARM_empty ,)
1029 IF_FEATURE_FIND_EXEC( PARM_exec ,)
1030 IF_FEATURE_FIND_EXECUTABLE(PARM_executable,)
1031 IF_FEATURE_FIND_PAREN( PARM_char_brace,)
1032
1033 PARM_name ,
1034 PARM_iname ,
1035 IF_FEATURE_FIND_PATH( PARM_path ,)
1036#if ENABLE_DESKTOP
1037
1038
1039 IF_FEATURE_FIND_PATH( PARM_wholename ,)
1040#endif
1041 IF_FEATURE_FIND_PATH( PARM_ipath ,)
1042 IF_FEATURE_FIND_REGEX( PARM_regex ,)
1043 IF_FEATURE_FIND_TYPE( PARM_type ,)
1044 IF_FEATURE_FIND_PERM( PARM_perm ,)
1045 IF_FEATURE_FIND_MTIME( PARM_mtime ,)
1046 IF_FEATURE_FIND_MMIN( PARM_mmin ,)
1047 IF_FEATURE_FIND_NEWER( PARM_newer ,)
1048 IF_FEATURE_FIND_INUM( PARM_inum ,)
1049 IF_FEATURE_FIND_USER( PARM_user ,)
1050 IF_FEATURE_FIND_GROUP( PARM_group ,)
1051 IF_FEATURE_FIND_SIZE( PARM_size ,)
1052 IF_FEATURE_FIND_CONTEXT(PARM_context ,)
1053 IF_FEATURE_FIND_LINKS( PARM_links ,)
1054 IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,)
1055 };
1056
1057 static const char params[] ALIGN1 =
1058 "-follow\0"
1059 IF_FEATURE_FIND_XDEV( "-xdev\0" )
1060 IF_FEATURE_FIND_DEPTH( "-depth\0" )
1061 "-a\0"
1062 "-o\0"
1063 IF_FEATURE_FIND_NOT( "!\0" )
1064#if ENABLE_DESKTOP
1065 "-and\0"
1066 "-or\0"
1067 IF_FEATURE_FIND_NOT( "-not\0" )
1068#endif
1069 "-print\0"
1070 IF_FEATURE_FIND_PRINT0( "-print0\0" )
1071 IF_FEATURE_FIND_PRUNE( "-prune\0" )
1072 IF_FEATURE_FIND_QUIT( "-quit\0" )
1073 IF_FEATURE_FIND_DELETE( "-delete\0" )
1074 IF_FEATURE_FIND_EMPTY( "-empty\0" )
1075 IF_FEATURE_FIND_EXEC( "-exec\0" )
1076 IF_FEATURE_FIND_EXECUTABLE("-executable\0")
1077 IF_FEATURE_FIND_PAREN( "(\0" )
1078
1079 "-name\0"
1080 "-iname\0"
1081 IF_FEATURE_FIND_PATH( "-path\0" )
1082#if ENABLE_DESKTOP
1083 IF_FEATURE_FIND_PATH( "-wholename\0")
1084#endif
1085 IF_FEATURE_FIND_PATH( "-ipath\0" )
1086 IF_FEATURE_FIND_REGEX( "-regex\0" )
1087 IF_FEATURE_FIND_TYPE( "-type\0" )
1088 IF_FEATURE_FIND_PERM( "-perm\0" )
1089 IF_FEATURE_FIND_MTIME( "-mtime\0" )
1090 IF_FEATURE_FIND_MMIN( "-mmin\0" )
1091 IF_FEATURE_FIND_NEWER( "-newer\0" )
1092 IF_FEATURE_FIND_INUM( "-inum\0" )
1093 IF_FEATURE_FIND_USER( "-user\0" )
1094 IF_FEATURE_FIND_GROUP( "-group\0" )
1095 IF_FEATURE_FIND_SIZE( "-size\0" )
1096 IF_FEATURE_FIND_CONTEXT("-context\0")
1097 IF_FEATURE_FIND_LINKS( "-links\0" )
1098 IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
1099 ;
1100
1101#if !USE_NESTED_FUNCTION
1102 struct pp_locals ppl;
1103#define appp (ppl.appp )
1104#define cur_group (ppl.cur_group )
1105#define cur_action (ppl.cur_action )
1106#define invert_flag (ppl.invert_flag)
1107#define ALLOC_ACTION(name) (action_##name*)alloc_action(&ppl, sizeof(action_##name), (action_fp) func_##name)
1108#else
1109 action*** appp;
1110 unsigned cur_group;
1111 unsigned cur_action;
1112 IF_FEATURE_FIND_NOT( bool invert_flag; )
1113
1114
1115
1116
1117 auto action* alloc_action(int sizeof_struct, action_fp f);
1118 action* alloc_action(int sizeof_struct, action_fp f)
1119 {
1120 action *ap;
1121 appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(appp[0][0]));
1122 appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct);
1123 appp[cur_group][cur_action] = NULL;
1124 ap->f = f;
1125 IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
1126 IF_FEATURE_FIND_NOT( invert_flag = 0; )
1127 return ap;
1128 }
1129#define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
1130#endif
1131
1132 cur_group = 0;
1133 cur_action = 0;
1134 IF_FEATURE_FIND_NOT( invert_flag = 0; )
1135 appp = xzalloc(2 * sizeof(appp[0]));
1136
1137 while (*argv) {
1138 const char *arg = argv[0];
1139 int parm = index_in_strings(params, arg);
1140 const char *arg1 = argv[1];
1141
1142 dbg("arg:'%s' arg1:'%s' parm:%d PARM_type:%d", arg, arg1, parm, PARM_type);
1143
1144 if (parm >= PARM_name) {
1145
1146 if (!arg1)
1147 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1148 argv++;
1149 }
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159 if (parm == OPT_FOLLOW) {
1160 dbg("follow enabled: %d", __LINE__);
1161 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1162 }
1163#if ENABLE_FEATURE_FIND_XDEV
1164 else if (parm == OPT_XDEV) {
1165 dbg("%d", __LINE__);
1166 G.xdev_on = 1;
1167 }
1168#endif
1169#if ENABLE_FEATURE_FIND_MAXDEPTH
1170 else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
1171 dbg("%d", __LINE__);
1172 G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
1173 }
1174#endif
1175#if ENABLE_FEATURE_FIND_DEPTH
1176 else if (parm == OPT_DEPTH) {
1177 dbg("%d", __LINE__);
1178 G.recurse_flags |= ACTION_DEPTHFIRST;
1179 }
1180#endif
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191 else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
1192 dbg("%d", __LINE__);
1193
1194 }
1195 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
1196 dbg("%d", __LINE__);
1197
1198 cur_group++;
1199 appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0]));
1200
1201 appp[cur_group+1] = NULL;
1202 cur_action = 0;
1203 }
1204#if ENABLE_FEATURE_FIND_NOT
1205 else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
1206
1207 invert_flag ^= 1;
1208 dbg("invert_flag:%d", invert_flag);
1209 }
1210#endif
1211
1212 else if (parm == PARM_print) {
1213 dbg("%d", __LINE__);
1214 G.need_print = 0;
1215 (void) ALLOC_ACTION(print);
1216 }
1217#if ENABLE_FEATURE_FIND_PRINT0
1218 else if (parm == PARM_print0) {
1219 dbg("%d", __LINE__);
1220 G.need_print = 0;
1221 (void) ALLOC_ACTION(print0);
1222 }
1223#endif
1224#if ENABLE_FEATURE_FIND_PRUNE
1225 else if (parm == PARM_prune) {
1226 dbg("%d", __LINE__);
1227 (void) ALLOC_ACTION(prune);
1228 }
1229#endif
1230#if ENABLE_FEATURE_FIND_QUIT
1231 else if (parm == PARM_quit) {
1232 dbg("%d", __LINE__);
1233 (void) ALLOC_ACTION(quit);
1234 }
1235#endif
1236#if ENABLE_FEATURE_FIND_DELETE
1237 else if (parm == PARM_delete) {
1238 dbg("%d", __LINE__);
1239 G.need_print = 0;
1240 G.recurse_flags |= ACTION_DEPTHFIRST;
1241 (void) ALLOC_ACTION(delete);
1242 }
1243#endif
1244#if ENABLE_FEATURE_FIND_EMPTY
1245 else if (parm == PARM_empty) {
1246 dbg("%d", __LINE__);
1247 (void) ALLOC_ACTION(empty);
1248 }
1249#endif
1250#if ENABLE_FEATURE_FIND_EXEC
1251 else if (parm == PARM_exec) {
1252 int i;
1253 action_exec *ap;
1254 IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;)
1255 dbg("%d", __LINE__);
1256 G.need_print = 0;
1257 ap = ALLOC_ACTION(exec);
1258 ap->exec_argv = ++argv;
1259
1260 while (1) {
1261 if (!*argv)
1262 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
1263
1264
1265
1266
1267 if ((argv[0][0] == ';' || argv[0][0] == '+')
1268 && argv[0][1] == '\0'
1269 ) {
1270# if ENABLE_FEATURE_FIND_EXEC_PLUS
1271 if (argv[0][0] == '+')
1272 ap->filelist = xzalloc(sizeof(ap->filelist[0]));
1273# endif
1274 break;
1275 }
1276 argv++;
1277 ap->exec_argc++;
1278 }
1279 if (ap->exec_argc == 0)
1280 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1281 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
1282 i = ap->exec_argc;
1283 while (i--) {
1284 ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}");
1285 IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];)
1286 }
1287# if ENABLE_FEATURE_FIND_EXEC_PLUS
1288
1289
1290
1291 if (all_subst != 1 && ap->filelist)
1292 bb_simple_error_msg_and_die("only one '{}' allowed for -exec +");
1293# endif
1294 }
1295#endif
1296#if ENABLE_FEATURE_FIND_PAREN
1297 else if (parm == PARM_char_brace) {
1298 action_paren *ap;
1299 char **endarg;
1300 unsigned nested = 1;
1301
1302 dbg("%d", __LINE__);
1303 endarg = argv;
1304 while (1) {
1305 if (!*++endarg)
1306 bb_simple_error_msg_and_die("unpaired '('");
1307 if (LONE_CHAR(*endarg, '('))
1308 nested++;
1309 else if (LONE_CHAR(*endarg, ')') && !--nested) {
1310 *endarg = NULL;
1311 break;
1312 }
1313 }
1314 ap = ALLOC_ACTION(paren);
1315 ap->subexpr = parse_params(argv + 1);
1316 *endarg = (char*) ")";
1317 argv = endarg;
1318 }
1319#endif
1320 else if (parm == PARM_name || parm == PARM_iname) {
1321 action_name *ap;
1322 dbg("%d", __LINE__);
1323 ap = ALLOC_ACTION(name);
1324 ap->pattern = arg1;
1325 ap->iname = (parm == PARM_iname);
1326 }
1327#if ENABLE_FEATURE_FIND_PATH
1328 else if (parm == PARM_path IF_DESKTOP(|| parm == PARM_wholename) || parm == PARM_ipath) {
1329 action_path *ap;
1330 dbg("%d", __LINE__);
1331 ap = ALLOC_ACTION(path);
1332 ap->pattern = arg1;
1333 ap->ipath = (parm == PARM_ipath);
1334 }
1335#endif
1336#if ENABLE_FEATURE_FIND_REGEX
1337 else if (parm == PARM_regex) {
1338 action_regex *ap;
1339 dbg("%d", __LINE__);
1340 ap = ALLOC_ACTION(regex);
1341 xregcomp(&ap->compiled_pattern, arg1, 0 );
1342 }
1343#endif
1344#if ENABLE_FEATURE_FIND_TYPE
1345 else if (parm == PARM_type) {
1346 action_type *ap;
1347 ap = ALLOC_ACTION(type);
1348 ap->type_mask = find_type(arg1);
1349 dbg("created:type mask:%x", ap->type_mask);
1350 }
1351#endif
1352#if ENABLE_FEATURE_FIND_EXECUTABLE
1353 else if (parm == PARM_executable) {
1354 (void) ALLOC_ACTION(executable);
1355 }
1356#endif
1357#if ENABLE_FEATURE_FIND_PERM
1358
1359
1360
1361
1362
1363 else if (parm == PARM_perm) {
1364 action_perm *ap;
1365 dbg("%d", __LINE__);
1366 ap = ALLOC_ACTION(perm);
1367 ap->perm_char = arg1[0];
1368 arg1 = (arg1[0] == '/' ? arg1+1 : plus_minus_num(arg1));
1369
1370 ap->perm_mask = bb_parse_mode(arg1, ap->perm_mask);
1371 if (ap->perm_mask == (mode_t)-1)
1372 bb_error_msg_and_die("invalid mode '%s'", arg1);
1373 }
1374#endif
1375#if ENABLE_FEATURE_FIND_MTIME
1376 else if (parm == PARM_mtime) {
1377 action_mtime *ap;
1378 dbg("%d", __LINE__);
1379 ap = ALLOC_ACTION(mtime);
1380 ap->mtime_char = arg1[0];
1381 ap->mtime_days = xatoul(plus_minus_num(arg1));
1382 }
1383#endif
1384#if ENABLE_FEATURE_FIND_MMIN
1385 else if (parm == PARM_mmin) {
1386 action_mmin *ap;
1387 dbg("%d", __LINE__);
1388 ap = ALLOC_ACTION(mmin);
1389 ap->mmin_char = arg1[0];
1390 ap->mmin_mins = xatoul(plus_minus_num(arg1));
1391 }
1392#endif
1393#if ENABLE_FEATURE_FIND_NEWER
1394 else if (parm == PARM_newer) {
1395 struct stat stat_newer;
1396 action_newer *ap;
1397 dbg("%d", __LINE__);
1398 ap = ALLOC_ACTION(newer);
1399 xstat(arg1, &stat_newer);
1400 ap->newer_mtime = stat_newer.st_mtime;
1401 }
1402#endif
1403#if ENABLE_FEATURE_FIND_INUM
1404 else if (parm == PARM_inum) {
1405 action_inum *ap;
1406 dbg("%d", __LINE__);
1407 ap = ALLOC_ACTION(inum);
1408 ap->inode_num = xatoul(arg1);
1409 }
1410#endif
1411#if ENABLE_FEATURE_FIND_USER
1412 else if (parm == PARM_user) {
1413 action_user *ap;
1414 dbg("%d", __LINE__);
1415 ap = ALLOC_ACTION(user);
1416 ap->uid = bb_strtou(arg1, NULL, 10);
1417 if (errno)
1418 ap->uid = xuname2uid(arg1);
1419 }
1420#endif
1421#if ENABLE_FEATURE_FIND_GROUP
1422 else if (parm == PARM_group) {
1423 action_group *ap;
1424 dbg("%d", __LINE__);
1425 ap = ALLOC_ACTION(group);
1426 ap->gid = bb_strtou(arg1, NULL, 10);
1427 if (errno)
1428 ap->gid = xgroup2gid(arg1);
1429 }
1430#endif
1431#if ENABLE_FEATURE_FIND_SIZE
1432 else if (parm == PARM_size) {
1433
1434
1435
1436
1437
1438
1439#if ENABLE_LFS
1440#define XATOU_SFX xatoull_sfx
1441#else
1442#define XATOU_SFX xatoul_sfx
1443#endif
1444 static const struct suffix_mult find_suffixes[] ALIGN_SUFFIX = {
1445 { "c", 1 },
1446 { "w", 2 },
1447 { "", 512 },
1448 { "b", 512 },
1449 { "k", 1024 },
1450 { "", 0 }
1451 };
1452 action_size *ap;
1453 dbg("%d", __LINE__);
1454 ap = ALLOC_ACTION(size);
1455 ap->size_char = arg1[0];
1456 ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
1457 }
1458#endif
1459#if ENABLE_FEATURE_FIND_CONTEXT
1460 else if (parm == PARM_context) {
1461 action_context *ap;
1462 dbg("%d", __LINE__);
1463 ap = ALLOC_ACTION(context);
1464
1465
1466 if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
1467 bb_simple_perror_msg(arg1);
1468 }
1469#endif
1470#if ENABLE_FEATURE_FIND_LINKS
1471 else if (parm == PARM_links) {
1472 action_links *ap;
1473 dbg("%d", __LINE__);
1474 ap = ALLOC_ACTION(links);
1475 ap->links_char = arg1[0];
1476 ap->links_count = xatoul(plus_minus_num(arg1));
1477 }
1478#endif
1479 else {
1480 bb_error_msg("unrecognized: %s", arg);
1481 bb_show_usage();
1482 }
1483 argv++;
1484 }
1485 dbg("exiting %s", __func__);
1486 return appp;
1487#undef ALLOC_ACTION
1488#undef appp
1489#undef cur_action
1490#undef invert_flag
1491}
1492
1493int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1494int find_main(int argc UNUSED_PARAM, char **argv)
1495{
1496 int i, firstopt;
1497 char **past_HLP, *saved;
1498
1499 INIT_G();
1500
1501
1502
1503
1504
1505 past_HLP = argv;
1506 for (;;) {
1507 saved = *++past_HLP;
1508 if (!saved)
1509 break;
1510 if (saved[0] != '-')
1511 break;
1512 if (!saved[1])
1513 break;
1514 if (saved[1] == '-' && !saved[2]) {
1515
1516
1517 saved = *++past_HLP;
1518 break;
1519 }
1520 if ((saved+1)[strspn(saved+1, "HLP")] != '\0')
1521 break;
1522 }
1523 *past_HLP = NULL;
1524
1525 i = getopt32(argv, "+""HLP");
1526 if (i & (1<<0))
1527 G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK;
1528 if (i & (1<<1))
1529 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1530
1531 argv = past_HLP;
1532 *past_HLP = saved;
1533
1534 for (firstopt = 0; argv[firstopt]; firstopt++) {
1535 if (argv[firstopt][0] == '-')
1536 break;
1537 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
1538 break;
1539 if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '('))
1540 break;
1541 }
1542 if (firstopt == 0) {
1543 *--argv = (char*)".";
1544 firstopt++;
1545 }
1546
1547 G.actions = parse_params(&argv[firstopt]);
1548 argv[firstopt] = NULL;
1549
1550#if ENABLE_FEATURE_FIND_XDEV
1551 if (G.xdev_on) {
1552 struct stat stbuf;
1553
1554 G.xdev_count = firstopt;
1555 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
1556 for (i = 0; argv[i]; i++) {
1557
1558
1559 if (stat(argv[i], &stbuf) == 0)
1560 G.xdev_dev[i] = stbuf.st_dev;
1561
1562
1563
1564 }
1565 }
1566#endif
1567
1568 for (i = 0; argv[i]; i++) {
1569 if (!recursive_action(argv[i],
1570 G.recurse_flags,
1571 fileAction,
1572 fileAction,
1573 NULL)
1574 ) {
1575 G.exitstatus |= EXIT_FAILURE;
1576 }
1577 }
1578
1579 IF_FEATURE_FIND_EXEC_PLUS(G.exitstatus |= flush_exec_plus();)
1580 return G.exitstatus;
1581}
1582