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.
6 * The "find" built-in command.
24 #define MAX_NAME_SIZE (1024 * 10)
28 * Items that can be specified to restrict the output.
31 static dev_t xdevDevice;
33 static const char * filePattern;
34 static const char * fileType;
38 * Recursive routine to examine the files in a directory.
40 static void examineDirectory(const char * path);
41 static BOOL testFile(const char * fullName, const struct stat * statBuf);
46 * Find files from the specified directory path.
47 * This is limited to just printing their file names.
50 do_find(int argc, const char ** argv)
64 if ((argc <= 0) || (**argv == '-'))
66 fprintf(stderr, "No path specified\n");
79 if (strcmp(cp, "-xdev") == 0)
81 else if (strcmp(cp, "-type") == 0)
83 if ((argc <= 0) || (**argv == '-'))
85 fprintf(stderr, "Missing type string\n");
93 else if (strcmp(cp, "-name") == 0)
95 if ((argc <= 0) || (**argv == '-'))
97 fprintf(stderr, "Missing file name\n");
103 filePattern = *argv++;
105 else if (strcmp(cp, "-size") == 0)
107 if ((argc <= 0) || (**argv == '-'))
109 fprintf(stderr, "Missing file size\n");
119 while (isDecimal(*cp))
120 fileSize = fileSize * 10 + (*cp++ - '0');
122 if (*cp || (fileSize < 0))
124 fprintf(stderr, "Bad file size specified\n");
132 fprintf(stderr, "Missing dash in option\n");
134 fprintf(stderr, "Unknown option\n");
141 * Get information about the path and make sure that it
144 if (stat(path, &statBuf) < 0)
146 fprintf(stderr, "Cannot stat \"%s\": %s\n", path,
152 if (!S_ISDIR(statBuf.st_mode))
154 fprintf(stderr, "Path \"%s\" is not a directory\n", path);
160 * Remember the device that this directory is on in case we need it.
162 xdevDevice = statBuf.st_dev;
165 * If the directory meets the specified criteria, then print it out.
167 if (testFile(path, &statBuf))
168 printf("%s\n", path);
171 * Now examine the files in the directory.
173 examineDirectory(path);
180 * Recursive routine to examine the files in a directory.
183 examineDirectory(const char * path)
187 struct dirent * entry;
189 char fullName[MAX_NAME_SIZE];
192 * Open the directory.
198 fprintf(stderr, "Cannot read directory \"%s\": %s\n",
199 path, strerror(errno));
205 * See if a slash is needed.
207 needSlash = (*path && (path[strlen(path) - 1] != '/'));
210 * Read all of the directory entries and check them,
211 * except for the current and parent directory entries.
213 while (!intFlag && ((entry = readdir(dir)) != NULL))
215 if ((strcmp(entry->d_name, ".") == 0) ||
216 (strcmp(entry->d_name, "..") == 0))
222 * Build the full path name.
224 strcpy(fullName, path);
227 strcat(fullName, "/");
229 strcat(fullName, entry->d_name);
232 * Find out about this file.
234 if (LSTAT(fullName, &statBuf) < 0)
236 fprintf(stderr, "Cannot stat \"%s\": %s\n",
237 fullName, strerror(errno));
243 * If this file matches the criteria that was
244 * specified then print its name out.
246 if (testFile(fullName, &statBuf))
247 printf("%s\n", fullName);
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.
254 if (S_ISDIR(statBuf.st_mode) &&
255 (!xdevFlag || (statBuf.st_dev == xdevDevice)))
257 examineDirectory(fullName);
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.
270 testFile(const char * fullName, const struct stat * statBuf)
273 const char * entryName;
277 mode = statBuf->st_mode;
280 * Check the file type if it was specified.
282 if (fileType != NULL)
286 for (cp = fileType; *cp; cp++)
342 * Check the file size if it was specified.
343 * This check only lets regular files and directories through.
347 if (!S_ISREG(mode) && !S_ISDIR(mode))
350 if (statBuf->st_size < fileSize)
355 * Check the file name pattern if it was specified.
357 if (filePattern != NULL)
359 entryName = strrchr(fullName, '/');
364 entryName = fullName;
366 if (!match(entryName, filePattern))
371 * This file name is wanted.