1 {-# OPTIONS_GHC -XNoImplicitPrelude #-}
3 -----------------------------------------------------------------------------
5 -- Module : System.IO.Error
6 -- Copyright : (c) The University of Glasgow 2001
7 -- License : BSD-style (see the file libraries/base/LICENSE)
9 -- Maintainer : libraries@haskell.org
10 -- Stability : provisional
11 -- Portability : portable
13 -- Standard IO Errors.
15 -----------------------------------------------------------------------------
17 module System.IO.Error (
20 IOError, -- = IOException
22 userError, -- :: String -> IOError
24 mkIOError, -- :: IOErrorType -> String -> Maybe Handle
25 -- -> Maybe FilePath -> IOError
27 annotateIOError, -- :: IOError -> String -> Maybe Handle
28 -- -> Maybe FilePath -> IOError
30 -- ** Classifying I\/O errors
31 isAlreadyExistsError, -- :: IOError -> Bool
40 -- ** Attributes of I\/O errors
41 ioeGetErrorType, -- :: IOError -> IOErrorType
42 ioeGetLocation, -- :: IOError -> String
43 ioeGetErrorString, -- :: IOError -> String
44 ioeGetHandle, -- :: IOError -> Maybe Handle
45 ioeGetFileName, -- :: IOError -> Maybe FilePath
47 ioeSetErrorType, -- :: IOError -> IOErrorType -> IOError
48 ioeSetErrorString, -- :: IOError -> String -> IOError
49 ioeSetLocation, -- :: IOError -> String -> IOError
50 ioeSetHandle, -- :: IOError -> Handle -> IOError
51 ioeSetFileName, -- :: IOError -> FilePath -> IOError
53 -- * Types of I\/O error
54 IOErrorType, -- abstract
56 alreadyExistsErrorType, -- :: IOErrorType
57 doesNotExistErrorType,
58 alreadyInUseErrorType,
61 illegalOperationErrorType,
65 -- ** 'IOErrorType' predicates
66 isAlreadyExistsErrorType, -- :: IOErrorType -> Bool
67 isDoesNotExistErrorType,
68 isAlreadyInUseErrorType,
71 isIllegalOperationErrorType,
72 isPermissionErrorType,
75 -- * Throwing and catching I\/O errors
77 ioError, -- :: IOError -> IO a
79 catch, -- :: IO a -> (IOError -> IO a) -> IO a
80 try, -- :: IO a -> IO (Either IOError a)
82 modifyIOError, -- :: (IOError -> IOError) -> IO a -> IO a
86 import qualified Control.Exception.Base as New (catch)
94 #ifdef __GLASGOW_HASKELL__
97 import GHC.IO.Exception
98 import GHC.IO.Handle.Types
103 import Hugs.Prelude(Handle, IOException(..), IOErrorType(..), IO)
113 , isAlreadyExistsError -- :: IOError -> Bool
114 , isDoesNotExistError
115 , isAlreadyInUseError
121 , ioeGetErrorString -- :: IOError -> String
122 , ioeGetHandle -- :: IOError -> Maybe Handle
123 , ioeGetFileName -- :: IOError -> Maybe FilePath
125 import qualified NHC.Internal as NHC (IOError(..))
126 import qualified NHC.DErrNo as NHC (ErrNo(..))
127 import Data.Maybe (fromJust)
128 import Control.Monad (MonadPlus(mplus))
131 -- | The construct 'try' @comp@ exposes IO errors which occur within a
132 -- computation, and which are not fully handled.
134 -- Non-I\/O exceptions are not caught by this variant; to catch all
135 -- exceptions, use 'Control.Exception.try' from "Control.Exception".
138 try :: IO a -> IO (Either IOError a)
139 try f = catch (do r <- f
144 #if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
145 -- -----------------------------------------------------------------------------
146 -- Constructing an IOError
148 -- | Construct an 'IOError' of the given type where the second argument
149 -- describes the error location and the third and fourth argument
150 -- contain the file handle and file path of the file involved in the
151 -- error if applicable.
152 mkIOError :: IOErrorType -> String -> Maybe Handle -> Maybe FilePath -> IOError
153 mkIOError t location maybe_hdl maybe_filename =
154 IOError{ ioe_type = t,
155 ioe_location = location,
156 ioe_description = "",
157 #if defined(__GLASGOW_HASKELL__)
160 ioe_handle = maybe_hdl,
161 ioe_filename = maybe_filename
163 #endif /* __GLASGOW_HASKELL__ || __HUGS__ */
165 mkIOError EOF location maybe_hdl maybe_filename =
166 NHC.EOFError location (fromJust maybe_hdl)
167 mkIOError UserError location maybe_hdl maybe_filename =
168 NHC.UserError location ""
169 mkIOError t location maybe_hdl maybe_filename =
170 NHC.IOError location maybe_filename maybe_hdl (ioeTypeToErrNo t)
172 ioeTypeToErrNo AlreadyExists = NHC.EEXIST
173 ioeTypeToErrNo NoSuchThing = NHC.ENOENT
174 ioeTypeToErrNo ResourceBusy = NHC.EBUSY
175 ioeTypeToErrNo ResourceExhausted = NHC.ENOSPC
176 ioeTypeToErrNo IllegalOperation = NHC.EPERM
177 ioeTypeToErrNo PermissionDenied = NHC.EACCES
181 -- -----------------------------------------------------------------------------
184 -- | An error indicating that an 'IO' operation failed because
185 -- one of its arguments already exists.
186 isAlreadyExistsError :: IOError -> Bool
187 isAlreadyExistsError = isAlreadyExistsErrorType . ioeGetErrorType
189 -- | An error indicating that an 'IO' operation failed because
190 -- one of its arguments does not exist.
191 isDoesNotExistError :: IOError -> Bool
192 isDoesNotExistError = isDoesNotExistErrorType . ioeGetErrorType
194 -- | An error indicating that an 'IO' operation failed because
195 -- one of its arguments is a single-use resource, which is already
196 -- being used (for example, opening the same file twice for writing
197 -- might give this error).
198 isAlreadyInUseError :: IOError -> Bool
199 isAlreadyInUseError = isAlreadyInUseErrorType . ioeGetErrorType
201 -- | An error indicating that an 'IO' operation failed because
202 -- the device is full.
203 isFullError :: IOError -> Bool
204 isFullError = isFullErrorType . ioeGetErrorType
206 -- | An error indicating that an 'IO' operation failed because
207 -- the end of file has been reached.
208 isEOFError :: IOError -> Bool
209 isEOFError = isEOFErrorType . ioeGetErrorType
211 -- | An error indicating that an 'IO' operation failed because
212 -- the operation was not possible.
213 -- Any computation which returns an 'IO' result may fail with
214 -- 'isIllegalOperation'. In some cases, an implementation will not be
215 -- able to distinguish between the possible error causes. In this case
216 -- it should fail with 'isIllegalOperation'.
217 isIllegalOperation :: IOError -> Bool
218 isIllegalOperation = isIllegalOperationErrorType . ioeGetErrorType
220 -- | An error indicating that an 'IO' operation failed because
221 -- the user does not have sufficient operating system privilege
222 -- to perform that operation.
223 isPermissionError :: IOError -> Bool
224 isPermissionError = isPermissionErrorType . ioeGetErrorType
226 -- | A programmer-defined error value constructed using 'userError'.
227 isUserError :: IOError -> Bool
228 isUserError = isUserErrorType . ioeGetErrorType
231 -- -----------------------------------------------------------------------------
235 data IOErrorType = AlreadyExists | NoSuchThing | ResourceBusy
236 | ResourceExhausted | EOF | IllegalOperation
237 | PermissionDenied | UserError
240 -- | I\/O error where the operation failed because one of its arguments
242 alreadyExistsErrorType :: IOErrorType
243 alreadyExistsErrorType = AlreadyExists
245 -- | I\/O error where the operation failed because one of its arguments
247 doesNotExistErrorType :: IOErrorType
248 doesNotExistErrorType = NoSuchThing
250 -- | I\/O error where the operation failed because one of its arguments
251 -- is a single-use resource, which is already being used.
252 alreadyInUseErrorType :: IOErrorType
253 alreadyInUseErrorType = ResourceBusy
255 -- | I\/O error where the operation failed because the device is full.
256 fullErrorType :: IOErrorType
257 fullErrorType = ResourceExhausted
259 -- | I\/O error where the operation failed because the end of file has
261 eofErrorType :: IOErrorType
264 -- | I\/O error where the operation is not possible.
265 illegalOperationErrorType :: IOErrorType
266 illegalOperationErrorType = IllegalOperation
268 -- | I\/O error where the operation failed because the user does not
269 -- have sufficient operating system privilege to perform that operation.
270 permissionErrorType :: IOErrorType
271 permissionErrorType = PermissionDenied
273 -- | I\/O error that is programmer-defined.
274 userErrorType :: IOErrorType
275 userErrorType = UserError
277 -- -----------------------------------------------------------------------------
278 -- IOErrorType predicates
280 -- | I\/O error where the operation failed because one of its arguments
282 isAlreadyExistsErrorType :: IOErrorType -> Bool
283 isAlreadyExistsErrorType AlreadyExists = True
284 isAlreadyExistsErrorType _ = False
286 -- | I\/O error where the operation failed because one of its arguments
288 isDoesNotExistErrorType :: IOErrorType -> Bool
289 isDoesNotExistErrorType NoSuchThing = True
290 isDoesNotExistErrorType _ = False
292 -- | I\/O error where the operation failed because one of its arguments
293 -- is a single-use resource, which is already being used.
294 isAlreadyInUseErrorType :: IOErrorType -> Bool
295 isAlreadyInUseErrorType ResourceBusy = True
296 isAlreadyInUseErrorType _ = False
298 -- | I\/O error where the operation failed because the device is full.
299 isFullErrorType :: IOErrorType -> Bool
300 isFullErrorType ResourceExhausted = True
301 isFullErrorType _ = False
303 -- | I\/O error where the operation failed because the end of file has
305 isEOFErrorType :: IOErrorType -> Bool
306 isEOFErrorType EOF = True
307 isEOFErrorType _ = False
309 -- | I\/O error where the operation is not possible.
310 isIllegalOperationErrorType :: IOErrorType -> Bool
311 isIllegalOperationErrorType IllegalOperation = True
312 isIllegalOperationErrorType _ = False
314 -- | I\/O error where the operation failed because the user does not
315 -- have sufficient operating system privilege to perform that operation.
316 isPermissionErrorType :: IOErrorType -> Bool
317 isPermissionErrorType PermissionDenied = True
318 isPermissionErrorType _ = False
320 -- | I\/O error that is programmer-defined.
321 isUserErrorType :: IOErrorType -> Bool
322 isUserErrorType UserError = True
323 isUserErrorType _ = False
325 -- -----------------------------------------------------------------------------
328 #if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
329 ioeGetErrorType :: IOError -> IOErrorType
330 ioeGetErrorString :: IOError -> String
331 ioeGetLocation :: IOError -> String
332 ioeGetHandle :: IOError -> Maybe Handle
333 ioeGetFileName :: IOError -> Maybe FilePath
335 ioeGetErrorType ioe = ioe_type ioe
337 ioeGetErrorString ioe
338 | isUserErrorType (ioe_type ioe) = ioe_description ioe
339 | otherwise = show (ioe_type ioe)
341 ioeGetLocation ioe = ioe_location ioe
343 ioeGetHandle ioe = ioe_handle ioe
345 ioeGetFileName ioe = ioe_filename ioe
347 ioeSetErrorType :: IOError -> IOErrorType -> IOError
348 ioeSetErrorString :: IOError -> String -> IOError
349 ioeSetLocation :: IOError -> String -> IOError
350 ioeSetHandle :: IOError -> Handle -> IOError
351 ioeSetFileName :: IOError -> FilePath -> IOError
353 ioeSetErrorType ioe errtype = ioe{ ioe_type = errtype }
354 ioeSetErrorString ioe str = ioe{ ioe_description = str }
355 ioeSetLocation ioe str = ioe{ ioe_location = str }
356 ioeSetHandle ioe hdl = ioe{ ioe_handle = Just hdl }
357 ioeSetFileName ioe filename = ioe{ ioe_filename = Just filename }
359 #elif defined(__NHC__)
360 ioeGetErrorType :: IOError -> IOErrorType
361 ioeGetLocation :: IOError -> String
363 ioeGetErrorType e | isAlreadyExistsError e = AlreadyExists
364 | isDoesNotExistError e = NoSuchThing
365 | isAlreadyInUseError e = ResourceBusy
366 | isFullError e = ResourceExhausted
368 | isIllegalOperation e = IllegalOperation
369 | isPermissionError e = PermissionDenied
370 | isUserError e = UserError
372 ioeGetLocation (NHC.IOError _ _ _ _) = "unknown location"
373 ioeGetLocation (NHC.EOFError _ _ ) = "unknown location"
374 ioeGetLocation (NHC.PatternError loc) = loc
375 ioeGetLocation (NHC.UserError loc _) = loc
377 ioeSetErrorType :: IOError -> IOErrorType -> IOError
378 ioeSetErrorString :: IOError -> String -> IOError
379 ioeSetLocation :: IOError -> String -> IOError
380 ioeSetHandle :: IOError -> Handle -> IOError
381 ioeSetFileName :: IOError -> FilePath -> IOError
383 ioeSetErrorType e _ = e
384 ioeSetErrorString (NHC.IOError _ f h e) s = NHC.IOError s f h e
385 ioeSetErrorString (NHC.EOFError _ f) s = NHC.EOFError s f
386 ioeSetErrorString e@(NHC.PatternError _) _ = e
387 ioeSetErrorString (NHC.UserError l _) s = NHC.UserError l s
388 ioeSetLocation e@(NHC.IOError _ _ _ _) _ = e
389 ioeSetLocation e@(NHC.EOFError _ _) _ = e
390 ioeSetLocation (NHC.PatternError _) l = NHC.PatternError l
391 ioeSetLocation (NHC.UserError _ m) l = NHC.UserError l m
392 ioeSetHandle (NHC.IOError o f _ e) h = NHC.IOError o f (Just h) e
393 ioeSetHandle (NHC.EOFError o _) h = NHC.EOFError o h
394 ioeSetHandle e@(NHC.PatternError _) _ = e
395 ioeSetHandle e@(NHC.UserError _ _) _ = e
396 ioeSetFileName (NHC.IOError o _ h e) f = NHC.IOError o (Just f) h e
397 ioeSetFileName e _ = e
400 -- | Catch any 'IOError' that occurs in the computation and throw a
402 modifyIOError :: (IOError -> IOError) -> IO a -> IO a
403 modifyIOError f io = catch io (\e -> ioError (f e))
405 -- -----------------------------------------------------------------------------
406 -- annotating an IOError
408 -- | Adds a location description and maybe a file path and file handle
409 -- to an 'IOError'. If any of the file handle or file path is not given
410 -- the corresponding value in the 'IOError' remains unaltered.
411 annotateIOError :: IOError
417 #if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
418 annotateIOError ioe loc hdl path =
419 ioe{ ioe_handle = hdl `mplus` ioe_handle ioe,
420 ioe_location = loc, ioe_filename = path `mplus` ioe_filename ioe }
422 Nothing `mplus` ys = ys
424 #endif /* __GLASGOW_HASKELL__ || __HUGS__ */
427 annotateIOError (NHC.IOError msg file hdl code) msg' hdl' file' =
428 NHC.IOError (msg++'\n':msg') (file`mplus`file') (hdl`mplus`hdl') code
429 annotateIOError (NHC.EOFError msg hdl) msg' _ _ =
430 NHC.EOFError (msg++'\n':msg') hdl
431 annotateIOError (NHC.UserError loc msg) msg' _ _ =
432 NHC.UserError loc (msg++'\n':msg')
433 annotateIOError (NHC.PatternError loc) msg' _ _ =
434 NHC.PatternError (loc++'\n':msg')
438 -- | The 'catch' function establishes a handler that receives any 'IOError'
439 -- raised in the action protected by 'catch'. An 'IOError' is caught by
440 -- the most recent handler established by 'catch'. These handlers are
441 -- not selective: all 'IOError's are caught. Exception propagation
442 -- must be explicitly provided in a handler by re-raising any unwanted
443 -- exceptions. For example, in
445 -- > f = catch g (\e -> if IO.isEOFError e then return [] else ioError e)
447 -- the function @f@ returns @[]@ when an end-of-file exception
448 -- (cf. 'System.IO.Error.isEOFError') occurs in @g@; otherwise, the
449 -- exception is propagated to the next outer handler.
451 -- When an exception propagates outside the main program, the Haskell
452 -- system prints the associated 'IOError' value and exits the program.
454 -- Non-I\/O exceptions are not caught by this variant; to catch all
455 -- exceptions, use 'Control.Exception.catch' from "Control.Exception".
456 catch :: IO a -> (IOError -> IO a) -> IO a
458 #endif /* !__HUGS__ */