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