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