]> err.no Git - sash/blob - cmd_find.c
Stop stripping during build. Also thanks to Helmut Grohne. Closes: #852771
[sash] / cmd_find.c
1 /*
2  * Copyright (c) 2014 by David I. Bell
3  * Permission is granted to use, distribute, or modify this source,
4  * provided that this copyright notice remains intact.
5  *
6  * The "find" built-in command.
7  */
8
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <dirent.h>
12 #include <errno.h>
13
14 #include "sash.h"
15
16
17 #ifdef  S_ISLNK
18 #define LSTAT   lstat
19 #else
20 #define LSTAT   stat
21 #endif
22
23
24 #define MAX_NAME_SIZE   (1024 * 10)
25
26
27 /*
28  * Items that can be specified to restrict the output.
29  */
30 static  BOOL            xdevFlag;
31 static  dev_t           xdevDevice;
32 static  long            fileSize;
33 static  const char *    filePattern;
34 static  const char *    fileType;
35
36
37 /*
38  * Recursive routine to examine the files in a directory.
39  */
40 static  void    examineDirectory(const char * path);
41 static  BOOL    testFile(const char * fullName, const struct stat * statBuf);
42
43
44
45 /*
46  * Find files from the specified directory path.
47  * This is limited to just printing their file names.
48  */
49 int
50 do_find(int argc, const char ** argv)
51 {
52         const char *    cp;
53         const char *    path;
54         struct stat     statBuf;
55
56         argc--;
57         argv++;
58
59         xdevFlag = FALSE;
60         fileType = NULL;
61         filePattern = NULL;
62         fileSize = 0;
63
64         if ((argc <= 0) || (**argv == '-'))
65         {
66                 fprintf(stderr, "No path specified\n");
67
68                 return 1;
69         }
70
71         path = *argv++;
72         argc--;
73
74         while (argc > 0)
75         {
76                 argc--;
77                 cp = *argv++;
78
79                 if (strcmp(cp, "-xdev") == 0)
80                         xdevFlag = TRUE;
81                 else if (strcmp(cp, "-type") == 0)
82                 {
83                         if ((argc <= 0) || (**argv == '-'))
84                         {
85                                 fprintf(stderr, "Missing type string\n");
86
87                                 return 1;
88                         }
89
90                         argc--;
91                         fileType = *argv++;
92                 }
93                 else if (strcmp(cp, "-name") == 0)
94                 {
95                         if ((argc <= 0) || (**argv == '-'))
96                         {
97                                 fprintf(stderr, "Missing file name\n");
98
99                                 return 1;
100                         }
101
102                         argc--;
103                         filePattern = *argv++;
104                 }
105                 else if (strcmp(cp, "-size") == 0)
106                 {
107                         if ((argc <= 0) || (**argv == '-'))
108                         {
109                                 fprintf(stderr, "Missing file size\n");
110
111                                 return 1;
112                         }
113
114                         argc--;
115                         cp = *argv++;
116
117                         fileSize = 0;
118
119                         while (isDecimal(*cp))
120                                 fileSize = fileSize * 10 + (*cp++ - '0');
121
122                         if (*cp || (fileSize < 0))
123                         {
124                                 fprintf(stderr, "Bad file size specified\n");
125
126                                 return 1;
127                         }
128                 }
129                 else
130                 {
131                         if (*cp != '-')
132                                 fprintf(stderr, "Missing dash in option\n");
133                         else
134                                 fprintf(stderr, "Unknown option\n");
135
136                         return 1;
137                 }
138         }
139
140         /*
141          * Get information about the path and make sure that it
142          * is a directory.
143          */
144         if (stat(path, &statBuf) < 0)
145         {
146                 fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
147                         strerror(errno));
148
149                 return 1;
150         }
151
152         if (!S_ISDIR(statBuf.st_mode))
153         {
154                 fprintf(stderr, "Path \"%s\" is not a directory\n", path);
155
156                 return 1;
157         }
158
159         /*
160          * Remember the device that this directory is on in case we need it.
161          */
162         xdevDevice = statBuf.st_dev;
163
164         /*
165          * If the directory meets the specified criteria, then print it out.
166          */
167         if (testFile(path, &statBuf))
168                 printf("%s\n", path);
169
170         /*
171          * Now examine the files in the directory.
172          */
173         examineDirectory(path);
174
175         return 0;
176 }
177
178
179 /*
180  * Recursive routine to examine the files in a directory.
181  */
182 static void
183 examineDirectory(const char * path)
184 {
185         DIR *           dir;
186         BOOL            needSlash;
187         struct dirent * entry;
188         struct stat     statBuf;
189         char            fullName[MAX_NAME_SIZE];
190
191         /*
192          * Open the directory.
193          */
194         dir = opendir(path);
195
196         if (dir == NULL)
197         {
198                 fprintf(stderr, "Cannot read directory \"%s\": %s\n",
199                         path, strerror(errno));
200
201                 return;
202         }
203
204         /*
205          * See if a slash is needed.
206          */
207         needSlash = (*path && (path[strlen(path) - 1] != '/'));
208
209         /*
210          * Read all of the directory entries and check them,
211          * except for the current and parent directory entries.
212          */
213         while (!intFlag && ((entry = readdir(dir)) != NULL))
214         {
215                 if ((strcmp(entry->d_name, ".") == 0) ||
216                         (strcmp(entry->d_name, "..") == 0))
217                 {
218                         continue;
219                 }
220
221                 /*
222                  * Build the full path name.
223                  */
224                 strcpy(fullName, path);
225
226                 if (needSlash)
227                         strcat(fullName, "/");
228
229                 strcat(fullName, entry->d_name);
230
231                 /*
232                  * Find out about this file.
233                  */
234                 if (LSTAT(fullName, &statBuf) < 0)
235                 {
236                         fprintf(stderr, "Cannot stat \"%s\": %s\n",
237                                 fullName, strerror(errno));
238
239                         continue;
240                 }
241
242                 /*
243                  * If this file matches the criteria that was
244                  * specified then print its name out.
245                  */
246                 if (testFile(fullName, &statBuf))
247                         printf("%s\n", fullName);
248
249                 /*
250                  * If this is a directory and we are allowed to cross
251                  * mount points or the directory is still on the same
252                  * device, then examine it's files too.
253                  */
254                 if (S_ISDIR(statBuf.st_mode) &&
255                         (!xdevFlag || (statBuf.st_dev == xdevDevice)))
256                 {
257                         examineDirectory(fullName);
258                 }
259         }
260
261         closedir(dir);
262 }
263
264
265 /*
266  * Test a file name having the specified status to see if it should
267  * be acted on.  Returns TRUE if the file name has been selected.
268  */
269 static BOOL
270 testFile(const char * fullName, const struct stat * statBuf)
271 {
272         const char *    cp;
273         const char *    entryName;
274         BOOL            wantType;
275         int             mode;
276
277         mode = statBuf->st_mode;
278
279         /*
280          * Check the file type if it was specified.
281          */
282         if (fileType != NULL)
283         {
284                 wantType = FALSE;
285
286                 for (cp = fileType; *cp; cp++)
287                 {
288                         switch (*cp)
289                         {
290                                 case 'f':
291                                         if (S_ISREG(mode))
292                                                 wantType = TRUE;
293                                         break;
294
295                                 case 'd':
296                                         if (S_ISDIR(mode))
297                                                 wantType = TRUE;
298
299                                         break;
300
301                                 case 'p':
302                                         if (S_ISFIFO(mode))
303                                                 wantType = TRUE;
304
305                                         break;
306
307                                 case 'c':
308                                         if (S_ISCHR(mode))
309                                                 wantType = TRUE;
310
311                                         break;
312
313                                 case 'b':
314                                         if (S_ISBLK(mode))
315                                                 wantType = TRUE;
316
317                                         break;
318
319                                 case 's':
320                                         if (S_ISSOCK(mode))
321                                                 wantType = TRUE;
322
323                                         break;
324
325 #ifdef  S_ISLNK
326                                 case 'l':
327                                         if (S_ISLNK(mode))
328                                                 wantType = TRUE;
329
330                                         break;
331 #endif
332                                 default:
333                                         break;
334                         }
335                 }
336
337                 if (!wantType)
338                         return FALSE;
339         }
340
341         /*
342          * Check the file size if it was specified.
343          * This check only lets regular files and directories through.
344          */
345         if (fileSize > 0)
346         {
347                 if (!S_ISREG(mode) && !S_ISDIR(mode))
348                         return FALSE;
349
350                 if (statBuf->st_size < fileSize)
351                         return FALSE;
352         }
353
354         /*
355          * Check the file name pattern if it was specified.
356          */
357         if (filePattern != NULL)
358         {
359                 entryName = strrchr(fullName, '/');
360
361                 if (entryName)
362                         entryName++;
363                 else
364                         entryName = fullName;
365
366                 if (!match(entryName, filePattern))
367                         return FALSE;
368         }
369
370         /*
371          * This file name is wanted.
372          */
373         return TRUE;
374 }
375
376 /* END CODE */