1 1.81 andvar /* $NetBSD: function.c,v 1.81 2024/02/05 21:46:07 andvar Exp $ */ 2 1.18 tls 3 1.1 cgd /*- 4 1.10 jtc * Copyright (c) 1990, 1993 5 1.10 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.46 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.21 lukem #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.21 lukem #if 0 38 1.22 mrg static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95"; 39 1.21 lukem #else 40 1.81 andvar __RCSID("$NetBSD: function.c,v 1.81 2024/02/05 21:46:07 andvar Exp $"); 41 1.21 lukem #endif 42 1.1 cgd #endif /* not lint */ 43 1.1 cgd 44 1.1 cgd #include <sys/param.h> 45 1.1 cgd #include <sys/stat.h> 46 1.1 cgd #include <sys/wait.h> 47 1.1 cgd #include <sys/mount.h> 48 1.10 jtc 49 1.41 provos #include <dirent.h> 50 1.10 jtc #include <err.h> 51 1.1 cgd #include <errno.h> 52 1.10 jtc #include <fnmatch.h> 53 1.10 jtc #include <fts.h> 54 1.1 cgd #include <grp.h> 55 1.30 kleink #include <inttypes.h> 56 1.56 apb #include <limits.h> 57 1.1 cgd #include <pwd.h> 58 1.64 daniel #include <stdbool.h> 59 1.1 cgd #include <stdio.h> 60 1.1 cgd #include <stdlib.h> 61 1.1 cgd #include <string.h> 62 1.10 jtc #include <tzfile.h> 63 1.10 jtc #include <unistd.h> 64 1.55 christos #include <util.h> 65 1.10 jtc 66 1.1 cgd #include "find.h" 67 1.1 cgd 68 1.10 jtc #define COMPARE(a, b) { \ 69 1.10 jtc switch (plan->flags) { \ 70 1.10 jtc case F_EQUAL: \ 71 1.10 jtc return (a == b); \ 72 1.10 jtc case F_LESSTHAN: \ 73 1.10 jtc return (a < b); \ 74 1.10 jtc case F_GREATER: \ 75 1.10 jtc return (a > b); \ 76 1.10 jtc default: \ 77 1.10 jtc abort(); \ 78 1.10 jtc } \ 79 1.1 cgd } 80 1.1 cgd 81 1.57 apb static int64_t find_parsenum(PLAN *, const char *, const char *, char *); 82 1.57 apb static void run_f_exec(PLAN *); 83 1.57 apb int f_always_true(PLAN *, FTSENT *); 84 1.57 apb int f_amin(PLAN *, FTSENT *); 85 1.57 apb int f_anewer(PLAN *, FTSENT *); 86 1.74 pgoyette int f_asince(PLAN *, FTSENT *); 87 1.57 apb int f_atime(PLAN *, FTSENT *); 88 1.57 apb int f_cmin(PLAN *, FTSENT *); 89 1.57 apb int f_cnewer(PLAN *, FTSENT *); 90 1.74 pgoyette int f_csince(PLAN *, FTSENT *); 91 1.57 apb int f_ctime(PLAN *, FTSENT *); 92 1.62 elad int f_delete(PLAN *, FTSENT *); 93 1.57 apb int f_empty(PLAN *, FTSENT *); 94 1.57 apb int f_exec(PLAN *, FTSENT *); 95 1.57 apb int f_execdir(PLAN *, FTSENT *); 96 1.57 apb int f_false(PLAN *, FTSENT *); 97 1.57 apb int f_flags(PLAN *, FTSENT *); 98 1.57 apb int f_fprint(PLAN *, FTSENT *); 99 1.57 apb int f_fstype(PLAN *, FTSENT *); 100 1.57 apb int f_group(PLAN *, FTSENT *); 101 1.57 apb int f_iname(PLAN *, FTSENT *); 102 1.57 apb int f_inum(PLAN *, FTSENT *); 103 1.57 apb int f_links(PLAN *, FTSENT *); 104 1.57 apb int f_ls(PLAN *, FTSENT *); 105 1.57 apb int f_mindepth(PLAN *, FTSENT *); 106 1.57 apb int f_maxdepth(PLAN *, FTSENT *); 107 1.57 apb int f_mmin(PLAN *, FTSENT *); 108 1.57 apb int f_mtime(PLAN *, FTSENT *); 109 1.57 apb int f_name(PLAN *, FTSENT *); 110 1.57 apb int f_newer(PLAN *, FTSENT *); 111 1.74 pgoyette /* 112 1.74 pgoyette * Unimplemented Gnu findutils options 113 1.74 pgoyette * 114 1.74 pgoyette int f_newerBB(PLAN *, FTSENT *); 115 1.74 pgoyette int f_newerBa(PLAN *, FTSENT *); 116 1.74 pgoyette int f_newerBc(PLAN *, FTSENT *); 117 1.74 pgoyette int f_newerBm(PLAN *, FTSENT *); 118 1.74 pgoyette int f_newerBt(PLAN *, FTSENT *); 119 1.74 pgoyette int f_neweraB(PLAN *, FTSENT *); 120 1.74 pgoyette int f_newerac(PLAN *, FTSENT *); 121 1.74 pgoyette int f_neweram(PLAN *, FTSENT *); 122 1.74 pgoyette int f_newerca(PLAN *, FTSENT *); 123 1.74 pgoyette int f_newercm(PLAN *, FTSENT *); 124 1.74 pgoyette int f_newercB(PLAN *, FTSENT *); 125 1.74 pgoyette int f_newermB(PLAN *, FTSENT *); 126 1.74 pgoyette int f_newerma(PLAN *, FTSENT *); 127 1.74 pgoyette int f_newermc(PLAN *, FTSENT *); 128 1.74 pgoyette * 129 1.74 pgoyette */ 130 1.57 apb int f_nogroup(PLAN *, FTSENT *); 131 1.57 apb int f_nouser(PLAN *, FTSENT *); 132 1.57 apb int f_path(PLAN *, FTSENT *); 133 1.57 apb int f_perm(PLAN *, FTSENT *); 134 1.57 apb int f_print(PLAN *, FTSENT *); 135 1.57 apb int f_print0(PLAN *, FTSENT *); 136 1.57 apb int f_printx(PLAN *, FTSENT *); 137 1.57 apb int f_prune(PLAN *, FTSENT *); 138 1.57 apb int f_regex(PLAN *, FTSENT *); 139 1.74 pgoyette int f_since(PLAN *, FTSENT *); 140 1.57 apb int f_size(PLAN *, FTSENT *); 141 1.57 apb int f_type(PLAN *, FTSENT *); 142 1.57 apb int f_user(PLAN *, FTSENT *); 143 1.57 apb int f_not(PLAN *, FTSENT *); 144 1.57 apb int f_or(PLAN *, FTSENT *); 145 1.64 daniel static PLAN *c_regex_common(char ***, int, enum ntype, bool); 146 1.57 apb static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *)); 147 1.1 cgd 148 1.35 christos extern int dotfd; 149 1.35 christos extern FTS *tree; 150 1.35 christos extern time_t now; 151 1.35 christos 152 1.1 cgd /* 153 1.1 cgd * find_parsenum -- 154 1.1 cgd * Parse a string of the form [+-]# and return the value. 155 1.1 cgd */ 156 1.30 kleink static int64_t 157 1.57 apb find_parsenum(PLAN *plan, const char *option, const char *vp, char *endch) 158 1.1 cgd { 159 1.30 kleink int64_t value; 160 1.57 apb const char *str; 161 1.57 apb char *endchar; /* Pointer to character ending conversion. */ 162 1.57 apb 163 1.10 jtc /* Determine comparison from leading + or -. */ 164 1.10 jtc str = vp; 165 1.10 jtc switch (*str) { 166 1.1 cgd case '+': 167 1.1 cgd ++str; 168 1.10 jtc plan->flags = F_GREATER; 169 1.1 cgd break; 170 1.1 cgd case '-': 171 1.1 cgd ++str; 172 1.10 jtc plan->flags = F_LESSTHAN; 173 1.1 cgd break; 174 1.1 cgd default: 175 1.10 jtc plan->flags = F_EQUAL; 176 1.1 cgd break; 177 1.1 cgd } 178 1.57 apb 179 1.1 cgd /* 180 1.10 jtc * Convert the string with strtol(). Note, if strtol() returns zero 181 1.1 cgd * and endchar points to the beginning of the string we know we have 182 1.1 cgd * a syntax error. 183 1.1 cgd */ 184 1.79 cheusov value = strtoll(str, &endchar, 10); 185 1.10 jtc if (value == 0 && endchar == str) 186 1.10 jtc errx(1, "%s: %s: illegal numeric value", option, vp); 187 1.10 jtc if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 188 1.10 jtc errx(1, "%s: %s: illegal trailing character", option, vp); 189 1.1 cgd if (endch) 190 1.1 cgd *endch = endchar[0]; 191 1.10 jtc return (value); 192 1.1 cgd } 193 1.1 cgd 194 1.1 cgd /* 195 1.74 pgoyette * find_parsedate -- 196 1.74 pgoyette * 197 1.74 pgoyette * Validate the timestamp argument or report an error 198 1.74 pgoyette */ 199 1.74 pgoyette static time_t 200 1.74 pgoyette find_parsedate(PLAN *plan, const char *option, const char *vp) 201 1.74 pgoyette { 202 1.74 pgoyette time_t timestamp; 203 1.74 pgoyette 204 1.74 pgoyette errno = 0; 205 1.74 pgoyette timestamp = parsedate(vp, NULL, NULL); 206 1.74 pgoyette if (timestamp == -1 && errno != 0) 207 1.74 pgoyette errx(1, "%s: %s: invalid timestamp value", option, vp); 208 1.74 pgoyette return timestamp; 209 1.74 pgoyette } 210 1.74 pgoyette 211 1.74 pgoyette /* 212 1.10 jtc * The value of n for the inode times (atime, ctime, and mtime) is a range, 213 1.10 jtc * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 214 1.10 jtc * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 215 1.10 jtc * user wanted. Correct so that -1 is "less than 1". 216 1.10 jtc */ 217 1.10 jtc #define TIME_CORRECT(p, ttype) \ 218 1.10 jtc if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 219 1.10 jtc ++((p)->t_data); 220 1.10 jtc 221 1.10 jtc /* 222 1.29 simonb * -amin n functions -- 223 1.29 simonb * 224 1.29 simonb * True if the difference between the file access time and the 225 1.29 simonb * current time is n 1 minute periods. 226 1.29 simonb */ 227 1.29 simonb int 228 1.57 apb f_amin(PLAN *plan, FTSENT *entry) 229 1.29 simonb { 230 1.29 simonb COMPARE((now - entry->fts_statp->st_atime + 231 1.29 simonb SECSPERMIN - 1) / SECSPERMIN, plan->t_data); 232 1.29 simonb } 233 1.57 apb 234 1.29 simonb PLAN * 235 1.74 pgoyette c_amin(char ***argvp, int isok, char *opt) 236 1.29 simonb { 237 1.29 simonb char *arg = **argvp; 238 1.29 simonb PLAN *new; 239 1.29 simonb 240 1.29 simonb (*argvp)++; 241 1.29 simonb ftsoptions &= ~FTS_NOSTAT; 242 1.29 simonb 243 1.29 simonb new = palloc(N_AMIN, f_amin); 244 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 245 1.29 simonb TIME_CORRECT(new, N_AMIN); 246 1.29 simonb return (new); 247 1.29 simonb } 248 1.40 kleink 249 1.40 kleink /* 250 1.40 kleink * -anewer file functions -- 251 1.40 kleink * 252 1.40 kleink * True if the current file has been accessed more recently 253 1.40 kleink * than the access time of the file named by the pathname 254 1.40 kleink * file. 255 1.40 kleink */ 256 1.40 kleink int 257 1.68 matt f_anewer(PLAN *plan, FTSENT *entry) 258 1.40 kleink { 259 1.40 kleink 260 1.72 uebayasi return timespeccmp(&entry->fts_statp->st_atim, &plan->ts_data, >); 261 1.40 kleink } 262 1.57 apb 263 1.40 kleink PLAN * 264 1.74 pgoyette c_anewer(char ***argvp, int isok, char *opt) 265 1.40 kleink { 266 1.40 kleink char *filename = **argvp; 267 1.40 kleink PLAN *new; 268 1.40 kleink struct stat sb; 269 1.57 apb 270 1.40 kleink (*argvp)++; 271 1.40 kleink ftsoptions &= ~FTS_NOSTAT; 272 1.40 kleink 273 1.40 kleink if (stat(filename, &sb)) 274 1.74 pgoyette err(1, "%s: %s", opt, filename); 275 1.40 kleink new = palloc(N_ANEWER, f_anewer); 276 1.72 uebayasi new->ts_data = sb.st_atim; 277 1.40 kleink return (new); 278 1.40 kleink } 279 1.57 apb 280 1.29 simonb /* 281 1.74 pgoyette * -asince "timestamp" functions -- 282 1.74 pgoyette * 283 1.74 pgoyette * True if the file access time is greater than the timestamp value 284 1.74 pgoyette */ 285 1.74 pgoyette int 286 1.74 pgoyette f_asince(PLAN *plan, FTSENT *entry) 287 1.74 pgoyette { 288 1.74 pgoyette COMPARE(entry->fts_statp->st_atime, plan->t_data); 289 1.74 pgoyette } 290 1.74 pgoyette 291 1.74 pgoyette PLAN * 292 1.74 pgoyette c_asince(char ***argvp, int isok, char *opt) 293 1.74 pgoyette { 294 1.74 pgoyette char *arg = **argvp; 295 1.74 pgoyette PLAN *new; 296 1.74 pgoyette 297 1.74 pgoyette (*argvp)++; 298 1.74 pgoyette ftsoptions &= ~FTS_NOSTAT; 299 1.74 pgoyette 300 1.74 pgoyette new = palloc(N_ASINCE, f_asince); 301 1.74 pgoyette new->t_data = find_parsedate(new, opt, arg); 302 1.74 pgoyette new->flags = F_GREATER; 303 1.74 pgoyette return (new); 304 1.74 pgoyette } 305 1.74 pgoyette 306 1.74 pgoyette /* 307 1.1 cgd * -atime n functions -- 308 1.1 cgd * 309 1.1 cgd * True if the difference between the file access time and the 310 1.1 cgd * current time is n 24 hour periods. 311 1.1 cgd */ 312 1.10 jtc int 313 1.57 apb f_atime(PLAN *plan, FTSENT *entry) 314 1.1 cgd { 315 1.7 deraadt COMPARE((now - entry->fts_statp->st_atime + 316 1.1 cgd SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 317 1.1 cgd } 318 1.57 apb 319 1.1 cgd PLAN * 320 1.74 pgoyette c_atime(char ***argvp, int isok, char *opt) 321 1.1 cgd { 322 1.24 christos char *arg = **argvp; 323 1.1 cgd PLAN *new; 324 1.1 cgd 325 1.24 christos (*argvp)++; 326 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 327 1.1 cgd 328 1.1 cgd new = palloc(N_ATIME, f_atime); 329 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 330 1.10 jtc TIME_CORRECT(new, N_ATIME); 331 1.10 jtc return (new); 332 1.1 cgd } 333 1.72 uebayasi 334 1.1 cgd /* 335 1.29 simonb * -cmin n functions -- 336 1.29 simonb * 337 1.29 simonb * True if the difference between the last change of file 338 1.29 simonb * status information and the current time is n 24 hour periods. 339 1.29 simonb */ 340 1.29 simonb int 341 1.57 apb f_cmin(PLAN *plan, FTSENT *entry) 342 1.29 simonb { 343 1.29 simonb COMPARE((now - entry->fts_statp->st_ctime + 344 1.29 simonb SECSPERMIN - 1) / SECSPERMIN, plan->t_data); 345 1.29 simonb } 346 1.57 apb 347 1.29 simonb PLAN * 348 1.74 pgoyette c_cmin(char ***argvp, int isok, char *opt) 349 1.29 simonb { 350 1.29 simonb char *arg = **argvp; 351 1.29 simonb PLAN *new; 352 1.29 simonb 353 1.29 simonb (*argvp)++; 354 1.29 simonb ftsoptions &= ~FTS_NOSTAT; 355 1.29 simonb 356 1.29 simonb new = palloc(N_CMIN, f_cmin); 357 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 358 1.29 simonb TIME_CORRECT(new, N_CMIN); 359 1.29 simonb return (new); 360 1.29 simonb } 361 1.39 kleink 362 1.39 kleink /* 363 1.39 kleink * -cnewer file functions -- 364 1.39 kleink * 365 1.39 kleink * True if the current file has been changed more recently 366 1.40 kleink * than the changed time of the file named by the pathname 367 1.39 kleink * file. 368 1.39 kleink */ 369 1.39 kleink int 370 1.57 apb f_cnewer(PLAN *plan, FTSENT *entry) 371 1.39 kleink { 372 1.39 kleink 373 1.72 uebayasi return timespeccmp(&entry->fts_statp->st_ctim, &plan->ts_data, >); 374 1.39 kleink } 375 1.57 apb 376 1.39 kleink PLAN * 377 1.74 pgoyette c_cnewer(char ***argvp, int isok, char *opt) 378 1.39 kleink { 379 1.39 kleink char *filename = **argvp; 380 1.39 kleink PLAN *new; 381 1.39 kleink struct stat sb; 382 1.57 apb 383 1.39 kleink (*argvp)++; 384 1.39 kleink ftsoptions &= ~FTS_NOSTAT; 385 1.39 kleink 386 1.39 kleink if (stat(filename, &sb)) 387 1.74 pgoyette err(1, "%s: %s ", opt, filename); 388 1.39 kleink new = palloc(N_CNEWER, f_cnewer); 389 1.72 uebayasi new->ts_data = sb.st_ctim; 390 1.39 kleink return (new); 391 1.39 kleink } 392 1.57 apb 393 1.29 simonb /* 394 1.74 pgoyette * -csince "timestamp" functions -- 395 1.74 pgoyette * 396 1.74 pgoyette * True if the file status change time is greater than the timestamp value 397 1.74 pgoyette */ 398 1.74 pgoyette int 399 1.74 pgoyette f_csince(PLAN *plan, FTSENT *entry) 400 1.74 pgoyette { 401 1.74 pgoyette COMPARE(entry->fts_statp->st_ctime, plan->t_data); 402 1.74 pgoyette } 403 1.74 pgoyette 404 1.74 pgoyette PLAN * 405 1.74 pgoyette c_csince(char ***argvp, int isok, char *opt) 406 1.74 pgoyette { 407 1.74 pgoyette char *arg = **argvp; 408 1.74 pgoyette PLAN *new; 409 1.74 pgoyette 410 1.74 pgoyette (*argvp)++; 411 1.74 pgoyette ftsoptions &= ~FTS_NOSTAT; 412 1.74 pgoyette 413 1.74 pgoyette new = palloc(N_CSINCE, f_csince); 414 1.74 pgoyette new->t_data = find_parsedate(new, opt, arg); 415 1.74 pgoyette new->flags = F_GREATER; 416 1.74 pgoyette return (new); 417 1.74 pgoyette } 418 1.74 pgoyette 419 1.74 pgoyette /* 420 1.1 cgd * -ctime n functions -- 421 1.1 cgd * 422 1.1 cgd * True if the difference between the last change of file 423 1.1 cgd * status information and the current time is n 24 hour periods. 424 1.1 cgd */ 425 1.10 jtc int 426 1.57 apb f_ctime(PLAN *plan, FTSENT *entry) 427 1.1 cgd { 428 1.7 deraadt COMPARE((now - entry->fts_statp->st_ctime + 429 1.1 cgd SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 430 1.1 cgd } 431 1.57 apb 432 1.1 cgd PLAN * 433 1.74 pgoyette c_ctime(char ***argvp, int isok, char *opt) 434 1.1 cgd { 435 1.24 christos char *arg = **argvp; 436 1.1 cgd PLAN *new; 437 1.1 cgd 438 1.24 christos (*argvp)++; 439 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 440 1.1 cgd 441 1.1 cgd new = palloc(N_CTIME, f_ctime); 442 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 443 1.10 jtc TIME_CORRECT(new, N_CTIME); 444 1.10 jtc return (new); 445 1.1 cgd } 446 1.1 cgd 447 1.1 cgd /* 448 1.62 elad * -delete functions -- 449 1.62 elad * 450 1.71 wiz * Always true. Makes its best shot and continues on regardless. 451 1.62 elad */ 452 1.62 elad int 453 1.62 elad f_delete(PLAN *plan __unused, FTSENT *entry) 454 1.62 elad { 455 1.62 elad /* ignore these from fts */ 456 1.62 elad if (strcmp(entry->fts_accpath, ".") == 0 || 457 1.62 elad strcmp(entry->fts_accpath, "..") == 0) 458 1.62 elad return 1; 459 1.62 elad 460 1.62 elad /* sanity check */ 461 1.62 elad if (isdepth == 0 || /* depth off */ 462 1.62 elad (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ 463 1.62 elad !(ftsoptions & FTS_PHYSICAL) || /* physical off */ 464 1.62 elad (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 465 1.62 elad errx(1, "-delete: insecure options got turned on"); 466 1.62 elad 467 1.62 elad /* Potentially unsafe - do not accept relative paths whatsoever */ 468 1.76 christos if (entry->fts_level > 0 && strchr(entry->fts_accpath, '/') != NULL) 469 1.62 elad errx(1, "-delete: %s: relative path potentially not safe", 470 1.62 elad entry->fts_accpath); 471 1.62 elad 472 1.62 elad /* Turn off user immutable bits if running as root */ 473 1.62 elad if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 474 1.62 elad !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 475 1.62 elad geteuid() == 0) 476 1.62 elad chflags(entry->fts_accpath, 477 1.62 elad entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 478 1.62 elad 479 1.62 elad /* rmdir directories, unlink everything else */ 480 1.62 elad if (S_ISDIR(entry->fts_statp->st_mode)) { 481 1.62 elad if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 482 1.62 elad warn("-delete: rmdir(%s)", entry->fts_path); 483 1.62 elad } else { 484 1.62 elad if (unlink(entry->fts_accpath) < 0) 485 1.62 elad warn("-delete: unlink(%s)", entry->fts_path); 486 1.62 elad } 487 1.62 elad 488 1.62 elad /* "succeed" */ 489 1.62 elad return 1; 490 1.62 elad } 491 1.62 elad 492 1.62 elad PLAN * 493 1.74 pgoyette c_delete(char ***argvp __unused, int isok, char *opt) 494 1.62 elad { 495 1.62 elad 496 1.62 elad ftsoptions &= ~FTS_NOSTAT; /* no optimize */ 497 1.62 elad ftsoptions |= FTS_PHYSICAL; /* disable -follow */ 498 1.62 elad ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ 499 1.62 elad isoutput = 1; /* possible output */ 500 1.62 elad isdepth = 1; /* -depth implied */ 501 1.62 elad 502 1.62 elad return palloc(N_DELETE, f_delete); 503 1.62 elad } 504 1.62 elad 505 1.62 elad /* 506 1.1 cgd * -depth functions -- 507 1.1 cgd * 508 1.1 cgd * Always true, causes descent of the directory hierarchy to be done 509 1.1 cgd * so that all entries in a directory are acted on before the directory 510 1.1 cgd * itself. 511 1.1 cgd */ 512 1.10 jtc int 513 1.57 apb f_always_true(PLAN *plan, FTSENT *entry) 514 1.1 cgd { 515 1.33 enami 516 1.10 jtc return (1); 517 1.1 cgd } 518 1.57 apb 519 1.1 cgd PLAN * 520 1.74 pgoyette c_depth(char ***argvp, int isok, char *opt) 521 1.1 cgd { 522 1.1 cgd isdepth = 1; 523 1.1 cgd 524 1.10 jtc return (palloc(N_DEPTH, f_always_true)); 525 1.1 cgd } 526 1.57 apb 527 1.1 cgd /* 528 1.41 provos * -empty functions -- 529 1.41 provos * 530 1.41 provos * True if the file or directory is empty 531 1.41 provos */ 532 1.41 provos int 533 1.57 apb f_empty(PLAN *plan, FTSENT *entry) 534 1.41 provos { 535 1.41 provos if (S_ISREG(entry->fts_statp->st_mode) && 536 1.41 provos entry->fts_statp->st_size == 0) 537 1.41 provos return (1); 538 1.41 provos if (S_ISDIR(entry->fts_statp->st_mode)) { 539 1.41 provos struct dirent *dp; 540 1.41 provos int empty; 541 1.41 provos DIR *dir; 542 1.41 provos 543 1.41 provos empty = 1; 544 1.41 provos dir = opendir(entry->fts_accpath); 545 1.41 provos if (dir == NULL) 546 1.66 jmcneill return (0); 547 1.41 provos for (dp = readdir(dir); dp; dp = readdir(dir)) 548 1.41 provos if (dp->d_name[0] != '.' || 549 1.41 provos (dp->d_name[1] != '\0' && 550 1.41 provos (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { 551 1.41 provos empty = 0; 552 1.41 provos break; 553 1.41 provos } 554 1.41 provos closedir(dir); 555 1.41 provos return (empty); 556 1.41 provos } 557 1.41 provos return (0); 558 1.41 provos } 559 1.41 provos 560 1.41 provos PLAN * 561 1.74 pgoyette c_empty(char ***argvp, int isok, char *opt) 562 1.41 provos { 563 1.41 provos ftsoptions &= ~FTS_NOSTAT; 564 1.41 provos 565 1.41 provos return (palloc(N_EMPTY, f_empty)); 566 1.41 provos } 567 1.41 provos 568 1.41 provos /* 569 1.1 cgd * [-exec | -ok] utility [arg ... ] ; functions -- 570 1.56 apb * [-exec | -ok] utility [arg ... ] {} + functions -- 571 1.1 cgd * 572 1.56 apb * If the end of the primary expression is delimited by a 573 1.56 apb * semicolon: true if the executed utility returns a zero value 574 1.56 apb * as exit status. If "{}" occurs anywhere, it gets replaced by 575 1.56 apb * the current pathname. 576 1.56 apb * 577 1.56 apb * If the end of the primary expression is delimited by a plus 578 1.56 apb * sign: always true. Pathnames for which the primary is 579 1.56 apb * evaluated shall be aggregated into sets. The utility will be 580 1.56 apb * executed once per set, with "{}" replaced by the entire set of 581 1.56 apb * pathnames (as if xargs). "{}" must appear last. 582 1.56 apb * 583 1.56 apb * The current directory for the execution of utility is the same 584 1.56 apb * as the current directory when the find utility was started. 585 1.1 cgd * 586 1.56 apb * The primary -ok is different in that it requests affirmation 587 1.56 apb * of the user before executing the utility. 588 1.1 cgd */ 589 1.10 jtc int 590 1.57 apb f_exec(PLAN *plan, FTSENT *entry) 591 1.1 cgd { 592 1.67 christos size_t cnt; 593 1.67 christos int l; 594 1.1 cgd pid_t pid; 595 1.1 cgd int status; 596 1.1 cgd 597 1.56 apb if (plan->flags & F_PLUSSET) { 598 1.56 apb /* 599 1.56 apb * Confirm sufficient buffer space, then copy the path 600 1.56 apb * to the buffer. 601 1.56 apb */ 602 1.56 apb l = strlen(entry->fts_path); 603 1.56 apb if (plan->ep_p + l < plan->ep_ebp) { 604 1.56 apb plan->ep_bxp[plan->ep_narg++] = 605 1.56 apb strcpy(plan->ep_p, entry->fts_path); 606 1.56 apb plan->ep_p += l + 1; 607 1.56 apb 608 1.56 apb if (plan->ep_narg == plan->ep_maxargs) 609 1.56 apb run_f_exec(plan); 610 1.56 apb } else { 611 1.56 apb /* 612 1.56 apb * Without sufficient space to copy in the next 613 1.56 apb * argument, run the command to empty out the 614 1.56 apb * buffer before re-attepting the copy. 615 1.56 apb */ 616 1.56 apb run_f_exec(plan); 617 1.56 apb if ((plan->ep_p + l < plan->ep_ebp)) { 618 1.56 apb plan->ep_bxp[plan->ep_narg++] 619 1.56 apb = strcpy(plan->ep_p, entry->fts_path); 620 1.56 apb plan->ep_p += l + 1; 621 1.56 apb } else 622 1.56 apb errx(1, "insufficient space for argument"); 623 1.56 apb } 624 1.56 apb return (1); 625 1.56 apb } else { 626 1.56 apb for (cnt = 0; plan->e_argv[cnt]; ++cnt) 627 1.56 apb if (plan->e_len[cnt]) 628 1.56 apb brace_subst(plan->e_orig[cnt], 629 1.56 apb &plan->e_argv[cnt], 630 1.56 apb entry->fts_path, 631 1.56 apb &plan->e_len[cnt]); 632 1.56 apb if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv)) 633 1.56 apb return (0); 634 1.56 apb 635 1.56 apb /* Don't mix output of command with find output. */ 636 1.56 apb fflush(stdout); 637 1.56 apb fflush(stderr); 638 1.56 apb 639 1.56 apb switch (pid = vfork()) { 640 1.56 apb case -1: 641 1.56 apb err(1, "vfork"); 642 1.56 apb /* NOTREACHED */ 643 1.56 apb case 0: 644 1.80 christos /* change dir back from where we started */ 645 1.80 christos if (!(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) { 646 1.56 apb warn("chdir"); 647 1.56 apb _exit(1); 648 1.56 apb } 649 1.56 apb execvp(plan->e_argv[0], plan->e_argv); 650 1.56 apb warn("%s", plan->e_argv[0]); 651 1.56 apb _exit(1); 652 1.56 apb } 653 1.56 apb pid = waitpid(pid, &status, 0); 654 1.56 apb return (pid != -1 && WIFEXITED(status) 655 1.56 apb && !WEXITSTATUS(status)); 656 1.56 apb } 657 1.56 apb } 658 1.56 apb 659 1.56 apb static void 660 1.57 apb run_f_exec(PLAN *plan) 661 1.56 apb { 662 1.56 apb pid_t pid; 663 1.56 apb int rval, status; 664 1.1 cgd 665 1.56 apb /* Ensure arg list is null terminated. */ 666 1.56 apb plan->ep_bxp[plan->ep_narg] = NULL; 667 1.11 jtc 668 1.56 apb /* Don't mix output of command with find output. */ 669 1.11 jtc fflush(stdout); 670 1.11 jtc fflush(stderr); 671 1.1 cgd 672 1.37 simonb switch (pid = vfork()) { 673 1.1 cgd case -1: 674 1.37 simonb err(1, "vfork"); 675 1.1 cgd /* NOTREACHED */ 676 1.1 cgd case 0: 677 1.80 christos /* change dir back from where we started */ 678 1.80 christos if (!(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) { 679 1.10 jtc warn("chdir"); 680 1.1 cgd _exit(1); 681 1.1 cgd } 682 1.1 cgd execvp(plan->e_argv[0], plan->e_argv); 683 1.10 jtc warn("%s", plan->e_argv[0]); 684 1.1 cgd _exit(1); 685 1.1 cgd } 686 1.56 apb 687 1.56 apb /* Clear out the argument list. */ 688 1.56 apb plan->ep_narg = 0; 689 1.56 apb plan->ep_bxp[plan->ep_narg] = NULL; 690 1.56 apb /* As well as the argument buffer. */ 691 1.56 apb plan->ep_p = plan->ep_bbp; 692 1.56 apb *plan->ep_p = '\0'; 693 1.56 apb 694 1.1 cgd pid = waitpid(pid, &status, 0); 695 1.56 apb if (WIFEXITED(status)) 696 1.56 apb rval = WEXITSTATUS(status); 697 1.56 apb else 698 1.56 apb rval = -1; 699 1.56 apb 700 1.56 apb /* 701 1.56 apb * If we have a non-zero exit status, preserve it so find(1) can 702 1.56 apb * later exit with it. 703 1.56 apb */ 704 1.56 apb if (rval) 705 1.56 apb plan->ep_rval = rval; 706 1.1 cgd } 707 1.56 apb 708 1.1 cgd /* 709 1.1 cgd * c_exec -- 710 1.1 cgd * build three parallel arrays, one with pointers to the strings passed 711 1.1 cgd * on the command line, one with (possibly duplicated) pointers to the 712 1.1 cgd * argv array, and one with integer values that are lengths of the 713 1.1 cgd * strings, but also flags meaning that the string has to be massaged. 714 1.56 apb * 715 1.56 apb * If -exec ... {} +, use only the first array, but make it large 716 1.56 apb * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a 717 1.56 apb * discussion), and then allocate ARG_MAX - 4K of space for args. 718 1.1 cgd */ 719 1.1 cgd PLAN * 720 1.74 pgoyette c_exec(char ***argvp, int isok, char *opt) 721 1.1 cgd { 722 1.1 cgd PLAN *new; /* node returned */ 723 1.67 christos size_t cnt; 724 1.67 christos int brace, lastbrace; 725 1.21 lukem char **argv, **ap, *p; 726 1.1 cgd 727 1.1 cgd isoutput = 1; 728 1.57 apb 729 1.1 cgd new = palloc(N_EXEC, f_exec); 730 1.10 jtc if (isok) 731 1.56 apb new->flags |= F_NEEDOK; 732 1.1 cgd 733 1.56 apb /* 734 1.70 dholland * Terminate if we encounter an arg exactly equal to ";", or an 735 1.70 dholland * arg exactly equal to "+" following an arg exactly equal to 736 1.56 apb * "{}". 737 1.56 apb */ 738 1.56 apb for (ap = argv = *argvp, brace = 0;; ++ap) { 739 1.1 cgd if (!*ap) 740 1.74 pgoyette errx(1, "%s: no terminating \";\" or \"+\"", opt); 741 1.56 apb lastbrace = brace; 742 1.65 dholland brace = 0; 743 1.56 apb if (strcmp(*ap, "{}") == 0) 744 1.56 apb brace = 1; 745 1.56 apb if (strcmp(*ap, ";") == 0) 746 1.56 apb break; 747 1.56 apb if (strcmp(*ap, "+") == 0 && lastbrace) { 748 1.56 apb new->flags |= F_PLUSSET; 749 1.1 cgd break; 750 1.56 apb } 751 1.1 cgd } 752 1.1 cgd 753 1.56 apb /* 754 1.56 apb * POSIX says -ok ... {} + "need not be supported," and it does 755 1.56 apb * not make much sense anyway. 756 1.56 apb */ 757 1.56 apb if (new->flags & F_NEEDOK && new->flags & F_PLUSSET) 758 1.74 pgoyette errx(1, "%s: terminating \"+\" not permitted.", opt); 759 1.56 apb 760 1.56 apb if (new->flags & F_PLUSSET) { 761 1.67 christos size_t c, bufsize; 762 1.56 apb 763 1.56 apb cnt = ap - *argvp - 1; /* units are words */ 764 1.77 kre new->ep_maxargs = ARG_MAX / (sizeof (char *) + 16); 765 1.77 kre if (new->ep_maxargs > 5000) 766 1.77 kre new->ep_maxargs = 5000; 767 1.67 christos new->e_argv = emalloc((cnt + new->ep_maxargs) 768 1.67 christos * sizeof(*new->e_argv)); 769 1.56 apb 770 1.56 apb /* We start stuffing arguments after the user's last one. */ 771 1.56 apb new->ep_bxp = &new->e_argv[cnt]; 772 1.56 apb new->ep_narg = 0; 773 1.56 apb 774 1.56 apb /* 775 1.56 apb * Count up the space of the user's arguments, and 776 1.56 apb * subtract that from what we allocate. 777 1.56 apb */ 778 1.67 christos #define MAXARG (ARG_MAX - 4 * 1024) 779 1.56 apb for (argv = *argvp, c = 0, cnt = 0; 780 1.56 apb argv < ap; 781 1.56 apb ++argv, ++cnt) { 782 1.56 apb c += strlen(*argv) + 1; 783 1.67 christos if (c >= MAXARG) 784 1.67 christos errx(1, "Arguments too long"); 785 1.56 apb new->e_argv[cnt] = *argv; 786 1.56 apb } 787 1.77 kre if (c + new->ep_maxargs * sizeof (char *) >= MAXARG) 788 1.77 kre errx(1, "Arguments too long"); 789 1.77 kre bufsize = MAXARG - c - new->ep_maxargs * sizeof (char *); 790 1.1 cgd 791 1.56 apb /* 792 1.56 apb * Allocate, and then initialize current, base, and 793 1.56 apb * end pointers. 794 1.56 apb */ 795 1.67 christos new->ep_p = new->ep_bbp = emalloc(bufsize + 1); 796 1.56 apb new->ep_ebp = new->ep_bbp + bufsize - 1; 797 1.56 apb new->ep_rval = 0; 798 1.56 apb } else { /* !F_PLUSSET */ 799 1.56 apb cnt = ap - *argvp + 1; 800 1.67 christos new->e_argv = emalloc(cnt * sizeof(*new->e_argv)); 801 1.67 christos new->e_orig = emalloc(cnt * sizeof(*new->e_orig)); 802 1.67 christos new->e_len = emalloc(cnt * sizeof(*new->e_len)); 803 1.56 apb 804 1.56 apb for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 805 1.56 apb new->e_orig[cnt] = *argv; 806 1.56 apb for (p = *argv; *p; ++p) 807 1.56 apb if (p[0] == '{' && p[1] == '}') { 808 1.56 apb new->e_argv[cnt] = 809 1.67 christos emalloc(MAXPATHLEN); 810 1.56 apb new->e_len[cnt] = MAXPATHLEN; 811 1.56 apb break; 812 1.56 apb } 813 1.56 apb if (!*p) { 814 1.56 apb new->e_argv[cnt] = *argv; 815 1.56 apb new->e_len[cnt] = 0; 816 1.1 cgd } 817 1.1 cgd } 818 1.56 apb new->e_orig[cnt] = NULL; 819 1.1 cgd } 820 1.1 cgd 821 1.56 apb new->e_argv[cnt] = NULL; 822 1.1 cgd *argvp = argv + 1; 823 1.10 jtc return (new); 824 1.1 cgd } 825 1.41 provos 826 1.41 provos /* 827 1.41 provos * -execdir utility [arg ... ] ; functions -- 828 1.41 provos * 829 1.41 provos * True if the executed utility returns a zero value as exit status. 830 1.41 provos * The end of the primary expression is delimited by a semicolon. If 831 1.41 provos * "{}" occurs anywhere, it gets replaced by the unqualified pathname. 832 1.41 provos * The current directory for the execution of utility is the same as 833 1.41 provos * the directory where the file lives. 834 1.41 provos */ 835 1.41 provos int 836 1.57 apb f_execdir(PLAN *plan, FTSENT *entry) 837 1.41 provos { 838 1.67 christos size_t cnt; 839 1.41 provos pid_t pid; 840 1.41 provos int status; 841 1.41 provos char *file; 842 1.41 provos 843 1.41 provos /* XXX - if file/dir ends in '/' this will not work -- can it? */ 844 1.41 provos if ((file = strrchr(entry->fts_path, '/'))) 845 1.41 provos file++; 846 1.41 provos else 847 1.41 provos file = entry->fts_path; 848 1.41 provos 849 1.41 provos for (cnt = 0; plan->e_argv[cnt]; ++cnt) 850 1.41 provos if (plan->e_len[cnt]) 851 1.41 provos brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 852 1.41 provos file, &plan->e_len[cnt]); 853 1.41 provos 854 1.41 provos /* don't mix output of command with find output */ 855 1.41 provos fflush(stdout); 856 1.41 provos fflush(stderr); 857 1.41 provos 858 1.41 provos switch (pid = vfork()) { 859 1.41 provos case -1: 860 1.41 provos err(1, "fork"); 861 1.41 provos /* NOTREACHED */ 862 1.41 provos case 0: 863 1.41 provos execvp(plan->e_argv[0], plan->e_argv); 864 1.41 provos warn("%s", plan->e_argv[0]); 865 1.41 provos _exit(1); 866 1.41 provos } 867 1.41 provos pid = waitpid(pid, &status, 0); 868 1.41 provos return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 869 1.41 provos } 870 1.57 apb 871 1.41 provos /* 872 1.41 provos * c_execdir -- 873 1.41 provos * build three parallel arrays, one with pointers to the strings passed 874 1.41 provos * on the command line, one with (possibly duplicated) pointers to the 875 1.41 provos * argv array, and one with integer values that are lengths of the 876 1.41 provos * strings, but also flags meaning that the string has to be massaged. 877 1.41 provos */ 878 1.41 provos PLAN * 879 1.74 pgoyette c_execdir(char ***argvp, int isok, char *opt) 880 1.41 provos { 881 1.41 provos PLAN *new; /* node returned */ 882 1.67 christos size_t cnt; 883 1.41 provos char **argv, **ap, *p; 884 1.41 provos 885 1.41 provos ftsoptions &= ~FTS_NOSTAT; 886 1.41 provos isoutput = 1; 887 1.57 apb 888 1.41 provos new = palloc(N_EXECDIR, f_execdir); 889 1.41 provos 890 1.41 provos for (ap = argv = *argvp;; ++ap) { 891 1.41 provos if (!*ap) 892 1.74 pgoyette errx(1, "%s: no terminating \";\"", opt); 893 1.41 provos if (**ap == ';') 894 1.41 provos break; 895 1.41 provos } 896 1.41 provos 897 1.41 provos cnt = ap - *argvp + 1; 898 1.67 christos new->e_argv = emalloc(cnt * sizeof(*new->e_argv)); 899 1.67 christos new->e_orig = emalloc(cnt * sizeof(*new->e_orig)); 900 1.67 christos new->e_len = emalloc(cnt * sizeof(*new->e_len)); 901 1.41 provos 902 1.41 provos for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 903 1.41 provos new->e_orig[cnt] = *argv; 904 1.41 provos for (p = *argv; *p; ++p) 905 1.41 provos if (p[0] == '{' && p[1] == '}') { 906 1.67 christos new->e_argv[cnt] = emalloc(MAXPATHLEN); 907 1.41 provos new->e_len[cnt] = MAXPATHLEN; 908 1.41 provos break; 909 1.41 provos } 910 1.41 provos if (!*p) { 911 1.41 provos new->e_argv[cnt] = *argv; 912 1.41 provos new->e_len[cnt] = 0; 913 1.41 provos } 914 1.41 provos } 915 1.41 provos new->e_argv[cnt] = new->e_orig[cnt] = NULL; 916 1.41 provos 917 1.41 provos *argvp = argv + 1; 918 1.41 provos return (new); 919 1.41 provos } 920 1.52 reed 921 1.53 jschauma PLAN * 922 1.74 pgoyette c_exit(char ***argvp, int isok, char *opt) 923 1.53 jschauma { 924 1.53 jschauma char *arg = **argvp; 925 1.53 jschauma PLAN *new; 926 1.53 jschauma 927 1.53 jschauma /* not technically true, but otherwise '-print' is implied */ 928 1.53 jschauma isoutput = 1; 929 1.53 jschauma 930 1.53 jschauma new = palloc(N_EXIT, f_always_true); 931 1.53 jschauma 932 1.53 jschauma if (arg) { 933 1.53 jschauma (*argvp)++; 934 1.74 pgoyette new->exit_val = find_parsenum(new, opt, arg, NULL); 935 1.53 jschauma } else 936 1.53 jschauma new->exit_val = 0; 937 1.53 jschauma 938 1.53 jschauma return (new); 939 1.53 jschauma } 940 1.53 jschauma 941 1.53 jschauma 942 1.52 reed /* 943 1.52 reed * -false function 944 1.52 reed */ 945 1.52 reed int 946 1.57 apb f_false(PLAN *plan, FTSENT *entry) 947 1.52 reed { 948 1.52 reed 949 1.52 reed return (0); 950 1.52 reed } 951 1.57 apb 952 1.52 reed PLAN * 953 1.74 pgoyette c_false(char ***argvp, int isok, char *opt) 954 1.52 reed { 955 1.52 reed return (palloc(N_FALSE, f_false)); 956 1.52 reed } 957 1.52 reed 958 1.57 apb 959 1.27 lukem /* 960 1.27 lukem * -flags [-]flags functions -- 961 1.27 lukem */ 962 1.27 lukem int 963 1.57 apb f_flags(PLAN *plan, FTSENT *entry) 964 1.27 lukem { 965 1.78 cheusov uint32_t flags; 966 1.27 lukem 967 1.27 lukem flags = entry->fts_statp->st_flags; 968 1.27 lukem if (plan->flags == F_ATLEAST) 969 1.27 lukem return ((plan->f_data | flags) == flags); 970 1.27 lukem else 971 1.27 lukem return (flags == plan->f_data); 972 1.27 lukem /* NOTREACHED */ 973 1.27 lukem } 974 1.57 apb 975 1.27 lukem PLAN * 976 1.74 pgoyette c_flags(char ***argvp, int isok, char *opt) 977 1.27 lukem { 978 1.27 lukem char *flags = **argvp; 979 1.27 lukem PLAN *new; 980 1.27 lukem u_long flagset; 981 1.27 lukem 982 1.27 lukem (*argvp)++; 983 1.27 lukem ftsoptions &= ~FTS_NOSTAT; 984 1.27 lukem 985 1.27 lukem new = palloc(N_FLAGS, f_flags); 986 1.27 lukem 987 1.27 lukem if (*flags == '-') { 988 1.27 lukem new->flags = F_ATLEAST; 989 1.27 lukem ++flags; 990 1.27 lukem } 991 1.27 lukem 992 1.27 lukem flagset = 0; 993 1.27 lukem if ((strcmp(flags, "none") != 0) && 994 1.27 lukem (string_to_flags(&flags, &flagset, NULL) != 0)) 995 1.74 pgoyette errx(1, "%s: %s: illegal flags string", opt, flags); 996 1.27 lukem new->f_data = flagset; 997 1.27 lukem return (new); 998 1.27 lukem } 999 1.57 apb 1000 1.1 cgd /* 1001 1.1 cgd * -follow functions -- 1002 1.1 cgd * 1003 1.1 cgd * Always true, causes symbolic links to be followed on a global 1004 1.1 cgd * basis. 1005 1.1 cgd */ 1006 1.1 cgd PLAN * 1007 1.74 pgoyette c_follow(char ***argvp, int isok, char *opt) 1008 1.1 cgd { 1009 1.1 cgd ftsoptions &= ~FTS_PHYSICAL; 1010 1.1 cgd ftsoptions |= FTS_LOGICAL; 1011 1.1 cgd 1012 1.10 jtc return (palloc(N_FOLLOW, f_always_true)); 1013 1.1 cgd } 1014 1.57 apb 1015 1.51 reed /* -fprint functions -- 1016 1.51 reed * 1017 1.81 andvar * Causes the current pathname to be written to the defined output file. 1018 1.51 reed */ 1019 1.51 reed int 1020 1.57 apb f_fprint(PLAN *plan, FTSENT *entry) 1021 1.51 reed { 1022 1.51 reed 1023 1.51 reed if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path)) 1024 1.51 reed warn("fprintf"); 1025 1.51 reed 1026 1.51 reed return(1); 1027 1.51 reed 1028 1.51 reed /* no descriptors are closed; they will be closed by 1029 1.51 reed operating system when this find command exits. */ 1030 1.51 reed } 1031 1.57 apb 1032 1.51 reed PLAN * 1033 1.74 pgoyette c_fprint(char ***argvp, int isok, char *opt) 1034 1.51 reed { 1035 1.51 reed PLAN *new; 1036 1.51 reed 1037 1.51 reed isoutput = 1; /* do not assume -print */ 1038 1.51 reed 1039 1.51 reed new = palloc(N_FPRINT, f_fprint); 1040 1.51 reed 1041 1.51 reed if (NULL == (new->fprint_file = fopen(**argvp, "w"))) 1042 1.74 pgoyette err(1, "%s: %s: cannot create file", opt, **argvp); 1043 1.51 reed 1044 1.51 reed (*argvp)++; 1045 1.51 reed return (new); 1046 1.51 reed } 1047 1.51 reed 1048 1.1 cgd /* 1049 1.1 cgd * -fstype functions -- 1050 1.1 cgd * 1051 1.1 cgd * True if the file is of a certain type. 1052 1.1 cgd */ 1053 1.10 jtc int 1054 1.57 apb f_fstype(PLAN *plan, FTSENT *entry) 1055 1.1 cgd { 1056 1.1 cgd static dev_t curdev; /* need a guaranteed illegal dev value */ 1057 1.1 cgd static int first = 1; 1058 1.48 christos struct statvfs sb; 1059 1.10 jtc static short val; 1060 1.63 christos static char fstype[sizeof(sb.f_fstypename)]; 1061 1.1 cgd char *p, save[2]; 1062 1.1 cgd 1063 1.54 mrg memset(&save, 0, sizeof save); /* XXX gcc */ 1064 1.54 mrg 1065 1.10 jtc /* Only check when we cross mount point. */ 1066 1.7 deraadt if (first || curdev != entry->fts_statp->st_dev) { 1067 1.7 deraadt curdev = entry->fts_statp->st_dev; 1068 1.1 cgd 1069 1.1 cgd /* 1070 1.1 cgd * Statfs follows symlinks; find wants the link's file system, 1071 1.1 cgd * not where it points. 1072 1.1 cgd */ 1073 1.1 cgd if (entry->fts_info == FTS_SL || 1074 1.1 cgd entry->fts_info == FTS_SLNONE) { 1075 1.21 lukem if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 1076 1.1 cgd ++p; 1077 1.1 cgd else 1078 1.1 cgd p = entry->fts_accpath; 1079 1.1 cgd save[0] = p[0]; 1080 1.1 cgd p[0] = '.'; 1081 1.1 cgd save[1] = p[1]; 1082 1.1 cgd p[1] = '\0'; 1083 1.57 apb 1084 1.57 apb } else 1085 1.1 cgd p = NULL; 1086 1.1 cgd 1087 1.48 christos if (statvfs(entry->fts_accpath, &sb)) 1088 1.10 jtc err(1, "%s", entry->fts_accpath); 1089 1.1 cgd 1090 1.1 cgd if (p) { 1091 1.1 cgd p[0] = save[0]; 1092 1.1 cgd p[1] = save[1]; 1093 1.1 cgd } 1094 1.1 cgd 1095 1.1 cgd first = 0; 1096 1.15 mycroft 1097 1.15 mycroft /* 1098 1.15 mycroft * Further tests may need both of these values, so 1099 1.15 mycroft * always copy both of them. 1100 1.15 mycroft */ 1101 1.48 christos val = sb.f_flag; 1102 1.44 itojun strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); 1103 1.10 jtc } 1104 1.14 mycroft switch (plan->flags) { 1105 1.10 jtc case F_MTFLAG: 1106 1.57 apb return (val & plan->mt_data); 1107 1.10 jtc case F_MTTYPE: 1108 1.63 christos return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); 1109 1.10 jtc default: 1110 1.10 jtc abort(); 1111 1.1 cgd } 1112 1.1 cgd } 1113 1.57 apb 1114 1.1 cgd PLAN * 1115 1.74 pgoyette c_fstype(char ***argvp, int isok, char *opt) 1116 1.1 cgd { 1117 1.24 christos char *arg = **argvp; 1118 1.21 lukem PLAN *new; 1119 1.57 apb 1120 1.24 christos (*argvp)++; 1121 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1122 1.57 apb 1123 1.1 cgd new = palloc(N_FSTYPE, f_fstype); 1124 1.22 mrg 1125 1.10 jtc switch (*arg) { 1126 1.1 cgd case 'l': 1127 1.1 cgd if (!strcmp(arg, "local")) { 1128 1.10 jtc new->flags = F_MTFLAG; 1129 1.10 jtc new->mt_data = MNT_LOCAL; 1130 1.10 jtc return (new); 1131 1.1 cgd } 1132 1.1 cgd break; 1133 1.2 cgd case 'r': 1134 1.2 cgd if (!strcmp(arg, "rdonly")) { 1135 1.10 jtc new->flags = F_MTFLAG; 1136 1.10 jtc new->mt_data = MNT_RDONLY; 1137 1.10 jtc return (new); 1138 1.1 cgd } 1139 1.1 cgd break; 1140 1.1 cgd } 1141 1.13 cgd 1142 1.13 cgd new->flags = F_MTTYPE; 1143 1.13 cgd new->c_data = arg; 1144 1.13 cgd return (new); 1145 1.1 cgd } 1146 1.57 apb 1147 1.1 cgd /* 1148 1.1 cgd * -group gname functions -- 1149 1.1 cgd * 1150 1.1 cgd * True if the file belongs to the group gname. If gname is numeric and 1151 1.1 cgd * an equivalent of the getgrnam() function does not return a valid group 1152 1.1 cgd * name, gname is taken as a group ID. 1153 1.1 cgd */ 1154 1.10 jtc int 1155 1.57 apb f_group(PLAN *plan, FTSENT *entry) 1156 1.1 cgd { 1157 1.33 enami 1158 1.73 dholland COMPARE(entry->fts_statp->st_gid, plan->g_data); 1159 1.1 cgd } 1160 1.57 apb 1161 1.1 cgd PLAN * 1162 1.74 pgoyette c_group(char ***argvp, int isok, char *opt) 1163 1.1 cgd { 1164 1.24 christos char *gname = **argvp; 1165 1.1 cgd PLAN *new; 1166 1.1 cgd struct group *g; 1167 1.1 cgd gid_t gid; 1168 1.57 apb 1169 1.24 christos (*argvp)++; 1170 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1171 1.1 cgd 1172 1.73 dholland new = palloc(N_GROUP, f_group); 1173 1.1 cgd g = getgrnam(gname); 1174 1.1 cgd if (g == NULL) { 1175 1.73 dholland if (atoi(gname) == 0 && gname[0] != '0' && 1176 1.73 dholland strcmp(gname, "+0") && strcmp(gname, "-0")) 1177 1.75 pgoyette errx(1, "%s: %s: no such group", opt, gname); 1178 1.73 dholland gid = find_parsenum(new, "-group", gname, NULL); 1179 1.73 dholland 1180 1.73 dholland } else { 1181 1.73 dholland new->flags = F_EQUAL; 1182 1.1 cgd gid = g->gr_gid; 1183 1.73 dholland } 1184 1.57 apb 1185 1.1 cgd new->g_data = gid; 1186 1.10 jtc return (new); 1187 1.1 cgd } 1188 1.1 cgd 1189 1.1 cgd /* 1190 1.1 cgd * -inum n functions -- 1191 1.1 cgd * 1192 1.1 cgd * True if the file has inode # n. 1193 1.1 cgd */ 1194 1.10 jtc int 1195 1.57 apb f_inum(PLAN *plan, FTSENT *entry) 1196 1.1 cgd { 1197 1.33 enami 1198 1.7 deraadt COMPARE(entry->fts_statp->st_ino, plan->i_data); 1199 1.1 cgd } 1200 1.57 apb 1201 1.1 cgd PLAN * 1202 1.74 pgoyette c_inum(char ***argvp, int isok, char *opt) 1203 1.1 cgd { 1204 1.24 christos char *arg = **argvp; 1205 1.1 cgd PLAN *new; 1206 1.57 apb 1207 1.24 christos (*argvp)++; 1208 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1209 1.57 apb 1210 1.1 cgd new = palloc(N_INUM, f_inum); 1211 1.74 pgoyette new->i_data = find_parsenum(new, opt, arg, NULL); 1212 1.10 jtc return (new); 1213 1.1 cgd } 1214 1.57 apb 1215 1.1 cgd /* 1216 1.1 cgd * -links n functions -- 1217 1.1 cgd * 1218 1.1 cgd * True if the file has n links. 1219 1.1 cgd */ 1220 1.10 jtc int 1221 1.57 apb f_links(PLAN *plan, FTSENT *entry) 1222 1.1 cgd { 1223 1.33 enami 1224 1.7 deraadt COMPARE(entry->fts_statp->st_nlink, plan->l_data); 1225 1.1 cgd } 1226 1.57 apb 1227 1.1 cgd PLAN * 1228 1.74 pgoyette c_links(char ***argvp, int isok, char *opt) 1229 1.1 cgd { 1230 1.24 christos char *arg = **argvp; 1231 1.1 cgd PLAN *new; 1232 1.57 apb 1233 1.24 christos (*argvp)++; 1234 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1235 1.57 apb 1236 1.1 cgd new = palloc(N_LINKS, f_links); 1237 1.74 pgoyette new->l_data = (nlink_t)find_parsenum(new, opt, arg, NULL); 1238 1.10 jtc return (new); 1239 1.1 cgd } 1240 1.57 apb 1241 1.1 cgd /* 1242 1.1 cgd * -ls functions -- 1243 1.1 cgd * 1244 1.1 cgd * Always true - prints the current entry to stdout in "ls" format. 1245 1.1 cgd */ 1246 1.10 jtc int 1247 1.57 apb f_ls(PLAN *plan, FTSENT *entry) 1248 1.1 cgd { 1249 1.33 enami 1250 1.7 deraadt printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 1251 1.10 jtc return (1); 1252 1.1 cgd } 1253 1.57 apb 1254 1.1 cgd PLAN * 1255 1.74 pgoyette c_ls(char ***argvp, int isok, char *opt) 1256 1.1 cgd { 1257 1.33 enami 1258 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1259 1.1 cgd isoutput = 1; 1260 1.57 apb 1261 1.10 jtc return (palloc(N_LS, f_ls)); 1262 1.10 jtc } 1263 1.10 jtc 1264 1.41 provos /* 1265 1.41 provos * - maxdepth n functions -- 1266 1.41 provos * 1267 1.41 provos * True if the current search depth is less than or equal to the 1268 1.41 provos * maximum depth specified 1269 1.41 provos */ 1270 1.41 provos int 1271 1.57 apb f_maxdepth(PLAN *plan, FTSENT *entry) 1272 1.41 provos { 1273 1.41 provos extern FTS *tree; 1274 1.41 provos 1275 1.41 provos if (entry->fts_level >= plan->max_data) 1276 1.41 provos fts_set(tree, entry, FTS_SKIP); 1277 1.41 provos return (entry->fts_level <= plan->max_data); 1278 1.41 provos } 1279 1.41 provos 1280 1.41 provos PLAN * 1281 1.74 pgoyette c_maxdepth(char ***argvp, int isok, char *opt) 1282 1.41 provos { 1283 1.41 provos char *arg = **argvp; 1284 1.41 provos PLAN *new; 1285 1.41 provos 1286 1.41 provos (*argvp)++; 1287 1.41 provos new = palloc(N_MAXDEPTH, f_maxdepth); 1288 1.41 provos new->max_data = atoi(arg); 1289 1.41 provos return (new); 1290 1.41 provos } 1291 1.41 provos 1292 1.41 provos /* 1293 1.41 provos * - mindepth n functions -- 1294 1.41 provos * 1295 1.41 provos * True if the current search depth is greater than or equal to the 1296 1.41 provos * minimum depth specified 1297 1.41 provos */ 1298 1.41 provos int 1299 1.57 apb f_mindepth(PLAN *plan, FTSENT *entry) 1300 1.41 provos { 1301 1.41 provos return (entry->fts_level >= plan->min_data); 1302 1.41 provos } 1303 1.41 provos 1304 1.41 provos PLAN * 1305 1.74 pgoyette c_mindepth(char ***argvp, int isok, char *opt) 1306 1.41 provos { 1307 1.41 provos char *arg = **argvp; 1308 1.41 provos PLAN *new; 1309 1.41 provos 1310 1.41 provos (*argvp)++; 1311 1.41 provos new = palloc(N_MINDEPTH, f_mindepth); 1312 1.41 provos new->min_data = atoi(arg); 1313 1.41 provos return (new); 1314 1.41 provos } 1315 1.72 uebayasi 1316 1.29 simonb /* 1317 1.29 simonb * -mmin n functions -- 1318 1.29 simonb * 1319 1.29 simonb * True if the difference between the file modification time and the 1320 1.29 simonb * current time is n 24 hour periods. 1321 1.29 simonb */ 1322 1.29 simonb int 1323 1.57 apb f_mmin(PLAN *plan, FTSENT *entry) 1324 1.29 simonb { 1325 1.29 simonb COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) / 1326 1.29 simonb SECSPERMIN, plan->t_data); 1327 1.29 simonb } 1328 1.57 apb 1329 1.29 simonb PLAN * 1330 1.74 pgoyette c_mmin(char ***argvp, int isok, char *opt) 1331 1.29 simonb { 1332 1.29 simonb char *arg = **argvp; 1333 1.29 simonb PLAN *new; 1334 1.29 simonb 1335 1.29 simonb (*argvp)++; 1336 1.29 simonb ftsoptions &= ~FTS_NOSTAT; 1337 1.29 simonb 1338 1.29 simonb new = palloc(N_MMIN, f_mmin); 1339 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 1340 1.29 simonb TIME_CORRECT(new, N_MMIN); 1341 1.29 simonb return (new); 1342 1.29 simonb } 1343 1.72 uebayasi 1344 1.10 jtc /* 1345 1.10 jtc * -mtime n functions -- 1346 1.10 jtc * 1347 1.10 jtc * True if the difference between the file modification time and the 1348 1.10 jtc * current time is n 24 hour periods. 1349 1.10 jtc */ 1350 1.10 jtc int 1351 1.57 apb f_mtime(PLAN *plan, FTSENT *entry) 1352 1.10 jtc { 1353 1.10 jtc COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / 1354 1.10 jtc SECSPERDAY, plan->t_data); 1355 1.10 jtc } 1356 1.57 apb 1357 1.10 jtc PLAN * 1358 1.74 pgoyette c_mtime(char ***argvp, int isok, char *opt) 1359 1.10 jtc { 1360 1.24 christos char *arg = **argvp; 1361 1.10 jtc PLAN *new; 1362 1.10 jtc 1363 1.24 christos (*argvp)++; 1364 1.10 jtc ftsoptions &= ~FTS_NOSTAT; 1365 1.10 jtc 1366 1.10 jtc new = palloc(N_MTIME, f_mtime); 1367 1.74 pgoyette new->t_data = find_parsenum(new, opt, arg, NULL); 1368 1.10 jtc TIME_CORRECT(new, N_MTIME); 1369 1.10 jtc return (new); 1370 1.1 cgd } 1371 1.1 cgd 1372 1.1 cgd /* 1373 1.1 cgd * -name functions -- 1374 1.1 cgd * 1375 1.1 cgd * True if the basename of the filename being examined 1376 1.1 cgd * matches pattern using Pattern Matching Notation S3.14 1377 1.1 cgd */ 1378 1.10 jtc int 1379 1.57 apb f_name(PLAN *plan, FTSENT *entry) 1380 1.1 cgd { 1381 1.33 enami 1382 1.10 jtc return (!fnmatch(plan->c_data, entry->fts_name, 0)); 1383 1.1 cgd } 1384 1.57 apb 1385 1.1 cgd PLAN * 1386 1.74 pgoyette c_name(char ***argvp, int isok, char *opt) 1387 1.1 cgd { 1388 1.24 christos char *pattern = **argvp; 1389 1.1 cgd PLAN *new; 1390 1.1 cgd 1391 1.24 christos (*argvp)++; 1392 1.1 cgd new = palloc(N_NAME, f_name); 1393 1.45 provos new->c_data = pattern; 1394 1.45 provos return (new); 1395 1.45 provos } 1396 1.57 apb 1397 1.45 provos /* 1398 1.45 provos * -iname functions -- 1399 1.45 provos * 1400 1.45 provos * Similar to -name, but does case insensitive matching 1401 1.57 apb * 1402 1.45 provos */ 1403 1.45 provos int 1404 1.57 apb f_iname(PLAN *plan, FTSENT *entry) 1405 1.45 provos { 1406 1.45 provos return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD)); 1407 1.45 provos } 1408 1.57 apb 1409 1.45 provos PLAN * 1410 1.74 pgoyette c_iname(char ***argvp, int isok, char *opt) 1411 1.45 provos { 1412 1.45 provos char *pattern = **argvp; 1413 1.45 provos PLAN *new; 1414 1.45 provos 1415 1.45 provos (*argvp)++; 1416 1.45 provos new = palloc(N_INAME, f_iname); 1417 1.1 cgd new->c_data = pattern; 1418 1.10 jtc return (new); 1419 1.1 cgd } 1420 1.57 apb 1421 1.1 cgd /* 1422 1.1 cgd * -newer file functions -- 1423 1.1 cgd * 1424 1.1 cgd * True if the current file has been modified more recently 1425 1.40 kleink * than the modification time of the file named by the pathname 1426 1.1 cgd * file. 1427 1.1 cgd */ 1428 1.10 jtc int 1429 1.57 apb f_newer(PLAN *plan, FTSENT *entry) 1430 1.1 cgd { 1431 1.33 enami 1432 1.72 uebayasi return timespeccmp(&entry->fts_statp->st_mtim, &plan->ts_data, >); 1433 1.1 cgd } 1434 1.57 apb 1435 1.1 cgd PLAN * 1436 1.74 pgoyette c_newer(char ***argvp, int isok, char *opt) 1437 1.1 cgd { 1438 1.24 christos char *filename = **argvp; 1439 1.1 cgd PLAN *new; 1440 1.1 cgd struct stat sb; 1441 1.57 apb 1442 1.24 christos (*argvp)++; 1443 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1444 1.1 cgd 1445 1.1 cgd if (stat(filename, &sb)) 1446 1.74 pgoyette err(1, "%s: %s", opt, filename); 1447 1.1 cgd new = palloc(N_NEWER, f_newer); 1448 1.72 uebayasi new->ts_data = sb.st_mtim; 1449 1.10 jtc return (new); 1450 1.1 cgd } 1451 1.57 apb 1452 1.1 cgd /* 1453 1.1 cgd * -nogroup functions -- 1454 1.1 cgd * 1455 1.1 cgd * True if file belongs to a user ID for which the equivalent 1456 1.1 cgd * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 1457 1.1 cgd */ 1458 1.10 jtc int 1459 1.57 apb f_nogroup(PLAN *plan, FTSENT *entry) 1460 1.1 cgd { 1461 1.22 mrg 1462 1.12 andrew return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 1463 1.1 cgd } 1464 1.57 apb 1465 1.1 cgd PLAN * 1466 1.74 pgoyette c_nogroup(char ***argvp, int isok, char *opt) 1467 1.1 cgd { 1468 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1469 1.1 cgd 1470 1.10 jtc return (palloc(N_NOGROUP, f_nogroup)); 1471 1.1 cgd } 1472 1.57 apb 1473 1.1 cgd /* 1474 1.1 cgd * -nouser functions -- 1475 1.1 cgd * 1476 1.1 cgd * True if file belongs to a user ID for which the equivalent 1477 1.1 cgd * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 1478 1.1 cgd */ 1479 1.10 jtc int 1480 1.57 apb f_nouser(PLAN *plan, FTSENT *entry) 1481 1.1 cgd { 1482 1.22 mrg 1483 1.12 andrew return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 1484 1.1 cgd } 1485 1.57 apb 1486 1.1 cgd PLAN * 1487 1.74 pgoyette c_nouser(char ***argvp, int isok, char *opt) 1488 1.1 cgd { 1489 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1490 1.1 cgd 1491 1.10 jtc return (palloc(N_NOUSER, f_nouser)); 1492 1.10 jtc } 1493 1.57 apb 1494 1.10 jtc /* 1495 1.10 jtc * -path functions -- 1496 1.10 jtc * 1497 1.10 jtc * True if the path of the filename being examined 1498 1.10 jtc * matches pattern using Pattern Matching Notation S3.14 1499 1.10 jtc */ 1500 1.10 jtc int 1501 1.57 apb f_path(PLAN *plan, FTSENT *entry) 1502 1.10 jtc { 1503 1.33 enami 1504 1.10 jtc return (!fnmatch(plan->c_data, entry->fts_path, 0)); 1505 1.10 jtc } 1506 1.57 apb 1507 1.10 jtc PLAN * 1508 1.74 pgoyette c_path(char ***argvp, int isok, char *opt) 1509 1.10 jtc { 1510 1.24 christos char *pattern = **argvp; 1511 1.10 jtc PLAN *new; 1512 1.10 jtc 1513 1.24 christos (*argvp)++; 1514 1.10 jtc new = palloc(N_NAME, f_path); 1515 1.10 jtc new->c_data = pattern; 1516 1.10 jtc return (new); 1517 1.1 cgd } 1518 1.57 apb 1519 1.1 cgd /* 1520 1.1 cgd * -perm functions -- 1521 1.1 cgd * 1522 1.1 cgd * The mode argument is used to represent file mode bits. If it starts 1523 1.1 cgd * with a leading digit, it's treated as an octal mode, otherwise as a 1524 1.1 cgd * symbolic mode. 1525 1.1 cgd */ 1526 1.10 jtc int 1527 1.57 apb f_perm(PLAN *plan, FTSENT *entry) 1528 1.1 cgd { 1529 1.1 cgd mode_t mode; 1530 1.1 cgd 1531 1.7 deraadt mode = entry->fts_statp->st_mode & 1532 1.1 cgd (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 1533 1.10 jtc if (plan->flags == F_ATLEAST) 1534 1.10 jtc return ((plan->m_data | mode) == mode); 1535 1.1 cgd else 1536 1.10 jtc return (mode == plan->m_data); 1537 1.1 cgd /* NOTREACHED */ 1538 1.1 cgd } 1539 1.57 apb 1540 1.1 cgd PLAN * 1541 1.74 pgoyette c_perm(char ***argvp, int isok, char *opt) 1542 1.1 cgd { 1543 1.24 christos char *perm = **argvp; 1544 1.1 cgd PLAN *new; 1545 1.1 cgd mode_t *set; 1546 1.1 cgd 1547 1.24 christos (*argvp)++; 1548 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1549 1.1 cgd 1550 1.1 cgd new = palloc(N_PERM, f_perm); 1551 1.1 cgd 1552 1.1 cgd if (*perm == '-') { 1553 1.10 jtc new->flags = F_ATLEAST; 1554 1.1 cgd ++perm; 1555 1.1 cgd } 1556 1.1 cgd 1557 1.1 cgd if ((set = setmode(perm)) == NULL) 1558 1.74 pgoyette err(1, "%s: Cannot set file mode `%s'", opt, perm); 1559 1.1 cgd 1560 1.1 cgd new->m_data = getmode(set, 0); 1561 1.34 enami free(set); 1562 1.10 jtc return (new); 1563 1.1 cgd } 1564 1.57 apb 1565 1.1 cgd /* 1566 1.1 cgd * -print functions -- 1567 1.1 cgd * 1568 1.81 andvar * Always true, causes the current pathname to be written to 1569 1.1 cgd * standard output. 1570 1.1 cgd */ 1571 1.10 jtc int 1572 1.57 apb f_print(PLAN *plan, FTSENT *entry) 1573 1.1 cgd { 1574 1.33 enami 1575 1.1 cgd (void)printf("%s\n", entry->fts_path); 1576 1.22 mrg return (1); 1577 1.1 cgd } 1578 1.9 jtc 1579 1.21 lukem int 1580 1.57 apb f_print0(PLAN *plan, FTSENT *entry) 1581 1.9 jtc { 1582 1.33 enami 1583 1.9 jtc (void)fputs(entry->fts_path, stdout); 1584 1.9 jtc (void)fputc('\0', stdout); 1585 1.22 mrg return (1); 1586 1.9 jtc } 1587 1.28 lukem 1588 1.28 lukem int 1589 1.57 apb f_printx(PLAN *plan, FTSENT *entry) 1590 1.28 lukem { 1591 1.28 lukem char *cp; 1592 1.28 lukem 1593 1.28 lukem for (cp = entry->fts_path; *cp; cp++) { 1594 1.28 lukem if (*cp == '\'' || *cp == '\"' || *cp == ' ' || 1595 1.42 matt *cp == '$' || *cp == '`' || 1596 1.28 lukem *cp == '\t' || *cp == '\n' || *cp == '\\') 1597 1.28 lukem fputc('\\', stdout); 1598 1.28 lukem 1599 1.28 lukem fputc(*cp, stdout); 1600 1.28 lukem } 1601 1.28 lukem 1602 1.28 lukem fputc('\n', stdout); 1603 1.33 enami return (1); 1604 1.28 lukem } 1605 1.57 apb 1606 1.1 cgd PLAN * 1607 1.74 pgoyette c_print(char ***argvp, int isok, char *opt) 1608 1.1 cgd { 1609 1.33 enami 1610 1.1 cgd isoutput = 1; 1611 1.1 cgd 1612 1.22 mrg return (palloc(N_PRINT, f_print)); 1613 1.9 jtc } 1614 1.9 jtc 1615 1.9 jtc PLAN * 1616 1.74 pgoyette c_print0(char ***argvp, int isok, char *opt) 1617 1.9 jtc { 1618 1.33 enami 1619 1.9 jtc isoutput = 1; 1620 1.9 jtc 1621 1.22 mrg return (palloc(N_PRINT0, f_print0)); 1622 1.28 lukem } 1623 1.28 lukem 1624 1.28 lukem PLAN * 1625 1.74 pgoyette c_printx(char ***argvp, int isok, char *opt) 1626 1.28 lukem { 1627 1.33 enami 1628 1.28 lukem isoutput = 1; 1629 1.28 lukem 1630 1.33 enami return (palloc(N_PRINTX, f_printx)); 1631 1.1 cgd } 1632 1.57 apb 1633 1.1 cgd /* 1634 1.1 cgd * -prune functions -- 1635 1.1 cgd * 1636 1.1 cgd * Prune a portion of the hierarchy. 1637 1.1 cgd */ 1638 1.10 jtc int 1639 1.57 apb f_prune(PLAN *plan, FTSENT *entry) 1640 1.1 cgd { 1641 1.1 cgd if (fts_set(tree, entry, FTS_SKIP)) 1642 1.10 jtc err(1, "%s", entry->fts_path); 1643 1.10 jtc return (1); 1644 1.1 cgd } 1645 1.57 apb 1646 1.1 cgd PLAN * 1647 1.74 pgoyette c_prune(char ***argvp, int isok, char *opt) 1648 1.1 cgd { 1649 1.33 enami 1650 1.10 jtc return (palloc(N_PRUNE, f_prune)); 1651 1.1 cgd } 1652 1.31 cgd 1653 1.31 cgd /* 1654 1.31 cgd * -regex regexp (and related) functions -- 1655 1.31 cgd * 1656 1.31 cgd * True if the complete file path matches the regular expression regexp. 1657 1.31 cgd * For -regex, regexp is a case-sensitive (basic) regular expression. 1658 1.31 cgd * For -iregex, regexp is a case-insensitive (basic) regular expression. 1659 1.31 cgd */ 1660 1.31 cgd int 1661 1.57 apb f_regex(PLAN *plan, FTSENT *entry) 1662 1.31 cgd { 1663 1.31 cgd 1664 1.31 cgd return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0); 1665 1.31 cgd } 1666 1.57 apb 1667 1.31 cgd static PLAN * 1668 1.64 daniel c_regex_common(char ***argvp, int isok, enum ntype type, bool icase) 1669 1.31 cgd { 1670 1.31 cgd char errbuf[LINE_MAX]; 1671 1.31 cgd regex_t reg; 1672 1.31 cgd char *regexp = **argvp; 1673 1.31 cgd char *lineregexp; 1674 1.31 cgd PLAN *new; 1675 1.31 cgd int rv; 1676 1.59 christos size_t len; 1677 1.31 cgd 1678 1.31 cgd (*argvp)++; 1679 1.31 cgd 1680 1.59 christos len = strlen(regexp) + 1 + 6; 1681 1.59 christos lineregexp = malloc(len); /* max needed */ 1682 1.59 christos if (lineregexp == NULL) 1683 1.59 christos err(1, NULL); 1684 1.59 christos snprintf(lineregexp, len, "^%s(%s%s)$", 1685 1.31 cgd (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp, 1686 1.31 cgd (regcomp_flags & REG_EXTENDED) ? "" : "\\"); 1687 1.64 daniel rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags| 1688 1.64 daniel (icase ? REG_ICASE : 0)); 1689 1.59 christos free(lineregexp); 1690 1.31 cgd if (rv != 0) { 1691 1.31 cgd regerror(rv, ®, errbuf, sizeof errbuf); 1692 1.31 cgd errx(1, "regexp %s: %s", regexp, errbuf); 1693 1.31 cgd } 1694 1.57 apb 1695 1.31 cgd new = palloc(type, f_regex); 1696 1.31 cgd new->regexp_data = reg; 1697 1.31 cgd return (new); 1698 1.31 cgd } 1699 1.31 cgd 1700 1.31 cgd PLAN * 1701 1.74 pgoyette c_regex(char ***argvp, int isok, char *opt) 1702 1.31 cgd { 1703 1.31 cgd 1704 1.64 daniel return (c_regex_common(argvp, isok, N_REGEX, false)); 1705 1.31 cgd } 1706 1.31 cgd 1707 1.31 cgd PLAN * 1708 1.74 pgoyette c_iregex(char ***argvp, int isok, char *opt) 1709 1.31 cgd { 1710 1.31 cgd 1711 1.64 daniel return (c_regex_common(argvp, isok, N_IREGEX, true)); 1712 1.31 cgd } 1713 1.31 cgd 1714 1.1 cgd /* 1715 1.74 pgoyette * -since "timestamp" functions -- 1716 1.74 pgoyette * 1717 1.74 pgoyette * True if the file modification time is greater than the timestamp value 1718 1.74 pgoyette */ 1719 1.74 pgoyette int 1720 1.74 pgoyette f_since(PLAN *plan, FTSENT *entry) 1721 1.74 pgoyette { 1722 1.74 pgoyette COMPARE(entry->fts_statp->st_mtime, plan->t_data); 1723 1.74 pgoyette } 1724 1.74 pgoyette 1725 1.74 pgoyette PLAN * 1726 1.74 pgoyette c_since(char ***argvp, int isok, char *opt) 1727 1.74 pgoyette { 1728 1.74 pgoyette char *arg = **argvp; 1729 1.74 pgoyette PLAN *new; 1730 1.74 pgoyette 1731 1.74 pgoyette (*argvp)++; 1732 1.74 pgoyette ftsoptions &= ~FTS_NOSTAT; 1733 1.74 pgoyette 1734 1.74 pgoyette new = palloc(N_SINCE, f_since); 1735 1.74 pgoyette new->t_data = find_parsedate(new, opt, arg); 1736 1.74 pgoyette new->flags = F_GREATER; 1737 1.74 pgoyette return (new); 1738 1.74 pgoyette } 1739 1.74 pgoyette 1740 1.74 pgoyette /* 1741 1.1 cgd * -size n[c] functions -- 1742 1.1 cgd * 1743 1.1 cgd * True if the file size in bytes, divided by an implementation defined 1744 1.1 cgd * value and rounded up to the next integer, is n. If n is followed by 1745 1.1 cgd * a c, the size is in bytes. 1746 1.1 cgd */ 1747 1.1 cgd #define FIND_SIZE 512 1748 1.1 cgd static int divsize = 1; 1749 1.1 cgd 1750 1.10 jtc int 1751 1.57 apb f_size(PLAN *plan, FTSENT *entry) 1752 1.1 cgd { 1753 1.1 cgd off_t size; 1754 1.1 cgd 1755 1.7 deraadt size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1756 1.7 deraadt FIND_SIZE : entry->fts_statp->st_size; 1757 1.1 cgd COMPARE(size, plan->o_data); 1758 1.1 cgd } 1759 1.57 apb 1760 1.1 cgd PLAN * 1761 1.74 pgoyette c_size(char ***argvp, int isok, char *opt) 1762 1.1 cgd { 1763 1.24 christos char *arg = **argvp; 1764 1.1 cgd PLAN *new; 1765 1.1 cgd char endch; 1766 1.57 apb 1767 1.24 christos (*argvp)++; 1768 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1769 1.1 cgd 1770 1.1 cgd new = palloc(N_SIZE, f_size); 1771 1.8 cgd endch = 'c'; 1772 1.74 pgoyette new->o_data = find_parsenum(new, opt, arg, &endch); 1773 1.1 cgd if (endch == 'c') 1774 1.1 cgd divsize = 0; 1775 1.10 jtc return (new); 1776 1.1 cgd } 1777 1.57 apb 1778 1.1 cgd /* 1779 1.1 cgd * -type c functions -- 1780 1.1 cgd * 1781 1.22 mrg * True if the type of the file is c, where c is b, c, d, p, f or w 1782 1.22 mrg * for block special file, character special file, directory, FIFO, 1783 1.22 mrg * regular file or whiteout respectively. 1784 1.1 cgd */ 1785 1.10 jtc int 1786 1.57 apb f_type(PLAN *plan, FTSENT *entry) 1787 1.1 cgd { 1788 1.33 enami 1789 1.10 jtc return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1790 1.1 cgd } 1791 1.57 apb 1792 1.1 cgd PLAN * 1793 1.74 pgoyette c_type(char ***argvp, int isok, char *opt) 1794 1.1 cgd { 1795 1.24 christos char *typestring = **argvp; 1796 1.1 cgd PLAN *new; 1797 1.25 wsanchez mode_t mask = (mode_t)0; 1798 1.57 apb 1799 1.24 christos (*argvp)++; 1800 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1801 1.1 cgd 1802 1.1 cgd switch (typestring[0]) { 1803 1.1 cgd case 'b': 1804 1.1 cgd mask = S_IFBLK; 1805 1.1 cgd break; 1806 1.1 cgd case 'c': 1807 1.1 cgd mask = S_IFCHR; 1808 1.1 cgd break; 1809 1.1 cgd case 'd': 1810 1.1 cgd mask = S_IFDIR; 1811 1.1 cgd break; 1812 1.1 cgd case 'f': 1813 1.1 cgd mask = S_IFREG; 1814 1.1 cgd break; 1815 1.1 cgd case 'l': 1816 1.1 cgd mask = S_IFLNK; 1817 1.1 cgd break; 1818 1.1 cgd case 'p': 1819 1.1 cgd mask = S_IFIFO; 1820 1.1 cgd break; 1821 1.1 cgd case 's': 1822 1.1 cgd mask = S_IFSOCK; 1823 1.1 cgd break; 1824 1.49 atatat #ifdef S_IFWHT 1825 1.49 atatat case 'W': 1826 1.22 mrg case 'w': 1827 1.22 mrg mask = S_IFWHT; 1828 1.49 atatat #ifdef FTS_WHITEOUT 1829 1.22 mrg ftsoptions |= FTS_WHITEOUT; 1830 1.49 atatat #endif 1831 1.22 mrg break; 1832 1.49 atatat #endif /* S_IFWHT */ 1833 1.1 cgd default: 1834 1.74 pgoyette errx(1, "%s: %s: unknown type", opt, typestring); 1835 1.1 cgd } 1836 1.57 apb 1837 1.1 cgd new = palloc(N_TYPE, f_type); 1838 1.1 cgd new->m_data = mask; 1839 1.10 jtc return (new); 1840 1.1 cgd } 1841 1.57 apb 1842 1.1 cgd /* 1843 1.1 cgd * -user uname functions -- 1844 1.1 cgd * 1845 1.1 cgd * True if the file belongs to the user uname. If uname is numeric and 1846 1.1 cgd * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1847 1.1 cgd * return a valid user name, uname is taken as a user ID. 1848 1.1 cgd */ 1849 1.10 jtc int 1850 1.57 apb f_user(PLAN *plan, FTSENT *entry) 1851 1.1 cgd { 1852 1.33 enami 1853 1.43 jhawk COMPARE(entry->fts_statp->st_uid, plan->u_data); 1854 1.1 cgd } 1855 1.57 apb 1856 1.1 cgd PLAN * 1857 1.74 pgoyette c_user(char ***argvp, int isok, char *opt) 1858 1.1 cgd { 1859 1.24 christos char *username = **argvp; 1860 1.1 cgd PLAN *new; 1861 1.1 cgd struct passwd *p; 1862 1.1 cgd uid_t uid; 1863 1.57 apb 1864 1.24 christos (*argvp)++; 1865 1.1 cgd ftsoptions &= ~FTS_NOSTAT; 1866 1.1 cgd 1867 1.43 jhawk new = palloc(N_USER, f_user); 1868 1.1 cgd p = getpwnam(username); 1869 1.1 cgd if (p == NULL) { 1870 1.43 jhawk if (atoi(username) == 0 && username[0] != '0' && 1871 1.43 jhawk strcmp(username, "+0") && strcmp(username, "-0")) 1872 1.74 pgoyette errx(1, "%s: %s: no such user", opt, username); 1873 1.74 pgoyette uid = find_parsenum(new, opt, username, NULL); 1874 1.43 jhawk 1875 1.43 jhawk } else { 1876 1.56 apb new->flags = F_EQUAL; 1877 1.1 cgd uid = p->pw_uid; 1878 1.43 jhawk } 1879 1.1 cgd 1880 1.1 cgd new->u_data = uid; 1881 1.10 jtc return (new); 1882 1.1 cgd } 1883 1.57 apb 1884 1.1 cgd /* 1885 1.1 cgd * -xdev functions -- 1886 1.1 cgd * 1887 1.61 christos * Always true, causes find not to descend past directories that have a 1888 1.1 cgd * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1889 1.1 cgd */ 1890 1.1 cgd PLAN * 1891 1.74 pgoyette c_xdev(char ***argvp, int isok, char *opt) 1892 1.1 cgd { 1893 1.1 cgd ftsoptions |= FTS_XDEV; 1894 1.1 cgd 1895 1.10 jtc return (palloc(N_XDEV, f_always_true)); 1896 1.1 cgd } 1897 1.1 cgd 1898 1.1 cgd /* 1899 1.1 cgd * ( expression ) functions -- 1900 1.1 cgd * 1901 1.1 cgd * True if expression is true. 1902 1.1 cgd */ 1903 1.10 jtc int 1904 1.57 apb f_expr(PLAN *plan, FTSENT *entry) 1905 1.1 cgd { 1906 1.21 lukem PLAN *p; 1907 1.21 lukem int state; 1908 1.1 cgd 1909 1.21 lukem state = 0; 1910 1.1 cgd for (p = plan->p_data[0]; 1911 1.1 cgd p && (state = (p->eval)(p, entry)); p = p->next); 1912 1.10 jtc return (state); 1913 1.1 cgd } 1914 1.57 apb 1915 1.1 cgd /* 1916 1.1 cgd * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1917 1.1 cgd * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1918 1.1 cgd * to a N_EXPR node containing the expression and the ')' node is discarded. 1919 1.1 cgd */ 1920 1.1 cgd PLAN * 1921 1.74 pgoyette c_openparen(char ***argvp, int isok, char *opt) 1922 1.1 cgd { 1923 1.33 enami 1924 1.57 apb return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1)); 1925 1.1 cgd } 1926 1.57 apb 1927 1.1 cgd PLAN * 1928 1.74 pgoyette c_closeparen(char ***argvp, int isok, char *opt) 1929 1.1 cgd { 1930 1.33 enami 1931 1.57 apb return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1)); 1932 1.1 cgd } 1933 1.57 apb 1934 1.1 cgd /* 1935 1.1 cgd * ! expression functions -- 1936 1.1 cgd * 1937 1.1 cgd * Negation of a primary; the unary NOT operator. 1938 1.1 cgd */ 1939 1.10 jtc int 1940 1.57 apb f_not(PLAN *plan, FTSENT *entry) 1941 1.1 cgd { 1942 1.21 lukem PLAN *p; 1943 1.21 lukem int state; 1944 1.1 cgd 1945 1.21 lukem state = 0; 1946 1.1 cgd for (p = plan->p_data[0]; 1947 1.1 cgd p && (state = (p->eval)(p, entry)); p = p->next); 1948 1.10 jtc return (!state); 1949 1.1 cgd } 1950 1.57 apb 1951 1.1 cgd PLAN * 1952 1.74 pgoyette c_not(char ***argvp, int isok, char *opt) 1953 1.1 cgd { 1954 1.33 enami 1955 1.10 jtc return (palloc(N_NOT, f_not)); 1956 1.1 cgd } 1957 1.57 apb 1958 1.1 cgd /* 1959 1.1 cgd * expression -o expression functions -- 1960 1.1 cgd * 1961 1.1 cgd * Alternation of primaries; the OR operator. The second expression is 1962 1.1 cgd * not evaluated if the first expression is true. 1963 1.1 cgd */ 1964 1.10 jtc int 1965 1.57 apb f_or(PLAN *plan, FTSENT *entry) 1966 1.1 cgd { 1967 1.21 lukem PLAN *p; 1968 1.21 lukem int state; 1969 1.1 cgd 1970 1.21 lukem state = 0; 1971 1.1 cgd for (p = plan->p_data[0]; 1972 1.1 cgd p && (state = (p->eval)(p, entry)); p = p->next); 1973 1.1 cgd 1974 1.1 cgd if (state) 1975 1.10 jtc return (1); 1976 1.1 cgd 1977 1.1 cgd for (p = plan->p_data[1]; 1978 1.1 cgd p && (state = (p->eval)(p, entry)); p = p->next); 1979 1.10 jtc return (state); 1980 1.1 cgd } 1981 1.1 cgd 1982 1.1 cgd PLAN * 1983 1.74 pgoyette c_or(char ***argvp, int isok, char *opt) 1984 1.1 cgd { 1985 1.33 enami 1986 1.10 jtc return (palloc(N_OR, f_or)); 1987 1.1 cgd } 1988 1.1 cgd 1989 1.24 christos PLAN * 1990 1.74 pgoyette c_null(char ***argvp, int isok, char *opt) 1991 1.24 christos { 1992 1.33 enami 1993 1.33 enami return (NULL); 1994 1.24 christos } 1995 1.24 christos 1996 1.56 apb 1997 1.56 apb /* 1998 1.56 apb * plan_cleanup -- 1999 1.56 apb * Check and see if the specified plan has any residual state, 2000 1.56 apb * and if so, clean it up as appropriate. 2001 1.56 apb * 2002 1.56 apb * At the moment, only N_EXEC has state. Two kinds: 1) 2003 1.56 apb * lists of files to feed to subprocesses 2) State on exit 2004 1.81 andvar * status of past subprocesses. 2005 1.56 apb */ 2006 1.56 apb /* ARGSUSED1 */ 2007 1.56 apb int 2008 1.57 apb plan_cleanup(PLAN *plan, void *arg) 2009 1.56 apb { 2010 1.56 apb if (plan->type==N_EXEC && plan->ep_narg) 2011 1.56 apb run_f_exec(plan); 2012 1.56 apb 2013 1.56 apb return plan->ep_rval; /* Passed save exit-status up chain */ 2014 1.56 apb } 2015 1.56 apb 2016 1.1 cgd static PLAN * 2017 1.57 apb palloc(enum ntype t, int (*f)(PLAN *, FTSENT *)) 2018 1.1 cgd { 2019 1.1 cgd PLAN *new; 2020 1.1 cgd 2021 1.22 mrg if ((new = malloc(sizeof(PLAN))) == NULL) 2022 1.32 drochner err(1, NULL); 2023 1.58 tacha memset(new, 0, sizeof(PLAN)); 2024 1.22 mrg new->type = t; 2025 1.22 mrg new->eval = f; 2026 1.22 mrg return (new); 2027 1.1 cgd } 2028