ANSI
[unix-history] / usr / src / usr.bin / find / find.c
CommitLineData
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
12char 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 18static 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
30FTS *tree; /* pointer to top of FTS hierarchy */
31time_t now; /* time find was run */
939beb9f
KB
32 /* options for the ftsopen(3) call */
33int ftsoptions = FTS_NOSTAT|FTS_PHYSICAL;
34int isdeprecated; /* using deprecated syntax */
35int isdepth; /* do directories on post-order visit */
36int isoutput; /* user specified output operator */
37int isrelative; /* can do -exec/ok on relative path */
38int isstopdnx; /* don't read unsearchable directories */
46b257b1 39
46b257b1
KM
40main(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 */
86PLAN *
87find_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 */
171find_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
220srcherr: (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}