SDL: filesystem: Use high-res file times on more platforms

From 4a7e3beeb94e4d3a0afd6a476ef4d31182dc6b36 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Wed, 20 Mar 2024 10:42:07 -0400
Subject: [PATCH] filesystem: Use high-res file times on more platforms

Some POSIX platforms don't define macros to note the presence of the POSIX.1-2008 st_*tim timespec members of the stat struct, so check if this member exists during CMake configuration and conditionally enable it.

Apple platforms use st_*timespec naming, which is supported as of OSX 10.6. SDL3 requires 10.9+, so no fallback is needed.

Android only supports the POSIX.1-2008 semantics as of API version 26 or higher, so this has to be conditionally enabled in the makefile build via an API version definition check.

In other cases, file times fall back to the legacy path with second precision.
---
 CMakeLists.txt                                  | 1 +
 include/build_config/SDL_build_config.h.cmake   | 1 +
 include/build_config/SDL_build_config_android.h | 3 ++-
 src/filesystem/posix/SDL_sysfsops.c             | 9 +++++++--
 4 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c235d0fa5df8f..a9f2c04e08ed8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1149,6 +1149,7 @@ if(SDL_LIBC)
     endif()
 
     check_struct_has_member("struct sigaction" "sa_sigaction" "signal.h" HAVE_SA_SIGACTION)
+    check_struct_has_member("struct stat" "st_mtim" "sys/stat.h" HAVE_ST_MTIM)
   endif()
 else()
   set(headers
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 951771e4942fb..f8eb06aea3111 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -190,6 +190,7 @@
 #cmakedefine HAVE_POSIX_FALLOCATE 1
 #cmakedefine HAVE_SIGACTION 1
 #cmakedefine HAVE_SA_SIGACTION 1
+#cmakedefine HAVE_ST_MTIM 1
 #cmakedefine HAVE_SETJMP 1
 #cmakedefine HAVE_NANOSLEEP 1
 #cmakedefine HAVE_GMTIME_R 1
diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h
index 19df2278f2092..49044b0b7302e 100644
--- a/include/build_config/SDL_build_config_android.h
+++ b/include/build_config/SDL_build_config_android.h
@@ -198,9 +198,10 @@
 #define SDL_CAMERA_DRIVER_ANDROID 1
 #define SDL_CAMERA_DRIVER_DUMMY 1
 
-/* Enable nl_langinfo on version 26 and higher. */
+/* Enable nl_langinfo and high-res file times on version 26 and higher. */
 #if __ANDROID_API__ >= 26
 #define HAVE_NL_LANGINFO 1
+#define HAVE_ST_MTIM 1
 #endif
 
 #endif /* SDL_build_config_android_h_ */
diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c
index 8e4939f1a9815..fcc38ed9bbda5 100644
--- a/src/filesystem/posix/SDL_sysfsops.c
+++ b/src/filesystem/posix/SDL_sysfsops.c
@@ -124,11 +124,16 @@ int SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
         info->size = (Uint64) statbuf.st_size;
     }
 
-#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700)
-    /* Use high-res file times, if available. */
+#if defined(HAVE_ST_MTIM)
+    /* POSIX.1-2008 standard */
     info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctim.tv_sec) + statbuf.st_ctim.tv_nsec;
     info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtim.tv_sec) + statbuf.st_mtim.tv_nsec;
     info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atim.tv_sec) + statbuf.st_atim.tv_nsec;
+#elif defined(SDL_PLATFORM_APPLE)
+    /* Apple platform stat structs use 'st_*timespec' naming. */
+    info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctimespec.tv_sec) + statbuf.st_ctimespec.tv_nsec;
+    info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtimespec.tv_sec) + statbuf.st_mtimespec.tv_nsec;
+    info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atimespec.tv_sec) + statbuf.st_atimespec.tv_nsec;
 #else
     info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctime);
     info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtime);