1 /* $NetBSD: function.c,v 1.24 1998/02/21 22:47:20 christos 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. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95"; 43 #else 44 __RCSID("$NetBSD: function.c,v 1.24 1998/02/21 22:47:20 christos Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/ucred.h> 50 #include <sys/stat.h> 51 #include <sys/wait.h> 52 #include <sys/mount.h> 53 54 #include <err.h> 55 #include <errno.h> 56 #include <fnmatch.h> 57 #include <fts.h> 58 #include <grp.h> 59 #include <pwd.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <tzfile.h> 64 #include <unistd.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 long find_parsenum __P((PLAN *, char *, char *, char *)); 82 int f_always_true __P((PLAN *, FTSENT *)); 83 int f_atime __P((PLAN *, FTSENT *)); 84 int f_ctime __P((PLAN *, FTSENT *)); 85 int f_exec __P((PLAN *, FTSENT *)); 86 int f_fstype __P((PLAN *, FTSENT *)); 87 int f_group __P((PLAN *, FTSENT *)); 88 int f_inum __P((PLAN *, FTSENT *)); 89 int f_links __P((PLAN *, FTSENT *)); 90 int f_ls __P((PLAN *, FTSENT *)); 91 int f_mtime __P((PLAN *, FTSENT *)); 92 int f_name __P((PLAN *, FTSENT *)); 93 int f_newer __P((PLAN *, FTSENT *)); 94 int f_nogroup __P((PLAN *, FTSENT *)); 95 int f_nouser __P((PLAN *, FTSENT *)); 96 int f_path __P((PLAN *, FTSENT *)); 97 int f_perm __P((PLAN *, FTSENT *)); 98 int f_print __P((PLAN *, FTSENT *)); 99 int f_print0 __P((PLAN *, FTSENT *)); 100 int f_prune __P((PLAN *, FTSENT *)); 101 int f_size __P((PLAN *, FTSENT *)); 102 int f_type __P((PLAN *, FTSENT *)); 103 int f_user __P((PLAN *, FTSENT *)); 104 int f_not __P((PLAN *, FTSENT *)); 105 int f_or __P((PLAN *, FTSENT *)); 106 static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); 107 108 /* 109 * find_parsenum -- 110 * Parse a string of the form [+-]# and return the value. 111 */ 112 static long 113 find_parsenum(plan, option, vp, endch) 114 PLAN *plan; 115 char *option, *vp, *endch; 116 { 117 long value; 118 char *endchar, *str; /* Pointer to character ending conversion. */ 119 120 /* Determine comparison from leading + or -. */ 121 str = vp; 122 switch (*str) { 123 case '+': 124 ++str; 125 plan->flags = F_GREATER; 126 break; 127 case '-': 128 ++str; 129 plan->flags = F_LESSTHAN; 130 break; 131 default: 132 plan->flags = F_EQUAL; 133 break; 134 } 135 136 /* 137 * Convert the string with strtol(). Note, if strtol() returns zero 138 * and endchar points to the beginning of the string we know we have 139 * a syntax error. 140 */ 141 value = strtol(str, &endchar, 10); 142 if (value == 0 && endchar == str) 143 errx(1, "%s: %s: illegal numeric value", option, vp); 144 if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 145 errx(1, "%s: %s: illegal trailing character", option, vp); 146 if (endch) 147 *endch = endchar[0]; 148 return (value); 149 } 150 151 /* 152 * The value of n for the inode times (atime, ctime, and mtime) is a range, 153 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 154 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 155 * user wanted. Correct so that -1 is "less than 1". 156 */ 157 #define TIME_CORRECT(p, ttype) \ 158 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 159 ++((p)->t_data); 160 161 /* 162 * -atime n functions -- 163 * 164 * True if the difference between the file access time and the 165 * current time is n 24 hour periods. 166 */ 167 int 168 f_atime(plan, entry) 169 PLAN *plan; 170 FTSENT *entry; 171 { 172 extern time_t now; 173 174 COMPARE((now - entry->fts_statp->st_atime + 175 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 176 } 177 178 PLAN * 179 c_atime(argvp, isok) 180 char ***argvp; 181 int isok; 182 { 183 char *arg = **argvp; 184 PLAN *new; 185 186 (*argvp)++; 187 ftsoptions &= ~FTS_NOSTAT; 188 189 new = palloc(N_ATIME, f_atime); 190 new->t_data = find_parsenum(new, "-atime", arg, NULL); 191 TIME_CORRECT(new, N_ATIME); 192 return (new); 193 } 194 /* 195 * -ctime n functions -- 196 * 197 * True if the difference between the last change of file 198 * status information and the current time is n 24 hour periods. 199 */ 200 int 201 f_ctime(plan, entry) 202 PLAN *plan; 203 FTSENT *entry; 204 { 205 extern time_t now; 206 207 COMPARE((now - entry->fts_statp->st_ctime + 208 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 209 } 210 211 PLAN * 212 c_ctime(argvp, isok) 213 char ***argvp; 214 int isok; 215 { 216 char *arg = **argvp; 217 PLAN *new; 218 219 (*argvp)++; 220 ftsoptions &= ~FTS_NOSTAT; 221 222 new = palloc(N_CTIME, f_ctime); 223 new->t_data = find_parsenum(new, "-ctime", arg, NULL); 224 TIME_CORRECT(new, N_CTIME); 225 return (new); 226 } 227 228 /* 229 * -depth functions -- 230 * 231 * Always true, causes descent of the directory hierarchy to be done 232 * so that all entries in a directory are acted on before the directory 233 * itself. 234 */ 235 int 236 f_always_true(plan, entry) 237 PLAN *plan; 238 FTSENT *entry; 239 { 240 return (1); 241 } 242 243 PLAN * 244 c_depth(argvp, isok) 245 char ***argvp; 246 int isok; 247 { 248 isdepth = 1; 249 250 return (palloc(N_DEPTH, f_always_true)); 251 } 252 253 /* 254 * [-exec | -ok] utility [arg ... ] ; functions -- 255 * 256 * True if the executed utility returns a zero value as exit status. 257 * The end of the primary expression is delimited by a semicolon. If 258 * "{}" occurs anywhere, it gets replaced by the current pathname. 259 * The current directory for the execution of utility is the same as 260 * the current directory when the find utility was started. 261 * 262 * The primary -ok is different in that it requests affirmation of the 263 * user before executing the utility. 264 */ 265 int 266 f_exec(plan, entry) 267 PLAN *plan; 268 FTSENT *entry; 269 { 270 extern int dotfd; 271 int cnt; 272 pid_t pid; 273 int status; 274 275 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 276 if (plan->e_len[cnt]) 277 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 278 entry->fts_path, plan->e_len[cnt]); 279 280 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 281 return (0); 282 283 /* don't mix output of command with find output */ 284 fflush(stdout); 285 fflush(stderr); 286 287 switch (pid = vfork()) { 288 case -1: 289 err(1, "fork"); 290 /* NOTREACHED */ 291 case 0: 292 if (fchdir(dotfd)) { 293 warn("chdir"); 294 _exit(1); 295 } 296 execvp(plan->e_argv[0], plan->e_argv); 297 warn("%s", plan->e_argv[0]); 298 _exit(1); 299 } 300 pid = waitpid(pid, &status, 0); 301 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 302 } 303 304 /* 305 * c_exec -- 306 * build three parallel arrays, one with pointers to the strings passed 307 * on the command line, one with (possibly duplicated) pointers to the 308 * argv array, and one with integer values that are lengths of the 309 * strings, but also flags meaning that the string has to be massaged. 310 */ 311 PLAN * 312 c_exec(argvp, isok) 313 char ***argvp; 314 int isok; 315 { 316 PLAN *new; /* node returned */ 317 int cnt; 318 char **argv, **ap, *p; 319 320 isoutput = 1; 321 322 new = palloc(N_EXEC, f_exec); 323 if (isok) 324 new->flags = F_NEEDOK; 325 326 for (ap = argv = *argvp;; ++ap) { 327 if (!*ap) 328 errx(1, 329 "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 330 if (**ap == ';') 331 break; 332 } 333 334 cnt = ap - *argvp + 1; 335 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 336 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 337 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 338 339 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 340 new->e_orig[cnt] = *argv; 341 for (p = *argv; *p; ++p) 342 if (p[0] == '{' && p[1] == '}') { 343 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 344 new->e_len[cnt] = MAXPATHLEN; 345 break; 346 } 347 if (!*p) { 348 new->e_argv[cnt] = *argv; 349 new->e_len[cnt] = 0; 350 } 351 } 352 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 353 354 *argvp = argv + 1; 355 return (new); 356 } 357 358 /* 359 * -follow functions -- 360 * 361 * Always true, causes symbolic links to be followed on a global 362 * basis. 363 */ 364 PLAN * 365 c_follow(argvp, isok) 366 char ***argvp; 367 int isok; 368 { 369 ftsoptions &= ~FTS_PHYSICAL; 370 ftsoptions |= FTS_LOGICAL; 371 372 return (palloc(N_FOLLOW, f_always_true)); 373 } 374 375 /* 376 * -fstype functions -- 377 * 378 * True if the file is of a certain type. 379 */ 380 int 381 f_fstype(plan, entry) 382 PLAN *plan; 383 FTSENT *entry; 384 { 385 static dev_t curdev; /* need a guaranteed illegal dev value */ 386 static int first = 1; 387 struct statfs sb; 388 static short val; 389 static char fstype[MFSNAMELEN]; 390 char *p, save[2]; 391 392 /* Only check when we cross mount point. */ 393 if (first || curdev != entry->fts_statp->st_dev) { 394 curdev = entry->fts_statp->st_dev; 395 396 /* 397 * Statfs follows symlinks; find wants the link's file system, 398 * not where it points. 399 */ 400 if (entry->fts_info == FTS_SL || 401 entry->fts_info == FTS_SLNONE) { 402 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 403 ++p; 404 else 405 p = entry->fts_accpath; 406 save[0] = p[0]; 407 p[0] = '.'; 408 save[1] = p[1]; 409 p[1] = '\0'; 410 411 } else 412 p = NULL; 413 414 if (statfs(entry->fts_accpath, &sb)) 415 err(1, "%s", entry->fts_accpath); 416 417 if (p) { 418 p[0] = save[0]; 419 p[1] = save[1]; 420 } 421 422 first = 0; 423 424 /* 425 * Further tests may need both of these values, so 426 * always copy both of them. 427 */ 428 val = sb.f_flags; 429 strncpy(fstype, sb.f_fstypename, MFSNAMELEN); 430 } 431 switch (plan->flags) { 432 case F_MTFLAG: 433 return (val & plan->mt_data); 434 case F_MTTYPE: 435 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0); 436 default: 437 abort(); 438 } 439 } 440 441 PLAN * 442 c_fstype(argvp, isok) 443 char ***argvp; 444 int isok; 445 { 446 char *arg = **argvp; 447 PLAN *new; 448 449 (*argvp)++; 450 ftsoptions &= ~FTS_NOSTAT; 451 452 new = palloc(N_FSTYPE, f_fstype); 453 454 switch (*arg) { 455 case 'l': 456 if (!strcmp(arg, "local")) { 457 new->flags = F_MTFLAG; 458 new->mt_data = MNT_LOCAL; 459 return (new); 460 } 461 break; 462 case 'r': 463 if (!strcmp(arg, "rdonly")) { 464 new->flags = F_MTFLAG; 465 new->mt_data = MNT_RDONLY; 466 return (new); 467 } 468 break; 469 } 470 471 new->flags = F_MTTYPE; 472 new->c_data = arg; 473 return (new); 474 } 475 476 /* 477 * -group gname functions -- 478 * 479 * True if the file belongs to the group gname. If gname is numeric and 480 * an equivalent of the getgrnam() function does not return a valid group 481 * name, gname is taken as a group ID. 482 */ 483 int 484 f_group(plan, entry) 485 PLAN *plan; 486 FTSENT *entry; 487 { 488 return (entry->fts_statp->st_gid == plan->g_data); 489 } 490 491 PLAN * 492 c_group(argvp, isok) 493 char ***argvp; 494 int isok; 495 { 496 char *gname = **argvp; 497 PLAN *new; 498 struct group *g; 499 gid_t gid; 500 501 (*argvp)++; 502 ftsoptions &= ~FTS_NOSTAT; 503 504 g = getgrnam(gname); 505 if (g == NULL) { 506 gid = atoi(gname); 507 if (gid == 0 && gname[0] != '0') 508 errx(1, "-group: %s: no such group", gname); 509 } else 510 gid = g->gr_gid; 511 512 new = palloc(N_GROUP, f_group); 513 new->g_data = gid; 514 return (new); 515 } 516 517 /* 518 * -inum n functions -- 519 * 520 * True if the file has inode # n. 521 */ 522 int 523 f_inum(plan, entry) 524 PLAN *plan; 525 FTSENT *entry; 526 { 527 COMPARE(entry->fts_statp->st_ino, plan->i_data); 528 } 529 530 PLAN * 531 c_inum(argvp, isok) 532 char ***argvp; 533 int isok; 534 { 535 char *arg = **argvp; 536 PLAN *new; 537 538 (*argvp)++; 539 ftsoptions &= ~FTS_NOSTAT; 540 541 new = palloc(N_INUM, f_inum); 542 new->i_data = find_parsenum(new, "-inum", arg, NULL); 543 return (new); 544 } 545 546 /* 547 * -links n functions -- 548 * 549 * True if the file has n links. 550 */ 551 int 552 f_links(plan, entry) 553 PLAN *plan; 554 FTSENT *entry; 555 { 556 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 557 } 558 559 PLAN * 560 c_links(argvp, isok) 561 char ***argvp; 562 int isok; 563 { 564 char *arg = **argvp; 565 PLAN *new; 566 567 (*argvp)++; 568 ftsoptions &= ~FTS_NOSTAT; 569 570 new = palloc(N_LINKS, f_links); 571 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 572 return (new); 573 } 574 575 /* 576 * -ls functions -- 577 * 578 * Always true - prints the current entry to stdout in "ls" format. 579 */ 580 int 581 f_ls(plan, entry) 582 PLAN *plan; 583 FTSENT *entry; 584 { 585 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 586 return (1); 587 } 588 589 PLAN * 590 c_ls(argvp, isok) 591 char ***argvp; 592 int isok; 593 { 594 ftsoptions &= ~FTS_NOSTAT; 595 isoutput = 1; 596 597 return (palloc(N_LS, f_ls)); 598 } 599 600 /* 601 * -mtime n functions -- 602 * 603 * True if the difference between the file modification time and the 604 * current time is n 24 hour periods. 605 */ 606 int 607 f_mtime(plan, entry) 608 PLAN *plan; 609 FTSENT *entry; 610 { 611 extern time_t now; 612 613 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / 614 SECSPERDAY, plan->t_data); 615 } 616 617 PLAN * 618 c_mtime(argvp, isok) 619 char ***argvp; 620 int isok; 621 { 622 char *arg = **argvp; 623 PLAN *new; 624 625 (*argvp)++; 626 ftsoptions &= ~FTS_NOSTAT; 627 628 new = palloc(N_MTIME, f_mtime); 629 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 630 TIME_CORRECT(new, N_MTIME); 631 return (new); 632 } 633 634 /* 635 * -name functions -- 636 * 637 * True if the basename of the filename being examined 638 * matches pattern using Pattern Matching Notation S3.14 639 */ 640 int 641 f_name(plan, entry) 642 PLAN *plan; 643 FTSENT *entry; 644 { 645 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 646 } 647 648 PLAN * 649 c_name(argvp, isok) 650 char ***argvp; 651 int isok; 652 { 653 char *pattern = **argvp; 654 PLAN *new; 655 656 (*argvp)++; 657 new = palloc(N_NAME, f_name); 658 new->c_data = pattern; 659 return (new); 660 } 661 662 /* 663 * -newer file functions -- 664 * 665 * True if the current file has been modified more recently 666 * then the modification time of the file named by the pathname 667 * file. 668 */ 669 int 670 f_newer(plan, entry) 671 PLAN *plan; 672 FTSENT *entry; 673 { 674 return (entry->fts_statp->st_mtime > plan->t_data); 675 } 676 677 PLAN * 678 c_newer(argvp, isok) 679 char ***argvp; 680 int isok; 681 { 682 char *filename = **argvp; 683 PLAN *new; 684 struct stat sb; 685 686 (*argvp)++; 687 ftsoptions &= ~FTS_NOSTAT; 688 689 if (stat(filename, &sb)) 690 err(1, "%s", filename); 691 new = palloc(N_NEWER, f_newer); 692 new->t_data = sb.st_mtime; 693 return (new); 694 } 695 696 /* 697 * -nogroup functions -- 698 * 699 * True if file belongs to a user ID for which the equivalent 700 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 701 */ 702 int 703 f_nogroup(plan, entry) 704 PLAN *plan; 705 FTSENT *entry; 706 { 707 708 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 709 } 710 711 PLAN * 712 c_nogroup(argvp, isok) 713 char ***argvp; 714 int isok; 715 { 716 ftsoptions &= ~FTS_NOSTAT; 717 718 return (palloc(N_NOGROUP, f_nogroup)); 719 } 720 721 /* 722 * -nouser functions -- 723 * 724 * True if file belongs to a user ID for which the equivalent 725 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 726 */ 727 int 728 f_nouser(plan, entry) 729 PLAN *plan; 730 FTSENT *entry; 731 { 732 733 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 734 } 735 736 PLAN * 737 c_nouser(argvp, isok) 738 char ***argvp; 739 int isok; 740 { 741 ftsoptions &= ~FTS_NOSTAT; 742 743 return (palloc(N_NOUSER, f_nouser)); 744 } 745 746 /* 747 * -path functions -- 748 * 749 * True if the path of the filename being examined 750 * matches pattern using Pattern Matching Notation S3.14 751 */ 752 int 753 f_path(plan, entry) 754 PLAN *plan; 755 FTSENT *entry; 756 { 757 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 758 } 759 760 PLAN * 761 c_path(argvp, isok) 762 char ***argvp; 763 int isok; 764 { 765 char *pattern = **argvp; 766 PLAN *new; 767 768 (*argvp)++; 769 new = palloc(N_NAME, f_path); 770 new->c_data = pattern; 771 return (new); 772 } 773 774 /* 775 * -perm functions -- 776 * 777 * The mode argument is used to represent file mode bits. If it starts 778 * with a leading digit, it's treated as an octal mode, otherwise as a 779 * symbolic mode. 780 */ 781 int 782 f_perm(plan, entry) 783 PLAN *plan; 784 FTSENT *entry; 785 { 786 mode_t mode; 787 788 mode = entry->fts_statp->st_mode & 789 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 790 if (plan->flags == F_ATLEAST) 791 return ((plan->m_data | mode) == mode); 792 else 793 return (mode == plan->m_data); 794 /* NOTREACHED */ 795 } 796 797 PLAN * 798 c_perm(argvp, isok) 799 char ***argvp; 800 int isok; 801 { 802 char *perm = **argvp; 803 PLAN *new; 804 mode_t *set; 805 806 (*argvp)++; 807 ftsoptions &= ~FTS_NOSTAT; 808 809 new = palloc(N_PERM, f_perm); 810 811 if (*perm == '-') { 812 new->flags = F_ATLEAST; 813 ++perm; 814 } 815 816 if ((set = setmode(perm)) == NULL) 817 err(1, "-perm: %s: illegal mode string", perm); 818 819 new->m_data = getmode(set, 0); 820 return (new); 821 } 822 823 /* 824 * -print functions -- 825 * 826 * Always true, causes the current pathame to be written to 827 * standard output. 828 */ 829 int 830 f_print(plan, entry) 831 PLAN *plan; 832 FTSENT *entry; 833 { 834 (void)printf("%s\n", entry->fts_path); 835 return (1); 836 } 837 838 int 839 f_print0(plan, entry) 840 PLAN *plan; 841 FTSENT *entry; 842 { 843 (void)fputs(entry->fts_path, stdout); 844 (void)fputc('\0', stdout); 845 return (1); 846 } 847 848 PLAN * 849 c_print(argvp, isok) 850 char ***argvp; 851 int isok; 852 { 853 isoutput = 1; 854 855 return (palloc(N_PRINT, f_print)); 856 } 857 858 PLAN * 859 c_print0(argvp, isok) 860 char ***argvp; 861 int isok; 862 { 863 isoutput = 1; 864 865 return (palloc(N_PRINT0, f_print0)); 866 } 867 868 /* 869 * -prune functions -- 870 * 871 * Prune a portion of the hierarchy. 872 */ 873 int 874 f_prune(plan, entry) 875 PLAN *plan; 876 FTSENT *entry; 877 { 878 extern FTS *tree; 879 880 if (fts_set(tree, entry, FTS_SKIP)) 881 err(1, "%s", entry->fts_path); 882 return (1); 883 } 884 885 PLAN * 886 c_prune(argvp, isok) 887 char ***argvp; 888 int isok; 889 { 890 return (palloc(N_PRUNE, f_prune)); 891 } 892 893 /* 894 * -size n[c] functions -- 895 * 896 * True if the file size in bytes, divided by an implementation defined 897 * value and rounded up to the next integer, is n. If n is followed by 898 * a c, the size is in bytes. 899 */ 900 #define FIND_SIZE 512 901 static int divsize = 1; 902 903 int 904 f_size(plan, entry) 905 PLAN *plan; 906 FTSENT *entry; 907 { 908 off_t size; 909 910 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 911 FIND_SIZE : entry->fts_statp->st_size; 912 COMPARE(size, plan->o_data); 913 } 914 915 PLAN * 916 c_size(argvp, isok) 917 char ***argvp; 918 int isok; 919 { 920 char *arg = **argvp; 921 PLAN *new; 922 char endch; 923 924 (*argvp)++; 925 ftsoptions &= ~FTS_NOSTAT; 926 927 new = palloc(N_SIZE, f_size); 928 endch = 'c'; 929 new->o_data = find_parsenum(new, "-size", arg, &endch); 930 if (endch == 'c') 931 divsize = 0; 932 return (new); 933 } 934 935 /* 936 * -type c functions -- 937 * 938 * True if the type of the file is c, where c is b, c, d, p, f or w 939 * for block special file, character special file, directory, FIFO, 940 * regular file or whiteout respectively. 941 */ 942 int 943 f_type(plan, entry) 944 PLAN *plan; 945 FTSENT *entry; 946 { 947 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 948 } 949 950 PLAN * 951 c_type(argvp, isok) 952 char ***argvp; 953 int isok; 954 { 955 char *typestring = **argvp; 956 PLAN *new; 957 mode_t mask; 958 959 (*argvp)++; 960 ftsoptions &= ~FTS_NOSTAT; 961 962 switch (typestring[0]) { 963 #ifdef S_IFWHT 964 case 'W': 965 #ifdef FTS_WHITEOUT 966 ftsoptions |= FTS_WHITEOUT; 967 #endif 968 mask = S_IFWHT; 969 break; 970 #endif 971 case 'b': 972 mask = S_IFBLK; 973 break; 974 case 'c': 975 mask = S_IFCHR; 976 break; 977 case 'd': 978 mask = S_IFDIR; 979 break; 980 case 'f': 981 mask = S_IFREG; 982 break; 983 case 'l': 984 mask = S_IFLNK; 985 break; 986 case 'p': 987 mask = S_IFIFO; 988 break; 989 case 's': 990 mask = S_IFSOCK; 991 break; 992 #ifdef FTS_WHITEOUT 993 case 'w': 994 mask = S_IFWHT; 995 ftsoptions |= FTS_WHITEOUT; 996 break; 997 #endif /* FTS_WHITEOUT */ 998 default: 999 errx(1, "-type: %s: unknown type", typestring); 1000 } 1001 1002 new = palloc(N_TYPE, f_type); 1003 new->m_data = mask; 1004 return (new); 1005 } 1006 1007 /* 1008 * -user uname functions -- 1009 * 1010 * True if the file belongs to the user uname. If uname is numeric and 1011 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1012 * return a valid user name, uname is taken as a user ID. 1013 */ 1014 int 1015 f_user(plan, entry) 1016 PLAN *plan; 1017 FTSENT *entry; 1018 { 1019 return (entry->fts_statp->st_uid == plan->u_data); 1020 } 1021 1022 PLAN * 1023 c_user(argvp, isok) 1024 char ***argvp; 1025 int isok; 1026 { 1027 char *username = **argvp; 1028 PLAN *new; 1029 struct passwd *p; 1030 uid_t uid; 1031 1032 (*argvp)++; 1033 ftsoptions &= ~FTS_NOSTAT; 1034 1035 p = getpwnam(username); 1036 if (p == NULL) { 1037 uid = atoi(username); 1038 if (uid == 0 && username[0] != '0') 1039 errx(1, "-user: %s: no such user", username); 1040 } else 1041 uid = p->pw_uid; 1042 1043 new = palloc(N_USER, f_user); 1044 new->u_data = uid; 1045 return (new); 1046 } 1047 1048 /* 1049 * -xdev functions -- 1050 * 1051 * Always true, causes find not to decend past directories that have a 1052 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1053 */ 1054 PLAN * 1055 c_xdev(argvp, isok) 1056 char ***argvp; 1057 int isok; 1058 { 1059 ftsoptions |= FTS_XDEV; 1060 1061 return (palloc(N_XDEV, f_always_true)); 1062 } 1063 1064 /* 1065 * ( expression ) functions -- 1066 * 1067 * True if expression is true. 1068 */ 1069 int 1070 f_expr(plan, entry) 1071 PLAN *plan; 1072 FTSENT *entry; 1073 { 1074 PLAN *p; 1075 int state; 1076 1077 state = 0; 1078 for (p = plan->p_data[0]; 1079 p && (state = (p->eval)(p, entry)); p = p->next); 1080 return (state); 1081 } 1082 1083 /* 1084 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1085 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1086 * to a N_EXPR node containing the expression and the ')' node is discarded. 1087 */ 1088 PLAN * 1089 c_openparen(argvp, isok) 1090 char ***argvp; 1091 int isok; 1092 { 1093 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1094 } 1095 1096 PLAN * 1097 c_closeparen(argvp, isok) 1098 char ***argvp; 1099 int isok; 1100 { 1101 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1102 } 1103 1104 /* 1105 * ! expression functions -- 1106 * 1107 * Negation of a primary; the unary NOT operator. 1108 */ 1109 int 1110 f_not(plan, entry) 1111 PLAN *plan; 1112 FTSENT *entry; 1113 { 1114 PLAN *p; 1115 int state; 1116 1117 state = 0; 1118 for (p = plan->p_data[0]; 1119 p && (state = (p->eval)(p, entry)); p = p->next); 1120 return (!state); 1121 } 1122 1123 PLAN * 1124 c_not(argvp, isok) 1125 char ***argvp; 1126 int isok; 1127 { 1128 return (palloc(N_NOT, f_not)); 1129 } 1130 1131 /* 1132 * expression -o expression functions -- 1133 * 1134 * Alternation of primaries; the OR operator. The second expression is 1135 * not evaluated if the first expression is true. 1136 */ 1137 int 1138 f_or(plan, entry) 1139 PLAN *plan; 1140 FTSENT *entry; 1141 { 1142 PLAN *p; 1143 int state; 1144 1145 state = 0; 1146 for (p = plan->p_data[0]; 1147 p && (state = (p->eval)(p, entry)); p = p->next); 1148 1149 if (state) 1150 return (1); 1151 1152 for (p = plan->p_data[1]; 1153 p && (state = (p->eval)(p, entry)); p = p->next); 1154 return (state); 1155 } 1156 1157 PLAN * 1158 c_or(argvp, isok) 1159 char ***argvp; 1160 int isok; 1161 { 1162 return (palloc(N_OR, f_or)); 1163 } 1164 1165 PLAN * 1166 c_null(argvp, isok) 1167 char ***argvp; 1168 int isok; 1169 { 1170 return NULL; 1171 } 1172 1173 static PLAN * 1174 palloc(t, f) 1175 enum ntype t; 1176 int (*f) __P((PLAN *, FTSENT *)); 1177 { 1178 PLAN *new; 1179 1180 if ((new = malloc(sizeof(PLAN))) == NULL) 1181 err(1, "%s", ""); 1182 new->type = t; 1183 new->eval = f; 1184 new->flags = 0; 1185 new->next = NULL; 1186 return (new); 1187 } 1188