string.h is ANSI C include file
[unix-history] / usr / src / usr.bin / find / find.c
... / ...
CommitLineData
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * 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 * %sccs.include.redist.c%
9 */
10
11#ifndef lint
12char copyright[] =
13"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
14 All rights reserved.\n";
15#endif /* not lint */
16
17#ifndef lint
18static char sccsid[] = "@(#)find.c 4.26 (Berkeley) %G%";
19#endif /* not lint */
20
21#include <sys/types.h>
22#include <sys/stat.h>
23#include <fts.h>
24#include <stdio.h>
25#include <strings.h>
26#include <errno.h>
27#include "find.h"
28
29FTS *tree; /* pointer to top of FTS hierarchy */
30time_t now; /* time find was run */
31dev_t curdev = (dev_t)-1; /* device number of current tree */
32int ftsoptions; /* options passed to ftsopen() */
33int deprecated; /* old or new syntax */
34int depth; /* set by -depth option */
35int output_specified; /* one of -print, -ok or -exec was specified */
36int xdev; /* set by -xdev option */
37
38main(argc, argv)
39 int argc;
40 char **argv;
41{
42 PLAN *plan;
43 char **p, **paths;
44 PLAN *find_formplan();
45 time_t time();
46
47 (void)time(&now); /* initialize the time-of-day */
48
49 if (argc < 2)
50 usage();
51
52 paths = argv;
53 ftsoptions = FTS_MULTIPLE|FTS_NOSTAT|FTS_PHYSICAL;
54
55 /*
56 * if arguments start with an option, it's new syntax; otherwise,
57 * if has a "-option" anywhere it must be old syntax.
58 */
59 if (argv[1][0] != '-')
60 for (p = argv + 1; *p; ++p)
61 if (**p == '-') {
62 deprecated = 1;
63 oldsyntax(&argv);
64 break;
65 }
66 if (!deprecated)
67 newsyntax(argc, &argv);
68
69 plan = find_formplan(argv); /* execution plan */
70 find_execute(plan, paths);
71}
72
73/*
74 * find_formplan --
75 * process the command line and create a "plan" corresponding to the
76 * command arguments.
77 */
78PLAN *
79find_formplan(argv)
80 char **argv;
81{
82 PLAN *plan, *tail, *new;
83 PLAN *c_print(), *find_create(), *find_squish_not(), *find_squish_or();
84 PLAN *find_squish_paren();
85
86 /*
87 * for each argument in the command line, determine what kind of node
88 * it is, create the appropriate node type and add the new plan node
89 * to the end of the existing plan. The resulting plan is a linked
90 * list of plan nodes. For example, the string:
91 *
92 * % find . -name foo -newer bar -print
93 *
94 * results in the plan:
95 *
96 * [-name foo]--> [-newer bar]--> [-print]
97 *
98 * in this diagram, `[-name foo]' represents the plan node generated
99 * by c_name() with an argument of foo and `-->' represents the
100 * plan->next pointer.
101 */
102 for (plan = NULL; *argv;) {
103 new = find_create(&argv);
104 if (plan == NULL)
105 tail = plan = new;
106 else {
107 tail->next = new;
108 tail = new;
109 }
110 }
111
112 /*
113 * if the user didn't specify one of -print, -ok or -exec, then -print
114 * is assumed so we add a -print node on the end. It is possible that
115 * the user might want the -print someplace else on the command line,
116 * but there's no way to know that.
117 */
118 if (!output_specified) {
119 new = c_print();
120 if (plan == NULL)
121 tail = plan = new;
122 else {
123 tail->next = new;
124 tail = new;
125 }
126 }
127
128 /*
129 * the command line has been completely processed into a search plan
130 * except for the (, ), !, and -o operators. Rearrange the plan so
131 * that the portions of the plan which are affected by the operators
132 * are moved into operator nodes themselves. For example:
133 *
134 * [!]--> [-name foo]--> [-print]
135 *
136 * becomes
137 *
138 * [! [-name foo] ]--> [-print]
139 *
140 * and
141 *
142 * [(]--> [-depth]--> [-name foo]--> [)]--> [-print]
143 *
144 * becomes
145 *
146 * [expr [-depth]-->[-name foo] ]--> [-print]
147 *
148 * operators are handled in order of precedence.
149 */
150
151 plan = find_squish_paren(plan); /* ()'s */
152 plan = find_squish_not(plan); /* !'s */
153 plan = find_squish_or(plan); /* -o's */
154 return(plan);
155}
156
157/*
158 * find_execute --
159 * take a search plan and an array of search paths and executes the plan
160 * over all FTSENT's returned for the given search paths.
161 */
162find_execute(plan, paths)
163 PLAN *plan; /* search plan */
164 char **paths; /* array of pathnames to traverse */
165{
166 FTSENT *entry; /* current fts entry */
167 PLAN *p;
168
169 if (!(tree = ftsopen(paths, ftsoptions, NULL))) {
170 (void)fprintf(stderr, "find: ftsopen: %s.\n", strerror(errno));
171 exit(1);
172 }
173 while (entry = ftsread(tree)) {
174 switch(entry->info) {
175 case FTS_DNR:
176 (void)fprintf(stderr,
177 "find: %s: unable to read.\n", entry->path);
178 continue;
179 case FTS_DNX:
180 (void)fprintf(stderr,
181 "find: %s: unable to search.\n", entry->path);
182 continue;
183 case FTS_ERR:
184 (void)fprintf(stderr,
185 "find: %s: %s.\n", entry->path, strerror(errno));
186 continue;
187 case FTS_D:
188 if (depth)
189 continue;
190 break;
191 case FTS_DC:
192 (void)fprintf(stderr,
193 "find: directory cycle: %s.\n", entry->path);
194 continue;
195 case FTS_DP:
196 if (!depth)
197 continue;
198 case FTS_NS:
199 if (!(ftsoptions & FTS_NOSTAT)) {
200 (void)fprintf(stderr,
201 "find: can't stat: %s.\n", entry->path);
202 continue;
203 }
204 break;
205 }
206
207 /* always keep curdev up to date, -fstype uses it. */
208 if (xdev && curdev != entry->statb.st_dev &&
209 curdev != -1 && ftsset(tree, entry, FTS_SKIP)) {
210 (void)fprintf(stderr, "find: %s: %s.\n",
211 entry->path, strerror(errno));
212 exit(1);
213 }
214
215 /*
216 * call all the functions in the execution plan until one is
217 * false or all have been executed. This is where we do all
218 * the work specified by the user on the command line.
219 */
220 for (p = plan; p && (p->eval)(p, entry); p = p->next);
221
222 curdev = entry->statb.st_dev;
223 }
224 (void)ftsclose(tree);
225}