module System.Linux.Btrfs.FilePathLike
( FilePathLike(..)
, RawFilePath
) where
import Data.String
import Data.Monoid
import Data.List
import Foreign.C.String hiding
( peekCString, peekCStringLen
, newCString, newCStringLen
, withCString, withCStringLen)
import qualified Foreign.C.String as S
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B
import qualified Data.ByteString.Char8 as B8
import System.Posix.Types
import System.Posix.IO hiding (openFd)
import System.Posix.ByteString.FilePath (RawFilePath)
import qualified System.Posix.IO as S (openFd)
import qualified System.Posix.IO.ByteString as B (openFd)
import System.IO.Unsafe (unsafePerformIO)
class (Monoid s, IsString s) => FilePathLike s where
asString :: s -> String
peekCString :: CString -> IO s
peekCStringLen :: CStringLen -> IO s
withCString :: s -> (CString -> IO a) -> IO a
withCStringLen :: s -> (CStringLen -> IO a) -> IO a
unsafeWithCStringLen :: s -> (CStringLen -> IO a) -> IO a
openFd :: s -> OpenMode -> Maybe FileMode -> OpenFileFlags -> IO Fd
dropTrailingSlash :: s -> s
(</>) :: s -> s -> s
splitFileName :: s -> (s, s)
instance FilePathLike [Char] where
asString = id
peekCString = S.peekCString
peekCStringLen = S.peekCStringLen
withCString = S.withCString
withCStringLen = S.withCStringLen
unsafeWithCStringLen = withCStringLen
openFd = S.openFd
dropTrailingSlash s = if null s' then "/" else s'
where
s' = dropWhileEnd (== '/') s
_ </> s2@('/' : _) = s2
s1 </> "" = s1
"" </> s2 = s2
s1 </> s2 = if s1' == "/" then '/' : s2 else s1' ++ "/" ++ s2
where
s1' = dropTrailingSlash s1
splitFileName s = (if null d then "./" else reverse d, reverse n)
where
(n, d) = span (/= '/') $ reverse s
instance FilePathLike B.ByteString where
asString = convert
peekCString = B.packCString
peekCStringLen = B.packCStringLen
withCString = B.useAsCString
withCStringLen = B.useAsCStringLen
unsafeWithCStringLen = B.unsafeUseAsCStringLen
openFd = B.openFd
dropTrailingSlash s = if B.null s' then slashBS else s'
where
(s', _) = B8.spanEnd (== '/') s
s1 </> s2
| B.null s1 = s2
| B.null s2 = s1
| B8.head s2 == '/' = s2
| otherwise =
let s1' = dropTrailingSlash s1
in if B8.last s1' == '/' then slashBS <> s2
else B.concat [s1', slashBS, s2]
splitFileName s = (if B.null d then curDir else d, n)
where
(d, n) = B8.spanEnd (/= '/') s
curDir = B8.pack "./"
convert :: (FilePathLike s1, FilePathLike s2) => s1 -> s2
convert s = unsafePerformIO $ unsafeWithCStringLen s peekCStringLen
slashBS :: B.ByteString
slashBS = B8.singleton '/'