Mercurial > hg > octave-kai > gnulib-hg
changeset 8565:c49cf515502f
2007-03-27 Bruno Haible <bruno@clisp.org>
* lib/stat-time.h: Include <sys/stat.h>.
2007-03-27 James Youngman <jay@gnu.org>
* lib/stat-time.h (get_stat_birthtime): New function for
retrieving st_birthtime as provided by UFS2 (hence *BSD).
* m4/stat-time.m4 (gl_STAT_BIRTHTIME): Probe for st_birthtime
and its variants.
* modules/stat-time (configure.ac): call gl_STAT_BIRTHTIME.
* modules/stat-time-test: New file.
* tests/test-stat-time.c: New test, devised by Bruno Haible.
author | Bruno Haible <bruno@clisp.org> |
---|---|
date | Tue, 27 Mar 2007 11:01:11 +0000 |
parents | 4da0aa038935 |
children | 47ac12cd6649 |
files | ChangeLog lib/stat-time.h m4/stat-time.m4 modules/stat-time modules/stat-time-tests tests/test-stat-time.c |
diffstat | 6 files changed, 319 insertions(+), 2 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-03-27 Bruno Haible <bruno@clisp.org> + + * lib/stat-time.h: Include <sys/stat.h>. + +2007-03-27 James Youngman <jay@gnu.org> + + * lib/stat-time.h (get_stat_birthtime): New function for + retrieving st_birthtime as provided by UFS2 (hence *BSD). + * m4/stat-time.m4 (gl_STAT_BIRTHTIME): Probe for st_birthtime + and its variants. + * modules/stat-time (configure.ac): call gl_STAT_BIRTHTIME. + * modules/stat-time-test: New file. + * tests/test-stat-time.c: New test, devised by Bruno Haible. + 2007-03-26 Bruno Haible <bruno@clisp.org> Better support of signalling NaNs.
--- a/lib/stat-time.h +++ b/lib/stat-time.h @@ -21,6 +21,7 @@ #ifndef STAT_TIME_H #define STAT_TIME_H 1 +#include <sys/stat.h> #include <time.h> /* STAT_TIMESPEC (ST, ST_XTIM) is the ST_XTIM member for *ST of type @@ -44,6 +45,13 @@ # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec) #endif +#if defined HAVE_STRUCT_STAT_ST_BIRTHTIME || defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC || defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC || defined HAVE_STRUCT_STAT_ST_SPARE4 +# define USE_BIRTHTIME 1 +#else +# undef USE_BIRTHTIME +#endif + + /* Return the nanosecond component of *ST's access time. */ static inline long int get_stat_atime_ns (struct stat const *st) @@ -89,6 +97,28 @@ # endif } +/* Return the nanosecond component of *ST's birth time. */ +static inline long int +get_stat_birthtime_ns (struct stat const *st) +{ +# if defined USE_BIRTHTIME +# if defined STAT_TIMESPEC && defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC + return STAT_TIMESPEC (st, st_birthtim).tv_nsec; +# elif defined STAT_TIMESPEC_NS && defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_SEC + return STAT_TIMESPEC_NS (st, st_birthtim); +# elif defined HAVE_STRUCT_STAT_ST_SPARE4 + /* Cygwin, without __CYGWIN_USE_BIG_TYPES__ */ + return st->st_spare4[1] * 1000L; +# else + /* Birthtime is available, but not at nanosecond resolution. */ + return 0; +# endif +# else + /* Birthtime is not available, so indicate this in the returned value. */ + return 0; +# endif +} + /* Return *ST's access time. */ static inline struct timespec get_stat_atime (struct stat const *st) @@ -131,4 +161,69 @@ #endif } +/* Return *ST's birth time, if available, in *PTS. A nonzero value is + * returned if the stat structure appears to indicate that the + * timestamp is available. + * + * The return value of this function does not reliably indicate that the + * returned data is valid; see the comments within the body of the + * function for an explanation. + */ +static inline int +get_stat_birthtime (struct stat const *st, + struct timespec *pts) +{ +#if defined USE_BIRTHTIME +# ifdef STAT_TIMESPEC + *pts = STAT_TIMESPEC (st, st_birthtim); +# else + struct timespec t; + pts->tv_sec = st->st_birthtime; + pts->tv_nsec = get_stat_birthtime_ns (st); +# endif + + /* NetBSD sometimes signals the absence of knowledge of the file's + * birth time by using zero. We indicate we don't know, by + * returning 0 from this function when that happens. This is + * slightly problematic since (time_t)0 is otherwise a valid, albeit + * unlikely, timestamp. + * + * NetBSD sometimes returns 0 for unknown values (for example on + * ffs) and sometimes begative values for tv_nsec (for example on + * NFS). For some filesystems (e.g. msdos) NetBSD also appears to + * fail to update the st_birthtime member at all, and just leaves in + * there whatever junk existed int he uninitialised stat structure + * the caller provided. Therefore, callers are advised to initialise + * the tv_nsec number to a negative value before they call stat in + * order to detect this problem. + */ + if (pts->tv_sec == (time_t)0) + { + return 0; /* result probably invalid, see above. */ + } + else + { + /* Sometimes NetBSD returns junk in the birth time fields, so + * do a simple range check on the data, and return 0 to indicate + * that the data is invalid if it just looks wrong. + */ + return (pts->tv_nsec >= 0) && (pts->tv_nsec <= 1000000000); + } +#elif (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__ + /* Woe32 native platforms (mingw, msvc, but not Cygwin) put the + * "file creation time" in st_ctime (!). See for example the + * article + * <http://msdn2.microsoft.com/de-de/library/14h5k7ff(VS.80).aspx> + */ + pts->tv_sec = st->st_ctime; + pts->tv_nsec = 0; + return 1; /* result is valid */ +#else + /* Birth time not supported. */ + pts->tv_sec = 0; + pts->tv_nsec = 0; + return 0; /* result is not valid */ #endif +} + +#endif
--- a/m4/stat-time.m4 +++ b/m4/stat-time.m4 @@ -10,11 +10,13 @@ dnl From Paul Eggert. # st_atim.tv_nsec - Linux, Solaris -# st_atimespec.tv_nsec - FreeBSD, if ! defined _POSIX_SOURCE -# st_atimensec - FreeBSD, if defined _POSIX_SOURCE +# st_atimespec.tv_nsec - FreeBSD, NetBSD, if ! defined _POSIX_SOURCE +# st_atimensec - FreeBSD, NetBSD, if defined _POSIX_SOURCE # st_atim.st__tim.tv_nsec - UnixWare (at least 2.1.2 through 7.1) # st_spare1 - Cygwin? +# st_birthtimespec present on NetBSD (probably also FreBSD, OpenBSD) + AC_DEFUN([gl_STAT_TIME], [ AC_REQUIRE([AC_C_INLINE]) @@ -61,3 +63,26 @@ [#include <sys/types.h> #include <sys/stat.h>]) ]) + +# Checks for st_birthtime, which is a feature from UFS2 (FreeBSD, NetBSD, OpenBSD, etc.) +# There was a time when this field was named st_createtime (21 June 2002 to 16 July 2002) +# But that window is very small and applied only to development code, so systems still +# using that configuration are not a realistic development target. +# See revisions 1.10 and 1.11 of FreeBSD's src/sys/ufs/ufs/dinode.h. +# +AC_DEFUN([gl_STAT_BIRTHTIME], +[ + AC_REQUIRE([AC_C_INLINE]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_CHECK_HEADERS_ONCE([sys/time.h]) + AC_CHECK_MEMBERS([struct stat.st_birthtimespec.tv_sec, struct stat.st_birthtimespec.tv_nsec], [], + [AC_CHECK_MEMBERS([struct stat.st_birthtime, struct stat.st_birthtimensec], [], + [AC_CHECK_MEMBERS([struct stat.st_spare4], [], + [], + [#include <sys/types.h> + #include <sys/stat.h>])], + [#include <sys/types.h> + #include <sys/stat.h>])], + [#include <sys/types.h> + #include <sys/stat.h>]) +])
--- a/modules/stat-time +++ b/modules/stat-time @@ -10,6 +10,7 @@ configure.ac: gl_STAT_TIME +gl_STAT_BIRTHTIME Makefile.am:
new file mode 100644 --- /dev/null +++ b/modules/stat-time-tests @@ -0,0 +1,11 @@ +Files: +tests/test-stat-time.c + +Depends-on: +time + +configure.ac: + +Makefile.am: +TESTS += test-stat-time +check_PROGRAMS += test-stat-time
new file mode 100644 --- /dev/null +++ b/tests/test-stat-time.c @@ -0,0 +1,171 @@ +/* Test of <stat-time.h>. + Copyright (C) 2007 Free Software Foundation, Inc. + + 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 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by James Youngman <jay@gnu.org>, 2007. */ + +#include <config.h> + +#include "stat-time.h" + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <unistd.h> + +#define ASSERT(condition) if (!(condition)) abort () + +enum { NFILES = 4 }; + +static void +cleanup (int sig) +{ + /* Remove temporary files. */ + unlink ("t-stt-stamp1"); + unlink ("t-stt-testfile"); + unlink ("t-stt-stamp2"); + unlink ("t-stt-renamed"); + unlink ("t-stt-stamp3"); +} + +static int +open_file (const char *filename, int flags) +{ + int fd = open (filename, flags | O_WRONLY, 0500); + if (fd >= 0) + { + close (fd); + return 1; + } + else + { + return 0; + } +} + +static void +create_file (const char *filename) +{ + ASSERT (open_file (filename, O_CREAT | O_EXCL)); +} + +static void +do_stat (const char *filename, struct stat *p) +{ + ASSERT (stat (filename, p) == 0); +} + +static void +prepare_test (struct stat *statinfo, struct timespec *modtimes) +{ + int i; + + create_file ("t-stt-stamp1"); + sleep (2); + create_file ("t-stt-testfile"); + sleep (2); + create_file ("t-stt-stamp2"); + sleep (2); + ASSERT (rename ("t-stt-testfile", "t-stt-renamed") == 0); + sleep (2); + create_file ("t-stt-stamp3"); + + do_stat ("t-stt-stamp1", &statinfo[0]); + do_stat ("t-stt-renamed", &statinfo[1]); + do_stat ("t-stt-stamp2", &statinfo[2]); + do_stat ("t-stt-stamp3", &statinfo[3]); + + /* Now use our access functions. */ + for (i = 0; i < NFILES; ++i) + { + modtimes[i] = get_stat_mtime (&statinfo[i]); + } +} + +static void +test_mtime (const struct stat *statinfo, struct timespec *modtimes) +{ + int i; + + /* Use the struct stat fields directly. */ + ASSERT (statinfo[0].st_mtime < statinfo[2].st_mtime); /* mtime(stamp1) < mtime(stamp2) */ + ASSERT (statinfo[2].st_mtime < statinfo[3].st_mtime); /* mtime(stamp2) < mtime(stamp3) */ + ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime); /* mtime(stamp2) < ctime(renamed) */ + + /* Now check the result of the access functions. */ + ASSERT (modtimes[0].tv_sec < modtimes[2].tv_sec); /* mtime(stamp1) < mtime(stamp2) */ + ASSERT (modtimes[2].tv_sec < modtimes[3].tv_sec); /* mtime(stamp2) < mtime(stamp3) */ + + /* verify equivalence */ + for (i = 0; i < NFILES; ++i) + { + struct timespec ts; + ts = get_stat_mtime (&statinfo[i]); + ASSERT (ts.tv_sec == statinfo[i].st_mtime); + } + + ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime); /* mtime(stamp2) < ctime(renamed) */ +} + +static void +test_birthtime (const struct stat *statinfo, + const struct timespec *modtimes, + struct timespec *birthtimes) +{ + int i; + + /* Collect the birth times.. */ + for (i = 0; i < NFILES; ++i) + { + if (!get_stat_birthtime (&statinfo[i], &birthtimes[i])) + { + return; + } + } + + ASSERT (modtimes[0].tv_sec < birthtimes[1].tv_sec); /* mtime(stamp1) < birthtime(renamed) */ + ASSERT (birthtimes[1].tv_sec < modtimes[2].tv_sec); /* birthtime(renamed) < mtime(stamp2) */ +} + +int +main () +{ + struct stat statinfo[NFILES]; + struct timespec modtimes[NFILES]; + struct timespec birthtimes[NFILES]; + +#ifdef SIGHUP + signal (SIGHUP, cleanup); +#endif +#ifdef SIGINT + signal (SIGINT, cleanup); +#endif +#ifdef SIGQUIT + signal (SIGQUIT, cleanup); +#endif +#ifdef SIGTERM + signal (SIGTERM, cleanup); +#endif + + prepare_test (statinfo, modtimes); + test_mtime (statinfo, modtimes); + test_birthtime (statinfo, modtimes, birthtimes); + + cleanup (0); + return 0; +}