[Ada] Improve precision of Ada.Directories.Modification_Time
authorDmitriy Anisimkov <anisimko@adacore.com>
Mon, 3 Aug 2020 06:18:20 +0000 (12:18 +0600)
committerPierre-Marie de Rodat <derodat@adacore.com>
Wed, 21 Oct 2020 07:22:46 +0000 (03:22 -0400)
gcc/ada/

* adaint.c (__gnat_file_time): New routine.
(__gnat_copy_attribs): Copy timestamps in nanoseconds.
* libgnat/a-direct.adb (C_Modification_Time): Bind to
__gnat_file_time.
(Modification_Time): Call to C_Modification_Time.

gcc/ada/adaint.c
gcc/ada/libgnat/a-direct.adb

index c44d193233272c8b1cd43a54a69e23da436eb58f..9ef02438308de7014380a86f9fb79d27b90f0975 100644 (file)
@@ -60,6 +60,7 @@
 /* We want to use the POSIX variants of include files.  */
 #define POSIX
 #include "vxWorks.h"
+#include <sys/time.h>
 
 #if defined (__mips_vxworks)
 #include "cacheLib.h"
@@ -1474,6 +1475,74 @@ __gnat_file_time_fd (int fd)
    return __gnat_file_time_fd_attr (fd, &attr);
 }
 
+extern long long __gnat_file_time(char* name)
+{
+  long long result;
+
+  if (name == NULL) {
+    return LLONG_MIN;
+  }
+  /* Number of seconds between <Jan 1st 1970> and <Jan 1st 2150>. */
+  static const long long ada_epoch_offset = (136 * 365 + 44 * 366) * 86400LL;
+#if defined(_WIN32)
+
+  /* Number of 100 nanoseconds between <Jan 1st 1601> and <Jan 1st 2150>. */
+  static const long long w32_epoch_offset =
+  (11644473600LL + ada_epoch_offset) * 1E7;
+
+  WIN32_FILE_ATTRIBUTE_DATA fad;
+  union
+  {
+    FILETIME ft_time;
+    long long ll_time;
+  } t_write;
+
+  if (!GetFileAttributesExA(name, GetFileExInfoStandard, &fad)) {
+    return LLONG_MIN;
+  }
+
+  t_write.ft_time = fad.ftLastWriteTime;
+
+  /* Next code similar to (t_write.ll_time - w32_epoch_offset) * 100
+     but on overflow returns LLONG_MIN value. */
+
+  if (__builtin_ssubll_overflow(t_write.ll_time, w32_epoch_offset, &result)) {
+    return LLONG_MIN;
+  }
+
+  if (__builtin_smulll_overflow(result, 100, &result)) {
+    return LLONG_MIN;
+  }
+
+#else
+
+  struct stat sb;
+  if (stat(name, &sb) != 0) {
+    return LLONG_MIN;
+  }
+
+  /* Next code similar to
+     (sb.st_mtime - ada_epoch_offset) * 1E9 + sb.st_mtim.tv_nsec
+     but on overflow returns LLONG_MIN value. */
+
+  if (__builtin_ssubll_overflow(sb.st_mtime, ada_epoch_offset, &result)) {
+    return LLONG_MIN;
+  }
+
+  if (__builtin_smulll_overflow(result, 1E9, &result)) {
+    return LLONG_MIN;
+  }
+
+#if defined(st_mtime)
+  if (__builtin_saddll_overflow(result, sb.st_mtim.tv_nsec, &result)) {
+    return LLONG_MIN;
+  }
+#endif
+
+#endif
+  return result;
+}
+
 /* Set the file time stamp.  */
 
 void
