xref: /minix3/usr.bin/find/find.c (revision 319e7a6d071ca2b4d307b084f9cb29f0d56e2e04)
1*319e7a6dSDavid van Moolenbroek /*	$NetBSD: find.c,v 1.29 2012/03/20 20:34:57 matt Exp $	*/
2*319e7a6dSDavid van Moolenbroek 
3*319e7a6dSDavid van Moolenbroek /*-
4*319e7a6dSDavid van Moolenbroek  * Copyright (c) 1991, 1993, 1994
5*319e7a6dSDavid van Moolenbroek  *	The Regents of the University of California.  All rights reserved.
6*319e7a6dSDavid van Moolenbroek  *
7*319e7a6dSDavid van Moolenbroek  * This code is derived from software contributed to Berkeley by
8*319e7a6dSDavid van Moolenbroek  * Cimarron D. Taylor of the University of California, Berkeley.
9*319e7a6dSDavid van Moolenbroek  *
10*319e7a6dSDavid van Moolenbroek  * Redistribution and use in source and binary forms, with or without
11*319e7a6dSDavid van Moolenbroek  * modification, are permitted provided that the following conditions
12*319e7a6dSDavid van Moolenbroek  * are met:
13*319e7a6dSDavid van Moolenbroek  * 1. Redistributions of source code must retain the above copyright
14*319e7a6dSDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer.
15*319e7a6dSDavid van Moolenbroek  * 2. Redistributions in binary form must reproduce the above copyright
16*319e7a6dSDavid van Moolenbroek  *    notice, this list of conditions and the following disclaimer in the
17*319e7a6dSDavid van Moolenbroek  *    documentation and/or other materials provided with the distribution.
18*319e7a6dSDavid van Moolenbroek  * 3. Neither the name of the University nor the names of its contributors
19*319e7a6dSDavid van Moolenbroek  *    may be used to endorse or promote products derived from this software
20*319e7a6dSDavid van Moolenbroek  *    without specific prior written permission.
21*319e7a6dSDavid van Moolenbroek  *
22*319e7a6dSDavid van Moolenbroek  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23*319e7a6dSDavid van Moolenbroek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24*319e7a6dSDavid van Moolenbroek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25*319e7a6dSDavid van Moolenbroek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26*319e7a6dSDavid van Moolenbroek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27*319e7a6dSDavid van Moolenbroek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28*319e7a6dSDavid van Moolenbroek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29*319e7a6dSDavid van Moolenbroek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30*319e7a6dSDavid van Moolenbroek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31*319e7a6dSDavid van Moolenbroek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*319e7a6dSDavid van Moolenbroek  * SUCH DAMAGE.
33*319e7a6dSDavid van Moolenbroek  */
34*319e7a6dSDavid van Moolenbroek 
35*319e7a6dSDavid van Moolenbroek #include <sys/cdefs.h>
36*319e7a6dSDavid van Moolenbroek #ifndef lint
37*319e7a6dSDavid van Moolenbroek #if 0
38*319e7a6dSDavid van Moolenbroek static char sccsid[] = "from: @(#)find.c	8.5 (Berkeley) 8/5/94";
39*319e7a6dSDavid van Moolenbroek #else
40*319e7a6dSDavid van Moolenbroek __RCSID("$NetBSD: find.c,v 1.29 2012/03/20 20:34:57 matt Exp $");
41*319e7a6dSDavid van Moolenbroek #endif
42*319e7a6dSDavid van Moolenbroek #endif /* not lint */
43*319e7a6dSDavid van Moolenbroek 
44*319e7a6dSDavid van Moolenbroek #include <sys/types.h>
45*319e7a6dSDavid van Moolenbroek #include <sys/stat.h>
46*319e7a6dSDavid van Moolenbroek 
47*319e7a6dSDavid van Moolenbroek #include <err.h>
48*319e7a6dSDavid van Moolenbroek #include <errno.h>
49*319e7a6dSDavid van Moolenbroek #include <fts.h>
50*319e7a6dSDavid van Moolenbroek #include <signal.h>
51*319e7a6dSDavid van Moolenbroek #include <stdio.h>
52*319e7a6dSDavid van Moolenbroek #include <string.h>
53*319e7a6dSDavid van Moolenbroek #include <stdlib.h>
54*319e7a6dSDavid van Moolenbroek #include <stdbool.h>
55*319e7a6dSDavid van Moolenbroek #include <unistd.h>
56*319e7a6dSDavid van Moolenbroek 
57*319e7a6dSDavid van Moolenbroek #include "find.h"
58*319e7a6dSDavid van Moolenbroek 
59*319e7a6dSDavid van Moolenbroek static int ftscompare(const FTSENT **, const FTSENT **);
60*319e7a6dSDavid van Moolenbroek 
61*319e7a6dSDavid van Moolenbroek /*
62*319e7a6dSDavid van Moolenbroek  * find_formplan --
63*319e7a6dSDavid van Moolenbroek  *	process the command line and create a "plan" corresponding to the
64*319e7a6dSDavid van Moolenbroek  *	command arguments.
65*319e7a6dSDavid van Moolenbroek  */
66*319e7a6dSDavid van Moolenbroek PLAN *
find_formplan(char ** argv)67*319e7a6dSDavid van Moolenbroek find_formplan(char **argv)
68*319e7a6dSDavid van Moolenbroek {
69*319e7a6dSDavid van Moolenbroek 	PLAN *plan, *tail, *new;
70*319e7a6dSDavid van Moolenbroek 
71*319e7a6dSDavid van Moolenbroek 	/*
72*319e7a6dSDavid van Moolenbroek 	 * for each argument in the command line, determine what kind of node
73*319e7a6dSDavid van Moolenbroek 	 * it is, create the appropriate node type and add the new plan node
74*319e7a6dSDavid van Moolenbroek 	 * to the end of the existing plan.  The resulting plan is a linked
75*319e7a6dSDavid van Moolenbroek 	 * list of plan nodes.  For example, the string:
76*319e7a6dSDavid van Moolenbroek 	 *
77*319e7a6dSDavid van Moolenbroek 	 *	% find . -name foo -newer bar -print
78*319e7a6dSDavid van Moolenbroek 	 *
79*319e7a6dSDavid van Moolenbroek 	 * results in the plan:
80*319e7a6dSDavid van Moolenbroek 	 *
81*319e7a6dSDavid van Moolenbroek 	 *	[-name foo]--> [-newer bar]--> [-print]
82*319e7a6dSDavid van Moolenbroek 	 *
83*319e7a6dSDavid van Moolenbroek 	 * in this diagram, `[-name foo]' represents the plan node generated
84*319e7a6dSDavid van Moolenbroek 	 * by c_name() with an argument of foo and `-->' represents the
85*319e7a6dSDavid van Moolenbroek 	 * plan->next pointer.
86*319e7a6dSDavid van Moolenbroek 	 */
87*319e7a6dSDavid van Moolenbroek 	for (plan = tail = NULL; *argv;) {
88*319e7a6dSDavid van Moolenbroek 		if (!(new = find_create(&argv)))
89*319e7a6dSDavid van Moolenbroek 			continue;
90*319e7a6dSDavid van Moolenbroek 		if (plan == NULL)
91*319e7a6dSDavid van Moolenbroek 			tail = plan = new;
92*319e7a6dSDavid van Moolenbroek 		else {
93*319e7a6dSDavid van Moolenbroek 			tail->next = new;
94*319e7a6dSDavid van Moolenbroek 			tail = new;
95*319e7a6dSDavid van Moolenbroek 		}
96*319e7a6dSDavid van Moolenbroek 	}
97*319e7a6dSDavid van Moolenbroek 
98*319e7a6dSDavid van Moolenbroek 	/*
99*319e7a6dSDavid van Moolenbroek 	 * if the user didn't specify one of -print, -ok, -fprint, -exec, or
100*319e7a6dSDavid van Moolenbroek 	 * -exit, then -print is assumed so we bracket the current expression
101*319e7a6dSDavid van Moolenbroek 	 * with parens, if necessary, and add a -print node on the end.
102*319e7a6dSDavid van Moolenbroek 	 */
103*319e7a6dSDavid van Moolenbroek 	if (!isoutput) {
104*319e7a6dSDavid van Moolenbroek 		if (plan == NULL) {
105*319e7a6dSDavid van Moolenbroek 			new = c_print(NULL, 0);
106*319e7a6dSDavid van Moolenbroek 			tail = plan = new;
107*319e7a6dSDavid van Moolenbroek 		} else {
108*319e7a6dSDavid van Moolenbroek 			new = c_openparen(NULL, 0);
109*319e7a6dSDavid van Moolenbroek 			new->next = plan;
110*319e7a6dSDavid van Moolenbroek 			plan = new;
111*319e7a6dSDavid van Moolenbroek 			new = c_closeparen(NULL, 0);
112*319e7a6dSDavid van Moolenbroek 			tail->next = new;
113*319e7a6dSDavid van Moolenbroek 			tail = new;
114*319e7a6dSDavid van Moolenbroek 			new = c_print(NULL, 0);
115*319e7a6dSDavid van Moolenbroek 			tail->next = new;
116*319e7a6dSDavid van Moolenbroek 			tail = new;
117*319e7a6dSDavid van Moolenbroek 		}
118*319e7a6dSDavid van Moolenbroek 	}
119*319e7a6dSDavid van Moolenbroek 
120*319e7a6dSDavid van Moolenbroek 	/*
121*319e7a6dSDavid van Moolenbroek 	 * the command line has been completely processed into a search plan
122*319e7a6dSDavid van Moolenbroek 	 * except for the (, ), !, and -o operators.  Rearrange the plan so
123*319e7a6dSDavid van Moolenbroek 	 * that the portions of the plan which are affected by the operators
124*319e7a6dSDavid van Moolenbroek 	 * are moved into operator nodes themselves.  For example:
125*319e7a6dSDavid van Moolenbroek 	 *
126*319e7a6dSDavid van Moolenbroek 	 *	[!]--> [-name foo]--> [-print]
127*319e7a6dSDavid van Moolenbroek 	 *
128*319e7a6dSDavid van Moolenbroek 	 * becomes
129*319e7a6dSDavid van Moolenbroek 	 *
130*319e7a6dSDavid van Moolenbroek 	 *	[! [-name foo] ]--> [-print]
131*319e7a6dSDavid van Moolenbroek 	 *
132*319e7a6dSDavid van Moolenbroek 	 * and
133*319e7a6dSDavid van Moolenbroek 	 *
134*319e7a6dSDavid van Moolenbroek 	 *	[(]--> [-depth]--> [-name foo]--> [)]--> [-print]
135*319e7a6dSDavid van Moolenbroek 	 *
136*319e7a6dSDavid van Moolenbroek 	 * becomes
137*319e7a6dSDavid van Moolenbroek 	 *
138*319e7a6dSDavid van Moolenbroek 	 *	[expr [-depth]-->[-name foo] ]--> [-print]
139*319e7a6dSDavid van Moolenbroek 	 *
140*319e7a6dSDavid van Moolenbroek 	 * operators are handled in order of precedence.
141*319e7a6dSDavid van Moolenbroek 	 */
142*319e7a6dSDavid van Moolenbroek 
143*319e7a6dSDavid van Moolenbroek 	plan = paren_squish(plan);		/* ()'s */
144*319e7a6dSDavid van Moolenbroek 	plan = not_squish(plan);		/* !'s */
145*319e7a6dSDavid van Moolenbroek 	plan = or_squish(plan);			/* -o's */
146*319e7a6dSDavid van Moolenbroek 	return (plan);
147*319e7a6dSDavid van Moolenbroek }
148*319e7a6dSDavid van Moolenbroek 
149*319e7a6dSDavid van Moolenbroek static int
ftscompare(const FTSENT ** e1,const FTSENT ** e2)150*319e7a6dSDavid van Moolenbroek ftscompare(const FTSENT **e1, const FTSENT **e2)
151*319e7a6dSDavid van Moolenbroek {
152*319e7a6dSDavid van Moolenbroek 
153*319e7a6dSDavid van Moolenbroek 	return (strcoll((*e1)->fts_name, (*e2)->fts_name));
154*319e7a6dSDavid van Moolenbroek }
155*319e7a6dSDavid van Moolenbroek 
156*319e7a6dSDavid van Moolenbroek static sigset_t ss;
157*319e7a6dSDavid van Moolenbroek static bool notty;
158*319e7a6dSDavid van Moolenbroek 
159*319e7a6dSDavid van Moolenbroek static __inline void
sig_init(void)160*319e7a6dSDavid van Moolenbroek sig_init(void)
161*319e7a6dSDavid van Moolenbroek {
162*319e7a6dSDavid van Moolenbroek 	struct sigaction sa;
163*319e7a6dSDavid van Moolenbroek 	notty = !(isatty(STDIN_FILENO) || isatty(STDOUT_FILENO) ||
164*319e7a6dSDavid van Moolenbroek 	    isatty(STDERR_FILENO));
165*319e7a6dSDavid van Moolenbroek 	if (notty)
166*319e7a6dSDavid van Moolenbroek 		return;
167*319e7a6dSDavid van Moolenbroek 	sigemptyset(&ss);
168*319e7a6dSDavid van Moolenbroek 	sigaddset(&ss, SIGINFO); /* block SIGINFO */
169*319e7a6dSDavid van Moolenbroek 
170*319e7a6dSDavid van Moolenbroek 	memset(&sa, 0, sizeof(sa));
171*319e7a6dSDavid van Moolenbroek 	sa.sa_flags = SA_RESTART;
172*319e7a6dSDavid van Moolenbroek 	sa.sa_handler = show_path;
173*319e7a6dSDavid van Moolenbroek 	(void)sigaction(SIGINFO, &sa, NULL);
174*319e7a6dSDavid van Moolenbroek 
175*319e7a6dSDavid van Moolenbroek }
176*319e7a6dSDavid van Moolenbroek 
177*319e7a6dSDavid van Moolenbroek static __inline void
sig_lock(sigset_t * s)178*319e7a6dSDavid van Moolenbroek sig_lock(sigset_t *s)
179*319e7a6dSDavid van Moolenbroek {
180*319e7a6dSDavid van Moolenbroek 	if (notty)
181*319e7a6dSDavid van Moolenbroek 		return;
182*319e7a6dSDavid van Moolenbroek 	sigprocmask(SIG_BLOCK, &ss, s);
183*319e7a6dSDavid van Moolenbroek }
184*319e7a6dSDavid van Moolenbroek 
185*319e7a6dSDavid van Moolenbroek static __inline void
sig_unlock(const sigset_t * s)186*319e7a6dSDavid van Moolenbroek sig_unlock(const sigset_t *s)
187*319e7a6dSDavid van Moolenbroek {
188*319e7a6dSDavid van Moolenbroek 	if (notty)
189*319e7a6dSDavid van Moolenbroek 		return;
190*319e7a6dSDavid van Moolenbroek 	sigprocmask(SIG_SETMASK, s, NULL);
191*319e7a6dSDavid van Moolenbroek }
192*319e7a6dSDavid van Moolenbroek 
193*319e7a6dSDavid van Moolenbroek FTS *tree;			/* pointer to top of FTS hierarchy */
194*319e7a6dSDavid van Moolenbroek FTSENT *g_entry;		/* shared with SIGINFO handler */
195*319e7a6dSDavid van Moolenbroek 
196*319e7a6dSDavid van Moolenbroek /*
197*319e7a6dSDavid van Moolenbroek  * find_execute --
198*319e7a6dSDavid van Moolenbroek  *	take a search plan and an array of search paths and executes the plan
199*319e7a6dSDavid van Moolenbroek  *	over all FTSENT's returned for the given search paths.
200*319e7a6dSDavid van Moolenbroek  */
201*319e7a6dSDavid van Moolenbroek int
find_execute(PLAN * plan,char ** paths)202*319e7a6dSDavid van Moolenbroek find_execute(PLAN *plan, char **paths)
203*319e7a6dSDavid van Moolenbroek {
204*319e7a6dSDavid van Moolenbroek 	PLAN *p;
205*319e7a6dSDavid van Moolenbroek 	int r, rval, cval;
206*319e7a6dSDavid van Moolenbroek 	sigset_t s;
207*319e7a6dSDavid van Moolenbroek 
208*319e7a6dSDavid van Moolenbroek 	cval = 1;
209*319e7a6dSDavid van Moolenbroek 
210*319e7a6dSDavid van Moolenbroek 	if (!(tree = fts_open(paths, ftsoptions, issort ? ftscompare : NULL)))
211*319e7a6dSDavid van Moolenbroek 		err(1, "ftsopen");
212*319e7a6dSDavid van Moolenbroek 
213*319e7a6dSDavid van Moolenbroek 	sig_init();
214*319e7a6dSDavid van Moolenbroek 	sig_lock(&s);
215*319e7a6dSDavid van Moolenbroek 	for (rval = 0; cval && (g_entry = fts_read(tree)) != NULL;) {
216*319e7a6dSDavid van Moolenbroek 		switch (g_entry->fts_info) {
217*319e7a6dSDavid van Moolenbroek 		case FTS_D:
218*319e7a6dSDavid van Moolenbroek 			if (isdepth)
219*319e7a6dSDavid van Moolenbroek 				continue;
220*319e7a6dSDavid van Moolenbroek 			break;
221*319e7a6dSDavid van Moolenbroek 		case FTS_DP:
222*319e7a6dSDavid van Moolenbroek 			if (!isdepth)
223*319e7a6dSDavid van Moolenbroek 				continue;
224*319e7a6dSDavid van Moolenbroek 			break;
225*319e7a6dSDavid van Moolenbroek 		case FTS_DNR:
226*319e7a6dSDavid van Moolenbroek 		case FTS_ERR:
227*319e7a6dSDavid van Moolenbroek 		case FTS_NS:
228*319e7a6dSDavid van Moolenbroek 			sig_unlock(&s);
229*319e7a6dSDavid van Moolenbroek 			(void)fflush(stdout);
230*319e7a6dSDavid van Moolenbroek 			warnx("%s: %s",
231*319e7a6dSDavid van Moolenbroek 			    g_entry->fts_path, strerror(g_entry->fts_errno));
232*319e7a6dSDavid van Moolenbroek 			rval = 1;
233*319e7a6dSDavid van Moolenbroek 			sig_lock(&s);
234*319e7a6dSDavid van Moolenbroek 			continue;
235*319e7a6dSDavid van Moolenbroek 		}
236*319e7a6dSDavid van Moolenbroek #define	BADCH	" \t\n\\'\""
237*319e7a6dSDavid van Moolenbroek 		if (isxargs && strpbrk(g_entry->fts_path, BADCH)) {
238*319e7a6dSDavid van Moolenbroek 			sig_unlock(&s);
239*319e7a6dSDavid van Moolenbroek 			(void)fflush(stdout);
240*319e7a6dSDavid van Moolenbroek 			warnx("%s: illegal path", g_entry->fts_path);
241*319e7a6dSDavid van Moolenbroek 			rval = 1;
242*319e7a6dSDavid van Moolenbroek 			sig_lock(&s);
243*319e7a6dSDavid van Moolenbroek 			continue;
244*319e7a6dSDavid van Moolenbroek 		}
245*319e7a6dSDavid van Moolenbroek 
246*319e7a6dSDavid van Moolenbroek 		/*
247*319e7a6dSDavid van Moolenbroek 		 * Call all the functions in the execution plan until one is
248*319e7a6dSDavid van Moolenbroek 		 * false or all have been executed.  This is where we do all
249*319e7a6dSDavid van Moolenbroek 		 * the work specified by the user on the command line.
250*319e7a6dSDavid van Moolenbroek 		 */
251*319e7a6dSDavid van Moolenbroek 		sig_unlock(&s);
252*319e7a6dSDavid van Moolenbroek 		for (p = plan; p && (p->eval)(p, g_entry); p = p->next)
253*319e7a6dSDavid van Moolenbroek 			if (p->type == N_EXIT) {
254*319e7a6dSDavid van Moolenbroek 				rval = p->exit_val;
255*319e7a6dSDavid van Moolenbroek 				cval = 0;
256*319e7a6dSDavid van Moolenbroek 			}
257*319e7a6dSDavid van Moolenbroek 		sig_lock(&s);
258*319e7a6dSDavid van Moolenbroek 	}
259*319e7a6dSDavid van Moolenbroek 
260*319e7a6dSDavid van Moolenbroek 	sig_unlock(&s);
261*319e7a6dSDavid van Moolenbroek 	if (g_entry == NULL && errno)
262*319e7a6dSDavid van Moolenbroek 		err(1, "fts_read");
263*319e7a6dSDavid van Moolenbroek 	(void)fts_close(tree);
264*319e7a6dSDavid van Moolenbroek 
265*319e7a6dSDavid van Moolenbroek 	/*
266*319e7a6dSDavid van Moolenbroek 	 * Cleanup any plans with leftover state.
267*319e7a6dSDavid van Moolenbroek 	 * Keep the last non-zero return value.
268*319e7a6dSDavid van Moolenbroek 	 */
269*319e7a6dSDavid van Moolenbroek 	if ((r = find_traverse(plan, plan_cleanup, NULL)) != 0)
270*319e7a6dSDavid van Moolenbroek 		rval = r;
271*319e7a6dSDavid van Moolenbroek 
272*319e7a6dSDavid van Moolenbroek 	return (rval);
273*319e7a6dSDavid van Moolenbroek }
274*319e7a6dSDavid van Moolenbroek 
275*319e7a6dSDavid van Moolenbroek /*
276*319e7a6dSDavid van Moolenbroek  * find_traverse --
277*319e7a6dSDavid van Moolenbroek  *	traverse the plan tree and execute func() on all plans.  This
278*319e7a6dSDavid van Moolenbroek  *	does not evaluate each plan's eval() function; it is intended
279*319e7a6dSDavid van Moolenbroek  *	for operations that must run on all plans, such as state
280*319e7a6dSDavid van Moolenbroek  *	cleanup.
281*319e7a6dSDavid van Moolenbroek  *
282*319e7a6dSDavid van Moolenbroek  *	If any func() returns non-zero, then so will find_traverse().
283*319e7a6dSDavid van Moolenbroek  */
284*319e7a6dSDavid van Moolenbroek int
find_traverse(PLAN * plan,int (* func)(PLAN *,void *),void * arg)285*319e7a6dSDavid van Moolenbroek find_traverse(PLAN *plan, int (*func)(PLAN *, void *), void *arg)
286*319e7a6dSDavid van Moolenbroek {
287*319e7a6dSDavid van Moolenbroek 	PLAN *p;
288*319e7a6dSDavid van Moolenbroek 	int r, rval;
289*319e7a6dSDavid van Moolenbroek 
290*319e7a6dSDavid van Moolenbroek 	rval = 0;
291*319e7a6dSDavid van Moolenbroek 	for (p = plan; p; p = p->next) {
292*319e7a6dSDavid van Moolenbroek 		if ((r = func(p, arg)) != 0)
293*319e7a6dSDavid van Moolenbroek 			rval = r;
294*319e7a6dSDavid van Moolenbroek 		if (p->type == N_EXPR || p->type == N_OR) {
295*319e7a6dSDavid van Moolenbroek 			if (p->p_data[0])
296*319e7a6dSDavid van Moolenbroek 				if ((r = find_traverse(p->p_data[0],
297*319e7a6dSDavid van Moolenbroek 					    func, arg)) != 0)
298*319e7a6dSDavid van Moolenbroek 					rval = r;
299*319e7a6dSDavid van Moolenbroek 			if (p->p_data[1])
300*319e7a6dSDavid van Moolenbroek 				if ((r = find_traverse(p->p_data[1],
301*319e7a6dSDavid van Moolenbroek 					    func, arg)) != 0)
302*319e7a6dSDavid van Moolenbroek 					rval = r;
303*319e7a6dSDavid van Moolenbroek 		}
304*319e7a6dSDavid van Moolenbroek 	}
305*319e7a6dSDavid van Moolenbroek 	return rval;
306*319e7a6dSDavid van Moolenbroek }
307