@@ -10,7 +10,8 @@ import Prelude.Cast
1010import Prelude.Bool
1111import Prelude.Basics
1212import Prelude.Classes
13−import Prelude.Monad
13+import Prelude.Either
14+import Prelude.Show
1415import IO
1516
1617%access public
@@ -19,6 +20,31 @@ import IO
1920abstract
2021data File = FHandle Ptr
2122
23+||| An error from a file operation
24+-- This is built in idris_mkFileError() in rts/idris_stdfgn.c. Make sure
25+-- the values correspond!
26+data FileError = FileReadError
27+ | FileWriteError
28+ | FileNotFound
29+ | PermissionDenied
30+ | GenericFileError Int -- errno
31+
32+private
33+strError : Int -> String
34+strError err = unsafePerformIO -- yeah, yeah...
35+ (foreign FFI_C "idris_showerror" (Int -> IO String) err)
36+
37+getFileError : IO FileError
38+getFileError = do MkRaw err <- foreign FFI_C "idris_mkFileError"
39+ (Ptr -> IO (Raw FileError)) prim__vm
40+ return err
41+
42+instance Show FileError where
43+ show Success = "Success"
44+ show FileNotFound = "File Not Found"
45+ show PermissionDenied = "Permission Denied"
46+ show (GenericFileError errno) = strError errno
47+
2248||| Standard input
2349stdin : File
2450stdin = FHandle prim__stdin
@@ -31,6 +57,13 @@ stdout = FHandle prim__stdout
3157stderr : File
3258stderr = FHandle prim__stderr
3359
60+do_ferror : Ptr -> IO Int
61+do_ferror h = foreign FFI_C "fileError" (Ptr -> IO Int) h
62+
63+ferror : File -> IO Bool
64+ferror (FHandle h) = do err <- do_ferror h
65+ return (not (err == 0))
66+
3467||| Call the RTS's file opening function
3568do_fopen : String -> String -> IO Ptr
3669do_fopen f m
@@ -39,12 +72,14 @@ do_fopen f m
3972||| Open a file
4073||| @ f the filename
4174||| @ m the mode as a String (`"r"`, `"w"`, or `"r+"`)
42−fopen : (f : String) -> (m : String) -> IO File
75+fopen : (f : String) -> (m : String) -> IO (Either FileError File)
4376fopen f m = do h <- do_fopen f m
44− return (FHandle h)
77+ if !(nullPtr h)
78+ then do err <- getFileError
79+ return (Left err)
80+ else return (Right (FHandle h))
4581
4682||| Check whether a file handle is actually a null pointer
47−partial
4883validFile : File -> IO Bool
4984validFile (FHandle h) = do x <- nullPtr h
5085 return (not x)
@@ -55,33 +90,26 @@ data Mode = Read | Write | ReadWrite
5590||| Open a file
5691||| @ f the filename
5792||| @ m the mode
58−partial
59−openFile : (f : String) -> (m : Mode) -> IO File
93+openFile : (f : String) -> (m : Mode) -> IO (Either FileError File)
6094openFile f m = fopen f (modeStr m) where
6195 modeStr Read = "r"
6296 modeStr Write = "w"
6397 modeStr ReadWrite = "r+"
6498
65−partial
6699do_fclose : Ptr -> IO ()
67100do_fclose h = foreign FFI_C "fileClose" (Ptr -> IO ()) h
68101
69−partial
70102closeFile : File -> IO ()
71103closeFile (FHandle h) = do_fclose h
72104
73−partial
74105do_fread : Ptr -> IO' l String
75106do_fread h = prim_fread h
76107
77−fgetc : File -> IO Char
78−fgetc (FHandle h) = return (cast !(foreign FFI_C "fgetc" (Ptr -> IO Int) h))
79−
80−fgetc' : File -> IO (Maybe Char)
81−fgetc' (FHandle h)
82− = do x <- foreign FFI_C "fgetc" (Ptr -> IO Int) h
83− if (x < 0) then return Nothing
84− else return (Just (cast x))
108+fgetc : File -> IO (Either FileError Char)
109+fgetc (FHandle h) = do let c = cast !(foreign FFI_C "fgetc" (Ptr -> IO Int) h)
110+ if !(ferror (FHandle h))
111+ then return (Left FileReadError)
112+ else return (Right c)
85113
86114fflush : File -> IO ()
87115fflush (FHandle h) = foreign FFI_C "fflush" (Ptr -> IO ()) h
@@ -89,9 +117,12 @@ fflush (FHandle h) = foreign FFI_C "fflush" (Ptr -> IO ()) h
89117do_popen : String -> String -> IO Ptr
90118do_popen f m = foreign FFI_C "do_popen" (String -> String -> IO Ptr) f m
91119
92−popen : String -> Mode -> IO File
120+popen : String -> Mode -> IO (Either FileError File)
93121popen f m = do ptr <- do_popen f (modeStr m)
94− return (FHandle ptr)
122+ if !(nullPtr ptr)
123+ then do err <- getFileError
124+ return (Left err)
125+ else return (Right (FHandle ptr))
95126 where
96127 modeStr Read = "r"
97128 modeStr Write = "w"
@@ -103,20 +134,25 @@ pclose (FHandle h) = foreign FFI_C "pclose" (Ptr -> IO ()) h
103134-- mkForeign (FFun "idris_readStr" [FPtr, FPtr] (FAny String))
104135-- prim__vm h
105136
106−partial
107−fread : File -> IO' l String
108−fread (FHandle h) = do_fread h
109−
110−partial
111−do_fwrite : Ptr -> String -> IO ()
112−do_fwrite h s = do prim_fwrite h s
113− return ()
114−
115−partial
116−fwrite : File -> String -> IO ()
137+fread : File -> IO (Either FileError String)
138+fread (FHandle h) = do str <- do_fread h
139+ if !(ferror (FHandle h))
140+ then return (Left FileReadError)
141+ else return (Right str)
142+
143+do_fwrite : Ptr -> String -> IO (Either FileError ())
144+do_fwrite h s = do res <- prim_fwrite h s
145+ if (res /= 0)
146+ then do errno <- getErrno
147+ if errno == 0
148+ then return (Left FileWriteError)
149+ else do err <- getFileError
150+ return (Left err)
151+ else return (Right ())
152+
153+fwrite : File -> String -> IO (Either FileError ())
117154fwrite (FHandle h) s = do_fwrite h s
118155
119−partial
120156do_feof : Ptr -> IO Int
121157do_feof h = foreign FFI_C "fileEOF" (Ptr -> IO Int) h
122158
@@ -125,32 +161,27 @@ feof : File -> IO Bool
125161feof (FHandle h) = do eof <- do_feof h
126162 return (not (eof == 0))
127163
128−partial
129−do_ferror : Ptr -> IO Int
130−do_ferror h = foreign FFI_C "fileError" (Ptr -> IO Int) h
131−
132−ferror : File -> IO Bool
133−ferror (FHandle h) = do err <- do_ferror h
134− return (not (err == 0))
135−
136164fpoll : File -> IO Bool
137165fpoll (FHandle h) = do p <- foreign FFI_C "fpoll" (Ptr -> IO Int) h
138166 return (p > 0)
139167
140168||| Read the contents of a file into a string
141−partial -- no error checking!
142−readFile : String -> IO String
143−readFile fn = do h <- openFile fn Read
169+partial -- might be reading something infinitely long like /dev/null ...
170+covering
171+readFile : String -> IO (Either FileError String)
172+readFile fn = do Right h <- openFile fn Read
173+ | Left err => return (Left err)
144174 c <- readFile' h ""
145175 closeFile h
146176 return c
147177 where
148178 partial
149− readFile' : File -> String -> IO String
179+ readFile' : File -> String -> IO (Either FileError String)
150180 readFile' h contents =
151181 do x <- feof h
152− if not x then do l <- fread h
182+ if not x then do Right l <- fread h
183+ | Left err => return (Left err)
153184 readFile' h (contents ++ l)
154− else return contents
185+ else return (Right contents)
155186
156187