Support for sub-second timestamp resolution in -newer, -anewer and -cnewer plus the various -printf actions

This commit is contained in:
James Youngman
2007-01-21 23:26:21 +00:00
parent f0dd7471e6
commit a4718831e1
9 changed files with 417 additions and 170 deletions

View File

@@ -1,3 +1,28 @@
2007-01-16 James Youngman <jay@gnu.org>
(ctime_format): format the time manually (rather than using ctime)
in order to include the sub-second part of the time.
(weekdays, months): new static variables used bu ctime_format.
(format_date): append a the sub-second part of the timestamp to
the seconds part of date/time output.
2007-01-15 James Youngman <jay@gnu.org>
* find/defs.h (time_val): define struct; use timespec to hold time
in the 'reftime' member.
(args): use struct timespec instead of time_t for predicates
-newer, -anewer, -cnewer. * find/parser.c (includes): include
stat-time.h.
(parse_anewer, parse_cnewer, parse_newer): use struct timespec to
hold timestamps. * find/pred.c (compare_ts): new function for
comparing timestamps in struct timespec.
(pred_anewer, pred_cnewer, pred_newer): use compare_ts() to
compare timestamps (hence takinng acoung of sub-second
granularity).
* find/ftsfind.c: Various improvements to comments.
(is_fts_enabled): Newline before function name to comply with GNU
coding standard.
2007-01-13 James Youngman <jay@gnu.org>
* xargs/xargs.c (read_line): Fixed Savannah bug# 18714; VT and FF

12
NEWS
View File

@@ -12,6 +12,18 @@ this change will happen. We now issue a warning indicating that the
change has already happened (in 4.3.x only, there is no plan to make
this change in the 4.2.x series).
The tests -newer, -anewer and -cnewer now support sub-second
timestamps. This support does not yet exist for the tests -mtime,
-atime, or -ctime, nor does it exist for -amin, -cmin, or -mmin.
The -printf format specifiers also support sub-second timestamps:
atime ctime mtime
%a %c %t
%AS %CS %TS
%AT %CT %TT
%A+ %C+ %T+
%AX %CX %TX
** Bug Fixes

View File

@@ -488,33 +488,54 @@ fi
# Check dynamic linker characteristics
# Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER.
# Unlike libtool.m4, here we don't care about _all_ names of the library, but
# only about the one the linker finds when passed -lNAME. This is the last
# element of library_names_spec in libtool.m4, or possibly two of them if the
# linker has special search rules.
library_names_spec= # the last element of library_names_spec in libtool.m4
libname_spec='lib$name'
case "$host_os" in
aix3*)
library_names_spec='$libname.a'
;;
aix4* | aix5*)
library_names_spec='$libname$shrext'
;;
amigaos*)
library_names_spec='$libname.a'
;;
beos*)
library_names_spec='$libname$shrext'
;;
bsdi[45]*)
library_names_spec='$libname$shrext'
;;
cygwin* | mingw* | pw32*)
shrext=.dll
library_names_spec='$libname.dll.a $libname.lib'
;;
darwin* | rhapsody*)
shrext=.dylib
library_names_spec='$libname$shrext'
;;
dgux*)
library_names_spec='$libname$shrext'
;;
freebsd1*)
;;
kfreebsd*-gnu)
library_names_spec='$libname$shrext'
;;
freebsd* | dragonfly*)
case "$host_os" in
freebsd[123]*)
library_names_spec='$libname$shrext$versuffix' ;;
*)
library_names_spec='$libname$shrext' ;;
esac
;;
gnu*)
library_names_spec='$libname$shrext'
;;
hpux9* | hpux10* | hpux11*)
case $host_cpu in
@@ -528,10 +549,13 @@ case "$host_os" in
shrext=.sl
;;
esac
library_names_spec='$libname$shrext'
;;
interix3*)
library_names_spec='$libname$shrext'
;;
irix5* | irix6* | nonstopux*)
library_names_spec='$libname$shrext'
case "$host_os" in
irix5* | nonstopux*)
libsuff= shlibsuff=
@@ -549,40 +573,56 @@ case "$host_os" in
linux*oldld* | linux*aout* | linux*coff*)
;;
linux*)
library_names_spec='$libname$shrext'
;;
knetbsd*-gnu)
library_names_spec='$libname$shrext'
;;
netbsd*)
library_names_spec='$libname$shrext'
;;
newsos6)
library_names_spec='$libname$shrext'
;;
nto-qnx*)
library_names_spec='$libname$shrext'
;;
openbsd*)
library_names_spec='$libname$shrext$versuffix'
;;
os2*)
libname_spec='$name'
shrext=.dll
library_names_spec='$libname.a'
;;
osf3* | osf4* | osf5*)
library_names_spec='$libname$shrext'
;;
solaris*)
library_names_spec='$libname$shrext'
;;
sunos4*)
library_names_spec='$libname$shrext$versuffix'
;;
sysv4 | sysv4.3*)
library_names_spec='$libname$shrext'
;;
sysv4*MP*)
library_names_spec='$libname$shrext'
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
library_names_spec='$libname$shrext'
;;
uts4*)
library_names_spec='$libname$shrext'
;;
esac
sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"`
shlibext=`echo "$shrext" | sed -e 's,^\.,,'`
escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF
@@ -596,6 +636,12 @@ libext="$libext"
# Shared library suffix (normally "so").
shlibext="$shlibext"
# Format of library name prefix.
libname_spec="$escaped_libname_spec"
# Library names that the linker finds when passed -lNAME.
library_names_spec="$escaped_library_names_spec"
# Flag to hardcode \$libdir into a binary during linking.
# This must work even if \$libdir does not exist.
hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec"

