75ed4e208106e7257ff8db35e5888b02b0740d0f
[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  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  *
24  */
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <dirent.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <fnmatch.h>
32 #include <time.h>
33 #include <ctype.h>
34 #include "busybox.h"
35
36 //XXX just found out about libbb/messages.c . maybe move stuff there ? - ghoz
37 static const char msg_req_arg[] = "option `%s' requires an argument";
38 static const char msg_invalid_arg[] = "invalid argument `%s' to `%s'";
39
40 static char *pattern;
41
42 #ifdef CONFIG_FEATURE_FIND_TYPE
43 static int type_mask = 0;
44 #endif
45
46 #ifdef CONFIG_FEATURE_FIND_PERM
47 static char perm_char = 0;
48 static int perm_mask = 0;
49 #endif
50
51 #ifdef CONFIG_FEATURE_FIND_MTIME
52 static char mtime_char;
53 static int mtime_days;
54 #endif
55
56 #ifdef CONFIG_FEATURE_FIND_XDEV
57 static dev_t *xdev_dev;
58 static int xdev_count = 0;
59 #endif
60
61 #ifdef CONFIG_FEATURE_FIND_NEWER
62 static time_t newer_mtime;
63 #endif
64
65 #ifdef CONFIG_FEATURE_FIND_INUM
66 static ino_t inode_num;
67 #endif
68
69 #ifdef CONFIG_FEATURE_FIND_EXEC
70 static char **exec_str;
71 static int num_matches;
72 static int exec_opt;
73 #endif
74
75 static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
76 {
77         if (pattern != NULL) {
78                 const char *tmp = strrchr(fileName, '/');
79
80                 if (tmp == NULL)
81                         tmp = fileName;
82                 else
83                         tmp++;
84                 if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0))
85                         goto no_match;
86         }
87 #ifdef CONFIG_FEATURE_FIND_TYPE
88         if (type_mask != 0) {
89                 if (!((statbuf->st_mode & S_IFMT) == type_mask))
90                         goto no_match;
91         }
92 #endif
93 #ifdef CONFIG_FEATURE_FIND_PERM
94         if (perm_mask != 0) {
95                 if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
96                          (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
97                          (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
98                         goto no_match;
99         }
100 #endif
101 #ifdef CONFIG_FEATURE_FIND_MTIME
102         if (mtime_char != 0) {
103                 time_t file_age = time(NULL) - statbuf->st_mtime;
104                 time_t mtime_secs = mtime_days * 24 * 60 * 60;
105                 if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
106                                                 file_age < mtime_secs + 24 * 60 * 60) ||
107                                 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
108                                 (mtime_char == '-' && file_age < mtime_secs)))
109                         goto no_match;
110         }
111 #endif
112 #ifdef CONFIG_FEATURE_FIND_XDEV
113         if (xdev_count) {
114                 int i;
115                 for (i=0; i<xdev_count; i++) {
116                         if (xdev_dev[i] == statbuf-> st_dev)
117                                 break;
118                 }
119                 if (i == xdev_count) {
120                         if(S_ISDIR(statbuf->st_mode))
121                                 return SKIP;
122                         else
123                                 goto no_match;
124                 }
125         }
126 #endif
127 #ifdef CONFIG_FEATURE_FIND_NEWER
128         if (newer_mtime != 0) {
129                 time_t file_age = newer_mtime - statbuf->st_mtime;
130                 if (file_age >= 0)
131                         goto no_match;
132         }
133 #endif
134 #ifdef CONFIG_FEATURE_FIND_INUM
135         if (inode_num != 0) {
136                 if (!(statbuf->st_ino == inode_num))
137                         goto no_match;
138         }
139 #endif
140 #ifdef CONFIG_FEATURE_FIND_EXEC
141         if (exec_opt) {
142                 int i;
143                 char *cmd_string = "";
144                 for (i = 0; i < num_matches; i++)
145                         cmd_string = bb_xasprintf("%s%s%s", cmd_string, exec_str[i], fileName);
146                 cmd_string = bb_xasprintf("%s%s", cmd_string, exec_str[num_matches]);
147                 system(cmd_string);
148                 goto no_match;
149         }
150 #endif
151         
152         puts(fileName);
153 no_match:
154         return (TRUE);
155 }
156
157 #ifdef CONFIG_FEATURE_FIND_TYPE
158 static int find_type(char *type)
159 {
160         int mask = 0;
161
162         switch (type[0]) {
163                 case 'b':
164                         mask = S_IFBLK;
165                         break;
166                 case 'c':
167                         mask = S_IFCHR;
168                         break;
169                 case 'd':
170                         mask = S_IFDIR;
171                         break;
172                 case 'p':
173                         mask = S_IFIFO;
174                         break;
175                 case 'f':
176                         mask = S_IFREG;
177                         break;
178                 case 'l':
179                         mask = S_IFLNK;
180                         break;
181                 case 's':
182                         mask = S_IFSOCK;
183                         break;
184         }
185
186         if (mask == 0 || type[1] != '\0')
187                 bb_error_msg_and_die(msg_invalid_arg, type, "-type");
188
189         return mask;
190 }
191 #endif
192
193 int find_main(int argc, char **argv)
194 {
195         int dereference = FALSE;
196         int i, firstopt, status = EXIT_SUCCESS;
197
198         for (firstopt = 1; firstopt < argc; firstopt++) {
199                 if (argv[firstopt][0] == '-')
200                         break;
201         }
202
203         /* Parse any options */
204         for (i = firstopt; i < argc; i++) {
205                 if (strcmp(argv[i], "-follow") == 0)
206                         dereference = TRUE;
207                 else if (strcmp(argv[i], "-print") == 0) {
208                         ;
209                         }
210                 else if (strcmp(argv[i], "-name") == 0) {
211                         if (++i == argc)
212                                 bb_error_msg_and_die(msg_req_arg, "-name");
213                         pattern = argv[i];
214 #ifdef CONFIG_FEATURE_FIND_TYPE
215                 } else if (strcmp(argv[i], "-type") == 0) {
216                         if (++i == argc)
217                                 bb_error_msg_and_die(msg_req_arg, "-type");
218                         type_mask = find_type(argv[i]);
219 #endif
220 #ifdef CONFIG_FEATURE_FIND_PERM
221                 } else if (strcmp(argv[i], "-perm") == 0) {
222                         char *end;
223                         if (++i == argc)
224                                 bb_error_msg_and_die(msg_req_arg, "-perm");
225                         perm_mask = strtol(argv[i], &end, 8);
226                         if ((end[0] != '\0') || (perm_mask > 07777))
227                                 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-perm");
228                         if ((perm_char = argv[i][0]) == '-')
229                                 perm_mask = -perm_mask;
230 #endif
231 #ifdef CONFIG_FEATURE_FIND_MTIME
232                 } else if (strcmp(argv[i], "-mtime") == 0) {
233                         char *end;
234                         if (++i == argc)
235                                 bb_error_msg_and_die(msg_req_arg, "-mtime");
236                         mtime_days = strtol(argv[i], &end, 10);
237                         if (end[0] != '\0')
238                                 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mtime");
239                         if ((mtime_char = argv[i][0]) == '-')
240                                 mtime_days = -mtime_days;
241 #endif
242 #ifdef CONFIG_FEATURE_FIND_XDEV
243                 } else if (strcmp(argv[i], "-xdev") == 0) {
244                         struct stat stbuf;
245
246                         xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1;
247                         xdev_dev = xmalloc ( xdev_count * sizeof( dev_t ));
248
249                         if ( firstopt == 1 ) {
250                                 if ( stat ( ".", &stbuf ) < 0 )
251                                         bb_error_msg_and_die("could not stat '.'" );
252                                 xdev_dev [0] = stbuf. st_dev;
253                         }
254                         else {
255
256                                 for (i = 1; i < firstopt; i++) {
257                                         if ( stat ( argv [i], &stbuf ) < 0 )
258                                                 bb_error_msg_and_die("could not stat '%s'", argv [i] );
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(msg_req_arg, "-newer");
268                     if (stat (argv[i], &stat_newer) != 0)
269                                 bb_error_msg_and_die("file %s not found", argv[i]);
270                         newer_mtime = stat_newer.st_mtime;
271 #endif
272 #ifdef CONFIG_FEATURE_FIND_INUM
273                 } else if (strcmp(argv[i], "-inum") == 0) {
274                         char *end;
275                         if (++i == argc)
276                                 bb_error_msg_and_die(msg_req_arg, "-inum");
277                         inode_num = strtol(argv[i], &end, 10);
278                         if (end[0] != '\0')
279                                 bb_error_msg_and_die(msg_invalid_arg, argv[i], "-inum");
280 #endif
281 #ifdef CONFIG_FEATURE_FIND_EXEC
282                 } else if (strcmp(argv[i], "-exec") == 0) {
283                         int b_pos;
284                         char *cmd_string = "";
285
286                         while (i++) {
287                                 if (i == argc)
288                                         bb_error_msg_and_die(msg_req_arg, "-exec");
289                                 if (*argv[i] == ';')
290                                         break;
291                                 cmd_string = bb_xasprintf("%s %s", cmd_string, argv[i]);
292                         }
293                         
294                         if (*cmd_string == 0)
295                                 bb_error_msg_and_die(msg_req_arg, "-exec");
296                         cmd_string++;
297                         exec_str = xmalloc(sizeof(char *));
298
299                         while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) {
300                                 num_matches++;
301                                 exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *));
302                                 exec_str[num_matches - 1] = bb_xstrndup(cmd_string, b_pos);
303                                 cmd_string += b_pos + 2;
304                         }
305                         exec_str[num_matches] = bb_xstrdup(cmd_string);
306                         exec_opt = 1;
307 #endif
308                 } else
309                         bb_show_usage();
310         }
311
312         if (firstopt == 1) {
313                 if (! recursive_action(".", TRUE, dereference, FALSE, fileAction,
314                                         fileAction, NULL))
315                         status = EXIT_FAILURE;
316         } else {
317                 for (i = 1; i < firstopt; i++) {
318                         if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
319                                                 fileAction, NULL))
320                                 status = EXIT_FAILURE;
321                 }
322         }
323
324         return status;
325 }