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