xref: /original-bsd/usr.bin/find/find.c (revision 331bfa8d)
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
12 char 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
18 static char sccsid[] = "@(#)find.c	4.35 (Berkeley) 11/15/90";
19 #endif /* not lint */
20 
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/errno.h>
24 #include <fts.h>
25 #include <stdio.h>
26 #include "find.h"
27 #include <string.h>
28 #include <stdlib.h>
29 
30 FTS *tree;			/* pointer to top of FTS hierarchy */
31 time_t now;			/* time find was run */
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 */
39 
40 main(argc, argv)
41 	int argc;
42 	char **argv;
43 {
44 	PLAN *plan;
45 	char **p, **paths;
46 	PLAN *find_formplan();
47 	time_t time();
48 	void newsyntax(), oldsyntax();
49 
50 	(void)time(&now);			/* initialize the time-of-day */
51 
52 	if (argc < 2)
53 		usage();
54 
55 	paths = argv;
56 
57 	/*
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.
61 	 */
62 	if (argv[1][0] != '-')
63 		for (p = argv + 1; *p; ++p) {
64 			if (!strcmp(*p, "exec") || !strcmp(*p, "ok")) {
65 				while (p[1] && strcmp(*++p, ";"));
66 				continue;
67 			}
68 			if (**p == '-') {
69 				isdeprecated = 1;
70 				oldsyntax(&argv);
71 				break;
72 			}
73 		}
74 	if (!isdeprecated)
75 		newsyntax(argc, &argv);
76 
77 	plan = find_formplan(argv);		/* execution plan */
78 	find_execute(plan, paths);
79 }
80 
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;
91 	PLAN *c_print(), *find_create(), *not_squish(), *or_squish();
92 	PLAN *paren_squish();
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;) {
111 		if (!(new = find_create(&argv)))
112 			continue;
113 		if (plan == NULL)
114 			tail = plan = new;
115 		else {
116 			tail->next = new;
117 			tail = new;
118 		}
119 	}
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 	 */
127 	if (!isoutput) {
128 		new = c_print();
129 		if (plan == NULL)
130 			tail = plan = new;
131 		else {
132 			tail->next = new;
133 			tail = new;
134 		}
135 	}
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 
160 	plan = paren_squish(plan);		/* ()'s */
161 	plan = not_squish(plan);		/* !'s */
162 	plan = or_squish(plan);			/* -o's */
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 {
175 	register FTSENT *entry;
176 	PLAN *p;
177 
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))) {
186 		(void)fprintf(stderr, "find: ftsopen: %s.\n", strerror(errno));
187 		exit(1);
188 	}
189 
190 	while (entry = fts_read(tree)) {
191 		switch(entry->fts_info) {
192 		case FTS_DNR:
193 			(void)fprintf(stderr,
194 			    "find: %s: unable to read.\n", entry->fts_path);
195 			continue;
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,
221 			    "find: %s: unable to search.\n", entry->fts_path);
222 			continue;
223 		}
224 		case FTS_ERR:
225 			(void)fprintf(stderr,
226 			    "find: %s: %s.\n", entry->fts_path,
227 			    strerror(errno));
228 			continue;
229 		case FTS_D:
230 			if (isdepth)
231 				continue;
232 			break;
233 		case FTS_DC:
234 			(void)fprintf(stderr,
235 			    "find: directory cycle: %s.\n", entry->fts_path);
236 			continue;
237 		case FTS_DP:
238 			if (!isdepth)
239 				continue;
240 			break;
241 		case FTS_NS:
242 			if (!(ftsoptions & FTS_NOSTAT)) {
243 				(void)fprintf(stderr,
244 				    "find: can't stat: %s.\n", entry->fts_path);
245 				continue;
246 			}
247 			break;
248 		}
249 
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);
256 	}
257 	(void)fts_close(tree);
258 }
259