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