@@ -6,6 +6,7 @@ import Prelude.Maybe
66import Prelude.Monad
77import Prelude.Chars
88import Prelude.Strings
9+import Prelude.Nat
910import Prelude.Cast
1011import Prelude.Bool
1112import Prelude.Basics
@@ -26,11 +27,12 @@ data File : Type where
2627||| An error from a file operation
2728-- This is built in idris_mkFileError() in rts/idris_stdfgn.c. Make sure
2829-- the values correspond!
29−data FileError = FileReadError
30+
31+data FileError = GenericFileError Int -- errno
32+ | FileReadError
3033 | FileWriteError
3134 | FileNotFound
3235 | PermissionDenied
33− | GenericFileError Int -- errno
3436
3537private
3638strError : Int -> String
@@ -133,6 +135,22 @@ export
133135closeFile : File -> IO ()
134136closeFile (FHandle h) = do_fclose h
135137
138+private
139+do_getFileSize : Ptr -> IO Int
140+do_getFileSize h = foreign FFI_C "fileSize" (Ptr -> IO Int) h
141+
142+||| Return the size of a File
143+||| Returns an error if the File is not an ordinary file (e.g. a directory)
144+||| Also note that this currently returns an Int, which may overflow if the
145+||| file is very big
146+export
147+fileSize : File -> IO (Either FileError Int)
148+fileSize (FHandle h) = do s <- do_getFileSize h
149+ if (s < 0)
150+ then do err <- getFileError
151+ return (Left err)
152+ else return (Right s)
153+
136154private
137155do_fread : Ptr -> IO' l String
138156do_fread h = prim_fread h
@@ -233,22 +251,28 @@ fpoll (FHandle h) = do p <- foreign FFI_C "fpoll" (Ptr -> IO Int) h
233251 return (p > 0)
234252
235253||| Read the contents of a file into a string
236−-- might be reading something infinitely long like /dev/null ...
237−covering export
238−readFile : String -> IO (Either FileError String)
254+||| This checks the size of the file before beginning to read, and only
255+||| reads that many bytes, to ensure that it remains a total function if
256+||| the file is appended to while being read.
257+||| Returns an error if filepath is not a normal file.
258+export
259+readFile : (filepath : String) -> IO (Either FileError String)
239260readFile fn = do Right h <- openFile fn Read
240261 | Left err => return (Left err)
241− c <- readFile' h ""
262+ Right max <- fileSize h
263+ | Left err => return (Left err)
264+ c <- readFile' h max ""
242265 closeFile h
243266 return c
244267 where
245− covering
246− readFile' : File -> String -> IO (Either FileError String)
247− readFile' h contents =
268+ readFile' : File -> Int -> String -> IO (Either FileError String)
269+ readFile' h max contents =
248270 do x <- fEOF h
249− if not x then do Right l <- fGetLine h
271+ if not x && max > 0
272+ then do Right l <- fGetLine h
250273 | Left err => return (Left err)
251− readFile' h (contents ++ l)
274+ assert_total $
275+ readFile' h (max - cast (length l)) (contents ++ l)
252276 else return (Right contents)
253277
254278||| Write a string to a file