1 /* $NetBSD: function.c,v 1.72 2013/05/04 06:29:32 uebayasi 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. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "from: @(#)function.c 8.10 (Berkeley) 5/4/95"; 39 #else 40 __RCSID("$NetBSD: function.c,v 1.72 2013/05/04 06:29:32 uebayasi Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <sys/param.h> 45 #include <sys/stat.h> 46 #include <sys/wait.h> 47 #include <sys/mount.h> 48 49 #include <dirent.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <fnmatch.h> 53 #include <fts.h> 54 #include <grp.h> 55 #include <inttypes.h> 56 #include <limits.h> 57 #include <pwd.h> 58 #include <stdbool.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <tzfile.h> 63 #include <unistd.h> 64 #include <util.h> 65 66 #include "find.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 int64_t find_parsenum(PLAN *, const char *, const char *, char *); 82 static void run_f_exec(PLAN *); 83 int f_always_true(PLAN *, FTSENT *); 84 int f_amin(PLAN *, FTSENT *); 85 int f_anewer(PLAN *, FTSENT *); 86 int f_atime(PLAN *, FTSENT *); 87 int f_cmin(PLAN *, FTSENT *); 88 int f_cnewer(PLAN *, FTSENT *); 89 int f_ctime(PLAN *, FTSENT *); 90 int f_delete(PLAN *, FTSENT *); 91 int f_empty(PLAN *, FTSENT *); 92 int f_exec(PLAN *, FTSENT *); 93 int f_execdir(PLAN *, FTSENT *); 94 int f_false(PLAN *, FTSENT *); 95 int f_flags(PLAN *, FTSENT *); 96 int f_fprint(PLAN *, FTSENT *); 97 int f_fstype(PLAN *, FTSENT *); 98 int f_group(PLAN *, FTSENT *); 99 int f_iname(PLAN *, FTSENT *); 100 int f_inum(PLAN *, FTSENT *); 101 int f_links(PLAN *, FTSENT *); 102 int f_ls(PLAN *, FTSENT *); 103 int f_mindepth(PLAN *, FTSENT *); 104 int f_maxdepth(PLAN *, FTSENT *); 105 int f_mmin(PLAN *, FTSENT *); 106 int f_mtime(PLAN *, FTSENT *); 107 int f_name(PLAN *, FTSENT *); 108 int f_newer(PLAN *, FTSENT *); 109 int f_nogroup(PLAN *, FTSENT *); 110 int f_nouser(PLAN *, FTSENT *); 111 int f_path(PLAN *, FTSENT *); 112 int f_perm(PLAN *, FTSENT *); 113 int f_print(PLAN *, FTSENT *); 114 int f_print0(PLAN *, FTSENT *); 115 int f_printx(PLAN *, FTSENT *); 116 int f_prune(PLAN *, FTSENT *); 117 int f_regex(PLAN *, FTSENT *); 118 int f_size(PLAN *, FTSENT *); 119 int f_type(PLAN *, FTSENT *); 120 int f_user(PLAN *, FTSENT *); 121 int f_not(PLAN *, FTSENT *); 122 int f_or(PLAN *, FTSENT *); 123 static PLAN *c_regex_common(char ***, int, enum ntype, bool); 124 static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *)); 125 126 extern int dotfd; 127 extern FTS *tree; 128 extern time_t now; 129 130 /* 131 * find_parsenum -- 132 * Parse a string of the form [+-]# and return the value. 133 */ 134 static int64_t 135 find_parsenum(PLAN *plan, const char *option, const char *vp, char *endch) 136 { 137 int64_t value; 138 const char *str; 139 char *endchar; /* Pointer to character ending conversion. */ 140 141 /* Determine comparison from leading + or -. */ 142 str = vp; 143 switch (*str) { 144 case '+': 145 ++str; 146 plan->flags = F_GREATER; 147 break; 148 case '-': 149 ++str; 150 plan->flags = F_LESSTHAN; 151 break; 152 default: 153 plan->flags = F_EQUAL; 154 break; 155 } 156 157 /* 158 * Convert the string with strtol(). Note, if strtol() returns zero 159 * and endchar points to the beginning of the string we know we have 160 * a syntax error. 161 */ 162 value = strtoq(str, &endchar, 10); 163 if (value == 0 && endchar == str) 164 errx(1, "%s: %s: illegal numeric value", option, vp); 165 if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 166 errx(1, "%s: %s: illegal trailing character", option, vp); 167 if (endch) 168 *endch = endchar[0]; 169 return (value); 170 } 171 172 /* 173 * The value of n for the inode times (atime, ctime, and mtime) is a range, 174 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 175 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 176 * user wanted. Correct so that -1 is "less than 1". 177 */ 178 #define TIME_CORRECT(p, ttype) \ 179 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 180 ++((p)->t_data); 181 182 /* 183 * -amin n functions -- 184 * 185 * True if the difference between the file access time and the 186 * current time is n 1 minute periods. 187 */ 188 int 189 f_amin(PLAN *plan, FTSENT *entry) 190 { 191 COMPARE((now - entry->fts_statp->st_atime + 192 SECSPERMIN - 1) / SECSPERMIN, plan->t_data); 193 } 194 195 PLAN * 196 c_amin(char ***argvp, int isok) 197 { 198 char *arg = **argvp; 199 PLAN *new; 200 201 (*argvp)++; 202 ftsoptions &= ~FTS_NOSTAT; 203 204 new = palloc(N_AMIN, f_amin); 205 new->t_data = find_parsenum(new, "-amin", arg, NULL); 206 TIME_CORRECT(new, N_AMIN); 207 return (new); 208 } 209 210 /* 211 * -anewer file functions -- 212 * 213 * True if the current file has been accessed more recently 214 * than the access time of the file named by the pathname 215 * file. 216 */ 217 int 218 f_anewer(PLAN *plan, FTSENT *entry) 219 { 220 221 return timespeccmp(&entry->fts_statp->st_atim, &plan->ts_data, >); 222 } 223 224 PLAN * 225 c_anewer(char ***argvp, int isok) 226 { 227 char *filename = **argvp; 228 PLAN *new; 229 struct stat sb; 230 231 (*argvp)++; 232 ftsoptions &= ~FTS_NOSTAT; 233 234 if (stat(filename, &sb)) 235 err(1, "%s", filename); 236 new = palloc(N_ANEWER, f_anewer); 237 new->ts_data = sb.st_atim; 238 return (new); 239 } 240 241 /* 242 * -atime n functions -- 243 * 244 * True if the difference between the file access time and the 245 * current time is n 24 hour periods. 246 */ 247 int 248 f_atime(PLAN *plan, FTSENT *entry) 249 { 250 COMPARE((now - entry->fts_statp->st_atime + 251 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 252 } 253 254 PLAN * 255 c_atime(char ***argvp, int isok) 256 { 257 char *arg = **argvp; 258 PLAN *new; 259 260 (*argvp)++; 261 ftsoptions &= ~FTS_NOSTAT; 262 263 new = palloc(N_ATIME, f_atime); 264 new->t_data = find_parsenum(new, "-atime", arg, NULL); 265 TIME_CORRECT(new, N_ATIME); 266 return (new); 267 } 268 269 /* 270 * -cmin n functions -- 271 * 272 * True if the difference between the last change of file 273 * status information and the current time is n 24 hour periods. 274 */ 275 int 276 f_cmin(PLAN *plan, FTSENT *entry) 277 { 278 COMPARE((now - entry->fts_statp->st_ctime + 279 SECSPERMIN - 1) / SECSPERMIN, plan->t_data); 280 } 281 282 PLAN * 283 c_cmin(char ***argvp, int isok) 284 { 285 char *arg = **argvp; 286 PLAN *new; 287 288 (*argvp)++; 289 ftsoptions &= ~FTS_NOSTAT; 290 291 new = palloc(N_CMIN, f_cmin); 292 new->t_data = find_parsenum(new, "-cmin", arg, NULL); 293 TIME_CORRECT(new, N_CMIN); 294 return (new); 295 } 296 297 /* 298 * -cnewer file functions -- 299 * 300 * True if the current file has been changed more recently 301 * than the changed time of the file named by the pathname 302 * file. 303 */ 304 int 305 f_cnewer(PLAN *plan, FTSENT *entry) 306 { 307 308 return timespeccmp(&entry->fts_statp->st_ctim, &plan->ts_data, >); 309 } 310 311 PLAN * 312 c_cnewer(char ***argvp, int isok) 313 { 314 char *filename = **argvp; 315 PLAN *new; 316 struct stat sb; 317 318 (*argvp)++; 319 ftsoptions &= ~FTS_NOSTAT; 320 321 if (stat(filename, &sb)) 322 err(1, "%s", filename); 323 new = palloc(N_CNEWER, f_cnewer); 324 new->ts_data = sb.st_ctim; 325 return (new); 326 } 327 328 /* 329 * -ctime n functions -- 330 * 331 * True if the difference between the last change of file 332 * status information and the current time is n 24 hour periods. 333 */ 334 int 335 f_ctime(PLAN *plan, FTSENT *entry) 336 { 337 COMPARE((now - entry->fts_statp->st_ctime + 338 SECSPERDAY - 1) / SECSPERDAY, plan->t_data); 339 } 340 341 PLAN * 342 c_ctime(char ***argvp, int isok) 343 { 344 char *arg = **argvp; 345 PLAN *new; 346 347 (*argvp)++; 348 ftsoptions &= ~FTS_NOSTAT; 349 350 new = palloc(N_CTIME, f_ctime); 351 new->t_data = find_parsenum(new, "-ctime", arg, NULL); 352 TIME_CORRECT(new, N_CTIME); 353 return (new); 354 } 355 356 /* 357 * -delete functions -- 358 * 359 * Always true. Makes its best shot and continues on regardless. 360 */ 361 int 362 f_delete(PLAN *plan __unused, FTSENT *entry) 363 { 364 /* ignore these from fts */ 365 if (strcmp(entry->fts_accpath, ".") == 0 || 366 strcmp(entry->fts_accpath, "..") == 0) 367 return 1; 368 369 /* sanity check */ 370 if (isdepth == 0 || /* depth off */ 371 (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ 372 !(ftsoptions & FTS_PHYSICAL) || /* physical off */ 373 (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 374 errx(1, "-delete: insecure options got turned on"); 375 376 /* Potentially unsafe - do not accept relative paths whatsoever */ 377 if (strchr(entry->fts_accpath, '/') != NULL) 378 errx(1, "-delete: %s: relative path potentially not safe", 379 entry->fts_accpath); 380 381 #ifndef __minix 382 /* Turn off user immutable bits if running as root */ 383 if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 384 !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 385 geteuid() == 0) 386 chflags(entry->fts_accpath, 387 entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 388 #endif /* !__minix */ 389 390 /* rmdir directories, unlink everything else */ 391 if (S_ISDIR(entry->fts_statp->st_mode)) { 392 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 393 warn("-delete: rmdir(%s)", entry->fts_path); 394 } else { 395 if (unlink(entry->fts_accpath) < 0) 396 warn("-delete: unlink(%s)", entry->fts_path); 397 } 398 399 /* "succeed" */ 400 return 1; 401 } 402 403 PLAN * 404 c_delete(char ***argvp __unused, int isok) 405 { 406 407 ftsoptions &= ~FTS_NOSTAT; /* no optimize */ 408 ftsoptions |= FTS_PHYSICAL; /* disable -follow */ 409 ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ 410 isoutput = 1; /* possible output */ 411 isdepth = 1; /* -depth implied */ 412 413 return palloc(N_DELETE, f_delete); 414 } 415 416 /* 417 * -depth functions -- 418 * 419 * Always true, causes descent of the directory hierarchy to be done 420 * so that all entries in a directory are acted on before the directory 421 * itself. 422 */ 423 int 424 f_always_true(PLAN *plan, FTSENT *entry) 425 { 426 427 return (1); 428 } 429 430 PLAN * 431 c_depth(char ***argvp, int isok) 432 { 433 isdepth = 1; 434 435 return (palloc(N_DEPTH, f_always_true)); 436 } 437 438 /* 439 * -empty functions -- 440 * 441 * True if the file or directory is empty 442 */ 443 int 444 f_empty(PLAN *plan, FTSENT *entry) 445 { 446 if (S_ISREG(entry->fts_statp->st_mode) && 447 entry->fts_statp->st_size == 0) 448 return (1); 449 if (S_ISDIR(entry->fts_statp->st_mode)) { 450 struct dirent *dp; 451 int empty; 452 DIR *dir; 453 454 empty = 1; 455 dir = opendir(entry->fts_accpath); 456 if (dir == NULL) 457 return (0); 458 for (dp = readdir(dir); dp; dp = readdir(dir)) 459 if (dp->d_name[0] != '.' || 460 (dp->d_name[1] != '\0' && 461 (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { 462 empty = 0; 463 break; 464 } 465 closedir(dir); 466 return (empty); 467 } 468 return (0); 469 } 470 471 PLAN * 472 c_empty(char ***argvp, int isok) 473 { 474 ftsoptions &= ~FTS_NOSTAT; 475 476 return (palloc(N_EMPTY, f_empty)); 477 } 478 479 /* 480 * [-exec | -ok] utility [arg ... ] ; functions -- 481 * [-exec | -ok] utility [arg ... ] {} + functions -- 482 * 483 * If the end of the primary expression is delimited by a 484 * semicolon: true if the executed utility returns a zero value 485 * as exit status. If "{}" occurs anywhere, it gets replaced by 486 * the current pathname. 487 * 488 * If the end of the primary expression is delimited by a plus 489 * sign: always true. Pathnames for which the primary is 490 * evaluated shall be aggregated into sets. The utility will be 491 * executed once per set, with "{}" replaced by the entire set of 492 * pathnames (as if xargs). "{}" must appear last. 493 * 494 * The current directory for the execution of utility is the same 495 * as the current directory when the find utility was started. 496 * 497 * The primary -ok is different in that it requests affirmation 498 * of the user before executing the utility. 499 */ 500 int 501 f_exec(PLAN *plan, FTSENT *entry) 502 { 503 size_t cnt; 504 int l; 505 pid_t pid; 506 int status; 507 508 if (plan->flags & F_PLUSSET) { 509 /* 510 * Confirm sufficient buffer space, then copy the path 511 * to the buffer. 512 */ 513 l = strlen(entry->fts_path); 514 if (plan->ep_p + l < plan->ep_ebp) { 515 plan->ep_bxp[plan->ep_narg++] = 516 strcpy(plan->ep_p, entry->fts_path); 517 plan->ep_p += l + 1; 518 519 if (plan->ep_narg == plan->ep_maxargs) 520 run_f_exec(plan); 521 } else { 522 /* 523 * Without sufficient space to copy in the next 524 * argument, run the command to empty out the 525 * buffer before re-attepting the copy. 526 */ 527 run_f_exec(plan); 528 if ((plan->ep_p + l < plan->ep_ebp)) { 529 plan->ep_bxp[plan->ep_narg++] 530 = strcpy(plan->ep_p, entry->fts_path); 531 plan->ep_p += l + 1; 532 } else 533 errx(1, "insufficient space for argument"); 534 } 535 return (1); 536 } else { 537 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 538 if (plan->e_len[cnt]) 539 brace_subst(plan->e_orig[cnt], 540 &plan->e_argv[cnt], 541 entry->fts_path, 542 &plan->e_len[cnt]); 543 if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv)) 544 return (0); 545 546 /* Don't mix output of command with find output. */ 547 fflush(stdout); 548 fflush(stderr); 549 550 switch (pid = vfork()) { 551 case -1: 552 err(1, "vfork"); 553 /* NOTREACHED */ 554 case 0: 555 if (fchdir(dotfd)) { 556 warn("chdir"); 557 _exit(1); 558 } 559 execvp(plan->e_argv[0], plan->e_argv); 560 warn("%s", plan->e_argv[0]); 561 _exit(1); 562 } 563 pid = waitpid(pid, &status, 0); 564 return (pid != -1 && WIFEXITED(status) 565 && !WEXITSTATUS(status)); 566 } 567 } 568 569 static void 570 run_f_exec(PLAN *plan) 571 { 572 pid_t pid; 573 int rval, status; 574 575 /* Ensure arg list is null terminated. */ 576 plan->ep_bxp[plan->ep_narg] = NULL; 577 578 /* Don't mix output of command with find output. */ 579 fflush(stdout); 580 fflush(stderr); 581 582 switch (pid = vfork()) { 583 case -1: 584 err(1, "vfork"); 585 /* NOTREACHED */ 586 case 0: 587 if (fchdir(dotfd)) { 588 warn("chdir"); 589 _exit(1); 590 } 591 execvp(plan->e_argv[0], plan->e_argv); 592 warn("%s", plan->e_argv[0]); 593 _exit(1); 594 } 595 596 /* Clear out the argument list. */ 597 plan->ep_narg = 0; 598 plan->ep_bxp[plan->ep_narg] = NULL; 599 /* As well as the argument buffer. */ 600 plan->ep_p = plan->ep_bbp; 601 *plan->ep_p = '\0'; 602 603 pid = waitpid(pid, &status, 0); 604 if (WIFEXITED(status)) 605 rval = WEXITSTATUS(status); 606 else 607 rval = -1; 608 609 /* 610 * If we have a non-zero exit status, preserve it so find(1) can 611 * later exit with it. 612 */ 613 if (rval) 614 plan->ep_rval = rval; 615 } 616 617 /* 618 * c_exec -- 619 * build three parallel arrays, one with pointers to the strings passed 620 * on the command line, one with (possibly duplicated) pointers to the 621 * argv array, and one with integer values that are lengths of the 622 * strings, but also flags meaning that the string has to be massaged. 623 * 624 * If -exec ... {} +, use only the first array, but make it large 625 * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a 626 * discussion), and then allocate ARG_MAX - 4K of space for args. 627 */ 628 PLAN * 629 c_exec(char ***argvp, int isok) 630 { 631 PLAN *new; /* node returned */ 632 size_t cnt; 633 int brace, lastbrace; 634 char **argv, **ap, *p; 635 636 isoutput = 1; 637 638 new = palloc(N_EXEC, f_exec); 639 if (isok) 640 new->flags |= F_NEEDOK; 641 642 /* 643 * Terminate if we encounter an arg exactly equal to ";", or an 644 * arg exactly equal to "+" following an arg exactly equal to 645 * "{}". 646 */ 647 for (ap = argv = *argvp, brace = 0;; ++ap) { 648 if (!*ap) 649 errx(1, "%s: no terminating \";\" or \"+\"", 650 isok ? "-ok" : "-exec"); 651 lastbrace = brace; 652 brace = 0; 653 if (strcmp(*ap, "{}") == 0) 654 brace = 1; 655 if (strcmp(*ap, ";") == 0) 656 break; 657 if (strcmp(*ap, "+") == 0 && lastbrace) { 658 new->flags |= F_PLUSSET; 659 break; 660 } 661 } 662 663 /* 664 * POSIX says -ok ... {} + "need not be supported," and it does 665 * not make much sense anyway. 666 */ 667 if (new->flags & F_NEEDOK && new->flags & F_PLUSSET) 668 errx(1, "-ok: terminating \"+\" not permitted."); 669 670 if (new->flags & F_PLUSSET) { 671 size_t c, bufsize; 672 673 cnt = ap - *argvp - 1; /* units are words */ 674 new->ep_maxargs = 5000; 675 new->e_argv = emalloc((cnt + new->ep_maxargs) 676 * sizeof(*new->e_argv)); 677 678 /* We start stuffing arguments after the user's last one. */ 679 new->ep_bxp = &new->e_argv[cnt]; 680 new->ep_narg = 0; 681 682 /* 683 * Count up the space of the user's arguments, and 684 * subtract that from what we allocate. 685 */ 686 #define MAXARG (ARG_MAX - 4 * 1024) 687 for (argv = *argvp, c = 0, cnt = 0; 688 argv < ap; 689 ++argv, ++cnt) { 690 c += strlen(*argv) + 1; 691 if (c >= MAXARG) 692 errx(1, "Arguments too long"); 693 new->e_argv[cnt] = *argv; 694 } 695 bufsize = MAXARG - c; 696 697 /* 698 * Allocate, and then initialize current, base, and 699 * end pointers. 700 */ 701 new->ep_p = new->ep_bbp = emalloc(bufsize + 1); 702 new->ep_ebp = new->ep_bbp + bufsize - 1; 703 new->ep_rval = 0; 704 } else { /* !F_PLUSSET */ 705 cnt = ap - *argvp + 1; 706 new->e_argv = emalloc(cnt * sizeof(*new->e_argv)); 707 new->e_orig = emalloc(cnt * sizeof(*new->e_orig)); 708 new->e_len = emalloc(cnt * sizeof(*new->e_len)); 709 710 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 711 new->e_orig[cnt] = *argv; 712 for (p = *argv; *p; ++p) 713 if (p[0] == '{' && p[1] == '}') { 714 new->e_argv[cnt] = 715 emalloc(MAXPATHLEN); 716 new->e_len[cnt] = MAXPATHLEN; 717 break; 718 } 719 if (!*p) { 720 new->e_argv[cnt] = *argv; 721 new->e_len[cnt] = 0; 722 } 723 } 724 new->e_orig[cnt] = NULL; 725 } 726 727 new->e_argv[cnt] = NULL; 728 *argvp = argv + 1; 729 return (new); 730 } 731 732 /* 733 * -execdir utility [arg ... ] ; functions -- 734 * 735 * True if the executed utility returns a zero value as exit status. 736 * The end of the primary expression is delimited by a semicolon. If 737 * "{}" occurs anywhere, it gets replaced by the unqualified pathname. 738 * The current directory for the execution of utility is the same as 739 * the directory where the file lives. 740 */ 741 int 742 f_execdir(PLAN *plan, FTSENT *entry) 743 { 744 size_t cnt; 745 pid_t pid; 746 int status; 747 char *file; 748 749 /* XXX - if file/dir ends in '/' this will not work -- can it? */ 750 if ((file = strrchr(entry->fts_path, '/'))) 751 file++; 752 else 753 file = entry->fts_path; 754 755 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 756 if (plan->e_len[cnt]) 757 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 758 file, &plan->e_len[cnt]); 759 760 /* don't mix output of command with find output */ 761 fflush(stdout); 762 fflush(stderr); 763 764 switch (pid = vfork()) { 765 case -1: 766 err(1, "fork"); 767 /* NOTREACHED */ 768 case 0: 769 execvp(plan->e_argv[0], plan->e_argv); 770 warn("%s", plan->e_argv[0]); 771 _exit(1); 772 } 773 pid = waitpid(pid, &status, 0); 774 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 775 } 776 777 /* 778 * c_execdir -- 779 * build three parallel arrays, one with pointers to the strings passed 780 * on the command line, one with (possibly duplicated) pointers to the 781 * argv array, and one with integer values that are lengths of the 782 * strings, but also flags meaning that the string has to be massaged. 783 */ 784 PLAN * 785 c_execdir(char ***argvp, int isok) 786 { 787 PLAN *new; /* node returned */ 788 size_t cnt; 789 char **argv, **ap, *p; 790 791 ftsoptions &= ~FTS_NOSTAT; 792 isoutput = 1; 793 794 new = palloc(N_EXECDIR, f_execdir); 795 796 for (ap = argv = *argvp;; ++ap) { 797 if (!*ap) 798 errx(1, 799 "-execdir: no terminating \";\""); 800 if (**ap == ';') 801 break; 802 } 803 804 cnt = ap - *argvp + 1; 805 new->e_argv = emalloc(cnt * sizeof(*new->e_argv)); 806 new->e_orig = emalloc(cnt * sizeof(*new->e_orig)); 807 new->e_len = emalloc(cnt * sizeof(*new->e_len)); 808 809 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 810 new->e_orig[cnt] = *argv; 811 for (p = *argv; *p; ++p) 812 if (p[0] == '{' && p[1] == '}') { 813 new->e_argv[cnt] = emalloc(MAXPATHLEN); 814 new->e_len[cnt] = MAXPATHLEN; 815 break; 816 } 817 if (!*p) { 818 new->e_argv[cnt] = *argv; 819 new->e_len[cnt] = 0; 820 } 821 } 822 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 823 824 *argvp = argv + 1; 825 return (new); 826 } 827 828 PLAN * 829 c_exit(char ***argvp, int isok) 830 { 831 char *arg = **argvp; 832 PLAN *new; 833 834 /* not technically true, but otherwise '-print' is implied */ 835 isoutput = 1; 836 837 new = palloc(N_EXIT, f_always_true); 838 839 if (arg) { 840 (*argvp)++; 841 new->exit_val = find_parsenum(new, "-exit", arg, NULL); 842 } else 843 new->exit_val = 0; 844 845 return (new); 846 } 847 848 849 /* 850 * -false function 851 */ 852 int 853 f_false(PLAN *plan, FTSENT *entry) 854 { 855 856 return (0); 857 } 858 859 PLAN * 860 c_false(char ***argvp, int isok) 861 { 862 return (palloc(N_FALSE, f_false)); 863 } 864 865 866 /* 867 * -flags [-]flags functions -- 868 */ 869 int 870 f_flags(PLAN *plan, FTSENT *entry) 871 { 872 u_int32_t flags; 873 874 flags = entry->fts_statp->st_flags; 875 if (plan->flags == F_ATLEAST) 876 return ((plan->f_data | flags) == flags); 877 else 878 return (flags == plan->f_data); 879 /* NOTREACHED */ 880 } 881 882 PLAN * 883 c_flags(char ***argvp, int isok) 884 { 885 char *flags = **argvp; 886 PLAN *new; 887 u_long flagset; 888 889 (*argvp)++; 890 ftsoptions &= ~FTS_NOSTAT; 891 892 new = palloc(N_FLAGS, f_flags); 893 894 if (*flags == '-') { 895 new->flags = F_ATLEAST; 896 ++flags; 897 } 898 899 flagset = 0; 900 if ((strcmp(flags, "none") != 0) && 901 (string_to_flags(&flags, &flagset, NULL) != 0)) 902 errx(1, "-flags: %s: illegal flags string", flags); 903 new->f_data = flagset; 904 return (new); 905 } 906 907 /* 908 * -follow functions -- 909 * 910 * Always true, causes symbolic links to be followed on a global 911 * basis. 912 */ 913 PLAN * 914 c_follow(char ***argvp, int isok) 915 { 916 ftsoptions &= ~FTS_PHYSICAL; 917 ftsoptions |= FTS_LOGICAL; 918 919 return (palloc(N_FOLLOW, f_always_true)); 920 } 921 922 /* -fprint functions -- 923 * 924 * Causes the current pathame to be written to the defined output file. 925 */ 926 int 927 f_fprint(PLAN *plan, FTSENT *entry) 928 { 929 930 if (-1 == fprintf(plan->fprint_file, "%s\n", entry->fts_path)) 931 warn("fprintf"); 932 933 return(1); 934 935 /* no descriptors are closed; they will be closed by 936 operating system when this find command exits. */ 937 } 938 939 PLAN * 940 c_fprint(char ***argvp, int isok) 941 { 942 PLAN *new; 943 944 isoutput = 1; /* do not assume -print */ 945 946 new = palloc(N_FPRINT, f_fprint); 947 948 if (NULL == (new->fprint_file = fopen(**argvp, "w"))) 949 err(1, "-fprint: %s: cannot create file", **argvp); 950 951 (*argvp)++; 952 return (new); 953 } 954 955 /* 956 * -fstype functions -- 957 * 958 * True if the file is of a certain type. 959 */ 960 int 961 f_fstype(PLAN *plan, FTSENT *entry) 962 { 963 static dev_t curdev; /* need a guaranteed illegal dev value */ 964 static int first = 1; 965 struct statvfs sb; 966 static short val; 967 static char fstype[sizeof(sb.f_fstypename)]; 968 char *p, save[2]; 969 970 memset(&save, 0, sizeof save); /* XXX gcc */ 971 972 /* Only check when we cross mount point. */ 973 if (first || curdev != entry->fts_statp->st_dev) { 974 curdev = entry->fts_statp->st_dev; 975 976 /* 977 * Statfs follows symlinks; find wants the link's file system, 978 * not where it points. 979 */ 980 if (entry->fts_info == FTS_SL || 981 entry->fts_info == FTS_SLNONE) { 982 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 983 ++p; 984 else 985 p = entry->fts_accpath; 986 save[0] = p[0]; 987 p[0] = '.'; 988 save[1] = p[1]; 989 p[1] = '\0'; 990 991 } else 992 p = NULL; 993 994 if (statvfs(entry->fts_accpath, &sb)) 995 err(1, "%s", entry->fts_accpath); 996 997 if (p) { 998 p[0] = save[0]; 999 p[1] = save[1]; 1000 } 1001 1002 first = 0; 1003 1004 /* 1005 * Further tests may need both of these values, so 1006 * always copy both of them. 1007 */ 1008 val = sb.f_flag; 1009 strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); 1010 } 1011 switch (plan->flags) { 1012 case F_MTFLAG: 1013 return (val & plan->mt_data); 1014 case F_MTTYPE: 1015 return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); 1016 default: 1017 abort(); 1018 } 1019 } 1020 1021 PLAN * 1022 c_fstype(char ***argvp, int isok) 1023 { 1024 char *arg = **argvp; 1025 PLAN *new; 1026 1027 (*argvp)++; 1028 ftsoptions &= ~FTS_NOSTAT; 1029 1030 new = palloc(N_FSTYPE, f_fstype); 1031 1032 switch (*arg) { 1033 case 'l': 1034 if (!strcmp(arg, "local")) { 1035 new->flags = F_MTFLAG; 1036 new->mt_data = MNT_LOCAL; 1037 return (new); 1038 } 1039 break; 1040 case 'r': 1041 if (!strcmp(arg, "rdonly")) { 1042 new->flags = F_MTFLAG; 1043 new->mt_data = MNT_RDONLY; 1044 return (new); 1045 } 1046 break; 1047 } 1048 1049 new->flags = F_MTTYPE; 1050 new->c_data = arg; 1051 return (new); 1052 } 1053 1054 /* 1055 * -group gname functions -- 1056 * 1057 * True if the file belongs to the group gname. If gname is numeric and 1058 * an equivalent of the getgrnam() function does not return a valid group 1059 * name, gname is taken as a group ID. 1060 */ 1061 int 1062 f_group(PLAN *plan, FTSENT *entry) 1063 { 1064 1065 return (entry->fts_statp->st_gid == plan->g_data); 1066 } 1067 1068 PLAN * 1069 c_group(char ***argvp, int isok) 1070 { 1071 char *gname = **argvp; 1072 PLAN *new; 1073 struct group *g; 1074 gid_t gid; 1075 1076 (*argvp)++; 1077 ftsoptions &= ~FTS_NOSTAT; 1078 1079 g = getgrnam(gname); 1080 if (g == NULL) { 1081 gid = atoi(gname); 1082 if (gid == 0 && gname[0] != '0') 1083 errx(1, "-group: %s: no such group", gname); 1084 } else 1085 gid = g->gr_gid; 1086 1087 new = palloc(N_GROUP, f_group); 1088 new->g_data = gid; 1089 return (new); 1090 } 1091 1092 /* 1093 * -inum n functions -- 1094 * 1095 * True if the file has inode # n. 1096 */ 1097 int 1098 f_inum(PLAN *plan, FTSENT *entry) 1099 { 1100 1101 COMPARE(entry->fts_statp->st_ino, plan->i_data); 1102 } 1103 1104 PLAN * 1105 c_inum(char ***argvp, int isok) 1106 { 1107 char *arg = **argvp; 1108 PLAN *new; 1109 1110 (*argvp)++; 1111 ftsoptions &= ~FTS_NOSTAT; 1112 1113 new = palloc(N_INUM, f_inum); 1114 new->i_data = find_parsenum(new, "-inum", arg, NULL); 1115 return (new); 1116 } 1117 1118 /* 1119 * -links n functions -- 1120 * 1121 * True if the file has n links. 1122 */ 1123 int 1124 f_links(PLAN *plan, FTSENT *entry) 1125 { 1126 1127 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 1128 } 1129 1130 PLAN * 1131 c_links(char ***argvp, int isok) 1132 { 1133 char *arg = **argvp; 1134 PLAN *new; 1135 1136 (*argvp)++; 1137 ftsoptions &= ~FTS_NOSTAT; 1138 1139 new = palloc(N_LINKS, f_links); 1140 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 1141 return (new); 1142 } 1143 1144 /* 1145 * -ls functions -- 1146 * 1147 * Always true - prints the current entry to stdout in "ls" format. 1148 */ 1149 int 1150 f_ls(PLAN *plan, FTSENT *entry) 1151 { 1152 1153 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 1154 return (1); 1155 } 1156 1157 PLAN * 1158 c_ls(char ***argvp, int isok) 1159 { 1160 1161 ftsoptions &= ~FTS_NOSTAT; 1162 isoutput = 1; 1163 1164 return (palloc(N_LS, f_ls)); 1165 } 1166 1167 /* 1168 * - maxdepth n functions -- 1169 * 1170 * True if the current search depth is less than or equal to the 1171 * maximum depth specified 1172 */ 1173 int 1174 f_maxdepth(PLAN *plan, FTSENT *entry) 1175 { 1176 extern FTS *tree; 1177 1178 if (entry->fts_level >= plan->max_data) 1179 fts_set(tree, entry, FTS_SKIP); 1180 return (entry->fts_level <= plan->max_data); 1181 } 1182 1183 PLAN * 1184 c_maxdepth(char ***argvp, int isok) 1185 { 1186 char *arg = **argvp; 1187 PLAN *new; 1188 1189 (*argvp)++; 1190 new = palloc(N_MAXDEPTH, f_maxdepth); 1191 new->max_data = atoi(arg); 1192 return (new); 1193 } 1194 1195 /* 1196 * - mindepth n functions -- 1197 * 1198 * True if the current search depth is greater than or equal to the 1199 * minimum depth specified 1200 */ 1201 int 1202 f_mindepth(PLAN *plan, FTSENT *entry) 1203 { 1204 return (entry->fts_level >= plan->min_data); 1205 } 1206 1207 PLAN * 1208 c_mindepth(char ***argvp, int isok) 1209 { 1210 char *arg = **argvp; 1211 PLAN *new; 1212 1213 (*argvp)++; 1214 new = palloc(N_MINDEPTH, f_mindepth); 1215 new->min_data = atoi(arg); 1216 return (new); 1217 } 1218 1219 /* 1220 * -mmin n functions -- 1221 * 1222 * True if the difference between the file modification time and the 1223 * current time is n 24 hour periods. 1224 */ 1225 int 1226 f_mmin(PLAN *plan, FTSENT *entry) 1227 { 1228 COMPARE((now - entry->fts_statp->st_mtime + SECSPERMIN - 1) / 1229 SECSPERMIN, plan->t_data); 1230 } 1231 1232 PLAN * 1233 c_mmin(char ***argvp, int isok) 1234 { 1235 char *arg = **argvp; 1236 PLAN *new; 1237 1238 (*argvp)++; 1239 ftsoptions &= ~FTS_NOSTAT; 1240 1241 new = palloc(N_MMIN, f_mmin); 1242 new->t_data = find_parsenum(new, "-mmin", arg, NULL); 1243 TIME_CORRECT(new, N_MMIN); 1244 return (new); 1245 } 1246 1247 /* 1248 * -mtime n functions -- 1249 * 1250 * True if the difference between the file modification time and the 1251 * current time is n 24 hour periods. 1252 */ 1253 int 1254 f_mtime(PLAN *plan, FTSENT *entry) 1255 { 1256 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) / 1257 SECSPERDAY, plan->t_data); 1258 } 1259 1260 PLAN * 1261 c_mtime(char ***argvp, int isok) 1262 { 1263 char *arg = **argvp; 1264 PLAN *new; 1265 1266 (*argvp)++; 1267 ftsoptions &= ~FTS_NOSTAT; 1268 1269 new = palloc(N_MTIME, f_mtime); 1270 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 1271 TIME_CORRECT(new, N_MTIME); 1272 return (new); 1273 } 1274 1275 /* 1276 * -name functions -- 1277 * 1278 * True if the basename of the filename being examined 1279 * matches pattern using Pattern Matching Notation S3.14 1280 */ 1281 int 1282 f_name(PLAN *plan, FTSENT *entry) 1283 { 1284 1285 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 1286 } 1287 1288 PLAN * 1289 c_name(char ***argvp, int isok) 1290 { 1291 char *pattern = **argvp; 1292 PLAN *new; 1293 1294 (*argvp)++; 1295 new = palloc(N_NAME, f_name); 1296 new->c_data = pattern; 1297 return (new); 1298 } 1299 1300 /* 1301 * -iname functions -- 1302 * 1303 * Similar to -name, but does case insensitive matching 1304 * 1305 */ 1306 int 1307 f_iname(PLAN *plan, FTSENT *entry) 1308 { 1309 return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD)); 1310 } 1311 1312 PLAN * 1313 c_iname(char ***argvp, int isok) 1314 { 1315 char *pattern = **argvp; 1316 PLAN *new; 1317 1318 (*argvp)++; 1319 new = palloc(N_INAME, f_iname); 1320 new->c_data = pattern; 1321 return (new); 1322 } 1323 1324 /* 1325 * -newer file functions -- 1326 * 1327 * True if the current file has been modified more recently 1328 * than the modification time of the file named by the pathname 1329 * file. 1330 */ 1331 int 1332 f_newer(PLAN *plan, FTSENT *entry) 1333 { 1334 1335 return timespeccmp(&entry->fts_statp->st_mtim, &plan->ts_data, >); 1336 } 1337 1338 PLAN * 1339 c_newer(char ***argvp, int isok) 1340 { 1341 char *filename = **argvp; 1342 PLAN *new; 1343 struct stat sb; 1344 1345 (*argvp)++; 1346 ftsoptions &= ~FTS_NOSTAT; 1347 1348 if (stat(filename, &sb)) 1349 err(1, "%s", filename); 1350 new = palloc(N_NEWER, f_newer); 1351 new->ts_data = sb.st_mtim; 1352 return (new); 1353 } 1354 1355 /* 1356 * -nogroup functions -- 1357 * 1358 * True if file belongs to a user ID for which the equivalent 1359 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 1360 */ 1361 int 1362 f_nogroup(PLAN *plan, FTSENT *entry) 1363 { 1364 1365 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 1366 } 1367 1368 PLAN * 1369 c_nogroup(char ***argvp, int isok) 1370 { 1371 ftsoptions &= ~FTS_NOSTAT; 1372 1373 return (palloc(N_NOGROUP, f_nogroup)); 1374 } 1375 1376 /* 1377 * -nouser functions -- 1378 * 1379 * True if file belongs to a user ID for which the equivalent 1380 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 1381 */ 1382 int 1383 f_nouser(PLAN *plan, FTSENT *entry) 1384 { 1385 1386 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 1387 } 1388 1389 PLAN * 1390 c_nouser(char ***argvp, int isok) 1391 { 1392 ftsoptions &= ~FTS_NOSTAT; 1393 1394 return (palloc(N_NOUSER, f_nouser)); 1395 } 1396 1397 /* 1398 * -path functions -- 1399 * 1400 * True if the path of the filename being examined 1401 * matches pattern using Pattern Matching Notation S3.14 1402 */ 1403 int 1404 f_path(PLAN *plan, FTSENT *entry) 1405 { 1406 1407 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 1408 } 1409 1410 PLAN * 1411 c_path(char ***argvp, int isok) 1412 { 1413 char *pattern = **argvp; 1414 PLAN *new; 1415 1416 (*argvp)++; 1417 new = palloc(N_NAME, f_path); 1418 new->c_data = pattern; 1419 return (new); 1420 } 1421 1422 /* 1423 * -perm functions -- 1424 * 1425 * The mode argument is used to represent file mode bits. If it starts 1426 * with a leading digit, it's treated as an octal mode, otherwise as a 1427 * symbolic mode. 1428 */ 1429 int 1430 f_perm(PLAN *plan, FTSENT *entry) 1431 { 1432 mode_t mode; 1433 1434 mode = entry->fts_statp->st_mode & 1435 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 1436 if (plan->flags == F_ATLEAST) 1437 return ((plan->m_data | mode) == mode); 1438 else 1439 return (mode == plan->m_data); 1440 /* NOTREACHED */ 1441 } 1442 1443 PLAN * 1444 c_perm(char ***argvp, int isok) 1445 { 1446 char *perm = **argvp; 1447 PLAN *new; 1448 mode_t *set; 1449 1450 (*argvp)++; 1451 ftsoptions &= ~FTS_NOSTAT; 1452 1453 new = palloc(N_PERM, f_perm); 1454 1455 if (*perm == '-') { 1456 new->flags = F_ATLEAST; 1457 ++perm; 1458 } 1459 1460 if ((set = setmode(perm)) == NULL) 1461 err(1, "-perm: Cannot set file mode `%s'", perm); 1462 1463 new->m_data = getmode(set, 0); 1464 free(set); 1465 return (new); 1466 } 1467 1468 /* 1469 * -print functions -- 1470 * 1471 * Always true, causes the current pathame to be written to 1472 * standard output. 1473 */ 1474 int 1475 f_print(PLAN *plan, FTSENT *entry) 1476 { 1477 1478 (void)printf("%s\n", entry->fts_path); 1479 return (1); 1480 } 1481 1482 int 1483 f_print0(PLAN *plan, FTSENT *entry) 1484 { 1485 1486 (void)fputs(entry->fts_path, stdout); 1487 (void)fputc('\0', stdout); 1488 return (1); 1489 } 1490 1491 int 1492 f_printx(PLAN *plan, FTSENT *entry) 1493 { 1494 char *cp; 1495 1496 for (cp = entry->fts_path; *cp; cp++) { 1497 if (*cp == '\'' || *cp == '\"' || *cp == ' ' || 1498 *cp == '$' || *cp == '`' || 1499 *cp == '\t' || *cp == '\n' || *cp == '\\') 1500 fputc('\\', stdout); 1501 1502 fputc(*cp, stdout); 1503 } 1504 1505 fputc('\n', stdout); 1506 return (1); 1507 } 1508 1509 PLAN * 1510 c_print(char ***argvp, int isok) 1511 { 1512 1513 isoutput = 1; 1514 1515 return (palloc(N_PRINT, f_print)); 1516 } 1517 1518 PLAN * 1519 c_print0(char ***argvp, int isok) 1520 { 1521 1522 isoutput = 1; 1523 1524 return (palloc(N_PRINT0, f_print0)); 1525 } 1526 1527 PLAN * 1528 c_printx(char ***argvp, int isok) 1529 { 1530 1531 isoutput = 1; 1532 1533 return (palloc(N_PRINTX, f_printx)); 1534 } 1535 1536 /* 1537 * -prune functions -- 1538 * 1539 * Prune a portion of the hierarchy. 1540 */ 1541 int 1542 f_prune(PLAN *plan, FTSENT *entry) 1543 { 1544 if (fts_set(tree, entry, FTS_SKIP)) 1545 err(1, "%s", entry->fts_path); 1546 return (1); 1547 } 1548 1549 PLAN * 1550 c_prune(char ***argvp, int isok) 1551 { 1552 1553 return (palloc(N_PRUNE, f_prune)); 1554 } 1555 1556 /* 1557 * -regex regexp (and related) functions -- 1558 * 1559 * True if the complete file path matches the regular expression regexp. 1560 * For -regex, regexp is a case-sensitive (basic) regular expression. 1561 * For -iregex, regexp is a case-insensitive (basic) regular expression. 1562 */ 1563 int 1564 f_regex(PLAN *plan, FTSENT *entry) 1565 { 1566 1567 return (regexec(&plan->regexp_data, entry->fts_path, 0, NULL, 0) == 0); 1568 } 1569 1570 static PLAN * 1571 c_regex_common(char ***argvp, int isok, enum ntype type, bool icase) 1572 { 1573 char errbuf[LINE_MAX]; 1574 regex_t reg; 1575 char *regexp = **argvp; 1576 char *lineregexp; 1577 PLAN *new; 1578 int rv; 1579 size_t len; 1580 1581 (*argvp)++; 1582 1583 len = strlen(regexp) + 1 + 6; 1584 lineregexp = malloc(len); /* max needed */ 1585 if (lineregexp == NULL) 1586 err(1, NULL); 1587 snprintf(lineregexp, len, "^%s(%s%s)$", 1588 (regcomp_flags & REG_EXTENDED) ? "" : "\\", regexp, 1589 (regcomp_flags & REG_EXTENDED) ? "" : "\\"); 1590 rv = regcomp(®, lineregexp, REG_NOSUB|regcomp_flags| 1591 (icase ? REG_ICASE : 0)); 1592 free(lineregexp); 1593 if (rv != 0) { 1594 regerror(rv, ®, errbuf, sizeof errbuf); 1595 errx(1, "regexp %s: %s", regexp, errbuf); 1596 } 1597 1598 new = palloc(type, f_regex); 1599 new->regexp_data = reg; 1600 return (new); 1601 } 1602 1603 PLAN * 1604 c_regex(char ***argvp, int isok) 1605 { 1606 1607 return (c_regex_common(argvp, isok, N_REGEX, false)); 1608 } 1609 1610 PLAN * 1611 c_iregex(char ***argvp, int isok) 1612 { 1613 1614 return (c_regex_common(argvp, isok, N_IREGEX, true)); 1615 } 1616 1617 /* 1618 * -size n[c] functions -- 1619 * 1620 * True if the file size in bytes, divided by an implementation defined 1621 * value and rounded up to the next integer, is n. If n is followed by 1622 * a c, the size is in bytes. 1623 */ 1624 #define FIND_SIZE 512 1625 static int divsize = 1; 1626 1627 int 1628 f_size(PLAN *plan, FTSENT *entry) 1629 { 1630 off_t size; 1631 1632 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1633 FIND_SIZE : entry->fts_statp->st_size; 1634 COMPARE(size, plan->o_data); 1635 } 1636 1637 PLAN * 1638 c_size(char ***argvp, int isok) 1639 { 1640 char *arg = **argvp; 1641 PLAN *new; 1642 char endch; 1643 1644 (*argvp)++; 1645 ftsoptions &= ~FTS_NOSTAT; 1646 1647 new = palloc(N_SIZE, f_size); 1648 endch = 'c'; 1649 new->o_data = find_parsenum(new, "-size", arg, &endch); 1650 if (endch == 'c') 1651 divsize = 0; 1652 return (new); 1653 } 1654 1655 /* 1656 * -type c functions -- 1657 * 1658 * True if the type of the file is c, where c is b, c, d, p, f or w 1659 * for block special file, character special file, directory, FIFO, 1660 * regular file or whiteout respectively. 1661 */ 1662 int 1663 f_type(PLAN *plan, FTSENT *entry) 1664 { 1665 1666 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1667 } 1668 1669 PLAN * 1670 c_type(char ***argvp, int isok) 1671 { 1672 char *typestring = **argvp; 1673 PLAN *new; 1674 mode_t mask = (mode_t)0; 1675 1676 (*argvp)++; 1677 ftsoptions &= ~FTS_NOSTAT; 1678 1679 switch (typestring[0]) { 1680 case 'b': 1681 mask = S_IFBLK; 1682 break; 1683 case 'c': 1684 mask = S_IFCHR; 1685 break; 1686 case 'd': 1687 mask = S_IFDIR; 1688 break; 1689 case 'f': 1690 mask = S_IFREG; 1691 break; 1692 case 'l': 1693 mask = S_IFLNK; 1694 break; 1695 case 'p': 1696 mask = S_IFIFO; 1697 break; 1698 case 's': 1699 mask = S_IFSOCK; 1700 break; 1701 #ifdef S_IFWHT 1702 case 'W': 1703 case 'w': 1704 mask = S_IFWHT; 1705 #ifdef FTS_WHITEOUT 1706 ftsoptions |= FTS_WHITEOUT; 1707 #endif 1708 break; 1709 #endif /* S_IFWHT */ 1710 default: 1711 errx(1, "-type: %s: unknown type", typestring); 1712 } 1713 1714 new = palloc(N_TYPE, f_type); 1715 new->m_data = mask; 1716 return (new); 1717 } 1718 1719 /* 1720 * -user uname functions -- 1721 * 1722 * True if the file belongs to the user uname. If uname is numeric and 1723 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1724 * return a valid user name, uname is taken as a user ID. 1725 */ 1726 int 1727 f_user(PLAN *plan, FTSENT *entry) 1728 { 1729 1730 COMPARE(entry->fts_statp->st_uid, plan->u_data); 1731 } 1732 1733 PLAN * 1734 c_user(char ***argvp, int isok) 1735 { 1736 char *username = **argvp; 1737 PLAN *new; 1738 struct passwd *p; 1739 uid_t uid; 1740 1741 (*argvp)++; 1742 ftsoptions &= ~FTS_NOSTAT; 1743 1744 new = palloc(N_USER, f_user); 1745 p = getpwnam(username); 1746 if (p == NULL) { 1747 if (atoi(username) == 0 && username[0] != '0' && 1748 strcmp(username, "+0") && strcmp(username, "-0")) 1749 errx(1, "-user: %s: no such user", username); 1750 uid = find_parsenum(new, "-user", username, NULL); 1751 1752 } else { 1753 new->flags = F_EQUAL; 1754 uid = p->pw_uid; 1755 } 1756 1757 new->u_data = uid; 1758 return (new); 1759 } 1760 1761 /* 1762 * -xdev functions -- 1763 * 1764 * Always true, causes find not to descend past directories that have a 1765 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1766 */ 1767 PLAN * 1768 c_xdev(char ***argvp, int isok) 1769 { 1770 ftsoptions |= FTS_XDEV; 1771 1772 return (palloc(N_XDEV, f_always_true)); 1773 } 1774 1775 /* 1776 * ( expression ) functions -- 1777 * 1778 * True if expression is true. 1779 */ 1780 int 1781 f_expr(PLAN *plan, FTSENT *entry) 1782 { 1783 PLAN *p; 1784 int state; 1785 1786 state = 0; 1787 for (p = plan->p_data[0]; 1788 p && (state = (p->eval)(p, entry)); p = p->next); 1789 return (state); 1790 } 1791 1792 /* 1793 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1794 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1795 * to a N_EXPR node containing the expression and the ')' node is discarded. 1796 */ 1797 PLAN * 1798 c_openparen(char ***argvp, int isok) 1799 { 1800 1801 return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1)); 1802 } 1803 1804 PLAN * 1805 c_closeparen(char ***argvp, int isok) 1806 { 1807 1808 return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1)); 1809 } 1810 1811 /* 1812 * ! expression functions -- 1813 * 1814 * Negation of a primary; the unary NOT operator. 1815 */ 1816 int 1817 f_not(PLAN *plan, FTSENT *entry) 1818 { 1819 PLAN *p; 1820 int state; 1821 1822 state = 0; 1823 for (p = plan->p_data[0]; 1824 p && (state = (p->eval)(p, entry)); p = p->next); 1825 return (!state); 1826 } 1827 1828 PLAN * 1829 c_not(char ***argvp, int isok) 1830 { 1831 1832 return (palloc(N_NOT, f_not)); 1833 } 1834 1835 /* 1836 * expression -o expression functions -- 1837 * 1838 * Alternation of primaries; the OR operator. The second expression is 1839 * not evaluated if the first expression is true. 1840 */ 1841 int 1842 f_or(PLAN *plan, FTSENT *entry) 1843 { 1844 PLAN *p; 1845 int state; 1846 1847 state = 0; 1848 for (p = plan->p_data[0]; 1849 p && (state = (p->eval)(p, entry)); p = p->next); 1850 1851 if (state) 1852 return (1); 1853 1854 for (p = plan->p_data[1]; 1855 p && (state = (p->eval)(p, entry)); p = p->next); 1856 return (state); 1857 } 1858 1859 PLAN * 1860 c_or(char ***argvp, int isok) 1861 { 1862 1863 return (palloc(N_OR, f_or)); 1864 } 1865 1866 PLAN * 1867 c_null(char ***argvp, int isok) 1868 { 1869 1870 return (NULL); 1871 } 1872 1873 1874 /* 1875 * plan_cleanup -- 1876 * Check and see if the specified plan has any residual state, 1877 * and if so, clean it up as appropriate. 1878 * 1879 * At the moment, only N_EXEC has state. Two kinds: 1) 1880 * lists of files to feed to subprocesses 2) State on exit 1881 * statusses of past subprocesses. 1882 */ 1883 /* ARGSUSED1 */ 1884 int 1885 plan_cleanup(PLAN *plan, void *arg) 1886 { 1887 if (plan->type==N_EXEC && plan->ep_narg) 1888 run_f_exec(plan); 1889 1890 return plan->ep_rval; /* Passed save exit-status up chain */ 1891 } 1892 1893 static PLAN * 1894 palloc(enum ntype t, int (*f)(PLAN *, FTSENT *)) 1895 { 1896 PLAN *new; 1897 1898 if ((new = malloc(sizeof(PLAN))) == NULL) 1899 err(1, NULL); 1900 memset(new, 0, sizeof(PLAN)); 1901 new->type = t; 1902 new->eval = f; 1903 return (new); 1904 } 1905