diff options
author | 2011-06-29 22:19:40 -0400 | |
---|---|---|
committer | 2011-06-29 22:19:40 -0400 | |
commit | 899ecbfba1c015c2c80f729c7e0d5544d7bcc415 (patch) | |
tree | a0e25e7427c175b8d470d06648c61f86e520d113 /Branch.hs | |
parent | e1c18ddec455e5d1259ab46ccccbe6a9c7079de6 (diff) |
improve git cat-file code
Now it reads the size specified, rather than using the sentinal hack to
determine EOF.
It still depends on error messages to handle files that are not present.
Diffstat (limited to 'Branch.hs')
-rw-r--r-- | Branch.hs | 58 |
1 files changed, 34 insertions, 24 deletions
@@ -17,7 +17,7 @@ module Branch ( name ) where -import Control.Monad (unless, liftM) +import Control.Monad (when, unless, liftM) import Control.Monad.State (liftIO) import System.FilePath import System.Directory @@ -26,6 +26,9 @@ import System.Cmd.Utils import Data.Maybe import Data.List import System.IO +import System.IO.Unsafe +import Foreign +import Data.Char import Types.BranchState import qualified GitRepo as Git @@ -239,32 +242,39 @@ catFile file = do g <- Annex.gitRepo let cmd = Git.gitCommandLine g [Param "cat-file", Param "--batch"] - let gitcmd = join " " $ "git" : toCommand cmd + let gitcmd = join " " ("git" : toCommand cmd) (_, from, to) <- liftIO $ hPipeBoth "sh" - -- want stderr on stdin for sentinal, and - -- to ignore other error messages - ["-c", gitcmd ++ " 2>&1"] + -- want stderr on stdin to handle error messages + ["-c", "LANG=C exec " ++ gitcmd ++ " 2>&1"] setState state { catFileHandles = Just (from, to) } ask (from, to) - ask (from, to) = do - _ <- liftIO $ do - hPutStr to $ - fullname ++ ":" ++ file ++ "\n" ++ - sentinal ++ "\n" - hFlush to - return . unlines =<< readContent from [] - readContent from ls = do - l <- liftIO $ hGetLine from - if l == sentinal_line - -- first line is blob info, - -- or maybe an error message - then return $ drop 1 $ reverse ls - else readContent from (l:ls) - -- To find the end of a catted file, ask for a sentinal - -- value that is always missing, and look for the error - -- message. Utterly nasty, probably will break one day. - sentinal = ":" - sentinal_line = sentinal ++ " missing" + ask (from, to) = liftIO $ do + let want = fullname ++ ":" ++ file + hPutStrLn to want + hFlush to + header <- hGetLine from + if header == want ++ " missing" + then return "" + else do + let [_sha, _type, size] = words header + let bytes = read size + fp <- mallocForeignPtrBytes (fromIntegral bytes) + len <- withForeignPtr fp $ \buf -> hGetBuf from buf (fromIntegral bytes) + when (len /= bytes) $ + error "short read from git cat-file" + content <- lazySlurp fp 0 len + c <- hGetChar from + when (c /= '\n') $ + error "missing newline from git cat-file" + return content + +lazySlurp :: ForeignPtr Word8 -> Int -> Int -> IO String +lazySlurp fp ix len + | ix == len = return [] + | otherwise = do + c <- withForeignPtr fp $ \p -> peekElemOff p ix + cs <- unsafeInterleaveIO (lazySlurp fp (ix+1) len) + return $ chr (fromIntegral c) : cs {- Lists all files on the branch. There may be duplicates in the list. -} files :: Annex [FilePath] |