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