{-| Module : Z.IO.FileSystem.FilePath Description : file path toolbox Copyright : (c) Dong Han, 2017-2020 License : BSD Maintainer : winterland1989@gmail.com Stability : experimental Portability : non-portable This module provides file path manipulations using , both unix and window style path is accepted. Default style is choosen during compile time, but can be changed during runtime. -} module Z.IO.FileSystem.FilePath ( -- * Paths splitBaseName, changeBaseName , splitRoot, changeRoot , splitSegments , isAbsolute , isRelative , join , concat , normalize , intersection , absolute , relative -- * Extensions , splitExtension, changeExtension -- * Path Style , PathStyle(..) , pathStyle , getPathStyle, setPathStyle , pathSeparator, searchPathSeparator, extensionSeparator -- * Search path , getSearchPath ) where import Data.Word import qualified Data.List as List import GHC.Generics import Z.Data.CBytes (CBytes(CB), allocCBytesUnsafe, withCBytesUnsafe, withCBytesListUnsafe) import qualified Z.Data.CBytes as CB import qualified Z.Data.Text.ShowT as T import Z.Data.JSON (FromValue, ToValue, EncodeJSON) import qualified Z.Data.Vector.Base as V import qualified Z.Data.Vector as V import Z.Foreign import Z.IO.Environment (getEnv') import Prelude hiding (concat) #include "hs_cwalk.h" -- \ #define BACKSLASH 92 -- / #define SLASH 47 -- " #define DOUBLE_QUOTE 34 -- : #define COLON 58 -- ; #define SEMICOLON 59 -- . #define DOT 46 #define BUF_EXT_SIZ 4 data PathStyle = WindowsStyle -- ^ Use backslashes as a separator and volume for the root. | UnixStyle -- ^ Use slashes as a separator and a slash for the root. deriving (Show, Eq, Ord, Generic) deriving anyclass (T.ShowT, FromValue, ToValue, EncodeJSON) enumToPathStyle_ :: CInt -> PathStyle enumToPathStyle_ (#const CWK_STYLE_WINDOWS) = WindowsStyle enumToPathStyle_ _ = UnixStyle pathStyleToEnum_ :: PathStyle -> CInt pathStyleToEnum_ WindowsStyle = (#const CWK_STYLE_WINDOWS) pathStyleToEnum_ _ = (#const CWK_STYLE_UNIX) -- | Guesses the path style. -- -- This function guesses the path style based on a submitted path-string. -- The guessing will look at the root and the type of slashes contained in the path -- and return the style which is more likely used in the path. The algorithm checks the following: -- -- * If the root is longer than 1 character -> WINDOWS -- * If the first separator is a backslash -> WINDOWS -- * If the first separator is a slash -> UNIX -- * If the last segment starts with a dot -> UNIX -- * If the last segment contains a dot -> WINDOWS -- * If nothing was found to determine the style -> UNIX -- pathStyle :: CBytes -> IO PathStyle pathStyle p = enumToPathStyle_ <$> withCBytesUnsafe p cwk_path_guess_style -- | Gets the path style currently using. getPathStyle :: IO PathStyle getPathStyle = enumToPathStyle_ <$> cwk_path_get_style -- | Configures which path style is used afterwards. -- -- This function configures which path style is used. -- call to this function is only required if a non-native behaviour is required. -- The style defaults to 'WindowsStyle' on windows builds and to 'UnixStyle' otherwise. setPathStyle :: PathStyle -> IO () setPathStyle = cwk_path_set_style . pathStyleToEnum_ -- | Get the character that separates directories. pathSeparator :: IO [Word8] pathSeparator = do s <- getPathStyle case s of UnixStyle -> return [(#const SLASH)] _ -> return [(#const SLASH), (#const BACKSLASH)] -- | The character that is used to separate the entries in the $PATH environment variable. -- -- * Windows: searchPathSeparator is ASCII @;@ -- * Unix: searchPathSeparator is ASCII @:@ -- searchPathSeparator :: IO Word8 searchPathSeparator = do s <- getPathStyle case s of UnixStyle -> return (#const COLON) _ -> return (#const SEMICOLON) -- | File extension character -- -- ExtSeparator is ASCII @.@ extensionSeparator :: Word8 extensionSeparator = #const DOT -- | Get the basename of a file path. -- -- The basename is the last segment of a path. For instance, @logs@ is the basename of the path @\/var\/logs@. -- -- +--------------------------+---------------+ -- | Path | Basename | -- +--------------------------+---------------+ -- | \/my\/path.txt | path.txt | -- +--------------------------+---------------+ -- | \/my\/path.txt\/ | path.txt | -- +--------------------------+---------------+ -- | \/my\/path.txt\/\/\/\/ | path.txt | -- +--------------------------+---------------+ -- | file_name | file_name | -- +--------------------------+---------------+ -- | .. | .. | -- +--------------------------+---------------+ -- | . | . | -- +--------------------------+---------------+ -- | \/ | "" | -- +--------------------------+---------------+ -- | C:\path\test.txt | test.txt | -- +--------------------------+---------------+ -- splitBaseName :: CBytes -> IO (CBytes, CBytes) -- ^ return (dir, basename) {-# INLINABLE splitBaseName #-} splitBaseName p = do (off, len) <- withCBytesUnsafe p $ \ pp -> allocPrimUnsafe $ \ poff -> hs_cwk_path_get_basename pp poff if len == 0 then return (p, CB.empty) else return ( CB (V.PrimVector (CB.rawPrimArray p) 0 off) , CB (V.PrimVector (CB.rawPrimArray p) off len)) -- | Changes the basename of a file path. -- -- @ -- > changeBaseName "foo\/bar.txt" "qux.png" -- "foo\/qux.png" -- @ -- changeBaseName :: CBytes -> CBytes -- ^ new base name -> IO CBytes changeBaseName p b = do let l = CB.length p + CB.length b + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe b $ \ pb -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_change_basename pp pb pbuf (fromIntegral l) return p' -- | Determines the root of a path. -- -- This function determines the root of a path by finding it’s length. -- The root comes before the first segment of the path. -- For example, @C:\\@ is the root of @C:\\folder\\file.txt@. -- It always starts at the submitted path. If the path has no root, 'CB.empty' will be returned. -- -- +---------+--------------------------+----------------------+ -- | Style | Path | Root | -- +---------+--------------------------+----------------------+ -- | UNIX | \/test\/ | \/ | -- +---------+--------------------------+----------------------+ -- | UNIX | test.txt | "" | -- +---------+--------------------------+----------------------+ -- | UNIX | C:\\test.txt | "" | -- +---------+--------------------------+----------------------+ -- | UNIX | \\folder\\ | "" | -- +---------+--------------------------+----------------------+ -- | WINDOWS | \/test.txt | \/ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | \\test.txt | \\ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | C:\\test.txt | C:\\ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | \\\\server\\folder\\data | \\\\server\\folder\\ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | \\\\.\\folder\\data | \\\\.\\ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | \\\\?\\folder\\data | \\\\?\\ | -- +---------+--------------------------+----------------------+ -- | WINDOWS | C:test.txt | C: | -- +---------+--------------------------+----------------------+ -- | WINDOWS | ..\\hello\\world.txt | "" | -- +---------+--------------------------+----------------------+ -- splitRoot :: CBytes -> IO (CBytes, CBytes) -- ^ return (root, rest path) {-# INLINABLE splitRoot #-} splitRoot p = do off <- withCBytesUnsafe p hs_cwk_path_get_root if off == 0 then return (CB.empty, p) else return ( CB (V.PrimVector (CB.rawPrimArray p) 0 off) , CB (V.PrimVector (CB.rawPrimArray p) off (CB.length p - off))) -- | Changes the root of a file path. -- -- @ -- > changeBaseName "C:\\\\test.txt" "D:\\\\" -- windows style -- "D:\\test.txt" -- @ -- changeRoot :: CBytes -> CBytes -- ^ new base name -> IO CBytes changeRoot p r = do let l = CB.length p + CB.length r + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe r $ \ pr -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_change_root pp pr pbuf (fromIntegral l) return p' -- | Split file path into (root, segments, basename) tuple. -- -- Root may be empty, segments are separated by 'pathSeparator' and never be empty if any. splitSegments :: CBytes -> IO (CBytes, [CBytes]) -- ^ return (root, segments) {-# INLINABLE splitSegments #-} splitSegments p = do (root, CB seg) <- splitRoot =<< normalize p sty <- getPathStyle let segs = if V.null seg then [] else case sty of UnixStyle -> V.splitWith (== (#const SLASH)) seg _ -> V.splitWith (\ w -> w == (#const SLASH) || w == (#const BACKSLASH)) seg return (root, (map CB segs)) -- | Determine whether the path is absolute or not. -- -- This function checks whether the path is an absolute (fully qualified) path or not. -- A path is considered to be absolute if the root ends with a separator. -- -- +---------+--------------------------+-----------+ -- | Style | Path | Result | -- +---------+--------------------------+-----------+ -- | UNIX | \/test\/ | True | -- +---------+--------------------------+-----------+ -- | UNIX | test.txt | False | -- +---------+--------------------------+-----------+ -- | UNIX | C:\\test.txt | False | -- +---------+--------------------------+-----------+ -- | UNIX | \\folder\\ | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | \/test.txt | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\test.txt | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | C:\\test.txt | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\server\\folder\\data | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\.\\folder\\data | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\?\\folder\\data | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | C:test.txt | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | ..\\hello\\world.txt | False | -- +---------+--------------------------+-----------+ -- isAbsolute :: CBytes -> IO Bool isAbsolute p = (/=0) <$> withCBytesUnsafe p cwk_path_is_absolute -- | Determine whether the path is relative or not. -- -- This function checks whether the path is a relative path or not. -- A path is considered to be relative if the root does not end with a separator. -- -- +---------+--------------------------+-----------+ -- | Style | Path | Result | -- +---------+--------------------------+-----------+ -- | UNIX | \/test\/ | False | -- +---------+--------------------------+-----------+ -- | UNIX | test.txt | True | -- +---------+--------------------------+-----------+ -- | UNIX | C:\\test.txt | True | -- +---------+--------------------------+-----------+ -- | UNIX | \\folder\\ | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | \/test.txt | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\test.txt | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | C:\\test.txt | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\server\\folder\\data | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\.\\folder\\data | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | \\\\?\\folder\\data | False | -- +---------+--------------------------+-----------+ -- | WINDOWS | C:test.txt | True | -- +---------+--------------------------+-----------+ -- | WINDOWS | ..\\hello\\world.txt | True | -- +---------+--------------------------+-----------+ -- isRelative :: CBytes -> IO Bool isRelative p = (/=0) <$> withCBytesUnsafe p cwk_path_is_relative -- | Joins two paths together. -- -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | Style | Path A | Path B | Result | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | UNIX | hello\/there | ..\/world | hello\/world | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | UNIX | \/first | \/second | \/first\/second | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | UNIX | hello | .. | . | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | UNIX | hello\/there | .. | hello | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | UNIX | hello | there | hello\/there | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | WINDOWS | this\\ | C:\\..\\..\\is\\a\\test\\ | is\\a\\test | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | WINDOWS | C:\\this\\path | C:\\is\\a\\test\\ | C:\\this\\path\\C:\\is\\a\\test | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | WINDOWS | C:\\this\\path | C:\\..\\is\\a\\test\\ | C:\\this\\path\\is\\a\\test | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- | WINDOWS | \\\\s1\\unc\\path | \\\\s2\\unc\\pa | \\\\s1\\unc\\pa\\s2\\unc\\path | -- +---------+-----------------------+---------------------------+--------------------------------------+ -- join :: CBytes -> CBytes -> IO CBytes join p p2 = do let l = CB.length p + CB.length p2 + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe p2 $ \ pp2 -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_join pp pp2 pbuf (fromIntegral l) return p' -- | Joins multiple paths together. -- -- This function generates a new path by joining multiple paths together. -- It will remove double separators, and unlike 'absolute', -- it permits the use of multiple relative paths to combine. concat :: [CBytes] -> IO CBytes concat ps = do (p', _) <- withCBytesListUnsafe ps $ \ pp l -> do let l' = sum (List.map (\ p -> CB.length p + (#const BUF_EXT_SIZ)) ps) allocCBytesUnsafe l' $ \ pbuf -> hs_cwk_path_join_multiple pp l pbuf (fromIntegral l') return p' -- | Creates a normalized version of the path. -- The following will be true for the normalized path: -- -- * "..\/" will be resolved. -- * ".\/" will be removed. -- * double separators will be fixed with a single separator. -- * separator suffixes will be removed. -- -- +--------------------------------------------------+-------------------+ -- | Input | Output | -- +--------------------------------------------------+-------------------+ -- | \/var | \/var | -- +--------------------------------------------------+-------------------+ -- | \/var\/logs\/test\/..\/..\/ | \/var | -- +--------------------------------------------------+-------------------+ -- | \/var\/logs\/test\/..\/..\/..\/..\/..\/..\/ | \/ | -- +--------------------------------------------------+-------------------+ -- | rel\/..\/..\/ | .. | -- +--------------------------------------------------+-------------------+ -- | \/var\/\/\/\/logs\/\/test\/ | \/var\/logs\/test | -- +--------------------------------------------------+-------------------+ -- | \/var\/.\/.\/.\/.\/ | \/var | -- +--------------------------------------------------+-------------------+ -- | \/var\/.\/logs\/.\/\/test\/..\/\/..\/\/\/\/\/\/ | \/var | -- +--------------------------------------------------+-------------------+ -- normalize :: CBytes -> IO CBytes normalize p = do let l = CB.length p + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_normalize pp pbuf (fromIntegral l) return p' -- | Finds common portions in two paths. -- -- +---------+---------------------------+---------------------------+----------------------+ -- | Style | Base | Other | Result | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/test\/abc\/..\/foo\/bar | \/test\/foo\/har | \/test\/abc\/..\/foo | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/test\/foo\/har | \/test\/abc\/..\/foo\/bar | \/test\/foo | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/test\/abc.txt | test\/abc.txt | "" | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/ | "" | "" | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/this\/\/\/is\/a\/\/test | \/this\/\/is\/a\/\/\/file | \/this\/\/\/is\/a | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/this\/is\/a\/test | \/this\/is\/a\/ | \/this\/is\/a | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/this\/is\/a\/test | \/this\/is\/a | \/this\/is\/a | -- +---------+---------------------------+---------------------------+----------------------+ -- | UNIX | \/this\/is\/a\/test | \/this\/is\/a\/string | \/this\/is\/a | -- +---------+---------------------------+---------------------------+----------------------+ -- | WINDOWS | C:\/abc\/test.txt | C:\/ | C:\/ | -- +---------+---------------------------+---------------------------+----------------------+ -- | WINDOWS | C:\/abc\/test.txt | C:\/def\/test.txt | C:\/ | -- +---------+---------------------------+---------------------------+----------------------+ -- | WINDOWS | C:\/test\/abc.txt | D:\/test\/abc.txt | "" | -- +---------+---------------------------+---------------------------+----------------------+ -- intersection :: CBytes -- ^ The base path which will be compared with the other path. -> CBytes -- ^ The other path which will compared with the base path. -> IO CBytes intersection p1 p2 = do len <- withCBytesUnsafe p1 $ \ pp1 -> withCBytesUnsafe p2 $ \ pp2 -> cwk_path_get_intersection pp1 pp2 if len == 0 then return CB.empty else return (CB (V.PrimVector (CB.rawPrimArray p1) 0 (fromIntegral len))) -- | Generates an absolute path based on a base. -- -- This function generates an absolute path based on a base path and another path. -- It is guaranteed to return an absolute path. -- If the second submitted path is absolute, it will override the base path. -- -- +----------------------+----------------------+-----------------------+ -- | Base | Path | Result | -- +----------------------+----------------------+-----------------------+ -- | \/hello\/there | ..\/..\/..\/..\/..\/ | \/ | -- +----------------------+----------------------+-----------------------+ -- | \/hello\/\/..\/there | test\/\/thing | \/there\/test\/thing | -- +----------------------+----------------------+-----------------------+ -- | hello\/there | \/test | \/test | -- +----------------------+----------------------+-----------------------+ -- | hello\/there | test | \/hello\/there\/test | -- +----------------------+----------------------+-----------------------+ -- | \/hello\/there | \/test | \/test | -- +----------------------+----------------------+-----------------------+ -- | \/hello\/there | .. | \/hello | -- +----------------------+----------------------+-----------------------+ -- absolute :: CBytes -- ^ The absolute base path on which the relative path will be applied. -> CBytes -- ^ The relative path which will be applied on the base path. -> IO CBytes absolute p p2 = do let l = CB.length p + CB.length p2 + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe p2 $ \ pp2 -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_get_absolute pp pp2 pbuf (fromIntegral l) return p' -- | Generates a relative path based on a base. -- -- This function generates a relative path based on a base path and another path. -- It determines how to get to the submitted path, starting from the base directory. -- -- +---------+--------------------------+--------------------------+-----------------+ -- | Style | Base | Path | Result | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/..\/..\/ | \/..\/..\/ | . | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/same | \/path\/not_same\/ho\/.. | ..\/not_same | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/not_same\/ho\/.. | \/path\/same | ..\/same | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/same | \/path\/same\/ho\/.. | . | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/same\/ho\/.. | \/path\/same | . | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/same | \/path\/same | . | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/long\/one | \/path\/long\/one\/two | two | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/path\/long\/one\/two | \/path\/long\/one | .. | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | .\/this\/is\/path_one | .\/this\/is\/path_two | ..\/path_two | -- +---------+--------------------------+--------------------------+-----------------+ -- | UNIX | \/this\/is\/path_one | \/this\/is\/path_two | ..\/path_two | -- +---------+--------------------------+--------------------------+-----------------+ -- | WINDOWS | C:\/path\/same | D:\/path\/same | "" | -- +---------+--------------------------+--------------------------+-----------------+ -- relative :: CBytes -- ^ The base path from which the relative path will start. -> CBytes -- ^ The target path where the relative path will point to. -> IO CBytes relative p p2 = do let l = CB.length p + CB.length p2 + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe p2 $ \ pp2 -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_get_relative pp pp2 pbuf (fromIntegral l) return p' -- | Split the extension of a file path. -- -- This function extracts the extension portion of a file path. -- -- +----------------------------+------------+ -- | Path | Result | -- +----------------------------+------------+ -- | \/my\/path.txt | .txt | -- +----------------------------+------------+ -- | \/my\/path | "" | -- +----------------------------+------------+ -- | \/my\/.path | .path | -- +----------------------------+------------+ -- | \/my\/path. | . | -- +----------------------------+------------+ -- | \/my\/path.abc.txt.tests | .tests | -- +----------------------------+------------+ -- splitExtension :: CBytes -> IO (CBytes, CBytes) -- ^ return (file, ext) {-# INLINABLE splitExtension #-} splitExtension p = do (len ,off) <- withCBytesUnsafe p $ \ pp -> allocPrimUnsafe $ \ plen -> hs_cwk_path_get_extension pp plen if off == -1 then return (p, CB.empty) else return ( CB (V.PrimVector (CB.rawPrimArray p) 0 off) , CB (V.PrimVector (CB.rawPrimArray p) off len)) -- | Changes the extension of a file path. -- -- This function changes the extension of a file name. -- The function will append an extension if the basename does not have an extension, -- or use the extension as a basename if the path does not have a basename. -- -- Note: -- -- * This function does not normalize the resulting path. You can use 'normalize' to do so. -- * If the new_extension parameter starts with a dot, -- the first dot will be ignored when the new extension is appended. -- -- @ -- > changeExtension "foo\/bar.txt" "png" -- "foo\/bar.png" -- @ -- changeExtension :: CBytes -- ^ The path which will be used to make the change. -> CBytes -- ^ The extension which will be placed within the new path. -> IO CBytes changeExtension p p2 = do let l = CB.length p + CB.length p2 + (#const BUF_EXT_SIZ) (p', _) <- withCBytesUnsafe p $ \ pp -> withCBytesUnsafe p2 $ \ pp2 -> allocCBytesUnsafe l $ \ pbuf -> cwk_path_change_extension pp pp2 pbuf (fromIntegral l) return p' -------------------------------------------------------------------------------- -- | Get a list of paths in the @$PATH@ variable. getSearchPath :: IO [CBytes] getSearchPath = do s <- getEnv' "PATH" sp <- searchPathSeparator return (splitSearchPath s sp) where splitSearchPath (CB bs) sp = go bs sp go bs sp = case V.break (== sp) bs of (p, rest) | V.null rest -> [] | otherwise -> g p : go (V.drop 1 rest) sp g bs = if V.null bs then CB.singleton (#const DOT) else CB bs -------------------------------------------------------------------------------- foreign import ccall unsafe hs_cwk_path_get_basename :: BA## Word8 -> MBA## Int -> IO Int foreign import ccall unsafe cwk_path_change_basename :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize -- foreign import ccall unsafe hs_cwk_path_get_dirname :: BA## Word8 -> IO Int foreign import ccall unsafe hs_cwk_path_get_root :: BA## Word8 -> IO Int foreign import ccall unsafe cwk_path_change_root :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe cwk_path_is_absolute :: BA## Word8 -> IO CBool foreign import ccall unsafe cwk_path_is_relative :: BA## Word8 -> IO CBool foreign import ccall unsafe cwk_path_join :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe hs_cwk_path_join_multiple :: BAArray## Word8 -> Int -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe cwk_path_normalize :: BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe cwk_path_get_intersection :: BA## Word8 -> BA## Word8 -> IO CSize foreign import ccall unsafe cwk_path_get_absolute :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe cwk_path_get_relative :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe hs_cwk_path_get_extension :: BA## Word8 -> MBA## CSize -> IO Int foreign import ccall unsafe cwk_path_change_extension :: BA## Word8 -> BA## Word8 -> MBA## Word8 -> CSize -> IO CSize foreign import ccall unsafe cwk_path_guess_style :: BA## Word8 -> IO CInt foreign import ccall unsafe cwk_path_get_style :: IO CInt foreign import ccall unsafe cwk_path_set_style :: CInt -> IO ()