From adc60fb49ad15b5e63988ab85f3a41ca7c2e1c0f Mon Sep 17 00:00:00 2001 From: jay Date: Sat, 24 Feb 2007 14:47:26 +0000 Subject: [PATCH] Support nanosecond timestamps and time arguments with fractional parts --- ChangeLog | 15 +++++++++++ NEWS | 6 ++--- config.rpath | 46 ------------------------------- depcomp | 17 ++---------- doc/find.texi | 8 +++--- find/ftsfind.c | 2 +- find/parser.c | 82 +++++++++++++++++++++++++++++++++++++++++--------------- find/pred.c | 20 +++++++++++--- find/tree.c | 6 ++--- import-gnulib.sh | 3 ++- 10 files changed, 107 insertions(+), 98 deletions(-) diff --git a/ChangeLog b/ChangeLog index 49ed85d..c91363f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,23 @@ 2007-02-24 James Youngman + * find/parser.c (pred_sanity_check): define this function even for + _NDEBUG, but do nothing in that case. + + * import-gnulib.sh (findutils_modules): Import xstrtod. + + * find/parser.c (get_num): Support numbers with a fractional part. + (get_num): Accept arguments with a fractional part + (insert_num): ditto + (get_num_days): Support numbers with a fractional part. + (do_parse_xmin): Support fractional arguments and + nanosecond timestamps. + (parse_used): ditto + (parse_time): ditto + * xargs/xargs.c (read_line): Give a warning message if a NUL is found in the input (this function is called only when -0 is not in effect). + * xargs/xargs.c (nullwarning_given): New variable indicating if the NULL character warning had already been issued. diff --git a/NEWS b/NEWS index c849fd3..30e01c6 100644 --- a/NEWS +++ b/NEWS @@ -12,9 +12,9 @@ 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 tests -newer, -anewer, -cnewer, -mtime, -atime, -ctime, -amin, +-cmin, -mmin and -used now support sub-second timestamps, including +the ability to specify times with non-integer arguments. The -printf format specifiers also support sub-second timestamps: atime ctime mtime diff --git a/config.rpath b/config.rpath index e082db6..c492a93 100755 --- a/config.rpath +++ b/config.rpath @@ -488,54 +488,33 @@ 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 @@ -549,13 +528,10 @@ 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= @@ -573,56 +549,40 @@ 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=/' < @@ -163,8 +164,8 @@ static struct segment **make_segment PARAMS((struct segment **segment, char *for int kind, char format_char, char aux_format_char, struct predicate *pred)); static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, char *argv[], int *arg_ptr)); -static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, enum comparison_type *comp_type)); -static boolean get_num PARAMS((char *str, uintmax_t *num, enum comparison_type *comp_type)); +static boolean get_num_days PARAMS((char *str, uintmax_t *num_days, double *fraction, enum comparison_type *comp_type)); +static boolean get_num PARAMS((char *str, uintmax_t *num, double *fractional_part, enum comparison_type *comp_type)); static struct predicate* insert_num PARAMS((char *argv[], int *arg_ptr, const struct parser_table *entry)); static FILE *open_output_file PARAMS((char *path)); static boolean stream_is_tty(FILE *fp); @@ -1102,19 +1103,20 @@ do_parse_xmin (const struct parser_table* entry, char **argv, int *arg_ptr) { struct predicate *our_pred; uintmax_t num; + double frac; enum comparison_type c_type; - time_t t; + struct timespec ts; if ((argv == NULL) || (argv[*arg_ptr] == NULL)) return false; - /* XXX: need to capture sub-second part */ - if (!get_num_days (argv[*arg_ptr], &num, &c_type)) /* actually not a number of days... */ + + if (!get_num_days (argv[*arg_ptr], &num, &frac, &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.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->args.reftime.ts.tv_sec = options.cur_day_start + DAYSECS - num * 60; + our_pred->args.reftime.ts.tv_nsec = frac * 1e9; our_pred->est_success_rate = estimate_file_age_success_rate(num); (*arg_ptr)++; return true; @@ -1704,7 +1706,8 @@ parse_size (const struct parser_table* entry, char **argv, int *arg_ptr) default: error (1, 0, _("invalid -size type `%c'"), argv[*arg_ptr][len - 1]); } - if (!get_num (argv[*arg_ptr], &num, &c_type)) + /* TODO: accept fractional megabytes etc. ? */ + if (!get_num (argv[*arg_ptr], &num, NULL, &c_type)) return false; our_pred = insert_primary (entry); our_pred->args.size.kind = c_type; @@ -1841,18 +1844,18 @@ parse_used (const struct parser_table* entry, char **argv, int *arg_ptr) uintmax_t num_days; enum comparison_type c_type; time_t t; + double frac; 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)) + if (!get_num (argv[*arg_ptr], &num_days, &frac, &c_type)) return false; t = num_days * DAYSECS; our_pred = insert_primary (entry); 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->args.reftime.ts.tv_nsec = frac * 1e9; our_pred->est_success_rate = estimate_file_age_success_rate(num_days); (*arg_ptr)++; return true; @@ -2624,9 +2627,11 @@ insert_exec_ok (const char *action, const struct parser_table *entry, char **arg get the appropriate information for a time predicate processor. */ static boolean -get_num_days (char *str, uintmax_t *num_days, enum comparison_type *comp_type) +get_num_days (char *str, + uintmax_t *num_days, double *fractional_part, + enum comparison_type *comp_type) { - boolean r = get_num (str, num_days, comp_type); + boolean r = get_num (str, num_days, fractional_part, comp_type); if (r) switch (*comp_type) { @@ -2654,18 +2659,18 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) { struct predicate *our_pred; uintmax_t num_days; + double fraction; enum comparison_type c_type; time_t t; 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)) + if (!get_num_days (argv[*arg_ptr], &num_days, &fraction, &c_type)) return false; /* Figure out the timestamp value we are looking for. */ - t = ( options.cur_day_start - num_days * DAYSECS + t = ( options.cur_day_start - (num_days + fraction) * DAYSECS + ((c_type == COMP_GT) ? DAYSECS - 1 : 0)); if (1) @@ -2674,7 +2679,8 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) * benefit of compilers that are really C89 compilers * which support intmax_t because config.h #defines it */ - intmax_t val = ( (intmax_t)options.cur_day_start - num_days * DAYSECS + intmax_t val = ( (intmax_t)options.cur_day_start - + (num_days + fraction) * DAYSECS + ((c_type == COMP_GT) ? DAYSECS - 1 : 0)); t = val; @@ -2688,7 +2694,7 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) our_pred = insert_primary (entry); 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->args.reftime.ts.tv_nsec = fraction * 1e9; our_pred->est_success_rate = estimate_file_age_success_rate(num_days); (*arg_ptr)++; @@ -2725,8 +2731,14 @@ parse_time (const struct parser_table* entry, char *argv[], int *arg_ptr) Return true if all okay, false if input error. */ static boolean -get_num (char *str, uintmax_t *num, enum comparison_type *comp_type) +get_num (char *str, + uintmax_t *num, + double *fractional_part, + enum comparison_type *comp_type) { + char **pend; + boolean ok; + if (str == NULL) return false; switch (str[0]) @@ -2744,7 +2756,33 @@ get_num (char *str, uintmax_t *num, enum comparison_type *comp_type) break; } - return xstrtoumax (str, NULL, 10, num, "") == LONGINT_OK; + ok = xstrtoumax (str, pend, 10, num, "") == LONGINT_OK; + if (ok && fractional_part) + { + if (*pend) + { + if ('.'== (**pend)) + { + /* We have a fractional part. */ + const char *p; + ok = xstrtod(*pend, &p, fractional_part, strtod); + if (!ok) + { + *fractional_part = 0.0; + ok = true; + } + } + else + { + *fractional_part = 0.0; + } + } + else + { + *fractional_part = 0.0; + } + } + return ok; } /* Insert a number predicate. @@ -2768,7 +2806,7 @@ insert_num (char **argv, int *arg_ptr, const struct parser_table *entry) if ((argv == NULL) || (argv[*arg_ptr] == NULL)) return NULL; - if (!get_num (argv[*arg_ptr], &num, &c_type)) + if (!get_num (argv[*arg_ptr], &num, NULL, &c_type)) return NULL; our_pred = insert_primary (entry); our_pred->args.numinfo.kind = c_type; diff --git a/find/pred.c b/find/pred.c index 9d40b3e..e2adf8a 100644 --- a/find/pred.c +++ b/find/pred.c @@ -1524,7 +1524,7 @@ pred_used (char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) (void) pathname; - /* XXX: this needs to be retested carefully (manually, if necessary) */ + /* 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; @@ -1826,8 +1826,8 @@ format_date (struct timespec ts, int kind) 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. + /* 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); @@ -1987,6 +1987,18 @@ print_optlist (FILE *fp, const struct predicate *p) } } +#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) { @@ -2047,7 +2059,7 @@ pred_sanity_check(const struct predicate *predicates) assert(!p->no_default_print); assert(!p->side_effects); break; - } } } +#endif diff --git a/find/tree.c b/find/tree.c index 0657b28..414cad2 100644 --- a/find/tree.c +++ b/find/tree.c @@ -1277,14 +1277,14 @@ build_expression_tree(int argc, char *argv[], int end_of_leading_options) predicates = NULL; - /* Find where in ARGV the predicates begin by skipping the list of start points. */ + /* Find where in ARGV the predicates begin by skipping the list of + * start points. + */ for (i = end_of_leading_options; i < argc && !looks_like_expression(argv[i], true); i++) { - /* fprintf(stderr, "Looks like %s is not a predicate\n", argv[i]); */ /* Do nothing. */ ; } - /* XXX: beginning of bit we need factor out of both find.c and ftsfind.c */ /* Enclose the expression in `( ... )' so a default -print will apply to the whole expression. */ entry_open = find_parser("("); diff --git a/import-gnulib.sh b/import-gnulib.sh index 3e3e9fa..0f14aaf 100644 --- a/import-gnulib.sh +++ b/import-gnulib.sh @@ -58,7 +58,8 @@ 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 stat-time" +getline stpcpy canonicalize mountlist closeout gettext stat-macros stat-time \ +xstrtod" # We need regex to ensure that we can build on platforms like # Solaris which lack those functions. -- 2.11.4.GIT