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