17import qualified Codec.Archive.Zip as Zip
18import Control.Exception (catch)
19import qualified Data.Binary as Binary
20import qualified Data.ByteString as BS
21import qualified Data.ByteString.Internal as BS
22import qualified Data.ByteString.Builder as B
23import qualified Data.ByteString.Lazy as LBS
24import qualified Data.Fixed as Fixed
25import qualified Data.List as List
26import qualified Data.Time.Clock.POSIX as Time
27import qualified Foreign.ForeignPtr as FPtr
28import GHC.IO.Exception (IOException, IOErrorType(InvalidArgument))
29import qualified System.Directory as Dir
30import qualified System.FilePath as FP
31import System.FilePath ((</>))
32import qualified System.IO as IO
33import System.IO.Error (ioeGetErrorType, annotateIOError, modifyIOError)
40newtype Time = Time Fixed.Pico
44getTime :: FilePath -> IO Time
47 (Time . realToFrac . Time.utcTimeToPOSIXSeconds)
48-- TODO (Time . Time.nominalDiffTimeToSeconds . Time.utcTimeToPOSIXSeconds)
49 (Dir.getModificationTime path)
52instance Binary.Binary Time where
53 put (Time time) = Binary.put time
54 get = Time <$> Binary.get
61writeBinary :: (Binary.Binary a) => FilePath -> a -> IO ()
62writeBinary path value =
63 do let dir = FP.dropFileName path
64 Dir.createDirectoryIfMissing True dir
65 Binary.encodeFile path value
68readBinary :: (Binary.Binary a) => FilePath -> IO (Maybe a)
70 do pathExists <- Dir.doesFileExist path
73 do result <- Binary.decodeFileOrFail path
75 Right a -> return (Just a)
76 Left e -> return Nothing
85writeUtf8 :: FilePath -> BS.ByteString -> IO ()
86writeUtf8 path content =
87 withUtf8 path IO.WriteMode $ \handle ->
88 BS.hPut handle content
91withUtf8 :: FilePath -> IO.IOMode -> (IO.Handle -> IO a) -> IO a
92withUtf8 path mode callback =
93 IO.withFile path mode $ \handle ->
94 do IO.hSetEncoding handle IO.utf8
102readUtf8 :: FilePath -> IO BS.ByteString
104 withUtf8 path IO.ReadMode $ \handle ->
105 modifyIOError (encodingError path) $
106 do fileSize <- catch (IO.hFileSize handle) useZeroIfNotRegularFile
107 let readSize = max 0 (fromIntegral fileSize) + 1
108 hGetContentsSizeHint handle readSize (max 255 readSize)
111useZeroIfNotRegularFile :: IOException -> IO Integer
112useZeroIfNotRegularFile _ =
116hGetContentsSizeHint :: IO.Handle -> Int -> Int -> IO BS.ByteString
117hGetContentsSizeHint handle =
120 readChunks chunks readSize incrementSize =
121 do fp <- BS.mallocByteString readSize
122 readCount <- FPtr.withForeignPtr fp $ \buf -> IO.hGetBuf handle buf readSize
123 let chunk = BS.PS fp 0 readCount
124 if readCount < readSize && readSize > 0
125 then return $! BS.concat (reverse (chunk:chunks))
126 else readChunks (chunk:chunks) incrementSize (min 32752 (readSize + incrementSize))
129encodingError :: FilePath -> IOError -> IOError
130encodingError path ioErr =
131 case ioeGetErrorType ioErr of
134 (userError "Bad encoding; the file must be valid UTF-8")
147writeBuilder :: FilePath -> B.Builder -> IO ()
148writeBuilder path builder =
149 IO.withBinaryFile path IO.WriteMode $ \handle ->
150 do IO.hSetBuffering handle (IO.BlockBuffering Nothing)
151 B.hPutBuilder handle builder
158writePackage :: FilePath -> Zip.Archive -> IO ()
159writePackage destination archive =
160 case Zip.zEntries archive of
165 do let root = length (Zip.eRelativePath entry)
166 mapM_ (writeEntry destination root) entries
169writeEntry :: FilePath -> Int -> Zip.Entry -> IO ()
170writeEntry destination root entry =
172 path = drop root (Zip.eRelativePath entry)
174 if List.isPrefixOf "src/" path then
175 if not (null path) && last path == '/'
176 then Dir.createDirectoryIfMissing True (destination </> path)
177 else LBS.writeFile (destination </> path) (Zip.fromEntry entry)
186exists :: FilePath -> IO Bool
188 Dir.doesFileExist path
195remove :: FilePath -> IO ()
197 do exists_ <- Dir.doesFileExist path
199 then Dir.removeFile path
203removeDir :: FilePath -> IO ()
205 do exists_ <- Dir.doesDirectoryExist path
207 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