/usr.bin/find/find.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 238 lines · 121 code · 21 blank · 96 comment · 34 complexity · 7c341c4da3995de462c132a656745702 MD5 · raw file

  1. /*-
  2. * Copyright (c) 1991, 1993, 1994
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * This code is derived from software contributed to Berkeley by
  6. * Cimarron D. Taylor of the University of California, Berkeley.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 4. Neither the name of the University nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. */
  32. #ifndef lint
  33. #if 0
  34. static char sccsid[] = "@(#)find.c 8.5 (Berkeley) 8/5/94";
  35. #else
  36. #endif
  37. #endif /* not lint */
  38. #include <sys/cdefs.h>
  39. __FBSDID("$FreeBSD$");
  40. #include <sys/types.h>
  41. #include <sys/stat.h>
  42. #include <err.h>
  43. #include <errno.h>
  44. #include <fts.h>
  45. #include <regex.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include "find.h"
  50. static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2);
  51. /*
  52. * find_compare --
  53. * tell fts_open() how to order the traversal of the hierarchy.
  54. * This variant gives lexicographical order, i.e., alphabetical
  55. * order within each directory.
  56. */
  57. static int
  58. find_compare(const FTSENT * const *s1, const FTSENT * const *s2)
  59. {
  60. return (strcoll((*s1)->fts_name, (*s2)->fts_name));
  61. }
  62. /*
  63. * find_formplan --
  64. * process the command line and create a "plan" corresponding to the
  65. * command arguments.
  66. */
  67. PLAN *
  68. find_formplan(char *argv[])
  69. {
  70. PLAN *plan, *tail, *new;
  71. /*
  72. * for each argument in the command line, determine what kind of node
  73. * it is, create the appropriate node type and add the new plan node
  74. * to the end of the existing plan. The resulting plan is a linked
  75. * list of plan nodes. For example, the string:
  76. *
  77. * % find . -name foo -newer bar -print
  78. *
  79. * results in the plan:
  80. *
  81. * [-name foo]--> [-newer bar]--> [-print]
  82. *
  83. * in this diagram, `[-name foo]' represents the plan node generated
  84. * by c_name() with an argument of foo and `-->' represents the
  85. * plan->next pointer.
  86. */
  87. for (plan = tail = NULL; *argv;) {
  88. if (!(new = find_create(&argv)))
  89. continue;
  90. if (plan == NULL)
  91. tail = plan = new;
  92. else {
  93. tail->next = new;
  94. tail = new;
  95. }
  96. }
  97. /*
  98. * if the user didn't specify one of -print, -ok or -exec, then -print
  99. * is assumed so we bracket the current expression with parens, if
  100. * necessary, and add a -print node on the end.
  101. */
  102. if (!isoutput) {
  103. OPTION *p;
  104. char **argv1 = 0;
  105. if (plan == NULL) {
  106. p = lookup_option("-print");
  107. new = (p->create)(p, &argv1);
  108. tail = plan = new;
  109. } else {
  110. p = lookup_option("(");
  111. new = (p->create)(p, &argv1);
  112. new->next = plan;
  113. plan = new;
  114. p = lookup_option(")");
  115. new = (p->create)(p, &argv1);
  116. tail->next = new;
  117. tail = new;
  118. p = lookup_option("-print");
  119. new = (p->create)(p, &argv1);
  120. tail->next = new;
  121. tail = new;
  122. }
  123. }
  124. /*
  125. * the command line has been completely processed into a search plan
  126. * except for the (, ), !, and -o operators. Rearrange the plan so
  127. * that the portions of the plan which are affected by the operators
  128. * are moved into operator nodes themselves. For example:
  129. *
  130. * [!]--> [-name foo]--> [-print]
  131. *
  132. * becomes
  133. *
  134. * [! [-name foo] ]--> [-print]
  135. *
  136. * and
  137. *
  138. * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
  139. *
  140. * becomes
  141. *
  142. * [expr [-depth]-->[-name foo] ]--> [-print]
  143. *
  144. * operators are handled in order of precedence.
  145. */
  146. plan = paren_squish(plan); /* ()'s */
  147. plan = not_squish(plan); /* !'s */
  148. plan = or_squish(plan); /* -o's */
  149. return (plan);
  150. }
  151. FTS *tree; /* pointer to top of FTS hierarchy */
  152. /*
  153. * find_execute --
  154. * take a search plan and an array of search paths and executes the plan
  155. * over all FTSENT's returned for the given search paths.
  156. */
  157. int
  158. find_execute(PLAN *plan, char *paths[])
  159. {
  160. FTSENT *entry;
  161. PLAN *p;
  162. int rval;
  163. tree = fts_open(paths, ftsoptions, (issort ? find_compare : NULL));
  164. if (tree == NULL)
  165. err(1, "ftsopen");
  166. for (rval = 0; (entry = fts_read(tree)) != NULL;) {
  167. if (maxdepth != -1 && entry->fts_level >= maxdepth) {
  168. if (fts_set(tree, entry, FTS_SKIP))
  169. err(1, "%s", entry->fts_path);
  170. }
  171. switch (entry->fts_info) {
  172. case FTS_D:
  173. if (isdepth)
  174. continue;
  175. break;
  176. case FTS_DP:
  177. if (!isdepth)
  178. continue;
  179. break;
  180. case FTS_DNR:
  181. case FTS_NS:
  182. if (ignore_readdir_race &&
  183. entry->fts_errno == ENOENT && entry->fts_level > 0)
  184. continue;
  185. /* FALLTHROUGH */
  186. case FTS_ERR:
  187. (void)fflush(stdout);
  188. warnx("%s: %s",
  189. entry->fts_path, strerror(entry->fts_errno));
  190. rval = 1;
  191. continue;
  192. #ifdef FTS_W
  193. case FTS_W:
  194. continue;
  195. #endif /* FTS_W */
  196. }
  197. #define BADCH " \t\n\\'\""
  198. if (isxargs && strpbrk(entry->fts_path, BADCH)) {
  199. (void)fflush(stdout);
  200. warnx("%s: illegal path", entry->fts_path);
  201. rval = 1;
  202. continue;
  203. }
  204. if (mindepth != -1 && entry->fts_level < mindepth)
  205. continue;
  206. /*
  207. * Call all the functions in the execution plan until one is
  208. * false or all have been executed. This is where we do all
  209. * the work specified by the user on the command line.
  210. */
  211. for (p = plan; p && (p->execute)(p, entry); p = p->next);
  212. }
  213. finish_execplus();
  214. if (errno && (!ignore_readdir_race || errno != ENOENT))
  215. err(1, "fts_read");
  216. return (rval);
  217. }