summaryrefslogtreecommitdiff
path: root/Branch.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2011-06-29 22:19:40 -0400
committerGravatar Joey Hess <joey@kitenet.net>2011-06-29 22:19:40 -0400
commit899ecbfba1c015c2c80f729c7e0d5544d7bcc415 (patch)
treea0e25e7427c175b8d470d06648c61f86e520d113 /Branch.hs
parente1c18ddec455e5d1259ab46ccccbe6a9c7079de6 (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.hs58
1 files changed, 34 insertions, 24 deletions
diff --git a/Branch.hs b/Branch.hs
index 4f568e36b..033b7c6d0 100644
--- a/Branch.hs
+++ b/Branch.hs
@@ -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]