1/*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Cimarron D. Taylor of the University of California, Berkeley. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#ifndef lint 34#if 0 35static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; 36#endif 37#endif /* not lint */ 38 39#include <sys/cdefs.h> 40__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.71 2011/06/13 05:22:07 avatar Exp $"); 41 42#include <sys/param.h> 43#include <sys/ucred.h> 44#include <sys/stat.h> 45#include <sys/types.h> 46#include <sys/acl.h> 47#include <sys/wait.h> 48#include <sys/mount.h> 49 50#include <dirent.h> 51#include <err.h> 52#include <errno.h> 53#include <fnmatch.h> 54#include <fts.h> 55#include <grp.h> 56#include <limits.h> 57#include <pwd.h> 58#include <regex.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <unistd.h> 63#include <ctype.h> 64 65#ifdef __APPLE__ 66#include <sys/sysctl.h> 67#include <sys/xattr.h> 68#include <libgen.h> 69#include <get_compat.h> 70#else 71#define COMPAT_MODE(func, mode) 1 72#endif 73 74#include "find.h" 75 76static PLAN *palloc(OPTION *); 77static long long find_parsenum(PLAN *, const char *, char *, char *); 78static long long find_parsetime(PLAN *, const char *, char *); 79static char *nextarg(OPTION *, char ***); 80 81extern char **environ; 82 83static PLAN *lastexecplus = NULL; 84int execplus_error; 85 86#define COMPARE(a, b) do { \ 87 switch (plan->flags & F_ELG_MASK) { \ 88 case F_EQUAL: \ 89 return (a == b); \ 90 case F_LESSTHAN: \ 91 return (a < b); \ 92 case F_GREATER: \ 93 return (a > b); \ 94 default: \ 95 abort(); \ 96 } \ 97} while(0) 98 99static PLAN * 100palloc(OPTION *option) 101{ 102 PLAN *new; 103 104 if ((new = malloc(sizeof(PLAN))) == NULL) 105 err(1, NULL); 106 new->execute = option->execute; 107 new->flags = option->flags; 108 new->next = NULL; 109 return new; 110} 111 112/* 113 * find_parsenum -- 114 * Parse a string of the form [+-]# and return the value. 115 */ 116static long long 117find_parsenum(PLAN *plan, const char *option, char *vp, char *endch) 118{ 119 long long value; 120 char *endchar, *str; /* Pointer to character ending conversion. */ 121 122 /* Determine comparison from leading + or -. */ 123 str = vp; 124 switch (*str) { 125 case '+': 126 ++str; 127 plan->flags |= F_GREATER; 128 break; 129 case '-': 130 ++str; 131 plan->flags |= F_LESSTHAN; 132 break; 133 default: 134 plan->flags |= F_EQUAL; 135 break; 136 } 137 138 /* 139 * Convert the string with strtoq(). Note, if strtoq() returns zero 140 * and endchar points to the beginning of the string we know we have 141 * a syntax error. 142 */ 143 value = strtoq(str, &endchar, 10); 144 if (value == 0 && endchar == str) 145 errx(1, "%s: %s: illegal numeric value", option, vp); 146 if (endchar[0] && endch == NULL) 147 errx(1, "%s: %s: illegal trailing character", option, vp); 148 if (endch) 149 *endch = endchar[0]; 150 return value; 151} 152 153/* 154 * find_parsetime -- 155 * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. 156 */ 157static long long 158find_parsetime(PLAN *plan, const char *option, char *vp) 159{ 160 long long secs, value; 161 char *str, *unit; /* Pointer to character ending conversion. */ 162 163 /* Determine comparison from leading + or -. */ 164 str = vp; 165 switch (*str) { 166 case '+': 167 ++str; 168 plan->flags |= F_GREATER; 169 break; 170 case '-': 171 ++str; 172 plan->flags |= F_LESSTHAN; 173 break; 174 default: 175 plan->flags |= F_EQUAL; 176 break; 177 } 178 179 value = strtoq(str, &unit, 10); 180 if (value == 0 && unit == str) { 181 errx(1, "%s: %s: illegal time value", option, vp); 182 /* NOTREACHED */ 183 } 184 if (*unit == '\0') 185 return value; 186 187 /* Units syntax. */ 188 secs = 0; 189 for (;;) { 190 switch(*unit) { 191 case 's': /* seconds */ 192 secs += value; 193 break; 194 case 'm': /* minutes */ 195 secs += value * 60; 196 break; 197 case 'h': /* hours */ 198 secs += value * 3600; 199 break; 200 case 'd': /* days */ 201 secs += value * 86400; 202 break; 203 case 'w': /* weeks */ 204 secs += value * 604800; 205 break; 206 default: 207 errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); 208 /* NOTREACHED */ 209 } 210 str = unit + 1; 211 if (*str == '\0') /* EOS */ 212 break; 213 value = strtoq(str, &unit, 10); 214 if (value == 0 && unit == str) { 215 errx(1, "%s: %s: illegal time value", option, vp); 216 /* NOTREACHED */ 217 } 218 if (*unit == '\0') { 219 errx(1, "%s: %s: missing trailing unit", option, vp); 220 /* NOTREACHED */ 221 } 222 } 223 plan->flags |= F_EXACTTIME; 224 return secs; 225} 226 227/* 228 * nextarg -- 229 * Check that another argument still exists, return a pointer to it, 230 * and increment the argument vector pointer. 231 */ 232static char * 233nextarg(OPTION *option, char ***argvp) 234{ 235 char *arg; 236 237 if ((arg = **argvp) == 0) 238 errx(1, "%s: requires additional arguments", option->name); 239 (*argvp)++; 240 return arg; 241} /* nextarg() */ 242 243/* 244 * The value of n for the inode times (atime, birthtime, ctime, mtime) is a 245 * range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts 246 * with -n, such that "-mtime -1" would be less than 0 days, which isn't what 247 * the user wanted. Correct so that -1 is "less than 1". 248 */ 249#define TIME_CORRECT(p) \ 250 if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ 251 ++((p)->t_data); 252 253/* 254 * -[acm]min n functions -- 255 * 256 * True if the difference between the 257 * file access time (-amin) 258 * file birth time (-Bmin) 259 * last change of file status information (-cmin) 260 * file modification time (-mmin) 261 * and the current time is n min periods. 262 */ 263int 264f_Xmin(PLAN *plan, FTSENT *entry) 265{ 266 if (plan->flags & F_TIME_C) { 267 COMPARE((now - entry->fts_statp->st_ctime + 268 60 - 1) / 60, plan->t_data); 269 } else if (plan->flags & F_TIME_A) { 270 COMPARE((now - entry->fts_statp->st_atime + 271 60 - 1) / 60, plan->t_data); 272 } else if (plan->flags & F_TIME_B) { 273 COMPARE((now - entry->fts_statp->st_birthtime + 274 60 - 1) / 60, plan->t_data); 275 } else { 276 COMPARE((now - entry->fts_statp->st_mtime + 277 60 - 1) / 60, plan->t_data); 278 } 279} 280 281PLAN * 282c_Xmin(OPTION *option, char ***argvp) 283{ 284 char *nmins; 285 PLAN *new; 286 287 nmins = nextarg(option, argvp); 288 ftsoptions &= ~FTS_NOSTAT; 289 290 new = palloc(option); 291 new->t_data = find_parsenum(new, option->name, nmins, NULL); 292 TIME_CORRECT(new); 293 return new; 294} 295 296/* 297 * -[acm]time n functions -- 298 * 299 * True if the difference between the 300 * file access time (-atime) 301 * file birth time (-Btime) 302 * last change of file status information (-ctime) 303 * file modification time (-mtime) 304 * and the current time is n 24 hour periods. 305 */ 306 307int 308f_Xtime(PLAN *plan, FTSENT *entry) 309{ 310 time_t xtime; 311 312 if (plan->flags & F_TIME_A) 313 xtime = entry->fts_statp->st_atime; 314 else if (plan->flags & F_TIME_B) 315 xtime = entry->fts_statp->st_birthtime; 316 else if (plan->flags & F_TIME_C) 317 xtime = entry->fts_statp->st_ctime; 318 else 319 xtime = entry->fts_statp->st_mtime; 320 321 if (plan->flags & F_EXACTTIME) 322 COMPARE(now - xtime, plan->t_data); 323 else 324 COMPARE((now - xtime + (COMPAT_MODE("bin/find", "unix2003") ? 0 : 86400 - 1)) / 86400, plan->t_data); 325} 326 327PLAN * 328c_Xtime(OPTION *option, char ***argvp) 329{ 330 char *value; 331 PLAN *new; 332 333 value = nextarg(option, argvp); 334 ftsoptions &= ~FTS_NOSTAT; 335 336 new = palloc(option); 337 new->t_data = find_parsetime(new, option->name, value); 338 if (!(new->flags & F_EXACTTIME) && !COMPAT_MODE("bin/find", "unix2003")) 339 TIME_CORRECT(new); 340 return new; 341} 342 343/* 344 * -maxdepth/-mindepth n functions -- 345 * 346 * Does the same as -prune if the level of the current file is 347 * greater/less than the specified maximum/minimum depth. 348 * 349 * Note that -maxdepth and -mindepth are handled specially in 350 * find_execute() so their f_* functions are set to f_always_true(). 351 */ 352PLAN * 353c_mXXdepth(OPTION *option, char ***argvp) 354{ 355 char *dstr; 356 PLAN *new; 357 358 dstr = nextarg(option, argvp); 359 if (dstr[0] == '-') 360 /* all other errors handled by find_parsenum() */ 361 errx(1, "%s: %s: value must be positive", option->name, dstr); 362 363 new = palloc(option); 364 if (option->flags & F_MAXDEPTH) 365 maxdepth = find_parsenum(new, option->name, dstr, NULL); 366 else 367 mindepth = find_parsenum(new, option->name, dstr, NULL); 368 return new; 369} 370 371/* 372 * -acl function -- 373 * 374 * Show files with EXTENDED ACL attributes. 375 */ 376#ifdef __APPLE__ 377int 378f_acl(PLAN *plan __unused, FTSENT *entry) 379{ 380 acl_t facl; 381 int match; 382 acl_entry_t ae; 383 384 match = 0; 385 if ((facl = acl_get_link_np(entry->fts_accpath, ACL_TYPE_EXTENDED)) != NULL) { 386 if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 0) { 387 match = 1; 388 } 389 acl_free(facl); 390 } 391 return match; 392} 393#else /* !__APPLE__ */ 394int 395f_acl(PLAN *plan __unused, FTSENT *entry) 396{ 397 acl_t facl; 398 acl_type_t acl_type; 399 int acl_supported = 0, ret, trivial; 400 401 if (S_ISLNK(entry->fts_statp->st_mode)) 402 return 0; 403 ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); 404 if (ret > 0) { 405 acl_supported = 1; 406 acl_type = ACL_TYPE_NFS4; 407 } else if (ret < 0 && errno != EINVAL) { 408 warn("%s", entry->fts_accpath); 409 return (0); 410 } 411 if (acl_supported == 0) { 412 ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); 413 if (ret > 0) { 414 acl_supported = 1; 415 acl_type = ACL_TYPE_ACCESS; 416 } else if (ret < 0 && errno != EINVAL) { 417 warn("%s", entry->fts_accpath); 418 return (0); 419 } 420 } 421 if (acl_supported == 0) 422 return (0); 423 424 facl = acl_get_file(entry->fts_accpath, acl_type); 425 if (facl == NULL) { 426 warn("%s", entry->fts_accpath); 427 return (0); 428 } 429 ret = acl_is_trivial_np(facl, &trivial); 430 acl_free(facl); 431 if (ret) { 432 warn("%s", entry->fts_accpath); 433 acl_free(facl); 434 return (0); 435 } 436 if (trivial) 437 return (0); 438 return (1); 439} 440#endif /* __APPLE__ */ 441 442PLAN * 443c_acl(OPTION *option, char ***argvp __unused) 444{ 445#ifndef __APPLE__ 446 ftsoptions &= ~FTS_NOSTAT; 447#endif /* !__APPLE__ */ 448 return (palloc(option)); 449} 450 451#ifdef __APPLE__ 452int 453f_xattr(PLAN *plan __unused, FTSENT *entry) 454{ 455 ssize_t xattr; 456 int match; 457 458 match = 0; 459 xattr = listxattr(entry->fts_accpath, NULL, 0, XATTR_NOFOLLOW); 460 if (xattr > 0) { 461 match = 1; 462 } 463 return match; 464} 465 466int 467f_xattrname(PLAN *plan, FTSENT *entry) 468{ 469 ssize_t xattr; 470 int match; 471 472 match = 0; 473 xattr = getxattr(entry->fts_accpath, plan->c_data, NULL, 0, 0, XATTR_NOFOLLOW); 474 if (xattr > 0) { 475 match = 1; 476 } 477 return match; 478} 479#endif /* __APPLE__ */ 480 481/* 482 * -delete functions -- 483 * 484 * True always. Makes its best shot and continues on regardless. 485 */ 486int 487f_delete(PLAN *plan __unused, FTSENT *entry) 488{ 489 /* ignore these from fts */ 490 if (strcmp(entry->fts_accpath, ".") == 0 || 491 strcmp(entry->fts_accpath, "..") == 0) 492 return 1; 493 494 /* sanity check */ 495 if (isdepth == 0 || /* depth off */ 496 (ftsoptions & FTS_NOSTAT)) /* not stat()ing */ 497 errx(1, "-delete: insecure options got turned on"); 498 499 if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ 500 (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 501 errx(1, "-delete: forbidden when symlinks are followed"); 502 503 /* Potentially unsafe - do not accept relative paths whatsoever */ 504 if (strchr(entry->fts_accpath, '/') != NULL) 505 errx(1, "-delete: %s: relative path potentially not safe", 506 entry->fts_accpath); 507 508 /* Turn off user immutable bits if running as root */ 509 if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 510 !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 511 geteuid() == 0) 512 lchflags(entry->fts_accpath, 513 entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 514 515 /* rmdir directories, unlink everything else */ 516 if (S_ISDIR(entry->fts_statp->st_mode)) { 517 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 518 warn("-delete: rmdir(%s)", entry->fts_path); 519 } else { 520 if (unlink(entry->fts_accpath) < 0) 521 warn("-delete: unlink(%s)", entry->fts_path); 522 } 523 524 /* "succeed" */ 525 return 1; 526} 527 528PLAN * 529c_delete(OPTION *option, char ***argvp __unused) 530{ 531 532 ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 533 isoutput = 1; /* possible output */ 534 isdepth = 1; /* -depth implied */ 535 536 return palloc(option); 537} 538 539 540/* 541 * always_true -- 542 * 543 * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true 544 */ 545int 546f_always_true(PLAN *plan __unused, FTSENT *entry __unused) 547{ 548 return 1; 549} 550 551/* 552 * -depth functions -- 553 * 554 * With argument: True if the file is at level n. 555 * Without argument: Always true, causes descent of the directory hierarchy 556 * to be done so that all entries in a directory are acted on before the 557 * directory itself. 558 */ 559int 560f_depth(PLAN *plan, FTSENT *entry) 561{ 562 if (plan->flags & F_DEPTH) 563 COMPARE(entry->fts_level, plan->d_data); 564 else 565 return 1; 566} 567 568PLAN * 569c_depth(OPTION *option, char ***argvp) 570{ 571 PLAN *new; 572 char *str; 573 574 new = palloc(option); 575 576 str = **argvp; 577 if (str && !(new->flags & F_DEPTH)) { 578 /* skip leading + or - */ 579 if (*str == '+' || *str == '-') 580 str++; 581 /* skip sign */ 582 if (*str == '+' || *str == '-') 583 str++; 584 if (isdigit(*str)) 585 new->flags |= F_DEPTH; 586 } 587 588 if (new->flags & F_DEPTH) { /* -depth n */ 589 char *ndepth; 590 591 ndepth = nextarg(option, argvp); 592 new->d_data = find_parsenum(new, option->name, ndepth, NULL); 593 } else { /* -d */ 594 isdepth = 1; 595 } 596 597 return new; 598} 599 600/* 601 * -empty functions -- 602 * 603 * True if the file or directory is empty 604 */ 605int 606f_empty(PLAN *plan __unused, FTSENT *entry) 607{ 608 if (S_ISREG(entry->fts_statp->st_mode) && 609 entry->fts_statp->st_size == 0) 610 return 1; 611 if (S_ISDIR(entry->fts_statp->st_mode)) { 612 struct dirent *dp; 613 int empty; 614 DIR *dir; 615 616 empty = 1; 617 dir = opendir(entry->fts_accpath); 618 if (dir == NULL) 619 return 0; 620 for (dp = readdir(dir); dp; dp = readdir(dir)) 621 if (dp->d_name[0] != '.' || 622 (dp->d_name[1] != '\0' && 623 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { 624 empty = 0; 625 break; 626 } 627 closedir(dir); 628 return empty; 629 } 630 return 0; 631} 632 633PLAN * 634c_empty(OPTION *option, char ***argvp __unused) 635{ 636 ftsoptions &= ~FTS_NOSTAT; 637 638 return palloc(option); 639} 640 641/* 642 * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- 643 * 644 * True if the executed utility returns a zero value as exit status. 645 * The end of the primary expression is delimited by a semicolon. If 646 * "{}" occurs anywhere, it gets replaced by the current pathname, 647 * or, in the case of -execdir, the current basename (filename 648 * without leading directory prefix). For -exec and -ok, 649 * the current directory for the execution of utility is the same as 650 * the current directory when the find utility was started, whereas 651 * for -execdir, it is the directory the file resides in. 652 * 653 * The primary -ok differs from -exec in that it requests affirmation 654 * of the user before executing the utility. 655 */ 656int 657f_exec(PLAN *plan, FTSENT *entry) 658{ 659 int cnt; 660 pid_t pid; 661 int status; 662 char *file; 663 664 if (entry == NULL && plan->flags & F_EXECPLUS) { 665 if (plan->e_ppos == plan->e_pbnum) 666 return (1); 667 plan->e_argv[plan->e_ppos] = NULL; 668 goto doexec; 669 } 670 671 /* XXX - if file/dir ends in '/' this will not work -- can it? */ 672 if ((plan->flags & F_EXECDIR) && \ 673 (file = strrchr(entry->fts_path, '/'))) 674 file++; 675 else 676 file = entry->fts_path; 677 678 if (plan->flags & F_EXECPLUS) { 679 if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) 680 err(1, NULL); 681 plan->e_len[plan->e_ppos] = strlen(file); 682 plan->e_psize += plan->e_len[plan->e_ppos]; 683 if (++plan->e_ppos < plan->e_pnummax && 684 plan->e_psize < plan->e_psizemax) 685 return (1); 686 plan->e_argv[plan->e_ppos] = NULL; 687 } else { 688 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 689 if (plan->e_len[cnt]) 690 brace_subst(plan->e_orig[cnt], 691 &plan->e_argv[cnt], file, 692 plan->e_len[cnt]); 693 } 694 695doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) 696 return 0; 697 698 /* make sure find output is interspersed correctly with subprocesses */ 699 fflush(stdout); 700 fflush(stderr); 701 702 switch (pid = fork()) { 703 case -1: 704 err(1, "fork"); 705 /* NOTREACHED */ 706 case 0: 707 /* change dir back from where we started */ 708 if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { 709 warn("chdir"); 710 _exit(1); 711 } 712 execvp(plan->e_argv[0], plan->e_argv); 713 warn("%s", plan->e_argv[0]); 714 _exit(1); 715 } 716 if (plan->flags & F_EXECPLUS) { 717 while (--plan->e_ppos >= plan->e_pbnum) 718 free(plan->e_argv[plan->e_ppos]); 719 plan->e_ppos = plan->e_pbnum; 720 plan->e_psize = plan->e_pbsize; 721 } 722 pid = waitpid(pid, &status, 0); 723 if (plan->flags & F_EXECPLUS && WIFEXITED(status) && WEXITSTATUS(status) && !execplus_error) { 724 /* Test 140 (8907531, 10656525) */ 725 execplus_error = WEXITSTATUS(status); 726 } 727 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 728} 729 730/* 731 * c_exec, c_execdir, c_ok -- 732 * build three parallel arrays, one with pointers to the strings passed 733 * on the command line, one with (possibly duplicated) pointers to the 734 * argv array, and one with integer values that are lengths of the 735 * strings, but also flags meaning that the string has to be massaged. 736 */ 737PLAN * 738c_exec(OPTION *option, char ***argvp) 739{ 740 PLAN *new; /* node returned */ 741 long argmax; 742 int cnt, i; 743 char **argv, **ap, **ep, *p; 744 745 /* XXX - was in c_execdir, but seems unnecessary!? 746 ftsoptions &= ~FTS_NOSTAT; 747 */ 748 isoutput = 1; 749 750 /* XXX - this is a change from the previous coding */ 751 new = palloc(option); 752 753 for (ap = argv = *argvp;; ++ap) { 754 if (!*ap) 755 errx(1, 756 "%s: no terminating \";\" or \"+\"", option->name); 757 if (**ap == ';') 758 break; 759 if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { 760 new->flags |= F_EXECPLUS; 761 break; 762 } 763 } 764 765 if (ap == argv) 766 errx(1, "%s: no command specified", option->name); 767 768 cnt = ap - *argvp + 1; 769 if (new->flags & F_EXECPLUS) { 770 new->e_ppos = new->e_pbnum = cnt - 2; 771 if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { 772 warn("sysconf(_SC_ARG_MAX)"); 773 argmax = _POSIX_ARG_MAX; 774 } 775 argmax -= 1024; 776 for (ep = environ; *ep != NULL; ep++) 777 argmax -= strlen(*ep) + 1 + sizeof(*ep); 778 argmax -= 1 + sizeof(*ep); 779 new->e_pnummax = argmax / 16; 780 argmax -= sizeof(char *) * new->e_pnummax; 781 if (argmax <= 0) 782 errx(1, "no space for arguments"); 783 new->e_psizemax = argmax; 784 new->e_pbsize = 0; 785 cnt += new->e_pnummax + 1; 786 new->e_next = lastexecplus; 787 lastexecplus = new; 788 } 789 if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) 790 err(1, NULL); 791 if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) 792 err(1, NULL); 793 if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) 794 err(1, NULL); 795 796 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 797 new->e_orig[cnt] = *argv; 798 if (new->flags & F_EXECPLUS) 799 new->e_pbsize += strlen(*argv) + 1; 800 for (p = *argv; *p; ++p) 801 if (!(new->flags & F_EXECPLUS) && p[0] == '{' && 802 p[1] == '}') { 803 if ((new->e_argv[cnt] = 804 malloc(MAXPATHLEN)) == NULL) 805 err(1, NULL); 806 new->e_len[cnt] = MAXPATHLEN; 807 break; 808 } 809 if (!*p) { 810 new->e_argv[cnt] = *argv; 811 new->e_len[cnt] = 0; 812 } 813 } 814 if (new->flags & F_EXECPLUS) { 815 new->e_psize = new->e_pbsize; 816 cnt--; 817 for (i = 0; i < new->e_pnummax; i++) { 818 new->e_argv[cnt] = NULL; 819 new->e_len[cnt] = 0; 820 cnt++; 821 } 822 argv = ap; 823 goto done; 824 } 825 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 826 827done: *argvp = argv + 1; 828 return new; 829} 830 831/* Finish any pending -exec ... {} + functions. */ 832void 833finish_execplus(void) 834{ 835 PLAN *p; 836 837 p = lastexecplus; 838 while (p != NULL) { 839 (p->execute)(p, NULL); 840 p = p->e_next; 841 } 842} 843 844int 845f_flags(PLAN *plan, FTSENT *entry) 846{ 847 u_long flags; 848 849 flags = entry->fts_statp->st_flags; 850 if (plan->flags & F_ATLEAST) 851 return (flags | plan->fl_flags) == flags && 852 !(flags & plan->fl_notflags); 853 else if (plan->flags & F_ANY) 854 return (flags & plan->fl_flags) || 855 (flags | plan->fl_notflags) != flags; 856 else 857 return flags == plan->fl_flags && 858 !(plan->fl_flags & plan->fl_notflags); 859} 860 861PLAN * 862c_flags(OPTION *option, char ***argvp) 863{ 864 char *flags_str; 865 PLAN *new; 866 u_long flags, notflags; 867 868 flags_str = nextarg(option, argvp); 869 ftsoptions &= ~FTS_NOSTAT; 870 871 new = palloc(option); 872 873 if (*flags_str == '-') { 874 new->flags |= F_ATLEAST; 875 flags_str++; 876 } else if (*flags_str == '+') { 877 new->flags |= F_ANY; 878 flags_str++; 879 } 880 if (strtofflags(&flags_str, &flags, ¬flags) == 1) 881 errx(1, "%s: %s: illegal flags string", option->name, flags_str); 882 883 new->fl_flags = flags; 884 new->fl_notflags = notflags; 885 return new; 886} 887 888/* 889 * -follow functions -- 890 * 891 * Always true, causes symbolic links to be followed on a global 892 * basis. 893 */ 894PLAN * 895c_follow(OPTION *option, char ***argvp __unused) 896{ 897 ftsoptions &= ~FTS_PHYSICAL; 898 ftsoptions |= FTS_LOGICAL; 899 900 return palloc(option); 901} 902 903/* 904 * -fstype functions -- 905 * 906 * True if the file is of a certain type. 907 */ 908int 909f_fstype(PLAN *plan, FTSENT *entry) 910{ 911 static dev_t curdev; /* need a guaranteed illegal dev value */ 912 static int first = 1; 913 struct statfs sb; 914 static int val_flags; 915 static char fstype[sizeof(sb.f_fstypename)]; 916 char *p, save[2] = {0,0}; 917 918 if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) 919 return 0; 920 921 /* Only check when we cross mount point. */ 922 if (first || curdev != entry->fts_statp->st_dev) { 923 curdev = entry->fts_statp->st_dev; 924 925 /* 926 * Statfs follows symlinks; find wants the link's filesystem, 927 * not where it points. 928 */ 929 if (entry->fts_info == FTS_SL || 930 entry->fts_info == FTS_SLNONE) { 931 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 932 ++p; 933 else 934 p = entry->fts_accpath; 935 save[0] = p[0]; 936 p[0] = '.'; 937 save[1] = p[1]; 938 p[1] = '\0'; 939 } else 940 p = NULL; 941 942 if (statfs(entry->fts_accpath, &sb)) 943 err(1, "%s", entry->fts_accpath); 944 945 if (p) { 946 p[0] = save[0]; 947 p[1] = save[1]; 948 } 949 950 first = 0; 951 952 /* 953 * Further tests may need both of these values, so 954 * always copy both of them. 955 */ 956 val_flags = sb.f_flags; 957 strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); 958 } 959 switch (plan->flags & F_MTMASK) { 960 case F_MTFLAG: 961 return val_flags & plan->mt_data; 962 case F_MTTYPE: 963 return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); 964 default: 965 abort(); 966 } 967} 968 969PLAN * 970c_fstype(OPTION *option, char ***argvp) 971{ 972 char *fsname; 973 PLAN *new; 974 975 fsname = nextarg(option, argvp); 976 ftsoptions &= ~FTS_NOSTAT; 977 978 new = palloc(option); 979 switch (*fsname) { 980 case 'l': 981 if (!strcmp(fsname, "local")) { 982 new->flags |= F_MTFLAG; 983 new->mt_data = MNT_LOCAL; 984 return new; 985 } 986 break; 987 case 'r': 988 if (!strcmp(fsname, "rdonly")) { 989 new->flags |= F_MTFLAG; 990 new->mt_data = MNT_RDONLY; 991 return new; 992 } 993 break; 994 } 995 996 new->flags |= F_MTTYPE; 997 new->c_data = fsname; 998 return new; 999} 1000 1001/* 1002 * -group gname functions -- 1003 * 1004 * True if the file belongs to the group gname. If gname is numeric and 1005 * an equivalent of the getgrnam() function does not return a valid group 1006 * name, gname is taken as a group ID. 1007 */ 1008int 1009f_group(PLAN *plan, FTSENT *entry) 1010{ 1011 COMPARE(entry->fts_statp->st_gid, plan->g_data); 1012} 1013 1014PLAN * 1015c_group(OPTION *option, char ***argvp) 1016{ 1017 char *gname; 1018 PLAN *new; 1019 struct group *g; 1020 gid_t gid; 1021 1022 gname = nextarg(option, argvp); 1023 ftsoptions &= ~FTS_NOSTAT; 1024 1025 new = palloc(option); 1026 g = getgrnam(gname); 1027 if (g == NULL) { 1028 char* cp = gname; 1029 if (gname[0] == '-' || gname[0] == '+') 1030 gname++; 1031 gid = atoi(gname); 1032 if (gid == 0 && gname[0] != '0') 1033 errx(1, "%s: %s: no such group", option->name, gname); 1034 gid = find_parsenum(new, option->name, cp, NULL); 1035 } else 1036 gid = g->gr_gid; 1037 1038 new->g_data = gid; 1039 return new; 1040} 1041 1042/* 1043 * -inum n functions -- 1044 * 1045 * True if the file has inode # n. 1046 */ 1047int 1048f_inum(PLAN *plan, FTSENT *entry) 1049{ 1050 COMPARE(entry->fts_statp->st_ino, plan->i_data); 1051} 1052 1053PLAN * 1054c_inum(OPTION *option, char ***argvp) 1055{ 1056 char *inum_str; 1057 PLAN *new; 1058 1059 inum_str = nextarg(option, argvp); 1060 ftsoptions &= ~FTS_NOSTAT; 1061 1062 new = palloc(option); 1063 new->i_data = find_parsenum(new, option->name, inum_str, NULL); 1064 return new; 1065} 1066 1067/* 1068 * -samefile FN 1069 * 1070 * True if the file has the same inode (eg hard link) FN 1071 */ 1072 1073/* f_samefile is just f_inum */ 1074PLAN * 1075c_samefile(OPTION *option, char ***argvp) 1076{ 1077 char *fn; 1078 PLAN *new; 1079 struct stat sb; 1080 1081 fn = nextarg(option, argvp); 1082 ftsoptions &= ~FTS_NOSTAT; 1083 1084 new = palloc(option); 1085 if (stat(fn, &sb)) 1086 err(1, "%s", fn); 1087 new->i_data = sb.st_ino; 1088 return new; 1089} 1090 1091/* 1092 * -links n functions -- 1093 * 1094 * True if the file has n links. 1095 */ 1096int 1097f_links(PLAN *plan, FTSENT *entry) 1098{ 1099 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 1100} 1101 1102PLAN * 1103c_links(OPTION *option, char ***argvp) 1104{ 1105 char *nlinks; 1106 PLAN *new; 1107 1108 nlinks = nextarg(option, argvp); 1109 ftsoptions &= ~FTS_NOSTAT; 1110 1111 new = palloc(option); 1112 new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); 1113 return new; 1114} 1115 1116/* 1117 * -ls functions -- 1118 * 1119 * Always true - prints the current entry to stdout in "ls" format. 1120 */ 1121int 1122f_ls(PLAN *plan __unused, FTSENT *entry) 1123{ 1124 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 1125 return 1; 1126} 1127 1128PLAN * 1129c_ls(OPTION *option, char ***argvp __unused) 1130{ 1131 ftsoptions &= ~FTS_NOSTAT; 1132 isoutput = 1; 1133 1134 return palloc(option); 1135} 1136 1137/* 1138 * -name functions -- 1139 * 1140 * True if the basename of the filename being examined 1141 * matches pattern using Pattern Matching Notation S3.14 1142 */ 1143int 1144f_name(PLAN *plan, FTSENT *entry) 1145{ 1146 char fn[PATH_MAX]; 1147 const char *name; 1148 1149 if (plan->flags & F_LINK) { 1150 name = fn; 1151 if (readlink(entry->fts_accpath, fn, sizeof(fn)) == -1) 1152 return 0; 1153 } else if (entry->fts_namelen == 0) { 1154 name = basename(entry->fts_path); 1155 } else 1156 name = entry->fts_name; 1157 return !fnmatch(plan->c_data, name, 1158 plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 1159} 1160 1161PLAN * 1162c_name(OPTION *option, char ***argvp) 1163{ 1164 char *pattern; 1165 PLAN *new; 1166 1167 pattern = nextarg(option, argvp); 1168 new = palloc(option); 1169 new->c_data = pattern; 1170 return new; 1171} 1172 1173/* 1174 * -newer file functions -- 1175 * 1176 * True if the current file has been modified more recently 1177 * then the modification time of the file named by the pathname 1178 * file. 1179 */ 1180int 1181f_newer(PLAN *plan, FTSENT *entry) 1182{ 1183 if (plan->flags & F_TIME_C) 1184 return entry->fts_statp->st_ctime > plan->t_data; 1185 else if (plan->flags & F_TIME_A) 1186 return entry->fts_statp->st_atime > plan->t_data; 1187 else if (plan->flags & F_TIME_B) 1188 return entry->fts_statp->st_birthtime > plan->t_data; 1189 else 1190 return entry->fts_statp->st_mtime > plan->t_data; 1191} 1192 1193PLAN * 1194c_newer(OPTION *option, char ***argvp) 1195{ 1196 char *fn_or_tspec; 1197 PLAN *new; 1198 struct stat sb; 1199 1200 fn_or_tspec = nextarg(option, argvp); 1201 ftsoptions &= ~FTS_NOSTAT; 1202 1203 new = palloc(option); 1204 /* compare against what */ 1205 if (option->flags & F_TIME2_T) { 1206 new->t_data = get_date(fn_or_tspec); 1207 if (new->t_data == (time_t) -1) 1208 errx(1, "Can't parse date/time: %s", fn_or_tspec); 1209 } else { 1210 if (stat(fn_or_tspec, &sb)) 1211 err(1, "%s", fn_or_tspec); 1212 if (option->flags & F_TIME2_C) 1213 new->t_data = sb.st_ctime; 1214 else if (option->flags & F_TIME2_A) 1215 new->t_data = sb.st_atime; 1216 else if (option->flags & F_TIME2_B) 1217 new->t_data = sb.st_birthtime; 1218 else 1219 new->t_data = sb.st_mtime; 1220 } 1221 return new; 1222} 1223 1224/* 1225 * -nogroup functions -- 1226 * 1227 * True if file belongs to a user ID for which the equivalent 1228 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 1229 */ 1230int 1231f_nogroup(PLAN *plan __unused, FTSENT *entry) 1232{ 1233 return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; 1234} 1235 1236PLAN * 1237c_nogroup(OPTION *option, char ***argvp __unused) 1238{ 1239 ftsoptions &= ~FTS_NOSTAT; 1240 1241 return palloc(option); 1242} 1243 1244/* 1245 * -nouser functions -- 1246 * 1247 * True if file belongs to a user ID for which the equivalent 1248 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 1249 */ 1250int 1251f_nouser(PLAN *plan __unused, FTSENT *entry) 1252{ 1253 return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; 1254} 1255 1256PLAN * 1257c_nouser(OPTION *option, char ***argvp __unused) 1258{ 1259 ftsoptions &= ~FTS_NOSTAT; 1260 1261 return palloc(option); 1262} 1263 1264/* 1265 * -path functions -- 1266 * 1267 * True if the path of the filename being examined 1268 * matches pattern using Pattern Matching Notation S3.14 1269 */ 1270int 1271f_path(PLAN *plan, FTSENT *entry) 1272{ 1273 return !fnmatch(plan->c_data, entry->fts_path, 1274 plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); 1275} 1276 1277/* c_path is the same as c_name */ 1278 1279/* 1280 * -perm functions -- 1281 * 1282 * The mode argument is used to represent file mode bits. If it starts 1283 * with a leading digit, it's treated as an octal mode, otherwise as a 1284 * symbolic mode. 1285 */ 1286int 1287f_perm(PLAN *plan, FTSENT *entry) 1288{ 1289 mode_t mode; 1290 1291 mode = entry->fts_statp->st_mode & 1292 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 1293 if (plan->flags & F_ATLEAST) 1294 return (plan->m_data | mode) == mode; 1295 else if (plan->flags & F_ANY) 1296 return (mode & plan->m_data); 1297 else 1298 return mode == plan->m_data; 1299 /* NOTREACHED */ 1300} 1301 1302PLAN * 1303c_perm(OPTION *option, char ***argvp) 1304{ 1305 char *perm; 1306 PLAN *new; 1307 mode_t *set; 1308 1309 perm = nextarg(option, argvp); 1310 ftsoptions &= ~FTS_NOSTAT; 1311 1312 new = palloc(option); 1313 1314 if (*perm == '-') { 1315 new->flags |= F_ATLEAST; 1316 ++perm; 1317 } else if (*perm == '+') { 1318 if ((set = setmode(perm + 1)) != NULL) { 1319 new->flags |= F_ANY; 1320 ++perm; 1321 free(set); 1322 } 1323 } 1324 1325 if ((set = setmode(perm)) == NULL) 1326 errx(1, "%s: %s: illegal mode string", option->name, perm); 1327 1328 new->m_data = getmode(set, 0); 1329 free(set); 1330 return new; 1331} 1332 1333/* 1334 * -print functions -- 1335 * 1336 * Always true, causes the current pathname to be written to 1337 * standard output. 1338 */ 1339int 1340f_print(PLAN *plan __unused, FTSENT *entry) 1341{ 1342 (void)puts(entry->fts_path); 1343 return 1; 1344} 1345 1346PLAN * 1347c_print(OPTION *option, char ***argvp __unused) 1348{ 1349 isoutput = 1; 1350 1351 return palloc(option); 1352} 1353 1354/* 1355 * -print0 functions -- 1356 * 1357 * Always true, causes the current pathname to be written to 1358 * standard output followed by a NUL character 1359 */ 1360int 1361f_print0(PLAN *plan __unused, FTSENT *entry) 1362{ 1363 fputs(entry->fts_path, stdout); 1364 fputc('\0', stdout); 1365 return 1; 1366} 1367 1368/* c_print0 is the same as c_print */ 1369 1370/* 1371 * -prune functions -- 1372 * 1373 * Prune a portion of the hierarchy. 1374 */ 1375int 1376f_prune(PLAN *plan __unused, FTSENT *entry) 1377{ 1378 if (fts_set(tree, entry, FTS_SKIP)) 1379 err(1, "%s", entry->fts_path); 1380 return 1; 1381} 1382 1383/* c_prune == c_simple */ 1384 1385/* 1386 * -regex functions -- 1387 * 1388 * True if the whole path of the file matches pattern using 1389 * regular expression. 1390 */ 1391int 1392f_regex(PLAN *plan, FTSENT *entry) 1393{ 1394 char *str; 1395 int len; 1396 regex_t *pre; 1397 regmatch_t pmatch; 1398 int errcode; 1399 char errbuf[LINE_MAX]; 1400 int matched; 1401 1402 pre = plan->re_data; 1403 str = entry->fts_path; 1404 len = strlen(str); 1405 matched = 0; 1406 1407 pmatch.rm_so = 0; 1408 pmatch.rm_eo = len; 1409 1410 errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); 1411 1412 if (errcode != 0 && errcode != REG_NOMATCH) { 1413 regerror(errcode, pre, errbuf, sizeof errbuf); 1414 errx(1, "%s: %s", 1415 plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); 1416 } 1417 1418 if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) 1419 matched = 1; 1420 1421 return matched; 1422} 1423 1424PLAN * 1425c_regex(OPTION *option, char ***argvp) 1426{ 1427 PLAN *new; 1428 char *pattern; 1429 regex_t *pre; 1430 int errcode; 1431 char errbuf[LINE_MAX]; 1432 1433 if ((pre = malloc(sizeof(regex_t))) == NULL) 1434 err(1, NULL); 1435 1436 pattern = nextarg(option, argvp); 1437 1438 if ((errcode = regcomp(pre, pattern, 1439 regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { 1440 regerror(errcode, pre, errbuf, sizeof errbuf); 1441 errx(1, "%s: %s: %s", 1442 option->flags & F_IGNCASE ? "-iregex" : "-regex", 1443 pattern, errbuf); 1444 } 1445 1446 new = palloc(option); 1447 new->re_data = pre; 1448 1449 return new; 1450} 1451 1452/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ 1453 1454PLAN * 1455c_simple(OPTION *option, char ***argvp __unused) 1456{ 1457 return palloc(option); 1458} 1459 1460/* 1461 * -size n[c] functions -- 1462 * 1463 * True if the file size in bytes, divided by an implementation defined 1464 * value and rounded up to the next integer, is n. If n is followed by 1465 * one of c k M G T P, the size is in bytes, kilobytes, 1466 * megabytes, gigabytes, terabytes or petabytes respectively. 1467 */ 1468#define FIND_SIZE 512 1469static int divsize = 1; 1470 1471int 1472f_size(PLAN *plan, FTSENT *entry) 1473{ 1474 off_t size; 1475 1476 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1477 FIND_SIZE : entry->fts_statp->st_size; 1478 COMPARE(size, plan->o_data); 1479} 1480 1481PLAN * 1482c_size(OPTION *option, char ***argvp) 1483{ 1484 char *size_str; 1485 PLAN *new; 1486 char endch; 1487 off_t scale; 1488 1489 size_str = nextarg(option, argvp); 1490 ftsoptions &= ~FTS_NOSTAT; 1491 1492 new = palloc(option); 1493 endch = 'c'; 1494 new->o_data = find_parsenum(new, option->name, size_str, &endch); 1495 if (endch != '\0') { 1496 divsize = 0; 1497 1498 switch (endch) { 1499 case 'c': /* characters */ 1500 scale = 0x1LL; 1501 break; 1502 case 'k': /* kilobytes 1<<10 */ 1503 scale = 0x400LL; 1504 break; 1505 case 'M': /* megabytes 1<<20 */ 1506 scale = 0x100000LL; 1507 break; 1508 case 'G': /* gigabytes 1<<30 */ 1509 scale = 0x40000000LL; 1510 break; 1511 case 'T': /* terabytes 1<<40 */ 1512 scale = 0x1000000000LL; 1513 break; 1514 case 'P': /* petabytes 1<<50 */ 1515 scale = 0x4000000000000LL; 1516 break; 1517 default: 1518 errx(1, "%s: %s: illegal trailing character", 1519 option->name, size_str); 1520 break; 1521 } 1522 if (new->o_data > QUAD_MAX / scale) 1523 errx(1, "%s: %s: value too large", 1524 option->name, size_str); 1525 new->o_data *= scale; 1526 } 1527 return new; 1528} 1529 1530/* 1531 * -type c functions -- 1532 * 1533 * True if the type of the file is c, where c is b, c, d, p, f or w 1534 * for block special file, character special file, directory, FIFO, 1535 * regular file or whiteout respectively. 1536 */ 1537int 1538f_type(PLAN *plan, FTSENT *entry) 1539{ 1540 return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; 1541} 1542 1543PLAN * 1544c_type(OPTION *option, char ***argvp) 1545{ 1546 char *typestring; 1547 PLAN *new; 1548 mode_t mask; 1549 1550 typestring = nextarg(option, argvp); 1551 ftsoptions &= ~FTS_NOSTAT; 1552 1553 switch (typestring[0]) { 1554 case 'b': 1555 mask = S_IFBLK; 1556 break; 1557 case 'c': 1558 mask = S_IFCHR; 1559 break; 1560 case 'd': 1561 mask = S_IFDIR; 1562 break; 1563 case 'f': 1564 mask = S_IFREG; 1565 break; 1566 case 'l': 1567 mask = S_IFLNK; 1568 break; 1569 case 'p': 1570 mask = S_IFIFO; 1571 break; 1572 case 's': 1573 mask = S_IFSOCK; 1574 break; 1575#ifdef FTS_WHITEOUT 1576 case 'w': 1577 mask = S_IFWHT; 1578 ftsoptions |= FTS_WHITEOUT; 1579 break; 1580#endif /* FTS_WHITEOUT */ 1581 default: 1582 errx(1, "%s: %s: unknown type", option->name, typestring); 1583 } 1584 1585 new = palloc(option); 1586 new->m_data = mask; 1587 return new; 1588} 1589 1590/* 1591 * -user uname functions -- 1592 * 1593 * True if the file belongs to the user uname. If uname is numeric and 1594 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1595 * return a valid user name, uname is taken as a user ID. 1596 */ 1597int 1598f_user(PLAN *plan, FTSENT *entry) 1599{ 1600 COMPARE(entry->fts_statp->st_uid, plan->u_data); 1601} 1602 1603PLAN * 1604c_user(OPTION *option, char ***argvp) 1605{ 1606 char *username; 1607 PLAN *new; 1608 struct passwd *p; 1609 uid_t uid; 1610 1611 username = nextarg(option, argvp); 1612 ftsoptions &= ~FTS_NOSTAT; 1613 1614 new = palloc(option); 1615 p = getpwnam(username); 1616 if (p == NULL) { 1617 char* cp = username; 1618 if( username[0] == '-' || username[0] == '+' ) 1619 username++; 1620 uid = atoi(username); 1621 if (uid == 0 && username[0] != '0') 1622 errx(1, "%s: %s: no such user", option->name, username); 1623 uid = find_parsenum(new, option->name, cp, NULL); 1624 } else 1625 uid = p->pw_uid; 1626 1627 new->u_data = uid; 1628 return new; 1629} 1630 1631/* 1632 * -xdev functions -- 1633 * 1634 * Always true, causes find not to descend past directories that have a 1635 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1636 */ 1637PLAN * 1638c_xdev(OPTION *option, char ***argvp __unused) 1639{ 1640 ftsoptions |= FTS_XDEV; 1641 1642 return palloc(option); 1643} 1644 1645/* 1646 * ( expression ) functions -- 1647 * 1648 * True if expression is true. 1649 */ 1650int 1651f_expr(PLAN *plan, FTSENT *entry) 1652{ 1653 PLAN *p; 1654 int state = 0; 1655 1656 for (p = plan->p_data[0]; 1657 p && (state = (p->execute)(p, entry)); p = p->next); 1658 return state; 1659} 1660 1661/* 1662 * f_openparen and f_closeparen nodes are temporary place markers. They are 1663 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1664 * to a f_expr node containing the expression and the ')' node is discarded. 1665 * The functions themselves are only used as constants. 1666 */ 1667 1668int 1669f_openparen(PLAN *plan __unused, FTSENT *entry __unused) 1670{ 1671 abort(); 1672} 1673 1674int 1675f_closeparen(PLAN *plan __unused, FTSENT *entry __unused) 1676{ 1677 abort(); 1678} 1679 1680/* c_openparen == c_simple */ 1681/* c_closeparen == c_simple */ 1682 1683/* 1684 * AND operator. Since AND is implicit, no node is allocated. 1685 */ 1686PLAN * 1687c_and(OPTION *option __unused, char ***argvp __unused) 1688{ 1689 return NULL; 1690} 1691 1692/* 1693 * ! expression functions -- 1694 * 1695 * Negation of a primary; the unary NOT operator. 1696 */ 1697int 1698f_not(PLAN *plan, FTSENT *entry) 1699{ 1700 PLAN *p; 1701 int state = 0; 1702 1703 for (p = plan->p_data[0]; 1704 p && (state = (p->execute)(p, entry)); p = p->next); 1705 return !state; 1706} 1707 1708/* c_not == c_simple */ 1709 1710/* 1711 * expression -o expression functions -- 1712 * 1713 * Alternation of primaries; the OR operator. The second expression is 1714 * not evaluated if the first expression is true. 1715 */ 1716int 1717f_or(PLAN *plan, FTSENT *entry) 1718{ 1719 PLAN *p; 1720 int state = 0; 1721 1722 for (p = plan->p_data[0]; 1723 p && (state = (p->execute)(p, entry)); p = p->next); 1724 1725 if (state) 1726 return 1; 1727 1728 for (p = plan->p_data[1]; 1729 p && (state = (p->execute)(p, entry)); p = p->next); 1730 return state; 1731} 1732 1733/* c_or == c_simple */ 1734 1735/* 1736 * -false 1737 * 1738 * Always false. 1739 */ 1740int 1741f_false(PLAN *plan __unused, FTSENT *entry __unused) 1742{ 1743 return 0; 1744} 1745 1746/* c_false == c_simple */ 1747 1748/* 1749 * -quit 1750 * 1751 * Exits the program 1752 */ 1753int 1754f_quit(PLAN *plan __unused, FTSENT *entry __unused) 1755{ 1756 exit(0); 1757} 1758 1759/* c_quit == c_simple */ 1760