2007-03-27 Bruno Haible <bruno@clisp.org>
[gnulib.git] / lib / stat-time.h
index 5fd18c1..50e907c 100644 (file)
@@ -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
 # 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 @@ get_stat_mtime_ns (struct stat const *st)
 # 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 @@ get_stat_mtime (struct stat const *st)
 #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