diff options
Diffstat (limited to 'find/pred.c')
-rw-r--r-- | find/pred.c | 2513 |
1 files changed, 815 insertions, 1698 deletions
diff --git a/find/pred.c b/find/pred.c index 75e6774a..9c8c6828 100644 --- a/find/pred.c +++ b/find/pred.c @@ -1,70 +1,40 @@ /* pred.c -- execute the expression tree. - Copyright (C) 1990, 1991, 1992, 1993, 1994, 2000, 2003, - 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc. - This program is free software: you can redistribute it and/or modify + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - + the Free Software Foundation; either version 2, or (at your option) + any later version. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <config.h> -#include "defs.h" - +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> #include <fnmatch.h> #include <signal.h> -#include <math.h> #include <pwd.h> #include <grp.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <errno.h> -#include <assert.h> -#include <stdarg.h> -#include <fcntl.h> -#include <locale.h> -#include <openat.h> -#include "xalloc.h" -#include "dirname.h" -#include "human.h" +#include "defs.h" #include "modetype.h" -#include "filemode.h" #include "wait.h" -#include "printquoted.h" -#include "buildcmd.h" -#include "yesno.h" -#include "listfile.h" -#include "stat-time.h" -#include "dircallback.h" -#include "error.h" -#include "verify.h" - -#if ENABLE_NLS -# include <libintl.h> -# define _(Text) gettext (Text) -#else -# define _(Text) Text -#endif -#ifdef gettext_noop -# define N_(String) gettext_noop (String) -#else -/* See locate.c for explanation as to why not use (String) */ -# define N_(String) String -#endif #if !defined(SIGCHLD) && defined(SIGCLD) #define SIGCHLD SIGCLD #endif - +#ifndef _POSIX_VERSION +struct passwd *getpwuid (); +struct group *getgrgid (); +#endif #if HAVE_DIRENT_H # include <dirent.h> @@ -90,83 +60,40 @@ #define CLOSEDIR(d) closedir (d) #endif - - - -/* Get or fake the disk device blocksize. - Usually defined by sys/param.h (if at all). */ -#ifndef DEV_BSIZE -# ifdef BSIZE -# define DEV_BSIZE BSIZE -# else /* !BSIZE */ -# define DEV_BSIZE 4096 -# endif /* !BSIZE */ -#endif /* !DEV_BSIZE */ - -/* Extract or fake data from a `struct stat'. - ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes. - ST_NBLOCKS: Number of blocks in the file, including indirect blocks. - ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS. */ -#ifndef HAVE_STRUCT_STAT_ST_BLOCKS -# define ST_BLKSIZE(statbuf) DEV_BSIZE -# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE. */ -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0) : 0) -# else /* !_POSIX_SOURCE && BSIZE */ -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? st_blocks ((statbuf).st_size) : 0) -# endif /* !_POSIX_SOURCE && BSIZE */ -#else /* HAVE_STRUCT_STAT_ST_BLOCKS */ -/* Some systems, like Sequents, return st_blksize of 0 on pipes. */ -# define ST_BLKSIZE(statbuf) ((statbuf).st_blksize > 0 \ - ? (statbuf).st_blksize : DEV_BSIZE) -# if defined hpux || defined __hpux__ || defined __hpux -/* HP-UX counts st_blocks in 1024-byte units. - This loses when mixing HP-UX and BSD file systems with NFS. */ -# define ST_NBLOCKSIZE 1024 -# else /* !hpux */ -# if defined _AIX && defined _I386 -/* AIX PS/2 counts st_blocks in 4K units. */ -# define ST_NBLOCKSIZE (4 * 1024) -# else /* not AIX PS/2 */ -# if defined _CRAY -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0) -# endif /* _CRAY */ -# endif /* not AIX PS/2 */ -# endif /* !hpux */ -#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */ - -#ifndef ST_NBLOCKS -# define ST_NBLOCKS(statbuf) \ - (S_ISREG ((statbuf).st_mode) \ - || S_ISDIR ((statbuf).st_mode) \ - ? (statbuf).st_blocks : 0) +#ifndef S_IFLNK +#define lstat stat #endif -#ifndef ST_NBLOCKSIZE -# define ST_NBLOCKSIZE 512 +/* Extract or fake data from a `struct stat'. + ST_NBLOCKS: Number of 512-byte blocks in the file + (including indirect blocks). + HP-UX, perhaps uniquely, counts st_blocks in 1024-byte units. + This workaround loses when mixing HP-UX and 4BSD filesystems, though. */ +#ifdef _POSIX_SOURCE +# define ST_NBLOCKS(statp) (((statp)->st_size + 512 - 1) / 512) +#else +# ifndef HAVE_ST_BLOCKS +# define ST_NBLOCKS(statp) (st_blocks ((statp)->st_size)) +# else +# if defined(hpux) || defined(__hpux__) +# define ST_NBLOCKS(statp) ((statp)->st_blocks * 2) +# else +# define ST_NBLOCKS(statp) ((statp)->st_blocks) +# endif +# endif #endif +int lstat (); +int stat (); -#undef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) - -static boolean match_lname PARAMS((const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); - -static char *format_date PARAMS((struct timespec ts, int kind)); -static char *ctime_format PARAMS((struct timespec ts)); +static boolean insert_lname P_((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case)); +static boolean launch P_((struct predicate *pred_ptr)); +static char *format_date P_((time_t when, int kind)); #ifdef DEBUG struct pred_assoc { - PRED_FUNC pred_func; + PFB pred_func; char *pred_name; }; @@ -176,16 +103,13 @@ struct pred_assoc pred_table[] = {pred_and, "and "}, {pred_anewer, "anewer "}, {pred_atime, "atime "}, - {pred_closeparen, ") "}, - {pred_cmin, "cmin "}, + {pred_close, ") "}, + {pred_amin, "cmin "}, {pred_cnewer, "cnewer "}, {pred_comma, ", "}, {pred_ctime, "ctime "}, - {pred_delete, "delete "}, {pred_empty, "empty "}, {pred_exec, "exec "}, - {pred_execdir, "execdir "}, - {pred_executable, "executable "}, {pred_false, "false "}, {pred_fprint, "fprint "}, {pred_fprint0, "fprint0 "}, @@ -200,64 +124,66 @@ struct pred_assoc pred_table[] = {pred_links, "links "}, {pred_lname, "lname "}, {pred_ls, "ls "}, - {pred_mmin, "mmin "}, + {pred_amin, "mmin "}, {pred_mtime, "mtime "}, {pred_name, "name "}, {pred_negate, "not "}, {pred_newer, "newer "}, - {pred_newerXY, "newerXY "}, {pred_nogroup, "nogroup "}, {pred_nouser, "nouser "}, {pred_ok, "ok "}, - {pred_okdir, "okdir "}, - {pred_openparen, "( "}, + {pred_open, "( "}, {pred_or, "or "}, {pred_path, "path "}, {pred_perm, "perm "}, {pred_print, "print "}, {pred_print0, "print0 "}, {pred_prune, "prune "}, - {pred_quit, "quit "}, - {pred_readable, "readable "}, {pred_regex, "regex "}, - {pred_samefile,"samefile "}, {pred_size, "size "}, {pred_true, "true "}, {pred_type, "type "}, {pred_uid, "uid "}, {pred_used, "used "}, {pred_user, "user "}, - {pred_writable, "writable "}, {pred_xtype, "xtype "}, {0, "none "} }; -#endif - -/* Returns ts1 - ts2 */ -static double ts_difference(struct timespec ts1, - struct timespec ts2) + +struct op_assoc { - double d = difftime(ts1.tv_sec, ts2.tv_sec) - + (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec)); - return d; -} + short type; + char *type_name; +}; - -static int -compare_ts(struct timespec ts1, - struct timespec ts2) +struct op_assoc type_table[] = { - if ((ts1.tv_sec == ts2.tv_sec) && - (ts1.tv_nsec == ts2.tv_nsec)) - { - return 0; - } - else - { - double diff = ts_difference(ts1, ts2); - return diff < 0.0 ? -1 : +1; - } -} + {NO_TYPE, "no "}, + {PRIMARY_TYPE, "primary "}, + {UNI_OP, "uni_op "}, + {BI_OP, "bi_op "}, + {OPEN_PAREN, "open_paren "}, + {CLOSE_PAREN, "close_paren "}, + {-1, "unknown "} +}; + +struct prec_assoc +{ + short prec; + char *prec_name; +}; + +struct prec_assoc prec_table[] = +{ + {NO_PREC, "no "}, + {COMMA_PREC, "comma "}, + {OR_PREC, "or "}, + {AND_PREC, "and "}, + {NEGATE_PREC, "negate "}, + {MAX_PREC, "max "}, + {-1, "unknown "} +}; +#endif /* DEBUG */ /* Predicate processing routines. @@ -267,212 +193,210 @@ compare_ts(struct timespec ts1, Return true if the file passes this predicate, false if not. */ - -/* pred_timewindow - * - * Returns true if THE_TIME is - * COMP_GT: after the specified time - * COMP_LT: before the specified time - * COMP_EQ: after the specified time but by not more than WINDOW seconds. - */ -static boolean -pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window) +boolean +pred_amin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - switch (pred_ptr->args.reftime.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; - + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; case COMP_LT: - return compare_ts(ts, pred_ptr->args.reftime.ts) < 0; - + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; case COMP_EQ: - { - /* consider "find . -mtime 0". - * - * Here, the origin is exactly 86400 seconds before the start - * of the program (since -daystart was not specified). This - * function will be called with window=86400 and - * pred_ptr->args.reftime.ts as the origin. Hence a file - * created the instant the program starts will show a time - * difference (value of delta) of 86400. Similarly, a file - * created exactly 24h ago would be the newest file which was - * _not_ created today. So, if delta is 0.0, the file - * was not created today. If the delta is 86400, the file - * was created this instant. - */ - double delta = ts_difference(ts, pred_ptr->args.reftime.ts); - return (delta > 0.0 && delta <= window); - } + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; } - assert (0); - abort (); -} - - -boolean -pred_amin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) &pathname; - return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60); + return (false); } boolean -pred_and (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_and (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left == NULL - || apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) + || (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) { - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } else - return false; + return (false); } boolean -pred_anewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_anewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0; + if (stat_buf->st_atime > pred_ptr->args.time) + return (true); + return (false); } boolean -pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_atime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_atime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_atime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_atime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); } boolean -pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_close (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - (void) &pred_ptr; - - return true; + return (true); } boolean -pred_cmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_cmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); } boolean -pred_cnewer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_cnewer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0; + if (stat_buf->st_ctime > pred_ptr->args.time) + return (true); + return (false); } boolean -pred_comma (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_comma (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left != NULL) + (*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) { - apply_predicate(pathname, stat_buf,pred_ptr->pred_left); + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; } - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } boolean -pred_ctime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ctime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS); -} - -static boolean -perform_delete(int flags) -{ - return 0 == unlinkat(state.cwd_dir_fd, state.rel_pathname, flags); -} - - -boolean -pred_delete (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pred_ptr; - (void) stat_buf; - if (strcmp (state.rel_pathname, ".")) + switch (pred_ptr->args.info.kind) { - int flags=0; - if (state.have_stat && S_ISDIR(stat_buf->st_mode)) - flags |= AT_REMOVEDIR; - if (perform_delete(flags)) - { - return true; - } - else - { - if (EISDIR == errno) - { - if ((flags & AT_REMOVEDIR) == 0) - { - /* unlink() operation failed because we should have done rmdir(). */ - flags |= AT_REMOVEDIR; - if (perform_delete(flags)) - return true; - } - } - } - error (0, errno, _("cannot delete %s"), - safely_quote_err_filename(0, pathname)); - /* Previously I had believed that having the -delete action - * return false provided the user with control over whether an - * error message is issued. While this is true, the policy of - * not affecting the exit status is contrary to the POSIX - * requirement that diagnostic messages are accompanied by a - * nonzero exit status. While -delete is not a POSIX option and - * we can therefore opt not to follow POSIX in this case, that - * seems somewhat arbitrary and confusing. So, as of - * findutils-4.3.11, we also set the exit status in this case. - */ - state.exit_status = 1; - return false; - } - else - { - /* nothing to do. */ - return true; + case COMP_GT: + if (stat_buf->st_ctime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_ctime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_ctime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; } + return (false); } boolean -pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_empty (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - if (S_ISDIR (stat_buf->st_mode)) { - int fd; DIR *d; struct dirent *dp; boolean empty = true; errno = 0; - if ((fd = openat(state.cwd_dir_fd, state.rel_pathname, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - )) < 0) - { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; - } - d = fdopendir (fd); + d = opendir (rel_pathname); if (d == NULL) { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } for (dp = readdir (d); dp; dp = readdir (d)) { @@ -486,9 +410,9 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } if (CLOSEDIR (d)) { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } return (empty); } @@ -498,288 +422,162 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ return (false); } -static boolean -new_impl_pred_exec (int dirfd, const char *pathname, - struct stat *stat_buf, - struct predicate *pred_ptr, - const char *prefix, size_t pfxlen) +boolean +pred_exec (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct exec_val *execp = &pred_ptr->args.exec_vec; - size_t len = strlen(pathname); - - (void) stat_buf; - execp->dirfd = dirfd; - if (execp->multiple) - { - /* Push the argument onto the current list. - * The command may or may not be run at this point, - * depending on the command line length limits. - */ - bc_push_arg(&execp->ctl, - &execp->state, - pathname, len+1, - prefix, pfxlen, - 0); - - /* remember that there are pending execdirs. */ - state.execdirs_outstanding = true; - - /* POSIX: If the primary expression is punctuated by a plus - * sign, the primary shall always evaluate as true - */ - return true; - } - else - { - int i; + int i; + int path_pos; + struct exec_val *execp; /* Pointer for efficiency. */ - for (i=0; i<execp->num_args; ++i) - { - bc_do_insert(&execp->ctl, - &execp->state, - execp->replace_vec[i], - strlen(execp->replace_vec[i]), - prefix, pfxlen, - pathname, len, - 0); - } + execp = &pred_ptr->args.exec_vec; - /* Actually invoke the command. */ - return execp->ctl.exec_callback(&execp->ctl, - &execp->state); + /* Replace "{}" with the real path in each affected arg. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + { + register char *from, *to; + + i = execp->paths[path_pos].offset; + execp->vec[i] = + xmalloc (strlen (execp->paths[path_pos].origarg) + 1 + + (strlen (pathname) - 2) * execp->paths[path_pos].count); + for (from = execp->paths[path_pos].origarg, to = execp->vec[i]; *from; ) + if (from[0] == '{' && from[1] == '}') + { + to = stpcpy (to, pathname); + from += 2; + } + else + *to++ = *from++; + *to = *from; /* Copy null. */ } -} + i = launch (pred_ptr); -boolean -pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - return new_impl_pred_exec(get_start_dirfd(), - pathname, stat_buf, pred_ptr, NULL, 0); -} + /* Free the temporary args. */ + for (path_pos = 0; execp->paths[path_pos].offset >= 0; path_pos++) + free (execp->vec[execp->paths[path_pos].offset]); -boolean -pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; - (void) &pathname; - return new_impl_pred_exec (get_current_dirfd(), - state.rel_pathname, stat_buf, pred_ptr, - prefix, (prefix ? 2 : 0)); + return (i); } boolean -pred_false (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_false (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - (void) &pred_ptr; - - return (false); } boolean -pred_fls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - FILE * stream = pred_ptr->args.printf_vec.stream; - list_file (pathname, state.cwd_dir_fd, state.rel_pathname, stat_buf, - options.start_time.tv_sec, - options.output_block_size, - pred_ptr->literal_control_chars, stream); - return true; -} - -boolean -pred_fprint (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - (void) &stat_buf; - - print_quoted(pred_ptr->args.printf_vec.stream, - pred_ptr->args.printf_vec.quote_opts, - pred_ptr->args.printf_vec.dest_is_tty, - "%s\n", - pathname); - return true; + list_file (pathname, rel_pathname, stat_buf, pred_ptr->args.stream); + return (true); } boolean -pred_fprint0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fprint (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - FILE * fp = pred_ptr->args.printf_vec.stream; - - (void) &stat_buf; - - fputs (pathname, fp); - putc (0, fp); - return true; -} - - - -static char* -mode_to_filetype(mode_t m) -{ -#define HANDLE_TYPE(t,letter) if (m==t) { return letter; } -#ifdef S_IFREG - HANDLE_TYPE(S_IFREG, "f"); /* regular file */ -#endif -#ifdef S_IFDIR - HANDLE_TYPE(S_IFDIR, "d"); /* directory */ -#endif -#ifdef S_IFLNK - HANDLE_TYPE(S_IFLNK, "l"); /* symbolic link */ -#endif -#ifdef S_IFSOCK - HANDLE_TYPE(S_IFSOCK, "s"); /* Unix domain socket */ -#endif -#ifdef S_IFBLK - HANDLE_TYPE(S_IFBLK, "b"); /* block device */ -#endif -#ifdef S_IFCHR - HANDLE_TYPE(S_IFCHR, "c"); /* character device */ -#endif -#ifdef S_IFIFO - HANDLE_TYPE(S_IFIFO, "p"); /* FIFO */ -#endif -#ifdef S_IFDOOR - HANDLE_TYPE(S_IFDOOR, "D"); /* Door (e.g. on Solaris) */ -#endif - return "U"; /* Unknown */ -} - -static double -file_sparseness(const struct stat *p) -{ -#if defined HAVE_STRUCT_STAT_ST_BLOCKS - if (0 == p->st_size) - { - if (0 == p->st_blocks) - return 1.0; - else - return p->st_blocks < 0 ? -HUGE_VAL : HUGE_VAL; - } - else - { - double blklen = file_blocksize(p) * (double)p->st_blocks; - return blklen / p->st_size; - } -#else - return 1.0; -#endif + fputs (pathname, pred_ptr->args.stream); + putc ('\n', pred_ptr->args.stream); + return (true); } - - -static void -checked_fprintf(struct format_val *dest, const char *fmt, ...) -{ - int rv; - va_list ap; - - va_start(ap, fmt); - rv = vfprintf(dest->stream, fmt, ap); - if (rv < 0) - nonfatal_file_error(dest->filename); -} - - -static void -checked_print_quoted (struct format_val *dest, - const char *format, const char *s) +boolean +pred_fprint0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - int rv = print_quoted(dest->stream, dest->quote_opts, dest->dest_is_tty, - format, s); - if (rv < 0) - nonfatal_file_error(dest->filename); + fputs (pathname, pred_ptr->args.stream); + putc (0, pred_ptr->args.stream); + return (true); } - -static void -checked_fwrite(void *p, size_t siz, size_t nmemb, struct format_val *dest) +boolean +pred_fprintf (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - int items_written = fwrite(p, siz, nmemb, dest->stream); - if (items_written < nmemb) - nonfatal_file_error(dest->filename); -} + FILE *fp = pred_ptr->args.printf_vec.stream; + struct segment *segment; + char *cp; -static void -checked_fflush(struct format_val *dest) -{ - if (0 != fflush(dest->stream)) + for (segment = pred_ptr->args.printf_vec.segment; segment; + segment = segment->next) { - nonfatal_file_error(dest->filename); - } -} + if (segment->kind & 0xff00) /* Component of date. */ + { + time_t t; -static void -do_fprintf(struct format_val *dest, - struct segment *segment, - const char *pathname, - const struct stat *stat_buf) -{ - char hbuf[LONGEST_HUMAN_READABLE + 1]; - const char *cp; + switch (segment->kind & 0xff) + { + case 'A': + t = stat_buf->st_atime; + break; + case 'C': + t = stat_buf->st_ctime; + break; + case 'T': + t = stat_buf->st_mtime; + break; + default: + abort (); + } + fprintf (fp, segment->text, + format_date (t, (segment->kind >> 8) & 0xff)); + continue; + } - switch (segment->segkind) - { - case KIND_PLAIN: /* Plain text string (no % conversion). */ - /* trusted */ - checked_fwrite(segment->text, 1, segment->text_len, dest); - break; - - case KIND_STOP: /* Terminate argument and flush output. */ - /* trusted */ - checked_fwrite(segment->text, 1, segment->text_len, dest); - checked_fflush(dest); - break; - - case KIND_FORMAT: - switch (segment->format_char[0]) + switch (segment->kind) { + case KIND_PLAIN: /* Plain text string (no % conversion). */ + fwrite (segment->text, 1, segment->text_len, fp); + break; + case KIND_STOP: /* Terminate argument and flush output. */ + fwrite (segment->text, 1, segment->text_len, fp); + fflush (fp); + return (true); case 'a': /* atime in `ctime' format. */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, ctime_format (get_stat_atime(stat_buf))); + cp = ctime (&stat_buf->st_atime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; case 'b': /* size in 512-byte blocks */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), - hbuf, human_ceiling, - ST_NBLOCKSIZE, 512)); + fprintf (fp, segment->text, ST_NBLOCKS (stat_buf)); break; case 'c': /* ctime in `ctime' format */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, ctime_format (get_stat_ctime(stat_buf))); + cp = ctime (&stat_buf->st_ctime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; case 'd': /* depth in search tree */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, state.curdepth); - break; - case 'D': /* Device on which file exists (stat.st_dev) */ - /* trusted */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_dev, hbuf, - human_ceiling, 1, 1)); + fprintf (fp, segment->text, curdepth); break; - case 'f': /* base name of path */ - /* sanitised */ - { - char *base = base_name (pathname); - checked_print_quoted (dest, segment->text, base); - free (base); - } + case 'f': /* basename of path */ + cp = strrchr (pathname, '/'); + if (cp) + cp++; + else + cp = pathname; + fprintf (fp, segment->text, cp); break; - case 'F': /* file system type */ - /* trusted */ - checked_print_quoted (dest, segment->text, filesystem_type (stat_buf, pathname)); + case 'F': /* filesystem type */ + fprintf (fp, segment->text, + filesystem_type (pathname, rel_pathname, stat_buf)); break; case 'g': /* group name */ - /* trusted */ - /* (well, the actual group is selected by the user but - * its name was selected by the system administrator) - */ { struct group *g; @@ -787,158 +585,77 @@ do_fprintf(struct format_val *dest, if (g) { segment->text[segment->text_len] = 's'; - checked_fprintf (dest, segment->text, g->gr_name); + fprintf (fp, segment->text, g->gr_name); break; } - else - { - /* Do nothing. */ - /*FALLTHROUGH*/ - } + /* else fallthru */ } - /*FALLTHROUGH*/ /*...sometimes, so 'G' case.*/ - case 'G': /* GID number */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_gid, hbuf, - human_ceiling, 1, 1)); + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_gid); break; case 'h': /* leading directories part of path */ - /* sanitised */ { + char cc; + cp = strrchr (pathname, '/'); if (cp == NULL) /* No leading directories. */ - { - /* If there is no slash in the pathname, we still - * print the string because it contains characters - * other than just '%s'. The %h expands to ".". - */ - checked_print_quoted (dest, segment->text, "."); - } - else - { - char *s = strdup(pathname); - s[cp - pathname] = 0; - checked_print_quoted (dest, segment->text, s); - free(s); - } + break; + cc = *cp; + *cp = '\0'; + fprintf (fp, segment->text, pathname); + *cp = cc; + break; } - break; - case 'H': /* ARGV element file was found under */ - /* trusted */ { - char *s = xmalloc(state.starting_path_length+1); - memcpy(s, pathname, state.starting_path_length); - s[state.starting_path_length] = 0; - checked_fprintf (dest, segment->text, s); - free(s); - } - break; + char cc = pathname[path_length]; + pathname[path_length] = '\0'; + fprintf (fp, segment->text, pathname); + pathname[path_length] = cc; + break; + } case 'i': /* inode number */ - /* UNTRUSTED, but not exploitable I think */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_ino, hbuf, - human_ceiling, - 1, 1)); + fprintf (fp, segment->text, stat_buf->st_ino); break; case 'k': /* size in 1K blocks */ - /* UNTRUSTED, but not exploitable I think */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) ST_NBLOCKS (*stat_buf), - hbuf, human_ceiling, - ST_NBLOCKSIZE, 1024)); + fprintf (fp, segment->text, (ST_NBLOCKS (stat_buf) + 1) / 2); break; case 'l': /* object of symlink */ - /* sanitised */ #ifdef S_ISLNK { char *linkname = 0; if (S_ISLNK (stat_buf->st_mode)) { - linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); + linkname = get_link_name (pathname, rel_pathname); if (linkname == 0) - state.exit_status = 1; + exit_status = 1; } if (linkname) { - checked_print_quoted (dest, segment->text, linkname); + fprintf (fp, segment->text, linkname); free (linkname); } else - { - /* We still need to honour the field width etc., so this is - * not a no-op. - */ - checked_print_quoted (dest, segment->text, ""); - } + fprintf (fp, segment->text, ""); } #endif /* S_ISLNK */ break; - - case 'M': /* mode as 10 chars (eg., "-rwxr-x--x" */ - /* UNTRUSTED, probably unexploitable */ - { - char modestring[16] ; - filemodestring (stat_buf, modestring); - modestring[10] = '\0'; - checked_fprintf (dest, segment->text, modestring); - } - break; - case 'm': /* mode as octal number (perms only) */ - /* UNTRUSTED, probably unexploitable */ - { - /* Output the mode portably using the traditional numbers, - even if the host unwisely uses some other numbering - scheme. But help the compiler in the common case where - the host uses the traditional numbering scheme. */ - mode_t m = stat_buf->st_mode; - boolean traditional_numbering_scheme = - (S_ISUID == 04000 && S_ISGID == 02000 && S_ISVTX == 01000 - && S_IRUSR == 00400 && S_IWUSR == 00200 && S_IXUSR == 00100 - && S_IRGRP == 00040 && S_IWGRP == 00020 && S_IXGRP == 00010 - && S_IROTH == 00004 && S_IWOTH == 00002 && S_IXOTH == 00001); - checked_fprintf (dest, segment->text, - (traditional_numbering_scheme - ? m & MODE_ALL - : ((m & S_ISUID ? 04000 : 0) - | (m & S_ISGID ? 02000 : 0) - | (m & S_ISVTX ? 01000 : 0) - | (m & S_IRUSR ? 00400 : 0) - | (m & S_IWUSR ? 00200 : 0) - | (m & S_IXUSR ? 00100 : 0) - | (m & S_IRGRP ? 00040 : 0) - | (m & S_IWGRP ? 00020 : 0) - | (m & S_IXGRP ? 00010 : 0) - | (m & S_IROTH ? 00004 : 0) - | (m & S_IWOTH ? 00002 : 0) - | (m & S_IXOTH ? 00001 : 0)))); - } + fprintf (fp, segment->text, stat_buf->st_mode & 07777); break; - case 'n': /* number of links */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_nlink, - hbuf, - human_ceiling, - 1, 1)); + fprintf (fp, segment->text, stat_buf->st_nlink); break; - case 'p': /* pathname */ - /* sanitised */ - checked_print_quoted (dest, segment->text, pathname); + fprintf (fp, segment->text, pathname); break; - case 'P': /* pathname with ARGV element stripped */ - /* sanitised */ - if (state.curdepth > 0) + if (curdepth) { - cp = pathname + state.starting_path_length; + cp = pathname + path_length; if (*cp == '/') /* Move past the slash between the ARGV element and the rest of the pathname. But if the ARGV element @@ -947,36 +664,18 @@ do_fprintf(struct format_val *dest, cp++; } else - { - cp = ""; - } - checked_print_quoted (dest, segment->text, cp); + cp = ""; + fprintf (fp, segment->text, cp); break; - case 's': /* size in bytes */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_size, - hbuf, human_ceiling, 1, 1)); + fprintf (fp, segment->text, stat_buf->st_size); break; - - case 'S': /* sparseness */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, file_sparseness(stat_buf));; - break; - case 't': /* mtime in `ctime' format */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - ctime_format (get_stat_mtime(stat_buf))); + cp = ctime (&stat_buf->st_mtime); + cp[24] = '\0'; + fprintf (fp, segment->text, cp); break; - case 'u': /* user name */ - /* trusted */ - /* (well, the actual user is selected by the user on systems - * where chown is not restricted, but the user name was - * selected by the system administrator) - */ { struct passwd *p; @@ -984,177 +683,50 @@ do_fprintf(struct format_val *dest, if (p) { segment->text[segment->text_len] = 's'; - checked_fprintf (dest, segment->text, p->pw_name); + fprintf (fp, segment->text, p->pw_name); break; } /* else fallthru */ } - /* FALLTHROUGH*/ /* .. to case U */ - case 'U': /* UID number */ - /* UNTRUSTED, probably unexploitable */ - checked_fprintf (dest, segment->text, - human_readable ((uintmax_t) stat_buf->st_uid, hbuf, - human_ceiling, 1, 1)); - break; - - /* %Y: type of file system entry like `ls -l`: - * (d,-,l,s,p,b,c,n) n=nonexistent(symlink) - */ - case 'Y': /* in case of symlink */ - /* trusted */ - { -#ifdef S_ISLNK - if (S_ISLNK (stat_buf->st_mode)) - { - struct stat sbuf; - /* If we would normally follow links, do not do so. - * If we would normally not follow links, do so. - */ - if ((following_links() ? lstat : stat) - (state.rel_pathname, &sbuf) != 0) - { - if ( errno == ENOENT ) - { - checked_fprintf (dest, segment->text, "N"); - break; - } - else if ( errno == ELOOP ) - { - checked_fprintf (dest, segment->text, "L"); - break; - } - else - { - checked_fprintf (dest, segment->text, "?"); - error (0, errno, "%s", - safely_quote_err_filename(0, pathname)); - /* exit_status = 1; - return ; */ - break; - } - } - checked_fprintf (dest, segment->text, - mode_to_filetype(sbuf.st_mode & S_IFMT)); - } -#endif /* S_ISLNK */ - else - { - checked_fprintf (dest, segment->text, - mode_to_filetype(stat_buf->st_mode & S_IFMT)); - } - } - break; - - case 'y': - /* trusted */ - { - checked_fprintf (dest, segment->text, - mode_to_filetype(stat_buf->st_mode & S_IFMT)); - } + segment->text[segment->text_len] = 'u'; + fprintf (fp, segment->text, stat_buf->st_uid); break; } - /* end of KIND_FORMAT case */ - break; - } -} - -boolean -pred_fprintf (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - struct format_val *dest = &pred_ptr->args.printf_vec; - struct segment *segment; - - for (segment = dest->segment; segment; segment = segment->next) - { - if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */ - { - struct timespec ts; - int valid = 0; - - switch (segment->format_char[0]) - { - case 'A': - ts = get_stat_atime(stat_buf); - valid = 1; - break; - case 'B': - ts = get_stat_birthtime(stat_buf); - if ('@' == segment->format_char[1]) - valid = 1; - else - valid = (ts.tv_nsec >= 0); - break; - case 'C': - ts = get_stat_ctime(stat_buf); - valid = 1; - break; - case 'T': - ts = get_stat_mtime(stat_buf); - valid = 1; - break; - default: - assert (0); - abort (); - } - /* We trust the output of format_date not to contain - * nasty characters, though the value of the date - * is itself untrusted data. - */ - if (valid) - { - /* trusted */ - checked_fprintf (dest, segment->text, - format_date (ts, segment->format_char[1])); - } - else - { - /* The specified timestamp is not available, output - * nothing for the timestamp, but use the rest (so that - * for example find foo -printf '[%Bs] %p\n' can print - * "[] foo"). - */ - /* trusted */ - checked_fprintf (dest, segment->text, ""); - } - } - else - { - /* Print a segment which is not a date. */ - do_fprintf(dest, segment, pathname, stat_buf); - } } - return true; + return (true); } boolean -pred_fstype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_fstype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - if (strcmp (filesystem_type (stat_buf, pathname), pred_ptr->args.str) == 0) - return true; - else - return false; + if (strcmp (filesystem_type (pathname, rel_pathname, stat_buf), + pred_ptr->args.str) == 0) + return (true); + return (false); } boolean -pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_gid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_gid == pred_ptr->args.info.l_val) return (true); break; } @@ -1162,10 +734,11 @@ pred_gid (const char *pathname, struct stat *stat_buf, struct predicate *pred_pt } boolean -pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_group (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - if (pred_ptr->args.gid == stat_buf->st_gid) return (true); else @@ -1173,60 +746,46 @@ pred_group (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_ilname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ilname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return match_lname (pathname, stat_buf, pred_ptr, true); -} - -/* Common code between -name, -iname. PATHNAME is being visited, STR - is name to compare basename against, and FLAGS are passed to - fnmatch. Recall that 'find / -name /' is one of the few times where a '/' - in the -name must actually find something. */ -static boolean -pred_name_common (const char *pathname, const char *str, int flags) -{ - boolean b; - /* We used to use last_component() here, but that would not allow us to modify the - * input string, which is const. We could optimise by duplicating the string only - * if we need to modify it, and I'll do that if there is a measurable - * performance difference on a machine built after 1990... - */ - char *base = base_name (pathname); - /* remove trailing slashes, but leave "/" or "//foo" unchanged. */ - strip_trailing_slashes(base); - - /* FNM_PERIOD is not used here because POSIX requires that it not be. - * See http://standards.ieee.org/reading/ieee/interp/1003-2-92_int/pasc-1003.2-126.html - */ - b = fnmatch (str, base, flags) == 0; - free (base); - return b; + return insert_lname (pathname, stat_buf, pred_ptr, true); } boolean -pred_iname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_iname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - return pred_name_common (pathname, pred_ptr->args.str, FNM_CASEFOLD); + char *base; + + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD | FNM_CASEFOLD) == 0) + return (true); + return (false); } boolean -pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_inum (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_ino == pred_ptr->args.info.l_val) return (true); break; } @@ -1234,32 +793,34 @@ pred_inum (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_ipath (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ipath (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - if (fnmatch (pred_ptr->args.str, pathname, FNM_CASEFOLD) == 0) return (true); return (false); } boolean -pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_links (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_nlink == pred_ptr->args.info.l_val) return (true); break; } @@ -1267,19 +828,26 @@ pred_links (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_lname (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return match_lname (pathname, stat_buf, pred_ptr, false); + return insert_lname (pathname, stat_buf, pred_ptr, false); } static boolean -match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case) +insert_lname (pathname, stat_buf, pred_ptr, ignore_case) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; + boolean ignore_case; { boolean ret = false; #ifdef S_ISLNK if (S_ISLNK (stat_buf->st_mode)) { - char *linkname = get_link_name_at (pathname, state.cwd_dir_fd, state.rel_pathname); + char *linkname = get_link_name (pathname, rel_pathname); if (linkname) { if (fnmatch (pred_ptr->args.str, linkname, @@ -1289,103 +857,122 @@ match_lname (const char *pathname, struct stat *stat_buf, struct predicate *pred } } #endif /* S_ISLNK */ - return ret; -} - -boolean -pred_ls (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - return pred_fls(pathname, stat_buf, pred_ptr); + return (ret); } boolean -pred_mmin (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_ls (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) &pathname; - return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60); + list_file (pathname, rel_pathname, stat_buf, stdout); + return (true); } boolean -pred_mtime (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_mmin (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + 60)) + return (true); + break; + } + return (false); } boolean -pred_name (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_mtime (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; - return pred_name_common (pathname, pred_ptr->args.str, 0); + switch (pred_ptr->args.info.kind) + { + case COMP_GT: + if (stat_buf->st_mtime > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((stat_buf->st_mtime >= (time_t) pred_ptr->args.info.l_val) + && (stat_buf->st_mtime < (time_t) pred_ptr->args.info.l_val + + DAYSECS)) + return (true); + break; + } + return (false); } boolean -pred_negate (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_name (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return !apply_predicate(pathname, stat_buf, pred_ptr->pred_right); -} + char *base; -boolean -pred_newer (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0; + base = basename (pathname); + if (fnmatch (pred_ptr->args.str, base, FNM_PERIOD) == 0) + return (true); + return (false); } boolean -pred_newerXY (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_negate (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct timespec ts; - boolean collected = false; - - assert (COMP_GT == pred_ptr->args.reftime.kind); - - switch (pred_ptr->args.reftime.xval) + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) { - case XVAL_TIME: - assert (pred_ptr->args.reftime.xval != XVAL_TIME); - return false; - - case XVAL_ATIME: - ts = get_stat_atime(stat_buf); - collected = true; - break; - - case XVAL_BIRTHTIME: - ts = get_stat_birthtime(stat_buf); - collected = true; - if (ts.tv_nsec < 0); + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) { - /* XXX: Cannot determine birth time. Warn once. */ - error(0, 0, _("Warning: cannot determine birth time of file %s"), - safely_quote_err_filename(0, pathname)); - return false; + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } - break; - - case XVAL_CTIME: - ts = get_stat_ctime(stat_buf); - collected = true; - break; - - case XVAL_MTIME: - ts = get_stat_mtime(stat_buf); - collected = true; - break; + have_stat = true; } - - assert (collected); - return compare_ts(ts, pred_ptr->args.reftime.ts) > 0; + return (!(*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } boolean -pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_newer (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; +{ + if (stat_buf->st_mtime > pred_ptr->args.time) + return (true); + return (false); +} + +boolean +pred_nogroup (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - #ifdef CACHE_IDS extern char *gid_unused; @@ -1396,249 +983,157 @@ pred_nogroup (const char *pathname, struct stat *stat_buf, struct predicate *pre } boolean -pred_nouser (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_nouser (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { #ifdef CACHE_IDS extern char *uid_unused; -#endif - - (void) pathname; - (void) pred_ptr; - -#ifdef CACHE_IDS + return uid_unused[(unsigned) stat_buf->st_uid]; #else return getpwuid (stat_buf->st_uid) == NULL; #endif } - -static boolean -is_ok(const char *program, const char *arg) +boolean +pred_ok (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { + int i, yes; + fflush (stdout); - /* The draft open standard requires that, in the POSIX locale, - the last non-blank character of this prompt be '?'. - The exact format is not specified. - This standard does not have requirements for locales other than POSIX - */ - /* XXX: printing UNTRUSTED data here. */ - fprintf (stderr, _("< %s ... %s > ? "), program, arg); + fprintf (stderr, "< %s ... %s > ? ", + pred_ptr->args.exec_vec.vec[0], pathname); fflush (stderr); - return yesno(); -} - -boolean -pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_start_dirfd(), - pathname, stat_buf, pred_ptr, NULL, 0); - else - return false; -} - -boolean -pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; - if (is_ok(pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_current_dirfd(), - state.rel_pathname, stat_buf, pred_ptr, - prefix, (prefix ? 2 : 0)); - else - return false; + i = getchar (); + yes = (i == 'y' || i == 'Y'); + while (i != EOF && i != '\n') + i = getchar (); + if (!yes) + return (false); + return pred_exec (pathname, stat_buf, pred_ptr); } boolean -pred_openparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_open (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - return true; + return (true); } boolean -pred_or (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_or (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { if (pred_ptr->pred_left == NULL - || !apply_predicate(pathname, stat_buf, pred_ptr->pred_left)) + || !(*pred_ptr->pred_left->pred_func) (pathname, stat_buf, + pred_ptr->pred_left)) { - return apply_predicate(pathname, stat_buf, pred_ptr->pred_right); + /* Check whether we need a stat here. */ + if (pred_ptr->need_stat) + { + if (!have_stat && (*xstat) (rel_pathname, stat_buf) != 0) + { + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); + } + have_stat = true; + } + return ((*pred_ptr->pred_right->pred_func) (pathname, stat_buf, + pred_ptr->pred_right)); } else - return true; + return (true); } boolean -pred_path (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_path (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) stat_buf; if (fnmatch (pred_ptr->args.str, pathname, 0) == 0) return (true); return (false); } boolean -pred_perm (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_perm (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - mode_t mode = stat_buf->st_mode; - mode_t perm_val = pred_ptr->args.perm.val[S_ISDIR (mode) != 0]; - (void) pathname; - switch (pred_ptr->args.perm.kind) + if (pred_ptr->args.perm & 010000) { - case PERM_AT_LEAST: - return (mode & perm_val) == perm_val; - break; - - case PERM_ANY: - /* True if any of the bits set in the mask are also set in the file's mode. - * - * - * Otherwise, if onum is prefixed by a hyphen, the primary shall - * evaluate as true if at least all of the bits specified in - * onum that are also set in the octal mask 07777 are set. - * - * Eric Blake's interpretation is that the mode argument is zero, - - */ - if (0 == perm_val) - return true; /* Savannah bug 14748; we used to return false */ - else - return (mode & perm_val) != 0; - break; - - case PERM_EXACT: - return (mode & MODE_ALL) == perm_val; - break; - - default: - abort (); - break; + /* Magic flag set in parse_perm: + true if at least the given bits are set. */ + if ((stat_buf->st_mode & 07777 & pred_ptr->args.perm) + == (pred_ptr->args.perm & 07777)) + return (true); } -} - - -struct access_check_args -{ - const char *filename; - int access_type; - int cb_errno; -}; - - -static int -access_callback(void *context) -{ - int rv; - struct access_check_args *args = context; - if ((rv = access(args->filename, args->access_type)) < 0) - args->cb_errno = errno; - return rv; -} - -static int -can_access(int access_type) -{ - struct access_check_args args; - args.filename = state.rel_pathname; - args.access_type = access_type; - args.cb_errno = 0; - return 0 == run_in_dir(state.cwd_dir_fd, access_callback, &args); -} - - -boolean -pred_executable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(X_OK); -} - -boolean -pred_readable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(R_OK); -} - -boolean -pred_writable (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - return can_access(W_OK); -} - -boolean -pred_print (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - (void) stat_buf; - (void) pred_ptr; - - print_quoted(pred_ptr->args.printf_vec.stream, - pred_ptr->args.printf_vec.quote_opts, - pred_ptr->args.printf_vec.dest_is_tty, - "%s\n", pathname); - return true; + else if (pred_ptr->args.perm & 020000) + { + /* Magic flag set in parse_perm: + true if any of the given bits are set. */ + if ((stat_buf->st_mode & 07777) & pred_ptr->args.perm) + return (true); + } + else + { + /* True if exactly the given bits are set. */ + if ((stat_buf->st_mode & 07777) == pred_ptr->args.perm) + return (true); + } + return (false); } boolean -pred_print0 (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_print (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - return pred_fprint0(pathname, stat_buf, pred_ptr); + puts (pathname); + return (true); } boolean -pred_prune (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_print0 (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) pred_ptr; - - if (options.do_dir_first == true && /* no effect with -depth */ - stat_buf != NULL && - S_ISDIR(stat_buf->st_mode)) - state.stop_at_current_level = true; - - /* findutils used to return options.do_dir_first here, so that -prune - * returns true only if -depth is not in effect. But POSIX requires - * that -prune always evaluate as true. - */ - return true; + fputs (pathname, stdout); + putc (0, stdout); + return (true); } boolean -pred_quit (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_prune (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - - /* Run any cleanups. This includes executing any command lines - * we have partly built but not executed. - */ - cleanup(); - - /* Since -exec and friends don't leave child processes running in the - * background, there is no need to wait for them here. - */ - exit(state.exit_status); /* 0 for success, etc. */ + stop_at_current_level = true; + return (do_dir_first); /* This is what SunOS find seems to do. */ } boolean -pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_regex (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { int len = strlen (pathname); -(void) stat_buf; if (re_match (pred_ptr->args.regex, pathname, len, 0, (struct re_registers *) NULL) == len) return (true); @@ -1646,13 +1141,15 @@ pred_regex (const char *pathname, struct stat *stat_buf, struct predicate *pred_ } boolean -pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_size (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - uintmax_t f_val; + unsigned long f_val; - (void) pathname; - f_val = ((stat_buf->st_size / pred_ptr->args.size.blocksize) - + (stat_buf->st_size % pred_ptr->args.size.blocksize != 0)); + f_val = (stat_buf->st_size + pred_ptr->args.size.blocksize - 1) + / pred_ptr->args.size.blocksize; switch (pred_ptr->args.size.kind) { case COMP_GT: @@ -1672,57 +1169,22 @@ pred_size (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_samefile (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) -{ - /* Potential optimisation: because of the loop protection, we always - * know the device of the current directory, hence the device number - * of the file we're currently considering. If -L is not in effect, - * and the device number of the file we're looking for is not the - * same as the device number of the current directory, this - * predicate cannot return true. Hence there would be no need to - * stat the file we're looking at. - */ - (void) pathname; - - /* We will often still have an fd open on the file under consideration, - * but that's just to ensure inode number stability by maintaining - * a reference to it; we don't need the file for anything else. - */ - return stat_buf->st_ino == pred_ptr->args.samefileid.ino - && stat_buf->st_dev == pred_ptr->args.samefileid.dev; -} - -boolean -pred_true (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_true (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - (void) stat_buf; - (void) pred_ptr; - return true; + return (true); } boolean -pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_type (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - mode_t mode; - mode_t type = pred_ptr->args.type; - - assert (state.have_type); - - if (0 == state.type) - { - /* This can sometimes happen with broken NFS servers. - * See Savannah bug #16378. - */ - return false; - } - - (void) pathname; - - if (state.have_stat) - mode = stat_buf->st_mode; - else - mode = state.type; + unsigned long mode = stat_buf->st_mode; + unsigned long type = pred_ptr->args.type; #ifndef S_IFMT /* POSIX system; check `mode' the slow way. */ @@ -1739,9 +1201,6 @@ pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_p #ifdef S_IFSOCK || (S_ISSOCK (mode) && type == S_IFSOCK) #endif -#ifdef S_IFDOOR - || (S_ISDOOR (mode) && type == S_IFDOOR) -#endif ) #else /* S_IFMT */ /* Unix system; check `mode' the fast way. */ @@ -1753,21 +1212,23 @@ pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_uid (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; - switch (pred_ptr->args.numinfo.kind) + switch (pred_ptr->args.info.kind) { case COMP_GT: - if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid > pred_ptr->args.info.l_val) return (true); break; case COMP_LT: - if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid < pred_ptr->args.info.l_val) return (true); break; case COMP_EQ: - if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val) + if (stat_buf->st_uid == pred_ptr->args.info.l_val) return (true); break; } @@ -1775,29 +1236,39 @@ pred_uid (const char *pathname, struct stat *stat_buf, struct predicate *pred_pt } boolean -pred_used (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_used (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct timespec delta, at, ct; + time_t delta; - (void) pathname; - - /* TODO: this needs to be retested carefully (manually, if necessary) */ - at = get_stat_atime(stat_buf); - ct = get_stat_ctime(stat_buf); - delta.tv_sec = at.tv_sec - ct.tv_sec; - delta.tv_nsec = at.tv_nsec - ct.tv_nsec; - if (delta.tv_nsec < 0) + delta = stat_buf->st_atime - stat_buf->st_ctime; /* Use difftime? */ + switch (pred_ptr->args.info.kind) { - delta.tv_nsec += 1000000000; - delta.tv_sec -= 1; + case COMP_GT: + if (delta > (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_LT: + if (delta < (time_t) pred_ptr->args.info.l_val) + return (true); + break; + case COMP_EQ: + if ((delta >= (time_t) pred_ptr->args.info.l_val) + && (delta < (time_t) pred_ptr->args.info.l_val + DAYSECS)) + return (true); + break; } - return pred_timewindow(delta, pred_ptr, DAYSECS); + return (false); } boolean -pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_user (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - (void) pathname; if (pred_ptr->args.uid == stat_buf->st_uid) return (true); else @@ -1805,40 +1276,24 @@ pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_p } boolean -pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) +pred_xtype (pathname, stat_buf, pred_ptr) + char *pathname; + struct stat *stat_buf; + struct predicate *pred_ptr; { - struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */ - int (*ystat) (const char*, struct stat *p); + struct stat sbuf; + int (*ystat) (); - /* If we would normally stat the link itself, stat the target instead. - * If we would normally follow the link, stat the link itself instead. - */ - if (following_links()) - ystat = optionp_stat; - else - ystat = optionl_stat; - - set_stat_placeholders(&sbuf); - if ((*ystat) (state.rel_pathname, &sbuf) != 0) + ystat = xstat == lstat ? stat : lstat; + if ((*ystat) (rel_pathname, &sbuf) != 0) { - if (following_links() && errno == ENOENT) - { - /* If we failed to follow the symlink, - * fall back on looking at the symlink itself. - */ - /* Mimic behavior of ls -lL. */ - return (pred_type (pathname, stat_buf, pred_ptr)); - } - else - { - error (0, errno, "%s", safely_quote_err_filename(0, pathname)); - state.exit_status = 1; - } - return false; + if (ystat == stat && errno == ENOENT) + /* Mimic behavior of ls -lL. */ + return (pred_type (pathname, stat_buf, pred_ptr)); + error (0, errno, "%s", pathname); + exit_status = 1; + return (false); } - /* Now that we have our stat() information, query it in the same - * way that -type does. - */ return (pred_type (pathname, &sbuf, pred_ptr)); } @@ -1860,84 +1315,21 @@ pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ zero, and the exit arg (status high) is 0. Otherwise return false, possibly printing an error message. */ - static boolean -prep_child_for_exec (boolean close_stdin, int dirfd) -{ - boolean ok = true; - if (close_stdin) - { - const char inputfile[] = "/dev/null"; - - if (close(0) < 0) - { - error(0, errno, _("Cannot close standard input")); - ok = false; - } - else - { - if (open(inputfile, O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ) < 0) - { - /* This is not entirely fatal, since - * executing the child with a closed - * stdin is almost as good as executing it - * with its stdin attached to /dev/null. - */ - error (0, errno, "%s", safely_quote_err_filename(0, inputfile)); - /* do not set ok=false, it is OK to continue anyway. */ - } - } - } - - /* Even if DebugSearch is set, don't announce our change of - * directory, since we're not going to emit a subsequent - * announcement of a call to stat() anyway, as we're about to exec - * something. - */ - if (dirfd != AT_FDCWD) - { - assert (dirfd >= 0); - if (0 != fchdir(dirfd)) - { - /* If we cannot execute our command in the correct directory, - * we should not execute it at all. - */ - error(0, errno, _("Failed to change directory")); - ok = false; - } - } - return ok; -} - - - -int -launch (const struct buildcmd_control *ctl, - struct buildcmd_state *buildstate) +launch (pred_ptr) + struct predicate *pred_ptr; { - int wait_status; - pid_t child_pid; + int status; + pid_t wait_ret, child_pid; + struct exec_val *execp; /* Pointer for efficiency. */ static int first_time = 1; - const struct exec_val *execp = buildstate->usercontext; - if (!execp->use_current_dir) - { - assert (starting_desc >= 0); - assert (execp->dirfd == starting_desc); - } - - - /* Null terminate the arg list. */ - bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false); - + execp = &pred_ptr->args.exec_vec; + /* Make sure output of command doesn't get mixed with find output. */ fflush (stdout); fflush (stderr); - + /* Make sure to listen for the kids. */ if (first_time) { @@ -1947,275 +1339,150 @@ launch (const struct buildcmd_control *ctl, child_pid = fork (); if (child_pid == -1) - error (1, errno, _("cannot fork")); + error (1, errno, "cannot fork"); if (child_pid == 0) { - /* We are the child. */ - assert (starting_desc >= 0); - if (!prep_child_for_exec(execp->close_stdin, execp->dirfd)) + /* We be the child. */ +#ifndef HAVE_FCHDIR + if (chdir (starting_dir) < 0) { - _exit(1); + error (0, errno, "%s", starting_dir); + _exit (1); } - - execvp (buildstate->cmd_argv[0], buildstate->cmd_argv); - error (0, errno, "%s", - safely_quote_err_filename(0, buildstate->cmd_argv[0])); +#else + if (fchdir (starting_desc) < 0) + { + error (0, errno, "cannot return to starting directory"); + _exit (1); + } +#endif + execvp (execp->vec[0], execp->vec); + error (0, errno, "%s", execp->vec[0]); _exit (1); } - - /* In parent; set up for next time. */ - bc_clear_args(ctl, buildstate); - - - while (waitpid (child_pid, &wait_status, 0) == (pid_t) -1) + wait_ret = wait (&status); + if (wait_ret == -1) { - if (errno != EINTR) - { - error (0, errno, _("error waiting for %s"), - safely_quote_err_filename(0, buildstate->cmd_argv[0])); - state.exit_status = 1; - return 0; /* FAIL */ - } + error (0, errno, "error waiting for %s", execp->vec[0]); + exit_status = 1; + return (false); } - - if (WIFSIGNALED (wait_status)) + if (wait_ret != child_pid) { - error (0, 0, _("%s terminated by signal %d"), - quotearg_n_style(0, options.err_quoting_style, - buildstate->cmd_argv[0]), - WTERMSIG (wait_status)); - - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - - return 1; /* OK */ + error (0, 0, "wait got pid %d, expected pid %d", wait_ret, child_pid); + exit_status = 1; + return (false); } - - if (0 == WEXITSTATUS (wait_status)) + if (WIFSTOPPED (status)) { - return 1; /* OK */ + error (0, 0, "%s stopped by signal %d", + execp->vec[0], WSTOPSIG (status)); + exit_status = 1; + return (false); } - else + if (WIFSIGNALED (status)) { - if (execp->multiple) - { - /* -exec \; just returns false if the invoked command fails. - * -exec {} + returns true if the invoked command fails, but - * sets the program exit status. - */ - state.exit_status = 1; - } - return 0; /* FAIL */ + error (0, 0, "%s terminated by signal %d", + execp->vec[0], WTERMSIG (status)); + exit_status = 1; + return (false); } - + return (!WEXITSTATUS (status)); } - /* Return a static string formatting the time WHEN according to the - * strftime format character KIND. - * - * This function contains a number of assertions. These look like - * runtime checks of the results of computations, which would be a - * problem since external events should not be tested for with - * "assert" (instead you should use "if"). However, they are not - * really runtime checks. The assertions actually exist to verify - * that the various buffers are correctly sized. - */ + strftime format character KIND. */ + static char * -format_date (struct timespec ts, int kind) -{ - /* In theory, we use an extra 10 characters for 9 digits of - * nanoseconds and 1 for the decimal point. However, the real - * world is more complex than that. - * - * For example, some systems return junk in the tv_nsec part of - * st_birthtime. An example of this is the NetBSD-4.0-RELENG kernel - * (at Sat Mar 24 18:46:46 2007) running a NetBSD-3.1-RELEASE - * runtime and examining files on an msdos filesytem. So for that - * reason we set NS_BUF_LEN to 32, which is simply "long enough" as - * opposed to "exactly the right size". Note that the behaviour of - * NetBSD appears to be a result of the use of uninitialised data, - * as it's not 100% reproducible (more like 25%). - */ - enum { - NS_BUF_LEN = 32, - DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/ - }; - static char buf[128u+10u + MAX(DATE_LEN_PERCENT_APLUS, - MAX (LONGEST_HUMAN_READABLE + 2, NS_BUF_LEN+64+200))]; - char ns_buf[NS_BUF_LEN]; /* -.9999999990 (- sign can happen!)*/ - int charsprinted, need_ns_suffix; - struct tm *tm; - char fmt[6]; - - /* human_readable() assumes we pass a buffer which is at least as - * long as LONGEST_HUMAN_READABLE. We use an assertion here to - * ensure that no nasty unsigned overflow happend in our calculation - * of the size of buf. Do the assertion here rather than in the - * code for %@ so that we find the problem quickly if it exists. If - * you want to submit a patch to move this into the if statement, go - * ahead, I'll apply it. But include performance timings - * demonstrating that the performance difference is actually - * measurable. - */ - verify (sizeof(buf) >= LONGEST_HUMAN_READABLE); - - charsprinted = 0; - need_ns_suffix = 0; - - /* Format the main part of the time. */ - if (kind == '+') +format_date (when, kind) + time_t when; + int kind; +{ + static char fmt[3]; + static char buf[64]; /* More than enough space. */ + + if (kind == '@') { - strcpy (fmt, "%F+%T"); - need_ns_suffix = 1; + sprintf (buf, "%ld", when); + return (buf); } else { fmt[0] = '%'; fmt[1] = kind; fmt[2] = '\0'; - - /* %a, %c, and %t are handled in ctime_format() */ - switch (kind) - { - case 'S': - case 'T': - case 'X': - case '@': - need_ns_suffix = 1; - break; - default: - need_ns_suffix = 0; - break; - } + if (strftime (buf, sizeof (buf), fmt, localtime (&when))) + return (buf); } + return ""; +} + +#ifdef DEBUG +/* Return a pointer to the string representation of + the predicate function PRED_FUNC. */ - if (need_ns_suffix) - { - /* Format the nanoseconds part. Leave a trailing zero to - * discourage people from writing scripts which extract the - * fractional part of the timestamp by using column offsets. - * The reason for discouraging this is that in the future, the - * granularity may not be nanoseconds. - */ - ns_buf[0] = 0; - charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec); - assert (charsprinted < NS_BUF_LEN); - } +char * +find_pred_name (pred_func) + PFB pred_func; +{ + int i; - if (kind != '@' - && (tm = localtime (&ts.tv_sec)) - && strftime (buf, sizeof buf, fmt, tm)) - { - /* For %AS, %CS, %TS, add the fractional part of the seconds - * information. - */ - if (need_ns_suffix) - { - assert ((sizeof buf - strlen(buf)) > strlen(ns_buf)); - strcat(buf, ns_buf); - } - return buf; - } - else - { - uintmax_t w = ts.tv_sec; - size_t used, len, remaining; - - /* XXX: note that we are negating an unsigned type which is the - * widest possible unsigned type. - */ - char *p = human_readable (ts.tv_sec < 0 ? -w : w, buf + 1, - human_ceiling, 1, 1); - assert (p > buf); - assert (p < (buf + (sizeof buf))); - if (ts.tv_sec < 0) - *--p = '-'; /* XXX: Ugh, relying on internal details of human_readable(). */ - - /* Add the nanoseconds part. Because we cannot enforce a - * particlar implementation of human_readable, we cannot assume - * any particular value for (p-buf). So we need to be careful - * that there is enough space remaining in the buffer. - */ - if (need_ns_suffix) - { - len = strlen(p); - used = (p-buf) + len; /* Offset into buf of current end */ - assert (sizeof buf > used); /* Ensure we can perform subtraction safely. */ - remaining = sizeof buf - used - 1u; /* allow space for NUL */ - - if (strlen(ns_buf) >= remaining) - { - error(0, 0, - "charsprinted=%ld but remaining=%lu: ns_buf=%s", - (long)charsprinted, (unsigned long)remaining, ns_buf); - } - assert (strlen(ns_buf) < remaining); - strcat(p, ns_buf); - } - return p; - } + for (i = 0; pred_table[i].pred_func != 0; i++) + if (pred_table[i].pred_func == pred_func) + break; + return (pred_table[i].pred_name); } -static const char *weekdays[] = - { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }; -static char * months[] = - { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" - }; +static char * +type_name (type) + short type; +{ + int i; + for (i = 0; type_table[i].type != (short) -1; i++) + if (type_table[i].type == type) + break; + return (type_table[i].type_name); +} static char * -ctime_format (struct timespec ts) +prec_name (prec) + short prec; { - const struct tm * ptm; -#define TIME_BUF_LEN 1024u - static char resultbuf[TIME_BUF_LEN]; - int nout; - - ptm = localtime(&ts.tv_sec); - if (ptm) - { - assert (ptm->tm_wday >= 0); - assert (ptm->tm_wday < 7); - assert (ptm->tm_mon >= 0); - assert (ptm->tm_mon < 12); - assert (ptm->tm_hour >= 0); - assert (ptm->tm_hour < 24); - assert (ptm->tm_min < 60); - assert (ptm->tm_sec <= 61); /* allows 2 leap seconds. */ - - /* wkday mon mday hh:mm:ss.nnnnnnnnn yyyy */ - nout = snprintf(resultbuf, TIME_BUF_LEN, - "%3s %3s %2d %02d:%02d:%02d.%010ld %04d", - weekdays[ptm->tm_wday], - months[ptm->tm_mon], - ptm->tm_mday, - ptm->tm_hour, - ptm->tm_min, - ptm->tm_sec, - (long int)ts.tv_nsec, - 1900 + ptm->tm_year); - - assert (nout < TIME_BUF_LEN); - return resultbuf; - } - else - { - /* The time cannot be represented as a struct tm. - Output it as an integer. */ - return format_date (ts, '@'); - } + int i; + + for (i = 0; prec_table[i].prec != (short) -1; i++) + if (prec_table[i].prec == prec) + break; + return (prec_table[i].prec_name); +} + +/* Walk the expression tree NODE to stdout. + INDENT is the number of levels to indent the left margin. */ + +void +print_tree (node, indent) + struct predicate *node; + int indent; +{ + int i; + + if (node == NULL) + return; + for (i = 0; i < indent; i++) + printf (" "); + printf ("pred = %s type = %s prec = %s addr = %x\n", + find_pred_name (node->pred_func), + type_name (node->p_type), prec_name (node->p_prec), node); + for (i = 0; i < indent; i++) + printf (" "); + printf ("left:\n"); + print_tree (node->pred_left, indent + 1); + for (i = 0; i < indent; i++) + printf (" "); + printf ("right:\n"); + print_tree (node->pred_right, indent + 1); } /* Copy STR into BUF and trim blanks from the end of BUF. @@ -2239,8 +1506,10 @@ blank_rtrim (str, buf) } /* Print out the predicate list starting at NODE. */ + void -print_list (FILE *fp, struct predicate *node) +print_list (node) + struct predicate *node; { struct predicate *cur; char name[256]; @@ -2248,161 +1517,9 @@ print_list (FILE *fp, struct predicate *node) cur = node; while (cur != NULL) { - fprintf (fp, "[%s] ", blank_rtrim (cur->p_name, name)); + printf ("%s ", blank_rtrim (find_pred_name (cur->pred_func), name)); cur = cur->pred_next; } - fprintf (fp, "\n"); -} - -/* Print out the predicate list starting at NODE. */ -static void -print_parenthesised(FILE *fp, struct predicate *node) -{ - int parens = 0; - - if (node) - { - if ((pred_is(node, pred_or) || pred_is(node, pred_and)) - && node->pred_left == NULL) - { - /* We print "<nothing> or X" as just "X" - * We print "<nothing> and X" as just "X" - */ - print_parenthesised(fp, node->pred_right); - } - else - { - if (node->pred_left || node->pred_right) - parens = 1; - - if (parens) - fprintf(fp, "%s", " ( "); - print_optlist(fp, node); - if (parens) - fprintf(fp, "%s", " ) "); - } - } -} - -void -print_optlist (FILE *fp, const struct predicate *p) -{ - if (p) - { - print_parenthesised(fp, p->pred_left); - fprintf (fp, - "%s%s", - p->need_stat ? "[call stat] " : "", - p->need_type ? "[need type] " : ""); - print_predicate(fp, p); - fprintf(fp, " [%g] ", p->est_success_rate); - if (options.debug_options & DebugSuccessRates) - { - fprintf(fp, "[%ld/%ld", p->perf.successes, p->perf.visits); - if (p->perf.visits) - { - double real_rate = (double)p->perf.successes / (double)p->perf.visits; - fprintf(fp, "=%g] ", real_rate); - } - else - { - fprintf(fp, "=_] "); - } - } - print_parenthesised(fp, p->pred_right); - } -} - -void show_success_rates(const struct predicate *p) -{ - if (options.debug_options & DebugSuccessRates) - { - fprintf(stderr, "Predicate success rates after completion:\n"); - print_optlist(stderr, p); - fprintf(stderr, "\n"); - } -} - - - - -#ifdef _NDEBUG -/* If _NDEBUG is defined, the assertions will do nothing. Hence - * there is no point in having a function body for pred_sanity_check() - * if that preprocessor macro is defined. - */ -void -pred_sanity_check(const struct predicate *predicates) -{ - /* Do nothing, since assert is a no-op with _NDEBUG set */ - return; -} -#else -void -pred_sanity_check(const struct predicate *predicates) -{ - const struct predicate *p; - - for (p=predicates; p != NULL; p=p->pred_next) - { - /* All predicates must do something. */ - assert (p->pred_func != NULL); - - /* All predicates must have a parser table entry. */ - assert (p->parser_entry != NULL); - - /* If the parser table tells us that just one predicate function is - * possible, verify that that is still the one that is in effect. - * If the parser has NULL for the predicate function, that means that - * the parse_xxx function fills it in, so we can't check it. - */ - if (p->parser_entry->pred_func) - { - assert (p->parser_entry->pred_func == p->pred_func); - } - - switch (p->parser_entry->type) - { - /* Options all take effect during parsing, so there should - * be no predicate entries corresponding to them. Hence we - * should not see any ARG_OPTION or ARG_POSITIONAL_OPTION - * items. - * - * This is a silly way of coding this test, but it prevents - * a compiler warning (i.e. otherwise it would think that - * there would be case statements missing). - */ - case ARG_OPTION: - case ARG_POSITIONAL_OPTION: - assert (p->parser_entry->type != ARG_OPTION); - assert (p->parser_entry->type != ARG_POSITIONAL_OPTION); - break; - - case ARG_ACTION: - assert(p->side_effects); /* actions have side effects. */ - if (!pred_is(p, pred_prune) && !pred_is(p, pred_quit)) - { - /* actions other than -prune and -quit should - * inhibit the default -print - */ - assert (p->no_default_print); - } - break; - - /* We happen to know that the only user of ARG_SPECIAL_PARSE - * is a test, so handle it like ARG_TEST. - */ - case ARG_SPECIAL_PARSE: - case ARG_TEST: - case ARG_PUNCTUATION: - case ARG_NOOP: - /* Punctuation and tests should have no side - * effects and not inhibit default print. - */ - assert (!p->no_default_print); - assert (!p->side_effects); - break; - } - } + printf ("\n"); } -#endif +#endif /* DEBUG */ |