function.c revision 30395
1254721Semaste/*- 2254721Semaste * Copyright (c) 1990, 1993 3254721Semaste * The Regents of the University of California. All rights reserved. 4254721Semaste * 5254721Semaste * This code is derived from software contributed to Berkeley by 6254721Semaste * Cimarron D. Taylor of the University of California, Berkeley. 7254721Semaste * 8254721Semaste * Redistribution and use in source and binary forms, with or without 9254721Semaste * modification, are permitted provided that the following conditions 10254721Semaste * are met: 11254721Semaste * 1. Redistributions of source code must retain the above copyright 12254721Semaste * notice, this list of conditions and the following disclaimer. 13254721Semaste * 2. Redistributions in binary form must reproduce the above copyright 14254721Semaste * notice, this list of conditions and the following disclaimer in the 15254721Semaste * documentation and/or other materials provided with the distribution. 16254721Semaste * 3. All advertising materials mentioning features or use of this software 17254721Semaste * must display the following acknowledgement: 18254721Semaste * This product includes software developed by the University of 19254721Semaste * California, Berkeley and its contributors. 20254721Semaste * 4. Neither the name of the University nor the names of its contributors 21254721Semaste * may be used to endorse or promote products derived from this software 22254721Semaste * without specific prior written permission. 23254721Semaste * 24254721Semaste * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27254721Semaste * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34254721Semaste * SUCH DAMAGE. 35254721Semaste */ 36254721Semaste 37254721Semaste#ifndef lint 38254721Semastestatic char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; 39254721Semaste#endif /* not lint */ 40254721Semaste 41254721Semaste#include <sys/param.h> 42254721Semaste#include <sys/ucred.h> 43254721Semaste#include <sys/stat.h> 44254721Semaste#include <sys/wait.h> 45254721Semaste#include <sys/mount.h> 46254721Semaste 47254721Semaste#include <err.h> 48254721Semaste#include <errno.h> 49254721Semaste#include <fnmatch.h> 50254721Semaste#include <fts.h> 51254721Semaste#include <grp.h> 52254721Semaste#include <pwd.h> 53254721Semaste#include <stdio.h> 54254721Semaste#include <stdlib.h> 55254721Semaste#include <string.h> 56254721Semaste#include <unistd.h> 57254721Semaste 58254721Semaste#include "find.h" 59254721Semaste 60254721Semaste#define COMPARE(a, b) { \ 61254721Semaste switch (plan->flags) { \ 62254721Semaste case F_EQUAL: \ 63254721Semaste return (a == b); \ 64254721Semaste case F_LESSTHAN: \ 65254721Semaste return (a < b); \ 66254721Semaste case F_GREATER: \ 67254721Semaste return (a > b); \ 68254721Semaste default: \ 69254721Semaste abort(); \ 70254721Semaste } \ 71254721Semaste} 72254721Semaste 73254721Semastestatic PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); 74254721Semaste 75254721Semaste/* 76254721Semaste * find_parsenum -- 77254721Semaste * Parse a string of the form [+-]# and return the value. 78254721Semaste */ 79254721Semastestatic long long 80254721Semastefind_parsenum(plan, option, vp, endch) 81254721Semaste PLAN *plan; 82254721Semaste char *option, *vp, *endch; 83254721Semaste{ 84254721Semaste long long value; 85254721Semaste char *endchar, *str; /* Pointer to character ending conversion. */ 86254721Semaste 87254721Semaste /* Determine comparison from leading + or -. */ 88254721Semaste str = vp; 89254721Semaste switch (*str) { 90254721Semaste case '+': 91254721Semaste ++str; 92254721Semaste plan->flags = F_GREATER; 93254721Semaste break; 94254721Semaste case '-': 95254721Semaste ++str; 96254721Semaste plan->flags = F_LESSTHAN; 97254721Semaste break; 98254721Semaste default: 99254721Semaste plan->flags = F_EQUAL; 100254721Semaste break; 101254721Semaste } 102254721Semaste 103254721Semaste /* 104254721Semaste * Convert the string with strtoq(). Note, if strtoq() returns zero 105254721Semaste * and endchar points to the beginning of the string we know we have 106254721Semaste * a syntax error. 107254721Semaste */ 108254721Semaste value = strtoq(str, &endchar, 10); 109254721Semaste if (value == 0 && endchar == str) 110254721Semaste errx(1, "%s: %s: illegal numeric value", option, vp); 111254721Semaste if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 112254721Semaste errx(1, "%s: %s: illegal trailing character", option, vp); 113254721Semaste if (endch) 114254721Semaste *endch = endchar[0]; 115254721Semaste return (value); 116254721Semaste} 117254721Semaste 118254721Semaste/* 119254721Semaste * The value of n for the inode times (atime, ctime, and mtime) is a range, 120254721Semaste * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 121254721Semaste * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 122254721Semaste * user wanted. Correct so that -1 is "less than 1". 123254721Semaste */ 124254721Semaste#define TIME_CORRECT(p, ttype) \ 125254721Semaste if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 126254721Semaste ++((p)->t_data); 127254721Semaste 128254721Semaste/* 129254721Semaste * -amin n functions -- 130254721Semaste * 131254721Semaste * True if the difference between the file access time and the 132254721Semaste * current time is n min periods. 133254721Semaste */ 134254721Semasteint 135254721Semastef_amin(plan, entry) 136254721Semaste PLAN *plan; 137254721Semaste FTSENT *entry; 138254721Semaste{ 139254721Semaste extern time_t now; 140254721Semaste 141254721Semaste COMPARE((now - entry->fts_statp->st_atime + 142254721Semaste 60 - 1) / 60, plan->t_data); 143254721Semaste} 144254721Semaste 145254721SemastePLAN * 146254721Semastec_amin(arg) 147254721Semaste char *arg; 148254721Semaste{ 149254721Semaste PLAN *new; 150254721Semaste 151254721Semaste ftsoptions &= ~FTS_NOSTAT; 152254721Semaste 153254721Semaste new = palloc(N_AMIN, f_amin); 154254721Semaste new->t_data = find_parsenum(new, "-amin", arg, NULL); 155254721Semaste TIME_CORRECT(new, N_AMIN); 156254721Semaste return (new); 157254721Semaste} 158254721Semaste 159254721Semaste 160254721Semaste/* 161254721Semaste * -atime n functions -- 162254721Semaste * 163254721Semaste * True if the difference between the file access time and the 164254721Semaste * current time is n 24 hour periods. 165254721Semaste */ 166254721Semasteint 167254721Semastef_atime(plan, entry) 168254721Semaste PLAN *plan; 169254721Semaste FTSENT *entry; 170254721Semaste{ 171254721Semaste extern time_t now; 172254721Semaste 173254721Semaste COMPARE((now - entry->fts_statp->st_atime + 174254721Semaste 86400 - 1) / 86400, plan->t_data); 175254721Semaste} 176254721Semaste 177254721SemastePLAN * 178254721Semastec_atime(arg) 179254721Semaste char *arg; 180254721Semaste{ 181254721Semaste PLAN *new; 182254721Semaste 183254721Semaste ftsoptions &= ~FTS_NOSTAT; 184254721Semaste 185254721Semaste new = palloc(N_ATIME, f_atime); 186254721Semaste new->t_data = find_parsenum(new, "-atime", arg, NULL); 187254721Semaste TIME_CORRECT(new, N_ATIME); 188254721Semaste return (new); 189254721Semaste} 190254721Semaste 191254721Semaste 192254721Semaste/* 193254721Semaste * -cmin n functions -- 194254721Semaste * 195254721Semaste * True if the difference between the last change of file 196254721Semaste * status information and the current time is n min periods. 197254721Semaste */ 198254721Semasteint 199254721Semastef_cmin(plan, entry) 200254721Semaste PLAN *plan; 201254721Semaste FTSENT *entry; 202254721Semaste{ 203254721Semaste extern time_t now; 204254721Semaste 205254721Semaste COMPARE((now - entry->fts_statp->st_ctime + 206254721Semaste 60 - 1) / 60, plan->t_data); 207254721Semaste} 208254721Semaste 209254721SemastePLAN * 210254721Semastec_cmin(arg) 211254721Semaste char *arg; 212254721Semaste{ 213254721Semaste PLAN *new; 214254721Semaste 215254721Semaste ftsoptions &= ~FTS_NOSTAT; 216254721Semaste 217254721Semaste new = palloc(N_CMIN, f_cmin); 218254721Semaste new->t_data = find_parsenum(new, "-cmin", arg, NULL); 219254721Semaste TIME_CORRECT(new, N_CMIN); 220254721Semaste return (new); 221254721Semaste} 222254721Semaste 223254721Semaste/* 224254721Semaste * -ctime n functions -- 225254721Semaste * 226254721Semaste * True if the difference between the last change of file 227254721Semaste * status information and the current time is n 24 hour periods. 228254721Semaste */ 229254721Semasteint 230254721Semastef_ctime(plan, entry) 231254721Semaste PLAN *plan; 232254721Semaste FTSENT *entry; 233254721Semaste{ 234254721Semaste extern time_t now; 235254721Semaste 236254721Semaste COMPARE((now - entry->fts_statp->st_ctime + 237254721Semaste 86400 - 1) / 86400, plan->t_data); 238254721Semaste} 239254721Semaste 240254721SemastePLAN * 241254721Semastec_ctime(arg) 242254721Semaste char *arg; 243254721Semaste{ 244254721Semaste PLAN *new; 245254721Semaste 246254721Semaste ftsoptions &= ~FTS_NOSTAT; 247254721Semaste 248254721Semaste new = palloc(N_CTIME, f_ctime); 249254721Semaste new->t_data = find_parsenum(new, "-ctime", arg, NULL); 250254721Semaste TIME_CORRECT(new, N_CTIME); 251254721Semaste return (new); 252254721Semaste} 253254721Semaste 254254721Semaste 255254721Semaste/* 256254721Semaste * -depth functions -- 257254721Semaste * 258254721Semaste * Always true, causes descent of the directory hierarchy to be done 259254721Semaste * so that all entries in a directory are acted on before the directory 260254721Semaste * itself. 261254721Semaste */ 262254721Semasteint 263254721Semastef_always_true(plan, entry) 264254721Semaste PLAN *plan; 265254721Semaste FTSENT *entry; 266254721Semaste{ 267254721Semaste return (1); 268254721Semaste} 269254721Semaste 270254721SemastePLAN * 271254721Semastec_depth() 272254721Semaste{ 273254721Semaste isdepth = 1; 274254721Semaste 275254721Semaste return (palloc(N_DEPTH, f_always_true)); 276254721Semaste} 277254721Semaste 278254721Semaste/* 279254721Semaste * [-exec | -ok] utility [arg ... ] ; functions -- 280254721Semaste * 281254721Semaste * True if the executed utility returns a zero value as exit status. 282254721Semaste * The end of the primary expression is delimited by a semicolon. If 283254721Semaste * "{}" occurs anywhere, it gets replaced by the current pathname. 284254721Semaste * The current directory for the execution of utility is the same as 285254721Semaste * the current directory when the find utility was started. 286254721Semaste * 287254721Semaste * The primary -ok is different in that it requests affirmation of the 288254721Semaste * user before executing the utility. 289254721Semaste */ 290254721Semasteint 291254721Semastef_exec(plan, entry) 292254721Semaste register PLAN *plan; 293254721Semaste FTSENT *entry; 294254721Semaste{ 295254721Semaste extern int dotfd; 296254721Semaste register int cnt; 297254721Semaste pid_t pid; 298254721Semaste int status; 299254721Semaste 300254721Semaste for (cnt = 0; plan->e_argv[cnt]; ++cnt) 301254721Semaste if (plan->e_len[cnt]) 302254721Semaste brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 303254721Semaste entry->fts_path, plan->e_len[cnt]); 304254721Semaste 305254721Semaste if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 306254721Semaste return (0); 307254721Semaste 308254721Semaste /* make sure find output is interspersed correctly with subprocesses */ 309254721Semaste fflush(stdout); 310254721Semaste 311254721Semaste switch (pid = vfork()) { 312254721Semaste case -1: 313254721Semaste err(1, "fork"); 314254721Semaste /* NOTREACHED */ 315254721Semaste case 0: 316254721Semaste if (fchdir(dotfd)) { 317254721Semaste warn("chdir"); 318254721Semaste _exit(1); 319254721Semaste } 320254721Semaste execvp(plan->e_argv[0], plan->e_argv); 321254721Semaste warn("%s", plan->e_argv[0]); 322254721Semaste _exit(1); 323254721Semaste } 324254721Semaste pid = waitpid(pid, &status, 0); 325254721Semaste return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 326254721Semaste} 327254721Semaste 328254721Semaste/* 329254721Semaste * c_exec -- 330254721Semaste * build three parallel arrays, one with pointers to the strings passed 331254721Semaste * on the command line, one with (possibly duplicated) pointers to the 332254721Semaste * argv array, and one with integer values that are lengths of the 333254721Semaste * strings, but also flags meaning that the string has to be massaged. 334254721Semaste */ 335254721SemastePLAN * 336254721Semastec_exec(argvp, isok) 337254721Semaste char ***argvp; 338254721Semaste int isok; 339254721Semaste{ 340254721Semaste PLAN *new; /* node returned */ 341254721Semaste register int cnt; 342254721Semaste register char **argv, **ap, *p; 343254721Semaste 344254721Semaste isoutput = 1; 345254721Semaste 346254721Semaste new = palloc(N_EXEC, f_exec); 347254721Semaste if (isok) 348254721Semaste new->flags = F_NEEDOK; 349254721Semaste 350254721Semaste for (ap = argv = *argvp;; ++ap) { 351254721Semaste if (!*ap) 352254721Semaste errx(1, 353254721Semaste "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 354254721Semaste if (**ap == ';') 355254721Semaste break; 356254721Semaste } 357254721Semaste 358254721Semaste cnt = ap - *argvp + 1; 359254721Semaste new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 360254721Semaste new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 361254721Semaste new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 362254721Semaste 363254721Semaste for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 364254721Semaste new->e_orig[cnt] = *argv; 365254721Semaste for (p = *argv; *p; ++p) 366254721Semaste if (p[0] == '{' && p[1] == '}') { 367254721Semaste new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 368254721Semaste new->e_len[cnt] = MAXPATHLEN; 369254721Semaste break; 370254721Semaste } 371254721Semaste if (!*p) { 372254721Semaste new->e_argv[cnt] = *argv; 373254721Semaste new->e_len[cnt] = 0; 374254721Semaste } 375254721Semaste } 376254721Semaste new->e_argv[cnt] = new->e_orig[cnt] = NULL; 377254721Semaste 378254721Semaste *argvp = argv + 1; 379254721Semaste return (new); 380254721Semaste} 381254721Semaste 382254721Semaste/* 383254721Semaste * -execdir utility [arg ... ] ; functions -- 384254721Semaste * 385254721Semaste * True if the executed utility returns a zero value as exit status. 386254721Semaste * The end of the primary expression is delimited by a semicolon. If 387 * "{}" occurs anywhere, it gets replaced by the unqualified pathname. 388 * The current directory for the execution of utility is the same as 389 * the directory where the file lives. 390 */ 391int 392f_execdir(plan, entry) 393 register PLAN *plan; 394 FTSENT *entry; 395{ 396 extern int dotfd; 397 register int cnt; 398 pid_t pid; 399 int status; 400 char *file; 401 402 /* XXX - if file/dir ends in '/' this will not work -- can it? */ 403 if ((file = strrchr(entry->fts_path, '/'))) 404 file++; 405 else 406 file = entry->fts_path; 407 408 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 409 if (plan->e_len[cnt]) 410 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 411 file, plan->e_len[cnt]); 412 413 /* don't mix output of command with find output */ 414 fflush(stdout); 415 fflush(stderr); 416 417 switch (pid = vfork()) { 418 case -1: 419 err(1, "fork"); 420 /* NOTREACHED */ 421 case 0: 422 execvp(plan->e_argv[0], plan->e_argv); 423 warn("%s", plan->e_argv[0]); 424 _exit(1); 425 } 426 pid = waitpid(pid, &status, 0); 427 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 428} 429 430/* 431 * c_execdir -- 432 * build three parallel arrays, one with pointers to the strings passed 433 * on the command line, one with (possibly duplicated) pointers to the 434 * argv array, and one with integer values that are lengths of the 435 * strings, but also flags meaning that the string has to be massaged. 436 */ 437PLAN * 438c_execdir(argvp) 439 char ***argvp; 440{ 441 PLAN *new; /* node returned */ 442 register int cnt; 443 register char **argv, **ap, *p; 444 445 ftsoptions &= ~FTS_NOSTAT; 446 isoutput = 1; 447 448 new = palloc(N_EXECDIR, f_execdir); 449 450 for (ap = argv = *argvp;; ++ap) { 451 if (!*ap) 452 errx(1, 453 "-execdir: no terminating \";\""); 454 if (**ap == ';') 455 break; 456 } 457 458 cnt = ap - *argvp + 1; 459 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 460 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 461 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 462 463 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 464 new->e_orig[cnt] = *argv; 465 for (p = *argv; *p; ++p) 466 if (p[0] == '{' && p[1] == '}') { 467 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 468 new->e_len[cnt] = MAXPATHLEN; 469 break; 470 } 471 if (!*p) { 472 new->e_argv[cnt] = *argv; 473 new->e_len[cnt] = 0; 474 } 475 } 476 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 477 478 *argvp = argv + 1; 479 return (new); 480} 481 482/* 483 * -follow functions -- 484 * 485 * Always true, causes symbolic links to be followed on a global 486 * basis. 487 */ 488PLAN * 489c_follow() 490{ 491 ftsoptions &= ~FTS_PHYSICAL; 492 ftsoptions |= FTS_LOGICAL; 493 494 return (palloc(N_FOLLOW, f_always_true)); 495} 496 497/* 498 * -fstype functions -- 499 * 500 * True if the file is of a certain type. 501 */ 502int 503f_fstype(plan, entry) 504 PLAN *plan; 505 FTSENT *entry; 506{ 507 static dev_t curdev; /* need a guaranteed illegal dev value */ 508 static int first = 1; 509 struct statfs sb; 510 static int val_type, val_flags; 511 char *p, save[2]; 512 513 /* Only check when we cross mount point. */ 514 if (first || curdev != entry->fts_statp->st_dev) { 515 curdev = entry->fts_statp->st_dev; 516 517 /* 518 * Statfs follows symlinks; find wants the link's file system, 519 * not where it points. 520 */ 521 if (entry->fts_info == FTS_SL || 522 entry->fts_info == FTS_SLNONE) { 523 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 524 ++p; 525 else 526 p = entry->fts_accpath; 527 save[0] = p[0]; 528 p[0] = '.'; 529 save[1] = p[1]; 530 p[1] = '\0'; 531 532 } else 533 p = NULL; 534 535 if (statfs(entry->fts_accpath, &sb)) 536 err(1, "%s", entry->fts_accpath); 537 538 if (p) { 539 p[0] = save[0]; 540 p[1] = save[1]; 541 } 542 543 first = 0; 544 545 /* 546 * Further tests may need both of these values, so 547 * always copy both of them. 548 */ 549 val_flags = sb.f_flags; 550 val_type = sb.f_type; 551 } 552 switch (plan->flags) { 553 case F_MTFLAG: 554 return (val_flags & plan->mt_data) != 0; 555 case F_MTTYPE: 556 return (val_type == plan->mt_data); 557 default: 558 abort(); 559 } 560} 561 562PLAN * 563c_fstype(arg) 564 char *arg; 565{ 566 register PLAN *new; 567 struct vfsconf vfc; 568 569 ftsoptions &= ~FTS_NOSTAT; 570 571 new = palloc(N_FSTYPE, f_fstype); 572 573 /* 574 * Check first for a filesystem name. 575 */ 576 if (getvfsbyname(arg, &vfc) == 0) { 577 new->flags = F_MTTYPE; 578 new->mt_data = vfc.vfc_typenum; 579 return (new); 580 } 581 582 switch (*arg) { 583 case 'l': 584 if (!strcmp(arg, "local")) { 585 new->flags = F_MTFLAG; 586 new->mt_data = MNT_LOCAL; 587 return (new); 588 } 589 break; 590 case 'r': 591 if (!strcmp(arg, "rdonly")) { 592 new->flags = F_MTFLAG; 593 new->mt_data = MNT_RDONLY; 594 return (new); 595 } 596 break; 597 } 598 errx(1, "%s: unknown file type", arg); 599 /* NOTREACHED */ 600} 601 602/* 603 * -group gname functions -- 604 * 605 * True if the file belongs to the group gname. If gname is numeric and 606 * an equivalent of the getgrnam() function does not return a valid group 607 * name, gname is taken as a group ID. 608 */ 609int 610f_group(plan, entry) 611 PLAN *plan; 612 FTSENT *entry; 613{ 614 return (entry->fts_statp->st_gid == plan->g_data); 615} 616 617PLAN * 618c_group(gname) 619 char *gname; 620{ 621 PLAN *new; 622 struct group *g; 623 gid_t gid; 624 625 ftsoptions &= ~FTS_NOSTAT; 626 627 g = getgrnam(gname); 628 if (g == NULL) { 629 gid = atoi(gname); 630 if (gid == 0 && gname[0] != '0') 631 errx(1, "-group: %s: no such group", gname); 632 } else 633 gid = g->gr_gid; 634 635 new = palloc(N_GROUP, f_group); 636 new->g_data = gid; 637 return (new); 638} 639 640/* 641 * -inum n functions -- 642 * 643 * True if the file has inode # n. 644 */ 645int 646f_inum(plan, entry) 647 PLAN *plan; 648 FTSENT *entry; 649{ 650 COMPARE(entry->fts_statp->st_ino, plan->i_data); 651} 652 653PLAN * 654c_inum(arg) 655 char *arg; 656{ 657 PLAN *new; 658 659 ftsoptions &= ~FTS_NOSTAT; 660 661 new = palloc(N_INUM, f_inum); 662 new->i_data = find_parsenum(new, "-inum", arg, NULL); 663 return (new); 664} 665 666/* 667 * -links n functions -- 668 * 669 * True if the file has n links. 670 */ 671int 672f_links(plan, entry) 673 PLAN *plan; 674 FTSENT *entry; 675{ 676 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 677} 678 679PLAN * 680c_links(arg) 681 char *arg; 682{ 683 PLAN *new; 684 685 ftsoptions &= ~FTS_NOSTAT; 686 687 new = palloc(N_LINKS, f_links); 688 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 689 return (new); 690} 691 692/* 693 * -ls functions -- 694 * 695 * Always true - prints the current entry to stdout in "ls" format. 696 */ 697int 698f_ls(plan, entry) 699 PLAN *plan; 700 FTSENT *entry; 701{ 702 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 703 return (1); 704} 705 706PLAN * 707c_ls() 708{ 709 ftsoptions &= ~FTS_NOSTAT; 710 isoutput = 1; 711 712 return (palloc(N_LS, f_ls)); 713} 714 715/* 716 * -mtime n functions -- 717 * 718 * True if the difference between the file modification time and the 719 * current time is n 24 hour periods. 720 */ 721int 722f_mtime(plan, entry) 723 PLAN *plan; 724 FTSENT *entry; 725{ 726 extern time_t now; 727 728 COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / 729 86400, plan->t_data); 730} 731 732PLAN * 733c_mtime(arg) 734 char *arg; 735{ 736 PLAN *new; 737 738 ftsoptions &= ~FTS_NOSTAT; 739 740 new = palloc(N_MTIME, f_mtime); 741 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 742 TIME_CORRECT(new, N_MTIME); 743 return (new); 744} 745 746/* 747 * -mmin n functions -- 748 * 749 * True if the difference between the file modification time and the 750 * current time is n min periods. 751 */ 752int 753f_mmin(plan, entry) 754 PLAN *plan; 755 FTSENT *entry; 756{ 757 extern time_t now; 758 759 COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / 760 60, plan->t_data); 761} 762 763PLAN * 764c_mmin(arg) 765 char *arg; 766{ 767 PLAN *new; 768 769 ftsoptions &= ~FTS_NOSTAT; 770 771 new = palloc(N_MMIN, f_mmin); 772 new->t_data = find_parsenum(new, "-mmin", arg, NULL); 773 TIME_CORRECT(new, N_MMIN); 774 return (new); 775} 776 777 778/* 779 * -name functions -- 780 * 781 * True if the basename of the filename being examined 782 * matches pattern using Pattern Matching Notation S3.14 783 */ 784int 785f_name(plan, entry) 786 PLAN *plan; 787 FTSENT *entry; 788{ 789 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 790} 791 792PLAN * 793c_name(pattern) 794 char *pattern; 795{ 796 PLAN *new; 797 798 new = palloc(N_NAME, f_name); 799 new->c_data = pattern; 800 return (new); 801} 802 803/* 804 * -newer file functions -- 805 * 806 * True if the current file has been modified more recently 807 * then the modification time of the file named by the pathname 808 * file. 809 */ 810int 811f_newer(plan, entry) 812 PLAN *plan; 813 FTSENT *entry; 814{ 815 return (entry->fts_statp->st_mtime > plan->t_data); 816} 817 818PLAN * 819c_newer(filename) 820 char *filename; 821{ 822 PLAN *new; 823 struct stat sb; 824 825 ftsoptions &= ~FTS_NOSTAT; 826 827 if (stat(filename, &sb)) 828 err(1, "%s", filename); 829 new = palloc(N_NEWER, f_newer); 830 new->t_data = sb.st_mtime; 831 return (new); 832} 833 834/* 835 * -nogroup functions -- 836 * 837 * True if file belongs to a user ID for which the equivalent 838 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 839 */ 840int 841f_nogroup(plan, entry) 842 PLAN *plan; 843 FTSENT *entry; 844{ 845 char *group_from_gid(); 846 847 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 848} 849 850PLAN * 851c_nogroup() 852{ 853 ftsoptions &= ~FTS_NOSTAT; 854 855 return (palloc(N_NOGROUP, f_nogroup)); 856} 857 858/* 859 * -nouser functions -- 860 * 861 * True if file belongs to a user ID for which the equivalent 862 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 863 */ 864int 865f_nouser(plan, entry) 866 PLAN *plan; 867 FTSENT *entry; 868{ 869 char *user_from_uid(); 870 871 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 872} 873 874PLAN * 875c_nouser() 876{ 877 ftsoptions &= ~FTS_NOSTAT; 878 879 return (palloc(N_NOUSER, f_nouser)); 880} 881 882/* 883 * -path functions -- 884 * 885 * True if the path of the filename being examined 886 * matches pattern using Pattern Matching Notation S3.14 887 */ 888int 889f_path(plan, entry) 890 PLAN *plan; 891 FTSENT *entry; 892{ 893 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 894} 895 896PLAN * 897c_path(pattern) 898 char *pattern; 899{ 900 PLAN *new; 901 902 new = palloc(N_NAME, f_path); 903 new->c_data = pattern; 904 return (new); 905} 906 907/* 908 * -perm functions -- 909 * 910 * The mode argument is used to represent file mode bits. If it starts 911 * with a leading digit, it's treated as an octal mode, otherwise as a 912 * symbolic mode. 913 */ 914int 915f_perm(plan, entry) 916 PLAN *plan; 917 FTSENT *entry; 918{ 919 mode_t mode; 920 921 mode = entry->fts_statp->st_mode & 922 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 923 if (plan->flags == F_ATLEAST) 924 return ((plan->m_data | mode) == mode); 925 else 926 return (mode == plan->m_data); 927 /* NOTREACHED */ 928} 929 930PLAN * 931c_perm(perm) 932 char *perm; 933{ 934 PLAN *new; 935 mode_t *set; 936 937 ftsoptions &= ~FTS_NOSTAT; 938 939 new = palloc(N_PERM, f_perm); 940 941 if (*perm == '-') { 942 new->flags = F_ATLEAST; 943 ++perm; 944 } 945 946 if ((set = setmode(perm)) == NULL) 947 err(1, "-perm: %s: illegal mode string", perm); 948 949 new->m_data = getmode(set, 0); 950 return (new); 951} 952 953/* 954 * -print functions -- 955 * 956 * Always true, causes the current pathame to be written to 957 * standard output. 958 */ 959int 960f_print(plan, entry) 961 PLAN *plan; 962 FTSENT *entry; 963{ 964 (void)puts(entry->fts_path); 965 return (1); 966} 967 968PLAN * 969c_print() 970{ 971 isoutput = 1; 972 973 return (palloc(N_PRINT, f_print)); 974} 975 976/* 977 * -print0 functions -- 978 * 979 * Always true, causes the current pathame to be written to 980 * standard output followed by a NUL character 981 */ 982int 983f_print0(plan, entry) 984 PLAN *plan; 985 FTSENT *entry; 986{ 987 fputs(entry->fts_path, stdout); 988 fputc('\0', stdout); 989 return (1); 990} 991 992PLAN * 993c_print0() 994{ 995 isoutput = 1; 996 997 return (palloc(N_PRINT0, f_print0)); 998} 999 1000/* 1001 * -prune functions -- 1002 * 1003 * Prune a portion of the hierarchy. 1004 */ 1005int 1006f_prune(plan, entry) 1007 PLAN *plan; 1008 FTSENT *entry; 1009{ 1010 extern FTS *tree; 1011 1012 if (fts_set(tree, entry, FTS_SKIP)) 1013 err(1, "%s", entry->fts_path); 1014 return (1); 1015} 1016 1017PLAN * 1018c_prune() 1019{ 1020 return (palloc(N_PRUNE, f_prune)); 1021} 1022 1023/* 1024 * -size n[c] functions -- 1025 * 1026 * True if the file size in bytes, divided by an implementation defined 1027 * value and rounded up to the next integer, is n. If n is followed by 1028 * a c, the size is in bytes. 1029 */ 1030#define FIND_SIZE 512 1031static int divsize = 1; 1032 1033int 1034f_size(plan, entry) 1035 PLAN *plan; 1036 FTSENT *entry; 1037{ 1038 off_t size; 1039 1040 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1041 FIND_SIZE : entry->fts_statp->st_size; 1042 COMPARE(size, plan->o_data); 1043} 1044 1045PLAN * 1046c_size(arg) 1047 char *arg; 1048{ 1049 PLAN *new; 1050 char endch; 1051 1052 ftsoptions &= ~FTS_NOSTAT; 1053 1054 new = palloc(N_SIZE, f_size); 1055 endch = 'c'; 1056 new->o_data = find_parsenum(new, "-size", arg, &endch); 1057 if (endch == 'c') 1058 divsize = 0; 1059 return (new); 1060} 1061 1062/* 1063 * -type c functions -- 1064 * 1065 * True if the type of the file is c, where c is b, c, d, p, f or w 1066 * for block special file, character special file, directory, FIFO, 1067 * regular file or whiteout respectively. 1068 */ 1069int 1070f_type(plan, entry) 1071 PLAN *plan; 1072 FTSENT *entry; 1073{ 1074 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1075} 1076 1077PLAN * 1078c_type(typestring) 1079 char *typestring; 1080{ 1081 PLAN *new; 1082 mode_t mask; 1083 1084 ftsoptions &= ~FTS_NOSTAT; 1085 1086 switch (typestring[0]) { 1087 case 'b': 1088 mask = S_IFBLK; 1089 break; 1090 case 'c': 1091 mask = S_IFCHR; 1092 break; 1093 case 'd': 1094 mask = S_IFDIR; 1095 break; 1096 case 'f': 1097 mask = S_IFREG; 1098 break; 1099 case 'l': 1100 mask = S_IFLNK; 1101 break; 1102 case 'p': 1103 mask = S_IFIFO; 1104 break; 1105 case 's': 1106 mask = S_IFSOCK; 1107 break; 1108#ifdef FTS_WHITEOUT 1109 case 'w': 1110 mask = S_IFWHT; 1111 ftsoptions |= FTS_WHITEOUT; 1112 break; 1113#endif /* FTS_WHITEOUT */ 1114 default: 1115 errx(1, "-type: %s: unknown type", typestring); 1116 } 1117 1118 new = palloc(N_TYPE, f_type); 1119 new->m_data = mask; 1120 return (new); 1121} 1122 1123/* 1124 * -delete functions -- 1125 * 1126 * True always. Makes it's best shot and continues on regardless. 1127 */ 1128int 1129f_delete(plan, entry) 1130 PLAN *plan; 1131 FTSENT *entry; 1132{ 1133 /* ignore these from fts */ 1134 if (strcmp(entry->fts_accpath, ".") == 0 || 1135 strcmp(entry->fts_accpath, "..") == 0) 1136 return (1); 1137 1138 /* sanity check */ 1139 if (isdepth == 0 || /* depth off */ 1140 (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ 1141 !(ftsoptions & FTS_PHYSICAL) || /* physical off */ 1142 (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 1143 errx(1, "-delete: insecure options got turned on"); 1144 1145 /* Potentially unsafe - do not accept relative paths whatsoever */ 1146 if (strchr(entry->fts_accpath, '/') != NULL) 1147 errx(1, "-delete: %s: relative path potentially not safe", 1148 entry->fts_accpath); 1149 1150 /* Turn off user immutable bits if running as root */ 1151 if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 1152 !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 1153 geteuid() == 0) 1154 chflags(entry->fts_accpath, 1155 entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 1156 1157 /* rmdir directories, unlink everything else */ 1158 if (S_ISDIR(entry->fts_statp->st_mode)) { 1159 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 1160 warn("-delete: rmdir(%s)", entry->fts_path); 1161 } else { 1162 if (unlink(entry->fts_accpath) < 0) 1163 warn("-delete: unlink(%s)", entry->fts_path); 1164 } 1165 1166 /* "succeed" */ 1167 return (1); 1168} 1169 1170PLAN * 1171c_delete() 1172{ 1173 1174 ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 1175 ftsoptions |= FTS_PHYSICAL; /* disable -follow */ 1176 ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ 1177 isoutput = 1; /* possible output */ 1178 isdepth = 1; /* -depth implied */ 1179 1180 return (palloc(N_DELETE, f_delete)); 1181} 1182 1183/* 1184 * -user uname functions -- 1185 * 1186 * True if the file belongs to the user uname. If uname is numeric and 1187 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1188 * return a valid user name, uname is taken as a user ID. 1189 */ 1190int 1191f_user(plan, entry) 1192 PLAN *plan; 1193 FTSENT *entry; 1194{ 1195 return (entry->fts_statp->st_uid == plan->u_data); 1196} 1197 1198PLAN * 1199c_user(username) 1200 char *username; 1201{ 1202 PLAN *new; 1203 struct passwd *p; 1204 uid_t uid; 1205 1206 ftsoptions &= ~FTS_NOSTAT; 1207 1208 p = getpwnam(username); 1209 if (p == NULL) { 1210 uid = atoi(username); 1211 if (uid == 0 && username[0] != '0') 1212 errx(1, "-user: %s: no such user", username); 1213 } else 1214 uid = p->pw_uid; 1215 1216 new = palloc(N_USER, f_user); 1217 new->u_data = uid; 1218 return (new); 1219} 1220 1221/* 1222 * -xdev functions -- 1223 * 1224 * Always true, causes find not to decend past directories that have a 1225 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1226 */ 1227PLAN * 1228c_xdev() 1229{ 1230 ftsoptions |= FTS_XDEV; 1231 1232 return (palloc(N_XDEV, f_always_true)); 1233} 1234 1235/* 1236 * ( expression ) functions -- 1237 * 1238 * True if expression is true. 1239 */ 1240int 1241f_expr(plan, entry) 1242 PLAN *plan; 1243 FTSENT *entry; 1244{ 1245 register PLAN *p; 1246 register int state; 1247 1248 for (p = plan->p_data[0]; 1249 p && (state = (p->eval)(p, entry)); p = p->next); 1250 return (state); 1251} 1252 1253/* 1254 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1255 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1256 * to a N_EXPR node containing the expression and the ')' node is discarded. 1257 */ 1258PLAN * 1259c_openparen() 1260{ 1261 return (palloc(N_OPENPAREN, (int (*)())-1)); 1262} 1263 1264PLAN * 1265c_closeparen() 1266{ 1267 return (palloc(N_CLOSEPAREN, (int (*)())-1)); 1268} 1269 1270/* 1271 * ! expression functions -- 1272 * 1273 * Negation of a primary; the unary NOT operator. 1274 */ 1275int 1276f_not(plan, entry) 1277 PLAN *plan; 1278 FTSENT *entry; 1279{ 1280 register PLAN *p; 1281 register int state; 1282 1283 for (p = plan->p_data[0]; 1284 p && (state = (p->eval)(p, entry)); p = p->next); 1285 return (!state); 1286} 1287 1288PLAN * 1289c_not() 1290{ 1291 return (palloc(N_NOT, f_not)); 1292} 1293 1294/* 1295 * expression -o expression functions -- 1296 * 1297 * Alternation of primaries; the OR operator. The second expression is 1298 * not evaluated if the first expression is true. 1299 */ 1300int 1301f_or(plan, entry) 1302 PLAN *plan; 1303 FTSENT *entry; 1304{ 1305 register PLAN *p; 1306 register int state; 1307 1308 for (p = plan->p_data[0]; 1309 p && (state = (p->eval)(p, entry)); p = p->next); 1310 1311 if (state) 1312 return (1); 1313 1314 for (p = plan->p_data[1]; 1315 p && (state = (p->eval)(p, entry)); p = p->next); 1316 return (state); 1317} 1318 1319PLAN * 1320c_or() 1321{ 1322 return (palloc(N_OR, f_or)); 1323} 1324 1325static PLAN * 1326palloc(t, f) 1327 enum ntype t; 1328 int (*f) __P((PLAN *, FTSENT *)); 1329{ 1330 PLAN *new; 1331 1332 if ((new = malloc(sizeof(PLAN))) == NULL) 1333 err(1, NULL); 1334 new->type = t; 1335 new->eval = f; 1336 new->flags = 0; 1337 new->next = NULL; 1338 return (new); 1339} 1340