1 /* $NetBSD: function.c,v 1.21 1997/10/19 11:52:32 lukem 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.1 (Berkeley) 6/6/93"; 43 #else 44 __RCSID("$NetBSD: function.c,v 1.21 1997/10/19 11:52:32 lukem 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(arg) 180 char *arg; 181 { 182 PLAN *new; 183 184 ftsoptions &= ~FTS_NOSTAT; 185 186 new = palloc(N_ATIME, f_atime); 187 new->t_data = find_parsenum(new, "-atime", arg, NULL); 188 TIME_CORRECT(new, N_ATIME); 189 return (new); 190 } 191 /* 192 * -ctime n functions -- 193 * 194 * True if the difference between the last change of file 195 * status information and the current time is n 24 hour periods. 196 */ 197 int 198 f_ctime(plan, entry) 199 PLAN *plan; 200 FTSENT *entry; 201 { 202 extern time_t now; 203 204 COMPARE((now - entry->fts_statp->st_ctime + 205 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 206 } 207 208 PLAN * 209 c_ctime(arg) 210 char *arg; 211 { 212 PLAN *new; 213 214 ftsoptions &= ~FTS_NOSTAT; 215 216 new = palloc(N_CTIME, f_ctime); 217 new->t_data = find_parsenum(new, "-ctime", arg, NULL); 218 TIME_CORRECT(new, N_CTIME); 219 return (new); 220 } 221 222 /* 223 * -depth functions -- 224 * 225 * Always true, causes descent of the directory hierarchy to be done 226 * so that all entries in a directory are acted on before the directory 227 * itself. 228 */ 229 int 230 f_always_true(plan, entry) 231 PLAN *plan; 232 FTSENT *entry; 233 { 234 return (1); 235 } 236 237 PLAN * 238 c_depth() 239 { 240 isdepth = 1; 241 242 return (palloc(N_DEPTH, f_always_true)); 243 } 244 245 /* 246 * [-exec | -ok] utility [arg ... ] ; functions -- 247 * 248 * True if the executed utility returns a zero value as exit status. 249 * The end of the primary expression is delimited by a semicolon. If 250 * "{}" occurs anywhere, it gets replaced by the current pathname. 251 * The current directory for the execution of utility is the same as 252 * the current directory when the find utility was started. 253 * 254 * The primary -ok is different in that it requests affirmation of the 255 * user before executing the utility. 256 */ 257 int 258 f_exec(plan, entry) 259 PLAN *plan; 260 FTSENT *entry; 261 { 262 extern int dotfd; 263 int cnt; 264 pid_t pid; 265 int status; 266 267 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 268 if (plan->e_len[cnt]) 269 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 270 entry->fts_path, plan->e_len[cnt]); 271 272 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 273 return (0); 274 275 /* don't mix output of command with find output */ 276 fflush(stdout); 277 fflush(stderr); 278 279 switch (pid = vfork()) { 280 case -1: 281 err(1, "fork"); 282 /* NOTREACHED */ 283 case 0: 284 if (fchdir(dotfd)) { 285 warn("chdir"); 286 _exit(1); 287 } 288 execvp(plan->e_argv[0], plan->e_argv); 289 warn("%s", plan->e_argv[0]); 290 _exit(1); 291 } 292 pid = waitpid(pid, &status, 0); 293 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 294 } 295 296 /* 297 * c_exec -- 298 * build three parallel arrays, one with pointers to the strings passed 299 * on the command line, one with (possibly duplicated) pointers to the 300 * argv array, and one with integer values that are lengths of the 301 * strings, but also flags meaning that the string has to be massaged. 302 */ 303 PLAN * 304 c_exec(argvp, isok) 305 char ***argvp; 306 int isok; 307 { 308 PLAN *new; /* node returned */ 309 int cnt; 310 char **argv, **ap, *p; 311 312 isoutput = 1; 313 314 new = palloc(N_EXEC, f_exec); 315 if (isok) 316 new->flags = F_NEEDOK; 317 318 for (ap = argv = *argvp;; ++ap) { 319 if (!*ap) 320 errx(1, 321 "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 322 if (**ap == ';') 323 break; 324 } 325 326 cnt = ap - *argvp + 1; 327 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 328 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 329 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 330 331 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 332 new->e_orig[cnt] = *argv; 333 for (p = *argv; *p; ++p) 334 if (p[0] == '{' && p[1] == '}') { 335 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 336 new->e_len[cnt] = MAXPATHLEN; 337 break; 338 } 339 if (!*p) { 340 new->e_argv[cnt] = *argv; 341 new->e_len[cnt] = 0; 342 } 343 } 344 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 345 346 *argvp = argv + 1; 347 return (new); 348 } 349 350 /* 351 * -follow functions -- 352 * 353 * Always true, causes symbolic links to be followed on a global 354 * basis. 355 */ 356 PLAN * 357 c_follow() 358 { 359 ftsoptions &= ~FTS_PHYSICAL; 360 ftsoptions |= FTS_LOGICAL; 361 362 return (palloc(N_FOLLOW, f_always_true)); 363 } 364 365 /* 366 * -fstype functions -- 367 * 368 * True if the file is of a certain type. 369 */ 370 int 371 f_fstype(plan, entry) 372 PLAN *plan; 373 FTSENT *entry; 374 { 375 static dev_t curdev; /* need a guaranteed illegal dev value */ 376 static int first = 1; 377 struct statfs sb; 378 static short val; 379 static char fstype[MFSNAMELEN]; 380 char *p, save[2]; 381 382 /* Only check when we cross mount point. */ 383 if (first || curdev != entry->fts_statp->st_dev) { 384 curdev = entry->fts_statp->st_dev; 385 386 /* 387 * Statfs follows symlinks; find wants the link's file system, 388 * not where it points. 389 */ 390 if (entry->fts_info == FTS_SL || 391 entry->fts_info == FTS_SLNONE) { 392 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 393 ++p; 394 else 395 p = entry->fts_accpath; 396 save[0] = p[0]; 397 p[0] = '.'; 398 save[1] = p[1]; 399 p[1] = '\0'; 400 401 } else 402 p = NULL; 403 404 if (statfs(entry->fts_accpath, &sb)) 405 err(1, "%s", entry->fts_accpath); 406 407 if (p) { 408 p[0] = save[0]; 409 p[1] = save[1]; 410 } 411 412 first = 0; 413 414 /* 415 * Further tests may need both of these values, so 416 * always copy both of them. 417 */ 418 val = sb.f_flags; 419 strncpy(fstype, sb.f_fstypename, MFSNAMELEN); 420 } 421 switch (plan->flags) { 422 case F_MTFLAG: 423 return (val & plan->mt_data); 424 case F_MTTYPE: 425 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0); 426 default: 427 abort(); 428 } 429 } 430 431 PLAN * 432 c_fstype(arg) 433 char *arg; 434 { 435 PLAN *new; 436 437 ftsoptions &= ~FTS_NOSTAT; 438 439 new = palloc(N_FSTYPE, f_fstype); 440 switch (*arg) { 441 case 'l': 442 if (!strcmp(arg, "local")) { 443 new->flags = F_MTFLAG; 444 new->mt_data = MNT_LOCAL; 445 return (new); 446 } 447 break; 448 case 'r': 449 if (!strcmp(arg, "rdonly")) { 450 new->flags = F_MTFLAG; 451 new->mt_data = MNT_RDONLY; 452 return (new); 453 } 454 break; 455 } 456 457 new->flags = F_MTTYPE; 458 new->c_data = arg; 459 return (new); 460 } 461 462 /* 463 * -group gname functions -- 464 * 465 * True if the file belongs to the group gname. If gname is numeric and 466 * an equivalent of the getgrnam() function does not return a valid group 467 * name, gname is taken as a group ID. 468 */ 469 int 470 f_group(plan, entry) 471 PLAN *plan; 472 FTSENT *entry; 473 { 474 return (entry->fts_statp->st_gid == plan->g_data); 475 } 476 477 PLAN * 478 c_group(gname) 479 char *gname; 480 { 481 PLAN *new; 482 struct group *g; 483 gid_t gid; 484 485 ftsoptions &= ~FTS_NOSTAT; 486 487 g = getgrnam(gname); 488 if (g == NULL) { 489 gid = atoi(gname); 490 if (gid == 0 && gname[0] != '0') 491 errx(1, "-group: %s: no such group", gname); 492 } else 493 gid = g->gr_gid; 494 495 new = palloc(N_GROUP, f_group); 496 new->g_data = gid; 497 return (new); 498 } 499 500 /* 501 * -inum n functions -- 502 * 503 * True if the file has inode # n. 504 */ 505 int 506 f_inum(plan, entry) 507 PLAN *plan; 508 FTSENT *entry; 509 { 510 COMPARE(entry->fts_statp->st_ino, plan->i_data); 511 } 512 513 PLAN * 514 c_inum(arg) 515 char *arg; 516 { 517 PLAN *new; 518 519 ftsoptions &= ~FTS_NOSTAT; 520 521 new = palloc(N_INUM, f_inum); 522 new->i_data = find_parsenum(new, "-inum", arg, NULL); 523 return (new); 524 } 525 526 /* 527 * -links n functions -- 528 * 529 * True if the file has n links. 530 */ 531 int 532 f_links(plan, entry) 533 PLAN *plan; 534 FTSENT *entry; 535 { 536 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 537 } 538 539 PLAN * 540 c_links(arg) 541 char *arg; 542 { 543 PLAN *new; 544 545 ftsoptions &= ~FTS_NOSTAT; 546 547 new = palloc(N_LINKS, f_links); 548 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 549 return (new); 550 } 551 552 /* 553 * -ls functions -- 554 * 555 * Always true - prints the current entry to stdout in "ls" format. 556 */ 557 int 558 f_ls(plan, entry) 559 PLAN *plan; 560 FTSENT *entry; 561 { 562 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 563 return (1); 564 } 565 566 PLAN * 567 c_ls() 568 { 569 ftsoptions &= ~FTS_NOSTAT; 570 isoutput = 1; 571 572 return (palloc(N_LS, f_ls)); 573 } 574 575 /* 576 * -mtime n functions -- 577 * 578 * True if the difference between the file modification time and the 579 * current time is n 24 hour periods. 580 */ 581 int 582 f_mtime(plan, entry) 583 PLAN *plan; 584 FTSENT *entry; 585 { 586 extern time_t now; 587 588 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / 589 SECSPERDAY, plan->t_data); 590 } 591 592 PLAN * 593 c_mtime(arg) 594 char *arg; 595 { 596 PLAN *new; 597 598 ftsoptions &= ~FTS_NOSTAT; 599 600 new = palloc(N_MTIME, f_mtime); 601 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 602 TIME_CORRECT(new, N_MTIME); 603 return (new); 604 } 605 606 /* 607 * -name functions -- 608 * 609 * True if the basename of the filename being examined 610 * matches pattern using Pattern Matching Notation S3.14 611 */ 612 int 613 f_name(plan, entry) 614 PLAN *plan; 615 FTSENT *entry; 616 { 617 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 618 } 619 620 PLAN * 621 c_name(pattern) 622 char *pattern; 623 { 624 PLAN *new; 625 626 new = palloc(N_NAME, f_name); 627 new->c_data = pattern; 628 return (new); 629 } 630 631 /* 632 * -newer file functions -- 633 * 634 * True if the current file has been modified more recently 635 * then the modification time of the file named by the pathname 636 * file. 637 */ 638 int 639 f_newer(plan, entry) 640 PLAN *plan; 641 FTSENT *entry; 642 { 643 return (entry->fts_statp->st_mtime > plan->t_data); 644 } 645 646 PLAN * 647 c_newer(filename) 648 char *filename; 649 { 650 PLAN *new; 651 struct stat sb; 652 653 ftsoptions &= ~FTS_NOSTAT; 654 655 if (stat(filename, &sb)) 656 err(1, "%s", filename); 657 new = palloc(N_NEWER, f_newer); 658 new->t_data = sb.st_mtime; 659 return (new); 660 } 661 662 /* 663 * -nogroup functions -- 664 * 665 * True if file belongs to a user ID for which the equivalent 666 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 667 */ 668 int 669 f_nogroup(plan, entry) 670 PLAN *plan; 671 FTSENT *entry; 672 { 673 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 674 } 675 676 PLAN * 677 c_nogroup() 678 { 679 ftsoptions &= ~FTS_NOSTAT; 680 681 return (palloc(N_NOGROUP, f_nogroup)); 682 } 683 684 /* 685 * -nouser functions -- 686 * 687 * True if file belongs to a user ID for which the equivalent 688 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 689 */ 690 int 691 f_nouser(plan, entry) 692 PLAN *plan; 693 FTSENT *entry; 694 { 695 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 696 } 697 698 PLAN * 699 c_nouser() 700 { 701 ftsoptions &= ~FTS_NOSTAT; 702 703 return (palloc(N_NOUSER, f_nouser)); 704 } 705 706 /* 707 * -path functions -- 708 * 709 * True if the path of the filename being examined 710 * matches pattern using Pattern Matching Notation S3.14 711 */ 712 int 713 f_path(plan, entry) 714 PLAN *plan; 715 FTSENT *entry; 716 { 717 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 718 } 719 720 PLAN * 721 c_path(pattern) 722 char *pattern; 723 { 724 PLAN *new; 725 726 new = palloc(N_NAME, f_path); 727 new->c_data = pattern; 728 return (new); 729 } 730 731 /* 732 * -perm functions -- 733 * 734 * The mode argument is used to represent file mode bits. If it starts 735 * with a leading digit, it's treated as an octal mode, otherwise as a 736 * symbolic mode. 737 */ 738 int 739 f_perm(plan, entry) 740 PLAN *plan; 741 FTSENT *entry; 742 { 743 mode_t mode; 744 745 mode = entry->fts_statp->st_mode & 746 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 747 if (plan->flags == F_ATLEAST) 748 return ((plan->m_data | mode) == mode); 749 else 750 return (mode == plan->m_data); 751 /* NOTREACHED */ 752 } 753 754 PLAN * 755 c_perm(perm) 756 char *perm; 757 { 758 PLAN *new; 759 mode_t *set; 760 761 ftsoptions &= ~FTS_NOSTAT; 762 763 new = palloc(N_PERM, f_perm); 764 765 if (*perm == '-') { 766 new->flags = F_ATLEAST; 767 ++perm; 768 } 769 770 if ((set = setmode(perm)) == NULL) 771 err(1, "-perm: %s: illegal mode string", perm); 772 773 new->m_data = getmode(set, 0); 774 return (new); 775 } 776 777 /* 778 * -print functions -- 779 * 780 * Always true, causes the current pathame to be written to 781 * standard output. 782 */ 783 int 784 f_print(plan, entry) 785 PLAN *plan; 786 FTSENT *entry; 787 { 788 (void)printf("%s\n", entry->fts_path); 789 return(1); 790 } 791 792 int 793 f_print0(plan, entry) 794 PLAN *plan; 795 FTSENT *entry; 796 { 797 (void)fputs(entry->fts_path, stdout); 798 (void)fputc('\0', stdout); 799 return(1); 800 } 801 802 PLAN * 803 c_print() 804 { 805 isoutput = 1; 806 807 return(palloc(N_PRINT, f_print)); 808 } 809 810 PLAN * 811 c_print0() 812 { 813 isoutput = 1; 814 815 return(palloc(N_PRINT0, f_print0)); 816 } 817 818 /* 819 * -prune functions -- 820 * 821 * Prune a portion of the hierarchy. 822 */ 823 int 824 f_prune(plan, entry) 825 PLAN *plan; 826 FTSENT *entry; 827 { 828 extern FTS *tree; 829 830 if (fts_set(tree, entry, FTS_SKIP)) 831 err(1, "%s", entry->fts_path); 832 return (1); 833 } 834 835 PLAN * 836 c_prune() 837 { 838 return (palloc(N_PRUNE, f_prune)); 839 } 840 841 /* 842 * -size n[c] functions -- 843 * 844 * True if the file size in bytes, divided by an implementation defined 845 * value and rounded up to the next integer, is n. If n is followed by 846 * a c, the size is in bytes. 847 */ 848 #define FIND_SIZE 512 849 static int divsize = 1; 850 851 int 852 f_size(plan, entry) 853 PLAN *plan; 854 FTSENT *entry; 855 { 856 off_t size; 857 858 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 859 FIND_SIZE : entry->fts_statp->st_size; 860 COMPARE(size, plan->o_data); 861 } 862 863 PLAN * 864 c_size(arg) 865 char *arg; 866 { 867 PLAN *new; 868 char endch; 869 870 ftsoptions &= ~FTS_NOSTAT; 871 872 new = palloc(N_SIZE, f_size); 873 endch = 'c'; 874 new->o_data = find_parsenum(new, "-size", arg, &endch); 875 if (endch == 'c') 876 divsize = 0; 877 return (new); 878 } 879 880 /* 881 * -type c functions -- 882 * 883 * True if the type of the file is c, where c is b, c, d, p, or f for 884 * block special file, character special file, directory, FIFO, or 885 * regular file, respectively. 886 */ 887 int 888 f_type(plan, entry) 889 PLAN *plan; 890 FTSENT *entry; 891 { 892 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 893 } 894 895 PLAN * 896 c_type(typestring) 897 char *typestring; 898 { 899 PLAN *new; 900 mode_t mask; 901 902 ftsoptions &= ~FTS_NOSTAT; 903 904 switch (typestring[0]) { 905 #ifdef S_IFWHT 906 case 'W': 907 #ifdef FTS_WHITEOUT 908 ftsoptions |= FTS_WHITEOUT; 909 #endif 910 mask = S_IFWHT; 911 break; 912 #endif 913 case 'b': 914 mask = S_IFBLK; 915 break; 916 case 'c': 917 mask = S_IFCHR; 918 break; 919 case 'd': 920 mask = S_IFDIR; 921 break; 922 case 'f': 923 mask = S_IFREG; 924 break; 925 case 'l': 926 mask = S_IFLNK; 927 break; 928 case 'p': 929 mask = S_IFIFO; 930 break; 931 case 's': 932 mask = S_IFSOCK; 933 break; 934 default: 935 errx(1, "-type: %s: unknown type", typestring); 936 } 937 938 new = palloc(N_TYPE, f_type); 939 new->m_data = mask; 940 return (new); 941 } 942 943 /* 944 * -user uname functions -- 945 * 946 * True if the file belongs to the user uname. If uname is numeric and 947 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 948 * return a valid user name, uname is taken as a user ID. 949 */ 950 int 951 f_user(plan, entry) 952 PLAN *plan; 953 FTSENT *entry; 954 { 955 return (entry->fts_statp->st_uid == plan->u_data); 956 } 957 958 PLAN * 959 c_user(username) 960 char *username; 961 { 962 PLAN *new; 963 struct passwd *p; 964 uid_t uid; 965 966 ftsoptions &= ~FTS_NOSTAT; 967 968 p = getpwnam(username); 969 if (p == NULL) { 970 uid = atoi(username); 971 if (uid == 0 && username[0] != '0') 972 errx(1, "-user: %s: no such user", username); 973 } else 974 uid = p->pw_uid; 975 976 new = palloc(N_USER, f_user); 977 new->u_data = uid; 978 return (new); 979 } 980 981 /* 982 * -xdev functions -- 983 * 984 * Always true, causes find not to decend past directories that have a 985 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 986 */ 987 PLAN * 988 c_xdev() 989 { 990 ftsoptions |= FTS_XDEV; 991 992 return (palloc(N_XDEV, f_always_true)); 993 } 994 995 /* 996 * ( expression ) functions -- 997 * 998 * True if expression is true. 999 */ 1000 int 1001 f_expr(plan, entry) 1002 PLAN *plan; 1003 FTSENT *entry; 1004 { 1005 PLAN *p; 1006 int state; 1007 1008 state = 0; 1009 for (p = plan->p_data[0]; 1010 p && (state = (p->eval)(p, entry)); p = p->next); 1011 return (state); 1012 } 1013 1014 /* 1015 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1016 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1017 * to a N_EXPR node containing the expression and the ')' node is discarded. 1018 */ 1019 PLAN * 1020 c_openparen() 1021 { 1022 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1023 } 1024 1025 PLAN * 1026 c_closeparen() 1027 { 1028 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1029 } 1030 1031 /* 1032 * ! expression functions -- 1033 * 1034 * Negation of a primary; the unary NOT operator. 1035 */ 1036 int 1037 f_not(plan, entry) 1038 PLAN *plan; 1039 FTSENT *entry; 1040 { 1041 PLAN *p; 1042 int state; 1043 1044 state = 0; 1045 for (p = plan->p_data[0]; 1046 p && (state = (p->eval)(p, entry)); p = p->next); 1047 return (!state); 1048 } 1049 1050 PLAN * 1051 c_not() 1052 { 1053 return (palloc(N_NOT, f_not)); 1054 } 1055 1056 /* 1057 * expression -o expression functions -- 1058 * 1059 * Alternation of primaries; the OR operator. The second expression is 1060 * not evaluated if the first expression is true. 1061 */ 1062 int 1063 f_or(plan, entry) 1064 PLAN *plan; 1065 FTSENT *entry; 1066 { 1067 PLAN *p; 1068 int state; 1069 1070 state = 0; 1071 for (p = plan->p_data[0]; 1072 p && (state = (p->eval)(p, entry)); p = p->next); 1073 1074 if (state) 1075 return (1); 1076 1077 for (p = plan->p_data[1]; 1078 p && (state = (p->eval)(p, entry)); p = p->next); 1079 return (state); 1080 } 1081 1082 PLAN * 1083 c_or() 1084 { 1085 return (palloc(N_OR, f_or)); 1086 } 1087 1088 static PLAN * 1089 palloc(t, f) 1090 enum ntype t; 1091 int (*f) __P((PLAN *, FTSENT *)); 1092 { 1093 PLAN *new; 1094 1095 if ((new = malloc(sizeof(PLAN))) != NULL) { 1096 new->type = t; 1097 new->eval = f; 1098 new->flags = 0; 1099 new->next = NULL; 1100 return (new); 1101 } 1102 err(1, "malloc"); 1103 /* NOTREACHED */ 1104 } 1105