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