Commit | Line | Data |
---|---|---|
45fc66f9 KB |
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 | */ | |
c08d5d47 | 10 | |
45fc66f9 KB |
11 | #ifndef lint |
12 | char copyright[] = | |
13 | "@(#) Copyright (c) 1990 The Regents of the University of California.\n\ | |
14 | All rights reserved.\n"; | |
15 | #endif /* not lint */ | |
b98da692 | 16 | |
45fc66f9 | 17 | #ifndef lint |
e0482c58 | 18 | static char sccsid[] = "@(#)find.c 4.35 (Berkeley) %G%"; |
45fc66f9 | 19 | #endif /* not lint */ |
c08d5d47 | 20 | |
939beb9f | 21 | #include <sys/param.h> |
45fc66f9 | 22 | #include <sys/stat.h> |
939beb9f | 23 | #include <sys/errno.h> |
45fc66f9 KB |
24 | #include <fts.h> |
25 | #include <stdio.h> | |
45fc66f9 | 26 | #include "find.h" |
939beb9f KB |
27 | #include <string.h> |
28 | #include <stdlib.h> | |
45fc66f9 KB |
29 | |
30 | FTS *tree; /* pointer to top of FTS hierarchy */ | |
31 | time_t now; /* time find was run */ | |
939beb9f KB |
32 | /* options for the ftsopen(3) call */ |
33 | int ftsoptions = FTS_NOSTAT|FTS_PHYSICAL; | |
34 | int isdeprecated; /* using deprecated syntax */ | |
35 | int isdepth; /* do directories on post-order visit */ | |
36 | int isoutput; /* user specified output operator */ | |
37 | int isrelative; /* can do -exec/ok on relative path */ | |
38 | int isstopdnx; /* don't read unsearchable directories */ | |
46b257b1 | 39 | |
46b257b1 KM |
40 | main(argc, argv) |
41 | int argc; | |
45fc66f9 | 42 | char **argv; |
c08d5d47 | 43 | { |
45fc66f9 | 44 | PLAN *plan; |
202dd4ce | 45 | char **p, **paths; |
45fc66f9 KB |
46 | PLAN *find_formplan(); |
47 | time_t time(); | |
939beb9f | 48 | void newsyntax(), oldsyntax(); |
45fc66f9 | 49 | |
202dd4ce | 50 | (void)time(&now); /* initialize the time-of-day */ |
c08d5d47 | 51 | |
45fc66f9 KB |
52 | if (argc < 2) |
53 | usage(); | |
202dd4ce KB |
54 | |
55 | paths = argv; | |
c08d5d47 | 56 | |
202dd4ce | 57 | /* |
a476d0ac KB |
58 | * if arguments start with an option, treat it like new syntax; |
59 | * otherwise, if has a "-option" anywhere (which isn't an argument | |
60 | * to another command) treat it as old syntax. | |
202dd4ce KB |
61 | */ |
62 | if (argv[1][0] != '-') | |
a476d0ac KB |
63 | for (p = argv + 1; *p; ++p) { |
64 | if (!strcmp(*p, "exec") || !strcmp(*p, "ok")) { | |
65 | while (p[1] && strcmp(*++p, ";")); | |
66 | continue; | |
67 | } | |
202dd4ce | 68 | if (**p == '-') { |
939beb9f | 69 | isdeprecated = 1; |
202dd4ce KB |
70 | oldsyntax(&argv); |
71 | break; | |
72 | } | |
a476d0ac | 73 | } |
939beb9f | 74 | if (!isdeprecated) |
202dd4ce KB |
75 | newsyntax(argc, &argv); |
76 | ||
45fc66f9 KB |
77 | plan = find_formplan(argv); /* execution plan */ |
78 | find_execute(plan, paths); | |
c08d5d47 | 79 | } |
c08d5d47 | 80 | |
45fc66f9 KB |
81 | /* |
82 | * find_formplan -- | |
83 | * process the command line and create a "plan" corresponding to the | |
84 | * command arguments. | |
85 | */ | |
86 | PLAN * | |
87 | find_formplan(argv) | |
88 | char **argv; | |
89 | { | |
90 | PLAN *plan, *tail, *new; | |
e0482c58 KB |
91 | PLAN *c_print(), *find_create(), *not_squish(), *or_squish(); |
92 | PLAN *paren_squish(); | |
45fc66f9 KB |
93 | |
94 | /* | |
95 | * for each argument in the command line, determine what kind of node | |
96 | * it is, create the appropriate node type and add the new plan node | |
97 | * to the end of the existing plan. The resulting plan is a linked | |
98 | * list of plan nodes. For example, the string: | |
99 | * | |
100 | * % find . -name foo -newer bar -print | |
101 | * | |
102 | * results in the plan: | |
103 | * | |
104 | * [-name foo]--> [-newer bar]--> [-print] | |
105 | * | |
106 | * in this diagram, `[-name foo]' represents the plan node generated | |
107 | * by c_name() with an argument of foo and `-->' represents the | |
108 | * plan->next pointer. | |
109 | */ | |
110 | for (plan = NULL; *argv;) { | |
b1a11722 KB |
111 | if (!(new = find_create(&argv))) |
112 | continue; | |
45fc66f9 KB |
113 | if (plan == NULL) |
114 | tail = plan = new; | |
115 | else { | |
116 | tail->next = new; | |
117 | tail = new; | |
c08d5d47 | 118 | } |
c08d5d47 | 119 | } |
45fc66f9 KB |
120 | |
121 | /* | |
122 | * if the user didn't specify one of -print, -ok or -exec, then -print | |
123 | * is assumed so we add a -print node on the end. It is possible that | |
124 | * the user might want the -print someplace else on the command line, | |
125 | * but there's no way to know that. | |
126 | */ | |
939beb9f | 127 | if (!isoutput) { |
45fc66f9 KB |
128 | new = c_print(); |
129 | if (plan == NULL) | |
130 | tail = plan = new; | |
131 | else { | |
132 | tail->next = new; | |
133 | tail = new; | |
c08d5d47 | 134 | } |
c08d5d47 | 135 | } |
45fc66f9 KB |
136 | |
137 | /* | |
138 | * the command line has been completely processed into a search plan | |
139 | * except for the (, ), !, and -o operators. Rearrange the plan so | |
140 | * that the portions of the plan which are affected by the operators | |
141 | * are moved into operator nodes themselves. For example: | |
142 | * | |
143 | * [!]--> [-name foo]--> [-print] | |
144 | * | |
145 | * becomes | |
146 | * | |
147 | * [! [-name foo] ]--> [-print] | |
148 | * | |
149 | * and | |
150 | * | |
151 | * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] | |
152 | * | |
153 | * becomes | |
154 | * | |
155 | * [expr [-depth]-->[-name foo] ]--> [-print] | |
156 | * | |
157 | * operators are handled in order of precedence. | |
158 | */ | |
159 | ||
e0482c58 KB |
160 | plan = paren_squish(plan); /* ()'s */ |
161 | plan = not_squish(plan); /* !'s */ | |
162 | plan = or_squish(plan); /* -o's */ | |
45fc66f9 KB |
163 | return(plan); |
164 | } | |
165 | ||
166 | /* | |
167 | * find_execute -- | |
168 | * take a search plan and an array of search paths and executes the plan | |
169 | * over all FTSENT's returned for the given search paths. | |
170 | */ | |
171 | find_execute(plan, paths) | |
172 | PLAN *plan; /* search plan */ | |
173 | char **paths; /* array of pathnames to traverse */ | |
174 | { | |
939beb9f | 175 | register FTSENT *entry; |
45fc66f9 KB |
176 | PLAN *p; |
177 | ||
939beb9f KB |
178 | /* |
179 | * If need stat info, might as well quit when the directory isn't | |
180 | * searchable. | |
181 | */ | |
182 | if (!(ftsoptions & FTS_NOSTAT)) | |
183 | isstopdnx = 1; | |
184 | ||
185 | if (!(tree = fts_open(paths, ftsoptions, (int (*)())NULL))) { | |
45fc66f9 | 186 | (void)fprintf(stderr, "find: ftsopen: %s.\n", strerror(errno)); |
b98da692 | 187 | exit(1); |
365a571b | 188 | } |
939beb9f KB |
189 | |
190 | while (entry = fts_read(tree)) { | |
c26d864e | 191 | switch(entry->fts_info) { |
45fc66f9 KB |
192 | case FTS_DNR: |
193 | (void)fprintf(stderr, | |
c26d864e | 194 | "find: %s: unable to read.\n", entry->fts_path); |
365a571b | 195 | continue; |
939beb9f KB |
196 | case FTS_DNX: { |
197 | /* | |
198 | * If can't search the directory, but able to read it, | |
199 | * and don't need stat information or to exec/ok the | |
200 | * file, use the fts_children list. | |
201 | */ | |
202 | register char *t; | |
203 | ||
204 | if (isstopdnx) | |
205 | goto srcherr; | |
206 | errno = 0; | |
207 | entry = fts_children(tree); | |
208 | if (errno) | |
209 | goto srcherr; | |
210 | for (t = entry->fts_path; *t; ++t); | |
211 | *t = '/'; | |
212 | for (; entry; entry = entry->fts_link) { | |
213 | (void)bcopy(entry->fts_name, t + 1, | |
214 | entry->fts_namelen + 1); | |
215 | for (p = plan; p && (p->eval)(p, entry); | |
216 | p = p->next); | |
217 | } | |
218 | continue; | |
219 | ||
220 | srcherr: (void)fprintf(stderr, | |
c26d864e | 221 | "find: %s: unable to search.\n", entry->fts_path); |
45fc66f9 | 222 | continue; |
939beb9f | 223 | } |
45fc66f9 KB |
224 | case FTS_ERR: |
225 | (void)fprintf(stderr, | |
c26d864e KB |
226 | "find: %s: %s.\n", entry->fts_path, |
227 | strerror(errno)); | |
45fc66f9 KB |
228 | continue; |
229 | case FTS_D: | |
939beb9f | 230 | if (isdepth) |
b98da692 | 231 | continue; |
45fc66f9 KB |
232 | break; |
233 | case FTS_DC: | |
234 | (void)fprintf(stderr, | |
c26d864e | 235 | "find: directory cycle: %s.\n", entry->fts_path); |
b98da692 | 236 | continue; |
45fc66f9 | 237 | case FTS_DP: |
939beb9f | 238 | if (!isdepth) |
45fc66f9 | 239 | continue; |
9dd52de3 | 240 | break; |
45fc66f9 KB |
241 | case FTS_NS: |
242 | if (!(ftsoptions & FTS_NOSTAT)) { | |
243 | (void)fprintf(stderr, | |
c26d864e | 244 | "find: can't stat: %s.\n", entry->fts_path); |
45fc66f9 KB |
245 | continue; |
246 | } | |
247 | break; | |
b98da692 | 248 | } |
b98da692 | 249 | |
45fc66f9 KB |
250 | /* |
251 | * call all the functions in the execution plan until one is | |
252 | * false or all have been executed. This is where we do all | |
253 | * the work specified by the user on the command line. | |
254 | */ | |
255 | for (p = plan; p && (p->eval)(p, entry); p = p->next); | |
45fc66f9 | 256 | } |
939beb9f | 257 | (void)fts_close(tree); |
b98da692 | 258 | } |