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