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