xref: /original-bsd/usr.bin/find/find.c (revision 95a66346)
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.36 (Berkeley) 03/10/91";
19 #endif /* not lint */
20 
21 #include <sys/param.h>
22 #include <sys/stat.h>
23 #include <sys/errno.h>
24 #include <time.h>
25 #include <fts.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include "find.h"
30 
31 FTS *tree;			/* pointer to top of FTS hierarchy */
32 time_t now;			/* time find was run */
33 				/* options for the ftsopen(3) call */
34 int ftsoptions = FTS_NOSTAT|FTS_PHYSICAL;
35 int isdeprecated;		/* using deprecated syntax */
36 int isdepth;			/* do directories on post-order visit */
37 int isoutput;			/* user specified output operator */
38 int isrelative;			/* can do -exec/ok on relative path */
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 	if (!(tree = fts_open(paths, ftsoptions, (int (*)())NULL))) {
179 		error("ftsopen", errno);
180 		exit(1);
181 	}
182 
183 	while (entry = fts_read(tree)) {
184 		switch(entry->fts_info) {
185 		case FTS_D:
186 			if (isdepth)
187 				continue;
188 			break;
189 		case FTS_DP:
190 			if (!isdepth)
191 				continue;
192 			break;
193 		case FTS_DNR:
194 		case FTS_ERR:
195 		case FTS_NS:
196 			error(entry->fts_path, errno);
197 			continue;
198 		}
199 
200 		/*
201 		 * call all the functions in the execution plan until one is
202 		 * false or all have been executed.  This is where we do all
203 		 * the work specified by the user on the command line.
204 		 */
205 		for (p = plan; p && (p->eval)(p, entry); p = p->next);
206 	}
207 	(void)fts_close(tree);
208 }
209