View File

@@ -64,6 +64,7 @@ extern int errno;
#endif
#include "regex.h"
#include "timespec.h"
#ifndef S_IFLNK
#define lstat stat
@@ -220,7 +221,13 @@ struct size_val
uintmax_t size;
};
struct time_val
{
enum comparison_type kind;
struct timespec ts;
};
#include "buildcmd.h"
struct exec_val
@@ -333,12 +340,11 @@ struct predicate
char *str; /* fstype [i]lname [i]name [i]path */
struct re_pattern_buffer *regex; /* regex */
struct exec_val exec_vec; /* exec ok */
struct long_val info; /* atime ctime gid inum links mtime
size uid */
struct long_val numinfo; /* gid inum links uid */
struct size_val size; /* size */
uid_t uid; /* user */
gid_t gid; /* group */
time_t time; /* newer */
struct time_val reftime; /* newer anewer cnewer mtime atime ctime mmin amin cmin */
struct perm_val perm; /* perm */
struct dir_id fileid; /* samefile */
mode_t type; /* type */

View File

@@ -35,6 +35,7 @@
#include "nextelem.h"
#include "stdio-safer.h"
#include "regextype.h"
#include "stat-time.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
@@ -491,7 +492,6 @@ estimate_timestamp_success_rate(time_t when)
return estimate_file_age_success_rate(num_days);
}
/* The parsers are responsible to continue scanning ARGV for
their arguments. Each parser knows what is and isn't
allowed for itself.
@@ -502,28 +502,7 @@ estimate_timestamp_success_rate(time_t when)
The predicate structure is updated with the new information. */
static boolean
parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
our_pred->est_success_rate = estimate_file_age_success_rate(num);
(*arg_ptr)++;
return true;
}
static boolean
parse_and (const struct parser_table* entry, char **argv, int *arg_ptr)
{
@@ -551,7 +530,8 @@ parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
our_pred->args.reftime.kind = COMP_GT;
our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
(*arg_ptr)++;
return true;
@@ -573,28 +553,6 @@ parse_close (const struct parser_table* entry, char **argv, int *arg_ptr)
return true;
}
static boolean
parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
enum comparison_type c_type;
time_t t;
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
our_pred->est_success_rate = estimate_file_age_success_rate(num);
(*arg_ptr)++;
return true;
}
static boolean
parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
{
@@ -606,7 +564,8 @@ parse_cnewer (const struct parser_table* entry, char **argv, int *arg_ptr)
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
our_pred->args.reftime.kind = COMP_GT;
our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
(*arg_ptr)++;
return true;
@@ -849,7 +808,7 @@ static boolean
parse_gid (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *p = insert_num (argv, arg_ptr, entry);
p->est_success_rate = (p->args.info.l_val < 100) ? 0.99 : 0.2;
p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
return p;
}
@@ -876,7 +835,7 @@ parse_group (const struct parser_table* entry, char **argv, int *arg_ptr)
}
our_pred = insert_primary (entry);
our_pred->args.gid = gid;
our_pred->est_success_rate = (our_pred->args.info.l_val < 100) ? 0.99 : 0.2;
our_pred->est_success_rate = (our_pred->args.numinfo.l_val < 100) ? 0.99 : 0.2;
(*arg_ptr)++;
return true;
}
@@ -1059,9 +1018,9 @@ static boolean
parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *p = insert_num (argv, arg_ptr, entry);
if (p->args.info.l_val == 1)
if (p->args.numinfo.l_val == 1)
p->est_success_rate = 0.99;
else if (p->args.info.l_val == 2)
else if (p->args.numinfo.l_val == 2)
p->est_success_rate = 0.01;
else
p->est_success_rate = 1e-3;
@@ -1136,9 +1095,10 @@ parse_mindepth (const struct parser_table* entry, char **argv, int *arg_ptr)
(*arg_ptr)++;
return parse_noop(entry, argv, arg_ptr);
}
static boolean
parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
do_parse_xmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *our_pred;
uintmax_t num;
@@ -1147,17 +1107,36 @@ parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
if (!get_num_days (argv[*arg_ptr], &num, &c_type))
/* XXX: need to capture sub-second part */
if (!get_num_days (argv[*arg_ptr], &num, &c_type)) /* actually not a number of days... */
return false;
t = options.cur_day_start + DAYSECS - num * 60;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
our_pred->args.reftime.kind = c_type;
our_pred->args.reftime.ts.tv_sec = t;
our_pred->args.reftime.ts.tv_nsec = 0; /* XXX: need to capture this. */
our_pred->est_success_rate = estimate_file_age_success_rate(num);
(*arg_ptr)++;
return true;
}
static boolean
parse_amin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return do_parse_xmin(entry, argv, arg_ptr);
}
static boolean
parse_cmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return do_parse_xmin(entry, argv, arg_ptr);
}
static boolean
parse_mmin (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return do_parse_xmin(entry, argv, arg_ptr);
}
static boolean
parse_name (const struct parser_table* entry, char **argv, int *arg_ptr)
@@ -1211,7 +1190,8 @@ parse_newer (const struct parser_table* entry, char **argv, int *arg_ptr)
if ((*options.xstat) (argv[*arg_ptr], &stat_newer))
error (1, errno, "%s", argv[*arg_ptr]);
our_pred = insert_primary (entry);
our_pred->args.time = stat_newer.st_mtime;
our_pred->args.reftime.ts = get_stat_mtime(&stat_newer);
our_pred->args.reftime.kind = COMP_GT;
our_pred->est_success_rate = estimate_timestamp_success_rate(stat_newer.st_mtime);
(*arg_ptr)++;
return true;
@@ -1850,7 +1830,7 @@ static boolean
parse_uid (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *p = insert_num (argv, arg_ptr, entry);
p->est_success_rate = (p->args.info.l_val < 100) ? 0.99 : 0.2;
p->est_success_rate = (p->args.numinfo.l_val < 100) ? 0.99 : 0.2;
return p;
}
@@ -1864,13 +1844,15 @@ parse_used (const struct parser_table* entry, char **argv, int *arg_ptr)
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
/* XXX: need to capture nanoseconds part */
if (!get_num (argv[*arg_ptr], &num_days, &c_type))
return false;
t = num_days * DAYSECS;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
our_pred->args.reftime.kind = c_type;
our_pred->args.reftime.ts.tv_sec = t;
our_pred->args.reftime.ts.tv_nsec = 0; /* XXX: capture ns part */
our_pred->est_success_rate = estimate_file_age_success_rate(num_days);
(*arg_ptr)++;
return true;
@@ -2129,7 +2111,8 @@ insert_fprintf (FILE *fp, const struct parser_table *entry, PRED_FUNC func, char
our_pred->args.printf_vec.quote_opts = clone_quoting_options (NULL);
our_pred->need_type = false;
our_pred->need_stat = false;
our_pred->p_cost = NeedsNothing;
segmentp = &our_pred->args.printf_vec.segment;
*segmentp = NULL;
@@ -2628,6 +2611,7 @@ insert_exec_ok (const char *action, const struct parser_table *entry, char **arg
/* Get a number of days and comparison type.
- but beware, this is also used to collect numbers of minutes -
STR is the ASCII representation.
Set *NUM_DAYS to the number of days, taken as being from
the current moment (or possibly midnight). Thus the sense of the
@@ -2653,7 +2637,7 @@ get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type)
return r;
}
/* Insert a time predicate PRED.
/* Insert a time predicate based on the information in ENTRY.
ARGV is a pointer to the argument array.
ARG_PTR is a pointer to an index into the array, incremented if
all went well.
@@ -2675,6 +2659,8 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
if ((argv == NULL) || (argv[*arg_ptr] == NULL))
return false;
/* XXX: capture sub-second part */
if (!get_num_days (argv[*arg_ptr], &num_days, &c_type))
return false;
@@ -2700,9 +2686,9 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
}
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.negative = t < 0;
our_pred->args.info.l_val = t;
our_pred->args.reftime.kind = c_type;
our_pred->args.reftime.ts.tv_sec = t;
our_pred->args.reftime.ts.tv_nsec = 0; /* XXX: capture this part */
our_pred->est_success_rate = estimate_file_age_success_rate(num_days);
(*arg_ptr)++;
@@ -2714,14 +2700,14 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr)
((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
(c_type == COMP_GT) ? " >" :
((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? ">=" : " ?")));
t = our_pred->args.info.l_val;
fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.info.l_val, ctime (&t));
t = our_pred->args.reftime.ts.tv_sec;
fprintf (stderr, "%ju %s", (uintmax_t) our_pred->args.reftime.ts.tv_sec, ctime (&t));
if (c_type == COMP_EQ)
{
t = our_pred->args.info.l_val += DAYSECS;
t = our_pred->args.reftime.ts.tv_sec += DAYSECS;
fprintf (stderr, " < %ju %s",
(uintmax_t) our_pred->args.info.l_val, ctime (&t));
our_pred->args.info.l_val -= DAYSECS;
(uintmax_t) our_pred->args.reftime.ts.tv_sec, ctime (&t));
our_pred->args.reftime.ts.tv_sec -= DAYSECS;
}
}
@@ -2785,8 +2771,8 @@ insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
if (!get_num (argv[*arg_ptr], &num, &c_type))
return NULL;
our_pred = insert_primary (entry);
our_pred->args.info.kind = c_type;
our_pred->args.info.l_val = num;
our_pred->args.numinfo.kind = c_type;
our_pred->args.numinfo.l_val = num;
(*arg_ptr)++;
if (options.debug_options & DebugExpressionTree)
@@ -2797,7 +2783,7 @@ insert_num (char **argv, int *arg_ptr, const struct parser_table *entry)
((c_type == COMP_LT) ? "lt" : ((c_type == COMP_EQ) ? "eq" : "?")),
(c_type == COMP_GT) ? " >" :
((c_type == COMP_LT) ? " <" : ((c_type == COMP_EQ) ? " =" : " ?")));
fprintf (stderr, "%ju\n", our_pred->args.info.l_val);
fprintf (stderr, "%ju\n", our_pred->args.numinfo.l_val);
}
return our_pred;
}

