{-|
Module      : Z.IO.FileSystem.Threaded
Description : Filesystem IO using threadpool
Copyright   : (c) Dong Han, 2017-2020
License     : BSD
Maintainer  : winterland1989@gmail.com
Stability   : experimental
Portability : non-portable

This module provides filesystem API exactly same with `Z.IO.FileSystem`, operations(except 'seek') are implemented using libuv's threadpool to achieve non-block behavior (non-block here meaning won't block other haskell threads), which would be prefered when the operations' estimated time is long(>1ms) or running with a non-threaded haskell runtime, such as accessing network filesystem or scan a very large directory. Otherwise you may block RTS's capability thus all the other haskell threads live on it.

The threadpool version operations have overheads similar to safe FFI, but provide same adventages:

  * The libuv's threadpool have a limit on concurrent threads number (4 by default), which can reduce disk contention.
  * The threadpool version works with non-threaded runtime, which doesn't have safe FFI available.
  * The threadpool version won't relinquish current HEC (Haskell Execution Context) a.k.a. capability.

Most of the time you don't need this module though, since modern hardware(SSD) are sufficiently fast.

-}

module Z.IO.FileSystem.Threaded
  ( -- * regular file devices
    File, initFile, readFileP, writeFileP, getFileFD, seek
  , readFile, readTextFile, writeFile, writeTextFile
  , readJSONFile, writeJSONFile
    -- * file offset bundle
  , FilePtrT, newFilePtrT, getFilePtrOffset, setFilePtrOffset
  -- * filesystem operations
  , mkdir, mkdirp
  , unlink
  , mkdtemp
  , rmdir, rmdirrf
  , DirEntType(..)
  , scandir
  , scandirRecursively
  , FStat(..), UVTimeSpec(..)
  , stat, lstat, fstat
  , isLink, isDir, isFile
  , rename
  , fsync, fdatasync
  , ftruncate
  , copyfile
  , AccessResult(..)
  , access
  , chmod, fchmod
  , utime, futime, lutime
  , link, symlink
  , readlink, realpath
  , chown, fchown, lchown
  -- * opening constant
  -- ** AccessMode
  , AccessMode
  , pattern F_OK
  , pattern R_OK
  , pattern W_OK
  , pattern X_OK
  -- ** FileMode
  , FileMode
  , pattern DEFAULT_MODE
  , pattern S_IRWXU
  , pattern S_IRUSR
  , pattern S_IWUSR
  , pattern S_IXUSR
  , pattern S_IRWXG
  , pattern S_IRGRP
  , pattern S_IWGRP
  , pattern S_IXGRP
  , pattern S_IRWXO
  , pattern S_IROTH
  -- ** file type constant
  , pattern S_IFMT
  , pattern S_IFLNK
  , pattern S_IFDIR
  , pattern S_IFREG
  -- ** FileFlag
  , FileFlag
  , pattern O_APPEND
  , pattern O_CREAT
  , pattern O_DIRECT
  , pattern O_DSYNC
  , pattern O_EXCL
  , pattern O_EXLOCK
  , pattern O_NOATIME
  , pattern O_NOFOLLOW
  , pattern O_RDONLY
  , pattern O_RDWR
  , pattern O_SYMLINK
  , pattern O_SYNC
  , pattern O_TRUNC
  , pattern O_WRONLY
  , pattern O_RANDOM
  , pattern O_SHORT_LIVED
  , pattern O_SEQUENTIAL
  , pattern O_TEMPORARY
  -- ** CopyFileFlag
  , CopyFileFlag
  , pattern COPYFILE_DEFAULT
  , pattern COPYFILE_EXCL
  , pattern COPYFILE_FICLONE
  , pattern COPYFILE_FICLONE_FORCE
  -- ** SymlinkFlag
  , SymlinkFlag
  , pattern SYMLINK_DEFAULT
  , pattern SYMLINK_DIR
  , pattern SYMLINK_JUNCTION
  ) where

import           Control.Monad
import           Data.Bits
import           Data.Int
import           Data.IORef
import           Data.Word
import           Foreign.Ptr
import           Foreign.Storable               (peekElemOff)
import           Foreign.Marshal.Alloc          (allocaBytes)
import           Z.Data.CBytes                  as CBytes
import           Z.Data.PrimRef.PrimIORef
import qualified Z.Data.Text                    as T
import qualified Z.Data.Text.Print              as T
import qualified Z.Data.Vector                  as V
import qualified Z.Data.JSON                    as JSON
import           Z.Foreign
import           Z.IO.Buffered
import           Z.IO.Exception
import qualified Z.IO.FileSystem.FilePath       as P
import           Z.IO.Resource
import           Z.IO.UV.FFI
import           Z.IO.UV.Manager
import           Prelude hiding                 (writeFile, readFile)

--------------------------------------------------------------------------------
-- File

-- | 'File' and its operations are NOT thread safe, use 'MVar' 'File' in multiple threads.
--
-- Note this is a differet data type from "Z.IO.FileSystem" \'s one, the 'Input'
-- and 'Output' instance use thread pool version functions.
--
-- libuv implements read and write method with both implict and explict offset capable.
-- Implict offset interface is provided by 'Input' \/ 'Output' instances.
-- Explict offset interface is provided by 'readFileP' \/ 'writeFileP'.
--
data File = File {-# UNPACK #-} !FD      -- ^ the file
                 {-# UNPACK #-} !(IORef Bool)  -- ^ closed flag

instance Show File where show :: File -> String
show = File -> String
forall a. Print a => a -> String
T.toString

instance T.Print File where
    toUTF8BuilderP :: Int -> File -> Builder ()
toUTF8BuilderP Int
_ (File FD
fd IORef Bool
_) = Builder ()
"File " Builder () -> Builder () -> Builder ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FD -> Builder ()
forall a. (Integral a, Bounded a) => a -> Builder ()
T.int FD
fd

-- | Return File fd.
getFileFD :: File -> IO FD
getFileFD :: File -> IO FD
getFileFD (File FD
fd IORef Bool
closedRef) = do
    Bool
closed <- IORef Bool -> IO Bool
forall a. IORef a -> IO a
readIORef IORef Bool
closedRef
    if Bool
closed then IO FD
forall a. HasCallStack => IO a
throwECLOSED else FD -> IO FD
forall (m :: * -> *) a. Monad m => a -> m a
return FD
fd

-- | If fd is -1 (closed), throw 'ResourceVanished' ECLOSED.
checkFileClosed :: HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed :: File -> (FD -> IO a) -> IO a
checkFileClosed (File FD
fd IORef Bool
closedRef) FD -> IO a
f = do
    Bool
closed <- IORef Bool -> IO Bool
forall a. IORef a -> IO a
readIORef IORef Bool
closedRef
    if Bool
closed then IO a
forall a. HasCallStack => IO a
throwECLOSED else FD -> IO a
f FD
fd

-- | Set file's system offset.
--
-- Equivalent to <https://linux.die.net/man/3/lseek64 lseek64(3)>.
seek :: HasCallStack => File -> Int64 -> Whence -> IO Int64
seek :: File -> Int64 -> FD -> IO Int64
seek File
uvf Int64
off FD
w = File -> (FD -> IO Int64) -> IO Int64
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO Int64) -> IO Int64) -> (FD -> IO Int64) -> IO Int64
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> IO Int64 -> IO Int64
forall a. (HasCallStack, Integral a) => IO a -> IO a
throwUVIfMinus (IO Int64 -> IO Int64) -> IO Int64 -> IO Int64
forall a b. (a -> b) -> a -> b
$ FD -> Int64 -> FD -> IO Int64
hs_seek FD
fd Int64
off FD
w

