-- copied from ndmitchell/shake/src/Development/Shake/Internal/FileName.hs commit 8a96542 module General.FileName( FileName, fileNameFromString, fileNameFromByteString, fileNameToString, fileNameToByteString, byteStringToFileName ) where import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.UTF8 as UTF8 import Development.Shake.Classes import qualified System.FilePath as Native import System.Info.Extra import Data.List --------------------------------------------------------------------- -- FileName newtype -- | The hash of the filename, and the UTF8 ByteString data FileName = FileName Int !BS.ByteString deriving Eq instance NFData FileName where rnf FileName{} = () instance Ord FileName where compare (FileName _ a) (FileName _ b) = compare a b instance Hashable FileName where hashWithSalt _ = hash hash (FileName x _) = x instance Show FileName where show = fileNameToString fileNameToString :: FileName -> FilePath fileNameToString = UTF8.toString . fileNameToByteString fileNameToByteString :: FileName -> BS.ByteString fileNameToByteString (FileName _ x) = x fileNameFromString :: FilePath -> FileName fileNameFromString = fileNameFromByteString . UTF8.fromString fileNameFromByteString :: BS.ByteString -> FileName fileNameFromByteString = byteStringToFileName . filepathNormalise -- don't normalise byteStringToFileName :: BS.ByteString -> FileName byteStringToFileName x = FileName (hash x) x --------------------------------------------------------------------- -- NORMALISATION -- | Equivalent to @toStandard . normaliseEx@ from "Development.Shake.FilePath". filepathNormalise :: BS.ByteString -> BS.ByteString filepathNormalise xs | isWindows, Just (a,xs) <- BS.uncons xs, sep a, Just (b,_) <- BS.uncons xs, sep b = '/' `BS.cons` f xs | otherwise = f xs where sep = Native.isPathSeparator f o = deslash o $ BS.concat $ (slash:) $ intersperse slash $ reverse $ (BS.empty:) $ g 0 $ reverse $ split o deslash o x | x == slash = case (pre,pos) of (True,True) -> slash (True,False) -> BS.pack "/." (False,True) -> BS.pack "./" (False,False) -> dot | otherwise = (if pre then id else BS.tail) $ (if pos then id else BS.init) x where pre = not (BS.null o) && sep (BS.head o) pos = not (BS.null o) && sep (BS.last o) g i [] = replicate i dotDot g i (x:xs) | BS.null x = g i xs g i (x:xs) | x == dotDot = g (i+1) xs g i (x:xs) | x == dot = g i xs g 0 (x:xs) = x : g 0 xs g i (_:xs) = g (i-1) xs -- equivalent to eliminating ../x split = BS.splitWith sep dotDot = BS.pack ".." dot = BS.singleton '.' slash = BS.singleton '/'