@@ -3173,22 +3242,45 @@ __gnat_copy_attribs (char *from ATTRIBUTE_UNUSED, char *to ATTRIBUTE_UNUSED,
 
 #else
   GNAT_STRUCT_STAT fbuf;
-  struct utimbuf tbuf;
 
   if (GNAT_STAT (from, &fbuf) == -1) {
      return -1;
   }
 
-  /* Do we need to copy timestamp ? */
+#if _POSIX_C_SOURCE >= 200809L
+  struct timespec tbuf[2];
+
   if (mode != 2) {
-     tbuf.actime = fbuf.st_atime;
-     tbuf.modtime = fbuf.st_mtime;
+     tbuf[0] = fbuf.st_atim;
+     tbuf[1] = fbuf.st_mtim;
 
-     if (utime (to, &tbuf) == -1) {
+     if (utimensat (AT_FDCWD, to, tbuf, 0) == -1) {
         return -1;
      }
   }
 
+#else
+  struct timeval tbuf[2];
+  /* Do we need to copy timestamp ? */
+
+  if (mode != 2) {
+     tbuf[0].tv_sec  = fbuf.st_atime;
+     tbuf[1].tv_sec  = fbuf.st_mtime;
+
+     #if defined(st_mtime)
+     tbuf[0].tv_usec = fbuf.st_atim.tv_nsec / 1000;
+     tbuf[1].tv_usec = fbuf.st_mtim.tv_nsec / 1000;
+     #else
+     tbuf[0].tv_usec = 0;
+     tbuf[1].tv_usec = 0;
+     #endif
+
+     if (utimes (to, tbuf) == -1) {
+        return -1;
+     }
+  }
+#endif
+
   /* Do we need to copy file permissions ? */
   if (mode != 0 && (chmod (to, fbuf.st_mode) == -1)) {
          return -1;
index b70bf0eee5d72d3873c1ba9c0c6f2c9995aa9054..162ace919f9e54e4f9aae4650ec381d337d68b6a 100644 (file)
@@ -30,7 +30,6 @@
 ------------------------------------------------------------------------------
 
 with Ada.Calendar;               use Ada.Calendar;
-with Ada.Calendar.Formatting;    use Ada.Calendar.Formatting;
 with Ada.Characters.Handling;    use Ada.Characters.Handling;
 with Ada.Directories.Validity;   use Ada.Directories.Validity;
 with Ada.Directories.Hierarchical_File_Names;
@@ -70,6 +69,15 @@ package body Ada.Directories is
    pragma Import (C, Max_Path, "__gnat_max_path_len");
    --  The maximum length of a path
 
+   function C_Modification_Time (N : System.Address) return Ada.Calendar.Time;
+   pragma Import (C, C_Modification_Time, "__gnat_file_time");
+   --  Get modification time for file with name referenced by N
+
+   Invalid_Time : constant Ada.Calendar.Time :=
+                    C_Modification_Time (System.Null_Address);
+   --  Result returned from C_Modification_Time call when routine unable to get
+   --  file modification time.
+
    type Search_Data is record
       Is_Valid      : Boolean := False;
       Name          : Unbounded_String;
@@ -991,14 +999,9 @@ package body Ada.Directories is
    -----------------------
 
    function Modification_Time (Name : String) return Time is
-      Date   : OS_Time;
-      Year   : Year_Type;
-      Month  : Month_Type;
-      Day    : Day_Type;
-      Hour   : Hour_Type;
-      Minute : Minute_Type;
-      Second : Second_Type;
 
+      Date   : Time;
+      C_Name : aliased String (1 .. Name'Length + 1);
    begin
       --  First, the invalid cases
 
@@ -1006,19 +1009,15 @@ package body Ada.Directories is
          raise Name_Error with '"' & Name & """ not a file or directory";
 
       else
-         Date := File_Time_Stamp (Name);
-
-         --  Break down the time stamp into its constituents relative to GMT.
-         --  This version of Split does not recognize leap seconds or buffer
-         --  space for time zone processing.
+         C_Name := Name & ASCII.NUL;
+         Date := C_Modification_Time (C_Name'Address);
 
-         GM_Split (Date, Year, Month, Day, Hour, Minute, Second);
-
-         --  The result must be in GMT. Ada.Calendar.
-         --  Formatting.Time_Of with default time zone of zero (0) is the
-         --  routine of choice.
+         if Date = Invalid_Time then
+            raise Use_Error with
+              "Unable to get modification time of the file """ & Name & '"';
+         end if;
 
-         return Time_Of (Year, Month, Day, Hour, Minute, Second, 0.0);
+         return Date;
       end if;
    end Modification_Time;