instance Input File where
    readInput :: File -> Ptr Word8 -> Int -> IO Int
readInput File
f Ptr Word8
buf Int
bufSiz = HasCallStack => File -> Ptr Word8 -> Int -> Int64 -> IO Int
File -> Ptr Word8 -> Int -> Int64 -> IO Int
readFileP File
f Ptr Word8
buf Int
bufSiz (-Int64
1)

-- | Read file with given offset
--
-- Read length may be smaller than buffer size.
readFileP :: HasCallStack
          => File
          -> Ptr Word8 -- ^ buffer
          -> Int       -- ^ buffer size
          -> Int64     -- ^ file offset, pass -1 to use default(system) offset
          -> IO Int    -- ^ read length
readFileP :: File -> Ptr Word8 -> Int -> Int64 -> IO Int
readFileP File
uvf Ptr Word8
buf Int
bufSiz Int64
off =
    File -> (FD -> IO Int) -> IO Int
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf  ((FD -> IO Int) -> IO Int) -> (FD -> IO Int) -> IO Int
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
        UVManager
uvm <- IO UVManager
getUVManager
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
withUVRequest UVManager
uvm (FD -> Ptr Word8 -> Int -> Int64 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_read_threaded FD
fd Ptr Word8
buf Int
bufSiz Int64
off)

instance Output File where
    writeOutput :: File -> Ptr Word8 -> Int -> IO ()
writeOutput File
f Ptr Word8
buf Int
bufSiz = HasCallStack => File -> Ptr Word8 -> Int -> Int64 -> IO ()
File -> Ptr Word8 -> Int -> Int64 -> IO ()
writeFileP File
f Ptr Word8
buf Int
bufSiz (-Int64
1)

-- | Write buffer to file
--
-- This function will loop until all bytes are written.
--
-- Note on linux files opened with 'O_APPEND' behave differently since this function use @pwrite@:
--
-- @
-- POSIX requires that opening a file with the O_APPEND flag should have no effect
-- on the location at which pwrite() writes data. However, on Linux,
-- if a file is opened with O_APPEND, pwrite() appends data to the end of the file,
-- regardless of the value of offset.
-- @
writeFileP :: HasCallStack
           => File
           -> Ptr Word8 -- ^ buffer
           -> Int       -- ^ buffer size
           -> Int64     -- ^ file offset, pass -1 to use default(system) offset
           -> IO ()
writeFileP :: File -> Ptr Word8 -> Int -> Int64 -> IO ()
writeFileP File
uvf Ptr Word8
buf0 Int
bufSiz0 Int64
off0 =
    File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
             (if Int64
off0 Int64 -> Int64 -> Bool
forall a. Eq a => a -> a -> Bool
== -Int64
1 then FD -> Ptr Word8 -> Int -> IO ()
go FD
fd Ptr Word8
buf0 Int
bufSiz0
                            else FD -> Ptr Word8 -> Int -> Int64 -> IO ()
go' FD
fd Ptr Word8
buf0 Int
bufSiz0 Int64
off0)
  where
    -- use -1 offset to use fd's default offset
    go :: FD -> Ptr Word8 -> Int -> IO ()
go FD
fd Ptr Word8
buf Int
bufSiz = do
        UVManager
uvm <- IO UVManager
getUVManager
        Int
written <- HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
withUVRequest UVManager
uvm
            (FD -> Ptr Word8 -> Int -> Int64 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_write_threaded FD
fd Ptr Word8
buf Int
bufSiz (-Int64
1))
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
written Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
bufSiz)
            (FD -> Ptr Word8 -> Int -> IO ()
go FD
fd (Ptr Word8
buf Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
written) (Int
bufSizInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
written))

    go' :: FD -> Ptr Word8 -> Int -> Int64 -> IO ()
go' FD
fd Ptr Word8
buf Int
bufSiz !Int64
off = do
        UVManager
uvm <- IO UVManager
getUVManager
        Int
written <- HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
withUVRequest UVManager
uvm
            (FD -> Ptr Word8 -> Int -> Int64 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_write_threaded FD
fd Ptr Word8
buf Int
bufSiz Int64
off)
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
written Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
bufSiz) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
            FD -> Ptr Word8 -> Int -> Int64 -> IO ()
go' FD
fd (Ptr Word8
buf Ptr Word8 -> Int -> Ptr Word8
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` Int
written)
                   (Int
bufSizInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
written)
                   (Int64
offInt64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
written)

-- | File bundled with offset.
--
-- Reading or writing using 'Input' \/ 'Output' instance will automatically increase offset.
-- 'FilePtrT' and its operations are NOT thread safe, use 'MVar' 'FilePtrT' in multiple threads.
--
-- The notes on linux 'writeFileP' applied to 'FilePtr' too.
data FilePtrT = FilePtrT {-# UNPACK #-} !File
                         {-# UNPACK #-} !(PrimIORef Int64)

-- |  Create a file offset bundle from an 'File'.
--
newFilePtrT :: File      -- ^ the file we're reading
            -> Int64      -- ^ initial offset
            -> IO FilePtrT
newFilePtrT :: File -> Int64 -> IO FilePtrT
newFilePtrT File
uvf Int64
off = File -> PrimIORef Int64 -> FilePtrT
FilePtrT File
uvf (PrimIORef Int64 -> FilePtrT)
-> IO (PrimIORef Int64) -> IO FilePtrT
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int64 -> IO (PrimIORef Int64)
forall a. Prim a => a -> IO (PrimIORef a)
newPrimIORef Int64
off

-- | Get current offset.
getFilePtrOffset :: FilePtrT -> IO Int64
getFilePtrOffset :: FilePtrT -> IO Int64
getFilePtrOffset (FilePtrT File
_ PrimIORef Int64
offsetRef) = PrimIORef Int64 -> IO Int64
forall a. Prim a => PrimIORef a -> IO a
readPrimIORef PrimIORef Int64
offsetRef

-- | Change current offset.
setFilePtrOffset :: FilePtrT -> Int64 -> IO ()
setFilePtrOffset :: FilePtrT -> Int64 -> IO ()
setFilePtrOffset (FilePtrT File
_ PrimIORef Int64
offsetRef) = PrimIORef Int64 -> Int64 -> IO ()
forall a. Prim a => PrimIORef a -> a -> IO ()
writePrimIORef PrimIORef Int64
offsetRef

instance Input FilePtrT where
    readInput :: FilePtrT -> Ptr Word8 -> Int -> IO Int
readInput (FilePtrT File
file PrimIORef Int64
offsetRef) Ptr Word8
buf Int
bufSiz =
        PrimIORef Int64 -> IO Int64
forall a. Prim a => PrimIORef a -> IO a
readPrimIORef PrimIORef Int64
offsetRef IO Int64 -> (Int64 -> IO Int) -> IO Int
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ Int64
off -> do
            Int
l <- HasCallStack => File -> Ptr Word8 -> Int -> Int64 -> IO Int
File -> Ptr Word8 -> Int -> Int64 -> IO Int
readFileP File
file Ptr Word8
buf Int
bufSiz Int64
off
            PrimIORef Int64 -> Int64 -> IO ()
forall a. Prim a => PrimIORef a -> a -> IO ()
writePrimIORef PrimIORef Int64
offsetRef (Int64
off Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
l)
            Int -> IO Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
l

instance Output FilePtrT where
    writeOutput :: FilePtrT -> Ptr Word8 -> Int -> IO ()
writeOutput (FilePtrT File
file PrimIORef Int64
offsetRef) Ptr Word8
buf Int
bufSiz =
        PrimIORef Int64 -> IO Int64
forall a. Prim a => PrimIORef a -> IO a
readPrimIORef PrimIORef Int64
offsetRef IO Int64 -> (Int64 -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ Int64
off -> do
            HasCallStack => File -> Ptr Word8 -> Int -> Int64 -> IO ()
File -> Ptr Word8 -> Int -> Int64 -> IO ()
writeFileP File
file Ptr Word8
buf Int
bufSiz Int64
off
            PrimIORef Int64 -> Int64 -> IO ()
forall a. Prim a => PrimIORef a -> a -> IO ()
writePrimIORef PrimIORef Int64
offsetRef (Int64
off Int64 -> Int64 -> Int64
forall a. Num a => a -> a -> a
+ Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
bufSiz)

--------------------------------------------------------------------------------

-- | init a file 'Resource', which open a file when used.
--
-- Resource closing will wait for the referencing counter goes
-- down to zero (no reading or writing is in process), which can
-- be a problem if you are using multiple readers or writers in multiple threads.
-- In that case you have to stop all reading or writing thread if you don't want to
-- block the resource thread.
initFile :: CBytes
          -> FileFlag        -- ^ Opening flags, e.g. 'O_CREAT' @.|.@ 'O_RDWR'
          -> FileMode        -- ^ Sets the file mode (permission and sticky bits),
                               -- but only if the file was created, see 'DEFAULT_MODE'.
          -> Resource File
initFile :: CBytes -> FD -> FD -> Resource File
initFile CBytes
path FD
flags FD
mode =
    IO File -> (File -> IO ()) -> Resource File
forall a. IO a -> (a -> IO ()) -> Resource a
initResource
        (do UVManager
uvm <- IO UVManager
getUVManager
            Int
fd <- CBytes -> (BA# Word8 -> IO Int) -> IO Int
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO Int) -> IO Int)
-> (BA# Word8 -> IO Int) -> IO Int
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
                HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
withUVRequest UVManager
uvm (BA# Word8 -> FD -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_open_threaded BA# Word8
p FD
flags FD
mode)
            FD -> IORef Bool -> File
File (Int -> FD
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
fd) (IORef Bool -> File) -> IO (IORef Bool) -> IO File
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Bool -> IO (IORef Bool)
forall a. a -> IO (IORef a)
newIORef Bool
False)
        (\ (File FD
fd IORef Bool
closedRef) -> do
            Bool
closed <- IORef Bool -> IO Bool
forall a. IORef a -> IO a
readIORef IORef Bool
closedRef
            Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
closed (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
                IO Int -> IO ()
forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (FD -> IO Int
hs_uv_fs_close FD
fd)
                IORef Bool -> Bool -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef Bool
closedRef Bool
True)

-- | Quickly open a file and read its content.
readFile :: HasCallStack => CBytes -> IO V.Bytes
readFile :: CBytes -> IO Bytes
readFile CBytes
filename = do
    Resource File -> (File -> IO Bytes) -> IO Bytes
forall (m :: * -> *) a b.
(MonadMask m, MonadIO m, HasCallStack) =>
Resource a -> (a -> m b) -> m b
withResource (CBytes -> FD -> FD -> Resource File
initFile CBytes
filename FD
O_RDONLY FD
DEFAULT_MODE) ((File -> IO Bytes) -> IO Bytes) -> (File -> IO Bytes) -> IO Bytes
forall a b. (a -> b) -> a -> b
$ \ File
file -> do
        HasCallStack => BufferedInput -> IO Bytes
BufferedInput -> IO Bytes
readAll' (BufferedInput -> IO Bytes) -> IO BufferedInput -> IO Bytes
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< File -> IO BufferedInput
forall i. Input i => i -> IO BufferedInput
newBufferedInput File
file

-- | Quickly open a file and read its content as UTF8 text.
readTextFile :: HasCallStack => CBytes -> IO T.Text
readTextFile :: CBytes -> IO Text
readTextFile CBytes
filename = HasCallStack => Bytes -> Text
Bytes -> Text
T.validate (Bytes -> Text) -> IO Bytes -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HasCallStack => CBytes -> IO Bytes
CBytes -> IO Bytes
readFile CBytes
filename

-- | Quickly open a file and write some content.
writeFile :: HasCallStack => CBytes -> V.Bytes -> IO ()
writeFile :: CBytes -> Bytes -> IO ()
writeFile CBytes
filename Bytes
content = do
    Resource File -> (File -> IO ()) -> IO ()
forall (m :: * -> *) a b.
(MonadMask m, MonadIO m, HasCallStack) =>
Resource a -> (a -> m b) -> m b
withResource (CBytes -> FD -> FD -> Resource File
initFile CBytes
filename (FD
O_WRONLY FD -> FD -> FD
forall a. Bits a => a -> a -> a
.|. FD
O_CREAT) FD
DEFAULT_MODE) ((File -> IO ()) -> IO ()) -> (File -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ File
file -> do
        Bytes -> (Ptr Word8 -> Int -> IO ()) -> IO ()
forall a b.
Prim a =>
PrimVector a -> (Ptr a -> Int -> IO b) -> IO b
withPrimVectorSafe Bytes
content (File -> Ptr Word8 -> Int -> IO ()
forall o. Output o => o -> Ptr Word8 -> Int -> IO ()
writeOutput File
file)

-- | Quickly open a file and write some content as UTF8 text.
writeTextFile :: HasCallStack => CBytes -> T.Text -> IO ()
writeTextFile :: CBytes -> Text -> IO ()
writeTextFile CBytes
filename Text
content = HasCallStack => CBytes -> Bytes -> IO ()
CBytes -> Bytes -> IO ()
writeFile CBytes
filename (Text -> Bytes
T.getUTF8Bytes Text
content)

-- | Quickly open a file and read its content as a JSON value.
-- Throw 'OtherError' with name @EJSON@ if JSON value is not parsed or converted.
readJSONFile :: (HasCallStack, JSON.JSON a) => CBytes -> IO a
readJSONFile :: CBytes -> IO a
readJSONFile CBytes
filename = Either DecodeError a -> IO a
forall e a.
(HasCallStack, Show e, Typeable e) =>
Either e a -> IO a
unwrap (Either DecodeError a -> IO a)
-> (Bytes -> Either DecodeError a) -> Bytes -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bytes -> Either DecodeError a
forall a. JSON a => Bytes -> Either DecodeError a
JSON.decode' (Bytes -> IO a) -> IO Bytes -> IO a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< HasCallStack => CBytes -> IO Bytes
CBytes -> IO Bytes
readFile CBytes
filename

-- | Quickly open a file and write a JSON Value.
writeJSONFile :: (HasCallStack, JSON.JSON a) => CBytes -> a -> IO ()
writeJSONFile :: CBytes -> a -> IO ()
writeJSONFile CBytes
filename a
x = HasCallStack => CBytes -> Bytes -> IO ()
CBytes -> Bytes -> IO ()
writeFile CBytes
filename (a -> Bytes
forall a. JSON a => a -> Bytes
JSON.encode a
x)

--------------------------------------------------------------------------------

-- | Equivalent to <http://linux.die.net/man/2/mkdir mkdir(2)>.
--
-- Note mode is currently not implemented on Windows. On unix you should set execute bit
-- if you want the directory is accessable, e.g. 0o777.
mkdir :: HasCallStack => CBytes -> FileMode -> IO ()
mkdir :: CBytes -> FD -> IO ()
mkdir CBytes
path FD
mode = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_mkdir_threaded BA# Word8
p FD
mode)

-- | Equivalent to @mkdir -p@
--
-- Note mode is currently not implemented on Windows. On unix you should set execute bit
-- if you want the directory is accessable(so that child folder can be created), e.g. 0o777.
mkdirp :: HasCallStack => CBytes -> FileMode -> IO ()
mkdirp :: CBytes -> FD -> IO ()
mkdirp CBytes
path FD
mode = do
    UVManager
uvm <- IO UVManager
getUVManager
    Int
r <- CBytes -> (BA# Word8 -> IO Int) -> IO Int
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO Int) -> IO Int)
-> (BA# Word8 -> IO Int) -> IO Int
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO Int) -> IO Int
forall b.
HasCallStack =>
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO b) -> IO b
withUVRequest' UVManager
uvm (BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_mkdir_threaded BA# Word8
p FD
mode)
            (\ Int
r -> do
                Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
r Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0 Bool -> Bool -> Bool
&& Int -> FD
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
r FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
/= FD
UV_ENOENT)
                    (IO Int -> IO ()
forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Int -> IO Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
r))
                Int -> IO Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
r)
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int -> FD
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
r FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
UV_ENOENT) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        (CBytes
root, [CBytes]
segs) <- CBytes -> IO (CBytes, [CBytes])
P.splitSegments CBytes
path
        case [CBytes]
segs of
            CBytes
seg:[CBytes]
segs' -> [CBytes] -> CBytes -> IO ()
loop [CBytes]
segs' (CBytes -> IO ()) -> IO CBytes -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< CBytes -> CBytes -> IO CBytes
P.join CBytes
root CBytes
seg
            [CBytes]
_ -> IO Int -> IO ()
forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (Int -> IO Int
forall (m :: * -> *) a. Monad m => a -> m a
return Int
r)
  where
    loop :: [CBytes] -> CBytes -> IO ()
loop [CBytes]
segs CBytes
p = do
        AccessResult
a <- HasCallStack => CBytes -> FD -> IO AccessResult
CBytes -> FD -> IO AccessResult
access CBytes
p FD
F_OK
        case AccessResult
a of
            AccessResult
AccessOK     -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
            AccessResult
NoExistence  -> HasCallStack => CBytes -> FD -> IO ()
CBytes -> FD -> IO ()
mkdir CBytes
p FD
mode
            AccessResult
NoPermission -> IO FD -> IO ()
forall a. (HasCallStack, Integral a) => IO a -> IO ()
throwUVIfMinus_ (FD -> IO FD
forall (m :: * -> *) a. Monad m => a -> m a
return FD
UV_EACCES)
        case [CBytes]
segs of
            (CBytes
nextp:[CBytes]
ps) -> CBytes -> CBytes -> IO CBytes
P.join CBytes
p CBytes
nextp IO CBytes -> (CBytes -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [CBytes] -> CBytes -> IO ()
loop [CBytes]
ps
            [CBytes]
_  -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Equivalent to <http://linux.die.net/man/2/unlink unlink(2)>.
unlink :: HasCallStack => CBytes -> IO ()
unlink :: CBytes -> IO ()
unlink CBytes
path = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_unlink_threaded BA# Word8
p)

-- | Equivalent to <mkdtemp http://linux.die.net/man/3/mkdtemp>
--
-- Creates a temporary directory in the most secure manner possible.
-- There are no race conditions in the directory’s creation.
-- The directory is readable, writable, and searchable only by the creating user ID.
-- The user of mkdtemp() is responsible for deleting the temporary directory and
-- its contents when done with it.
--
-- Note: the argument is the prefix of the temporary directory,
-- so no need to add XXXXXX ending.
mkdtemp :: HasCallStack => CBytes -> IO CBytes
mkdtemp :: CBytes -> IO CBytes
mkdtemp CBytes
path = do
    let size :: Int
size = CBytes -> Int
CBytes.length CBytes
path
    CBytes -> (BA# Word8 -> IO CBytes) -> IO CBytes
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO CBytes) -> IO CBytes)
-> (BA# Word8 -> IO CBytes) -> IO CBytes
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p -> do
        (CBytes
p'', ()
_) <- Int -> (MBA# Word8 -> IO ()) -> IO (CBytes, ())
forall a.
HasCallStack =>
Int -> (MBA# Word8 -> IO a) -> IO (CBytes, a)
CBytes.allocCBytesUnsafe (Int
sizeInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
7) ((MBA# Word8 -> IO ()) -> IO (CBytes, ()))
-> (MBA# Word8 -> IO ()) -> IO (CBytes, ())
forall a b. (a -> b) -> a -> b
$ \ MBA# Word8
p' -> do  -- we append "XXXXXX\NUL" in C
            UVManager
uvm <- IO UVManager
getUVManager
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Int -> MBA# Word8 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_mkdtemp_threaded BA# Word8
p Int
size MBA# Word8
p')
        CBytes -> IO CBytes
forall (m :: * -> *) a. Monad m => a -> m a
return CBytes
p''

-- | Equivalent to <http://linux.die.net/man/2/rmdir rmdir(2)>.
--
-- Note this function may inherent OS limitations such as argument must be an empty folder.
rmdir :: HasCallStack => CBytes -> IO ()
rmdir :: CBytes -> IO ()
rmdir CBytes
path = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path (\ BA# Word8
p -> IO Int -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Int -> IO ())
-> ((Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int)
-> (Ptr UVLoop -> IO UVSlotUnsafe)
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO Int
withUVRequest UVManager
uvm ((Ptr UVLoop -> IO UVSlotUnsafe) -> IO ())
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
forall a b. (a -> b) -> a -> b
$ BA# Word8 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_rmdir_threaded BA# Word8
p)

-- | Equivalent to @rmdir -rf@
--
-- This function will try to remove folder and files contained by it.
rmdirrf :: HasCallStack => CBytes -> IO ()
rmdirrf :: CBytes -> IO ()
rmdirrf CBytes
path = do
    [(CBytes, DirEntType)]
ds <- HasCallStack => CBytes -> IO [(CBytes, DirEntType)]
CBytes -> IO [(CBytes, DirEntType)]
scandir CBytes
path
    [(CBytes, DirEntType)] -> ((CBytes, DirEntType) -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [(CBytes, DirEntType)]
ds (((CBytes, DirEntType) -> IO ()) -> IO ())
-> ((CBytes, DirEntType) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ (CBytes
d, DirEntType
t) -> do
        if DirEntType
t DirEntType -> DirEntType -> Bool
forall a. Eq a => a -> a -> Bool
/= DirEntType
DirEntDir
        then HasCallStack => CBytes -> IO ()
CBytes -> IO ()
unlink CBytes
d
        else HasCallStack => CBytes -> IO ()
CBytes -> IO ()
rmdirrf (CBytes -> IO ()) -> IO CBytes -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< CBytes
path CBytes -> CBytes -> IO CBytes
`P.join` CBytes
d
    HasCallStack => CBytes -> IO ()
CBytes -> IO ()
rmdir CBytes
path

--------------------------------------------------------------------------------

-- | Equivalent to <http://linux.die.net/man/3/scandir scandir(3)>.
--
-- Note Unlike scandir(3), this function does not return the “.” and “..” entries.
--
-- Note On Linux, getting the type of an entry is only supported by some file systems (btrfs, ext2, ext3 and ext4 at the time of this writing), check the <http://linux.die.net/man/2/getdents getdents(2)> man page.
scandir :: HasCallStack => CBytes -> IO [(CBytes, DirEntType)]
scandir :: CBytes -> IO [(CBytes, DirEntType)]
scandir CBytes
path = do
    UVManager
uvm <- IO UVManager
getUVManager
    IO (Ptr (Ptr DirEntType), Int)
-> ((Ptr (Ptr DirEntType), Int) -> IO ())
-> ((Ptr (Ptr DirEntType), Int) -> IO [(CBytes, DirEntType)])
-> IO [(CBytes, DirEntType)]
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
        (CBytes
-> (BA# Word8 -> IO (Ptr (Ptr DirEntType), Int))
-> IO (Ptr (Ptr DirEntType), Int)
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO (Ptr (Ptr DirEntType), Int))
 -> IO (Ptr (Ptr DirEntType), Int))
-> (BA# Word8 -> IO (Ptr (Ptr DirEntType), Int))
-> IO (Ptr (Ptr DirEntType), Int)
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
            (Ptr (Ptr (Ptr DirEntType)) -> IO Int)
-> IO (Ptr (Ptr DirEntType), Int)
forall a b. Prim a => (Ptr a -> IO b) -> IO (a, b)
allocPrimSafe ((Ptr (Ptr (Ptr DirEntType)) -> IO Int)
 -> IO (Ptr (Ptr DirEntType), Int))
-> (Ptr (Ptr (Ptr DirEntType)) -> IO Int)
-> IO (Ptr (Ptr DirEntType), Int)
forall a b. (a -> b) -> a -> b
$ \ Ptr (Ptr (Ptr DirEntType))
dents ->
                HasCallStack =>
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
withUVRequestEx UVManager
uvm
                    (BA# Word8
-> Ptr (Ptr (Ptr DirEntType)) -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_scandir_threaded BA# Word8
p Ptr (Ptr (Ptr DirEntType))
dents)
                    (Ptr (Ptr (Ptr DirEntType)) -> Int -> IO ()
hs_uv_fs_scandir_extra_cleanup Ptr (Ptr (Ptr DirEntType))
dents))
        (\ (Ptr (Ptr DirEntType)
dents, Int
n) -> Ptr (Ptr DirEntType) -> Int -> IO ()
hs_uv_fs_scandir_cleanup Ptr (Ptr DirEntType)
dents Int
n)
        (\ (Ptr (Ptr DirEntType)
dents, Int
n) -> [Int]
-> (Int -> IO (CBytes, DirEntType)) -> IO [(CBytes, DirEntType)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [Int
0..Int
nInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1] ((Int -> IO (CBytes, DirEntType)) -> IO [(CBytes, DirEntType)])
-> (Int -> IO (CBytes, DirEntType)) -> IO [(CBytes, DirEntType)]
forall a b. (a -> b) -> a -> b
$ \ Int
i -> do
            Ptr DirEntType
dent <- Ptr (Ptr DirEntType) -> Int -> IO (Ptr DirEntType)
forall a. Storable a => Ptr a -> Int -> IO a
peekElemOff Ptr (Ptr DirEntType)
dents Int
i
            (CString
p, UVDirEntType
typ) <- Ptr DirEntType -> IO (CString, UVDirEntType)
peekUVDirEnt Ptr DirEntType
dent
            let !typ' :: DirEntType
typ' = UVDirEntType -> DirEntType
fromUVDirEntType UVDirEntType
typ
            !CBytes
p' <- CString -> IO CBytes
fromCString CString
p
            (CBytes, DirEntType) -> IO (CBytes, DirEntType)
forall (m :: * -> *) a. Monad m => a -> m a
return (CBytes
p', DirEntType
typ'))

-- | Find all files and directories within a given directory with a predicator.
--
-- @
--  import Z.IO.FileSystem.FilePath (splitExtension)
--  -- find all haskell source file within current dir
--  scandirRecursively "."  (\ p _ -> (== ".hs") . snd <$> splitExtension p)
-- @
scandirRecursively :: HasCallStack => CBytes -> (CBytes -> DirEntType -> IO Bool) -> IO [CBytes]
scandirRecursively :: CBytes -> (CBytes -> DirEntType -> IO Bool) -> IO [CBytes]
scandirRecursively CBytes
dir CBytes -> DirEntType -> IO Bool
p = [CBytes] -> CBytes -> IO [CBytes]
loop [] (CBytes -> IO [CBytes]) -> IO CBytes -> IO [CBytes]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< CBytes -> IO CBytes
P.normalize CBytes
dir
  where
    loop :: [CBytes] -> CBytes -> IO [CBytes]
loop [CBytes]
acc0 CBytes
pdir =
        ([CBytes] -> (CBytes, DirEntType) -> IO [CBytes])
-> [CBytes] -> [(CBytes, DirEntType)] -> IO [CBytes]
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (\ [CBytes]
acc (CBytes
d,DirEntType
t) -> do
            CBytes
d' <- CBytes
pdir CBytes -> CBytes -> IO CBytes
`P.join` CBytes
d
            Bool
r <- CBytes -> DirEntType -> IO Bool
p CBytes
d' DirEntType
t
            let acc' :: [CBytes]
acc' = if Bool
r then (CBytes
d'CBytes -> [CBytes] -> [CBytes]
forall a. a -> [a] -> [a]
:[CBytes]
acc) else [CBytes]
acc
            if (DirEntType
t DirEntType -> DirEntType -> Bool
forall a. Eq a => a -> a -> Bool
== DirEntType
DirEntDir)
            then [CBytes] -> CBytes -> IO [CBytes]
loop [CBytes]
acc' CBytes
d'
            else [CBytes] -> IO [CBytes]
forall (m :: * -> *) a. Monad m => a -> m a
return [CBytes]
acc'
        ) [CBytes]
acc0 ([(CBytes, DirEntType)] -> IO [CBytes])
-> IO [(CBytes, DirEntType)] -> IO [CBytes]
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< HasCallStack => CBytes -> IO [(CBytes, DirEntType)]
CBytes -> IO [(CBytes, DirEntType)]
scandir CBytes
pdir

--------------------------------------------------------------------------------

-- | Equivalent to <http://linux.die.net/man/2/stat stat(2)>
stat :: HasCallStack => CBytes -> IO FStat
stat :: CBytes -> IO FStat
stat CBytes
path = do
    CBytes -> (BA# Word8 -> IO FStat) -> IO FStat
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO FStat) -> IO FStat)
-> (BA# Word8 -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
         Int -> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
uvStatSize ((Ptr FStat -> IO FStat) -> IO FStat)
-> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ Ptr FStat
s -> do
            UVManager
uvm <- IO UVManager
getUVManager
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Ptr FStat -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_stat_threaded BA# Word8
p Ptr FStat
s)
            Ptr FStat -> IO FStat
peekUVStat Ptr FStat
s

-- | Equivalent to <http://linux.die.net/man/2/lstat lstat(2)>
lstat :: HasCallStack => CBytes -> IO FStat
lstat :: CBytes -> IO FStat
lstat CBytes
path =
    CBytes -> (BA# Word8 -> IO FStat) -> IO FStat
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO FStat) -> IO FStat)
-> (BA# Word8 -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
         Int -> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
uvStatSize ((Ptr FStat -> IO FStat) -> IO FStat)
-> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ Ptr FStat
s -> do
            UVManager
uvm <- IO UVManager
getUVManager
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Ptr FStat -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_lstat_threaded BA# Word8
p Ptr FStat
s)
            Ptr FStat -> IO FStat
peekUVStat Ptr FStat
s

-- | Equivalent to <http://linux.die.net/man/2/fstat fstat(2)>
fstat :: HasCallStack => File -> IO FStat
fstat :: File -> IO FStat
fstat File
uvf = File -> (FD -> IO FStat) -> IO FStat
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO FStat) -> IO FStat) -> (FD -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ FD
fd ->
     (Int -> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
uvStatSize ((Ptr FStat -> IO FStat) -> IO FStat)
-> (Ptr FStat -> IO FStat) -> IO FStat
forall a b. (a -> b) -> a -> b
$ \ Ptr FStat
s -> do
        UVManager
uvm <- IO UVManager
getUVManager
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> Ptr FStat -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_fstat_threaded FD
fd Ptr FStat
s)
        Ptr FStat -> IO FStat
peekUVStat Ptr FStat
s)

-- | If given path is a symbolic link?
isLink :: HasCallStack => CBytes -> IO Bool
isLink :: CBytes -> IO Bool
isLink CBytes
p = HasCallStack => CBytes -> IO FStat
CBytes -> IO FStat
lstat CBytes
p IO FStat -> (FStat -> IO Bool) -> IO Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ FStat
st -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (FStat -> FD
stMode FStat
st FD -> FD -> FD
forall a. Bits a => a -> a -> a
.&. FD
S_IFMT FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
S_IFLNK)

-- | If given path is a directory or a symbolic link to a directory?
isDir :: HasCallStack => CBytes -> IO Bool
isDir :: CBytes -> IO Bool
isDir CBytes
p = HasCallStack => CBytes -> IO FStat
CBytes -> IO FStat
stat CBytes
p IO FStat -> (FStat -> IO Bool) -> IO Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ FStat
st -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (FStat -> FD
stMode FStat
st FD -> FD -> FD
forall a. Bits a => a -> a -> a
.&. FD
S_IFMT FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
S_IFDIR)

-- | If given path is a file or a symbolic link to a file?
isFile :: HasCallStack => CBytes -> IO Bool
isFile :: CBytes -> IO Bool
isFile CBytes
p = HasCallStack => CBytes -> IO FStat
CBytes -> IO FStat
stat CBytes
p IO FStat -> (FStat -> IO Bool) -> IO Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \ FStat
st -> Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (FStat -> FD
stMode FStat
st FD -> FD -> FD
forall a. Bits a => a -> a -> a
.&. FD
S_IFMT FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
S_IFREG)

--------------------------------------------------------------------------------

-- | Equivalent to <http://linux.die.net/man/2/rename rename(2)>.
--
-- Note On Windows if this function fails with UV_EBUSY, UV_EPERM or UV_EACCES, it will retry to rename the file up to four times with 250ms wait between attempts before giving up. If both path and new_path are existing directories this function will work only if target directory is empty.
rename :: HasCallStack => CBytes -> CBytes -> IO ()
rename :: CBytes -> CBytes -> IO ()
rename CBytes
path CBytes
path' = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path' ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p' ->
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> BA# Word8 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_rename_threaded BA# Word8
p BA# Word8
p')

-- | Equivalent to <http://linux.die.net/man/2/fsync fsync(2)>.
fsync :: HasCallStack => File -> IO ()
fsync :: File -> IO ()
fsync File
uvf = File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_fsync_threaded FD
fd)

-- | Equivalent to <http://linux.die.net/man/2/fdatasync fdatasync(2)>.
fdatasync :: HasCallStack => File -> IO ()
fdatasync :: File -> IO ()
fdatasync File
uvf = File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_fdatasync_threaded FD
fd)

-- | Equivalent to <http://linux.die.net/man/2/ftruncate ftruncate(2)>.
ftruncate :: HasCallStack => File -> Int64 -> IO ()
ftruncate :: File -> Int64 -> IO ()
ftruncate File
uvf Int64
off = File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> Int64 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_ftruncate_threaded FD
fd Int64
off)

-- | Copies a file from path to new_path.
--
-- Warning: If the destination path is created, but an error occurs while copying the data, then the destination path is removed. There is a brief window of time between closing and removing the file where another process could access the file.
copyfile :: HasCallStack => CBytes -> CBytes -> CopyFileFlag -> IO ()
copyfile :: CBytes -> CBytes -> FD -> IO ()
copyfile CBytes
path CBytes
path' FD
flag = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path' ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p' ->
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_copyfile_threaded BA# Word8
p BA# Word8
p' FD
flag)

-- | Equivalent to <http://linux.die.net/man/2/access access(2)> on Unix.
-- Windows uses GetFileAttributesW().
access :: HasCallStack => CBytes -> AccessMode -> IO AccessResult
access :: CBytes -> FD -> IO AccessResult
access CBytes
path FD
mode = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO AccessResult) -> IO AccessResult
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO AccessResult) -> IO AccessResult)
-> (BA# Word8 -> IO AccessResult) -> IO AccessResult
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe)
-> (Int -> IO AccessResult)
-> IO AccessResult
forall b.
HasCallStack =>
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO b) -> IO b
withUVRequest' UVManager
uvm (BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_access_threaded BA# Word8
p FD
mode) (FD -> IO AccessResult
handleResult (FD -> IO AccessResult) -> (Int -> FD) -> Int -> IO AccessResult
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> FD
forall a b. (Integral a, Num b) => a -> b
fromIntegral)
  where
    handleResult :: FD -> IO AccessResult
handleResult FD
r
        | FD
r FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
0           = AccessResult -> IO AccessResult
forall (m :: * -> *) a. Monad m => a -> m a
return AccessResult
AccessOK
        | FD
r FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
UV_ENOENT   = AccessResult -> IO AccessResult
forall (m :: * -> *) a. Monad m => a -> m a
return AccessResult
NoExistence
        | FD
r FD -> FD -> Bool
forall a. Eq a => a -> a -> Bool
== FD
UV_EACCES   = AccessResult -> IO AccessResult
forall (m :: * -> *) a. Monad m => a -> m a
return AccessResult
NoPermission
        | Bool
otherwise        = do
            Text
name <- FD -> IO Text
uvErrName FD
r
            Text
desc <- FD -> IO Text
uvStdError FD
r
            FD -> IOEInfo -> IO AccessResult
forall a. FD -> IOEInfo -> IO a
throwUVError FD
r (Text -> Text -> CallStack -> IOEInfo
IOEInfo Text
name Text
desc CallStack
HasCallStack => CallStack
callStack)

-- | Equivalent to <http://linux.die.net/man/2/chmod chmod(2)>.
chmod :: HasCallStack => CBytes -> FileMode -> IO ()
chmod :: CBytes -> FD -> IO ()
chmod CBytes
path FD
mode = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_chmod_threaded BA# Word8
p FD
mode)

-- | Equivalent to <http://linux.die.net/man/2/fchmod fchmod(2)>.
fchmod :: HasCallStack => File -> FileMode -> IO ()
fchmod :: File -> FD -> IO ()
fchmod File
uvf FD
mode = File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_fchmod_threaded FD
fd FD
mode)

-- | Equivalent to <http://linux.die.net/man/2/utime utime(2)>.
--
-- libuv choose 'Double' type due to cross platform concerns, we only provide micro-second precision.
--
utime :: HasCallStack
      => CBytes
      -> Double     -- ^ atime, i.e. access time
      -> Double     -- ^ mtime, i.e. modify time
      -> IO ()
utime :: CBytes -> Double -> Double -> IO ()
utime CBytes
path Double
atime Double
mtime = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Double -> Double -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_utime_threaded BA# Word8
p Double
atime Double
mtime)

-- | Equivalent to <https://man7.org/linux/man-pages/man3/futimes.3.html futime(3)>.
--
-- Same precision notes with 'utime'.
futime :: HasCallStack => File -> Double -> Double -> IO ()
futime :: File -> Double -> Double -> IO ()
futime File
uvf Double
atime Double
mtime = File -> (FD -> IO ()) -> IO ()
forall a. HasCallStack => File -> (FD -> IO a) -> IO a
checkFileClosed File
uvf ((FD -> IO ()) -> IO ()) -> (FD -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ FD
fd -> do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> Double -> Double -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_futime_threaded FD
fd Double
atime Double
mtime)

-- | Equivalent to <https://man7.org/linux/man-pages/man3/lutimes.3.html lutime(3)>.
--
-- Same precision notes with 'utime'.
lutime :: HasCallStack
       => CBytes
       -> Double     -- ^ atime, i.e. access time
       -> Double     -- ^ mtime, i.e. modify time
       -> IO ()
lutime :: CBytes -> Double -> Double -> IO ()
lutime CBytes
path Double
atime Double
mtime = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> Double -> Double -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_lutime_threaded BA# Word8
p Double
atime Double
mtime)

-- | Equivalent to <http://linux.die.net/man/2/link link(2)>.
link :: HasCallStack => CBytes -> CBytes -> IO ()
link :: CBytes -> CBytes -> IO ()
link CBytes
path CBytes
path' = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path' ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p' ->
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> BA# Word8 -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_link_threaded BA# Word8
p BA# Word8
p')

-- | Equivalent to <http://linux.die.net/man/2/symlink symlink(2)>.
--
-- | Note On Windows the flags parameter can be specified to control how the symlink will be created.
--
--   * 'SYMLINK_DIR': indicates that path points to a directory.
--   * 'SYMLINK_JUNCTION': request that the symlink is created using junction points.
--
-- On other platforms these flags are ignored.
symlink :: HasCallStack => CBytes -> CBytes -> SymlinkFlag -> IO ()
symlink :: CBytes -> CBytes -> FD -> IO ()
symlink CBytes
path CBytes
path' FD
flag = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path' ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p' ->
            HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> BA# Word8 -> FD -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_symlink_threaded BA# Word8
p BA# Word8
p' FD
flag)

-- | Equivalent to <http://linux.die.net/man/2/readlink readlink(2)>.
readlink :: HasCallStack => CBytes -> IO CBytes
readlink :: CBytes -> IO CBytes
readlink CBytes
path = do
    UVManager
uvm <- IO UVManager
getUVManager
    IO (CString, Int)
-> ((CString, Int) -> IO ())
-> ((CString, Int) -> IO CBytes)
-> IO CBytes
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
        (CBytes -> (BA# Word8 -> IO (CString, Int)) -> IO (CString, Int)
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO (CString, Int)) -> IO (CString, Int))
-> (BA# Word8 -> IO (CString, Int)) -> IO (CString, Int)
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
            (Ptr CString -> IO Int) -> IO (CString, Int)
forall a b. Prim a => (Ptr a -> IO b) -> IO (a, b)
allocPrimSafe ((Ptr CString -> IO Int) -> IO (CString, Int))
-> (Ptr CString -> IO Int) -> IO (CString, Int)
forall a b. (a -> b) -> a -> b
$ \ Ptr CString
p' ->
                HasCallStack =>
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
withUVRequestEx UVManager
uvm
                    (BA# Word8 -> Ptr CString -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_readlink_threaded BA# Word8
p Ptr CString
p')
                    (\ Int
_ -> Ptr CString -> IO ()
hs_uv_fs_readlink_extra_cleanup Ptr CString
p'))
        (CString -> IO ()
hs_uv_fs_readlink_cleanup (CString -> IO ())
-> ((CString, Int) -> CString) -> (CString, Int) -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CString, Int) -> CString
forall a b. (a, b) -> a
fst)
        (CString -> IO CBytes
fromCString (CString -> IO CBytes)
-> ((CString, Int) -> CString) -> (CString, Int) -> IO CBytes
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CString, Int) -> CString
forall a b. (a, b) -> a
fst)

-- | Equivalent to <http://linux.die.net/man/3/realpath realpath(3)> on Unix. Windows uses <https://msdn.microsoft.com/en-us/library/windows/desktop/aa364962(v=vs.85).aspx GetFinalPathNameByHandle>.
--
-- Warning This function has certain platform-specific caveats that were discovered when used in Node.
--
--  * macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are found while
--    resolving the given path. This limit is hardcoded and cannot be sidestepped.
--
--  * Windows: while this function works in the common case, there are a number of corner cases where it doesn’t:
--
--      * Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) cannot be resolved.
--      * Inconsistent casing when using drive letters.
--      * Resolved path bypasses subst’d drives.
--
-- While this function can still be used, it’s not recommended if scenarios such as the above need to be supported.
-- The background story and some more details on these issues can be checked <https://github.com/nodejs/node/issues/7726 here>.
--
-- Note This function is not implemented on Windows XP and Windows Server 2003. On these systems, UV_ENOSYS is returned.
realpath :: HasCallStack => CBytes -> IO CBytes
realpath :: CBytes -> IO CBytes
realpath CBytes
path = do
    UVManager
uvm <- IO UVManager
getUVManager
    IO (CString, Int)
-> ((CString, Int) -> IO ())
-> ((CString, Int) -> IO CBytes)
-> IO CBytes
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
        (CBytes -> (BA# Word8 -> IO (CString, Int)) -> IO (CString, Int)
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO (CString, Int)) -> IO (CString, Int))
-> (BA# Word8 -> IO (CString, Int)) -> IO (CString, Int)
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
            (Ptr CString -> IO Int) -> IO (CString, Int)
forall a b. Prim a => (Ptr a -> IO b) -> IO (a, b)
allocPrimSafe ((Ptr CString -> IO Int) -> IO (CString, Int))
-> (Ptr CString -> IO Int) -> IO (CString, Int)
forall a b. (a -> b) -> a -> b
$ \ Ptr CString
p' ->
                HasCallStack =>
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
UVManager
-> (Ptr UVLoop -> IO UVSlotUnsafe) -> (Int -> IO ()) -> IO Int
withUVRequestEx UVManager
uvm
                    (BA# Word8 -> Ptr CString -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_realpath_threaded BA# Word8
p Ptr CString
p')
                    (\ Int
_ -> Ptr CString -> IO ()
hs_uv_fs_readlink_extra_cleanup Ptr CString
p'))
        (CString -> IO ()
hs_uv_fs_readlink_cleanup (CString -> IO ())
-> ((CString, Int) -> CString) -> (CString, Int) -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CString, Int) -> CString
forall a b. (a, b) -> a
fst)
        (CString -> IO CBytes
fromCString (CString -> IO CBytes)
-> ((CString, Int) -> CString) -> (CString, Int) -> IO CBytes
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CString, Int) -> CString
forall a b. (a, b) -> a
fst)

-- | Equivalent to <http://linux.die.net/man/2/chown chown(2)>.
chown :: HasCallStack => CBytes -> UID -> GID -> IO ()
chown :: CBytes -> UID -> GID -> IO ()
chown CBytes
path UID
uid GID
gid = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> UID -> GID -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_chown_threaded BA# Word8
p UID
uid GID
gid)

-- | Equivalent to <http://linux.die.net/man/2/fchown fchown(2)>.
fchown :: HasCallStack => FD -> UID -> GID -> IO ()
fchown :: FD -> UID -> GID -> IO ()
fchown FD
fd UID
uid GID
gid = do
    UVManager
uvm <- IO UVManager
getUVManager
    HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (FD -> UID -> GID -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_fchown_threaded FD
fd UID
uid GID
gid)

-- | Equivalent to <http://linux.die.net/man/2/lchown lchown(2)>.
lchown :: HasCallStack => CBytes -> UID -> GID -> IO ()
lchown :: CBytes -> UID -> GID -> IO ()
lchown CBytes
path UID
uid GID
gid = do
    UVManager
uvm <- IO UVManager
getUVManager
    CBytes -> (BA# Word8 -> IO ()) -> IO ()
forall a. CBytes -> (BA# Word8 -> IO a) -> IO a
withCBytesUnsafe CBytes
path ((BA# Word8 -> IO ()) -> IO ()) -> (BA# Word8 -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \ BA# Word8
p ->
        HasCallStack =>
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
UVManager -> (Ptr UVLoop -> IO UVSlotUnsafe) -> IO ()
withUVRequest_ UVManager
uvm (BA# Word8 -> UID -> GID -> Ptr UVLoop -> IO UVSlotUnsafe
hs_uv_fs_lchown_threaded BA# Word8
p UID
uid GID
gid)