{-# LINE 1 "libraries/unix/System/Posix/PosixPath/FilePath.hsc" #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PackageImports #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  System.Posix.PosixPath.FilePath
-- Copyright   :  (c) The University of Glasgow 2002
-- License     :  BSD-style (see the file libraries/base/LICENSE)
--
-- Maintainer  :  libraries@haskell.org
-- Stability   :  provisional
-- Portability :  non-portable (requires POSIX)
--
-- Internal stuff: support for ByteString FilePaths
--
-----------------------------------------------------------------------------

module System.Posix.PosixPath.FilePath (
     withFilePath, peekFilePath, peekFilePathLen,
     throwErrnoPathIfMinus1Retry,
     throwErrnoPathIfMinus1Retry_,
     throwErrnoPathIfNullRetry,
     throwErrnoPathIfRetry,
     throwErrnoPath,
     throwErrnoPathIf,
     throwErrnoPathIf_,
     throwErrnoPathIfNull,
     throwErrnoPathIfMinus1,
     throwErrnoPathIfMinus1_,
     throwErrnoTwoPathsIfMinus1_
  ) where

import Foreign hiding ( void )
import Foreign.C hiding (
     throwErrnoPath,
     throwErrnoPathIf,
     throwErrnoPathIf_,
     throwErrnoPathIfNull,
     throwErrnoPathIfMinus1,
     throwErrnoPathIfMinus1_ )

import System.OsPath.Types
import Data.ByteString.Internal (c_strlen)
import Control.Monad
import Control.Exception
import System.OsPath.Posix as PS

{-# LINE 50 "libraries/unix/System/Posix/PosixPath/FilePath.hsc" #-}
import "os-string" System.OsString.Data.ByteString.Short as BSS

{-# LINE 54 "libraries/unix/System/Posix/PosixPath/FilePath.hsc" #-}
import Prelude hiding (FilePath)
import System.OsString.Internal.Types (PosixString(..), pattern PS)
import GHC.IO.Exception


{-# LINE 61 "libraries/unix/System/Posix/PosixPath/FilePath.hsc" #-}


withFilePath :: PosixPath -> (CString -> IO a) -> IO a
withFilePath :: forall a. PosixPath -> (CString -> IO a) -> IO a
withFilePath PosixPath
path = PosixPath -> (CString -> IO a) -> IO a
forall a. PosixPath -> (CString -> IO a) -> IO a
useAsCStringSafe PosixPath
path

peekFilePath :: CString -> IO PosixPath
peekFilePath :: CString -> IO PosixPath
peekFilePath = (ShortByteString -> PosixPath)
-> IO ShortByteString -> IO PosixPath
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ShortByteString -> PosixPath
PosixString (IO ShortByteString -> IO PosixPath)
-> (CString -> IO ShortByteString) -> CString -> IO PosixPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CString -> IO ShortByteString
packCString

peekFilePathLen :: CStringLen -> IO PosixPath
peekFilePathLen :: CStringLen -> IO PosixPath
peekFilePathLen = (ShortByteString -> PosixPath)
-> IO ShortByteString -> IO PosixPath
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ShortByteString -> PosixPath
PosixString (IO ShortByteString -> IO PosixPath)
-> (CStringLen -> IO ShortByteString) -> CStringLen -> IO PosixPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CStringLen -> IO ShortByteString
packCStringLen


throwErrnoPathIfMinus1Retry :: (Eq a, Num a)
                            => String -> PosixPath -> IO a -> IO a
throwErrnoPathIfMinus1Retry :: forall a. (Eq a, Num a) => String -> PosixPath -> IO a -> IO a
throwErrnoPathIfMinus1Retry String
loc PosixPath
path IO a
f = do
  (a -> Bool) -> String -> PosixPath -> IO a -> IO a
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIfRetry (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== -a
1) String
loc PosixPath
path IO a
f

throwErrnoPathIfMinus1Retry_ :: (Eq a, Num a)
                             => String -> PosixPath -> IO a -> IO ()
throwErrnoPathIfMinus1Retry_ :: forall a. (Eq a, Num a) => String -> PosixPath -> IO a -> IO ()
throwErrnoPathIfMinus1Retry_ String
loc PosixPath
path IO a
f =
  IO a -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO a -> IO ()) -> IO a -> IO ()
forall a b. (a -> b) -> a -> b
$ (a -> Bool) -> String -> PosixPath -> IO a -> IO a
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIfRetry (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== -a
1) String
loc PosixPath
path IO a
f

throwErrnoPathIfNullRetry :: String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNullRetry :: forall a. String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNullRetry String
loc PosixPath
path IO (Ptr a)
f =
  (Ptr a -> Bool) -> String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIfRetry (Ptr a -> Ptr a -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr a
forall a. Ptr a
nullPtr) String
loc PosixPath
path IO (Ptr a)
f

throwErrnoPathIfRetry :: (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIfRetry :: forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIfRetry a -> Bool
pr String
loc PosixPath
rpath IO a
f =
  do
    res <- IO a
f
    if pr res
      then do
        err <- getErrno
        if err == eINTR
          then throwErrnoPathIfRetry pr loc rpath f
          else throwErrnoPath loc rpath
      else return res

-- | as 'throwErrno', but exceptions include the given path when appropriate.
--
throwErrnoPath :: String -> PosixPath -> IO a
throwErrnoPath :: forall a. String -> PosixPath -> IO a
throwErrnoPath String
loc PosixPath
path =
  do
    errno <- IO Errno
getErrno
    path' <- either (const (_toStr path)) id <$> try @IOException (PS.decodeFS path)
    ioError (errnoToIOError loc errno Nothing (Just path'))

-- | as 'throwErrnoIf', but exceptions include the given path when
--   appropriate.
--
throwErrnoPathIf :: (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIf :: forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIf a -> Bool
cond String
loc PosixPath
path IO a
f =
  do
    res <- IO a
f
    if cond res then throwErrnoPath loc path else return res

-- | as 'throwErrnoIf_', but exceptions include the given path when
--   appropriate.
--
throwErrnoPathIf_ :: (a -> Bool) -> String -> PosixPath -> IO a -> IO ()
throwErrnoPathIf_ :: forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO ()
throwErrnoPathIf_ a -> Bool
cond String
loc PosixPath
path IO a
f  = IO a -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO a -> IO ()) -> IO a -> IO ()
forall a b. (a -> b) -> a -> b
$ (a -> Bool) -> String -> PosixPath -> IO a -> IO a
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIf a -> Bool
cond String
loc PosixPath
path IO a
f

-- | as 'throwErrnoIfNull', but exceptions include the given path when
--   appropriate.
--
throwErrnoPathIfNull :: String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNull :: forall a. String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
throwErrnoPathIfNull  = (Ptr a -> Bool) -> String -> PosixPath -> IO (Ptr a) -> IO (Ptr a)
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIf (Ptr a -> Ptr a -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr a
forall a. Ptr a
nullPtr)

-- | as 'throwErrnoIfMinus1', but exceptions include the given path when
--   appropriate.
--
throwErrnoPathIfMinus1 :: (Eq a, Num a) => String -> PosixPath -> IO a -> IO a
throwErrnoPathIfMinus1 :: forall a. (Eq a, Num a) => String -> PosixPath -> IO a -> IO a
throwErrnoPathIfMinus1 = (a -> Bool) -> String -> PosixPath -> IO a -> IO a
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO a
throwErrnoPathIf (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== -a
1)

-- | as 'throwErrnoIfMinus1_', but exceptions include the given path when
--   appropriate.
--
throwErrnoPathIfMinus1_ :: (Eq a, Num a) => String -> PosixPath -> IO a -> IO ()
throwErrnoPathIfMinus1_ :: forall a. (Eq a, Num a) => String -> PosixPath -> IO a -> IO ()
throwErrnoPathIfMinus1_  = (a -> Bool) -> String -> PosixPath -> IO a -> IO ()
forall a. (a -> Bool) -> String -> PosixPath -> IO a -> IO ()
throwErrnoPathIf_ (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== -a
1)

-- | as 'throwErrnoTwoPathsIfMinus1_', but exceptions include two paths when appropriate.
--
throwErrnoTwoPathsIfMinus1_ :: (Eq a, Num a) => String -> PosixPath -> PosixPath -> IO a -> IO ()
throwErrnoTwoPathsIfMinus1_ :: forall a.
(Eq a, Num a) =>
String -> PosixPath -> PosixPath -> IO a -> IO ()
throwErrnoTwoPathsIfMinus1_ String
loc PosixPath
path1 PosixPath
path2 IO a
action = do
    path1' <- (IOException -> String)
-> (String -> String) -> Either IOException String -> String
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> IOException -> String
forall a b. a -> b -> a
const (PosixPath -> String
_toStr PosixPath
path1)) String -> String
forall a. a -> a
id (Either IOException String -> String)
-> IO (Either IOException String) -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall e a. Exception e => IO a -> IO (Either e a)
try @IOException (PosixPath -> IO String
PS.decodeFS PosixPath
path1)
    path2' <- either (const (_toStr path2)) id <$> try @IOException (PS.decodeFS path2)
    throwErrnoIfMinus1_ (loc <> " '" <> path1' <> "' to '" <> path2' <> "'") action

_toStr :: PosixPath -> String
_toStr :: PosixPath -> String
_toStr = (PosixChar -> Char) -> [PosixChar] -> String
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PosixChar -> Char
PS.toChar ([PosixChar] -> String)
-> (PosixPath -> [PosixChar]) -> PosixPath -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PosixPath -> [PosixChar]
PS.unpack

-- | Wrapper around 'useAsCString', checking the encoded 'FilePath' for internal NUL octets as these are
-- disallowed in POSIX filepaths. See https://gitlab.haskell.org/ghc/ghc/-/issues/13660
useAsCStringSafe :: PosixPath -> (CString -> IO a) -> IO a
useAsCStringSafe :: forall a. PosixPath -> (CString -> IO a) -> IO a
useAsCStringSafe pp :: PosixPath
pp@(PS ShortByteString
path) CString -> IO a
f = ShortByteString -> (CString -> IO a) -> IO a
forall a. ShortByteString -> (CString -> IO a) -> IO a
useAsCString ShortByteString
path ((CString -> IO a) -> IO a) -> (CString -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \CString
ptr -> do
    let len :: Int
len = ShortByteString -> Int
BSS.length ShortByteString
path
    clen <- CString -> IO CSize
c_strlen CString
ptr
    if clen == fromIntegral len
        then f ptr
        else do
          path' <- either (const (_toStr pp)) id <$> try @IOException (PS.decodeFS pp)
          ioError (err path')
  where
    err :: String -> IOException
err String
path' =
        IOError
          { ioe_handle :: Maybe Handle
ioe_handle = Maybe Handle
forall a. Maybe a
Nothing
          , ioe_type :: IOErrorType
ioe_type = IOErrorType
InvalidArgument
          , ioe_location :: String
ioe_location = String
"checkForInteriorNuls"
          , ioe_description :: String
ioe_description = String
"POSIX filepaths must not contain internal NUL octets."
          , ioe_errno :: Maybe CInt
ioe_errno = Maybe CInt
forall a. Maybe a
Nothing
          , ioe_filename :: Maybe String
ioe_filename = String -> Maybe String
forall a. a -> Maybe a
Just String
path'
          }