17import Control.Exception (catch)
18import qualified Data.Binary as Binary
19import qualified Data.ByteString as BS
20import qualified Data.ByteString.Internal as BS
21import qualified Data.ByteString.Builder as B
22import qualified Data.Fixed as Fixed
23import qualified Data.Time.Clock.POSIX as Time
24import qualified Foreign.ForeignPtr as FPtr
25import GHC.IO.Exception (IOException, IOErrorType(InvalidArgument))
26import qualified System.Directory as Dir
27import qualified System.FileLock as Lock
28import qualified System.FilePath as FP
29import qualified System.IO as IO
30import System.IO.Error (ioeGetErrorType, annotateIOError, modifyIOError)
37newtype Time = Time Fixed.Pico
41getTime :: FilePath -> IO Time
44 (Time . realToFrac . Time.utcTimeToPOSIXSeconds)
45-- TODO (Time . Time.nominalDiffTimeToSeconds . Time.utcTimeToPOSIXSeconds)
46 (Dir.getModificationTime path)
49instance Binary.Binary Time where
50 put (Time time) = Binary.put time
51 get = Time <$> Binary.get
58withLock :: FilePath -> IO a -> IO a
60 Lock.withFileLock path Lock.Exclusive (\_ -> work)
67writeBinary :: (Binary.Binary a) => FilePath -> a -> IO ()
68writeBinary path value =
69 do let dir = FP.dropFileName path
70 Dir.createDirectoryIfMissing True dir
71 Binary.encodeFile path value
74readBinary :: (Binary.Binary a) => FilePath -> IO (Maybe a)
76 do result <- Binary.decodeFileOrFail path
78 Right a -> return (Just a)
79 Left _ -> return Nothing
86writeUtf8 :: FilePath -> BS.ByteString -> IO ()
87writeUtf8 path content =
88 withUtf8 path IO.WriteMode $ \handle ->
89 BS.hPut handle content
92withUtf8 :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a
93withUtf8 path mode callback =
94 IO.withFile path mode $ \handle ->
95 do IO.hSetEncoding handle IO.utf8
103readUtf8 :: FilePath -> IO BS.ByteString
105 withUtf8 path IO.ReadMode $ \handle ->
106 modifyIOError (encodingError path) $
107 do fileSize <- catch (IO.hFileSize handle) useZeroIfNotRegularFile
108 let readSize = max 0 (fromIntegral fileSize) + 1
109 hGetContentsSizeHint handle readSize (max 255 readSize)
112useZeroIfNotRegularFile :: IOException -> IO Integer
113useZeroIfNotRegularFile _ =
117hGetContentsSizeHint :: IO.Handle -> Int -> Int -> IO BS.ByteString
118hGetContentsSizeHint handle =
121 readChunks chunks readSize incrementSize =
122 do fp <- BS.mallocByteString readSize
123 readCount <- FPtr.withForeignPtr fp $ \buf -> IO.hGetBuf handle buf readSize
124 let chunk = BS.PS fp 0 readCount
125 if readCount < readSize && readSize > 0
126 then return $! BS.concat (reverse (chunk:chunks))
127 else readChunks (chunk:chunks) incrementSize (min 32752 (readSize + incrementSize))
130encodingError :: FilePath -> IOError -> IOError
131encodingError path ioErr =
132 case ioeGetErrorType ioErr of
135 (userError "Bad encoding; the file must be valid UTF-8")
148writeBuilder :: FilePath -> B.Builder -> IO ()
149writeBuilder path builder =
150 IO.withBinaryFile path IO.WriteMode $ \handle ->
151 do IO.hSetBuffering handle (IO.BlockBuffering Nothing)
152 B.hPutBuilder handle builder
159exists :: FilePath -> IO Bool
161 Dir.doesFileExist path
168remove :: FilePath -> IO ()
170 do exists_ <- Dir.doesFileExist path
172 then Dir.removeFile path
176removeDir :: FilePath -> IO ()
178 do exists_ <- Dir.doesDirectoryExist path
180 then Dir.removeDirectoryRecursive path
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list