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