xref: /original-bsd/usr.bin/find/find.c (revision d5354517)
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.26 (Berkeley) 05/12/90";
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 
29 FTS *tree;			/* pointer to top of FTS hierarchy */
30 time_t now;			/* time find was run */
31 dev_t curdev = (dev_t)-1;	/* device number of current tree */
32 int ftsoptions;			/* options passed to ftsopen() */
33 int deprecated;			/* old or new syntax */
34 int depth;			/* set by -depth option */
35 int output_specified;		/* one of -print, -ok or -exec was specified */
36 int xdev;			/* set by -xdev option */
37 
38 main(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  */
78 PLAN *
79 find_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  */
162 find_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 }
226