| 1 |
/*- |
| 2 |
* SPDX-License-Identifier: BSD-3-Clause |
| 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 |
#if 0 |
| 36 |
static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; |
| 37 |
#endif |
| 38 |
|
| 39 |
#include <sys/cdefs.h> |
| 40 |
__FBSDID("$FreeBSD$"); |
| 41 |
|
| 42 |
#include <sys/param.h> |
| 43 |
#include <sys/ucred.h> |
| 44 |
#include <sys/stat.h> |
| 45 |
#include <sys/types.h> |
| 46 |
#include <sys/acl.h> |
| 47 |
#include <sys/wait.h> |
| 48 |
#include <sys/mount.h> |
| 49 |
|
| 50 |
#include <dirent.h> |
| 51 |
#include <err.h> |
| 52 |
#include <errno.h> |
| 53 |
#include <fnmatch.h> |
| 54 |
#include <fts.h> |
| 55 |
#include <grp.h> |
| 56 |
#include <limits.h> |
| 57 |
#include <pwd.h> |
| 58 |
#include <regex.h> |
| 59 |
#include <stdio.h> |
| 60 |
#include <stdlib.h> |
| 61 |
#include <string.h> |
| 62 |
#include <unistd.h> |
| 63 |
#include <ctype.h> |
| 64 |
|
| 65 |
#include "find.h" |
| 66 |
|
| 67 |
static PLAN *palloc(OPTION *); |
| 68 |
static long long find_parsenum(PLAN *, const char *, char *, char *); |
| 69 |
static long long find_parsetime(PLAN *, const char *, char *); |
| 70 |
static char *nextarg(OPTION *, char ***); |
| 71 |
|
| 72 |
extern char **environ; |
| 73 |
|
| 74 |
static PLAN *lastexecplus = NULL; |
| 75 |
|
| 76 |
#define COMPARE(a, b) do { \ |
| 77 |
switch (plan->flags & F_ELG_MASK) { \ |
| 78 |
case F_EQUAL: \ |
| 79 |
return (a == b); \ |
| 80 |
case F_LESSTHAN: \ |
| 81 |
return (a < b); \ |
| 82 |
case F_GREATER: \ |
| 83 |
return (a > b); \ |
| 84 |
default: \ |
| 85 |
abort(); \ |
| 86 |
} \ |
| 87 |
} while(0) |
| 88 |
|
| 89 |
static PLAN * |
| 90 |
palloc(OPTION *option) |
| 91 |
{ |
| 92 |
PLAN *new; |
| 93 |
|
| 94 |
if ((new = malloc(sizeof(PLAN))) == NULL) |
| 95 |
err(1, NULL); |
| 96 |
new->execute = option->execute; |
| 97 |
new->flags = option->flags; |
| 98 |
new->next = NULL; |
| 99 |
return new; |
| 100 |
} |
| 101 |
|
| 102 |
/* |
| 103 |
* find_parsenum -- |
| 104 |
* Parse a string of the form [+-]# and return the value. |
| 105 |
*/ |
| 106 |
static long long |
| 107 |
find_parsenum(PLAN *plan, const char *option, char *vp, char *endch) |
| 108 |
{ |
| 109 |
long long value; |
| 110 |
char *endchar, *str; /* Pointer to character ending conversion. */ |
| 111 |
|
| 112 |
/* Determine comparison from leading + or -. */ |
| 113 |
str = vp; |
| 114 |
switch (*str) { |
| 115 |
case '+': |
| 116 |
++str; |
| 117 |
plan->flags |= F_GREATER; |
| 118 |
break; |
| 119 |
case '-': |
| 120 |
++str; |
| 121 |
plan->flags |= F_LESSTHAN; |
| 122 |
break; |
| 123 |
default: |
| 124 |
plan->flags |= F_EQUAL; |
| 125 |
break; |
| 126 |
} |
| 127 |
|
| 128 |
/* |
| 129 |
* Convert the string with strtoq(). Note, if strtoq() returns zero |
| 130 |
* and endchar points to the beginning of the string we know we have |
| 131 |
* a syntax error. |
| 132 |
*/ |
| 133 |
value = strtoq(str, &endchar, 10); |
| 134 |
if (value == 0 && endchar == str) |
| 135 |
errx(1, "%s: %s: illegal numeric value", option, vp); |
| 136 |
if (endchar[0] && endch == NULL) |
| 137 |
errx(1, "%s: %s: illegal trailing character", option, vp); |
| 138 |
if (endch) |
| 139 |
*endch = endchar[0]; |
| 140 |
return value; |
| 141 |
} |
| 142 |
|
| 143 |
/* |
| 144 |
* find_parsetime -- |
| 145 |
* Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. |
| 146 |
*/ |
| 147 |
static long long |
| 148 |
find_parsetime(PLAN *plan, const char *option, char *vp) |
| 149 |
{ |
| 150 |
long long secs, value; |
| 151 |
char *str, *unit; /* Pointer to character ending conversion. */ |
| 152 |
|
| 153 |
/* Determine comparison from leading + or -. */ |
| 154 |
str = vp; |
| 155 |
switch (*str) { |
| 156 |
case '+': |
| 157 |
++str; |
| 158 |
plan->flags |= F_GREATER; |
| 159 |
break; |
| 160 |
case '-': |
| 161 |
++str; |
| 162 |
plan->flags |= F_LESSTHAN; |
| 163 |
break; |
| 164 |
default: |
| 165 |
plan->flags |= F_EQUAL; |
| 166 |
break; |
| 167 |
} |
| 168 |
|
| 169 |
value = strtoq(str, &unit, 10); |
| 170 |
if (value == 0 && unit == str) { |
| 171 |
errx(1, "%s: %s: illegal time value", option, vp); |
| 172 |
/* NOTREACHED */ |
| 173 |
} |
| 174 |
if (*unit == '\0') |
| 175 |
return value; |
| 176 |
|
| 177 |
/* Units syntax. */ |
| 178 |
secs = 0; |
| 179 |
for (;;) { |
| 180 |
switch(*unit) { |
| 181 |
case 's': /* seconds */ |
| 182 |
secs += value; |
| 183 |
break; |
| 184 |
case 'm': /* minutes */ |
| 185 |
secs += value * 60; |
| 186 |
break; |
| 187 |
case 'h': /* hours */ |
| 188 |
secs += value * 3600; |
| 189 |
break; |
| 190 |
case 'd': /* days */ |
| 191 |
secs += value * 86400; |
| 192 |
break; |
| 193 |
case 'w': /* weeks */ |
| 194 |
secs += value * 604800; |
| 195 |
break; |
| 196 |
default: |
| 197 |
errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); |
| 198 |
/* NOTREACHED */ |
| 199 |
} |
| 200 |
str = unit + 1; |
| 201 |
if (*str == '\0') /* EOS */ |
| 202 |
break; |
| 203 |
value = strtoq(str, &unit, 10); |
| 204 |
if (value == 0 && unit == str) { |
| 205 |
errx(1, "%s: %s: illegal time value", option, vp); |
| 206 |
/* NOTREACHED */ |
| 207 |
} |
| 208 |
if (*unit == '\0') { |
| 209 |
errx(1, "%s: %s: missing trailing unit", option, vp); |
| 210 |
/* NOTREACHED */ |
| 211 |
} |
| 212 |
} |
| 213 |
plan->flags |= F_EXACTTIME; |
| 214 |
return secs; |
| 215 |
} |
| 216 |
|
| 217 |
/* |
| 218 |
* nextarg -- |
| 219 |
* Check that another argument still exists, return a pointer to it, |
| 220 |
* and increment the argument vector pointer. |
| 221 |
*/ |
| 222 |
static char * |
| 223 |
nextarg(OPTION *option, char ***argvp) |
| 224 |
{ |
| 225 |
char *arg; |
| 226 |
|
| 227 |
if ((arg = **argvp) == NULL) |
| 228 |
errx(1, "%s: requires additional arguments", option->name); |
| 229 |
(*argvp)++; |
| 230 |
return arg; |
| 231 |
} /* nextarg() */ |
| 232 |
|
| 233 |
/* |
| 234 |
* The value of n for the inode times (atime, birthtime, ctime, mtime) is a |
| 235 |
* range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts |
| 236 |
* with -n, such that "-mtime -1" would be less than 0 days, which isn't what |
| 237 |
* the user wanted. Correct so that -1 is "less than 1". |
| 238 |
*/ |
| 239 |
#define TIME_CORRECT(p) \ |
| 240 |
if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ |
| 241 |
++((p)->t_data.tv_sec); |
| 242 |
|
| 243 |
/* |
| 244 |
* -[acm]min n functions -- |
| 245 |
* |
| 246 |
* True if the difference between the |
| 247 |
* file access time (-amin) |
| 248 |
* file birth time (-Bmin) |
| 249 |
* last change of file status information (-cmin) |
| 250 |
* file modification time (-mmin) |
| 251 |
* and the current time is n min periods. |
| 252 |
*/ |
| 253 |
int |
| 254 |
f_Xmin(PLAN *plan, FTSENT *entry) |
| 255 |
{ |
| 256 |
if (plan->flags & F_TIME_C) { |
| 257 |
COMPARE((now - entry->fts_statp->st_ctime + |
| 258 |
60 - 1) / 60, plan->t_data.tv_sec); |
| 259 |
} else if (plan->flags & F_TIME_A) { |
| 260 |
COMPARE((now - entry->fts_statp->st_atime + |
| 261 |
60 - 1) / 60, plan->t_data.tv_sec); |
| 262 |
#if HAVE_STRUCT_STAT_ST_BIRTHTIME |
| 263 |
} else if (plan->flags & F_TIME_B) { |
| 264 |
COMPARE((now - entry->fts_statp->st_birthtime + |
| 265 |
60 - 1) / 60, plan->t_data.tv_sec); |
| 266 |
#endif |
| 267 |
} else { |
| 268 |
COMPARE((now - entry->fts_statp->st_mtime + |
| 269 |
60 - 1) / 60, plan->t_data.tv_sec); |
| 270 |
} |
| 271 |
} |
| 272 |
|
| 273 |
PLAN * |
| 274 |
c_Xmin(OPTION *option, char ***argvp) |
| 275 |
{ |
| 276 |
char *nmins; |
| 277 |
PLAN *new; |
| 278 |
|
| 279 |
nmins = nextarg(option, argvp); |
| 280 |
ftsoptions &= ~FTS_NOSTAT; |
| 281 |
|
| 282 |
new = palloc(option); |
| 283 |
new->t_data.tv_sec = find_parsenum(new, option->name, nmins, NULL); |
| 284 |
new->t_data.tv_nsec = 0; |
| 285 |
TIME_CORRECT(new); |
| 286 |
return new; |
| 287 |
} |
| 288 |
|
| 289 |
/* |
| 290 |
* -[acm]time n functions -- |
| 291 |
* |
| 292 |
* True if the difference between the |
| 293 |
* file access time (-atime) |
| 294 |
* file birth time (-Btime) |
| 295 |
* last change of file status information (-ctime) |
| 296 |
* file modification time (-mtime) |
| 297 |
* and the current time is n 24 hour periods. |
| 298 |
*/ |
| 299 |
|
| 300 |
int |
| 301 |
f_Xtime(PLAN *plan, FTSENT *entry) |
| 302 |
{ |
| 303 |
time_t xtime; |
| 304 |
|
| 305 |
if (plan->flags & F_TIME_A) |
| 306 |
xtime = entry->fts_statp->st_atime; |
| 307 |
#if HAVE_STRUCT_STAT_ST_BIRTHTIME |
| 308 |
else if (plan->flags & F_TIME_B) |
| 309 |
xtime = entry->fts_statp->st_birthtime; |
| 310 |
#endif |
| 311 |
else if (plan->flags & F_TIME_C) |
| 312 |
xtime = entry->fts_statp->st_ctime; |
| 313 |
else |
| 314 |
xtime = entry->fts_statp->st_mtime; |
| 315 |
|
| 316 |
if (plan->flags & F_EXACTTIME) |
| 317 |
COMPARE(now - xtime, plan->t_data.tv_sec); |
| 318 |
else |
| 319 |
COMPARE((now - xtime + 86400 - 1) / 86400, plan->t_data.tv_sec); |
| 320 |
} |
| 321 |
|
| 322 |
PLAN * |
| 323 |
c_Xtime(OPTION *option, char ***argvp) |
| 324 |
{ |
| 325 |
char *value; |
| 326 |
PLAN *new; |
| 327 |
|
| 328 |
value = nextarg(option, argvp); |
| 329 |
ftsoptions &= ~FTS_NOSTAT; |
| 330 |
|
| 331 |
new = palloc(option); |
| 332 |
new->t_data.tv_sec = find_parsetime(new, option->name, value); |
| 333 |
new->t_data.tv_nsec = 0; |
| 334 |
if (!(new->flags & F_EXACTTIME)) |
| 335 |
TIME_CORRECT(new); |
| 336 |
return new; |
| 337 |
} |
| 338 |
|
| 339 |
/* |
| 340 |
* -maxdepth/-mindepth n functions -- |
| 341 |
* |
| 342 |
* Does the same as -prune if the level of the current file is |
| 343 |
* greater/less than the specified maximum/minimum depth. |
| 344 |
* |
| 345 |
* Note that -maxdepth and -mindepth are handled specially in |
| 346 |
* find_execute() so their f_* functions are set to f_always_true(). |
| 347 |
*/ |
| 348 |
PLAN * |
| 349 |
c_mXXdepth(OPTION *option, char ***argvp) |
| 350 |
{ |
| 351 |
char *dstr; |
| 352 |
PLAN *new; |
| 353 |
|
| 354 |
dstr = nextarg(option, argvp); |
| 355 |
if (dstr[0] == '-') |
| 356 |
/* all other errors handled by find_parsenum() */ |
| 357 |
errx(1, "%s: %s: value must be positive", option->name, dstr); |
| 358 |
|
| 359 |
new = palloc(option); |
| 360 |
if (option->flags & F_MAXDEPTH) |
| 361 |
maxdepth = find_parsenum(new, option->name, dstr, NULL); |
| 362 |
else |
| 363 |
mindepth = find_parsenum(new, option->name, dstr, NULL); |
| 364 |
return new; |
| 365 |
} |
| 366 |
|
| 367 |
#ifdef ACL_TYPE_NFS4 |
| 368 |
/* |
| 369 |
* -acl function -- |
| 370 |
* |
| 371 |
* Show files with EXTENDED ACL attributes. |
| 372 |
*/ |
| 373 |
int |
| 374 |
f_acl(PLAN *plan __unused, FTSENT *entry) |
| 375 |
{ |
| 376 |
acl_t facl; |
| 377 |
acl_type_t acl_type; |
| 378 |
int acl_supported = 0, ret, trivial; |
| 379 |
|
| 380 |
if (S_ISLNK(entry->fts_statp->st_mode)) |
| 381 |
return 0; |
| 382 |
ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); |
| 383 |
if (ret > 0) { |
| 384 |
acl_supported = 1; |
| 385 |
acl_type = ACL_TYPE_NFS4; |
| 386 |
} else if (ret < 0 && errno != EINVAL) { |
| 387 |
warn("%s", entry->fts_accpath); |
| 388 |
return (0); |
| 389 |
} |
| 390 |
if (acl_supported == 0) { |
| 391 |
ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); |
| 392 |
if (ret > 0) { |
| 393 |
acl_supported = 1; |
| 394 |
acl_type = ACL_TYPE_ACCESS; |
| 395 |
} else if (ret < 0 && errno != EINVAL) { |
| 396 |
warn("%s", entry->fts_accpath); |
| 397 |
return (0); |
| 398 |
} |
| 399 |
} |
| 400 |
if (acl_supported == 0) |
| 401 |
return (0); |
| 402 |
|
| 403 |
facl = acl_get_file(entry->fts_accpath, acl_type); |
| 404 |
if (facl == NULL) { |
| 405 |
warn("%s", entry->fts_accpath); |
| 406 |
return (0); |
| 407 |
} |
| 408 |
ret = acl_is_trivial_np(facl, &trivial); |
| 409 |
acl_free(facl); |
| 410 |
if (ret) { |
| 411 |
warn("%s", entry->fts_accpath); |
| 412 |
return (0); |
| 413 |
} |
| 414 |
if (trivial) |
| 415 |
return (0); |
| 416 |
return (1); |
| 417 |
} |
| 418 |
#endif |
| 419 |
|
| 420 |
PLAN * |
| 421 |
c_acl(OPTION *option, char ***argvp __unused) |
| 422 |
{ |
| 423 |
ftsoptions &= ~FTS_NOSTAT; |
| 424 |
return (palloc(option)); |
| 425 |
} |
| 426 |
|
| 427 |
/* |
| 428 |
* -delete functions -- |
| 429 |
* |
| 430 |
* True always. Makes its best shot and continues on regardless. |
| 431 |
*/ |
| 432 |
int |
| 433 |
f_delete(PLAN *plan __unused, FTSENT *entry) |
| 434 |
{ |
| 435 |
/* ignore these from fts */ |
| 436 |
if (strcmp(entry->fts_accpath, ".") == 0 || |
| 437 |
strcmp(entry->fts_accpath, "..") == 0) |
| 438 |
return 1; |
| 439 |
|
| 440 |
/* sanity check */ |
| 441 |
if (isdepth == 0 || /* depth off */ |
| 442 |
(ftsoptions & FTS_NOSTAT)) /* not stat()ing */ |
| 443 |
errx(1, "-delete: insecure options got turned on"); |
| 444 |
|
| 445 |
if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ |
| 446 |
(ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ |
| 447 |
errx(1, "-delete: forbidden when symlinks are followed"); |
| 448 |
|
| 449 |
/* Potentially unsafe - do not accept relative paths whatsoever */ |
| 450 |
if (entry->fts_level > FTS_ROOTLEVEL && |
| 451 |
strchr(entry->fts_accpath, '/') != NULL) |
| 452 |
errx(1, "-delete: %s: relative path potentially not safe", |
| 453 |
entry->fts_accpath); |
| 454 |
|
| 455 |
#if HAVE_STRUCT_STAT_ST_FLAGS |
| 456 |
/* Turn off user immutable bits if running as root */ |
| 457 |
if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && |
| 458 |
!(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && |
| 459 |
geteuid() == 0) |
| 460 |
lchflags(entry->fts_accpath, |
| 461 |
entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); |
| 462 |
#endif |
| 463 |
|
| 464 |
/* rmdir directories, unlink everything else */ |
| 465 |
if (S_ISDIR(entry->fts_statp->st_mode)) { |
| 466 |
if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) |
| 467 |
warn("-delete: rmdir(%s)", entry->fts_path); |
| 468 |
} else { |
| 469 |
if (unlink(entry->fts_accpath) < 0) |
| 470 |
warn("-delete: unlink(%s)", entry->fts_path); |
| 471 |
} |
| 472 |
|
| 473 |
/* "succeed" */ |
| 474 |
return 1; |
| 475 |
} |
| 476 |
|
| 477 |
PLAN * |
| 478 |
c_delete(OPTION *option, char ***argvp __unused) |
| 479 |
{ |
| 480 |
|
| 481 |
ftsoptions &= ~FTS_NOSTAT; /* no optimise */ |
| 482 |
isoutput = 1; /* possible output */ |
| 483 |
isdepth = 1; /* -depth implied */ |
| 484 |
|
| 485 |
/* |
| 486 |
* Try to avoid the confusing error message about relative paths |
| 487 |
* being potentially not safe. |
| 488 |
*/ |
| 489 |
if (ftsoptions & FTS_NOCHDIR) |
| 490 |
errx(1, "%s: forbidden when the current directory cannot be opened", |
| 491 |
"-delete"); |
| 492 |
|
| 493 |
return palloc(option); |
| 494 |
} |
| 495 |
|
| 496 |
|
| 497 |
/* |
| 498 |
* always_true -- |
| 499 |
* |
| 500 |
* Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true |
| 501 |
*/ |
| 502 |
int |
| 503 |
f_always_true(PLAN *plan __unused, FTSENT *entry __unused) |
| 504 |
{ |
| 505 |
return 1; |
| 506 |
} |
| 507 |
|
| 508 |
/* |
| 509 |
* -depth functions -- |
| 510 |
* |
| 511 |
* With argument: True if the file is at level n. |
| 512 |
* Without argument: Always true, causes descent of the directory hierarchy |
| 513 |
* to be done so that all entries in a directory are acted on before the |
| 514 |
* directory itself. |
| 515 |
*/ |
| 516 |
int |
| 517 |
f_depth(PLAN *plan, FTSENT *entry) |
| 518 |
{ |
| 519 |
if (plan->flags & F_DEPTH) |
| 520 |
COMPARE(entry->fts_level, plan->d_data); |
| 521 |
else |
| 522 |
return 1; |
| 523 |
} |
| 524 |
|
| 525 |
PLAN * |
| 526 |
c_depth(OPTION *option, char ***argvp) |
| 527 |
{ |
| 528 |
PLAN *new; |
| 529 |
char *str; |
| 530 |
|
| 531 |
new = palloc(option); |
| 532 |
|
| 533 |
str = **argvp; |
| 534 |
if (str && !(new->flags & F_DEPTH)) { |
| 535 |
/* skip leading + or - */ |
| 536 |
if (*str == '+' || *str == '-') |
| 537 |
str++; |
| 538 |
/* skip sign */ |
| 539 |
if (*str == '+' || *str == '-') |
| 540 |
str++; |
| 541 |
if (isdigit(*str)) |
| 542 |
new->flags |= F_DEPTH; |
| 543 |
} |
| 544 |
|
| 545 |
if (new->flags & F_DEPTH) { /* -depth n */ |
| 546 |
char *ndepth; |
| 547 |
|
| 548 |
ndepth = nextarg(option, argvp); |
| 549 |
new->d_data = find_parsenum(new, option->name, ndepth, NULL); |
| 550 |
} else { /* -d */ |
| 551 |
isdepth = 1; |
| 552 |
} |
| 553 |
|
| 554 |
return new; |
| 555 |
} |
| 556 |
|
| 557 |
/* |
| 558 |
* -empty functions -- |
| 559 |
* |
| 560 |
* True if the file or directory is empty |
| 561 |
*/ |
| 562 |
int |
| 563 |
f_empty(PLAN *plan __unused, FTSENT *entry) |
| 564 |
{ |
| 565 |
if (S_ISREG(entry->fts_statp->st_mode) && |
| 566 |
entry->fts_statp->st_size == 0) |
| 567 |
return 1; |
| 568 |
if (S_ISDIR(entry->fts_statp->st_mode)) { |
| 569 |
struct dirent *dp; |
| 570 |
int empty; |
| 571 |
DIR *dir; |
| 572 |
|
| 573 |
empty = 1; |
| 574 |
dir = opendir(entry->fts_accpath); |
| 575 |
if (dir == NULL) |
| 576 |
return 0; |
| 577 |
for (dp = readdir(dir); dp; dp = readdir(dir)) |
| 578 |
if (dp->d_name[0] != '.' || |
| 579 |
(dp->d_name[1] != '\0' && |
| 580 |
(dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { |
| 581 |
empty = 0; |
| 582 |
break; |
| 583 |
} |
| 584 |
closedir(dir); |
| 585 |
return empty; |
| 586 |
} |
| 587 |
return 0; |
| 588 |
} |
| 589 |
|
| 590 |
PLAN * |
| 591 |
c_empty(OPTION *option, char ***argvp __unused) |
| 592 |
{ |
| 593 |
ftsoptions &= ~FTS_NOSTAT; |
| 594 |
|
| 595 |
return palloc(option); |
| 596 |
} |
| 597 |
|
| 598 |
/* |
| 599 |
* [-exec | -execdir | -ok] utility [arg ... ] ; functions -- |
| 600 |
* |
| 601 |
* True if the executed utility returns a zero value as exit status. |
| 602 |
* The end of the primary expression is delimited by a semicolon. If |
| 603 |
* "{}" occurs anywhere, it gets replaced by the current pathname, |
| 604 |
* or, in the case of -execdir, the current basename (filename |
| 605 |
* without leading directory prefix). For -exec and -ok, |
| 606 |
* the current directory for the execution of utility is the same as |
| 607 |
* the current directory when the find utility was started, whereas |
| 608 |
* for -execdir, it is the directory the file resides in. |
| 609 |
* |
| 610 |
* The primary -ok differs from -exec in that it requests affirmation |
| 611 |
* of the user before executing the utility. |
| 612 |
*/ |
| 613 |
int |
| 614 |
f_exec(PLAN *plan, FTSENT *entry) |
| 615 |
{ |
| 616 |
int cnt; |
| 617 |
pid_t pid; |
| 618 |
int status; |
| 619 |
char *file; |
| 620 |
|
| 621 |
if (entry == NULL && plan->flags & F_EXECPLUS) { |
| 622 |
if (plan->e_ppos == plan->e_pbnum) |
| 623 |
return (1); |
| 624 |
plan->e_argv[plan->e_ppos] = NULL; |
| 625 |
goto doexec; |
| 626 |
} |
| 627 |
|
| 628 |
/* XXX - if file/dir ends in '/' this will not work -- can it? */ |
| 629 |
if ((plan->flags & F_EXECDIR) && \ |
| 630 |
(file = strrchr(entry->fts_path, '/'))) |
| 631 |
file++; |
| 632 |
else |
| 633 |
file = entry->fts_path; |
| 634 |
|
| 635 |
if (plan->flags & F_EXECPLUS) { |
| 636 |
if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) |
| 637 |
err(1, NULL); |
| 638 |
plan->e_len[plan->e_ppos] = strlen(file); |
| 639 |
plan->e_psize += plan->e_len[plan->e_ppos]; |
| 640 |
if (++plan->e_ppos < plan->e_pnummax && |
| 641 |
plan->e_psize < plan->e_psizemax) |
| 642 |
return (1); |
| 643 |
plan->e_argv[plan->e_ppos] = NULL; |
| 644 |
} else { |
| 645 |
for (cnt = 0; plan->e_argv[cnt]; ++cnt) |
| 646 |
if (plan->e_len[cnt]) |
| 647 |
brace_subst(plan->e_orig[cnt], |
| 648 |
&plan->e_argv[cnt], file, |
| 649 |
plan->e_len[cnt]); |
| 650 |
} |
| 651 |
|
| 652 |
doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) |
| 653 |
return 0; |
| 654 |
|
| 655 |
/* make sure find output is interspersed correctly with subprocesses */ |
| 656 |
fflush(stdout); |
| 657 |
fflush(stderr); |
| 658 |
|
| 659 |
switch (pid = fork()) { |
| 660 |
case -1: |
| 661 |
err(1, "fork"); |
| 662 |
/* NOTREACHED */ |
| 663 |
case 0: |
| 664 |
/* change dir back from where we started */ |
| 665 |
if (!(plan->flags & F_EXECDIR) && |
| 666 |
!(ftsoptions & FTS_NOCHDIR) && fchdir(dotfd)) { |
| 667 |
warn("chdir"); |
| 668 |
_exit(1); |
| 669 |
} |
| 670 |
execvp(plan->e_argv[0], plan->e_argv); |
| 671 |
warn("%s", plan->e_argv[0]); |
| 672 |
_exit(1); |
| 673 |
} |
| 674 |
if (plan->flags & F_EXECPLUS) { |
| 675 |
while (--plan->e_ppos >= plan->e_pbnum) |
| 676 |
free(plan->e_argv[plan->e_ppos]); |
| 677 |
plan->e_ppos = plan->e_pbnum; |
| 678 |
plan->e_psize = plan->e_pbsize; |
| 679 |
} |
| 680 |
pid = waitpid(pid, &status, 0); |
| 681 |
if (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)) |
| 682 |
return (1); |
| 683 |
if (plan->flags & F_EXECPLUS) { |
| 684 |
exitstatus = 1; |
| 685 |
return (1); |
| 686 |
} |
| 687 |
return (0); |
| 688 |
} |
| 689 |
|
| 690 |
/* |
| 691 |
* c_exec, c_execdir, c_ok -- |
| 692 |
* build three parallel arrays, one with pointers to the strings passed |
| 693 |
* on the command line, one with (possibly duplicated) pointers to the |
| 694 |
* argv array, and one with integer values that are lengths of the |
| 695 |
* strings, but also flags meaning that the string has to be massaged. |
| 696 |
*/ |
| 697 |
PLAN * |
| 698 |
c_exec(OPTION *option, char ***argvp) |
| 699 |
{ |
| 700 |
PLAN *new; /* node returned */ |
| 701 |
long argmax; |
| 702 |
int cnt, i; |
| 703 |
char **argv, **ap, **ep, *p; |
| 704 |
|
| 705 |
/* This would defeat -execdir's intended security. */ |
| 706 |
if (option->flags & F_EXECDIR && ftsoptions & FTS_NOCHDIR) |
| 707 |
errx(1, "%s: forbidden when the current directory cannot be opened", |
| 708 |
"-execdir"); |
| 709 |
|
| 710 |
/* XXX - was in c_execdir, but seems unnecessary!? |
| 711 |
ftsoptions &= ~FTS_NOSTAT; |
| 712 |
*/ |
| 713 |
isoutput = 1; |
| 714 |
|
| 715 |
/* XXX - this is a change from the previous coding */ |
| 716 |
new = palloc(option); |
| 717 |
|
| 718 |
for (ap = argv = *argvp;; ++ap) { |
| 719 |
if (!*ap) |
| 720 |
errx(1, |
| 721 |
"%s: no terminating \";\" or \"+\"", option->name); |
| 722 |
if (**ap == ';') |
| 723 |
break; |
| 724 |
if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { |
| 725 |
new->flags |= F_EXECPLUS; |
| 726 |
break; |
| 727 |
} |
| 728 |
} |
| 729 |
|
| 730 |
if (ap == argv) |
| 731 |
errx(1, "%s: no command specified", option->name); |
| 732 |
|
| 733 |
cnt = ap - *argvp + 1; |
| 734 |
if (new->flags & F_EXECPLUS) { |
| 735 |
new->e_ppos = new->e_pbnum = cnt - 2; |
| 736 |
if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { |
| 737 |
warn("sysconf(_SC_ARG_MAX)"); |
| 738 |
argmax = _POSIX_ARG_MAX; |
| 739 |
} |
| 740 |
argmax -= 1024; |
| 741 |
for (ep = environ; *ep != NULL; ep++) |
| 742 |
argmax -= strlen(*ep) + 1 + sizeof(*ep); |
| 743 |
argmax -= 1 + sizeof(*ep); |
| 744 |
/* |
| 745 |
* Ensure that -execdir ... {} + does not mix files |
| 746 |
* from different directories in one invocation. |
| 747 |
* Files from the same directory should be handled |
| 748 |
* in one invocation but there is no code for it. |
| 749 |
*/ |
| 750 |
new->e_pnummax = new->flags & F_EXECDIR ? 1 : argmax / 16; |
| 751 |
argmax -= sizeof(char *) * new->e_pnummax; |
| 752 |
if (argmax <= 0) |
| 753 |
errx(1, "no space for arguments"); |
| 754 |
new->e_psizemax = argmax; |
| 755 |
new->e_pbsize = 0; |
| 756 |
cnt += new->e_pnummax + 1; |
| 757 |
new->e_next = lastexecplus; |
| 758 |
lastexecplus = new; |
| 759 |
} |
| 760 |
if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) |
| 761 |
err(1, NULL); |
| 762 |
if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) |
| 763 |
err(1, NULL); |
| 764 |
if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) |
| 765 |
err(1, NULL); |
| 766 |
|
| 767 |
for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { |
| 768 |
new->e_orig[cnt] = *argv; |
| 769 |
if (new->flags & F_EXECPLUS) |
| 770 |
new->e_pbsize += strlen(*argv) + 1; |
| 771 |
for (p = *argv; *p; ++p) |
| 772 |
if (!(new->flags & F_EXECPLUS) && p[0] == '{' && |
| 773 |
p[1] == '}') { |
| 774 |
if ((new->e_argv[cnt] = |
| 775 |
malloc(MAXPATHLEN)) == NULL) |
| 776 |
err(1, NULL); |
| 777 |
new->e_len[cnt] = MAXPATHLEN; |
| 778 |
break; |
| 779 |
} |
| 780 |
if (!*p) { |
| 781 |
new->e_argv[cnt] = *argv; |
| 782 |
new->e_len[cnt] = 0; |
| 783 |
} |
| 784 |
} |
| 785 |
if (new->flags & F_EXECPLUS) { |
| 786 |
new->e_psize = new->e_pbsize; |
| 787 |
cnt--; |
| 788 |
for (i = 0; i < new->e_pnummax; i++) { |
| 789 |
new->e_argv[cnt] = NULL; |
| 790 |
new->e_len[cnt] = 0; |
| 791 |
cnt++; |
| 792 |
} |
| 793 |
argv = ap; |
| 794 |
goto done; |
| 795 |
} |
| 796 |
new->e_argv[cnt] = new->e_orig[cnt] = NULL; |
| 797 |
|
| 798 |
done: *argvp = argv + 1; |
| 799 |
return new; |
| 800 |
} |
| 801 |
|
| 802 |
/* Finish any pending -exec ... {} + functions. */ |
| 803 |
void |
| 804 |
finish_execplus(void) |
| 805 |
{ |
| 806 |
PLAN *p; |
| 807 |
|
| 808 |
p = lastexecplus; |
| 809 |
while (p != NULL) { |
| 810 |
(p->execute)(p, NULL); |
| 811 |
p = p->e_next; |
| 812 |
} |
| 813 |
} |
| 814 |
|
| 815 |
#if HAVE_STRUCT_STAT_ST_FLAGS |
| 816 |
int |
| 817 |
f_flags(PLAN *plan, FTSENT *entry) |
| 818 |
{ |
| 819 |
u_long flags; |
| 820 |
|
| 821 |
flags = entry->fts_statp->st_flags; |
| 822 |
if (plan->flags & F_ATLEAST) |
| 823 |
return (flags | plan->fl_flags) == flags && |
| 824 |
!(flags & plan->fl_notflags); |
| 825 |
else if (plan->flags & F_ANY) |
| 826 |
return (flags & plan->fl_flags) || |
| 827 |
(flags | plan->fl_notflags) != flags; |
| 828 |
else |
| 829 |
return flags == plan->fl_flags && |
| 830 |
!(plan->fl_flags & plan->fl_notflags); |
| 831 |
} |
| 832 |
|
| 833 |
PLAN * |
| 834 |
c_flags(OPTION *option, char ***argvp) |
| 835 |
{ |
| 836 |
char *flags_str; |
| 837 |
PLAN *new; |
| 838 |
u_long flags, notflags; |
| 839 |
|
| 840 |
flags_str = nextarg(option, argvp); |
| 841 |
ftsoptions &= ~FTS_NOSTAT; |
| 842 |
|
| 843 |
new = palloc(option); |
| 844 |
|
| 845 |
if (*flags_str == '-') { |
| 846 |
new->flags |= F_ATLEAST; |
| 847 |
flags_str++; |
| 848 |
} else if (*flags_str == '+') { |
| 849 |
new->flags |= F_ANY; |
| 850 |
flags_str++; |
| 851 |
} |
| 852 |
if (strtofflags(&flags_str, &flags, ¬flags) == 1) |
| 853 |
errx(1, "%s: %s: illegal flags string", option->name, flags_str); |
| 854 |
|
| 855 |
new->fl_flags = flags; |
| 856 |
new->fl_notflags = notflags; |
| 857 |
return new; |
| 858 |
} |
| 859 |
#endif |
| 860 |
|
| 861 |
/* |
| 862 |
* -follow functions -- |
| 863 |
* |
| 864 |
* Always true, causes symbolic links to be followed on a global |
| 865 |
* basis. |
| 866 |
*/ |
| 867 |
PLAN * |
| 868 |
c_follow(OPTION *option, char ***argvp __unused) |
| 869 |
{ |
| 870 |
ftsoptions &= ~FTS_PHYSICAL; |
| 871 |
ftsoptions |= FTS_LOGICAL; |
| 872 |
|
| 873 |
return palloc(option); |
| 874 |
} |
| 875 |
|
| 876 |
#if HAVE_STRUCT_STATFS_F_FSTYPENAME |
| 877 |
/* |
| 878 |
* -fstype functions -- |
| 879 |
* |
| 880 |
* True if the file is of a certain type. |
| 881 |
*/ |
| 882 |
int |
| 883 |
f_fstype(PLAN *plan, FTSENT *entry) |
| 884 |
{ |
| 885 |
static dev_t curdev; /* need a guaranteed illegal dev value */ |
| 886 |
static int first = 1; |
| 887 |
struct statfs sb; |
| 888 |
static int val_flags; |
| 889 |
static char fstype[sizeof(sb.f_fstypename)]; |
| 890 |
char *p, save[2] = {0,0}; |
| 891 |
|
| 892 |
if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) |
| 893 |
return 0; |
| 894 |
|
| 895 |
/* Only check when we cross mount point. */ |
| 896 |
if (first || curdev != entry->fts_statp->st_dev) { |
| 897 |
curdev = entry->fts_statp->st_dev; |
| 898 |
|
| 899 |
/* |
| 900 |
* Statfs follows symlinks; find wants the link's filesystem, |
| 901 |
* not where it points. |
| 902 |
*/ |
| 903 |
if (entry->fts_info == FTS_SL || |
| 904 |
entry->fts_info == FTS_SLNONE) { |
| 905 |
if ((p = strrchr(entry->fts_accpath, '/')) != NULL) |
| 906 |
++p; |
| 907 |
else |
| 908 |
p = entry->fts_accpath; |
| 909 |
save[0] = p[0]; |
| 910 |
p[0] = '.'; |
| 911 |
save[1] = p[1]; |
| 912 |
p[1] = '\0'; |
| 913 |
} else |
| 914 |
p = NULL; |
| 915 |
|
| 916 |
if (statfs(entry->fts_accpath, &sb)) { |
| 917 |
if (!ignore_readdir_race || errno != ENOENT) { |
| 918 |
warn("statfs: %s", entry->fts_accpath); |
| 919 |
exitstatus = 1; |
| 920 |
} |
| 921 |
return 0; |
| 922 |
} |
| 923 |
|
| 924 |
if (p) { |
| 925 |
p[0] = save[0]; |
| 926 |
p[1] = save[1]; |
| 927 |
} |
| 928 |
|
| 929 |
first = 0; |
| 930 |
|
| 931 |
/* |
| 932 |
* Further tests may need both of these values, so |
| 933 |
* always copy both of them. |
| 934 |
*/ |
| 935 |
val_flags = sb.f_flags; |
| 936 |
strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); |
| 937 |
} |
| 938 |
switch (plan->flags & F_MTMASK) { |
| 939 |
case F_MTFLAG: |
| 940 |
return val_flags & plan->mt_data; |
| 941 |
case F_MTTYPE: |
| 942 |
return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); |
| 943 |
default: |
| 944 |
abort(); |
| 945 |
} |
| 946 |
} |
| 947 |
|
| 948 |
PLAN * |
| 949 |
c_fstype(OPTION *option, char ***argvp) |
| 950 |
{ |
| 951 |
char *fsname; |
| 952 |
PLAN *new; |
| 953 |
|
| 954 |
fsname = nextarg(option, argvp); |
| 955 |
ftsoptions &= ~FTS_NOSTAT; |
| 956 |
|
| 957 |
new = palloc(option); |
| 958 |
switch (*fsname) { |
| 959 |
case 'l': |
| 960 |
if (!strcmp(fsname, "local")) { |
| 961 |
new->flags |= F_MTFLAG; |
| 962 |
new->mt_data = MNT_LOCAL; |
| 963 |
return new; |
| 964 |
} |
| 965 |
break; |
| 966 |
case 'r': |
| 967 |
if (!strcmp(fsname, "rdonly")) { |
| 968 |
new->flags |= F_MTFLAG; |
| 969 |
new->mt_data = MNT_RDONLY; |
| 970 |
return new; |
| 971 |
} |
| 972 |
break; |
| 973 |
} |
| 974 |
|
| 975 |
new->flags |= F_MTTYPE; |
| 976 |
new->c_data = fsname; |
| 977 |
return new; |
| 978 |
} |
| 979 |
#endif |
| 980 |
|
| 981 |
/* |
| 982 |
* -group gname functions -- |
| 983 |
* |
| 984 |
* True if the file belongs to the group gname. If gname is numeric and |
| 985 |
* an equivalent of the getgrnam() function does not return a valid group |
| 986 |
* name, gname is taken as a group ID. |
| 987 |
*/ |
| 988 |
int |
| 989 |
f_group(PLAN *plan, FTSENT *entry) |
| 990 |
{ |
| 991 |
COMPARE(entry->fts_statp->st_gid, plan->g_data); |
| 992 |
} |
| 993 |
|
| 994 |
PLAN * |
| 995 |
c_group(OPTION *option, char ***argvp) |
| 996 |
{ |
| 997 |
char *gname; |
| 998 |
PLAN *new; |
| 999 |
struct group *g; |
| 1000 |
gid_t gid; |
| 1001 |
|
| 1002 |
gname = nextarg(option, argvp); |
| 1003 |
ftsoptions &= ~FTS_NOSTAT; |
| 1004 |
|
| 1005 |
new = palloc(option); |
| 1006 |
g = getgrnam(gname); |
| 1007 |
if (g == NULL) { |
| 1008 |
char* cp = gname; |
| 1009 |
if (gname[0] == '-' || gname[0] == '+') |
| 1010 |
gname++; |
| 1011 |
gid = atoi(gname); |
| 1012 |
if (gid == 0 && gname[0] != '0') |
| 1013 |
errx(1, "%s: %s: no such group", option->name, gname); |
| 1014 |
gid = find_parsenum(new, option->name, cp, NULL); |
| 1015 |
} else |
| 1016 |
gid = g->gr_gid; |
| 1017 |
|
| 1018 |
new->g_data = gid; |
| 1019 |
return new; |
| 1020 |
} |
| 1021 |
|
| 1022 |
/* |
| 1023 |
* -ignore_readdir_race functions -- |
| 1024 |
* |
| 1025 |
* Always true. Ignore errors which occur if a file or a directory |
| 1026 |
* in a starting point gets deleted between reading the name and calling |
| 1027 |
* stat on it while find is traversing the starting point. |
| 1028 |
*/ |
| 1029 |
|
| 1030 |
PLAN * |
| 1031 |
c_ignore_readdir_race(OPTION *option, char ***argvp __unused) |
| 1032 |
{ |
| 1033 |
if (strcmp(option->name, "-ignore_readdir_race") == 0) |
| 1034 |
ignore_readdir_race = 1; |
| 1035 |
else |
| 1036 |
ignore_readdir_race = 0; |
| 1037 |
|
| 1038 |
return palloc(option); |
| 1039 |
} |
| 1040 |
|
| 1041 |
/* |
| 1042 |
* -inum n functions -- |
| 1043 |
* |
| 1044 |
* True if the file has inode # n. |
| 1045 |
*/ |
| 1046 |
int |
| 1047 |
f_inum(PLAN *plan, FTSENT *entry) |
| 1048 |
{ |
| 1049 |
COMPARE(entry->fts_statp->st_ino, plan->i_data); |
| 1050 |
} |
| 1051 |
|
| 1052 |
PLAN * |
| 1053 |
c_inum(OPTION *option, char ***argvp) |
| 1054 |
{ |
| 1055 |
char *inum_str; |
| 1056 |
PLAN *new; |
| 1057 |
|
| 1058 |
inum_str = nextarg(option, argvp); |
| 1059 |
ftsoptions &= ~FTS_NOSTAT; |
| 1060 |
|
| 1061 |
new = palloc(option); |
| 1062 |
new->i_data = find_parsenum(new, option->name, inum_str, NULL); |
| 1063 |
return new; |
| 1064 |
} |
| 1065 |
|
| 1066 |
/* |
| 1067 |
* -samefile FN |
| 1068 |
* |
| 1069 |
* True if the file has the same inode (eg hard link) FN |
| 1070 |
*/ |
| 1071 |
|
| 1072 |
/* f_samefile is just f_inum */ |
| 1073 |
PLAN * |
| 1074 |
c_samefile(OPTION *option, char ***argvp) |
| 1075 |
{ |
| 1076 |
char *fn; |
| 1077 |
PLAN *new; |
| 1078 |
struct stat sb; |
| 1079 |
int error; |
| 1080 |
|
| 1081 |
fn = nextarg(option, argvp); |
| 1082 |
ftsoptions &= ~FTS_NOSTAT; |
| 1083 |
|
| 1084 |
new = palloc(option); |
| 1085 |
if (ftsoptions & FTS_PHYSICAL) |
| 1086 |
error = lstat(fn, &sb); |
| 1087 |
else |
| 1088 |
error = stat(fn, &sb); |
| 1089 |
if (error != 0) |
| 1090 |
err(1, "%s", fn); |
| 1091 |
new->i_data = sb.st_ino; |
| 1092 |
return new; |
| 1093 |
} |
| 1094 |
|
| 1095 |
/* |
| 1096 |
* -links n functions -- |
| 1097 |
* |
| 1098 |
* True if the file has n links. |
| 1099 |
*/ |
| 1100 |
int |
| 1101 |
f_links(PLAN *plan, FTSENT *entry) |
| 1102 |
{ |
| 1103 |
COMPARE(entry->fts_statp->st_nlink, plan->l_data); |
| 1104 |
} |
| 1105 |
|
| 1106 |
PLAN * |
| 1107 |
c_links(OPTION *option, char ***argvp) |
| 1108 |
{ |
| 1109 |
char *nlinks; |
| 1110 |
PLAN *new; |
| 1111 |
|
| 1112 |
nlinks = nextarg(option, argvp); |
| 1113 |
ftsoptions &= ~FTS_NOSTAT; |
| 1114 |
|
| 1115 |
new = palloc(option); |
| 1116 |
new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); |
| 1117 |
return new; |
| 1118 |
} |
| 1119 |
|
| 1120 |
/* |
| 1121 |
* -ls functions -- |
| 1122 |
* |
| 1123 |
* Always true - prints the current entry to stdout in "ls" format. |
| 1124 |
*/ |
| 1125 |
int |
| 1126 |
f_ls(PLAN *plan __unused, FTSENT *entry) |
| 1127 |
{ |
| 1128 |
printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); |
| 1129 |
return 1; |
| 1130 |
} |
| 1131 |
|
| 1132 |
PLAN * |
| 1133 |
c_ls(OPTION *option, char ***argvp __unused) |
| 1134 |
{ |
| 1135 |
ftsoptions &= ~FTS_NOSTAT; |
| 1136 |
isoutput = 1; |
| 1137 |
|
| 1138 |
return palloc(option); |
| 1139 |
} |
| 1140 |
|
| 1141 |
/* |
| 1142 |
* -name functions -- |
| 1143 |
* |
| 1144 |
* True if the basename of the filename being examined |
| 1145 |
* matches pattern using Pattern Matching Notation S3.14 |
| 1146 |
*/ |
| 1147 |
int |
| 1148 |
f_name(PLAN *plan, FTSENT *entry) |
| 1149 |
{ |
| 1150 |
char fn[PATH_MAX]; |
| 1151 |
const char *name; |
| 1152 |
ssize_t len; |
| 1153 |
|
| 1154 |
if (plan->flags & F_LINK) { |
| 1155 |
/* |
| 1156 |
* The below test both avoids obviously useless readlink() |
| 1157 |
* calls and ensures that symlinks with existent target do |
| 1158 |
* not match if symlinks are being followed. |
| 1159 |
* Assumption: fts will stat all symlinks that are to be |
| 1160 |
* followed and will return the stat information. |
| 1161 |
*/ |
| 1162 |
if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL && |
| 1163 |
entry->fts_info != FTS_SLNONE) |
| 1164 |
return 0; |
| 1165 |
len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1); |
| 1166 |
if (len == -1) |
| 1167 |
return 0; |
| 1168 |
fn[len] = '\0'; |
| 1169 |
name = fn; |
| 1170 |
} else |
| 1171 |
name = entry->fts_name; |
| 1172 |
return !fnmatch(plan->c_data, name, |
| 1173 |
plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); |
| 1174 |
} |
| 1175 |
|
| 1176 |
PLAN * |
| 1177 |
c_name(OPTION *option, char ***argvp) |
| 1178 |
{ |
| 1179 |
char *pattern; |
| 1180 |
PLAN *new; |
| 1181 |
|
| 1182 |
pattern = nextarg(option, argvp); |
| 1183 |
new = palloc(option); |
| 1184 |
new->c_data = pattern; |
| 1185 |
return new; |
| 1186 |
} |
| 1187 |
|
| 1188 |
/* |
| 1189 |
* -newer file functions -- |
| 1190 |
* |
| 1191 |
* True if the current file has been modified more recently |
| 1192 |
* then the modification time of the file named by the pathname |
| 1193 |
* file. |
| 1194 |
*/ |
| 1195 |
int |
| 1196 |
f_newer(PLAN *plan, FTSENT *entry) |
| 1197 |
{ |
| 1198 |
struct timespec ft; |
| 1199 |
|
| 1200 |
if (plan->flags & F_TIME_C) |
| 1201 |
ft = entry->fts_statp->st_ctim; |
| 1202 |
#if HAVE_STRUCT_STAT_ST_BIRTHTIME |
| 1203 |
else if (plan->flags & F_TIME_A) |
| 1204 |
ft = entry->fts_statp->st_atim; |
| 1205 |
else if (plan->flags & F_TIME_B) |
| 1206 |
ft = entry->fts_statp->st_birthtim; |
| 1207 |
#endif |
| 1208 |
else |
| 1209 |
ft = entry->fts_statp->st_mtim; |
| 1210 |
return (ft.tv_sec > plan->t_data.tv_sec || |
| 1211 |
(ft.tv_sec == plan->t_data.tv_sec && |
| 1212 |
ft.tv_nsec > plan->t_data.tv_nsec)); |
| 1213 |
} |
| 1214 |
|
| 1215 |
PLAN * |
| 1216 |
c_newer(OPTION *option, char ***argvp) |
| 1217 |
{ |
| 1218 |
char *fn_or_tspec; |
| 1219 |
PLAN *new; |
| 1220 |
struct stat sb; |
| 1221 |
int error; |
| 1222 |
|
| 1223 |
fn_or_tspec = nextarg(option, argvp); |
| 1224 |
ftsoptions &= ~FTS_NOSTAT; |
| 1225 |
|
| 1226 |
new = palloc(option); |
| 1227 |
/* compare against what */ |
| 1228 |
if (option->flags & F_TIME2_T) { |
| 1229 |
new->t_data.tv_sec = get_date(fn_or_tspec); |
| 1230 |
if (new->t_data.tv_sec == (time_t) -1) |
| 1231 |
errx(1, "Can't parse date/time: %s", fn_or_tspec); |
| 1232 |
/* Use the seconds only in the comparison. */ |
| 1233 |
new->t_data.tv_nsec = 999999999; |
| 1234 |
} else { |
| 1235 |
if (ftsoptions & FTS_PHYSICAL) |
| 1236 |
error = lstat(fn_or_tspec, &sb); |
| 1237 |
else |
| 1238 |
error = stat(fn_or_tspec, &sb); |
| 1239 |
if (error != 0) |
| 1240 |
err(1, "%s", fn_or_tspec); |
| 1241 |
if (option->flags & F_TIME2_C) |
| 1242 |
new->t_data = sb.st_ctim; |
| 1243 |
else if (option->flags & F_TIME2_A) |
| 1244 |
new->t_data = sb.st_atim; |
| 1245 |
#if HAVE_STRUCT_STAT_ST_BIRTHTIME |
| 1246 |
else if (option->flags & F_TIME2_B) |
| 1247 |
new->t_data = sb.st_birthtim; |
| 1248 |
#endif |
| 1249 |
else |
| 1250 |
new->t_data = sb.st_mtim; |
| 1251 |
} |
| 1252 |
return new; |
| 1253 |
} |
| 1254 |
|
| 1255 |
/* |
| 1256 |
* -nogroup functions -- |
| 1257 |
* |
| 1258 |
* True if file belongs to a user ID for which the equivalent |
| 1259 |
* of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. |
| 1260 |
*/ |
| 1261 |
int |
| 1262 |
f_nogroup(PLAN *plan __unused, FTSENT *entry) |
| 1263 |
{ |
| 1264 |
return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; |
| 1265 |
} |
| 1266 |
|
| 1267 |
PLAN * |
| 1268 |
c_nogroup(OPTION *option, char ***argvp __unused) |
| 1269 |
{ |
| 1270 |
ftsoptions &= ~FTS_NOSTAT; |
| 1271 |
|
| 1272 |
return palloc(option); |
| 1273 |
} |
| 1274 |
|
| 1275 |
/* |
| 1276 |
* -nouser functions -- |
| 1277 |
* |
| 1278 |
* True if file belongs to a user ID for which the equivalent |
| 1279 |
* of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. |
| 1280 |
*/ |
| 1281 |
int |
| 1282 |
f_nouser(PLAN *plan __unused, FTSENT *entry) |
| 1283 |
{ |
| 1284 |
return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; |
| 1285 |
} |
| 1286 |
|
| 1287 |
PLAN * |
| 1288 |
c_nouser(OPTION *option, char ***argvp __unused) |
| 1289 |
{ |
| 1290 |
ftsoptions &= ~FTS_NOSTAT; |
| 1291 |
|
| 1292 |
return palloc(option); |
| 1293 |
} |
| 1294 |
|
| 1295 |
/* |
| 1296 |
* -path functions -- |
| 1297 |
* |
| 1298 |
* True if the path of the filename being examined |
| 1299 |
* matches pattern using Pattern Matching Notation S3.14 |
| 1300 |
*/ |
| 1301 |
int |
| 1302 |
f_path(PLAN *plan, FTSENT *entry) |
| 1303 |
{ |
| 1304 |
return !fnmatch(plan->c_data, entry->fts_path, |
| 1305 |
plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); |
| 1306 |
} |
| 1307 |
|
| 1308 |
/* c_path is the same as c_name */ |
| 1309 |
|
| 1310 |
/* |
| 1311 |
* -perm functions -- |
| 1312 |
* |
| 1313 |
* The mode argument is used to represent file mode bits. If it starts |
| 1314 |
* with a leading digit, it's treated as an octal mode, otherwise as a |
| 1315 |
* symbolic mode. |
| 1316 |
*/ |
| 1317 |
int |
| 1318 |
f_perm(PLAN *plan, FTSENT *entry) |
| 1319 |
{ |
| 1320 |
mode_t mode; |
| 1321 |
|
| 1322 |
mode = entry->fts_statp->st_mode & |
| 1323 |
(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); |
| 1324 |
if (plan->flags & F_ATLEAST) |
| 1325 |
return (plan->m_data | mode) == mode; |
| 1326 |
else if (plan->flags & F_ANY) |
| 1327 |
return (mode & plan->m_data); |
| 1328 |
else |
| 1329 |
return mode == plan->m_data; |
| 1330 |
/* NOTREACHED */ |
| 1331 |
} |
| 1332 |
|
| 1333 |
PLAN * |
| 1334 |
c_perm(OPTION *option, char ***argvp) |
| 1335 |
{ |
| 1336 |
char *perm; |
| 1337 |
PLAN *new; |
| 1338 |
mode_t *set; |
| 1339 |
|
| 1340 |
perm = nextarg(option, argvp); |
| 1341 |
ftsoptions &= ~FTS_NOSTAT; |
| 1342 |
|
| 1343 |
new = palloc(option); |
| 1344 |
|
| 1345 |
if (*perm == '-') { |
| 1346 |
new->flags |= F_ATLEAST; |
| 1347 |
++perm; |
| 1348 |
} else if (*perm == '+') { |
| 1349 |
new->flags |= F_ANY; |
| 1350 |
++perm; |
| 1351 |
} |
| 1352 |
|
| 1353 |
if ((set = setmode(perm)) == NULL) |
| 1354 |
errx(1, "%s: %s: illegal mode string", option->name, perm); |
| 1355 |
|
| 1356 |
new->m_data = getmode(set, 0); |
| 1357 |
free(set); |
| 1358 |
return new; |
| 1359 |
} |
| 1360 |
|
| 1361 |
/* |
| 1362 |
* -print functions -- |
| 1363 |
* |
| 1364 |
* Always true, causes the current pathname to be written to |
| 1365 |
* standard output. |
| 1366 |
*/ |
| 1367 |
int |
| 1368 |
f_print(PLAN *plan __unused, FTSENT *entry) |
| 1369 |
{ |
| 1370 |
(void)puts(entry->fts_path); |
| 1371 |
return 1; |
| 1372 |
} |
| 1373 |
|
| 1374 |
PLAN * |
| 1375 |
c_print(OPTION *option, char ***argvp __unused) |
| 1376 |
{ |
| 1377 |
isoutput = 1; |
| 1378 |
|
| 1379 |
return palloc(option); |
| 1380 |
} |
| 1381 |
|
| 1382 |
/* |
| 1383 |
* -print0 functions -- |
| 1384 |
* |
| 1385 |
* Always true, causes the current pathname to be written to |
| 1386 |
* standard output followed by a NUL character |
| 1387 |
*/ |
| 1388 |
int |
| 1389 |
f_print0(PLAN *plan __unused, FTSENT *entry) |
| 1390 |
{ |
| 1391 |
fputs(entry->fts_path, stdout); |
| 1392 |
fputc('\0', stdout); |
| 1393 |
return 1; |
| 1394 |
} |
| 1395 |
|
| 1396 |
/* c_print0 is the same as c_print */ |
| 1397 |
|
| 1398 |
/* |
| 1399 |
* -prune functions -- |
| 1400 |
* |
| 1401 |
* Prune a portion of the hierarchy. |
| 1402 |
*/ |
| 1403 |
int |
| 1404 |
f_prune(PLAN *plan __unused, FTSENT *entry) |
| 1405 |
{ |
| 1406 |
if (fts_set(tree, entry, FTS_SKIP)) |
| 1407 |
err(1, "%s", entry->fts_path); |
| 1408 |
return 1; |
| 1409 |
} |
| 1410 |
|
| 1411 |
/* c_prune == c_simple */ |
| 1412 |
|
| 1413 |
/* |
| 1414 |
* -regex functions -- |
| 1415 |
* |
| 1416 |
* True if the whole path of the file matches pattern using |
| 1417 |
* regular expression. |
| 1418 |
*/ |
| 1419 |
int |
| 1420 |
f_regex(PLAN *plan, FTSENT *entry) |
| 1421 |
{ |
| 1422 |
char *str; |
| 1423 |
int len; |
| 1424 |
regex_t *pre; |
| 1425 |
regmatch_t pmatch; |
| 1426 |
int errcode; |
| 1427 |
char errbuf[LINE_MAX]; |
| 1428 |
int matched; |
| 1429 |
|
| 1430 |
pre = plan->re_data; |
| 1431 |
str = entry->fts_path; |
| 1432 |
len = strlen(str); |
| 1433 |
matched = 0; |
| 1434 |
|
| 1435 |
pmatch.rm_so = 0; |
| 1436 |
pmatch.rm_eo = len; |
| 1437 |
|
| 1438 |
errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); |
| 1439 |
|
| 1440 |
if (errcode != 0 && errcode != REG_NOMATCH) { |
| 1441 |
regerror(errcode, pre, errbuf, sizeof errbuf); |
| 1442 |
errx(1, "%s: %s", |
| 1443 |
plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); |
| 1444 |
} |
| 1445 |
|
| 1446 |
if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) |
| 1447 |
matched = 1; |
| 1448 |
|
| 1449 |
return matched; |
| 1450 |
} |
| 1451 |
|
| 1452 |
PLAN * |
| 1453 |
c_regex(OPTION *option, char ***argvp) |
| 1454 |
{ |
| 1455 |
PLAN *new; |
| 1456 |
char *pattern; |
| 1457 |
regex_t *pre; |
| 1458 |
int errcode; |
| 1459 |
char errbuf[LINE_MAX]; |
| 1460 |
|
| 1461 |
if ((pre = malloc(sizeof(regex_t))) == NULL) |
| 1462 |
err(1, NULL); |
| 1463 |
|
| 1464 |
pattern = nextarg(option, argvp); |
| 1465 |
|
| 1466 |
if ((errcode = regcomp(pre, pattern, |
| 1467 |
regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { |
| 1468 |
regerror(errcode, pre, errbuf, sizeof errbuf); |
| 1469 |
errx(1, "%s: %s: %s", |
| 1470 |
option->flags & F_IGNCASE ? "-iregex" : "-regex", |
| 1471 |
pattern, errbuf); |
| 1472 |
} |
| 1473 |
|
| 1474 |
new = palloc(option); |
| 1475 |
new->re_data = pre; |
| 1476 |
|
| 1477 |
return new; |
| 1478 |
} |
| 1479 |
|
| 1480 |
/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ |
| 1481 |
|
| 1482 |
PLAN * |
| 1483 |
c_simple(OPTION *option, char ***argvp __unused) |
| 1484 |
{ |
| 1485 |
return palloc(option); |
| 1486 |
} |
| 1487 |
|
| 1488 |
/* |
| 1489 |
* -size n[c] functions -- |
| 1490 |
* |
| 1491 |
* True if the file size in bytes, divided by an implementation defined |
| 1492 |
* value and rounded up to the next integer, is n. If n is followed by |
| 1493 |
* one of c k M G T P, the size is in bytes, kilobytes, |
| 1494 |
* megabytes, gigabytes, terabytes or petabytes respectively. |
| 1495 |
*/ |
| 1496 |
#define FIND_SIZE 512 |
| 1497 |
static int divsize = 1; |
| 1498 |
|
| 1499 |
int |
| 1500 |
f_size(PLAN *plan, FTSENT *entry) |
| 1501 |
{ |
| 1502 |
off_t size; |
| 1503 |
|
| 1504 |
size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / |
| 1505 |
FIND_SIZE : entry->fts_statp->st_size; |
| 1506 |
COMPARE(size, plan->o_data); |
| 1507 |
} |
| 1508 |
|
| 1509 |
PLAN * |
| 1510 |
c_size(OPTION *option, char ***argvp) |
| 1511 |
{ |
| 1512 |
char *size_str; |
| 1513 |
PLAN *new; |
| 1514 |
char endch; |
| 1515 |
off_t scale; |
| 1516 |
|
| 1517 |
size_str = nextarg(option, argvp); |
| 1518 |
ftsoptions &= ~FTS_NOSTAT; |
| 1519 |
|
| 1520 |
new = palloc(option); |
| 1521 |
endch = 'c'; |
| 1522 |
new->o_data = find_parsenum(new, option->name, size_str, &endch); |
| 1523 |
if (endch != '\0') { |
| 1524 |
divsize = 0; |
| 1525 |
|
| 1526 |
switch (endch) { |
| 1527 |
case 'c': /* characters */ |
| 1528 |
scale = 0x1LL; |
| 1529 |
break; |
| 1530 |
case 'k': /* kilobytes 1<<10 */ |
| 1531 |
scale = 0x400LL; |
| 1532 |
break; |
| 1533 |
case 'M': /* megabytes 1<<20 */ |
| 1534 |
scale = 0x100000LL; |
| 1535 |
break; |
| 1536 |
case 'G': /* gigabytes 1<<30 */ |
| 1537 |
scale = 0x40000000LL; |
| 1538 |
break; |
| 1539 |
case 'T': /* terabytes 1<<40 */ |
| 1540 |
scale = 0x10000000000LL; |
| 1541 |
break; |
| 1542 |
case 'P': /* petabytes 1<<50 */ |
| 1543 |
scale = 0x4000000000000LL; |
| 1544 |
break; |
| 1545 |
default: |
| 1546 |
errx(1, "%s: %s: illegal trailing character", |
| 1547 |
option->name, size_str); |
| 1548 |
break; |
| 1549 |
} |
| 1550 |
if (new->o_data > QUAD_MAX / scale) |
| 1551 |
errx(1, "%s: %s: value too large", |
| 1552 |
option->name, size_str); |
| 1553 |
new->o_data *= scale; |
| 1554 |
} |
| 1555 |
return new; |
| 1556 |
} |
| 1557 |
|
| 1558 |
/* |
| 1559 |
* -sparse functions -- |
| 1560 |
* |
| 1561 |
* Check if a file is sparse by finding if it occupies fewer blocks |
| 1562 |
* than we expect based on its size. |
| 1563 |
*/ |
| 1564 |
int |
| 1565 |
f_sparse(PLAN *plan __unused, FTSENT *entry) |
| 1566 |
{ |
| 1567 |
off_t expected_blocks; |
| 1568 |
|
| 1569 |
expected_blocks = (entry->fts_statp->st_size + 511) / 512; |
| 1570 |
return entry->fts_statp->st_blocks < expected_blocks; |
| 1571 |
} |
| 1572 |
|
| 1573 |
PLAN * |
| 1574 |
c_sparse(OPTION *option, char ***argvp __unused) |
| 1575 |
{ |
| 1576 |
ftsoptions &= ~FTS_NOSTAT; |
| 1577 |
|
| 1578 |
return palloc(option); |
| 1579 |
} |
| 1580 |
|
| 1581 |
/* |
| 1582 |
* -type c functions -- |
| 1583 |
* |
| 1584 |
* True if the type of the file is c, where c is b, c, d, p, f or w |
| 1585 |
* for block special file, character special file, directory, FIFO, |
| 1586 |
* regular file or whiteout respectively. |
| 1587 |
*/ |
| 1588 |
int |
| 1589 |
f_type(PLAN *plan, FTSENT *entry) |
| 1590 |
{ |
| 1591 |
if (plan->m_data == S_IFDIR) |
| 1592 |
return (entry->fts_info == FTS_D || entry->fts_info == FTS_DC || |
| 1593 |
entry->fts_info == FTS_DNR || entry->fts_info == FTS_DOT || |
| 1594 |
entry->fts_info == FTS_DP); |
| 1595 |
else |
| 1596 |
return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; |
| 1597 |
} |
| 1598 |
|
| 1599 |
PLAN * |
| 1600 |
c_type(OPTION *option, char ***argvp) |
| 1601 |
{ |
| 1602 |
char *typestring; |
| 1603 |
PLAN *new; |
| 1604 |
mode_t mask; |
| 1605 |
|
| 1606 |
typestring = nextarg(option, argvp); |
| 1607 |
if (typestring[0] != 'd') |
| 1608 |
ftsoptions &= ~FTS_NOSTAT; |
| 1609 |
|
| 1610 |
switch (typestring[0]) { |
| 1611 |
case 'b': |
| 1612 |
mask = S_IFBLK; |
| 1613 |
break; |
| 1614 |
case 'c': |
| 1615 |
mask = S_IFCHR; |
| 1616 |
break; |
| 1617 |
case 'd': |
| 1618 |
mask = S_IFDIR; |
| 1619 |
break; |
| 1620 |
case 'f': |
| 1621 |
mask = S_IFREG; |
| 1622 |
break; |
| 1623 |
case 'l': |
| 1624 |
mask = S_IFLNK; |
| 1625 |
break; |
| 1626 |
case 'p': |
| 1627 |
mask = S_IFIFO; |
| 1628 |
break; |
| 1629 |
case 's': |
| 1630 |
mask = S_IFSOCK; |
| 1631 |
break; |
| 1632 |
#if defined(FTS_WHITEOUT) && defined(S_IFWHT) |
| 1633 |
case 'w': |
| 1634 |
mask = S_IFWHT; |
| 1635 |
ftsoptions |= FTS_WHITEOUT; |
| 1636 |
break; |
| 1637 |
#endif /* FTS_WHITEOUT */ |
| 1638 |
default: |
| 1639 |
errx(1, "%s: %s: unknown type", option->name, typestring); |
| 1640 |
} |
| 1641 |
|
| 1642 |
new = palloc(option); |
| 1643 |
new->m_data = mask; |
| 1644 |
return new; |
| 1645 |
} |
| 1646 |
|
| 1647 |
/* |
| 1648 |
* -user uname functions -- |
| 1649 |
* |
| 1650 |
* True if the file belongs to the user uname. If uname is numeric and |
| 1651 |
* an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not |
| 1652 |
* return a valid user name, uname is taken as a user ID. |
| 1653 |
*/ |
| 1654 |
int |
| 1655 |
f_user(PLAN *plan, FTSENT *entry) |
| 1656 |
{ |
| 1657 |
COMPARE(entry->fts_statp->st_uid, plan->u_data); |
| 1658 |
} |
| 1659 |
|
| 1660 |
PLAN * |
| 1661 |
c_user(OPTION *option, char ***argvp) |
| 1662 |
{ |
| 1663 |
char *username; |
| 1664 |
PLAN *new; |
| 1665 |
struct passwd *p; |
| 1666 |
uid_t uid; |
| 1667 |
|
| 1668 |
username = nextarg(option, argvp); |
| 1669 |
ftsoptions &= ~FTS_NOSTAT; |
| 1670 |
|
| 1671 |
new = palloc(option); |
| 1672 |
p = getpwnam(username); |
| 1673 |
if (p == NULL) { |
| 1674 |
char* cp = username; |
| 1675 |
if( username[0] == '-' || username[0] == '+' ) |
| 1676 |
username++; |
| 1677 |
uid = atoi(username); |
| 1678 |
if (uid == 0 && username[0] != '0') |
| 1679 |
errx(1, "%s: %s: no such user", option->name, username); |
| 1680 |
uid = find_parsenum(new, option->name, cp, NULL); |
| 1681 |
} else |
| 1682 |
uid = p->pw_uid; |
| 1683 |
|
| 1684 |
new->u_data = uid; |
| 1685 |
return new; |
| 1686 |
} |
| 1687 |
|
| 1688 |
/* |
| 1689 |
* -xdev functions -- |
| 1690 |
* |
| 1691 |
* Always true, causes find not to descend past directories that have a |
| 1692 |
* different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) |
| 1693 |
*/ |
| 1694 |
PLAN * |
| 1695 |
c_xdev(OPTION *option, char ***argvp __unused) |
| 1696 |
{ |
| 1697 |
ftsoptions |= FTS_XDEV; |
| 1698 |
|
| 1699 |
return palloc(option); |
| 1700 |
} |
| 1701 |
|
| 1702 |
/* |
| 1703 |
* ( expression ) functions -- |
| 1704 |
* |
| 1705 |
* True if expression is true. |
| 1706 |
*/ |
| 1707 |
int |
| 1708 |
f_expr(PLAN *plan, FTSENT *entry) |
| 1709 |
{ |
| 1710 |
PLAN *p; |
| 1711 |
int state = 0; |
| 1712 |
|
| 1713 |
for (p = plan->p_data[0]; |
| 1714 |
p && (state = (p->execute)(p, entry)); p = p->next); |
| 1715 |
return state; |
| 1716 |
} |
| 1717 |
|
| 1718 |
/* |
| 1719 |
* f_openparen and f_closeparen nodes are temporary place markers. They are |
| 1720 |
* eliminated during phase 2 of find_formplan() --- the '(' node is converted |
| 1721 |
* to a f_expr node containing the expression and the ')' node is discarded. |
| 1722 |
* The functions themselves are only used as constants. |
| 1723 |
*/ |
| 1724 |
|
| 1725 |
int |
| 1726 |
f_openparen(PLAN *plan __unused, FTSENT *entry __unused) |
| 1727 |
{ |
| 1728 |
abort(); |
| 1729 |
} |
| 1730 |
|
| 1731 |
int |
| 1732 |
f_closeparen(PLAN *plan __unused, FTSENT *entry __unused) |
| 1733 |
{ |
| 1734 |
abort(); |
| 1735 |
} |
| 1736 |
|
| 1737 |
/* c_openparen == c_simple */ |
| 1738 |
/* c_closeparen == c_simple */ |
| 1739 |
|
| 1740 |
/* |
| 1741 |
* AND operator. Since AND is implicit, no node is allocated. |
| 1742 |
*/ |
| 1743 |
PLAN * |
| 1744 |
c_and(OPTION *option __unused, char ***argvp __unused) |
| 1745 |
{ |
| 1746 |
return NULL; |
| 1747 |
} |
| 1748 |
|
| 1749 |
/* |
| 1750 |
* ! expression functions -- |
| 1751 |
* |
| 1752 |
* Negation of a primary; the unary NOT operator. |
| 1753 |
*/ |
| 1754 |
int |
| 1755 |
f_not(PLAN *plan, FTSENT *entry) |
| 1756 |
{ |
| 1757 |
PLAN *p; |
| 1758 |
int state = 0; |
| 1759 |
|
| 1760 |
for (p = plan->p_data[0]; |
| 1761 |
p && (state = (p->execute)(p, entry)); p = p->next); |
| 1762 |
return !state; |
| 1763 |
} |
| 1764 |
|
| 1765 |
/* c_not == c_simple */ |
| 1766 |
|
| 1767 |
/* |
| 1768 |
* expression -o expression functions -- |
| 1769 |
* |
| 1770 |
* Alternation of primaries; the OR operator. The second expression is |
| 1771 |
* not evaluated if the first expression is true. |
| 1772 |
*/ |
| 1773 |
int |
| 1774 |
f_or(PLAN *plan, FTSENT *entry) |
| 1775 |
{ |
| 1776 |
PLAN *p; |
| 1777 |
int state = 0; |
| 1778 |
|
| 1779 |
for (p = plan->p_data[0]; |
| 1780 |
p && (state = (p->execute)(p, entry)); p = p->next); |
| 1781 |
|
| 1782 |
if (state) |
| 1783 |
return 1; |
| 1784 |
|
| 1785 |
for (p = plan->p_data[1]; |
| 1786 |
p && (state = (p->execute)(p, entry)); p = p->next); |
| 1787 |
return state; |
| 1788 |
} |
| 1789 |
|
| 1790 |
/* c_or == c_simple */ |
| 1791 |
|
| 1792 |
/* |
| 1793 |
* -false |
| 1794 |
* |
| 1795 |
* Always false. |
| 1796 |
*/ |
| 1797 |
int |
| 1798 |
f_false(PLAN *plan __unused, FTSENT *entry __unused) |
| 1799 |
{ |
| 1800 |
return 0; |
| 1801 |
} |
| 1802 |
|
| 1803 |
/* c_false == c_simple */ |
| 1804 |
|
| 1805 |
/* |
| 1806 |
* -quit |
| 1807 |
* |
| 1808 |
* Exits the program |
| 1809 |
*/ |
| 1810 |
int |
| 1811 |
f_quit(PLAN *plan __unused, FTSENT *entry __unused) |
| 1812 |
{ |
| 1813 |
finish_execplus(); |
| 1814 |
exit(exitstatus); |
| 1815 |
} |
| 1816 |
|
| 1817 |
/* c_quit == c_simple */ |