attempt to regularize atoi mess.
[oweals/busybox.git] / findutils / find.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini find implementation for busybox
4  *
5  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Reworked by David Douthitt <n9ubh@callsign.net> and
8  *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
9  *
10  * Licensed under the GPL version 2, see the file LICENSE in this tarball.
11  */
12
13 #include "busybox.h"
14 #include <fnmatch.h>
15
16 static char *pattern;
17 #ifdef CONFIG_FEATURE_FIND_PRINT0
18 static char printsep = '\n';
19 #endif
20
21 #ifdef CONFIG_FEATURE_FIND_TYPE
22 static int type_mask = 0;
23 #endif
24
25 #ifdef CONFIG_FEATURE_FIND_PERM
26 static char perm_char = 0;
27 static int perm_mask = 0;
28 #endif
29
30 #ifdef CONFIG_FEATURE_FIND_MTIME
31 static char mtime_char;
32 static int mtime_days;
33 #endif
34
35 #ifdef CONFIG_FEATURE_FIND_MMIN
36 static char mmin_char;
37 static int mmin_mins;
38 #endif
39
40 #ifdef CONFIG_FEATURE_FIND_XDEV
41 static dev_t *xdev_dev;
42 static int xdev_count = 0;
43 #endif
44
45 #ifdef CONFIG_FEATURE_FIND_NEWER
46 static time_t newer_mtime;
47 #endif
48
49 #ifdef CONFIG_FEATURE_FIND_INUM
50 static ino_t inode_num;
51 #endif
52
53 #ifdef CONFIG_FEATURE_FIND_EXEC
54 static char **exec_str;
55 static int num_matches;
56 static int exec_opt;
57 #endif
58
59 static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
60 {
61 #ifdef CONFIG_FEATURE_FIND_XDEV
62         if (S_ISDIR(statbuf->st_mode) && xdev_count) {
63                 int i;
64                 for (i=0; i<xdev_count; i++) {
65                         if (xdev_dev[i] != statbuf->st_dev)
66                                 return SKIP;
67                 }
68         }
69 #endif
70         if (pattern != NULL) {
71                 const char *tmp = strrchr(fileName, '/');
72
73                 if (tmp == NULL)
74                         tmp = fileName;
75                 else
76                         tmp++;
77                 if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0))
78                         goto no_match;
79         }
80 #ifdef CONFIG_FEATURE_FIND_TYPE
81         if (type_mask != 0) {
82                 if (!((statbuf->st_mode & S_IFMT) == type_mask))
83                         goto no_match;
84         }
85 #endif
86 #ifdef CONFIG_FEATURE_FIND_PERM
87         if (perm_mask != 0) {
88                 if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
89                          (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
90                          (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
91                         goto no_match;
92         }
93 #endif
94 #ifdef CONFIG_FEATURE_FIND_MTIME
95         if (mtime_char != 0) {
96                 time_t file_age = time(NULL) - statbuf->st_mtime;
97                 time_t mtime_secs = mtime_days * 24 * 60 * 60;
98                 if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
99                                                 file_age < mtime_secs + 24 * 60 * 60) ||
100                                 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
101                                 (mtime_char == '-' && file_age < mtime_secs)))
102                         goto no_match;
103         }
104 #endif
105 #ifdef CONFIG_FEATURE_FIND_MMIN
106         if (mmin_char != 0) {
107                 time_t file_age = time(NULL) - statbuf->st_mtime;
108                 time_t mmin_secs = mmin_mins * 60;
109                 if (!((isdigit(mmin_char) && file_age >= mmin_secs &&
110                                                 file_age < mmin_secs + 60) ||
111                                 (mmin_char == '+' && file_age >= mmin_secs + 60) ||
112                                 (mmin_char == '-' && file_age < mmin_secs)))
113                         goto no_match;
114         }
115 #endif
116 #ifdef CONFIG_FEATURE_FIND_NEWER
117         if (newer_mtime != 0) {
118                 time_t file_age = newer_mtime - statbuf->st_mtime;
119                 if (file_age >= 0)
120                         goto no_match;
121         }
122 #endif
123 #ifdef CONFIG_FEATURE_FIND_INUM
124         if (inode_num != 0) {
125                 if (!(statbuf->st_ino == inode_num))
126                         goto no_match;
127         }
128 #endif
129 #ifdef CONFIG_FEATURE_FIND_EXEC
130         if (exec_opt) {
131                 int i;
132                 char *cmd_string = "";
133                 for (i = 0; i < num_matches; i++)
134                         cmd_string = xasprintf("%s%s%s", cmd_string, exec_str[i], fileName);
135                 cmd_string = xasprintf("%s%s", cmd_string, exec_str[num_matches]);
136                 system(cmd_string);
137                 goto no_match;
138         }
139 #endif
140
141 #ifdef CONFIG_FEATURE_FIND_PRINT0
142         printf("%s%c", fileName, printsep);
143 #else
144         puts(fileName);
145 #endif
146 no_match:
147         return (TRUE);
148 }
149
150 #ifdef CONFIG_FEATURE_FIND_TYPE
151 static int find_type(char *type)
152 {
153         int mask = 0;
154
155         switch (type[0]) {
156                 case 'b':
157                         mask = S_IFBLK;
158                         break;
159                 case 'c':
160                         mask = S_IFCHR;
161                         break;
162                 case 'd':
163                         mask = S_IFDIR;
164                         break;
165                 case 'p':
166                         mask = S_IFIFO;
167                         break;
168                 case 'f':
169                         mask = S_IFREG;
170                         break;
171                 case 'l':
172                         mask = S_IFLNK;
173                         break;
174                 case 's':
175                         mask = S_IFSOCK;
176                         break;
177         }
178
179         if (mask == 0 || type[1] != '\0')
180                 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
181
182         return mask;
183 }
184 #endif
185
186 int find_main(int argc, char **argv)
187 {
188         int dereference = FALSE;
189         int i, firstopt, status = EXIT_SUCCESS;
190
191         for (firstopt = 1; firstopt < argc; firstopt++) {
192                 if (argv[firstopt][0] == '-')
193                         break;
194         }
195
196         /* Parse any options */
197         for (i = firstopt; i < argc; i++) {
198                 if (strcmp(argv[i], "-follow") == 0)
199                         dereference = TRUE;
200                 else if (strcmp(argv[i], "-print") == 0) {
201                         ;
202                         }
203 #ifdef CONFIG_FEATURE_FIND_PRINT0
204                 else if (strcmp(argv[i], "-print0") == 0)
205                         printsep = '\0';
206 #endif
207                 else if (strcmp(argv[i], "-name") == 0) {
208                         if (++i == argc)
209                                 bb_error_msg_and_die(bb_msg_requires_arg, "-name");
210                         pattern = argv[i];
211 #ifdef CONFIG_FEATURE_FIND_TYPE
212                 } else if (strcmp(argv[i], "-type") == 0) {
213                         if (++i == argc)
214                                 bb_error_msg_and_die(bb_msg_requires_arg, "-type");
215                         type_mask = find_type(argv[i]);
216 #endif
217 #ifdef CONFIG_FEATURE_FIND_PERM
218                 } else if (strcmp(argv[i], "-perm") == 0) {
219                         if (++i == argc)
220                                 bb_error_msg_and_die(bb_msg_requires_arg, "-perm");
221                         perm_mask = xstrtol_range(argv[i], 8, 0, 07777);
222                         perm_char = argv[i][0];
223                         if (perm_char == '-')
224                                 perm_mask = -perm_mask;
225 #endif
226 #ifdef CONFIG_FEATURE_FIND_MTIME
227                 } else if (strcmp(argv[i], "-mtime") == 0) {
228                         if (++i == argc)
229                                 bb_error_msg_and_die(bb_msg_requires_arg, "-mtime");
230                         mtime_days = xatol(argv[i]);
231                         mtime_char = argv[i][0];
232                         if (mtime_char == '-')
233                                 mtime_days = -mtime_days;
234 #endif
235 #ifdef CONFIG_FEATURE_FIND_MMIN
236                 } else if (strcmp(argv[i], "-mmin") == 0) {
237                         if (++i == argc)
238                                 bb_error_msg_and_die(bb_msg_requires_arg, "-mmin");
239                         mmin_mins = xatol(argv[i]);
240                         mmin_char = argv[i][0];
241                         if (mmin_char == '-')
242                                 mmin_mins = -mmin_mins;
243 #endif
244 #ifdef CONFIG_FEATURE_FIND_XDEV
245                 } else if (strcmp(argv[i], "-xdev") == 0) {
246                         struct stat stbuf;
247
248                         xdev_count = (firstopt - 1) ? (firstopt - 1) : 1;
249                         xdev_dev = xmalloc(xdev_count * sizeof(dev_t));
250
251                         if (firstopt == 1) {
252                                 xstat(".", &stbuf);
253                                 xdev_dev[0] = stbuf.st_dev;
254                         }
255                         else {
256
257                                 for (i = 1; i < firstopt; i++) {
258                                         xstat(argv[i], &stbuf);
259                                         xdev_dev[i-1] = stbuf.st_dev;
260                                 }
261                         }
262 #endif
263 #ifdef CONFIG_FEATURE_FIND_NEWER
264                 } else if (strcmp(argv[i], "-newer") == 0) {
265                         struct stat stat_newer;
266                         if (++i == argc)
267                                 bb_error_msg_and_die(bb_msg_requires_arg, "-newer");
268                         xstat(argv[i], &stat_newer);
269                         newer_mtime = stat_newer.st_mtime;
270 #endif
271 #ifdef CONFIG_FEATURE_FIND_INUM
272                 } else if (strcmp(argv[i], "-inum") == 0) {
273                         if (++i == argc)
274                                 bb_error_msg_and_die(bb_msg_requires_arg, "-inum");
275                         inode_num = xatoul(argv[i]);
276 #endif
277 #ifdef CONFIG_FEATURE_FIND_EXEC
278                 } else if (strcmp(argv[i], "-exec") == 0) {
279                         int b_pos;
280                         char *cmd_string = "";
281
282                         while (i++) {
283                                 if (i == argc)
284                                         bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
285                                 if (*argv[i] == ';')
286                                         break;
287                                 cmd_string = xasprintf("%s %s", cmd_string, argv[i]);
288                         }
289
290                         if (*cmd_string == 0)
291                                 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
292                         cmd_string++;
293                         exec_str = xmalloc(sizeof(char *));
294
295                         while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) {
296                                 num_matches++;
297                                 exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *));
298                                 exec_str[num_matches - 1] = xstrndup(cmd_string, b_pos);
299                                 cmd_string += b_pos + 2;
300                         }
301                         exec_str[num_matches] = xstrdup(cmd_string);
302                         exec_opt = 1;
303 #endif
304                 } else
305                         bb_show_usage();
306         }
307
308         if (firstopt == 1) {
309                 if (! recursive_action(".", TRUE, dereference, FALSE, fileAction,
310                                         fileAction, NULL))
311                         status = EXIT_FAILURE;
312         } else {
313                 for (i = 1; i < firstopt; i++) {
314                         if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
315                                                 fileAction, NULL))
316                                 status = EXIT_FAILURE;
317                 }
318         }
319
320         return status;
321 }