@@ -19,6 +19,7 @@ import System.Nix.Hash
1919 , decodeBase32
2020 , SomeNamedDigest
2121 )
22+ import System.Nix.Internal.Base32 (digits32 )
2223
2324import Data.Text (Text )
2425import Data.Text.Encoding (encodeUtf8 )
@@ -27,13 +28,15 @@ import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)
2728import Data.ByteString (ByteString )
2829import qualified Data.ByteString as BS
2930import qualified Data.ByteString.Char8 as BC
31+ import qualified Data.Char
3032import Data.Hashable (Hashable (.. ))
3133import Data.HashSet (HashSet )
3234import Data.Proxy (Proxy (.. ))
3335
36+ import Data.Attoparsec.ByteString.Char8 (Parser , (<?>) )
37+ import qualified Data.Attoparsec.ByteString.Char8 as P
3438import System.FilePath (splitFileName )
3539
36- import Data.Char
3740-- | A path in a Nix store.
3841--
3942-- From the Nix thesis: A store path is the full path of a store
@@ -113,20 +116,23 @@ makeStorePathName n = case validStorePathName n of
113116 True -> Right $ StorePathName n
114117 False -> Left $ reasonInvalid n
115118
119+ reasonInvalid :: Text -> String
116120reasonInvalid n | n == " " = " Empty name"
117121reasonInvalid n | (T. length n > 211 ) = " Path too long"
118122reasonInvalid n | (T. head n == ' .' ) = " Leading dot"
119123reasonInvalid n | otherwise = " Invalid character"
120124
125+ validStorePathName :: Text -> Bool
121126validStorePathName " " = False
122127validStorePathName n = (T. length n <= 211 )
123128 && T. head n /= ' .'
124- && T. all validChar n
125- where
126- validChar c = any ($ c) $
127- [ isAsciiLower -- 'a'..'z'
128- , isAsciiUpper -- 'A'..'Z'
129- , isDigit
129+ && T. all validStorePathNameChar n
130+
131+ validStorePathNameChar :: Char -> Bool
132+ validStorePathNameChar c = any ($ c) $
133+ [ Data.Char. isAsciiLower -- 'a'..'z'
134+ , Data.Char. isAsciiUpper -- 'A'..'Z'
135+ , Data.Char. isDigit
130136 ] ++
131137 map (==) " +-._?="
132138
@@ -186,3 +192,31 @@ parsePath expectedRoot x =
186192 else Left $ unwords $ [ " Root store dir mismatch, expected " , expectedRoot, " got" , rootDir']
187193 in
188194 StorePath <$> digest <*> name <*> storeDir
195+
196+ pathParser :: FilePath -> Parser StorePath
197+ pathParser expectedRoot = do
198+ P. string (BC. pack expectedRoot)
199+ <?> " Store root mismatch" -- e.g. /nix/store
200+
201+ P. char ' /'
202+ <?> " Expecting path separator"
203+
204+ digest <- decodeBase32 . T. pack . BC. unpack
205+ <$> P. takeWhile1 (\ c -> c `elem` digits32)
206+ <?> " Invalid Base32 part"
207+
208+ P. char ' -'
209+ <?> " Expecting dash (path name separator)"
210+
211+ c0 <- P. satisfy (\ c -> c /= ' .' && validStorePathNameChar c)
212+ <?> " Leading path name character is a dot or invalid character"
213+
214+ rest <- P. takeWhile validStorePathNameChar
215+ <?> " Path name contains invalid character"
216+
217+ let name = makeStorePathName
218+ $ T. pack . BC. unpack
219+ $ BC. cons c0 rest
220+
221+ either fail return
222+ $ StorePath <$> digest <*> name <*> pure expectedRoot
0 commit comments