stat-time.h 7.5 KB
Newer Older
1 2
/* stat-related time functions.

Paul Eggert's avatar
Paul Eggert committed
3
   Copyright (C) 2005, 2007, 2009-2020 Free Software Foundation, Inc.
4 5 6 7 8 9 10 11 12 13 14 15

   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.

   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
Paul Eggert's avatar
Paul Eggert committed
16
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
17 18 19 20 21 22

/* Written by Paul Eggert.  */

#ifndef STAT_TIME_H
#define STAT_TIME_H 1

Paul Eggert's avatar
Paul Eggert committed
23 24 25 26
#include "intprops.h"

#include <errno.h>
#include <stddef.h>
27 28 29
#include <sys/stat.h>
#include <time.h>

Paul Eggert's avatar
Paul Eggert committed
30 31 32
#ifndef _GL_INLINE_HEADER_BEGIN
 #error "Please include config.h first."
#endif
33 34 35 36 37
_GL_INLINE_HEADER_BEGIN
#ifndef _GL_STAT_TIME_INLINE
# define _GL_STAT_TIME_INLINE _GL_INLINE
#endif

Paul Eggert's avatar
Paul Eggert committed
38 39 40 41
#ifdef __cplusplus
extern "C" {
#endif

42 43 44 45 46 47 48 49
/* STAT_TIMESPEC (ST, ST_XTIM) is the ST_XTIM member for *ST of type
   struct timespec, if available.  If not, then STAT_TIMESPEC_NS (ST,
   ST_XTIM) is the nanosecond component of the ST_XTIM member for *ST,
   if available.  ST_XTIM can be st_atim, st_ctim, st_mtim, or st_birthtim
   for access, status change, data modification, or birth (creation)
   time respectively.

   These macros are private to stat-time.h.  */
Paul Eggert's avatar
Paul Eggert committed
50 51
#if _GL_WINDOWS_STAT_TIMESPEC || defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
# if _GL_WINDOWS_STAT_TIMESPEC || defined TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC
52 53 54 55 56 57 58 59 60 61 62 63 64
#  define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim)
# else
#  define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec)
# endif
#elif defined HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC
# define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim##espec)
#elif defined HAVE_STRUCT_STAT_ST_ATIMENSEC
# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim##ensec)
#elif defined HAVE_STRUCT_STAT_ST_ATIM_ST__TIM_TV_NSEC
# define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.st__tim.tv_nsec)
#endif

/* Return the nanosecond component of *ST's access time.  */
Paul Eggert's avatar
Paul Eggert committed
65
_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
66 67 68 69 70 71 72 73 74 75 76 77
get_stat_atime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_atim).tv_nsec;
# elif defined STAT_TIMESPEC_NS
  return STAT_TIMESPEC_NS (st, st_atim);
# else
  return 0;
# endif
}

/* Return the nanosecond component of *ST's status change time.  */
Paul Eggert's avatar
Paul Eggert committed
78
_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
79 80 81 82 83 84 85 86 87 88 89 90
get_stat_ctime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_ctim).tv_nsec;
# elif defined STAT_TIMESPEC_NS
  return STAT_TIMESPEC_NS (st, st_ctim);
# else
  return 0;
# endif
}

/* Return the nanosecond component of *ST's data modification time.  */
Paul Eggert's avatar
Paul Eggert committed
91
_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
92 93 94 95 96 97 98 99 100 101 102 103
get_stat_mtime_ns (struct stat const *st)
{
# if defined STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_mtim).tv_nsec;
# elif defined STAT_TIMESPEC_NS
  return STAT_TIMESPEC_NS (st, st_mtim);
# else
  return 0;
# endif
}

/* Return the nanosecond component of *ST's birth time.  */
Paul Eggert's avatar
Paul Eggert committed
104
_GL_STAT_TIME_INLINE long int _GL_ATTRIBUTE_PURE
Paul Eggert's avatar
Paul Eggert committed
105
get_stat_birthtime_ns (struct stat const *st _GL_UNUSED)
106 107 108 109 110 111 112 113 114 115 116
{
# if defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
  return STAT_TIMESPEC (st, st_birthtim).tv_nsec;
# elif defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC
  return STAT_TIMESPEC_NS (st, st_birthtim);
# else
  return 0;
# endif
}

/* Return *ST's access time.  */
Paul Eggert's avatar
Paul Eggert committed
117
_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
118 119 120 121 122 123 124 125 126 127 128 129 130
get_stat_atime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_atim);
#else
  struct timespec t;
  t.tv_sec = st->st_atime;
  t.tv_nsec = get_stat_atime_ns (st);
  return t;
#endif
}

/* Return *ST's status change time.  */
Paul Eggert's avatar
Paul Eggert committed
131
_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
132 133 134 135 136 137 138 139 140 141 142 143 144
get_stat_ctime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_ctim);
#else
  struct timespec t;
  t.tv_sec = st->st_ctime;
  t.tv_nsec = get_stat_ctime_ns (st);
  return t;
#endif
}

