1 /* $NetBSD: function.c,v 1.28 1999/01/12 00:18:50 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Cimarron D. Taylor of the University of California, Berkeley. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #ifndef lint 41 #if 0 42 static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95"; 43 #else 44 __RCSID("$NetBSD: function.c,v 1.28 1999/01/12 00:18:50 lukem Exp $"); 45 #endif 46 #endif /* not lint */ 47 48 #include <sys/param.h> 49 #include <sys/stat.h> 50 #include <sys/wait.h> 51 #include <sys/mount.h> 52 53 #include <err.h> 54 #include <errno.h> 55 #include <fnmatch.h> 56 #include <fts.h> 57 #include <grp.h> 58 #include <pwd.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <tzfile.h> 63 #include <unistd.h> 64 65 #include "find.h" 66 #include "stat_flags.h" 67 68 #define COMPARE(a, b) { \ 69 switch (plan->flags) { \ 70 case F_EQUAL: \ 71 return (a == b); \ 72 case F_LESSTHAN: \ 73 return (a < b); \ 74 case F_GREATER: \ 75 return (a > b); \ 76 default: \ 77 abort(); \ 78 } \ 79 } 80 81 static long find_parsenum __P((PLAN *, char *, char *, char *)); 82 int f_always_true __P((PLAN *, FTSENT *)); 83 int f_atime __P((PLAN *, FTSENT *)); 84 int f_ctime __P((PLAN *, FTSENT *)); 85 int f_exec __P((PLAN *, FTSENT *)); 86 int f_flags __P((PLAN *, FTSENT *)); 87 int f_fstype __P((PLAN *, FTSENT *)); 88 int f_group __P((PLAN *, FTSENT *)); 89 int f_inum __P((PLAN *, FTSENT *)); 90 int f_links __P((PLAN *, FTSENT *)); 91 int f_ls __P((PLAN *, FTSENT *)); 92 int f_mtime __P((PLAN *, FTSENT *)); 93 int f_name __P((PLAN *, FTSENT *)); 94 int f_newer __P((PLAN *, FTSENT *)); 95 int f_nogroup __P((PLAN *, FTSENT *)); 96 int f_nouser __P((PLAN *, FTSENT *)); 97 int f_path __P((PLAN *, FTSENT *)); 98 int f_perm __P((PLAN *, FTSENT *)); 99 int f_print __P((PLAN *, FTSENT *)); 100 int f_print0 __P((PLAN *, FTSENT *)); 101 int f_printx __P((PLAN *, FTSENT *)); 102 int f_prune __P((PLAN *, FTSENT *)); 103 int f_size __P((PLAN *, FTSENT *)); 104 int f_type __P((PLAN *, FTSENT *)); 105 int f_user __P((PLAN *, FTSENT *)); 106 int f_not __P((PLAN *, FTSENT *)); 107 int f_or __P((PLAN *, FTSENT *)); 108 static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); 109 110 /* 111 * find_parsenum -- 112 * Parse a string of the form [+-]# and return the value. 113 */ 114 static long 115 find_parsenum(plan, option, vp, endch) 116 PLAN *plan; 117 char *option, *vp, *endch; 118 { 119 long value; 120 char *endchar, *str; /* Pointer to character ending conversion. */ 121 122 /* Determine comparison from leading + or -. */ 123 str = vp; 124 switch (*str) { 125 case '+': 126 ++str; 127 plan->flags = F_GREATER; 128 break; 129 case '-': 130 ++str; 131 plan->flags = F_LESSTHAN; 132 break; 133 default: 134 plan->flags = F_EQUAL; 135 break; 136 } 137 138 /* 139 * Convert the string with strtol(). Note, if strtol() returns zero 140 * and endchar points to the beginning of the string we know we have 141 * a syntax error. 142 */ 143 value = strtol(str, &endchar, 10); 144 if (value == 0 && endchar == str) 145 errx(1, "%s: %s: illegal numeric value", option, vp); 146 if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 147 errx(1, "%s: %s: illegal trailing character", option, vp); 148 if (endch) 149 *endch = endchar[0]; 150 return (value); 151 } 152 153 /* 154 * The value of n for the inode times (atime, ctime, and mtime) is a range, 155 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 156 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 157 * user wanted. Correct so that -1 is "less than 1". 158 */ 159 #define TIME_CORRECT(p, ttype) \ 160 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 161 ++((p)->t_data); 162 163 /* 164 * -atime n functions -- 165 * 166 * True if the difference between the file access time and the 167 * current time is n 24 hour periods. 168 */ 169 int 170 f_atime(plan, entry) 171 PLAN *plan; 172 FTSENT *entry; 173 { 174 extern time_t now; 175 176 COMPARE((now - entry->fts_statp->st_atime + 177 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 178 } 179 180 PLAN * 181 c_atime(argvp, isok) 182 char ***argvp; 183 int isok; 184 { 185 char *arg = **argvp; 186 PLAN *new; 187 188 (*argvp)++; 189 ftsoptions &= ~FTS_NOSTAT; 190 191 new = palloc(N_ATIME, f_atime); 192 new->t_data = find_parsenum(new, "-atime", arg, NULL); 193 TIME_CORRECT(new, N_ATIME); 194 return (new); 195 } 196 /* 197 * -ctime n functions -- 198 * 199 * True if the difference between the last change of file 200 * status information and the current time is n 24 hour periods. 201 */ 202 int 203 f_ctime(plan, entry) 204 PLAN *plan; 205 FTSENT *entry; 206 { 207 extern time_t now; 208 209 COMPARE((now - entry->fts_statp->st_ctime + 210 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 211 } 212 213 PLAN * 214 c_ctime(argvp, isok) 215 char ***argvp; 216 int isok; 217 { 218 char *arg = **argvp; 219 PLAN *new; 220 221 (*argvp)++; 222 ftsoptions &= ~FTS_NOSTAT; 223 224 new = palloc(N_CTIME, f_ctime); 225 new->t_data = find_parsenum(new, "-ctime", arg, NULL); 226 TIME_CORRECT(new, N_CTIME); 227 return (new); 228 } 229 230 /* 231 * -depth functions -- 232 * 233 * Always true, causes descent of the directory hierarchy to be done 234 * so that all entries in a directory are acted on before the directory 235 * itself. 236 */ 237 int 238 f_always_true(plan, entry) 239 PLAN *plan; 240 FTSENT *entry; 241 { 242 return (1); 243 } 244 245 PLAN * 246 c_depth(argvp, isok) 247 char ***argvp; 248 int isok; 249 { 250 isdepth = 1; 251 252 return (palloc(N_DEPTH, f_always_true)); 253 } 254 255 /* 256 * [-exec | -ok] utility [arg ... ] ; functions -- 257 * 258 * True if the executed utility returns a zero value as exit status. 259 * The end of the primary expression is delimited by a semicolon. If 260 * "{}" occurs anywhere, it gets replaced by the current pathname. 261 * The current directory for the execution of utility is the same as 262 * the current directory when the find utility was started. 263 * 264 * The primary -ok is different in that it requests affirmation of the 265 * user before executing the utility. 266 */ 267 int 268 f_exec(plan, entry) 269 PLAN *plan; 270 FTSENT *entry; 271 { 272 extern int dotfd; 273 int cnt; 274 pid_t pid; 275 int status; 276 277 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 278 if (plan->e_len[cnt]) 279 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 280 entry->fts_path, plan->e_len[cnt]); 281 282 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 283 return (0); 284 285 /* don't mix output of command with find output */ 286 fflush(stdout); 287 fflush(stderr); 288 289 switch (pid = vfork()) { 290 case -1: 291 err(1, "fork"); 292 /* NOTREACHED */ 293 case 0: 294 if (fchdir(dotfd)) { 295 warn("chdir"); 296 _exit(1); 297 } 298 execvp(plan->e_argv[0], plan->e_argv); 299 warn("%s", plan->e_argv[0]); 300 _exit(1); 301 } 302 pid = waitpid(pid, &status, 0); 303 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 304 } 305 306 /* 307 * c_exec -- 308 * build three parallel arrays, one with pointers to the strings passed 309 * on the command line, one with (possibly duplicated) pointers to the 310 * argv array, and one with integer values that are lengths of the 311 * strings, but also flags meaning that the string has to be massaged. 312 */ 313 PLAN * 314 c_exec(argvp, isok) 315 char ***argvp; 316 int isok; 317 { 318 PLAN *new; /* node returned */ 319 int cnt; 320 char **argv, **ap, *p; 321 322 isoutput = 1; 323 324 new = palloc(N_EXEC, f_exec); 325 if (isok) 326 new->flags = F_NEEDOK; 327 328 for (ap = argv = *argvp;; ++ap) { 329 if (!*ap) 330 errx(1, 331 "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 332 if (**ap == ';') 333 break; 334 } 335 336 cnt = ap - *argvp + 1; 337 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 338 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 339 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 340 341 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 342 new->e_orig[cnt] = *argv; 343 for (p = *argv; *p; ++p) 344 if (p[0] == '{' && p[1] == '}') { 345 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 346 new->e_len[cnt] = MAXPATHLEN; 347 break; 348 } 349 if (!*p) { 350 new->e_argv[cnt] = *argv; 351 new->e_len[cnt] = 0; 352 } 353 } 354 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 355 356 *argvp = argv + 1; 357 return (new); 358 } 359 360 /* 361 * -flags [-]flags functions -- 362 */ 363 int 364 f_flags(plan, entry) 365 PLAN *plan; 366 FTSENT *entry; 367 { 368 u_int32_t flags; 369 370 flags = entry->fts_statp->st_flags; 371 if (plan->flags == F_ATLEAST) 372 return ((plan->f_data | flags) == flags); 373 else 374 return (flags == plan->f_data); 375 /* NOTREACHED */ 376 } 377 378 PLAN * 379 c_flags(argvp, isok) 380 char ***argvp; 381 int isok; 382 { 383 char *flags = **argvp; 384 PLAN *new; 385 u_long flagset; 386 387 (*argvp)++; 388 ftsoptions &= ~FTS_NOSTAT; 389 390 new = palloc(N_FLAGS, f_flags); 391 392 if (*flags == '-') { 393 new->flags = F_ATLEAST; 394 ++flags; 395 } 396 397 flagset = 0; 398 if ((strcmp(flags, "none") != 0) && 399 (string_to_flags(&flags, &flagset, NULL) != 0)) 400 errx(1, "-flags: %s: illegal flags string", flags); 401 new->f_data = flagset; 402 return (new); 403 } 404 405 406 /* 407 * -follow functions -- 408 * 409 * Always true, causes symbolic links to be followed on a global 410 * basis. 411 */ 412 PLAN * 413 c_follow(argvp, isok) 414 char ***argvp; 415 int isok; 416 { 417 ftsoptions &= ~FTS_PHYSICAL; 418 ftsoptions |= FTS_LOGICAL; 419 420 return (palloc(N_FOLLOW, f_always_true)); 421 } 422 423 /* 424 * -fstype functions -- 425 * 426 * True if the file is of a certain type. 427 */ 428 int 429 f_fstype(plan, entry) 430 PLAN *plan; 431 FTSENT *entry; 432 { 433 static dev_t curdev; /* need a guaranteed illegal dev value */ 434 static int first = 1; 435 struct statfs sb; 436 static short val; 437 static char fstype[MFSNAMELEN]; 438 char *p, save[2]; 439 440 /* Only check when we cross mount point. */ 441 if (first || curdev != entry->fts_statp->st_dev) { 442 curdev = entry->fts_statp->st_dev; 443 444 /* 445 * Statfs follows symlinks; find wants the link's file system, 446 * not where it points. 447 */ 448 if (entry->fts_info == FTS_SL || 449 entry->fts_info == FTS_SLNONE) { 450 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 451 ++p; 452 else 453 p = entry->fts_accpath; 454 save[0] = p[0]; 455 p[0] = '.'; 456 save[1] = p[1]; 457 p[1] = '\0'; 458 459 } else 460 p = NULL; 461 462 if (statfs(entry->fts_accpath, &sb)) 463 err(1, "%s", entry->fts_accpath); 464 465 if (p) { 466 p[0] = save[0]; 467 p[1] = save[1]; 468 } 469 470 first = 0; 471 472 /* 473 * Further tests may need both of these values, so 474 * always copy both of them. 475 */ 476 val = sb.f_flags; 477 strncpy(fstype, sb.f_fstypename, MFSNAMELEN); 478 } 479 switch (plan->flags) { 480 case F_MTFLAG: 481 return (val & plan->mt_data); 482 case F_MTTYPE: 483 return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0); 484 default: 485 abort(); 486 } 487 } 488 489 PLAN * 490 c_fstype(argvp, isok) 491 char ***argvp; 492 int isok; 493 { 494 char *arg = **argvp; 495 PLAN *new; 496 497 (*argvp)++; 498 ftsoptions &= ~FTS_NOSTAT; 499 500 new = palloc(N_FSTYPE, f_fstype); 501 502 switch (*arg) { 503 case 'l': 504 if (!strcmp(arg, "local")) { 505 new->flags = F_MTFLAG; 506 new->mt_data = MNT_LOCAL; 507 return (new); 508 } 509 break; 510 case 'r': 511 if (!strcmp(arg, "rdonly")) { 512 new->flags = F_MTFLAG; 513 new->mt_data = MNT_RDONLY; 514 return (new); 515 } 516 break; 517 } 518 519 new->flags = F_MTTYPE; 520 new->c_data = arg; 521 return (new); 522 } 523 524 /* 525 * -group gname functions -- 526 * 527 * True if the file belongs to the group gname. If gname is numeric and 528 * an equivalent of the getgrnam() function does not return a valid group 529 * name, gname is taken as a group ID. 530 */ 531 int 532 f_group(plan, entry) 533 PLAN *plan; 534 FTSENT *entry; 535 { 536 return (entry->fts_statp->st_gid == plan->g_data); 537 } 538 539 PLAN * 540 c_group(argvp, isok) 541 char ***argvp; 542 int isok; 543 { 544 char *gname = **argvp; 545 PLAN *new; 546 struct group *g; 547 gid_t gid; 548 549 (*argvp)++; 550 ftsoptions &= ~FTS_NOSTAT; 551 552 g = getgrnam(gname); 553 if (g == NULL) { 554 gid = atoi(gname); 555 if (gid == 0 && gname[0] != '0') 556 errx(1, "-group: %s: no such group", gname); 557 } else 558 gid = g->gr_gid; 559 560 new = palloc(N_GROUP, f_group); 561 new->g_data = gid; 562 return (new); 563 } 564 565 /* 566 * -inum n functions -- 567 * 568 * True if the file has inode # n. 569 */ 570 int 571 f_inum(plan, entry) 572 PLAN *plan; 573 FTSENT *entry; 574 { 575 COMPARE(entry->fts_statp->st_ino, plan->i_data); 576 } 577 578 PLAN * 579 c_inum(argvp, isok) 580 char ***argvp; 581 int isok; 582 { 583 char *arg = **argvp; 584 PLAN *new; 585 586 (*argvp)++; 587 ftsoptions &= ~FTS_NOSTAT; 588 589 new = palloc(N_INUM, f_inum); 590 new->i_data = find_parsenum(new, "-inum", arg, NULL); 591 return (new); 592 } 593 594 /* 595 * -links n functions -- 596 * 597 * True if the file has n links. 598 */ 599 int 600 f_links(plan, entry) 601 PLAN *plan; 602 FTSENT *entry; 603 { 604 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 605 } 606 607 PLAN * 608 c_links(argvp, isok) 609 char ***argvp; 610 int isok; 611 { 612 char *arg = **argvp; 613 PLAN *new; 614 615 (*argvp)++; 616 ftsoptions &= ~FTS_NOSTAT; 617 618 new = palloc(N_LINKS, f_links); 619 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 620 return (new); 621 } 622 623 /* 624 * -ls functions -- 625 * 626 * Always true - prints the current entry to stdout in "ls" format. 627 */ 628 int 629 f_ls(plan, entry) 630 PLAN *plan; 631 FTSENT *entry; 632 { 633 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 634 return (1); 635 } 636 637 PLAN * 638 c_ls(argvp, isok) 639 char ***argvp; 640 int isok; 641 { 642 ftsoptions &= ~FTS_NOSTAT; 643 isoutput = 1; 644 645 return (palloc(N_LS, f_ls)); 646 } 647 648 /* 649 * -mtime n functions -- 650 * 651 * True if the difference between the file modification time and the 652 * current time is n 24 hour periods. 653 */ 654 int 655 f_mtime(plan, entry) 656 PLAN *plan; 657 FTSENT *entry; 658 { 659 extern time_t now; 660 661 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / 662 SECSPERDAY, plan->t_data); 663 } 664 665 PLAN * 666 c_mtime(argvp, isok) 667 char ***argvp; 668 int isok; 669 { 670 char *arg = **argvp; 671 PLAN *new; 672 673 (*argvp)++; 674 ftsoptions &= ~FTS_NOSTAT; 675 676 new = palloc(N_MTIME, f_mtime); 677 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 678 TIME_CORRECT(new, N_MTIME); 679 return (new); 680 } 681 682 /* 683 * -name functions -- 684 * 685 * True if the basename of the filename being examined 686 * matches pattern using Pattern Matching Notation S3.14 687 */ 688 int 689 f_name(plan, entry) 690 PLAN *plan; 691 FTSENT *entry; 692 { 693 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 694 } 695 696 PLAN * 697 c_name(argvp, isok) 698 char ***argvp; 699 int isok; 700 { 701 char *pattern = **argvp; 702 PLAN *new; 703 704 (*argvp)++; 705 new = palloc(N_NAME, f_name); 706 new->c_data = pattern; 707 return (new); 708 } 709 710 /* 711 * -newer file functions -- 712 * 713 * True if the current file has been modified more recently 714 * then the modification time of the file named by the pathname 715 * file. 716 */ 717 int 718 f_newer(plan, entry) 719 PLAN *plan; 720 FTSENT *entry; 721 { 722 return (entry->fts_statp->st_mtime > plan->t_data); 723 } 724 725 PLAN * 726 c_newer(argvp, isok) 727 char ***argvp; 728 int isok; 729 { 730 char *filename = **argvp; 731 PLAN *new; 732 struct stat sb; 733 734 (*argvp)++; 735 ftsoptions &= ~FTS_NOSTAT; 736 737 if (stat(filename, &sb)) 738 err(1, "%s", filename); 739 new = palloc(N_NEWER, f_newer); 740 new->t_data = sb.st_mtime; 741 return (new); 742 } 743 744 /* 745 * -nogroup functions -- 746 * 747 * True if file belongs to a user ID for which the equivalent 748 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 749 */ 750 int 751 f_nogroup(plan, entry) 752 PLAN *plan; 753 FTSENT *entry; 754 { 755 756 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 757 } 758 759 PLAN * 760 c_nogroup(argvp, isok) 761 char ***argvp; 762 int isok; 763 { 764 ftsoptions &= ~FTS_NOSTAT; 765 766 return (palloc(N_NOGROUP, f_nogroup)); 767 } 768 769 /* 770 * -nouser functions -- 771 * 772 * True if file belongs to a user ID for which the equivalent 773 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 774 */ 775 int 776 f_nouser(plan, entry) 777 PLAN *plan; 778 FTSENT *entry; 779 { 780 781 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 782 } 783 784 PLAN * 785 c_nouser(argvp, isok) 786 char ***argvp; 787 int isok; 788 { 789 ftsoptions &= ~FTS_NOSTAT; 790 791 return (palloc(N_NOUSER, f_nouser)); 792 } 793 794 /* 795 * -path functions -- 796 * 797 * True if the path of the filename being examined 798 * matches pattern using Pattern Matching Notation S3.14 799 */ 800 int 801 f_path(plan, entry) 802 PLAN *plan; 803 FTSENT *entry; 804 { 805 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 806 } 807 808 PLAN * 809 c_path(argvp, isok) 810 char ***argvp; 811 int isok; 812 { 813 char *pattern = **argvp; 814 PLAN *new; 815 816 (*argvp)++; 817 new = palloc(N_NAME, f_path); 818 new->c_data = pattern; 819 return (new); 820 } 821 822 /* 823 * -perm functions -- 824 * 825 * The mode argument is used to represent file mode bits. If it starts 826 * with a leading digit, it's treated as an octal mode, otherwise as a 827 * symbolic mode. 828 */ 829 int 830 f_perm(plan, entry) 831 PLAN *plan; 832 FTSENT *entry; 833 { 834 mode_t mode; 835 836 mode = entry->fts_statp->st_mode & 837 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 838 if (plan->flags == F_ATLEAST) 839 return ((plan->m_data | mode) == mode); 840 else 841 return (mode == plan->m_data); 842 /* NOTREACHED */ 843 } 844 845 PLAN * 846 c_perm(argvp, isok) 847 char ***argvp; 848 int isok; 849 { 850 char *perm = **argvp; 851 PLAN *new; 852 mode_t *set; 853 854 (*argvp)++; 855 ftsoptions &= ~FTS_NOSTAT; 856 857 new = palloc(N_PERM, f_perm); 858 859 if (*perm == '-') { 860 new->flags = F_ATLEAST; 861 ++perm; 862 } 863 864 if ((set = setmode(perm)) == NULL) 865 err(1, "-perm: %s: illegal mode string", perm); 866 867 new->m_data = getmode(set, 0); 868 return (new); 869 } 870 871 /* 872 * -print functions -- 873 * 874 * Always true, causes the current pathame to be written to 875 * standard output. 876 */ 877 int 878 f_print(plan, entry) 879 PLAN *plan; 880 FTSENT *entry; 881 { 882 (void)printf("%s\n", entry->fts_path); 883 return (1); 884 } 885 886 int 887 f_print0(plan, entry) 888 PLAN *plan; 889 FTSENT *entry; 890 { 891 (void)fputs(entry->fts_path, stdout); 892 (void)fputc('\0', stdout); 893 return (1); 894 } 895 896 int 897 f_printx(plan, entry) 898 PLAN *plan; 899 FTSENT *entry; 900 { 901 char *cp; 902 903 for (cp = entry->fts_path; *cp; cp++) { 904 if (*cp == '\'' || *cp == '\"' || *cp == ' ' || 905 *cp == '\t' || *cp == '\n' || *cp == '\\') 906 fputc('\\', stdout); 907 908 fputc(*cp, stdout); 909 } 910 911 fputc('\n', stdout); 912 return 1; 913 } 914 915 PLAN * 916 c_print(argvp, isok) 917 char ***argvp; 918 int isok; 919 { 920 isoutput = 1; 921 922 return (palloc(N_PRINT, f_print)); 923 } 924 925 PLAN * 926 c_print0(argvp, isok) 927 char ***argvp; 928 int isok; 929 { 930 isoutput = 1; 931 932 return (palloc(N_PRINT0, f_print0)); 933 } 934 935 PLAN * 936 c_printx(argvp, isok) 937 char ***argvp; 938 int isok; 939 { 940 isoutput = 1; 941 942 return palloc(N_PRINTX, f_printx); 943 } 944 945 /* 946 * -prune functions -- 947 * 948 * Prune a portion of the hierarchy. 949 */ 950 int 951 f_prune(plan, entry) 952 PLAN *plan; 953 FTSENT *entry; 954 { 955 extern FTS *tree; 956 957 if (fts_set(tree, entry, FTS_SKIP)) 958 err(1, "%s", entry->fts_path); 959 return (1); 960 } 961 962 PLAN * 963 c_prune(argvp, isok) 964 char ***argvp; 965 int isok; 966 { 967 return (palloc(N_PRUNE, f_prune)); 968 } 969 970 /* 971 * -size n[c] functions -- 972 * 973 * True if the file size in bytes, divided by an implementation defined 974 * value and rounded up to the next integer, is n. If n is followed by 975 * a c, the size is in bytes. 976 */ 977 #define FIND_SIZE 512 978 static int divsize = 1; 979 980 int 981 f_size(plan, entry) 982 PLAN *plan; 983 FTSENT *entry; 984 { 985 off_t size; 986 987 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 988 FIND_SIZE : entry->fts_statp->st_size; 989 COMPARE(size, plan->o_data); 990 } 991 992 PLAN * 993 c_size(argvp, isok) 994 char ***argvp; 995 int isok; 996 { 997 char *arg = **argvp; 998 PLAN *new; 999 char endch; 1000 1001 (*argvp)++; 1002 ftsoptions &= ~FTS_NOSTAT; 1003 1004 new = palloc(N_SIZE, f_size); 1005 endch = 'c'; 1006 new->o_data = find_parsenum(new, "-size", arg, &endch); 1007 if (endch == 'c') 1008 divsize = 0; 1009 return (new); 1010 } 1011 1012 /* 1013 * -type c functions -- 1014 * 1015 * True if the type of the file is c, where c is b, c, d, p, f or w 1016 * for block special file, character special file, directory, FIFO, 1017 * regular file or whiteout respectively. 1018 */ 1019 int 1020 f_type(plan, entry) 1021 PLAN *plan; 1022 FTSENT *entry; 1023 { 1024 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1025 } 1026 1027 PLAN * 1028 c_type(argvp, isok) 1029 char ***argvp; 1030 int isok; 1031 { 1032 char *typestring = **argvp; 1033 PLAN *new; 1034 mode_t mask = (mode_t)0; 1035 1036 (*argvp)++; 1037 ftsoptions &= ~FTS_NOSTAT; 1038 1039 switch (typestring[0]) { 1040 #ifdef S_IFWHT 1041 case 'W': 1042 #ifdef FTS_WHITEOUT 1043 ftsoptions |= FTS_WHITEOUT; 1044 #endif 1045 mask = S_IFWHT; 1046 break; 1047 #endif 1048 case 'b': 1049 mask = S_IFBLK; 1050 break; 1051 case 'c': 1052 mask = S_IFCHR; 1053 break; 1054 case 'd': 1055 mask = S_IFDIR; 1056 break; 1057 case 'f': 1058 mask = S_IFREG; 1059 break; 1060 case 'l': 1061 mask = S_IFLNK; 1062 break; 1063 case 'p': 1064 mask = S_IFIFO; 1065 break; 1066 case 's': 1067 mask = S_IFSOCK; 1068 break; 1069 #ifdef FTS_WHITEOUT 1070 case 'w': 1071 mask = S_IFWHT; 1072 ftsoptions |= FTS_WHITEOUT; 1073 break; 1074 #endif /* FTS_WHITEOUT */ 1075 default: 1076 errx(1, "-type: %s: unknown type", typestring); 1077 } 1078 1079 new = palloc(N_TYPE, f_type); 1080 new->m_data = mask; 1081 return (new); 1082 } 1083 1084 /* 1085 * -user uname functions -- 1086 * 1087 * True if the file belongs to the user uname. If uname is numeric and 1088 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1089 * return a valid user name, uname is taken as a user ID. 1090 */ 1091 int 1092 f_user(plan, entry) 1093 PLAN *plan; 1094 FTSENT *entry; 1095 { 1096 return (entry->fts_statp->st_uid == plan->u_data); 1097 } 1098 1099 PLAN * 1100 c_user(argvp, isok) 1101 char ***argvp; 1102 int isok; 1103 { 1104 char *username = **argvp; 1105 PLAN *new; 1106 struct passwd *p; 1107 uid_t uid; 1108 1109 (*argvp)++; 1110 ftsoptions &= ~FTS_NOSTAT; 1111 1112 p = getpwnam(username); 1113 if (p == NULL) { 1114 uid = atoi(username); 1115 if (uid == 0 && username[0] != '0') 1116 errx(1, "-user: %s: no such user", username); 1117 } else 1118 uid = p->pw_uid; 1119 1120 new = palloc(N_USER, f_user); 1121 new->u_data = uid; 1122 return (new); 1123 } 1124 1125 /* 1126 * -xdev functions -- 1127 * 1128 * Always true, causes find not to decend past directories that have a 1129 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1130 */ 1131 PLAN * 1132 c_xdev(argvp, isok) 1133 char ***argvp; 1134 int isok; 1135 { 1136 ftsoptions |= FTS_XDEV; 1137 1138 return (palloc(N_XDEV, f_always_true)); 1139 } 1140 1141 /* 1142 * ( expression ) functions -- 1143 * 1144 * True if expression is true. 1145 */ 1146 int 1147 f_expr(plan, entry) 1148 PLAN *plan; 1149 FTSENT *entry; 1150 { 1151 PLAN *p; 1152 int state; 1153 1154 state = 0; 1155 for (p = plan->p_data[0]; 1156 p && (state = (p->eval)(p, entry)); p = p->next); 1157 return (state); 1158 } 1159 1160 /* 1161 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1162 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1163 * to a N_EXPR node containing the expression and the ')' node is discarded. 1164 */ 1165 PLAN * 1166 c_openparen(argvp, isok) 1167 char ***argvp; 1168 int isok; 1169 { 1170 return (palloc(N_OPENPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1171 } 1172 1173 PLAN * 1174 c_closeparen(argvp, isok) 1175 char ***argvp; 1176 int isok; 1177 { 1178 return (palloc(N_CLOSEPAREN, (int (*) __P((PLAN *, FTSENT *)))-1)); 1179 } 1180 1181 /* 1182 * ! expression functions -- 1183 * 1184 * Negation of a primary; the unary NOT operator. 1185 */ 1186 int 1187 f_not(plan, entry) 1188 PLAN *plan; 1189 FTSENT *entry; 1190 { 1191 PLAN *p; 1192 int state; 1193 1194 state = 0; 1195 for (p = plan->p_data[0]; 1196 p && (state = (p->eval)(p, entry)); p = p->next); 1197 return (!state); 1198 } 1199 1200 PLAN * 1201 c_not(argvp, isok) 1202 char ***argvp; 1203 int isok; 1204 { 1205 return (palloc(N_NOT, f_not)); 1206 } 1207 1208 /* 1209 * expression -o expression functions -- 1210 * 1211 * Alternation of primaries; the OR operator. The second expression is 1212 * not evaluated if the first expression is true. 1213 */ 1214 int 1215 f_or(plan, entry) 1216 PLAN *plan; 1217 FTSENT *entry; 1218 { 1219 PLAN *p; 1220 int state; 1221 1222 state = 0; 1223 for (p = plan->p_data[0]; 1224 p && (state = (p->eval)(p, entry)); p = p->next); 1225 1226 if (state) 1227 return (1); 1228 1229 for (p = plan->p_data[1]; 1230 p && (state = (p->eval)(p, entry)); p = p->next); 1231 return (state); 1232 } 1233 1234 PLAN * 1235 c_or(argvp, isok) 1236 char ***argvp; 1237 int isok; 1238 { 1239 return (palloc(N_OR, f_or)); 1240 } 1241 1242 PLAN * 1243 c_null(argvp, isok) 1244 char ***argvp; 1245 int isok; 1246 { 1247 return NULL; 1248 } 1249 1250 static PLAN * 1251 palloc(t, f) 1252 enum ntype t; 1253 int (*f) __P((PLAN *, FTSENT *)); 1254 { 1255 PLAN *new; 1256 1257 if ((new = malloc(sizeof(PLAN))) == NULL) 1258 err(1, "%s", ""); 1259 new->type = t; 1260 new->eval = f; 1261 new->flags = 0; 1262 new->next = NULL; 1263 return (new); 1264 } 1265