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