/* Return *ST's data modification time.  */
Paul Eggert's avatar
Paul Eggert committed
145
_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
146 147 148 149 150 151 152 153 154 155 156 157 158 159
get_stat_mtime (struct stat const *st)
{
#ifdef STAT_TIMESPEC
  return STAT_TIMESPEC (st, st_mtim);
#else
  struct timespec t;
  t.tv_sec = st->st_mtime;
  t.tv_nsec = get_stat_mtime_ns (st);
  return t;
#endif
}

/* Return *ST's birth time, if available; otherwise return a value
   with tv_sec and tv_nsec both equal to -1.  */
Paul Eggert's avatar
Paul Eggert committed
160
_GL_STAT_TIME_INLINE struct timespec _GL_ATTRIBUTE_PURE
Paul Eggert's avatar
Paul Eggert committed
161
get_stat_birthtime (struct stat const *st _GL_UNUSED)
162 163 164 165 166 167 168 169 170
{
  struct timespec t;

#if (defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC \
     || defined HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC)
  t = STAT_TIMESPEC (st, st_birthtim);
#elif defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC
  t.tv_sec = st->st_birthtime;
  t.tv_nsec = st->st_birthtimensec;
Paul Eggert's avatar
Paul Eggert committed
171
#elif defined _WIN32 && ! defined __CYGWIN__
172 173
  /* Native Windows platforms (but not Cygwin) put the "file creation
     time" in st_ctime (!).  See
Paul Eggert's avatar
Paul Eggert committed
174
     <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions>.  */
Paul Eggert's avatar
Paul Eggert committed
175 176 177
# if _GL_WINDOWS_STAT_TIMESPEC
  t = st->st_ctim;
# else
178 179
  t.tv_sec = st->st_ctime;
  t.tv_nsec = 0;
Paul Eggert's avatar
Paul Eggert committed
180
# endif
181 182 183 184 185 186 187 188 189 190 191
#else
  /* Birth time is not supported.  */
  t.tv_sec = -1;
  t.tv_nsec = -1;
#endif

#if (defined HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC \
     || defined HAVE_STRUCT_STAT_ST_BIRTHTIM_TV_NSEC \
     || defined HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC)
  /* FreeBSD and NetBSD sometimes signal the absence of knowledge by
     using zero.  Attempt to work around this problem.  Alas, this can
Paul Eggert's avatar
Paul Eggert committed
192
     report failure even for valid timestamps.  Also, NetBSD
193 194 195 196 197 198 199 200 201 202 203 204
     sometimes returns junk in the birth time fields; work around this
     bug if it is detected.  */
  if (! (t.tv_sec && 0 <= t.tv_nsec && t.tv_nsec < 1000000000))
    {
      t.tv_sec = -1;
      t.tv_nsec = -1;
    }
#endif

  return t;
}

Paul Eggert's avatar
Paul Eggert committed
205 206 207 208 209 210
/* If a stat-like function returned RESULT, normalize the timestamps
   in *ST, in case this platform suffers from the Solaris 11 bug where
   tv_nsec might be negative.  Return the adjusted RESULT, setting
   errno to EOVERFLOW if normalization overflowed.  This function
   is intended to be private to this .h file.  */
_GL_STAT_TIME_INLINE int
Paul Eggert's avatar
Paul Eggert committed
211
stat_time_normalize (int result, struct stat *st _GL_UNUSED)
Paul Eggert's avatar
Paul Eggert committed
212 213 214 215
{
#if defined __sun && defined STAT_TIMESPEC
  if (result == 0)
    {
Paul Eggert's avatar
Paul Eggert committed
216
      long int timespec_hz = 1000000000;
Paul Eggert's avatar
Paul Eggert committed
217 218 219 220 221 222 223
      short int const ts_off[] = { offsetof (struct stat, st_atim),
                                   offsetof (struct stat, st_mtim),
                                   offsetof (struct stat, st_ctim) };
      int i;
      for (i = 0; i < sizeof ts_off / sizeof *ts_off; i++)
        {
          struct timespec *ts = (struct timespec *) ((char *) st + ts_off[i]);
Paul Eggert's avatar
Paul Eggert committed
224 225
          long int q = ts->tv_nsec / timespec_hz;
          long int r = ts->tv_nsec % timespec_hz;
Paul Eggert's avatar
Paul Eggert committed
226 227
          if (r < 0)
            {
Paul Eggert's avatar
Paul Eggert committed
228
              r += timespec_hz;
Paul Eggert's avatar
Paul Eggert committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
              q--;
            }
          ts->tv_nsec = r;
          /* Overflow is possible, as Solaris 11 stat can yield
             tv_sec == TYPE_MINIMUM (time_t) && tv_nsec == -1000000000.
             INT_ADD_WRAPV is OK, since time_t is signed on Solaris.  */
          if (INT_ADD_WRAPV (q, ts->tv_sec, &ts->tv_sec))
            {
              errno = EOVERFLOW;
              return -1;
            }
        }
    }
#endif
  return result;
}

Paul Eggert's avatar
Paul Eggert committed
246 247 248 249
#ifdef __cplusplus
}
#endif

250 251
_GL_INLINE_HEADER_END

252
#endif