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