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