function.c revision 51037
1219820Sjeff/*- 2219820Sjeff * Copyright (c) 1990, 1993 3219820Sjeff * The Regents of the University of California. All rights reserved. 4219820Sjeff * 5219820Sjeff * This code is derived from software contributed to Berkeley by 6219820Sjeff * Cimarron D. Taylor of the University of California, Berkeley. 7219820Sjeff * 8219820Sjeff * Redistribution and use in source and binary forms, with or without 9219820Sjeff * modification, are permitted provided that the following conditions 10219820Sjeff * are met: 11219820Sjeff * 1. Redistributions of source code must retain the above copyright 12219820Sjeff * notice, this list of conditions and the following disclaimer. 13219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 14219820Sjeff * notice, this list of conditions and the following disclaimer in the 15219820Sjeff * documentation and/or other materials provided with the distribution. 16219820Sjeff * 3. All advertising materials mentioning features or use of this software 17219820Sjeff * must display the following acknowledgement: 18219820Sjeff * This product includes software developed by the University of 19219820Sjeff * California, Berkeley and its contributors. 20219820Sjeff * 4. Neither the name of the University nor the names of its contributors 21219820Sjeff * may be used to endorse or promote products derived from this software 22219820Sjeff * without specific prior written permission. 23219820Sjeff * 24219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27219820Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34219820Sjeff * SUCH DAMAGE. 35219820Sjeff */ 36324685Shselasky 37324685Shselasky#ifndef lint 38324685Shselaskystatic char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; 39219820Sjeff#endif /* not lint */ 40219820Sjeff 41219820Sjeff#include <sys/param.h> 42219820Sjeff#include <sys/ucred.h> 43219820Sjeff#include <sys/stat.h> 44219820Sjeff#include <sys/wait.h> 45219820Sjeff#include <sys/mount.h> 46219820Sjeff 47219820Sjeff#include <err.h> 48219820Sjeff#include <errno.h> 49219820Sjeff#include <fnmatch.h> 50219820Sjeff#include <fts.h> 51219820Sjeff#include <grp.h> 52219820Sjeff#include <pwd.h> 53219820Sjeff#include <stdio.h> 54219820Sjeff#include <stdlib.h> 55219820Sjeff#include <string.h> 56219820Sjeff#include <unistd.h> 57219820Sjeff 58219820Sjeff#include "find.h" 59219820Sjeff 60219820Sjeff#define COMPARE(a, b) { \ 61219820Sjeff switch (plan->flags) { \ 62219820Sjeff case F_EQUAL: \ 63219820Sjeff return (a == b); \ 64219820Sjeff case F_LESSTHAN: \ 65219820Sjeff return (a < b); \ 66219820Sjeff case F_GREATER: \ 67219820Sjeff return (a > b); \ 68219820Sjeff default: \ 69219820Sjeff abort(); \ 70219820Sjeff } \ 71219820Sjeff} 72219820Sjeff 73219820Sjeffstatic PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); 74219820Sjeff 75219820Sjeff/* 76219820Sjeff * find_parsenum -- 77219820Sjeff * Parse a string of the form [+-]# and return the value. 78219820Sjeff */ 79219820Sjeffstatic long long 80219820Sjefffind_parsenum(plan, option, vp, endch) 81219820Sjeff PLAN *plan; 82219820Sjeff char *option, *vp, *endch; 83219820Sjeff{ 84219820Sjeff long long value; 85219820Sjeff char *endchar, *str; /* Pointer to character ending conversion. */ 86219820Sjeff 87219820Sjeff /* Determine comparison from leading + or -. */ 88219820Sjeff str = vp; 89219820Sjeff switch (*str) { 90219820Sjeff case '+': 91219820Sjeff ++str; 92219820Sjeff plan->flags = F_GREATER; 93219820Sjeff break; 94219820Sjeff case '-': 95219820Sjeff ++str; 96219820Sjeff plan->flags = F_LESSTHAN; 97219820Sjeff break; 98219820Sjeff default: 99219820Sjeff plan->flags = F_EQUAL; 100219820Sjeff break; 101219820Sjeff } 102219820Sjeff 103219820Sjeff /* 104219820Sjeff * Convert the string with strtoq(). Note, if strtoq() returns zero 105219820Sjeff * and endchar points to the beginning of the string we know we have 106219820Sjeff * a syntax error. 107219820Sjeff */ 108219820Sjeff value = strtoq(str, &endchar, 10); 109219820Sjeff if (value == 0 && endchar == str) 110219820Sjeff errx(1, "%s: %s: illegal numeric value", option, vp); 111219820Sjeff if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 112219820Sjeff errx(1, "%s: %s: illegal trailing character", option, vp); 113219820Sjeff if (endch) 114219820Sjeff *endch = endchar[0]; 115219820Sjeff return (value); 116219820Sjeff} 117219820Sjeff 118219820Sjeff/* 119219820Sjeff * The value of n for the inode times (atime, ctime, and mtime) is a range, 120219820Sjeff * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 121219820Sjeff * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 122219820Sjeff * user wanted. Correct so that -1 is "less than 1". 123219820Sjeff */ 124219820Sjeff#define TIME_CORRECT(p, ttype) \ 125219820Sjeff if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 126219820Sjeff ++((p)->t_data); 127219820Sjeff 128219820Sjeff/* 129219820Sjeff * -amin n functions -- 130219820Sjeff * 131219820Sjeff * True if the difference between the file access time and the 132219820Sjeff * current time is n min periods. 133219820Sjeff */ 134219820Sjeffint 135219820Sjefff_amin(plan, entry) 136219820Sjeff PLAN *plan; 137219820Sjeff FTSENT *entry; 138219820Sjeff{ 139219820Sjeff extern time_t now; 140219820Sjeff 141219820Sjeff COMPARE((now - entry->fts_statp->st_atime + 142219820Sjeff 60 - 1) / 60, plan->t_data); 143219820Sjeff} 144219820Sjeff 145219820SjeffPLAN * 146219820Sjeffc_amin(arg) 147219820Sjeff char *arg; 148219820Sjeff{ 149219820Sjeff PLAN *new; 150219820Sjeff 151219820Sjeff ftsoptions &= ~FTS_NOSTAT; 152219820Sjeff 153219820Sjeff new = palloc(N_AMIN, f_amin); 154219820Sjeff new->t_data = find_parsenum(new, "-amin", arg, NULL); 155219820Sjeff TIME_CORRECT(new, N_AMIN); 156219820Sjeff return (new); 157219820Sjeff} 158219820Sjeff 159219820Sjeff 160219820Sjeff/* 161219820Sjeff * -atime n functions -- 162219820Sjeff * 163219820Sjeff * True if the difference between the file access time and the 164219820Sjeff * current time is n 24 hour periods. 165219820Sjeff */ 166219820Sjeffint 167219820Sjefff_atime(plan, entry) 168219820Sjeff PLAN *plan; 169219820Sjeff FTSENT *entry; 170219820Sjeff{ 171219820Sjeff extern time_t now; 172219820Sjeff 173219820Sjeff COMPARE((now - entry->fts_statp->st_atime + 174219820Sjeff 86400 - 1) / 86400, plan->t_data); 175219820Sjeff} 176219820Sjeff 177219820SjeffPLAN * 178219820Sjeffc_atime(arg) 179219820Sjeff char *arg; 180219820Sjeff{ 181219820Sjeff PLAN *new; 182219820Sjeff 183219820Sjeff ftsoptions &= ~FTS_NOSTAT; 184219820Sjeff 185219820Sjeff new = palloc(N_ATIME, f_atime); 186219820Sjeff new->t_data = find_parsenum(new, "-atime", arg, NULL); 187219820Sjeff TIME_CORRECT(new, N_ATIME); 188219820Sjeff return (new); 189219820Sjeff} 190219820Sjeff 191219820Sjeff 192219820Sjeff/* 193219820Sjeff * -cmin n functions -- 194219820Sjeff * 195219820Sjeff * True if the difference between the last change of file 196219820Sjeff * status information and the current time is n min periods. 197219820Sjeff */ 198219820Sjeffint 199219820Sjefff_cmin(plan, entry) 200219820Sjeff PLAN *plan; 201219820Sjeff FTSENT *entry; 202219820Sjeff{ 203219820Sjeff extern time_t now; 204219820Sjeff 205219820Sjeff COMPARE((now - entry->fts_statp->st_ctime + 206219820Sjeff 60 - 1) / 60, plan->t_data); 207219820Sjeff} 208219820Sjeff 209219820SjeffPLAN * 210219820Sjeffc_cmin(arg) 211219820Sjeff char *arg; 212219820Sjeff{ 213219820Sjeff PLAN *new; 214219820Sjeff 215219820Sjeff ftsoptions &= ~FTS_NOSTAT; 216219820Sjeff 217219820Sjeff new = palloc(N_CMIN, f_cmin); 218219820Sjeff new->t_data = find_parsenum(new, "-cmin", arg, NULL); 219219820Sjeff TIME_CORRECT(new, N_CMIN); 220219820Sjeff return (new); 221219820Sjeff} 222219820Sjeff 223219820Sjeff/* 224219820Sjeff * -ctime n functions -- 225219820Sjeff * 226219820Sjeff * True if the difference between the last change of file 227219820Sjeff * status information and the current time is n 24 hour periods. 228219820Sjeff */ 229219820Sjeffint 230219820Sjefff_ctime(plan, entry) 231219820Sjeff PLAN *plan; 232219820Sjeff FTSENT *entry; 233219820Sjeff{ 234219820Sjeff extern time_t now; 235219820Sjeff 236219820Sjeff COMPARE((now - entry->fts_statp->st_ctime + 237219820Sjeff 86400 - 1) / 86400, plan->t_data); 238219820Sjeff} 239219820Sjeff 240219820SjeffPLAN * 241219820Sjeffc_ctime(arg) 242219820Sjeff char *arg; 243219820Sjeff{ 244219820Sjeff PLAN *new; 245219820Sjeff 246219820Sjeff ftsoptions &= ~FTS_NOSTAT; 247219820Sjeff 248219820Sjeff new = palloc(N_CTIME, f_ctime); 249219820Sjeff new->t_data = find_parsenum(new, "-ctime", arg, NULL); 250219820Sjeff TIME_CORRECT(new, N_CTIME); 251219820Sjeff return (new); 252219820Sjeff} 253219820Sjeff 254219820Sjeff 255219820Sjeff/* 256219820Sjeff * -depth functions -- 257219820Sjeff * 258219820Sjeff * Always true, causes descent of the directory hierarchy to be done 259219820Sjeff * so that all entries in a directory are acted on before the directory 260219820Sjeff * itself. 261219820Sjeff */ 262219820Sjeffint 263219820Sjefff_always_true(plan, entry) 264219820Sjeff PLAN *plan; 265219820Sjeff FTSENT *entry; 266219820Sjeff{ 267219820Sjeff return (1); 268219820Sjeff} 269219820Sjeff 270219820SjeffPLAN * 271219820Sjeffc_depth() 272219820Sjeff{ 273219820Sjeff isdepth = 1; 274219820Sjeff 275219820Sjeff return (palloc(N_DEPTH, f_always_true)); 276219820Sjeff} 277219820Sjeff 278219820Sjeff/* 279219820Sjeff * [-exec | -ok] utility [arg ... ] ; functions -- 280219820Sjeff * 281219820Sjeff * True if the executed utility returns a zero value as exit status. 282219820Sjeff * The end of the primary expression is delimited by a semicolon. If 283219820Sjeff * "{}" occurs anywhere, it gets replaced by the current pathname. 284219820Sjeff * The current directory for the execution of utility is the same as 285219820Sjeff * the current directory when the find utility was started. 286219820Sjeff * 287219820Sjeff * The primary -ok is different in that it requests affirmation of the 288219820Sjeff * user before executing the utility. 289219820Sjeff */ 290219820Sjeffint 291219820Sjefff_exec(plan, entry) 292219820Sjeff register PLAN *plan; 293219820Sjeff FTSENT *entry; 294219820Sjeff{ 295219820Sjeff extern int dotfd; 296219820Sjeff register int cnt; 297219820Sjeff pid_t pid; 298219820Sjeff int status; 299219820Sjeff 300219820Sjeff for (cnt = 0; plan->e_argv[cnt]; ++cnt) 301219820Sjeff if (plan->e_len[cnt]) 302219820Sjeff brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 303219820Sjeff entry->fts_path, plan->e_len[cnt]); 304219820Sjeff 305219820Sjeff if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 306219820Sjeff return (0); 307219820Sjeff 308219820Sjeff /* make sure find output is interspersed correctly with subprocesses */ 309219820Sjeff fflush(stdout); 310219820Sjeff 311219820Sjeff switch (pid = fork()) { 312219820Sjeff case -1: 313219820Sjeff err(1, "fork"); 314219820Sjeff /* NOTREACHED */ 315219820Sjeff case 0: 316219820Sjeff if (fchdir(dotfd)) { 317219820Sjeff warn("chdir"); 318219820Sjeff _exit(1); 319219820Sjeff } 320219820Sjeff execvp(plan->e_argv[0], plan->e_argv); 321219820Sjeff warn("%s", plan->e_argv[0]); 322219820Sjeff _exit(1); 323219820Sjeff } 324219820Sjeff pid = waitpid(pid, &status, 0); 325219820Sjeff return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 326219820Sjeff} 327219820Sjeff 328219820Sjeff/* 329219820Sjeff * c_exec -- 330219820Sjeff * build three parallel arrays, one with pointers to the strings passed 331219820Sjeff * on the command line, one with (possibly duplicated) pointers to the 332219820Sjeff * argv array, and one with integer values that are lengths of the 333219820Sjeff * strings, but also flags meaning that the string has to be massaged. 334219820Sjeff */ 335219820SjeffPLAN * 336219820Sjeffc_exec(argvp, isok) 337219820Sjeff char ***argvp; 338219820Sjeff int isok; 339219820Sjeff{ 340219820Sjeff PLAN *new; /* node returned */ 341219820Sjeff register int cnt; 342219820Sjeff register char **argv, **ap, *p; 343219820Sjeff 344219820Sjeff isoutput = 1; 345219820Sjeff 346219820Sjeff new = palloc(N_EXEC, f_exec); 347219820Sjeff if (isok) 348219820Sjeff new->flags = F_NEEDOK; 349219820Sjeff 350219820Sjeff for (ap = argv = *argvp;; ++ap) { 351219820Sjeff if (!*ap) 352219820Sjeff errx(1, 353219820Sjeff "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 354219820Sjeff if (**ap == ';') 355219820Sjeff break; 356219820Sjeff } 357219820Sjeff 358219820Sjeff cnt = ap - *argvp + 1; 359219820Sjeff new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 360219820Sjeff new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 361219820Sjeff new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 362219820Sjeff 363219820Sjeff for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 364219820Sjeff new->e_orig[cnt] = *argv; 365219820Sjeff for (p = *argv; *p; ++p) 366219820Sjeff if (p[0] == '{' && p[1] == '}') { 367219820Sjeff new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 368219820Sjeff new->e_len[cnt] = MAXPATHLEN; 369219820Sjeff break; 370219820Sjeff } 371219820Sjeff if (!*p) { 372219820Sjeff new->e_argv[cnt] = *argv; 373219820Sjeff new->e_len[cnt] = 0; 374219820Sjeff } 375219820Sjeff } 376219820Sjeff new->e_argv[cnt] = new->e_orig[cnt] = NULL; 377219820Sjeff 378219820Sjeff *argvp = argv + 1; 379219820Sjeff return (new); 380219820Sjeff} 381219820Sjeff 382219820Sjeff/* 383219820Sjeff * -execdir utility [arg ... ] ; functions -- 384219820Sjeff * 385219820Sjeff * True if the executed utility returns a zero value as exit status. 386219820Sjeff * The end of the primary expression is delimited by a semicolon. If 387219820Sjeff * "{}" occurs anywhere, it gets replaced by the unqualified pathname. 388219820Sjeff * The current directory for the execution of utility is the same as 389219820Sjeff * the directory where the file lives. 390219820Sjeff */ 391219820Sjeffint 392219820Sjefff_execdir(plan, entry) 393219820Sjeff register PLAN *plan; 394219820Sjeff FTSENT *entry; 395219820Sjeff{ 396219820Sjeff register int cnt; 397219820Sjeff pid_t pid; 398219820Sjeff int status; 399219820Sjeff char *file; 400219820Sjeff 401219820Sjeff /* XXX - if file/dir ends in '/' this will not work -- can it? */ 402219820Sjeff if ((file = strrchr(entry->fts_path, '/'))) 403219820Sjeff file++; 404219820Sjeff else 405219820Sjeff file = entry->fts_path; 406219820Sjeff 407219820Sjeff for (cnt = 0; plan->e_argv[cnt]; ++cnt) 408219820Sjeff if (plan->e_len[cnt]) 409219820Sjeff brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 410219820Sjeff file, plan->e_len[cnt]); 411219820Sjeff 412219820Sjeff /* don't mix output of command with find output */ 413219820Sjeff fflush(stdout); 414219820Sjeff fflush(stderr); 415219820Sjeff 416219820Sjeff switch (pid = fork()) { 417219820Sjeff case -1: 418219820Sjeff err(1, "fork"); 419219820Sjeff /* NOTREACHED */ 420219820Sjeff case 0: 421219820Sjeff execvp(plan->e_argv[0], plan->e_argv); 422219820Sjeff warn("%s", plan->e_argv[0]); 423219820Sjeff _exit(1); 424219820Sjeff } 425219820Sjeff pid = waitpid(pid, &status, 0); 426219820Sjeff return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 427219820Sjeff} 428219820Sjeff 429219820Sjeff/* 430219820Sjeff * c_execdir -- 431219820Sjeff * build three parallel arrays, one with pointers to the strings passed 432219820Sjeff * on the command line, one with (possibly duplicated) pointers to the 433219820Sjeff * argv array, and one with integer values that are lengths of the 434219820Sjeff * strings, but also flags meaning that the string has to be massaged. 435219820Sjeff */ 436219820SjeffPLAN * 437219820Sjeffc_execdir(argvp) 438219820Sjeff char ***argvp; 439219820Sjeff{ 440219820Sjeff PLAN *new; /* node returned */ 441219820Sjeff register int cnt; 442219820Sjeff register char **argv, **ap, *p; 443219820Sjeff 444219820Sjeff ftsoptions &= ~FTS_NOSTAT; 445219820Sjeff isoutput = 1; 446219820Sjeff 447219820Sjeff new = palloc(N_EXECDIR, f_execdir); 448219820Sjeff 449219820Sjeff for (ap = argv = *argvp;; ++ap) { 450219820Sjeff if (!*ap) 451219820Sjeff errx(1, 452219820Sjeff "-execdir: no terminating \";\""); 453219820Sjeff if (**ap == ';') 454219820Sjeff break; 455219820Sjeff } 456219820Sjeff 457219820Sjeff cnt = ap - *argvp + 1; 458219820Sjeff new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 459219820Sjeff new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 460219820Sjeff new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 461219820Sjeff 462219820Sjeff for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 463219820Sjeff new->e_orig[cnt] = *argv; 464219820Sjeff for (p = *argv; *p; ++p) 465219820Sjeff if (p[0] == '{' && p[1] == '}') { 466219820Sjeff new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 467219820Sjeff new->e_len[cnt] = MAXPATHLEN; 468219820Sjeff break; 469219820Sjeff } 470219820Sjeff if (!*p) { 471219820Sjeff new->e_argv[cnt] = *argv; 472219820Sjeff new->e_len[cnt] = 0; 473219820Sjeff } 474219820Sjeff } 475219820Sjeff new->e_argv[cnt] = new->e_orig[cnt] = NULL; 476219820Sjeff 477219820Sjeff *argvp = argv + 1; 478219820Sjeff return (new); 479219820Sjeff} 480219820Sjeff 481219820Sjeff/* 482219820Sjeff * -follow functions -- 483219820Sjeff * 484219820Sjeff * Always true, causes symbolic links to be followed on a global 485219820Sjeff * basis. 486219820Sjeff */ 487219820SjeffPLAN * 488219820Sjeffc_follow() 489219820Sjeff{ 490219820Sjeff ftsoptions &= ~FTS_PHYSICAL; 491219820Sjeff ftsoptions |= FTS_LOGICAL; 492219820Sjeff 493219820Sjeff return (palloc(N_FOLLOW, f_always_true)); 494219820Sjeff} 495219820Sjeff 496219820Sjeff/* 497219820Sjeff * -fstype functions -- 498219820Sjeff * 499219820Sjeff * True if the file is of a certain type. 500219820Sjeff */ 501219820Sjeffint 502219820Sjefff_fstype(plan, entry) 503219820Sjeff PLAN *plan; 504219820Sjeff FTSENT *entry; 505219820Sjeff{ 506219820Sjeff static dev_t curdev; /* need a guaranteed illegal dev value */ 507219820Sjeff static int first = 1; 508219820Sjeff struct statfs sb; 509219820Sjeff static int val_type, val_flags; 510219820Sjeff char *p, save[2]; 511219820Sjeff 512219820Sjeff /* Only check when we cross mount point. */ 513219820Sjeff if (first || curdev != entry->fts_statp->st_dev) { 514219820Sjeff curdev = entry->fts_statp->st_dev; 515219820Sjeff 516219820Sjeff /* 517219820Sjeff * Statfs follows symlinks; find wants the link's file system, 518219820Sjeff * not where it points. 519219820Sjeff */ 520219820Sjeff if (entry->fts_info == FTS_SL || 521219820Sjeff entry->fts_info == FTS_SLNONE) { 522219820Sjeff if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 523219820Sjeff ++p; 524219820Sjeff else 525219820Sjeff p = entry->fts_accpath; 526219820Sjeff save[0] = p[0]; 527219820Sjeff p[0] = '.'; 528219820Sjeff save[1] = p[1]; 529219820Sjeff p[1] = '\0'; 530219820Sjeff 531219820Sjeff } else 532219820Sjeff p = NULL; 533219820Sjeff 534219820Sjeff if (statfs(entry->fts_accpath, &sb)) 535219820Sjeff err(1, "%s", entry->fts_accpath); 536219820Sjeff 537219820Sjeff if (p) { 538219820Sjeff p[0] = save[0]; 539219820Sjeff p[1] = save[1]; 540219820Sjeff } 541219820Sjeff 542219820Sjeff first = 0; 543219820Sjeff 544219820Sjeff /* 545219820Sjeff * Further tests may need both of these values, so 546219820Sjeff * always copy both of them. 547219820Sjeff */ 548219820Sjeff val_flags = sb.f_flags; 549219820Sjeff val_type = sb.f_type; 550219820Sjeff } 551219820Sjeff switch (plan->flags) { 552219820Sjeff case F_MTFLAG: 553219820Sjeff return (val_flags & plan->mt_data) != 0; 554219820Sjeff case F_MTTYPE: 555219820Sjeff return (val_type == plan->mt_data); 556219820Sjeff default: 557219820Sjeff abort(); 558219820Sjeff } 559219820Sjeff} 560219820Sjeff 561219820Sjeff#if !defined(__NetBSD__) 562219820SjeffPLAN * 563219820Sjeffc_fstype(arg) 564219820Sjeff char *arg; 565219820Sjeff{ 566219820Sjeff register PLAN *new; 567219820Sjeff struct vfsconf vfc; 568219820Sjeff 569219820Sjeff ftsoptions &= ~FTS_NOSTAT; 570219820Sjeff 571219820Sjeff new = palloc(N_FSTYPE, f_fstype); 572219820Sjeff 573219820Sjeff /* 574219820Sjeff * Check first for a filesystem name. 575219820Sjeff */ 576219820Sjeff if (getvfsbyname(arg, &vfc) == 0) { 577219820Sjeff new->flags = F_MTTYPE; 578219820Sjeff new->mt_data = vfc.vfc_typenum; 579219820Sjeff return (new); 580219820Sjeff } 581219820Sjeff 582219820Sjeff switch (*arg) { 583219820Sjeff case 'l': 584219820Sjeff if (!strcmp(arg, "local")) { 585219820Sjeff new->flags = F_MTFLAG; 586219820Sjeff new->mt_data = MNT_LOCAL; 587219820Sjeff return (new); 588219820Sjeff } 589219820Sjeff break; 590219820Sjeff case 'r': 591219820Sjeff if (!strcmp(arg, "rdonly")) { 592219820Sjeff new->flags = F_MTFLAG; 593219820Sjeff new->mt_data = MNT_RDONLY; 594219820Sjeff return (new); 595219820Sjeff } 596219820Sjeff break; 597219820Sjeff } 598219820Sjeff errx(1, "%s: unknown file type", arg); 599219820Sjeff /* NOTREACHED */ 600219820Sjeff} 601219820Sjeff#endif 602219820Sjeff 603219820Sjeff/* 604219820Sjeff * -group gname functions -- 605219820Sjeff * 606219820Sjeff * True if the file belongs to the group gname. If gname is numeric and 607219820Sjeff * an equivalent of the getgrnam() function does not return a valid group 608219820Sjeff * name, gname is taken as a group ID. 609219820Sjeff */ 610219820Sjeffint 611219820Sjefff_group(plan, entry) 612219820Sjeff PLAN *plan; 613219820Sjeff FTSENT *entry; 614219820Sjeff{ 615219820Sjeff return (entry->fts_statp->st_gid == plan->g_data); 616219820Sjeff} 617219820Sjeff 618219820SjeffPLAN * 619219820Sjeffc_group(gname) 620219820Sjeff char *gname; 621219820Sjeff{ 622219820Sjeff PLAN *new; 623219820Sjeff struct group *g; 624219820Sjeff gid_t gid; 625219820Sjeff 626219820Sjeff ftsoptions &= ~FTS_NOSTAT; 627219820Sjeff 628219820Sjeff g = getgrnam(gname); 629219820Sjeff if (g == NULL) { 630219820Sjeff gid = atoi(gname); 631219820Sjeff if (gid == 0 && gname[0] != '0') 632219820Sjeff errx(1, "-group: %s: no such group", gname); 633219820Sjeff } else 634219820Sjeff gid = g->gr_gid; 635219820Sjeff 636219820Sjeff new = palloc(N_GROUP, f_group); 637219820Sjeff new->g_data = gid; 638219820Sjeff return (new); 639219820Sjeff} 640219820Sjeff 641219820Sjeff/* 642219820Sjeff * -inum n functions -- 643219820Sjeff * 644219820Sjeff * True if the file has inode # n. 645219820Sjeff */ 646219820Sjeffint 647219820Sjefff_inum(plan, entry) 648219820Sjeff PLAN *plan; 649219820Sjeff FTSENT *entry; 650219820Sjeff{ 651219820Sjeff COMPARE(entry->fts_statp->st_ino, plan->i_data); 652219820Sjeff} 653219820Sjeff 654219820SjeffPLAN * 655219820Sjeffc_inum(arg) 656219820Sjeff char *arg; 657219820Sjeff{ 658219820Sjeff PLAN *new; 659219820Sjeff 660219820Sjeff ftsoptions &= ~FTS_NOSTAT; 661219820Sjeff 662219820Sjeff new = palloc(N_INUM, f_inum); 663219820Sjeff new->i_data = find_parsenum(new, "-inum", arg, NULL); 664219820Sjeff return (new); 665219820Sjeff} 666219820Sjeff 667219820Sjeff/* 668219820Sjeff * -links n functions -- 669219820Sjeff * 670219820Sjeff * True if the file has n links. 671219820Sjeff */ 672219820Sjeffint 673219820Sjefff_links(plan, entry) 674219820Sjeff PLAN *plan; 675219820Sjeff FTSENT *entry; 676219820Sjeff{ 677219820Sjeff COMPARE(entry->fts_statp->st_nlink, plan->l_data); 678219820Sjeff} 679219820Sjeff 680219820SjeffPLAN * 681219820Sjeffc_links(arg) 682219820Sjeff char *arg; 683219820Sjeff{ 684219820Sjeff PLAN *new; 685219820Sjeff 686219820Sjeff ftsoptions &= ~FTS_NOSTAT; 687219820Sjeff 688219820Sjeff new = palloc(N_LINKS, f_links); 689219820Sjeff new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 690219820Sjeff return (new); 691219820Sjeff} 692219820Sjeff 693219820Sjeff/* 694219820Sjeff * -ls functions -- 695219820Sjeff * 696219820Sjeff * Always true - prints the current entry to stdout in "ls" format. 697219820Sjeff */ 698219820Sjeffint 699219820Sjefff_ls(plan, entry) 700219820Sjeff PLAN *plan; 701219820Sjeff FTSENT *entry; 702219820Sjeff{ 703219820Sjeff printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 704219820Sjeff return (1); 705219820Sjeff} 706219820Sjeff 707219820SjeffPLAN * 708219820Sjeffc_ls() 709219820Sjeff{ 710219820Sjeff ftsoptions &= ~FTS_NOSTAT; 711219820Sjeff isoutput = 1; 712219820Sjeff 713219820Sjeff return (palloc(N_LS, f_ls)); 714219820Sjeff} 715219820Sjeff 716219820Sjeff/* 717219820Sjeff * -mtime n functions -- 718219820Sjeff * 719219820Sjeff * True if the difference between the file modification time and the 720219820Sjeff * current time is n 24 hour periods. 721219820Sjeff */ 722219820Sjeffint 723219820Sjefff_mtime(plan, entry) 724219820Sjeff PLAN *plan; 725219820Sjeff FTSENT *entry; 726219820Sjeff{ 727219820Sjeff extern time_t now; 728219820Sjeff 729219820Sjeff COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / 730219820Sjeff 86400, plan->t_data); 731219820Sjeff} 732219820Sjeff 733219820SjeffPLAN * 734219820Sjeffc_mtime(arg) 735219820Sjeff char *arg; 736219820Sjeff{ 737219820Sjeff PLAN *new; 738219820Sjeff 739219820Sjeff ftsoptions &= ~FTS_NOSTAT; 740219820Sjeff 741219820Sjeff new = palloc(N_MTIME, f_mtime); 742219820Sjeff new->t_data = find_parsenum(new, "-mtime", arg, NULL); 743219820Sjeff TIME_CORRECT(new, N_MTIME); 744219820Sjeff return (new); 745219820Sjeff} 746219820Sjeff 747219820Sjeff/* 748219820Sjeff * -mmin n functions -- 749219820Sjeff * 750219820Sjeff * True if the difference between the file modification time and the 751219820Sjeff * current time is n min periods. 752219820Sjeff */ 753219820Sjeffint 754219820Sjefff_mmin(plan, entry) 755219820Sjeff PLAN *plan; 756219820Sjeff FTSENT *entry; 757219820Sjeff{ 758219820Sjeff extern time_t now; 759219820Sjeff 760219820Sjeff COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / 761219820Sjeff 60, plan->t_data); 762219820Sjeff} 763219820Sjeff 764219820SjeffPLAN * 765219820Sjeffc_mmin(arg) 766219820Sjeff char *arg; 767219820Sjeff{ 768219820Sjeff PLAN *new; 769219820Sjeff 770219820Sjeff ftsoptions &= ~FTS_NOSTAT; 771219820Sjeff 772219820Sjeff new = palloc(N_MMIN, f_mmin); 773219820Sjeff new->t_data = find_parsenum(new, "-mmin", arg, NULL); 774219820Sjeff TIME_CORRECT(new, N_MMIN); 775219820Sjeff return (new); 776219820Sjeff} 777219820Sjeff 778219820Sjeff 779219820Sjeff/* 780219820Sjeff * -name functions -- 781219820Sjeff * 782219820Sjeff * True if the basename of the filename being examined 783219820Sjeff * matches pattern using Pattern Matching Notation S3.14 784219820Sjeff */ 785219820Sjeffint 786219820Sjefff_name(plan, entry) 787219820Sjeff PLAN *plan; 788219820Sjeff FTSENT *entry; 789219820Sjeff{ 790219820Sjeff return (!fnmatch(plan->c_data, entry->fts_name, 0)); 791219820Sjeff} 792219820Sjeff 793219820SjeffPLAN * 794219820Sjeffc_name(pattern) 795219820Sjeff char *pattern; 796219820Sjeff{ 797219820Sjeff PLAN *new; 798219820Sjeff 799219820Sjeff new = palloc(N_NAME, f_name); 800219820Sjeff new->c_data = pattern; 801219820Sjeff return (new); 802219820Sjeff} 803219820Sjeff 804219820Sjeff/* 805219820Sjeff * -newer file functions -- 806219820Sjeff * 807219820Sjeff * True if the current file has been modified more recently 808219820Sjeff * then the modification time of the file named by the pathname 809219820Sjeff * file. 810219820Sjeff */ 811219820Sjeffint 812219820Sjefff_newer(plan, entry) 813219820Sjeff PLAN *plan; 814219820Sjeff FTSENT *entry; 815219820Sjeff{ 816219820Sjeff return (entry->fts_statp->st_mtime > plan->t_data); 817219820Sjeff} 818219820Sjeff 819219820SjeffPLAN * 820219820Sjeffc_newer(filename) 821219820Sjeff char *filename; 822219820Sjeff{ 823219820Sjeff PLAN *new; 824219820Sjeff struct stat sb; 825219820Sjeff 826219820Sjeff ftsoptions &= ~FTS_NOSTAT; 827219820Sjeff 828219820Sjeff if (stat(filename, &sb)) 829219820Sjeff err(1, "%s", filename); 830219820Sjeff new = palloc(N_NEWER, f_newer); 831219820Sjeff new->t_data = sb.st_mtime; 832219820Sjeff return (new); 833219820Sjeff} 834219820Sjeff 835219820Sjeff/* 836219820Sjeff * -nogroup functions -- 837219820Sjeff * 838219820Sjeff * True if file belongs to a user ID for which the equivalent 839219820Sjeff * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 840219820Sjeff */ 841219820Sjeffint 842219820Sjefff_nogroup(plan, entry) 843219820Sjeff PLAN *plan; 844219820Sjeff FTSENT *entry; 845219820Sjeff{ 846219820Sjeff char *group_from_gid(); 847219820Sjeff 848219820Sjeff return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 849219820Sjeff} 850219820Sjeff 851219820SjeffPLAN * 852219820Sjeffc_nogroup() 853219820Sjeff{ 854219820Sjeff ftsoptions &= ~FTS_NOSTAT; 855219820Sjeff 856219820Sjeff return (palloc(N_NOGROUP, f_nogroup)); 857219820Sjeff} 858219820Sjeff 859219820Sjeff/* 860219820Sjeff * -nouser functions -- 861219820Sjeff * 862219820Sjeff * True if file belongs to a user ID for which the equivalent 863219820Sjeff * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 864219820Sjeff */ 865219820Sjeffint 866219820Sjefff_nouser(plan, entry) 867219820Sjeff PLAN *plan; 868219820Sjeff FTSENT *entry; 869219820Sjeff{ 870219820Sjeff char *user_from_uid(); 871219820Sjeff 872219820Sjeff return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 873219820Sjeff} 874219820Sjeff 875219820SjeffPLAN * 876219820Sjeffc_nouser() 877219820Sjeff{ 878219820Sjeff ftsoptions &= ~FTS_NOSTAT; 879219820Sjeff 880219820Sjeff return (palloc(N_NOUSER, f_nouser)); 881219820Sjeff} 882219820Sjeff 883219820Sjeff/* 884219820Sjeff * -path functions -- 885219820Sjeff * 886219820Sjeff * True if the path of the filename being examined 887219820Sjeff * matches pattern using Pattern Matching Notation S3.14 888219820Sjeff */ 889219820Sjeffint 890219820Sjefff_path(plan, entry) 891219820Sjeff PLAN *plan; 892219820Sjeff FTSENT *entry; 893219820Sjeff{ 894219820Sjeff return (!fnmatch(plan->c_data, entry->fts_path, 0)); 895219820Sjeff} 896219820Sjeff 897219820SjeffPLAN * 898219820Sjeffc_path(pattern) 899219820Sjeff char *pattern; 900219820Sjeff{ 901219820Sjeff PLAN *new; 902219820Sjeff 903219820Sjeff new = palloc(N_NAME, f_path); 904219820Sjeff new->c_data = pattern; 905219820Sjeff return (new); 906219820Sjeff} 907219820Sjeff 908219820Sjeff/* 909219820Sjeff * -perm functions -- 910219820Sjeff * 911219820Sjeff * The mode argument is used to represent file mode bits. If it starts 912219820Sjeff * with a leading digit, it's treated as an octal mode, otherwise as a 913219820Sjeff * symbolic mode. 914219820Sjeff */ 915219820Sjeffint 916219820Sjefff_perm(plan, entry) 917219820Sjeff PLAN *plan; 918219820Sjeff FTSENT *entry; 919219820Sjeff{ 920219820Sjeff mode_t mode; 921219820Sjeff 922219820Sjeff mode = entry->fts_statp->st_mode & 923219820Sjeff (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 924219820Sjeff if (plan->flags == F_ATLEAST) 925219820Sjeff return ((plan->m_data | mode) == mode); 926219820Sjeff else 927219820Sjeff return (mode == plan->m_data); 928219820Sjeff /* NOTREACHED */ 929219820Sjeff} 930219820Sjeff 931219820SjeffPLAN * 932219820Sjeffc_perm(perm) 933219820Sjeff char *perm; 934219820Sjeff{ 935219820Sjeff PLAN *new; 936219820Sjeff mode_t *set; 937219820Sjeff 938219820Sjeff ftsoptions &= ~FTS_NOSTAT; 939219820Sjeff 940219820Sjeff new = palloc(N_PERM, f_perm); 941219820Sjeff 942219820Sjeff if (*perm == '-') { 943219820Sjeff new->flags = F_ATLEAST; 944219820Sjeff ++perm; 945219820Sjeff } 946219820Sjeff 947219820Sjeff if ((set = setmode(perm)) == NULL) 948219820Sjeff err(1, "-perm: %s: illegal mode string", perm); 949219820Sjeff 950219820Sjeff new->m_data = getmode(set, 0); 951219820Sjeff free(set); 952219820Sjeff return (new); 953219820Sjeff} 954219820Sjeff 955219820Sjeff/* 956219820Sjeff * -print functions -- 957219820Sjeff * 958219820Sjeff * Always true, causes the current pathame to be written to 959219820Sjeff * standard output. 960219820Sjeff */ 961219820Sjeffint 962219820Sjefff_print(plan, entry) 963219820Sjeff PLAN *plan; 964219820Sjeff FTSENT *entry; 965219820Sjeff{ 966219820Sjeff (void)puts(entry->fts_path); 967219820Sjeff return (1); 968219820Sjeff} 969219820Sjeff 970219820SjeffPLAN * 971219820Sjeffc_print() 972219820Sjeff{ 973219820Sjeff isoutput = 1; 974219820Sjeff 975219820Sjeff return (palloc(N_PRINT, f_print)); 976219820Sjeff} 977219820Sjeff 978219820Sjeff/* 979219820Sjeff * -print0 functions -- 980219820Sjeff * 981219820Sjeff * Always true, causes the current pathame to be written to 982219820Sjeff * standard output followed by a NUL character 983219820Sjeff */ 984219820Sjeffint 985219820Sjefff_print0(plan, entry) 986219820Sjeff PLAN *plan; 987219820Sjeff FTSENT *entry; 988219820Sjeff{ 989219820Sjeff fputs(entry->fts_path, stdout); 990219820Sjeff fputc('\0', stdout); 991219820Sjeff return (1); 992219820Sjeff} 993219820Sjeff 994219820SjeffPLAN * 995219820Sjeffc_print0() 996219820Sjeff{ 997219820Sjeff isoutput = 1; 998219820Sjeff 999219820Sjeff return (palloc(N_PRINT0, f_print0)); 1000219820Sjeff} 1001219820Sjeff 1002219820Sjeff/* 1003219820Sjeff * -prune functions -- 1004219820Sjeff * 1005219820Sjeff * Prune a portion of the hierarchy. 1006219820Sjeff */ 1007219820Sjeffint 1008219820Sjefff_prune(plan, entry) 1009219820Sjeff PLAN *plan; 1010219820Sjeff FTSENT *entry; 1011219820Sjeff{ 1012219820Sjeff extern FTS *tree; 1013219820Sjeff 1014219820Sjeff if (fts_set(tree, entry, FTS_SKIP)) 1015219820Sjeff err(1, "%s", entry->fts_path); 1016219820Sjeff return (1); 1017219820Sjeff} 1018219820Sjeff 1019219820SjeffPLAN * 1020219820Sjeffc_prune() 1021219820Sjeff{ 1022219820Sjeff return (palloc(N_PRUNE, f_prune)); 1023219820Sjeff} 1024219820Sjeff 1025219820Sjeff/* 1026219820Sjeff * -size n[c] functions -- 1027219820Sjeff * 1028219820Sjeff * True if the file size in bytes, divided by an implementation defined 1029219820Sjeff * value and rounded up to the next integer, is n. If n is followed by 1030219820Sjeff * a c, the size is in bytes. 1031219820Sjeff */ 1032219820Sjeff#define FIND_SIZE 512 1033219820Sjeffstatic int divsize = 1; 1034219820Sjeff 1035219820Sjeffint 1036219820Sjefff_size(plan, entry) 1037219820Sjeff PLAN *plan; 1038219820Sjeff FTSENT *entry; 1039219820Sjeff{ 1040219820Sjeff off_t size; 1041219820Sjeff 1042219820Sjeff size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1043219820Sjeff FIND_SIZE : entry->fts_statp->st_size; 1044219820Sjeff COMPARE(size, plan->o_data); 1045219820Sjeff} 1046219820Sjeff 1047219820SjeffPLAN * 1048219820Sjeffc_size(arg) 1049219820Sjeff char *arg; 1050219820Sjeff{ 1051219820Sjeff PLAN *new; 1052219820Sjeff char endch; 1053219820Sjeff 1054219820Sjeff ftsoptions &= ~FTS_NOSTAT; 1055219820Sjeff 1056219820Sjeff new = palloc(N_SIZE, f_size); 1057219820Sjeff endch = 'c'; 1058219820Sjeff new->o_data = find_parsenum(new, "-size", arg, &endch); 1059219820Sjeff if (endch == 'c') 1060219820Sjeff divsize = 0; 1061219820Sjeff return (new); 1062219820Sjeff} 1063219820Sjeff 1064219820Sjeff/* 1065219820Sjeff * -type c functions -- 1066219820Sjeff * 1067219820Sjeff * True if the type of the file is c, where c is b, c, d, p, f or w 1068219820Sjeff * for block special file, character special file, directory, FIFO, 1069219820Sjeff * regular file or whiteout respectively. 1070219820Sjeff */ 1071219820Sjeffint 1072219820Sjefff_type(plan, entry) 1073219820Sjeff PLAN *plan; 1074219820Sjeff FTSENT *entry; 1075219820Sjeff{ 1076219820Sjeff return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1077219820Sjeff} 1078219820Sjeff 1079219820SjeffPLAN * 1080219820Sjeffc_type(typestring) 1081219820Sjeff char *typestring; 1082219820Sjeff{ 1083219820Sjeff PLAN *new; 1084219820Sjeff mode_t mask; 1085219820Sjeff 1086219820Sjeff ftsoptions &= ~FTS_NOSTAT; 1087219820Sjeff 1088219820Sjeff switch (typestring[0]) { 1089219820Sjeff case 'b': 1090219820Sjeff mask = S_IFBLK; 1091219820Sjeff break; 1092219820Sjeff case 'c': 1093219820Sjeff mask = S_IFCHR; 1094219820Sjeff break; 1095219820Sjeff case 'd': 1096219820Sjeff mask = S_IFDIR; 1097219820Sjeff break; 1098219820Sjeff case 'f': 1099219820Sjeff mask = S_IFREG; 1100219820Sjeff break; 1101219820Sjeff case 'l': 1102219820Sjeff mask = S_IFLNK; 1103219820Sjeff break; 1104219820Sjeff case 'p': 1105219820Sjeff mask = S_IFIFO; 1106219820Sjeff break; 1107219820Sjeff case 's': 1108219820Sjeff mask = S_IFSOCK; 1109219820Sjeff break; 1110219820Sjeff#ifdef FTS_WHITEOUT 1111219820Sjeff case 'w': 1112219820Sjeff mask = S_IFWHT; 1113219820Sjeff ftsoptions |= FTS_WHITEOUT; 1114219820Sjeff break; 1115219820Sjeff#endif /* FTS_WHITEOUT */ 1116219820Sjeff default: 1117219820Sjeff errx(1, "-type: %s: unknown type", typestring); 1118219820Sjeff } 1119219820Sjeff 1120219820Sjeff new = palloc(N_TYPE, f_type); 1121219820Sjeff new->m_data = mask; 1122219820Sjeff return (new); 1123219820Sjeff} 1124219820Sjeff 1125219820Sjeff/* 1126219820Sjeff * -delete functions -- 1127219820Sjeff * 1128219820Sjeff * True always. Makes it's best shot and continues on regardless. 1129219820Sjeff */ 1130219820Sjeffint 1131219820Sjefff_delete(plan, entry) 1132219820Sjeff PLAN *plan; 1133219820Sjeff FTSENT *entry; 1134219820Sjeff{ 1135219820Sjeff /* ignore these from fts */ 1136219820Sjeff if (strcmp(entry->fts_accpath, ".") == 0 || 1137219820Sjeff strcmp(entry->fts_accpath, "..") == 0) 1138219820Sjeff return (1); 1139219820Sjeff 1140219820Sjeff /* sanity check */ 1141219820Sjeff if (isdepth == 0 || /* depth off */ 1142219820Sjeff (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ 1143219820Sjeff !(ftsoptions & FTS_PHYSICAL) || /* physical off */ 1144219820Sjeff (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 1145219820Sjeff errx(1, "-delete: insecure options got turned on"); 1146219820Sjeff 1147219820Sjeff /* Potentially unsafe - do not accept relative paths whatsoever */ 1148219820Sjeff if (strchr(entry->fts_accpath, '/') != NULL) 1149219820Sjeff errx(1, "-delete: %s: relative path potentially not safe", 1150219820Sjeff entry->fts_accpath); 1151219820Sjeff 1152219820Sjeff /* Turn off user immutable bits if running as root */ 1153219820Sjeff if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 1154219820Sjeff !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 1155219820Sjeff geteuid() == 0) 1156219820Sjeff chflags(entry->fts_accpath, 1157219820Sjeff entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 1158219820Sjeff 1159219820Sjeff /* rmdir directories, unlink everything else */ 1160219820Sjeff if (S_ISDIR(entry->fts_statp->st_mode)) { 1161219820Sjeff if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 1162219820Sjeff warn("-delete: rmdir(%s)", entry->fts_path); 1163219820Sjeff } else { 1164219820Sjeff if (unlink(entry->fts_accpath) < 0) 1165219820Sjeff warn("-delete: unlink(%s)", entry->fts_path); 1166219820Sjeff } 1167219820Sjeff 1168219820Sjeff /* "succeed" */ 1169219820Sjeff return (1); 1170219820Sjeff} 1171219820Sjeff 1172219820SjeffPLAN * 1173219820Sjeffc_delete() 1174219820Sjeff{ 1175219820Sjeff 1176219820Sjeff ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 1177219820Sjeff ftsoptions |= FTS_PHYSICAL; /* disable -follow */ 1178219820Sjeff ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ 1179219820Sjeff isoutput = 1; /* possible output */ 1180219820Sjeff isdepth = 1; /* -depth implied */ 1181219820Sjeff 1182219820Sjeff return (palloc(N_DELETE, f_delete)); 1183219820Sjeff} 1184219820Sjeff 1185219820Sjeff/* 1186219820Sjeff * -user uname functions -- 1187219820Sjeff * 1188219820Sjeff * True if the file belongs to the user uname. If uname is numeric and 1189219820Sjeff * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1190219820Sjeff * return a valid user name, uname is taken as a user ID. 1191219820Sjeff */ 1192219820Sjeffint 1193219820Sjefff_user(plan, entry) 1194219820Sjeff PLAN *plan; 1195219820Sjeff FTSENT *entry; 1196219820Sjeff{ 1197219820Sjeff return (entry->fts_statp->st_uid == plan->u_data); 1198219820Sjeff} 1199219820Sjeff 1200219820SjeffPLAN * 1201219820Sjeffc_user(username) 1202219820Sjeff char *username; 1203219820Sjeff{ 1204219820Sjeff PLAN *new; 1205219820Sjeff struct passwd *p; 1206219820Sjeff uid_t uid; 1207219820Sjeff 1208219820Sjeff ftsoptions &= ~FTS_NOSTAT; 1209219820Sjeff 1210219820Sjeff p = getpwnam(username); 1211219820Sjeff if (p == NULL) { 1212219820Sjeff uid = atoi(username); 1213219820Sjeff if (uid == 0 && username[0] != '0') 1214219820Sjeff errx(1, "-user: %s: no such user", username); 1215219820Sjeff } else 1216219820Sjeff uid = p->pw_uid; 1217219820Sjeff 1218219820Sjeff new = palloc(N_USER, f_user); 1219219820Sjeff new->u_data = uid; 1220219820Sjeff return (new); 1221219820Sjeff} 1222219820Sjeff 1223219820Sjeff/* 1224219820Sjeff * -xdev functions -- 1225219820Sjeff * 1226219820Sjeff * Always true, causes find not to decend past directories that have a 1227219820Sjeff * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1228219820Sjeff */ 1229219820SjeffPLAN * 1230219820Sjeffc_xdev() 1231219820Sjeff{ 1232219820Sjeff ftsoptions |= FTS_XDEV; 1233219820Sjeff 1234219820Sjeff return (palloc(N_XDEV, f_always_true)); 1235219820Sjeff} 1236219820Sjeff 1237219820Sjeff/* 1238219820Sjeff * ( expression ) functions -- 1239219820Sjeff * 1240219820Sjeff * True if expression is true. 1241219820Sjeff */ 1242219820Sjeffint 1243219820Sjefff_expr(plan, entry) 1244219820Sjeff PLAN *plan; 1245219820Sjeff FTSENT *entry; 1246219820Sjeff{ 1247219820Sjeff register PLAN *p; 1248219820Sjeff register int state; 1249219820Sjeff 1250219820Sjeff state = 0; 1251219820Sjeff for (p = plan->p_data[0]; 1252219820Sjeff p && (state = (p->eval)(p, entry)); p = p->next); 1253219820Sjeff return (state); 1254219820Sjeff} 1255219820Sjeff 1256219820Sjeff/* 1257219820Sjeff * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1258219820Sjeff * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1259219820Sjeff * to a N_EXPR node containing the expression and the ')' node is discarded. 1260219820Sjeff */ 1261219820SjeffPLAN * 1262219820Sjeffc_openparen() 1263219820Sjeff{ 1264219820Sjeff return (palloc(N_OPENPAREN, (int (*)())-1)); 1265219820Sjeff} 1266219820Sjeff 1267219820SjeffPLAN * 1268219820Sjeffc_closeparen() 1269219820Sjeff{ 1270219820Sjeff return (palloc(N_CLOSEPAREN, (int (*)())-1)); 1271219820Sjeff} 1272219820Sjeff 1273219820Sjeff/* 1274219820Sjeff * ! expression functions -- 1275219820Sjeff * 1276219820Sjeff * Negation of a primary; the unary NOT operator. 1277219820Sjeff */ 1278219820Sjeffint 1279219820Sjefff_not(plan, entry) 1280219820Sjeff PLAN *plan; 1281219820Sjeff FTSENT *entry; 1282219820Sjeff{ 1283219820Sjeff register PLAN *p; 1284219820Sjeff register int state; 1285219820Sjeff 1286219820Sjeff state = 0; 1287219820Sjeff for (p = plan->p_data[0]; 1288219820Sjeff p && (state = (p->eval)(p, entry)); p = p->next); 1289219820Sjeff return (!state); 1290219820Sjeff} 1291219820Sjeff 1292219820SjeffPLAN * 1293219820Sjeffc_not() 1294219820Sjeff{ 1295219820Sjeff return (palloc(N_NOT, f_not)); 1296219820Sjeff} 1297219820Sjeff 1298219820Sjeff/* 1299219820Sjeff * expression -o expression functions -- 1300219820Sjeff * 1301219820Sjeff * Alternation of primaries; the OR operator. The second expression is 1302219820Sjeff * not evaluated if the first expression is true. 1303219820Sjeff */ 1304219820Sjeffint 1305219820Sjefff_or(plan, entry) 1306219820Sjeff PLAN *plan; 1307219820Sjeff FTSENT *entry; 1308219820Sjeff{ 1309219820Sjeff register PLAN *p; 1310219820Sjeff register int state; 1311219820Sjeff 1312219820Sjeff state = 0; 1313219820Sjeff for (p = plan->p_data[0]; 1314219820Sjeff p && (state = (p->eval)(p, entry)); p = p->next); 1315219820Sjeff 1316219820Sjeff if (state) 1317219820Sjeff return (1); 1318219820Sjeff 1319219820Sjeff for (p = plan->p_data[1]; 1320219820Sjeff p && (state = (p->eval)(p, entry)); p = p->next); 1321219820Sjeff return (state); 1322219820Sjeff} 1323219820Sjeff 1324219820SjeffPLAN * 1325219820Sjeffc_or() 1326219820Sjeff{ 1327219820Sjeff return (palloc(N_OR, f_or)); 1328219820Sjeff} 1329219820Sjeff 1330219820Sjeffstatic PLAN * 1331219820Sjeffpalloc(t, f) 1332219820Sjeff enum ntype t; 1333219820Sjeff int (*f) __P((PLAN *, FTSENT *)); 1334219820Sjeff{ 1335219820Sjeff PLAN *new; 1336219820Sjeff 1337219820Sjeff if ((new = malloc(sizeof(PLAN))) == NULL) 1338219820Sjeff err(1, NULL); 1339219820Sjeff new->type = t; 1340219820Sjeff new->eval = f; 1341219820Sjeff new->flags = 0; 1342219820Sjeff new->next = NULL; 1343219820Sjeff return (new); 1344219820Sjeff} 1345219820Sjeff