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