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