SafeHaskell: Added SafeHaskell to base
[ghc.git] / libraries / base / GHC / IO / Handle / FD.hs
1 {-# LANGUAGE Trustworthy #-}
2 {-# LANGUAGE CPP, NoImplicitPrelude, PatternGuards, ForeignFunctionInterface #-}
3
4 -----------------------------------------------------------------------------
5 -- |
6 -- Module : GHC.IO.Handle.FD
7 -- Copyright : (c) The University of Glasgow, 1994-2008
8 -- License : see libraries/base/LICENSE
9 --
10 -- Maintainer : libraries@haskell.org
11 -- Stability : internal
12 -- Portability : non-portable
13 --
14 -- Handle operations implemented by file descriptors (FDs)
15 --
16 -----------------------------------------------------------------------------
17
18 module GHC.IO.Handle.FD (
19 stdin, stdout, stderr,
20 openFile, openBinaryFile, openFileBlocking,
21 mkHandleFromFD, fdToHandle, fdToHandle',
22 isEOF
23 ) where
24
25 import GHC.Base
26 import GHC.Show
27 import Data.Maybe
28 import Foreign.C.Types
29 import GHC.MVar
30 import GHC.IO
31 import GHC.IO.Encoding
32 import GHC.IO.Device as IODevice
33 import GHC.IO.Exception
34 import GHC.IO.IOMode
35 import GHC.IO.Handle
36 import GHC.IO.Handle.Types
37 import GHC.IO.Handle.Internals
38 import qualified GHC.IO.FD as FD
39 import qualified System.Posix.Internals as Posix
40
41 -- ---------------------------------------------------------------------------
42 -- Standard Handles
43
44 -- Three handles are allocated during program initialisation. The first
45 -- two manage input or output from the Haskell program's standard input
46 -- or output channel respectively. The third manages output to the
47 -- standard error channel. These handles are initially open.
48
49 -- | A handle managing input from the Haskell program's standard input channel.
50 stdin :: Handle
51 {-# NOINLINE stdin #-}
52 stdin = unsafePerformIO $ do
53 -- ToDo: acquire lock
54 setBinaryMode FD.stdin
55 mkHandle FD.stdin "<stdin>" ReadHandle True (Just localeEncoding)
56 nativeNewlineMode{-translate newlines-}
57 (Just stdHandleFinalizer) Nothing
58
59 -- | A handle managing output to the Haskell program's standard output channel.
60 stdout :: Handle
61 {-# NOINLINE stdout #-}
62 stdout = unsafePerformIO $ do
63 -- ToDo: acquire lock
64 setBinaryMode FD.stdout
65 mkHandle FD.stdout "<stdout>" WriteHandle True (Just localeEncoding)
66 nativeNewlineMode{-translate newlines-}
67 (Just stdHandleFinalizer) Nothing
68
69 -- | A handle managing output to the Haskell program's standard error channel.
70 stderr :: Handle
71 {-# NOINLINE stderr #-}
72 stderr = unsafePerformIO $ do
73 -- ToDo: acquire lock
74 setBinaryMode FD.stderr
75 mkHandle FD.stderr "<stderr>" WriteHandle False{-stderr is unbuffered-}
76 (Just localeEncoding)
77 nativeNewlineMode{-translate newlines-}
78 (Just stdHandleFinalizer) Nothing
79
80 stdHandleFinalizer :: FilePath -> MVar Handle__ -> IO ()
81 stdHandleFinalizer fp m = do
82 h_ <- takeMVar m
83 flushWriteBuffer h_
84 case haType h_ of
85 ClosedHandle -> return ()
86 _other -> closeTextCodecs h_
87 putMVar m (ioe_finalizedHandle fp)
88
89 -- We have to put the FDs into binary mode on Windows to avoid the newline
90 -- translation that the CRT IO library does.
91 setBinaryMode :: FD.FD -> IO ()
92 #ifdef mingw32_HOST_OS
93 setBinaryMode fd = do _ <- setmode (fdFD fd) True
94 return ()
95 #else
96 setBinaryMode _ = return ()
97 #endif
98
99 #ifdef mingw32_HOST_OS
100 foreign import ccall unsafe "__hscore_setmode"
101 setmode :: CInt -> Bool -> IO CInt
102 #endif
103
104 -- ---------------------------------------------------------------------------
105 -- isEOF
106
107 -- | The computation 'isEOF' is identical to 'hIsEOF',
108 -- except that it works only on 'stdin'.
109
110 isEOF :: IO Bool
111 isEOF = hIsEOF stdin
112
113 -- ---------------------------------------------------------------------------
114 -- Opening and Closing Files
115
116 addFilePathToIOError :: String -> FilePath -> IOException -> IOException
117 addFilePathToIOError fun fp ioe
118 = ioe{ ioe_location = fun, ioe_filename = Just fp }
119
120 -- | Computation 'openFile' @file mode@ allocates and returns a new, open
121 -- handle to manage the file @file@. It manages input if @mode@
122 -- is 'ReadMode', output if @mode@ is 'WriteMode' or 'AppendMode',
123 -- and both input and output if mode is 'ReadWriteMode'.
124 --
125 -- If the file does not exist and it is opened for output, it should be
126 -- created as a new file. If @mode@ is 'WriteMode' and the file
127 -- already exists, then it should be truncated to zero length.
128 -- Some operating systems delete empty files, so there is no guarantee
129 -- that the file will exist following an 'openFile' with @mode@
130 -- 'WriteMode' unless it is subsequently written to successfully.
131 -- The handle is positioned at the end of the file if @mode@ is
132 -- 'AppendMode', and otherwise at the beginning (in which case its
133 -- internal position is 0).
134 -- The initial buffer mode is implementation-dependent.
135 --
136 -- This operation may fail with:
137 --
138 -- * 'isAlreadyInUseError' if the file is already open and cannot be reopened;
139 --
140 -- * 'isDoesNotExistError' if the file does not exist; or
141 --
142 -- * 'isPermissionError' if the user does not have permission to open the file.
143 --
144 -- Note: if you will be working with files containing binary data, you'll want to
145 -- be using 'openBinaryFile'.
146 openFile :: FilePath -> IOMode -> IO Handle
147 openFile fp im =
148 catchException
149 (openFile' fp im dEFAULT_OPEN_IN_BINARY_MODE True)
150 (\e -> ioError (addFilePathToIOError "openFile" fp e))
151
152 -- | Like 'openFile', but opens the file in ordinary blocking mode.
153 -- This can be useful for opening a FIFO for reading: if we open in
154 -- non-blocking mode then the open will fail if there are no writers,
155 -- whereas a blocking open will block until a writer appears.
156 openFileBlocking :: FilePath -> IOMode -> IO Handle
157 openFileBlocking fp im =
158 catchException
159 (openFile' fp im dEFAULT_OPEN_IN_BINARY_MODE False)
160 (\e -> ioError (addFilePathToIOError "openFile" fp e))
161
162 -- | Like 'openFile', but open the file in binary mode.
163 -- On Windows, reading a file in text mode (which is the default)
164 -- will translate CRLF to LF, and writing will translate LF to CRLF.
165 -- This is usually what you want with text files. With binary files
166 -- this is undesirable; also, as usual under Microsoft operating systems,
167 -- text mode treats control-Z as EOF. Binary mode turns off all special
168 -- treatment of end-of-line and end-of-file characters.
169 -- (See also 'hSetBinaryMode'.)
170
171 openBinaryFile :: FilePath -> IOMode -> IO Handle
172 openBinaryFile fp m =
173 catchException
174 (openFile' fp m True True)
175 (\e -> ioError (addFilePathToIOError "openBinaryFile" fp e))
176
177 openFile' :: String -> IOMode -> Bool -> Bool -> IO Handle
178 openFile' filepath iomode binary non_blocking = do
179 -- first open the file to get an FD
180 (fd, fd_type) <- FD.openFile filepath iomode non_blocking
181
182 let mb_codec = if binary then Nothing else Just localeEncoding
183
184 -- then use it to make a Handle
185 mkHandleFromFD fd fd_type filepath iomode
186 False {- do not *set* non-blocking mode -}
187 mb_codec
188 `onException` IODevice.close fd
189 -- NB. don't forget to close the FD if mkHandleFromFD fails, otherwise
190 -- this FD leaks.
191 -- ASSERT: if we just created the file, then fdToHandle' won't fail
192 -- (so we don't need to worry about removing the newly created file
193 -- in the event of an error).
194
195
196 -- ---------------------------------------------------------------------------
197 -- Converting file descriptors to Handles
198
199 mkHandleFromFD
200 :: FD.FD
201 -> IODeviceType
202 -> FilePath -- a string describing this file descriptor (e.g. the filename)
203 -> IOMode
204 -> Bool -- *set* non-blocking mode on the FD
205 -> Maybe TextEncoding
206 -> IO Handle
207
208 mkHandleFromFD fd0 fd_type filepath iomode set_non_blocking mb_codec
209 = do
210 #ifndef mingw32_HOST_OS
211 -- turn on non-blocking mode
212 fd <- if set_non_blocking
213 then FD.setNonBlockingMode fd0 True
214 else return fd0
215 #else
216 let _ = set_non_blocking -- warning suppression
217 fd <- return fd0
218 #endif
219
220 let nl | isJust mb_codec = nativeNewlineMode
221 | otherwise = noNewlineTranslation
222
223 case fd_type of
224 Directory ->
225 ioException (IOError Nothing InappropriateType "openFile"
226 "is a directory" Nothing Nothing)
227
228 Stream
229 -- only *Streams* can be DuplexHandles. Other read/write
230 -- Handles must share a buffer.
231 | ReadWriteMode <- iomode ->
232 mkDuplexHandle fd filepath mb_codec nl
233
234
235 _other ->
236 mkFileHandle fd filepath iomode mb_codec nl
237
238 -- | Old API kept to avoid breaking clients
239 fdToHandle' :: CInt
240 -> Maybe IODeviceType
241 -> Bool -- is_socket on Win, non-blocking on Unix
242 -> FilePath
243 -> IOMode
244 -> Bool -- binary
245 -> IO Handle
246 fdToHandle' fdint mb_type is_socket filepath iomode binary = do
247 let mb_stat = case mb_type of
248 Nothing -> Nothing
249 -- mkFD will do the stat:
250 Just RegularFile -> Nothing
251 -- no stat required for streams etc.:
252 Just other -> Just (other,0,0)
253 (fd,fd_type) <- FD.mkFD fdint iomode mb_stat
254 is_socket
255 is_socket
256 mkHandleFromFD fd fd_type filepath iomode is_socket
257 (if binary then Nothing else Just localeEncoding)
258
259
260 -- | Turn an existing file descriptor into a Handle. This is used by
261 -- various external libraries to make Handles.
262 --
263 -- Makes a binary Handle. This is for historical reasons; it should
264 -- probably be a text Handle with the default encoding and newline
265 -- translation instead.
266 fdToHandle :: Posix.FD -> IO Handle
267 fdToHandle fdint = do
268 iomode <- Posix.fdGetMode fdint
269 (fd,fd_type) <- FD.mkFD fdint iomode Nothing
270 False{-is_socket-}
271 -- NB. the is_socket flag is False, meaning that:
272 -- on Windows we're guessing this is not a socket (XXX)
273 False{-is_nonblock-}
274 -- file descriptors that we get from external sources are
275 -- not put into non-blocking mode, becuase that would affect
276 -- other users of the file descriptor
277 let fd_str = "<file descriptor: " ++ show fd ++ ">"
278 mkHandleFromFD fd fd_type fd_str iomode False{-non-block-}
279 Nothing -- bin mode
280
281 -- ---------------------------------------------------------------------------
282 -- Are files opened by default in text or binary mode, if the user doesn't
283 -- specify?
284
285 dEFAULT_OPEN_IN_BINARY_MODE :: Bool
286 dEFAULT_OPEN_IN_BINARY_MODE = False