diff --git a/libs/base/System/File/Meta.idr b/libs/base/System/File/Meta.idr index c16293f18fd..063f63fe4d9 100644 --- a/libs/base/System/File/Meta.idr +++ b/libs/base/System/File/Meta.idr @@ -1,12 +1,20 @@ ||| Functions for accessing file metadata. module System.File.Meta +import Data.String + +import System.FFI + import System.File.Handle import System.File.Support import public System.File.Types %default total +||| Pointer to a structure holding File's time attributes +FileTimePtr : Type +FileTimePtr = AnyPtr + %foreign supportC "idris2_fileSize" "node:lambda:fp=>require('fs').fstatSync(fp.fd).size" prim__fileSize : FilePtr -> PrimIO Int @@ -14,25 +22,33 @@ prim__fileSize : FilePtr -> PrimIO Int %foreign supportC "idris2_fileSize" prim__fPoll : FilePtr -> PrimIO Int -%foreign supportC "idris2_fileAccessTime" -prim__fileAccessTime : FilePtr -> PrimIO Int +%foreign supportC "idris2_fileTime" + "node:support:filetime,support_system_file" +prim__fileTime : FilePtr -> PrimIO FileTimePtr + +%foreign supportC "idris2_filetimeAccessTimeSec" + "node:lambda:ft=>ft.atime_sec" +prim__filetimeAccessTimeSec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileAccessTimeNs" -prim__fileAccessTimeNs : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeAccessTimeNsec" + "node:lambda:ft=>ft.atime_nsec" +prim__filetimeAccessTimeNsec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileModifiedTime" - "node:lambda:fp=>require('fs').fstatSync(fp.fd).mtimeMs / 1000" -prim__fileModifiedTime : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeModifiedTimeSec" + "node:lambda:ft=>ft.mtime_sec" +prim__filetimeModifiedTimeSec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileModifiedTimeNs" - "node:lambda:fp=>require('fs').fstatSync(fp.fd).mtimeMs * 1000000 % 1000000000" -prim__fileModifiedTimeNs : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeModifiedTimeNsec" + "node:lambda:ft=>ft.mtime_nsec" +prim__filetimeModifiedTimeNsec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileStatusTime" -prim__fileStatusTime : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeStatusTimeSec" + "node:lambda:ft=>ft.ctime_sec" +prim__filetimeStatusTimeSec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileStatusTimeNs" -prim__fileStatusTimeNs : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeStatusTimeNsec" + "node:lambda:ft=>ft.ctime_nsec" +prim__filetimeStatusTimeNsec : FileTimePtr -> PrimIO Int %foreign supportC "idris2_fileIsTTY" "node:lambda:fp=>Number(require('tty').isatty(fp.fd))" @@ -53,59 +69,70 @@ firstExists : HasIO io => List String -> io (Maybe String) firstExists [] = pure Nothing firstExists (x :: xs) = if !(exists x) then pure (Just x) else firstExists xs -||| Get the File's atime. +||| Record that holds timestamps with nanosecond precision +public export +record Timestamp where + constructor MkTimestamp + sec : Int + nsec : Int + export -fileAccessTime : HasIO io => (h : File) -> io (Either FileError Int) -fileAccessTime (FHandle f) - = do res <- primIO (prim__fileAccessTime f) - if res > 0 - then ok res - else returnError +Eq Timestamp where + t == t' = (t.sec == t'.sec) && (t.nsec == t'.nsec) -||| Get the nanosecond part of File's atime. export -fileAccessTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileAccessTimeNs (FHandle f) - = do res <- primIO (prim__fileAccessTimeNs f) - if res > 0 - then ok res - else returnError +Ord Timestamp where + t < t' = (t.sec < t'.sec) || (t.sec == t'.sec && t.nsec < t'.nsec) -||| Get the File's mtime. export -fileModifiedTime : HasIO io => (h : File) -> io (Either FileError Int) -fileModifiedTime (FHandle f) - = do res <- primIO (prim__fileModifiedTime f) - if res > 0 - then ok res +Show Timestamp where + show t = "\{show t.sec}.\{padLeft 9 '0' $ show t.nsec}" + +||| Record that holds file's time attributes +public export +record FileTime where + constructor MkFileTime + atime : Timestamp + mtime : Timestamp + ctime : Timestamp + +||| Get File's time attributes +export +fileTime : HasIO io => (h : File) -> io (Either FileError FileTime) +fileTime (FHandle f) + = do res <- primIO (prim__fileTime f) + ft <- parseFileTime res + free res + if ft.atime.sec > 0 + then ok ft else returnError + where + parseFileTime : FileTimePtr -> io FileTime + parseFileTime ft = pure $ MkFileTime { atime = MkTimestamp { sec = !(primIO (prim__filetimeAccessTimeSec ft)) + , nsec = !(primIO (prim__filetimeAccessTimeNsec ft)) + } + , mtime = MkTimestamp { sec = !(primIO (prim__filetimeModifiedTimeSec ft)) + , nsec = !(primIO (prim__filetimeModifiedTimeNsec ft)) + } + , ctime = MkTimestamp { sec = !(primIO (prim__filetimeStatusTimeSec ft)) + , nsec = !(primIO (prim__filetimeStatusTimeNsec ft)) + } + } -||| Get the nanosecond part of File's mtime. +||| Get the File's atime. export -fileModifiedTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileModifiedTimeNs (FHandle f) - = do res <- primIO (prim__fileModifiedTimeNs f) - if res > 0 - then ok res - else returnError +fileAccessTime : HasIO io => (h : File) -> io (Either FileError Int) +fileAccessTime h = (fileTime h <&> (.atime.sec)) @{Compose} -||| Get the File's ctime. +||| Get the File's mtime. export -fileStatusTime : HasIO io => (h : File) -> io (Either FileError Int) -fileStatusTime (FHandle f) - = do res <- primIO (prim__fileStatusTime f) - if res > 0 - then ok res - else returnError +fileModifiedTime : HasIO io => (h : File) -> io (Either FileError Int) +fileModifiedTime h = (fileTime h <&> (.mtime.sec)) @{Compose} -||| Get the nanosecond part of File's ctime. +||| Get the File's ctime. export -fileStatusTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileStatusTimeNs (FHandle f) - = do res <- primIO (prim__fileStatusTimeNs f) - if res > 0 - then ok res - else returnError +fileStatusTime : HasIO io => (h : File) -> io (Either FileError Int) +fileStatusTime h = (fileTime h <&> (.ctime.sec)) @{Compose} ||| Get the File's size. export diff --git a/src/Core/Binary/Prims.idr b/src/Core/Binary/Prims.idr index a9787eb5f55..3659baa281c 100644 --- a/src/Core/Binary/Prims.idr +++ b/src/Core/Binary/Prims.idr @@ -12,7 +12,7 @@ import Data.String import Data.Vect import Libraries.Data.PosMap -import Libraries.System.File.Meta as L -- Remove after release 0.7.0 +import public Libraries.System.File.Meta as L -- Remove after release 0.7.0 import public Libraries.Utils.Binary import public Libraries.Utils.String @@ -442,18 +442,15 @@ TTC Nat where ||| Get a file's modified time. If it doesn't exist, return 0 (UNIX Epoch) export -modTime : String -> Core (Int, Int) +modTime : String -> Core Timestamp modTime fname = do Right f <- coreLift $ openFile fname Read - | Left err => pure (0, 0) -- Beginning of Time :) - Right s <- coreLift $ fileModifiedTime f + | Left err => pure $ MkTimestamp 0 0 -- Beginning of Time :) + Right t <- coreLift $ fileTime f | Left err => do coreLift $ closeFile f - pure (0, 0) - Right ns <- coreLift $ L.fileModifiedTimeNs f - | Left err => do coreLift $ closeFile f - pure (0, 0) + pure $ MkTimestamp 0 0 coreLift $ closeFile f - pure (s, ns) + pure $ t.mtime export hashFileWith : Maybe String -> String -> Core (Maybe String) diff --git a/src/Libraries/System/File/Meta.idr b/src/Libraries/System/File/Meta.idr index 5836791ba7a..a1fe07cdc30 100644 --- a/src/Libraries/System/File/Meta.idr +++ b/src/Libraries/System/File/Meta.idr @@ -1,45 +1,110 @@ module Libraries.System.File.Meta +import Data.String + +import System.FFI + import System.File.Handle import System.File.Support import public System.File.Types %default total -%foreign supportC "idris2_fileAccessTimeNs" -prim__fileAccessTimeNs : FilePtr -> PrimIO Int +||| Pointer to a structure holding File's time attributes +FileTimePtr : Type +FileTimePtr = AnyPtr + +%foreign supportC "idris2_fileTime" + "node:support:filetime,support_system_file" +prim__fileTime : FilePtr -> PrimIO FileTimePtr + +%foreign supportC "idris2_filetimeAccessTimeSec" + "node:lambda:ft=>ft.atime_sec" +prim__filetimeAccessTimeSec : FileTimePtr -> PrimIO Int + +%foreign supportC "idris2_filetimeAccessTimeNsec" + "node:lambda:ft=>ft.atime_nsec" +prim__filetimeAccessTimeNsec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileModifiedTimeNs" - "node:lambda:fp=>require('fs').fstatSync(fp.fd).mtimeMs * 1000000 % 1000000000" -prim__fileModifiedTimeNs : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeModifiedTimeSec" + "node:lambda:ft=>ft.mtime_sec" +prim__filetimeModifiedTimeSec : FileTimePtr -> PrimIO Int -%foreign supportC "idris2_fileStatusTimeNs" -prim__fileStatusTimeNs : FilePtr -> PrimIO Int +%foreign supportC "idris2_filetimeModifiedTimeNsec" + "node:lambda:ft=>ft.mtime_nsec" +prim__filetimeModifiedTimeNsec : FileTimePtr -> PrimIO Int + +%foreign supportC "idris2_filetimeStatusTimeSec" + "node:lambda:ft=>ft.ctime_sec" +prim__filetimeStatusTimeSec : FileTimePtr -> PrimIO Int + +%foreign supportC "idris2_filetimeStatusTimeNsec" + "node:lambda:ft=>ft.ctime_nsec" +prim__filetimeStatusTimeNsec : FileTimePtr -> PrimIO Int + +||| Record that holds timestamps with nanosecond precision +public export +record Timestamp where + constructor MkTimestamp + sec : Int + nsec : Int -||| Get the nanosecond part of File's atime. export -fileAccessTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileAccessTimeNs (FHandle f) - = do res <- primIO (prim__fileAccessTimeNs f) - if res > 0 - then ok res - else returnError +Eq Timestamp where + t == t' = (t.sec == t'.sec) && (t.nsec == t'.nsec) -||| Get the nanosecond part of File's mtime. export -fileModifiedTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileModifiedTimeNs (FHandle f) - = do res <- primIO (prim__fileModifiedTimeNs f) - if res > 0 - then ok res - else returnError +Ord Timestamp where + t < t' = (t.sec < t'.sec) || (t.sec == t'.sec && t.nsec < t'.nsec) -||| Get the nanosecond part of File's ctime. export -fileStatusTimeNs : HasIO io => (h : File) -> io (Either FileError Int) -fileStatusTimeNs (FHandle f) - = do res <- primIO (prim__fileStatusTimeNs f) - if res > 0 - then ok res +Show Timestamp where + show t = "\{show t.sec}.\{padLeft 9 '0' $ show t.nsec}" + +||| Record that holds file's time attributes +public export +record FileTime where + constructor MkFileTime + atime : Timestamp + mtime : Timestamp + ctime : Timestamp + +||| Get File's time attributes +export +fileTime : HasIO io => (h : File) -> io (Either FileError FileTime) +fileTime (FHandle f) + = do res <- primIO (prim__fileTime f) + ft <- parseFileTime res + free res + if ft.atime.sec > 0 + then ok ft else returnError + where + parseFileTime : FileTimePtr -> io FileTime + parseFileTime ft = pure $ MkFileTime { atime = MkTimestamp { sec = !(primIO (prim__filetimeAccessTimeSec ft)) + , nsec = !(primIO (prim__filetimeAccessTimeNsec ft)) + } + , mtime = MkTimestamp { sec = !(primIO (prim__filetimeModifiedTimeSec ft)) + , nsec = !(primIO (prim__filetimeModifiedTimeNsec ft)) + } + , ctime = MkTimestamp { sec = !(primIO (prim__filetimeStatusTimeSec ft)) + , nsec = !(primIO (prim__filetimeStatusTimeNsec ft)) + } + } + +||| Get the File's atime. +export +fileAccessTime : HasIO io => (h : File) -> io (Either FileError Int) +fileAccessTime h = (fileTime h <&> (.atime.sec)) @{Compose} + +||| Get the File's mtime. +export +fileModifiedTime : HasIO io => (h : File) -> io (Either FileError Int) +fileModifiedTime h = (fileTime h <&> (.mtime.sec)) @{Compose} + +||| Get the File's ctime. +export +fileStatusTime : HasIO io => (h : File) -> io (Either FileError Int) +fileStatusTime h = (fileTime h <&> (.ctime.sec)) @{Compose} + diff --git a/support/c/idris_file.c b/support/c/idris_file.c index d43d60311e5..abf7a47925f 100644 --- a/support/c/idris_file.c +++ b/support/c/idris_file.c @@ -177,112 +177,75 @@ size_t idris2_writeBufferData(FILE *h, const char *buffer, size_t loc, int idris2_eof(FILE *f) { return feof(f); } -int idris2_fileAccessTime(FILE *f) { - int fd = idris2_getFileNo(f); +struct filetime { + int atime_sec; + int atime_nsec; + int mtime_sec; + int mtime_nsec; + int ctime_sec; + int ctime_nsec; +}; - struct stat buf; - if (fstat(fd, &buf) == 0) { - return buf.st_atime; - } else { - return -1; - } -} +struct filetime *idris2_fileTime(FILE *f) { + struct filetime *ft = malloc(sizeof(*ft)); -int idris2_fileAccessTimeNs(FILE *f) { #ifdef _WIN32 - int64_t sec, nsec; - if (win32_getFileAccessTime(f, &sec, &nsec)) { - return -1; + if (win32_getFileTimes(f, &ft->atime_sec, &ft->atime_nsec, &ft->mtime_sec, + &ft->mtime_nsec, &ft->ctime_sec, &ft->ctime_nsec_)) { + ft->atime_sec = -1; + return ft; } - return nsec; -#else - int fd = idris2_getFileNo(f); - struct stat buf; - if (fstat(fd, &buf) == 0) { -#if defined(__MACH__) || defined(__APPLE__) - return buf.st_atimespec.tv_nsec; -#elif (_POSIX_VERSION >= 200809L) || defined(__FreeBSD__) - return buf.st_atim.tv_nsec; + return ft; #else - return 0; -#endif - } else { - return -1; - } -#endif -} - -int idris2_fileModifiedTime(FILE *f) { int fd = idris2_getFileNo(f); struct stat buf; - if (fstat(fd, &buf) == 0) { - return buf.st_mtime; - } else { - return -1; + if (fstat(fd, &buf)) { + ft->atime_sec = -1; + return ft; } -} -int idris2_fileModifiedTimeNs(FILE *f) { -#ifdef _WIN32 - int64_t sec, nsec; - if (win32_getFileModifiedTime(f, &sec, &nsec)) { - return -1; - } - return nsec; -#else - int fd = idris2_getFileNo(f); + ft->atime_sec = buf.st_atime; + ft->mtime_sec = buf.st_mtime; + ft->ctime_sec = buf.st_ctime; - struct stat buf; - if (fstat(fd, &buf) == 0) { #if defined(__MACH__) || defined(__APPLE__) - return buf.st_mtimespec.tv_nsec; + ft->atime_nsec = buf.st_atimespec.tv_nsec; + ft->mtime_nsec = buf.st_mtimespec.tv_nsec; + ft->ctime_nsec = buf.st_ctimespec.tv_nsec; #elif (_POSIX_VERSION >= 200809L) || defined(__FreeBSD__) - return buf.st_mtim.tv_nsec; + ft->atime_nsec = buf.st_atim.tv_nsec; + ft->mtime_nsec = buf.st_mtim.tv_nsec; + ft->ctime_nsec = buf.st_ctim.tv_nsec; #else - return 0; + ft->atime_nsec = 0; + ft->mtime_nsec = 0; + ft->ctime_nsec = 0; #endif - } else { - return -1; - } + + return ft; #endif } -int idris2_fileStatusTime(FILE *f) { - int fd = idris2_getFileNo(f); +int idris2_filetimeAccessTimeSec(struct filetime *ft) { return ft->atime_sec; } - struct stat buf; - if (fstat(fd, &buf) == 0) { - return buf.st_ctime; - } else { - return -1; - } +int idris2_filetimeAccessTimeNsec(struct filetime *ft) { + return ft->atime_nsec; } -int idris2_fileStatusTimeNs(FILE *f) { -#ifdef _WIN32 - int64_t sec, nsec; - if (win32_getFileStatusTime(f, &sec, &nsec)) { - return -1; - } - return nsec; -#else - int fd = idris2_getFileNo(f); +int idris2_filetimeModifiedTimeSec(struct filetime *ft) { + return ft->mtime_sec; +} - struct stat buf; - if (fstat(fd, &buf) == 0) { -#if defined(__MACH__) || defined(__APPLE__) - return buf.st_ctimespec.tv_nsec; -#elif (_POSIX_VERSION >= 200809L) || defined(__FreeBSD__) - return buf.st_ctim.tv_nsec; -#else - return 0; -#endif - } else { - return -1; - } -#endif +int idris2_filetimeModifiedTimeNsec(struct filetime *ft) { + return ft->mtime_nsec; +} + +int idris2_filetimeStatusTimeSec(struct filetime *ft) { return ft->ctime_sec; } + +int idris2_filetimeStatusTimeNsec(struct filetime *ft) { + return ft->ctime_nsec; } int idris2_fileIsTTY(FILE *f) { diff --git a/support/c/idris_file.h b/support/c/idris_file.h index cf9df9e697b..7e259772c2a 100644 --- a/support/c/idris_file.h +++ b/support/c/idris_file.h @@ -39,13 +39,17 @@ size_t idris2_writeBufferData(FILE *h, const char *buffer, size_t loc, size_t len); int idris2_eof(FILE *f); -int idris2_fileAccessTime(FILE *f); -int idris2_fileAccessTimeNs(FILE *f); -int idris2_fileModifiedTime(FILE *f); -int idris2_fileModifiedTimeNs(FILE *f); -int idris2_fileStatusTime(FILE *f); -int idris2_fileStatusTimeNs(FILE *f); -int idris2_fileIsTTY(FILE *f); + +struct filetime; + +struct filetime *idris2_fileTime(FILE *f); + +int idris2_filetimeAccessTimeSec(struct filetime *f); +int idris2_filetimeAccessTimeNsec(struct filetime *f); +int idris2_filetimeModifiedTimeSec(struct filetime *f); +int idris2_filetimeModifiedTimeNsec(struct filetime *f); +int idris2_filetimeStatusTimeSec(struct filetime *f); +int idris2_filetimeStatusTimeNsec(struct filetime *f); FILE *idris2_stdin(); FILE *idris2_stdout(); diff --git a/support/c/windows/win_utils.c b/support/c/windows/win_utils.c index b6ecca3b5c0..37fa235c16b 100644 --- a/support/c/windows/win_utils.c +++ b/support/c/windows/win_utils.c @@ -228,4 +228,42 @@ int win32_getFileStatusTime(FILE *f, int64_t *sec, int64_t *nsec) { } } +int win32_getFileTime(FILE *f, int64_t *atime_sec, int64_t *atime_nsec, + int64_t *mtime_sec, int64_t *mtime_nsec, + int64_t *ctime_sec, int64_t ctime_nsec) { + HANDLE wh = (HANDLE)_get_osfhandle(_fileno(f)); + if (wh == INVALID_HANDLE_VALUE) { + return -1; + } + + FILETIME atime, mtime, ctime; + + if (GetFileTime(wh, &ctime, &atime, &mtime)) { + ULARGE_INTEGER at, mt, ct; + + at.HighPart = atime.dwHighDateTime; + at.LowPart = atime.dwLowDateTime; + mt.HighPart = mtime.dwHighDateTime; + mt.LowPart = mtime.dwLowDateTime; + ct.HighPart = ctime.dwHighDateTime; + ct.LowPart = ctime.dwLowDateTime; + + *atime_sec = at.QuadPart / 10000000; + *atime_sec -= 11644473600; + *atime_nsec = (at.QuadPart % 10000000) * 100; + + *mtime_sec = mt.QuadPart / 10000000; + *mtime_sec -= 11644473600; + *mtime_nsec = (mt.QuadPart % 10000000) * 100; + + *ctime_sec = ct.QuadPart / 10000000; + *ctime_sec -= 11644473600; + *ctime_nsec = (ct.QuadPart % 10000000) * 100; + + return 0; + } else { + return -1; + } +} + int win32_isTTY(int fd) { return _isatty(fd); } diff --git a/support/c/windows/win_utils.h b/support/c/windows/win_utils.h index 6c0885d31d6..67c2690079f 100644 --- a/support/c/windows/win_utils.h +++ b/support/c/windows/win_utils.h @@ -19,4 +19,7 @@ int win32_getFileNo(FILE *); int win32_getFileAccessTime(FILE *f, int64_t *sec, int64_t *nsec); int win32_getFileModifiedTime(FILE *f, int64_t *sec, int64_t *nsec); int win32_getFileStatusTime(FILE *f, int64_t *sec, int64_t *nsec); +int win32_getFileTime(FILE *f, int64_t *atime_sec, int64_t *atime_nsec, + int64_t *mtime_sec, int64_t *mtime_nsec, + int64_t *ctime_sec, int64_t ctime_nsec); int win32_isTTY(int fd); diff --git a/support/js/support_system_file.js b/support/js/support_system_file.js index 9118cb44135..c85fa578c7e 100644 --- a/support/js/support_system_file.js +++ b/support/js/support_system_file.js @@ -167,3 +167,16 @@ function support_system_file_pclose (file_ptr) { support_system_file_removeFile(name) return exit_code } + +function support_system_file_filetime(file_ptr) { + const {fd, name, exit_code} = file_ptr + const st = support_system_file_fs.fstatSync(fd) + const ft = { + atime_sec : _truncInt32(Math.trunc(st.atimeMs / 1000)), + atime_nsec : st.atimeMs * 1000000 % 1000000000, + mtime_sec : _truncInt32(Math.trunc(st.mtimeMs / 1000)), + mtime_nsec : st.mtimeMs * 1000000 % 1000000000, + ctime_sec : _truncInt32(Math.trunc(st.ctimeMs / 1000)), + ctime_nsec : st.mtimeMs * 1000000 % 1000000000 + } return ft +}