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