View File

@@ -29,6 +29,7 @@
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
#include <locale.h>
#include "xalloc.h"
#include "dirname.h"
#include "human.h"
@@ -39,6 +40,7 @@
#include "buildcmd.h"
#include "yesno.h"
#include "listfile.h"
#include "stat-time.h"
#if ENABLE_NLS
# include <libintl.h>
@@ -153,8 +155,8 @@
static boolean match_lname PARAMS((char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, boolean ignore_case));
static char *format_date PARAMS((time_t when, int kind));
static char *ctime_format PARAMS((time_t when));
static char *format_date PARAMS((struct timespec ts, int kind));
static char *ctime_format PARAMS((struct timespec ts));
#ifdef DEBUG
struct pred_assoc
@@ -225,6 +227,32 @@ struct pred_assoc pred_table[] =
};
#endif
/* Returns ts1 - ts2 */
static double ts_difference(struct timespec ts1,
struct timespec ts2)
{
double d = difftime(ts1.tv_sec, ts2.tv_sec)
+ (1.0e-9 * (ts1.tv_nsec - ts2.tv_nsec));
return d;
}
static int
compare_ts(struct timespec ts1,
struct timespec ts2)
{
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;
}
}
/* Predicate processing routines.
PATHNAME is the full pathname of the file being checked.
@@ -242,25 +270,22 @@ struct pred_assoc pred_table[] =
* COMP_EQ: less than WINDOW seconds after the specified time.
*/
static boolean
pred_timewindow(time_t the_time, struct predicate const *pred_ptr, int window)
pred_timewindow(struct timespec ts, struct predicate const *pred_ptr, int window)
{
switch (pred_ptr->args.info.kind)
double delta;
switch (pred_ptr->args.reftime.kind)
{
case COMP_GT:
if (the_time > (time_t) pred_ptr->args.info.l_val)
return true;
break;
return compare_ts(ts, pred_ptr->args.reftime.ts) > 0;
case COMP_LT:
if (the_time < (time_t) pred_ptr->args.info.l_val)
return true;
break;
return compare_ts(ts, pred_ptr->args.reftime.ts) < 0;
case COMP_EQ:
if ((the_time >= (time_t) pred_ptr->args.info.l_val)
&& (the_time < (time_t) pred_ptr->args.info.l_val + window))
return true;
break;
delta = ts_difference(ts, pred_ptr->args.reftime.ts);
return (delta >= 0.0 && delta < window);
}
return false;
}
@@ -268,7 +293,7 @@ boolean
pred_amin (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
return pred_timewindow(stat_buf->st_atime, pred_ptr, 60);
return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, 60);
}
boolean
@@ -292,17 +317,15 @@ boolean
pred_anewer (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
if (stat_buf->st_atime > pred_ptr->args.time)
return (true);
return (false);
assert(COMP_GT == pred_ptr->args.reftime.kind);
return compare_ts(get_stat_atime(stat_buf), pred_ptr->args.reftime.ts) > 0;
}
boolean
pred_atime (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
return pred_timewindow(stat_buf->st_atime, pred_ptr, DAYSECS);
return pred_timewindow(get_stat_atime(stat_buf), pred_ptr, DAYSECS);
}
boolean
@@ -319,7 +342,7 @@ boolean
pred_cmin (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
return pred_timewindow(stat_buf->st_ctime, pred_ptr, 60);
return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, 60);
}
boolean
@@ -327,10 +350,8 @@ pred_cnewer (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
if (stat_buf->st_ctime > pred_ptr->args.time)
return true;
else
return false;
assert(COMP_GT == pred_ptr->args.reftime.kind);
return compare_ts(get_stat_ctime(stat_buf), pred_ptr->args.reftime.ts) > 0;
}
boolean
@@ -351,7 +372,7 @@ boolean
pred_ctime (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
return pred_timewindow(stat_buf->st_ctime, pred_ptr, DAYSECS);
return pred_timewindow(get_stat_ctime(stat_buf), pred_ptr, DAYSECS);
}
boolean
@@ -595,7 +616,7 @@ do_fprintf(FILE *fp,
{
case 'a': /* atime in `ctime' format. */
/* UNTRUSTED, probably unexploitable */
fprintf (fp, segment->text, ctime_format (stat_buf->st_atime));
fprintf (fp, segment->text, ctime_format (get_stat_atime(stat_buf)));
break;
case 'b': /* size in 512-byte blocks */
/* UNTRUSTED, probably unexploitable */
@@ -606,7 +627,7 @@ do_fprintf(FILE *fp,
break;
case 'c': /* ctime in `ctime' format */
/* UNTRUSTED, probably unexploitable */
fprintf (fp, segment->text, ctime_format (stat_buf->st_ctime));
fprintf (fp, segment->text, ctime_format (get_stat_ctime(stat_buf)));
break;
case 'd': /* depth in search tree */
/* UNTRUSTED, probably unexploitable */
@@ -806,8 +827,9 @@ do_fprintf(FILE *fp,
case 't': /* mtime in `ctime' format */
/* UNTRUSTED, probably unexploitable */
fprintf (fp, segment->text, ctime_format (stat_buf->st_mtime));
fprintf (fp, segment->text, ctime_format (get_stat_mtime(stat_buf)));
break;
case 'u': /* user name */
/* trusted */
/* (well, the actual user is selected by the user on systems
@@ -897,18 +919,18 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
if ( (KIND_FORMAT == segment->segkind) && segment->format_char[1]) /* Component of date. */
{
time_t t;
struct timespec ts;
switch (segment->format_char[0])
{
case 'A':
t = stat_buf->st_atime;
ts = get_stat_atime(stat_buf);
break;
case 'C':
t = stat_buf->st_ctime;
ts = get_stat_ctime(stat_buf);
break;
case 'T':
t = stat_buf->st_mtime;
ts = get_stat_mtime(stat_buf);
break;
default:
assert(0);
@@ -920,7 +942,7 @@ pred_fprintf (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
*/
/* trusted */
fprintf (fp, segment->text,
format_date (t, segment->format_char[1]));
format_date (ts, segment->format_char[1]));
}
else
{
@@ -946,18 +968,18 @@ pred_gid (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
switch (pred_ptr->args.info.kind)
switch (pred_ptr->args.numinfo.kind)
{
case COMP_GT:
if (stat_buf->st_gid > pred_ptr->args.info.l_val)
if (stat_buf->st_gid > pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_LT:
if (stat_buf->st_gid < pred_ptr->args.info.l_val)
if (stat_buf->st_gid < pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_EQ:
if (stat_buf->st_gid == pred_ptr->args.info.l_val)
if (stat_buf->st_gid == pred_ptr->args.numinfo.l_val)
return (true);
break;
}
@@ -1002,18 +1024,18 @@ pred_inum (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
switch (pred_ptr->args.info.kind)
switch (pred_ptr->args.numinfo.kind)
{
case COMP_GT:
if (stat_buf->st_ino > pred_ptr->args.info.l_val)
if (stat_buf->st_ino > pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_LT:
if (stat_buf->st_ino < pred_ptr->args.info.l_val)
if (stat_buf->st_ino < pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_EQ:
if (stat_buf->st_ino == pred_ptr->args.info.l_val)
if (stat_buf->st_ino == pred_ptr->args.numinfo.l_val)
return (true);
break;
}
@@ -1035,18 +1057,18 @@ pred_links (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
switch (pred_ptr->args.info.kind)
switch (pred_ptr->args.numinfo.kind)
{
case COMP_GT:
if (stat_buf->st_nlink > pred_ptr->args.info.l_val)
if (stat_buf->st_nlink > pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_LT:
if (stat_buf->st_nlink < pred_ptr->args.info.l_val)
if (stat_buf->st_nlink < pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_EQ:
if (stat_buf->st_nlink == pred_ptr->args.info.l_val)
if (stat_buf->st_nlink == pred_ptr->args.numinfo.l_val)
return (true);
break;
}
@@ -1093,14 +1115,14 @@ boolean
pred_mmin (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
return pred_timewindow(stat_buf->st_mtime, pred_ptr, 60);
return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, 60);
}
boolean
pred_mtime (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
return pred_timewindow(stat_buf->st_mtime, pred_ptr, DAYSECS);
return pred_timewindow(get_stat_mtime(stat_buf), pred_ptr, DAYSECS);
}
boolean
@@ -1135,9 +1157,8 @@ pred_newer (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
if (stat_buf->st_mtime > pred_ptr->args.time)
return (true);
return (false);
assert(COMP_GT == pred_ptr->args.reftime.kind);
return compare_ts(get_stat_mtime(stat_buf), pred_ptr->args.reftime.ts) > 0;
}
boolean
@@ -1478,18 +1499,18 @@ boolean
pred_uid (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) pathname;
switch (pred_ptr->args.info.kind)
switch (pred_ptr->args.numinfo.kind)
{
case COMP_GT:
if (stat_buf->st_uid > pred_ptr->args.info.l_val)
if (stat_buf->st_uid > pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_LT:
if (stat_buf->st_uid < pred_ptr->args.info.l_val)
if (stat_buf->st_uid < pred_ptr->args.numinfo.l_val)
return (true);
break;
case COMP_EQ:
if (stat_buf->st_uid == pred_ptr->args.info.l_val)
if (stat_buf->st_uid == pred_ptr->args.numinfo.l_val)
return (true);
break;
}
@@ -1499,10 +1520,20 @@ pred_uid (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
boolean
pred_used (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
time_t delta;
struct timespec delta, at, ct;
(void) pathname;
delta = stat_buf->st_atime - stat_buf->st_ctime; /* Use difftime? */
/* XXX: 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.tv_nsec += 1000000000;
delta.tv_sec -= 1;
}
return pred_timewindow(delta, pred_ptr, DAYSECS);
}
@@ -1704,52 +1735,172 @@ launch (const struct buildcmd_control *ctl,
/* Return a static string formatting the time WHEN according to the
strftime format character KIND. */
* 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.
*/
static char *
format_date (time_t when, int kind)
format_date (struct timespec ts, int kind)
{
static char buf[MAX (LONGEST_HUMAN_READABLE + 2, 64)];
/* Use an extra 10 characters for 9 digits of nanoseconds and 1 for
* the decimal point
*/
enum {
NS_BUF_LEN = 12,
DATE_LEN_PERCENT_APLUS=21 /* length of result of %A+ (it's longer than %c)*/
};
static char buf[10u + MAX(DATE_LEN_PERCENT_APLUS, MAX (LONGEST_HUMAN_READABLE + 2, 64))];
char ns_buf[NS_BUF_LEN]; /* .9999999990 */
int charsprinted, need_ns_suffix;
struct tm *tm;
char fmt[6];
fmt[0] = '%';
fmt[1] = kind;
fmt[2] = '\0';
/* 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.
*/
assert(sizeof(buf) >= LONGEST_HUMAN_READABLE);
/* Format the main part of the time. */
if (kind == '+')
strcpy (fmt, "%F+%T");
if (kind != '@'
&& (tm = localtime (&when))
&& strftime (buf, sizeof buf, fmt, tm))
return buf;
{
strcpy (fmt, "%F+%T");
need_ns_suffix = 1;
}
else
{
uintmax_t w = when;
char *p = human_readable (when < 0 ? -w : w, buf + 1,
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':
need_ns_suffix = 1;
break;
default:
need_ns_suffix = 0;
break;
}
}
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.
*/
charsprinted = snprintf(ns_buf, NS_BUF_LEN, ".%09ld0", (long int)ts.tv_nsec);
assert(charsprinted < NS_BUF_LEN);
}
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);
if (when < 0)
*--p = '-';
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.
*/
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 */
assert(strlen(ns_buf) < remaining);
strcat(p, ns_buf);
return p;
}
}
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 *
ctime_format (time_t when)
ctime_format (struct timespec ts)
{
char *r = ctime (&when);
if (!r)
const struct tm * ptm;
#define TIME_BUF_LEN 1024u
static char resultbuf[TIME_BUF_LEN];
int nout;
ptm = localtime(&ts.tv_sec);
if (ptm)
{
/* The time cannot be represented as a struct tm.
Output it as an integer. */
return format_date (when, '@');
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
{
/* Remove the trailing newline from the ctime output,
being careful not to assume that the output is fixed-width. */
*strchr (r, '\n') = '\0';
return r;
/* The time cannot be represented as a struct tm.
Output it as an integer. */
return format_date (ts, '@');
}
}

View File

@@ -91,7 +91,7 @@ proc find_version {} {
# Run find and leave the output in $comp_output.
# Called by individual test scripts.
proc do_find_start { suffix findprogram flags passfail options {infile ""}} {
proc do_find_start { suffix findprogram flags passfail options infile output } {
global verbose
global comp_output
@@ -112,6 +112,11 @@ proc do_find_start { suffix findprogram flags passfail options {infile ""}} {
# set compareprog "cmp"
set compareprog "diff -u"
set tmpout ""
if { $output != "" } {
error "The output option is not supported yet"
}
set outfile "$testbase.xo"
if {$infile != ""} then {
set infile "[file dirname [file dirname $testbase]]/inputs/$infile"
@@ -168,12 +173,18 @@ proc do_find_start { suffix findprogram flags passfail options {infile ""}} {
}
proc find_start { passfail options {infile ""}} {
proc find_start { passfail options {infile ""} {output ""}} {
global OLDFIND
global FTSFIND
global FINDFLAGS
global comp_output
if {$infile != ""} then {
set msg "Did not expect infile parameter to be set"
untested $msg
error $msg
}
if {[which $FTSFIND] == 0} then {
error "$FTSFIND, program does not exist"
exit 1
@@ -186,8 +197,8 @@ proc find_start { passfail options {infile ""}} {
# Now run the test with each binary, once with each optimisation level.
foreach optlevel {0 1 2 3} {
set flags "$FINDFLAGS -O$optlevel"
do_find_start old-O$optlevel $OLDFIND $flags $passfail $options $infile
do_find_start new-O$optlevel $FTSFIND $flags $passfail $options $infile
do_find_start old-O$optlevel $OLDFIND $flags $passfail $options $infile $output
do_find_start new-O$optlevel $FTSFIND $flags $passfail $options $infile $output
}
}
@@ -197,7 +208,7 @@ proc find_exit {} {
catch "exec rm -f find.out cmp.out"
}
proc unsafe_path {} {
proc path_setting_is_unsafe {} {
global env;
set itemlist [ split $env(PATH) : ]
foreach item $itemlist {
@@ -216,7 +227,7 @@ proc unsafe_path {} {
}
proc safe_path [ ] {
if { [ unsafe_path ] } {
if { [ path_setting_is_unsafe ] } {
warning { Cannot perform test as your $PATH environment variable includes a reference to the current directory or a directory name which is not absolute }
untested { skipping this test because your $PATH variable is wrongly set }
return 0

View File

@@ -0,0 +1,10 @@
# tests for -execdir pwd \+
if { [ safe_path ] } {
exec rm -rf tmp
exec mkdir tmp
exec touch tmp/foo
set expected [ exec sh -c "cd tmp && /bin/pwd" ]
verbose "Expected output for the pwd invoked by find is is $expected" 2
find_start p {tmp -name foo -execdir /bin/pwd \; } "" $expected
exec rm -rf tmp
}

View File

@@ -58,7 +58,7 @@ getline getopt human idcache lstat malloc memcmp memset mktime \
modechange pathmax quotearg realloc regex rpmatch savedir \
stpcpy strdup strftime strstr strtol strtoul strtoull strtoumax \
xalloc xalloc-die xgetcwd xstrtod xstrtol xstrtoumax yesno human filemode \
getline stpcpy canonicalize mountlist closeout gettext stat-macros"
getline stpcpy canonicalize mountlist closeout gettext stat-macros stat-time"
# We need regex to ensure that we can build on platforms like
# Solaris which lack those functions.