Wolfgang Jeltsch pushed to branch wip/jeltsch/system-io-implementation-into-base at Glasgow Haskell Compiler / GHC
Commits:
-
fca4d682
by Wolfgang Jeltsch at 2026-03-13T16:56:51+02:00
9 changed files:
- libraries/base/src/GHC/IO/Handle.hs
- libraries/base/src/Prelude.hs
- libraries/base/src/System/IO.hs
- libraries/base/src/Text/Printf.hs
- libraries/ghc-internal/src/GHC/Internal/System/IO.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/base-exports.stdout-ws-32
Changes:
| ... | ... | @@ -53,6 +53,7 @@ module GHC.IO.Handle |
| 53 | 53 | hGetEcho,
|
| 54 | 54 | hIsTerminalDevice,
|
| 55 | 55 | hSetNewlineMode,
|
| 56 | + hGetNewlineMode,
|
|
| 56 | 57 | Newline(..),
|
| 57 | 58 | NewlineMode(..),
|
| 58 | 59 | nativeNewline,
|
| ... | ... | @@ -165,7 +165,7 @@ module Prelude ( |
| 165 | 165 | ) where
|
| 166 | 166 | |
| 167 | 167 | import GHC.Internal.Control.Monad
|
| 168 | -import GHC.Internal.System.IO
|
|
| 168 | +import System.IO
|
|
| 169 | 169 | import GHC.Internal.System.IO.Error
|
| 170 | 170 | import qualified GHC.Internal.Data.List as List
|
| 171 | 171 | import GHC.Internal.Data.Either
|
| 1 | -{-# LANGUAGE Safe #-}
|
|
| 1 | +{-# LANGUAGE Trustworthy #-}
|
|
| 2 | +{-# LANGUAGE CPP #-}
|
|
| 2 | 3 | |
| 3 | 4 | -- |
|
| 4 | 5 | --
|
| ... | ... | @@ -184,8 +185,666 @@ module System.IO |
| 184 | 185 | nativeNewlineMode
|
| 185 | 186 | ) where
|
| 186 | 187 | |
| 187 | -import GHC.Internal.System.IO
|
|
| 188 | +import GHC.Internal.System.IO (putStrLn, print)
|
|
| 189 | + |
|
| 190 | +import GHC.Base (Bool (False, True), otherwise, failIO)
|
|
| 191 | +import GHC.Err (errorWithoutStackTrace)
|
|
| 192 | +import GHC.List (null, elem, last, (++), reverse, break)
|
|
| 193 | +import GHC.Num ((+))
|
|
| 194 | +import GHC.IO (IO, FilePath)
|
|
| 195 | +import GHC.IO.IOMode (IOMode (ReadMode, WriteMode, ReadWriteMode, AppendMode))
|
|
| 196 | +import qualified GHC.Internal.IO.FD as FD
|
|
| 197 | +import GHC.IO.Encoding
|
|
| 198 | + (
|
|
| 199 | + TextEncoding,
|
|
| 200 | + mkTextEncoding,
|
|
| 201 | + getLocaleEncoding,
|
|
| 202 | + initLocaleEncoding,
|
|
| 203 | + utf8,
|
|
| 204 | + utf8_bom,
|
|
| 205 | + utf16,
|
|
| 206 | + utf16be,
|
|
| 207 | + utf16le,
|
|
| 208 | + utf32,
|
|
| 209 | + utf32be,
|
|
| 210 | + utf32le,
|
|
| 211 | + latin1,
|
|
| 212 | + char8
|
|
| 213 | + )
|
|
| 214 | +import GHC.IO.Handle
|
|
| 215 | + (
|
|
| 216 | + Handle,
|
|
| 217 | + hLookAhead,
|
|
| 218 | + hFlush,
|
|
| 219 | + hClose,
|
|
| 220 | + hSetBinaryMode,
|
|
| 221 | + hSetEncoding,
|
|
| 222 | + hSetNewlineMode,
|
|
| 223 | + hSetEcho,
|
|
| 224 | + hSetFileSize,
|
|
| 225 | + hGetEncoding,
|
|
| 226 | + hGetNewlineMode,
|
|
| 227 | + hGetEcho,
|
|
| 228 | + hFileSize,
|
|
| 229 | + hIsOpen,
|
|
| 230 | + hIsReadable,
|
|
| 231 | + hIsSeekable,
|
|
| 232 | + hIsWritable,
|
|
| 233 | + hIsTerminalDevice,
|
|
| 234 | + hIsEOF,
|
|
| 235 | + hIsClosed,
|
|
| 236 | + hShow,
|
|
| 237 | + BufferMode (NoBuffering, LineBuffering, BlockBuffering),
|
|
| 238 | + hSetBuffering,
|
|
| 239 | + hGetBuffering,
|
|
| 240 | + HandlePosn,
|
|
| 241 | + hSetPosn,
|
|
| 242 | + hGetPosn,
|
|
| 243 | + SeekMode (AbsoluteSeek, RelativeSeek, SeekFromEnd),
|
|
| 244 | + hSeek,
|
|
| 245 | + hTell,
|
|
| 246 | + Newline (LF, CRLF),
|
|
| 247 | + nativeNewline,
|
|
| 248 | + NewlineMode (NewlineMode, inputNL, outputNL),
|
|
| 249 | + noNewlineTranslation,
|
|
| 250 | + nativeNewlineMode,
|
|
| 251 | + universalNewlineMode,
|
|
| 252 | + isEOF
|
|
| 253 | + )
|
|
| 254 | +import GHC.IO.Handle.Text
|
|
| 255 | + (
|
|
| 256 | + hPutChar,
|
|
| 257 | + hPutStr,
|
|
| 258 | + hPutStrLn,
|
|
| 259 | + hPutBuf,
|
|
| 260 | + hPutBufNonBlocking,
|
|
| 261 | + hGetChar,
|
|
| 262 | + hGetContents,
|
|
| 263 | + hGetContents',
|
|
| 264 | + hGetLine,
|
|
| 265 | + hGetBuf,
|
|
| 266 | + hGetBufNonBlocking,
|
|
| 267 | + hGetBufSome,
|
|
| 268 | + hWaitForInput
|
|
| 269 | + )
|
|
| 270 | +import qualified GHC.Internal.IO.Handle.FD as POSIX
|
|
| 271 | +import GHC.IO.StdHandles
|
|
| 272 | + (
|
|
| 273 | + openBinaryFile,
|
|
| 274 | + withBinaryFile,
|
|
| 275 | + openFile,
|
|
| 276 | + withFile,
|
|
| 277 | + stdin,
|
|
| 278 | + stdout,
|
|
| 279 | + stderr
|
|
| 280 | + )
|
|
| 281 | +import GHC.IORef (atomicModifyIORef'_)
|
|
| 188 | 282 | import GHC.Internal.Control.Monad.Fix (fixIO)
|
| 283 | +import Control.Monad (return, (>>=))
|
|
| 284 | +import Control.Exception (ioError)
|
|
| 285 | +import Data.Eq ((==))
|
|
| 286 | +import Data.Ord ((<))
|
|
| 287 | +import Data.Bits ((.|.))
|
|
| 288 | +import Data.Function (($), (.))
|
|
| 289 | +import Data.Maybe (Maybe (Nothing, Just))
|
|
| 290 | +import Data.Char (Char)
|
|
| 291 | +import Data.String (String)
|
|
| 292 | +import Data.Int (Int)
|
|
| 293 | +import Data.IORef (IORef, newIORef)
|
|
| 294 | +import System.IO.Error (userError)
|
|
| 295 | +import System.IO.Unsafe (unsafePerformIO)
|
|
| 296 | +import System.Posix.Internals
|
|
| 297 | + (
|
|
| 298 | + c_getpid,
|
|
| 299 | + c_open,
|
|
| 300 | + o_CREAT,
|
|
| 301 | + o_EXCL,
|
|
| 302 | + o_BINARY,
|
|
| 303 | + o_NONBLOCK,
|
|
| 304 | + o_RDWR,
|
|
| 305 | + o_NOCTTY,
|
|
| 306 | + withFilePath
|
|
| 307 | + )
|
|
| 308 | +import System.Posix.Types (CMode)
|
|
| 309 | +import Text.Read (lex, Read, reads)
|
|
| 310 | +import Text.Show (Show, show)
|
|
| 311 | +import Foreign.C.Types (CInt)
|
|
| 312 | +import Foreign.C.Error (Errno, eEXIST, getErrno, errnoToIOError)
|
|
| 313 | + |
|
| 314 | +#if defined(mingw32_HOST_OS)
|
|
| 315 | +import GHC.IO.SubSystem
|
|
| 316 | +import GHC.IO.Windows.Handle (openFileAsTemp)
|
|
| 317 | +import GHC.IO.Handle.Windows (mkHandleFromHANDLE)
|
|
| 318 | +import GHC.IO.Device as IODevice
|
|
| 319 | +import GHC.Internal.Real (fromIntegral)
|
|
| 320 | +import Foreign.C.String
|
|
| 321 | +import Foreign.Ptr
|
|
| 322 | +import Foreign.Marshal.Alloc
|
|
| 323 | +import Foreign.Marshal.Utils (with)
|
|
| 324 | +import Foreign.Storable
|
|
| 325 | +#endif
|
|
| 326 | + |
|
| 327 | +-----------------------------------------------------------------------------
|
|
| 328 | +-- Standard IO
|
|
| 329 | + |
|
| 330 | +-- | Write a character to the standard output device
|
|
| 331 | +--
|
|
| 332 | +-- 'putChar' is implemented as @'hPutChar' 'stdout'@.
|
|
| 333 | +--
|
|
| 334 | +-- This operation may fail with the same errors as 'hPutChar'.
|
|
| 335 | +--
|
|
| 336 | +-- ==== __Examples__
|
|
| 337 | +--
|
|
| 338 | +-- Note that the following do not put a newline.
|
|
| 339 | +--
|
|
| 340 | +-- >>> putChar 'x'
|
|
| 341 | +-- x
|
|
| 342 | +--
|
|
| 343 | +-- >>> putChar '\0042'
|
|
| 344 | +-- *
|
|
| 345 | +putChar :: Char -> IO ()
|
|
| 346 | +putChar c = hPutChar stdout c
|
|
| 347 | + |
|
| 348 | +-- | Write a string to the standard output device
|
|
| 349 | +--
|
|
| 350 | +-- 'putStr' is implemented as @'hPutStr' 'stdout'@.
|
|
| 351 | +--
|
|
| 352 | +-- This operation may fail with the same errors, and has the same issues with concurrency, as 'hPutStr'!
|
|
| 353 | +--
|
|
| 354 | +-- ==== __Examples__
|
|
| 355 | +--
|
|
| 356 | +-- Note that the following do not put a newline.
|
|
| 357 | +--
|
|
| 358 | +-- >>> putStr "Hello, World!"
|
|
| 359 | +-- Hello, World!
|
|
| 360 | +--
|
|
| 361 | +-- >>> putStr "\0052\0042\0050"
|
|
| 362 | +-- 4*2
|
|
| 363 | +--
|
|
| 364 | +putStr :: String -> IO ()
|
|
| 365 | +putStr s = hPutStr stdout s
|
|
| 366 | + |
|
| 367 | +-- | Read a single character from the standard input device.
|
|
| 368 | +--
|
|
| 369 | +-- 'getChar' is implemented as @'hGetChar' 'stdin'@.
|
|
| 370 | +--
|
|
| 371 | +-- This operation may fail with the same errors as 'hGetChar'.
|
|
| 372 | +--
|
|
| 373 | +-- ==== __Examples__
|
|
| 374 | +--
|
|
| 375 | +-- >>> getChar
|
|
| 376 | +-- a'a'
|
|
| 377 | +--
|
|
| 378 | +-- >>> getChar
|
|
| 379 | +-- >
|
|
| 380 | +-- '\n'
|
|
| 381 | +getChar :: IO Char
|
|
| 382 | +getChar = hGetChar stdin
|
|
| 383 | + |
|
| 384 | +-- | Read a line from the standard input device.
|
|
| 385 | +--
|
|
| 386 | +-- 'getLine' is implemented as @'hGetLine' 'stdin'@.
|
|
| 387 | +--
|
|
| 388 | +-- This operation may fail with the same errors as 'hGetLine'.
|
|
| 389 | +--
|
|
| 390 | +-- ==== __Examples__
|
|
| 391 | +--
|
|
| 392 | +-- >>> getLine
|
|
| 393 | +-- > Hello World!
|
|
| 394 | +-- "Hello World!"
|
|
| 395 | +--
|
|
| 396 | +-- >>> getLine
|
|
| 397 | +-- >
|
|
| 398 | +-- ""
|
|
| 399 | +getLine :: IO String
|
|
| 400 | +getLine = hGetLine stdin
|
|
| 401 | + |
|
| 402 | +-- | The 'getContents' operation returns all user input as a single string,
|
|
| 403 | +-- which is read lazily as it is needed.
|
|
| 404 | +--
|
|
| 405 | +-- 'getContents' is implemented as @'hGetContents' 'stdin'@.
|
|
| 406 | +--
|
|
| 407 | +-- This operation may fail with the same errors as 'hGetContents'.
|
|
| 408 | +--
|
|
| 409 | +-- ==== __Examples__
|
|
| 410 | +--
|
|
| 411 | +-- >>> getContents >>= putStr
|
|
| 412 | +-- > aaabbbccc :D
|
|
| 413 | +-- aaabbbccc :D
|
|
| 414 | +-- > I hope you have a great day
|
|
| 415 | +-- I hope you have a great day
|
|
| 416 | +-- > ^D
|
|
| 417 | +--
|
|
| 418 | +-- >>> getContents >>= print . length
|
|
| 419 | +-- > abc
|
|
| 420 | +-- > <3
|
|
| 421 | +-- > def ^D
|
|
| 422 | +-- 11
|
|
| 423 | +getContents :: IO String
|
|
| 424 | +getContents = hGetContents stdin
|
|
| 425 | + |
|
| 426 | +-- | The 'getContents'' operation returns all user input as a single string,
|
|
| 427 | +-- which is fully read before being returned
|
|
| 428 | +--
|
|
| 429 | +-- 'getContents'' is implemented as @'hGetContents'' 'stdin'@.
|
|
| 430 | +--
|
|
| 431 | +-- This operation may fail with the same errors as 'hGetContents''.
|
|
| 432 | +--
|
|
| 433 | +-- ==== __Examples__
|
|
| 434 | +--
|
|
| 435 | +-- >>> getContents' >>= putStr
|
|
| 436 | +-- > aaabbbccc :D
|
|
| 437 | +-- > I hope you have a great day
|
|
| 438 | +-- aaabbbccc :D
|
|
| 439 | +-- I hope you have a great day
|
|
| 440 | +--
|
|
| 441 | +-- >>> getContents' >>= print . length
|
|
| 442 | +-- > abc
|
|
| 443 | +-- > <3
|
|
| 444 | +-- > def ^D
|
|
| 445 | +-- 11
|
|
| 446 | +--
|
|
| 447 | +-- @since base-4.15.0.0
|
|
| 448 | +getContents' :: IO String
|
|
| 449 | +getContents' = hGetContents' stdin
|
|
| 450 | + |
|
| 451 | +-- | @'interact' f@ takes the entire input from 'stdin' and applies @f@ to it.
|
|
| 452 | +-- The resulting string is written to the 'stdout' device.
|
|
| 453 | +--
|
|
| 454 | +-- Note that this operation is lazy, which allows to produce output
|
|
| 455 | +-- even before all input has been consumed.
|
|
| 456 | +--
|
|
| 457 | +-- This operation may fail with the same errors as 'getContents' and 'putStr'.
|
|
| 458 | +--
|
|
| 459 | +-- If it doesn't produce output the buffering settings may not be
|
|
| 460 | +-- correct, use ^D (ctrl+D) to close stdin which forces
|
|
| 461 | +-- the buffer to be consumed.
|
|
| 462 | +--
|
|
| 463 | +-- You may wish to set the buffering style appropriate to your program's
|
|
| 464 | +-- needs before using this function, for example:
|
|
| 465 | +--
|
|
| 466 | +-- @
|
|
| 467 | +-- main :: IO ()
|
|
| 468 | +-- main = do
|
|
| 469 | +-- hSetBuffering stdin LineBuffering
|
|
| 470 | +-- hSetBuffering stdout NoBuffering
|
|
| 471 | +-- interact (concatMap (\str -> str ++ str) . L.lines)
|
|
| 472 | +-- @
|
|
| 473 | +--
|
|
| 474 | +-- ==== __Examples__
|
|
| 475 | +--
|
|
| 476 | +-- >>> interact (\str -> str ++ str)
|
|
| 477 | +-- > hi :)
|
|
| 478 | +-- hi :)
|
|
| 479 | +-- > ^D
|
|
| 480 | +-- hi :)
|
|
| 481 | +--
|
|
| 482 | +-- >>> interact (const ":D")
|
|
| 483 | +-- :D
|
|
| 484 | +--
|
|
| 485 | +-- >>> interact (show . words)
|
|
| 486 | +-- > hello world!
|
|
| 487 | +-- > I hope you have a great day
|
|
| 488 | +-- > ^D
|
|
| 489 | +-- ["hello","world!","I","hope","you","have","a","great","day"]
|
|
| 490 | +interact :: (String -> String) -> IO ()
|
|
| 491 | +interact f = do s <- getContents
|
|
| 492 | + putStr (f s)
|
|
| 493 | + |
|
| 494 | +-- | The 'readFile' function reads a file and
|
|
| 495 | +-- returns the contents of the file as a string.
|
|
| 496 | +--
|
|
| 497 | +-- The file is read lazily, on demand, as with 'getContents'.
|
|
| 498 | +--
|
|
| 499 | +-- This operation may fail with the same errors as 'hGetContents' and 'openFile'.
|
|
| 500 | +--
|
|
| 501 | +-- ==== __Examples__
|
|
| 502 | +--
|
|
| 503 | +-- >>> readFile "~/hello_world"
|
|
| 504 | +-- "Greetings!"
|
|
| 505 | +--
|
|
| 506 | +-- >>> take 5 <$> readFile "/dev/zero"
|
|
| 507 | +-- "\NUL\NUL\NUL\NUL\NUL"
|
|
| 508 | +readFile :: FilePath -> IO String
|
|
| 509 | +readFile name = openFile name ReadMode >>= hGetContents
|
|
| 510 | + |
|
| 511 | +-- | The 'readFile'' function reads a file and
|
|
| 512 | +-- returns the contents of the file as a string.
|
|
| 513 | +--
|
|
| 514 | +-- This is identical to 'readFile', but the file is fully read before being returned,
|
|
| 515 | +-- as with 'getContents''.
|
|
| 516 | +--
|
|
| 517 | +-- @since base-4.15.0.0
|
|
| 518 | +readFile' :: FilePath -> IO String
|
|
| 519 | +-- There's a bit of overkill here—both withFile and
|
|
| 520 | +-- hGetContents' will close the file in the end.
|
|
| 521 | +readFile' name = withFile name ReadMode hGetContents'
|
|
| 522 | + |
|
| 523 | +-- | The computation @'writeFile' file str@ function writes the string @str@,
|
|
| 524 | +-- to the file @file@.
|
|
| 525 | +--
|
|
| 526 | +-- This operation may fail with the same errors as 'hPutStr' and 'withFile'.
|
|
| 527 | +--
|
|
| 528 | +-- ==== __Examples__
|
|
| 529 | +--
|
|
| 530 | +-- >>> writeFile "hello" "world" >> readFile "hello"
|
|
| 531 | +-- "world"
|
|
| 532 | +--
|
|
| 533 | +-- >>> writeFile "~/" "D:"
|
|
| 534 | +-- *** Exception: ~/: withFile: inappropriate type (Is a directory)
|
|
| 535 | +writeFile :: FilePath -> String -> IO ()
|
|
| 536 | +writeFile f txt = withFile f WriteMode (\ hdl -> hPutStr hdl txt)
|
|
| 537 | + |
|
| 538 | +-- | The computation @'appendFile' file str@ function appends the string @str@,
|
|
| 539 | +-- to the file @file@.
|
|
| 540 | +--
|
|
| 541 | +-- Note that 'writeFile' and 'appendFile' write a literal string
|
|
| 542 | +-- to a file. To write a value of any printable type, as with 'print',
|
|
| 543 | +-- use the 'show' function to convert the value to a string first.
|
|
| 544 | +--
|
|
| 545 | +-- This operation may fail with the same errors as 'hPutStr' and 'withFile'.
|
|
| 546 | +--
|
|
| 547 | +-- ==== __Examples__
|
|
| 548 | +--
|
|
| 549 | +-- The following example could be more efficently written by acquiring a handle
|
|
| 550 | +-- instead with 'openFile' and using the computations capable of writing to handles
|
|
| 551 | +-- such as 'hPutStr'.
|
|
| 552 | +--
|
|
| 553 | +-- >>> let fn = "hello_world"
|
|
| 554 | +-- >>> in writeFile fn "hello" >> appendFile fn " world!" >> (readFile fn >>= putStrLn)
|
|
| 555 | +-- "hello world!"
|
|
| 556 | +--
|
|
| 557 | +-- >>> let fn = "foo"; output = readFile' fn >>= putStrLn
|
|
| 558 | +-- >>> in output >> appendFile fn (show [1,2,3]) >> output
|
|
| 559 | +-- this is what's in the file
|
|
| 560 | +-- this is what's in the file[1,2,3]
|
|
| 561 | +appendFile :: FilePath -> String -> IO ()
|
|
| 562 | +appendFile f txt = withFile f AppendMode (\ hdl -> hPutStr hdl txt)
|
|
| 563 | + |
|
| 564 | +-- | The 'readLn' function combines 'getLine' and 'readIO'.
|
|
| 565 | +--
|
|
| 566 | +-- This operation may fail with the same errors as 'getLine' and 'readIO'.
|
|
| 567 | +--
|
|
| 568 | +-- ==== __Examples__
|
|
| 569 | +--
|
|
| 570 | +-- >>> fmap (+ 5) readLn
|
|
| 571 | +-- > 25
|
|
| 572 | +-- 30
|
|
| 573 | +--
|
|
| 574 | +-- >>> readLn :: IO String
|
|
| 575 | +-- > this is not a string literal
|
|
| 576 | +-- *** Exception: user error (Prelude.readIO: no parse)
|
|
| 577 | +readLn :: Read a => IO a
|
|
| 578 | +readLn = getLine >>= readIO
|
|
| 579 | + |
|
| 580 | +-- | The 'readIO' function is similar to 'read' except that it signals
|
|
| 581 | +-- parse failure to the 'IO' monad instead of terminating the program.
|
|
| 582 | +--
|
|
| 583 | +-- This operation may fail with:
|
|
| 584 | +--
|
|
| 585 | +-- * 'GHC.Internal.System.IO.Error.isUserError' if there is no unambiguous parse.
|
|
| 586 | +--
|
|
| 587 | +-- ==== __Examples__
|
|
| 588 | +--
|
|
| 589 | +-- >>> fmap (+ 1) (readIO "1")
|
|
| 590 | +-- 2
|
|
| 591 | +--
|
|
| 592 | +-- >>> readIO "not quite ()" :: IO ()
|
|
| 593 | +-- *** Exception: user error (Prelude.readIO: no parse)
|
|
| 594 | +readIO :: Read a => String -> IO a
|
|
| 595 | +readIO s = case (do { (x,t) <- reads s ;
|
|
| 596 | + ("","") <- lex t ;
|
|
| 597 | + return x }) of
|
|
| 598 | + [x] -> return x
|
|
| 599 | + [] -> ioError (userError "Prelude.readIO: no parse")
|
|
| 600 | + _ -> ioError (userError "Prelude.readIO: ambiguous parse")
|
|
| 601 | + |
|
| 602 | +-- | The encoding of the current locale.
|
|
| 603 | +--
|
|
| 604 | +-- This is the initial locale encoding: if it has been subsequently changed by
|
|
| 605 | +-- 'GHC.Internal.IO.Encoding.setLocaleEncoding' this value will not reflect that change.
|
|
| 606 | +localeEncoding :: TextEncoding
|
|
| 607 | +localeEncoding = initLocaleEncoding
|
|
| 608 | + |
|
| 609 | +-- | Computation 'hReady' @hdl@ indicates whether at least one item is
|
|
| 610 | +-- available for input from handle @hdl@.
|
|
| 611 | +--
|
|
| 612 | +-- This operation may fail with:
|
|
| 613 | +--
|
|
| 614 | +-- * 'GHC.Internal.System.IO.Error.isEOFError' if the end of file has been reached.
|
|
| 615 | +hReady :: Handle -> IO Bool
|
|
| 616 | +hReady h = hWaitForInput h 0
|
|
| 617 | + |
|
| 618 | +-- | Computation 'hPrint' @hdl t@ writes the string representation of @t@
|
|
| 619 | +-- given by the 'show' function to the file or channel managed by @hdl@
|
|
| 620 | +-- and appends a newline.
|
|
| 621 | +--
|
|
| 622 | +-- This operation may fail with the same errors as 'hPutStrLn'
|
|
| 623 | +--
|
|
| 624 | +-- ==== __Examples__
|
|
| 625 | +--
|
|
| 626 | +-- >>> hPrint stdout [1,2,3]
|
|
| 627 | +-- [1,2,3]
|
|
| 628 | +--
|
|
| 629 | +-- >>> hPrint stdin [4,5,6]
|
|
| 630 | +-- *** Exception: <stdin>: hPutStr: illegal operation (handle is not open for writing)
|
|
| 631 | +hPrint :: Show a => Handle -> a -> IO ()
|
|
| 632 | +hPrint hdl = hPutStrLn hdl . show
|
|
| 633 | + |
|
| 634 | +-- | The function creates a temporary file in ReadWrite mode.
|
|
| 635 | +-- The created file isn\'t deleted automatically, so you need to delete it manually.
|
|
| 636 | +--
|
|
| 637 | +-- The file is created with permissions such that only the current
|
|
| 638 | +-- user can read\/write it.
|
|
| 639 | +--
|
|
| 640 | +-- With some exceptions (see below), the file will be created securely
|
|
| 641 | +-- in the sense that an attacker should not be able to cause
|
|
| 642 | +-- openTempFile to overwrite another file on the filesystem using your
|
|
| 643 | +-- credentials, by putting symbolic links (on Unix) in the place where
|
|
| 644 | +-- the temporary file is to be created. On Unix the @O_CREAT@ and
|
|
| 645 | +-- @O_EXCL@ flags are used to prevent this attack, but note that
|
|
| 646 | +-- @O_EXCL@ is sometimes not supported on NFS filesystems, so if you
|
|
| 647 | +-- rely on this behaviour it is best to use local filesystems only.
|
|
| 648 | +openTempFile :: FilePath -- ^ Directory in which to create the file
|
|
| 649 | + -> String -- ^ File name template. If the template is \"foo.ext\" then
|
|
| 650 | + -- the created file will be \"fooXXX.ext\" where XXX is some
|
|
| 651 | + -- random number. Note that this should not contain any path
|
|
| 652 | + -- separator characters. On Windows, the template prefix may
|
|
| 653 | + -- be truncated to 3 chars, e.g. \"foobar.ext\" will be
|
|
| 654 | + -- \"fooXXX.ext\".
|
|
| 655 | + -> IO (FilePath, Handle)
|
|
| 656 | +openTempFile tmp_dir template
|
|
| 657 | + = openTempFile' "openTempFile" tmp_dir template False 0o600
|
|
| 658 | + |
|
| 659 | +-- | Like 'openTempFile', but opens the file in binary mode. See 'openBinaryFile' for more comments.
|
|
| 660 | +openBinaryTempFile :: FilePath -> String -> IO (FilePath, Handle)
|
|
| 661 | +openBinaryTempFile tmp_dir template
|
|
| 662 | + = openTempFile' "openBinaryTempFile" tmp_dir template True 0o600
|
|
| 663 | + |
|
| 664 | +-- | Like 'openTempFile', but uses the default file permissions
|
|
| 665 | +openTempFileWithDefaultPermissions :: FilePath -> String
|
|
| 666 | + -> IO (FilePath, Handle)
|
|
| 667 | +openTempFileWithDefaultPermissions tmp_dir template
|
|
| 668 | + = openTempFile' "openTempFileWithDefaultPermissions" tmp_dir template False 0o666
|
|
| 669 | + |
|
| 670 | +-- | Like 'openBinaryTempFile', but uses the default file permissions
|
|
| 671 | +openBinaryTempFileWithDefaultPermissions :: FilePath -> String
|
|
| 672 | + -> IO (FilePath, Handle)
|
|
| 673 | +openBinaryTempFileWithDefaultPermissions tmp_dir template
|
|
| 674 | + = openTempFile' "openBinaryTempFileWithDefaultPermissions" tmp_dir template True 0o666
|
|
| 675 | + |
|
| 676 | +openTempFile' :: String -> FilePath -> String -> Bool -> CMode
|
|
| 677 | + -> IO (FilePath, Handle)
|
|
| 678 | +openTempFile' loc tmp_dir template binary mode
|
|
| 679 | + | pathSeparator template
|
|
| 680 | + = failIO $ "openTempFile': Template string must not contain path separator characters: "++template
|
|
| 681 | + | otherwise = findTempName
|
|
| 682 | + where
|
|
| 683 | + -- We split off the last extension, so we can use .foo.ext files
|
|
| 684 | + -- for temporary files (hidden on Unix OSes). Unfortunately we're
|
|
| 685 | + -- below filepath in the hierarchy here.
|
|
| 686 | + (prefix, suffix) =
|
|
| 687 | + case break (== '.') $ reverse template of
|
|
| 688 | + -- First case: template contains no '.'s. Just re-reverse it.
|
|
| 689 | + (rev_suffix, "") -> (reverse rev_suffix, "")
|
|
| 690 | + -- Second case: template contains at least one '.'. Strip the
|
|
| 691 | + -- dot from the prefix and prepend it to the suffix (if we don't
|
|
| 692 | + -- do this, the unique number will get added after the '.' and
|
|
| 693 | + -- thus be part of the extension, which is wrong.)
|
|
| 694 | + (rev_suffix, '.':rest) -> (reverse rest, '.':reverse rev_suffix)
|
|
| 695 | + -- Otherwise, something is wrong, because (break (== '.')) should
|
|
| 696 | + -- always return a pair with either the empty string or a string
|
|
| 697 | + -- beginning with '.' as the second component.
|
|
| 698 | + _ -> errorWithoutStackTrace "bug in GHC.Internal.System.IO.openTempFile"
|
|
| 699 | +#if defined(mingw32_HOST_OS)
|
|
| 700 | + findTempName = findTempNamePosix <!> findTempNameWinIO
|
|
| 701 | + |
|
| 702 | + findTempNameWinIO = do
|
|
| 703 | + let label = if null prefix then "ghc" else prefix
|
|
| 704 | + withCWString tmp_dir $ \c_tmp_dir ->
|
|
| 705 | + withCWString label $ \c_template ->
|
|
| 706 | + withCWString suffix $ \c_suffix ->
|
|
| 707 | + with nullPtr $ \c_ptr -> do
|
|
| 708 | + res <- c_createUUIDTempFileErrNo c_tmp_dir c_template c_suffix c_ptr
|
|
| 709 | + if not res
|
|
| 710 | + then do errno <- getErrno
|
|
| 711 | + ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 712 | + else do c_p <- peek c_ptr
|
|
| 713 | + filename <- peekCWString c_p
|
|
| 714 | + free c_p
|
|
| 715 | + let flags = fromIntegral mode .&. o_EXCL
|
|
| 716 | + handleResultsWinIO filename (flags == o_EXCL)
|
|
| 717 | + |
|
| 718 | + findTempNamePosix = do
|
|
| 719 | + let label = if null prefix then "ghc" else prefix
|
|
| 720 | + withCWString tmp_dir $ \c_tmp_dir ->
|
|
| 721 | + withCWString label $ \c_template ->
|
|
| 722 | + withCWString suffix $ \c_suffix ->
|
|
| 723 | + allocaBytes (sizeOf (undefined :: CWchar) * 260) $ \c_str -> do
|
|
| 724 | + res <- c_getTempFileNameErrorNo c_tmp_dir c_template c_suffix 0
|
|
| 725 | + c_str
|
|
| 726 | + if not res
|
|
| 727 | + then do errno <- getErrno
|
|
| 728 | + ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 729 | + else do filename <- peekCWString c_str
|
|
| 730 | + handleResultsPosix filename
|
|
| 731 | + |
|
| 732 | + handleResultsPosix filename = do
|
|
| 733 | + let oflags1 = rw_flags .|. o_EXCL
|
|
| 734 | + binary_flags
|
|
| 735 | + | binary = o_BINARY
|
|
| 736 | + | otherwise = 0
|
|
| 737 | + oflags = oflags1 .|. binary_flags
|
|
| 738 | + fd <- withFilePath filename $ \ f -> c_open f oflags mode
|
|
| 739 | + case fd < 0 of
|
|
| 740 | + True -> do errno <- getErrno
|
|
| 741 | + ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 742 | + False ->
|
|
| 743 | + do (fD,fd_type) <- FD.mkFD fd ReadWriteMode Nothing{-no stat-}
|
|
| 744 | + False{-is_socket-}
|
|
| 745 | + True{-is_nonblock-}
|
|
| 746 | + |
|
| 747 | + enc <- getLocaleEncoding
|
|
| 748 | + h <- POSIX.mkHandleFromFD fD fd_type filename ReadWriteMode
|
|
| 749 | + False{-set non-block-} (Just enc)
|
|
| 750 | + |
|
| 751 | + return (filename, h)
|
|
| 752 | + |
|
| 753 | + handleResultsWinIO filename excl = do
|
|
| 754 | + (hwnd, hwnd_type) <- openFileAsTemp filename True excl
|
|
| 755 | + mb_codec <- if binary then return Nothing else fmap Just getLocaleEncoding
|
|
| 756 | + |
|
| 757 | + -- then use it to make a Handle
|
|
| 758 | + h <- mkHandleFromHANDLE hwnd hwnd_type filename ReadWriteMode mb_codec
|
|
| 759 | + `onException` IODevice.close hwnd
|
|
| 760 | + return (filename, h)
|
|
| 761 | + |
|
| 762 | +foreign import ccall "getTempFileNameErrorNo" c_getTempFileNameErrorNo
|
|
| 763 | + :: CWString -> CWString -> CWString -> CUInt -> Ptr CWchar -> IO Bool
|
|
| 764 | + |
|
| 765 | +foreign import ccall "__createUUIDTempFileErrNo" c_createUUIDTempFileErrNo
|
|
| 766 | + :: CWString -> CWString -> CWString -> Ptr CWString -> IO Bool
|
|
| 767 | + |
|
| 768 | +pathSeparator :: String -> Bool
|
|
| 769 | +pathSeparator template = any (\x-> x == '/' || x == '\\') template
|
|
| 770 | + |
|
| 771 | +output_flags = std_flags
|
|
| 772 | +#else /* else mingw32_HOST_OS */
|
|
| 773 | + findTempName = do
|
|
| 774 | + rs <- rand_string
|
|
| 775 | + let filename = prefix ++ rs ++ suffix
|
|
| 776 | + filepath = tmp_dir `combine` filename
|
|
| 777 | + r <- openNewFile filepath binary mode
|
|
| 778 | + case r of
|
|
| 779 | + FileExists -> findTempName
|
|
| 780 | + OpenNewError errno -> ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 781 | + NewFileCreated fd -> do
|
|
| 782 | + (fD,fd_type) <- FD.mkFD fd ReadWriteMode Nothing{-no stat-}
|
|
| 783 | + False{-is_socket-}
|
|
| 784 | + True{-is_nonblock-}
|
|
| 785 | + |
|
| 786 | + enc <- getLocaleEncoding
|
|
| 787 | + h <- POSIX.mkHandleFromFD fD fd_type filepath ReadWriteMode False{-set non-block-} (Just enc)
|
|
| 788 | + |
|
| 789 | + return (filepath, h)
|
|
| 790 | + |
|
| 791 | + where
|
|
| 792 | + -- XXX bits copied from System.FilePath, since that's not available here
|
|
| 793 | + combine a b
|
|
| 794 | + | null b = a
|
|
| 795 | + | null a = b
|
|
| 796 | + | pathSeparator [last a] = a ++ b
|
|
| 797 | + | otherwise = a ++ [pathSeparatorChar] ++ b
|
|
| 798 | + |
|
| 799 | +tempCounter :: IORef Int
|
|
| 800 | +tempCounter = unsafePerformIO $ newIORef 0
|
|
| 801 | +{-# NOINLINE tempCounter #-}
|
|
| 802 | + |
|
| 803 | +-- build large digit-alike number
|
|
| 804 | +rand_string :: IO String
|
|
| 805 | +rand_string = do
|
|
| 806 | + r1 <- c_getpid
|
|
| 807 | + (r2, _) <- atomicModifyIORef'_ tempCounter (+1)
|
|
| 808 | + return $ show r1 ++ "-" ++ show r2
|
|
| 809 | + |
|
| 810 | +data OpenNewFileResult
|
|
| 811 | + = NewFileCreated CInt
|
|
| 812 | + | FileExists
|
|
| 813 | + | OpenNewError Errno
|
|
| 814 | + |
|
| 815 | +openNewFile :: FilePath -> Bool -> CMode -> IO OpenNewFileResult
|
|
| 816 | +openNewFile filepath binary mode = do
|
|
| 817 | + let oflags1 = rw_flags .|. o_EXCL
|
|
| 818 | + |
|
| 819 | + binary_flags
|
|
| 820 | + | binary = o_BINARY
|
|
| 821 | + | otherwise = 0
|
|
| 822 | + |
|
| 823 | + oflags = oflags1 .|. binary_flags
|
|
| 824 | + fd <- withFilePath filepath $ \ f ->
|
|
| 825 | + c_open f oflags mode
|
|
| 826 | + if fd < 0
|
|
| 827 | + then do
|
|
| 828 | + errno <- getErrno
|
|
| 829 | + case errno of
|
|
| 830 | + _ | errno == eEXIST -> return FileExists
|
|
| 831 | + _ -> return (OpenNewError errno)
|
|
| 832 | + else return (NewFileCreated fd)
|
|
| 833 | + |
|
| 834 | +-- XXX Should use filepath library
|
|
| 835 | +pathSeparatorChar :: Char
|
|
| 836 | +pathSeparatorChar = '/'
|
|
| 837 | + |
|
| 838 | +pathSeparator :: String -> Bool
|
|
| 839 | +pathSeparator template = pathSeparatorChar `elem` template
|
|
| 840 | + |
|
| 841 | +output_flags = std_flags .|. o_CREAT
|
|
| 842 | +#endif /* mingw32_HOST_OS */
|
|
| 843 | + |
|
| 844 | +-- XXX Copied from GHC.Handle
|
|
| 845 | +std_flags, output_flags, rw_flags :: CInt
|
|
| 846 | +std_flags = o_NONBLOCK .|. o_NOCTTY
|
|
| 847 | +rw_flags = output_flags .|. o_RDWR
|
|
| 189 | 848 | |
| 190 | 849 | -- $locking
|
| 191 | 850 | -- Implementations should enforce as far as possible, at least locally to the
|
| ... | ... | @@ -99,7 +99,7 @@ import GHC.Internal.Data.List (stripPrefix) |
| 99 | 99 | import GHC.Internal.Word
|
| 100 | 100 | import GHC.Internal.Numeric
|
| 101 | 101 | import GHC.Internal.Numeric.Natural
|
| 102 | -import GHC.Internal.System.IO
|
|
| 102 | +import System.IO
|
|
| 103 | 103 | |
| 104 | 104 | -- $setup
|
| 105 | 105 | -- >>> import Prelude
|
| 1 | 1 | {-# LANGUAGE Trustworthy #-}
|
| 2 | -{-# LANGUAGE CPP, NoImplicitPrelude, CApiFFI #-}
|
|
| 3 | -{-# OPTIONS_GHC -Wno-x-partial #-}
|
|
| 4 | 2 | |
| 5 | 3 | -----------------------------------------------------------------------------
|
| 6 | 4 | -- |
|
| ... | ... | @@ -16,286 +14,13 @@ |
| 16 | 14 | --
|
| 17 | 15 | -----------------------------------------------------------------------------
|
| 18 | 16 | |
| 19 | -module GHC.Internal.System.IO (
|
|
| 20 | - -- * The IO monad
|
|
| 17 | +module GHC.Internal.System.IO (putStrLn, print) where
|
|
| 21 | 18 | |
| 22 | - IO,
|
|
| 23 | - |
|
| 24 | - -- * Files and handles
|
|
| 25 | - |
|
| 26 | - FilePath,
|
|
| 27 | - |
|
| 28 | - Handle, -- abstract, instance of: Eq, Show.
|
|
| 29 | - |
|
| 30 | - -- | GHC note: a 'Handle' will be automatically closed when the garbage
|
|
| 31 | - -- collector detects that it has become unreferenced by the program.
|
|
| 32 | - -- However, relying on this behaviour is not generally recommended:
|
|
| 33 | - -- the garbage collector is unpredictable. If possible, use
|
|
| 34 | - -- an explicit 'hClose' to close 'Handle's when they are no longer
|
|
| 35 | - -- required. GHC does not currently attempt to free up file
|
|
| 36 | - -- descriptors when they have run out, it is your responsibility to
|
|
| 37 | - -- ensure that this doesn't happen.
|
|
| 38 | - |
|
| 39 | - -- ** Standard handles
|
|
| 40 | - |
|
| 41 | - -- | Three handles are allocated during program initialisation,
|
|
| 42 | - -- and are initially open.
|
|
| 43 | - |
|
| 44 | - stdin, stdout, stderr,
|
|
| 45 | - |
|
| 46 | - -- * Opening and closing files
|
|
| 47 | - |
|
| 48 | - -- ** Opening files
|
|
| 49 | - |
|
| 50 | - withFile,
|
|
| 51 | - openFile,
|
|
| 52 | - IOMode(ReadMode,WriteMode,AppendMode,ReadWriteMode),
|
|
| 53 | - |
|
| 54 | - -- ** Closing files
|
|
| 55 | - |
|
| 56 | - hClose,
|
|
| 57 | - |
|
| 58 | - -- ** Special cases
|
|
| 59 | - |
|
| 60 | - -- | These functions are also exported by the "Prelude".
|
|
| 61 | - |
|
| 62 | - readFile,
|
|
| 63 | - readFile',
|
|
| 64 | - writeFile,
|
|
| 65 | - appendFile,
|
|
| 66 | - |
|
| 67 | - -- * Operations on handles
|
|
| 68 | - |
|
| 69 | - -- ** Determining and changing the size of a file
|
|
| 70 | - |
|
| 71 | - hFileSize,
|
|
| 72 | - hSetFileSize,
|
|
| 73 | - |
|
| 74 | - -- ** Detecting the end of input
|
|
| 75 | - |
|
| 76 | - hIsEOF,
|
|
| 77 | - isEOF,
|
|
| 78 | - |
|
| 79 | - -- ** Buffering operations
|
|
| 80 | - |
|
| 81 | - BufferMode(NoBuffering,LineBuffering,BlockBuffering),
|
|
| 82 | - hSetBuffering,
|
|
| 83 | - hGetBuffering,
|
|
| 84 | - hFlush,
|
|
| 85 | - |
|
| 86 | - -- ** Repositioning handles
|
|
| 87 | - |
|
| 88 | - hGetPosn,
|
|
| 89 | - hSetPosn,
|
|
| 90 | - HandlePosn, -- abstract, instance of: Eq, Show.
|
|
| 91 | - |
|
| 92 | - hSeek,
|
|
| 93 | - SeekMode(AbsoluteSeek,RelativeSeek,SeekFromEnd),
|
|
| 94 | - hTell,
|
|
| 95 | - |
|
| 96 | - -- ** Handle properties
|
|
| 97 | - |
|
| 98 | - hIsOpen, hIsClosed,
|
|
| 99 | - hIsReadable, hIsWritable,
|
|
| 100 | - hIsSeekable,
|
|
| 101 | - |
|
| 102 | - -- ** Terminal operations (not portable: GHC only)
|
|
| 103 | - |
|
| 104 | - hIsTerminalDevice,
|
|
| 105 | - |
|
| 106 | - hSetEcho,
|
|
| 107 | - hGetEcho,
|
|
| 108 | - |
|
| 109 | - -- ** Showing handle state (not portable: GHC only)
|
|
| 110 | - |
|
| 111 | - hShow,
|
|
| 112 | - |
|
| 113 | - -- * Text input and output
|
|
| 114 | - |
|
| 115 | - -- ** Text input
|
|
| 116 | - |
|
| 117 | - hWaitForInput,
|
|
| 118 | - hReady,
|
|
| 119 | - hGetChar,
|
|
| 120 | - hGetLine,
|
|
| 121 | - hLookAhead,
|
|
| 122 | - hGetContents,
|
|
| 123 | - hGetContents',
|
|
| 124 | - |
|
| 125 | - -- ** Text output
|
|
| 126 | - |
|
| 127 | - hPutChar,
|
|
| 128 | - hPutStr,
|
|
| 129 | - hPutStrLn,
|
|
| 130 | - hPrint,
|
|
| 131 | - |
|
| 132 | - -- ** Special cases for standard input and output
|
|
| 133 | - |
|
| 134 | - -- | These functions are also exported by the "Prelude".
|
|
| 135 | - |
|
| 136 | - interact,
|
|
| 137 | - putChar,
|
|
| 138 | - putStr,
|
|
| 139 | - putStrLn,
|
|
| 140 | - print,
|
|
| 141 | - getChar,
|
|
| 142 | - getLine,
|
|
| 143 | - getContents,
|
|
| 144 | - getContents',
|
|
| 145 | - readIO,
|
|
| 146 | - readLn,
|
|
| 147 | - |
|
| 148 | - -- * Binary input and output
|
|
| 149 | - |
|
| 150 | - withBinaryFile,
|
|
| 151 | - openBinaryFile,
|
|
| 152 | - hSetBinaryMode,
|
|
| 153 | - hPutBuf,
|
|
| 154 | - hGetBuf,
|
|
| 155 | - hGetBufSome,
|
|
| 156 | - hPutBufNonBlocking,
|
|
| 157 | - hGetBufNonBlocking,
|
|
| 158 | - |
|
| 159 | - -- * Temporary files
|
|
| 160 | - |
|
| 161 | - openTempFile,
|
|
| 162 | - openBinaryTempFile,
|
|
| 163 | - openTempFileWithDefaultPermissions,
|
|
| 164 | - openBinaryTempFileWithDefaultPermissions,
|
|
| 165 | - |
|
| 166 | - -- * Unicode encoding\/decoding
|
|
| 167 | - |
|
| 168 | - -- | A text-mode 'Handle' has an associated 'TextEncoding', which
|
|
| 169 | - -- is used to decode bytes into Unicode characters when reading,
|
|
| 170 | - -- and encode Unicode characters into bytes when writing.
|
|
| 171 | - --
|
|
| 172 | - -- The default 'TextEncoding' is the same as the default encoding
|
|
| 173 | - -- on your system, which is also available as 'localeEncoding'.
|
|
| 174 | - -- (GHC note: on Windows, we currently do not support double-byte
|
|
| 175 | - -- encodings; if the console\'s code page is unsupported, then
|
|
| 176 | - -- 'localeEncoding' will be 'latin1'.)
|
|
| 177 | - --
|
|
| 178 | - -- Encoding and decoding errors are always detected and reported,
|
|
| 179 | - -- except during lazy I/O ('hGetContents', 'getContents', and
|
|
| 180 | - -- 'readFile'), where a decoding error merely results in
|
|
| 181 | - -- termination of the character stream, as with other I/O errors.
|
|
| 182 | - |
|
| 183 | - hSetEncoding,
|
|
| 184 | - hGetEncoding,
|
|
| 185 | - |
|
| 186 | - -- ** Unicode encodings
|
|
| 187 | - TextEncoding,
|
|
| 188 | - latin1,
|
|
| 189 | - utf8, utf8_bom,
|
|
| 190 | - utf16, utf16le, utf16be,
|
|
| 191 | - utf32, utf32le, utf32be,
|
|
| 192 | - localeEncoding,
|
|
| 193 | - char8,
|
|
| 194 | - mkTextEncoding,
|
|
| 195 | - |
|
| 196 | - -- * Newline conversion
|
|
| 197 | - |
|
| 198 | - -- | In Haskell, a newline is always represented by the character
|
|
| 199 | - -- @\'\\n\'@. However, in files and external character streams, a
|
|
| 200 | - -- newline may be represented by another character sequence, such
|
|
| 201 | - -- as @\'\\r\\n\'@.
|
|
| 202 | - --
|
|
| 203 | - -- A text-mode 'Handle' has an associated 'NewlineMode' that
|
|
| 204 | - -- specifies how to translate newline characters. The
|
|
| 205 | - -- 'NewlineMode' specifies the input and output translation
|
|
| 206 | - -- separately, so that for instance you can translate @\'\\r\\n\'@
|
|
| 207 | - -- to @\'\\n\'@ on input, but leave newlines as @\'\\n\'@ on output.
|
|
| 208 | - --
|
|
| 209 | - -- The default 'NewlineMode' for a 'Handle' is
|
|
| 210 | - -- 'nativeNewlineMode', which does no translation on Unix systems,
|
|
| 211 | - -- but translates @\'\\r\\n\'@ to @\'\\n\'@ and back on Windows.
|
|
| 212 | - --
|
|
| 213 | - -- Binary-mode 'Handle's do no newline translation at all.
|
|
| 214 | - --
|
|
| 215 | - hSetNewlineMode,
|
|
| 216 | - hGetNewlineMode,
|
|
| 217 | - Newline(..), nativeNewline,
|
|
| 218 | - NewlineMode(..),
|
|
| 219 | - noNewlineTranslation, universalNewlineMode, nativeNewlineMode,
|
|
| 220 | - ) where
|
|
| 221 | - |
|
| 222 | -import GHC.Internal.Control.Exception.Base
|
|
| 223 | - |
|
| 224 | -import GHC.Internal.Data.Bits
|
|
| 225 | -import GHC.Internal.Data.Maybe
|
|
| 226 | -import GHC.Internal.Foreign.C.Error
|
|
| 227 | -#if defined(mingw32_HOST_OS)
|
|
| 228 | -import GHC.Internal.Foreign.C.String
|
|
| 229 | -import GHC.Internal.Foreign.Ptr
|
|
| 230 | -import GHC.Internal.Foreign.Marshal.Alloc
|
|
| 231 | -import GHC.Internal.Foreign.Marshal.Utils (with)
|
|
| 232 | -import GHC.Internal.Foreign.Storable
|
|
| 233 | -import GHC.Internal.IO.SubSystem
|
|
| 234 | -import GHC.Internal.IO.Windows.Handle (openFileAsTemp)
|
|
| 235 | -import GHC.Internal.IO.Handle.Windows (mkHandleFromHANDLE)
|
|
| 236 | -import GHC.Internal.IO.Device as IODevice
|
|
| 237 | -import GHC.Internal.Real (fromIntegral)
|
|
| 238 | -#endif
|
|
| 239 | -import GHC.Internal.Foreign.C.Types
|
|
| 240 | -import GHC.Internal.System.Posix.Internals
|
|
| 241 | -import GHC.Internal.System.Posix.Types
|
|
| 242 | - |
|
| 243 | -import GHC.Internal.Base
|
|
| 244 | -import GHC.Internal.List
|
|
| 245 | -#if !defined(mingw32_HOST_OS)
|
|
| 246 | -import GHC.Internal.IORef
|
|
| 247 | -#endif
|
|
| 248 | -import GHC.Internal.Num
|
|
| 249 | -import GHC.Internal.IO hiding ( bracket, onException )
|
|
| 250 | -import GHC.Internal.IO.IOMode
|
|
| 251 | -import qualified GHC.Internal.IO.FD as FD
|
|
| 252 | -import GHC.Internal.IO.Handle
|
|
| 253 | -import qualified GHC.Internal.IO.Handle.FD as POSIX
|
|
| 254 | -import GHC.Internal.IO.Handle.Text ( hGetBufSome, hPutStrLn )
|
|
| 255 | -import GHC.Internal.IO.Exception ( userError )
|
|
| 256 | -import GHC.Internal.IO.Encoding
|
|
| 257 | -import GHC.Internal.Text.Read
|
|
| 258 | -import GHC.Internal.IO.StdHandles
|
|
| 259 | -import GHC.Internal.Show
|
|
| 260 | ------------------------------------------------------------------------------
|
|
| 261 | --- Standard IO
|
|
| 262 | - |
|
| 263 | --- | Write a character to the standard output device
|
|
| 264 | ---
|
|
| 265 | --- 'putChar' is implemented as @'hPutChar' 'stdout'@.
|
|
| 266 | ---
|
|
| 267 | --- This operation may fail with the same errors as 'hPutChar'.
|
|
| 268 | ---
|
|
| 269 | --- ==== __Examples__
|
|
| 270 | ---
|
|
| 271 | --- Note that the following do not put a newline.
|
|
| 272 | ---
|
|
| 273 | --- >>> putChar 'x'
|
|
| 274 | --- x
|
|
| 275 | ---
|
|
| 276 | --- >>> putChar '\0042'
|
|
| 277 | --- *
|
|
| 278 | -putChar :: Char -> IO ()
|
|
| 279 | -putChar c = hPutChar stdout c
|
|
| 280 | - |
|
| 281 | --- | Write a string to the standard output device
|
|
| 282 | ---
|
|
| 283 | --- 'putStr' is implemented as @'hPutStr' 'stdout'@.
|
|
| 284 | ---
|
|
| 285 | --- This operation may fail with the same errors, and has the same issues with concurrency, as 'hPutStr'!
|
|
| 286 | ---
|
|
| 287 | --- ==== __Examples__
|
|
| 288 | ---
|
|
| 289 | --- Note that the following do not put a newline.
|
|
| 290 | ---
|
|
| 291 | --- >>> putStr "Hello, World!"
|
|
| 292 | --- Hello, World!
|
|
| 293 | ---
|
|
| 294 | --- >>> putStr "\0052\0042\0050"
|
|
| 295 | --- 4*2
|
|
| 296 | ---
|
|
| 297 | -putStr :: String -> IO ()
|
|
| 298 | -putStr s = hPutStr stdout s
|
|
| 19 | +import GHC.Internal.Base (String)
|
|
| 20 | +import GHC.Internal.IO (IO)
|
|
| 21 | +import GHC.Internal.IO.Handle.Text (hPutStrLn)
|
|
| 22 | +import GHC.Internal.IO.StdHandles (stdout)
|
|
| 23 | +import GHC.Internal.Show (Show, show)
|
|
| 299 | 24 | |
| 300 | 25 | -- | The same as 'putStr', but adds a newline character.
|
| 301 | 26 | --
|
| ... | ... | @@ -332,485 +57,3 @@ putStrLn s = hPutStrLn stdout s |
| 332 | 57 | -- [(0,1),(1,2),(2,4),(3,8),(4,16),(5,32),(6,64),(7,128),(8,256)]
|
| 333 | 58 | print :: Show a => a -> IO ()
|
| 334 | 59 | print x = putStrLn (show x) |
| 335 | - |
|
| 336 | --- | Read a single character from the standard input device.
|
|
| 337 | ---
|
|
| 338 | --- 'getChar' is implemented as @'hGetChar' 'stdin'@.
|
|
| 339 | ---
|
|
| 340 | --- This operation may fail with the same errors as 'hGetChar'.
|
|
| 341 | ---
|
|
| 342 | --- ==== __Examples__
|
|
| 343 | ---
|
|
| 344 | --- >>> getChar
|
|
| 345 | --- a'a'
|
|
| 346 | ---
|
|
| 347 | --- >>> getChar
|
|
| 348 | --- >
|
|
| 349 | --- '\n'
|
|
| 350 | -getChar :: IO Char
|
|
| 351 | -getChar = hGetChar stdin
|
|
| 352 | - |
|
| 353 | --- | Read a line from the standard input device.
|
|
| 354 | ---
|
|
| 355 | --- 'getLine' is implemented as @'hGetLine' 'stdin'@.
|
|
| 356 | ---
|
|
| 357 | --- This operation may fail with the same errors as 'hGetLine'.
|
|
| 358 | ---
|
|
| 359 | --- ==== __Examples__
|
|
| 360 | ---
|
|
| 361 | --- >>> getLine
|
|
| 362 | --- > Hello World!
|
|
| 363 | --- "Hello World!"
|
|
| 364 | ---
|
|
| 365 | --- >>> getLine
|
|
| 366 | --- >
|
|
| 367 | --- ""
|
|
| 368 | -getLine :: IO String
|
|
| 369 | -getLine = hGetLine stdin
|
|
| 370 | - |
|
| 371 | --- | The 'getContents' operation returns all user input as a single string,
|
|
| 372 | --- which is read lazily as it is needed.
|
|
| 373 | ---
|
|
| 374 | --- 'getContents' is implemented as @'hGetContents' 'stdin'@.
|
|
| 375 | ---
|
|
| 376 | --- This operation may fail with the same errors as 'hGetContents'.
|
|
| 377 | ---
|
|
| 378 | --- ==== __Examples__
|
|
| 379 | ---
|
|
| 380 | --- >>> getContents >>= putStr
|
|
| 381 | --- > aaabbbccc :D
|
|
| 382 | --- aaabbbccc :D
|
|
| 383 | --- > I hope you have a great day
|
|
| 384 | --- I hope you have a great day
|
|
| 385 | --- > ^D
|
|
| 386 | ---
|
|
| 387 | --- >>> getContents >>= print . length
|
|
| 388 | --- > abc
|
|
| 389 | --- > <3
|
|
| 390 | --- > def ^D
|
|
| 391 | --- 11
|
|
| 392 | -getContents :: IO String
|
|
| 393 | -getContents = hGetContents stdin
|
|
| 394 | - |
|
| 395 | --- | The 'getContents'' operation returns all user input as a single string,
|
|
| 396 | --- which is fully read before being returned
|
|
| 397 | ---
|
|
| 398 | --- 'getContents'' is implemented as @'hGetContents'' 'stdin'@.
|
|
| 399 | ---
|
|
| 400 | --- This operation may fail with the same errors as 'hGetContents''.
|
|
| 401 | ---
|
|
| 402 | --- ==== __Examples__
|
|
| 403 | ---
|
|
| 404 | --- >>> getContents' >>= putStr
|
|
| 405 | --- > aaabbbccc :D
|
|
| 406 | --- > I hope you have a great day
|
|
| 407 | --- aaabbbccc :D
|
|
| 408 | --- I hope you have a great day
|
|
| 409 | ---
|
|
| 410 | --- >>> getContents' >>= print . length
|
|
| 411 | --- > abc
|
|
| 412 | --- > <3
|
|
| 413 | --- > def ^D
|
|
| 414 | --- 11
|
|
| 415 | ---
|
|
| 416 | --- @since base-4.15.0.0
|
|
| 417 | -getContents' :: IO String
|
|
| 418 | -getContents' = hGetContents' stdin
|
|
| 419 | - |
|
| 420 | --- | @'interact' f@ takes the entire input from 'stdin' and applies @f@ to it.
|
|
| 421 | --- The resulting string is written to the 'stdout' device.
|
|
| 422 | ---
|
|
| 423 | --- Note that this operation is lazy, which allows to produce output
|
|
| 424 | --- even before all input has been consumed.
|
|
| 425 | ---
|
|
| 426 | --- This operation may fail with the same errors as 'getContents' and 'putStr'.
|
|
| 427 | ---
|
|
| 428 | --- If it doesn't produce output the buffering settings may not be
|
|
| 429 | --- correct, use ^D (ctrl+D) to close stdin which forces
|
|
| 430 | --- the buffer to be consumed.
|
|
| 431 | ---
|
|
| 432 | --- You may wish to set the buffering style appropriate to your program's
|
|
| 433 | --- needs before using this function, for example:
|
|
| 434 | ---
|
|
| 435 | --- @
|
|
| 436 | --- main :: IO ()
|
|
| 437 | --- main = do
|
|
| 438 | --- hSetBuffering stdin LineBuffering
|
|
| 439 | --- hSetBuffering stdout NoBuffering
|
|
| 440 | --- interact (concatMap (\str -> str ++ str) . L.lines)
|
|
| 441 | --- @
|
|
| 442 | ---
|
|
| 443 | --- ==== __Examples__
|
|
| 444 | ---
|
|
| 445 | --- >>> interact (\str -> str ++ str)
|
|
| 446 | --- > hi :)
|
|
| 447 | --- hi :)
|
|
| 448 | --- > ^D
|
|
| 449 | --- hi :)
|
|
| 450 | ---
|
|
| 451 | --- >>> interact (const ":D")
|
|
| 452 | --- :D
|
|
| 453 | ---
|
|
| 454 | --- >>> interact (show . words)
|
|
| 455 | --- > hello world!
|
|
| 456 | --- > I hope you have a great day
|
|
| 457 | --- > ^D
|
|
| 458 | --- ["hello","world!","I","hope","you","have","a","great","day"]
|
|
| 459 | -interact :: (String -> String) -> IO ()
|
|
| 460 | -interact f = do s <- getContents
|
|
| 461 | - putStr (f s)
|
|
| 462 | - |
|
| 463 | --- | The 'readFile' function reads a file and
|
|
| 464 | --- returns the contents of the file as a string.
|
|
| 465 | ---
|
|
| 466 | --- The file is read lazily, on demand, as with 'getContents'.
|
|
| 467 | ---
|
|
| 468 | --- This operation may fail with the same errors as 'hGetContents' and 'openFile'.
|
|
| 469 | ---
|
|
| 470 | --- ==== __Examples__
|
|
| 471 | ---
|
|
| 472 | --- >>> readFile "~/hello_world"
|
|
| 473 | --- "Greetings!"
|
|
| 474 | ---
|
|
| 475 | --- >>> take 5 <$> readFile "/dev/zero"
|
|
| 476 | --- "\NUL\NUL\NUL\NUL\NUL"
|
|
| 477 | -readFile :: FilePath -> IO String
|
|
| 478 | -readFile name = openFile name ReadMode >>= hGetContents
|
|
| 479 | - |
|
| 480 | --- | The 'readFile'' function reads a file and
|
|
| 481 | --- returns the contents of the file as a string.
|
|
| 482 | ---
|
|
| 483 | --- This is identical to 'readFile', but the file is fully read before being returned,
|
|
| 484 | --- as with 'getContents''.
|
|
| 485 | ---
|
|
| 486 | --- @since base-4.15.0.0
|
|
| 487 | -readFile' :: FilePath -> IO String
|
|
| 488 | --- There's a bit of overkill here—both withFile and
|
|
| 489 | --- hGetContents' will close the file in the end.
|
|
| 490 | -readFile' name = withFile name ReadMode hGetContents'
|
|
| 491 | - |
|
| 492 | --- | The computation @'writeFile' file str@ function writes the string @str@,
|
|
| 493 | --- to the file @file@.
|
|
| 494 | ---
|
|
| 495 | --- This operation may fail with the same errors as 'hPutStr' and 'withFile'.
|
|
| 496 | ---
|
|
| 497 | --- ==== __Examples__
|
|
| 498 | ---
|
|
| 499 | --- >>> writeFile "hello" "world" >> readFile "hello"
|
|
| 500 | --- "world"
|
|
| 501 | ---
|
|
| 502 | --- >>> writeFile "~/" "D:"
|
|
| 503 | --- *** Exception: ~/: withFile: inappropriate type (Is a directory)
|
|
| 504 | -writeFile :: FilePath -> String -> IO ()
|
|
| 505 | -writeFile f txt = withFile f WriteMode (\ hdl -> hPutStr hdl txt)
|
|
| 506 | - |
|
| 507 | --- | The computation @'appendFile' file str@ function appends the string @str@,
|
|
| 508 | --- to the file @file@.
|
|
| 509 | ---
|
|
| 510 | --- Note that 'writeFile' and 'appendFile' write a literal string
|
|
| 511 | --- to a file. To write a value of any printable type, as with 'print',
|
|
| 512 | --- use the 'show' function to convert the value to a string first.
|
|
| 513 | ---
|
|
| 514 | --- This operation may fail with the same errors as 'hPutStr' and 'withFile'.
|
|
| 515 | ---
|
|
| 516 | --- ==== __Examples__
|
|
| 517 | ---
|
|
| 518 | --- The following example could be more efficently written by acquiring a handle
|
|
| 519 | --- instead with 'openFile' and using the computations capable of writing to handles
|
|
| 520 | --- such as 'hPutStr'.
|
|
| 521 | ---
|
|
| 522 | --- >>> let fn = "hello_world"
|
|
| 523 | --- >>> in writeFile fn "hello" >> appendFile fn " world!" >> (readFile fn >>= putStrLn)
|
|
| 524 | --- "hello world!"
|
|
| 525 | ---
|
|
| 526 | --- >>> let fn = "foo"; output = readFile' fn >>= putStrLn
|
|
| 527 | --- >>> in output >> appendFile fn (show [1,2,3]) >> output
|
|
| 528 | --- this is what's in the file
|
|
| 529 | --- this is what's in the file[1,2,3]
|
|
| 530 | -appendFile :: FilePath -> String -> IO ()
|
|
| 531 | -appendFile f txt = withFile f AppendMode (\ hdl -> hPutStr hdl txt)
|
|
| 532 | - |
|
| 533 | --- | The 'readLn' function combines 'getLine' and 'readIO'.
|
|
| 534 | ---
|
|
| 535 | --- This operation may fail with the same errors as 'getLine' and 'readIO'.
|
|
| 536 | ---
|
|
| 537 | --- ==== __Examples__
|
|
| 538 | ---
|
|
| 539 | --- >>> fmap (+ 5) readLn
|
|
| 540 | --- > 25
|
|
| 541 | --- 30
|
|
| 542 | ---
|
|
| 543 | --- >>> readLn :: IO String
|
|
| 544 | --- > this is not a string literal
|
|
| 545 | --- *** Exception: user error (Prelude.readIO: no parse)
|
|
| 546 | -readLn :: Read a => IO a
|
|
| 547 | -readLn = getLine >>= readIO
|
|
| 548 | - |
|
| 549 | --- | The 'readIO' function is similar to 'read' except that it signals
|
|
| 550 | --- parse failure to the 'IO' monad instead of terminating the program.
|
|
| 551 | ---
|
|
| 552 | --- This operation may fail with:
|
|
| 553 | ---
|
|
| 554 | --- * 'GHC.Internal.System.IO.Error.isUserError' if there is no unambiguous parse.
|
|
| 555 | ---
|
|
| 556 | --- ==== __Examples__
|
|
| 557 | ---
|
|
| 558 | --- >>> fmap (+ 1) (readIO "1")
|
|
| 559 | --- 2
|
|
| 560 | ---
|
|
| 561 | --- >>> readIO "not quite ()" :: IO ()
|
|
| 562 | --- *** Exception: user error (Prelude.readIO: no parse)
|
|
| 563 | -readIO :: Read a => String -> IO a
|
|
| 564 | -readIO s = case (do { (x,t) <- reads s ;
|
|
| 565 | - ("","") <- lex t ;
|
|
| 566 | - return x }) of
|
|
| 567 | - [x] -> return x
|
|
| 568 | - [] -> ioError (userError "Prelude.readIO: no parse")
|
|
| 569 | - _ -> ioError (userError "Prelude.readIO: ambiguous parse")
|
|
| 570 | - |
|
| 571 | --- | The encoding of the current locale.
|
|
| 572 | ---
|
|
| 573 | --- This is the initial locale encoding: if it has been subsequently changed by
|
|
| 574 | --- 'GHC.Internal.IO.Encoding.setLocaleEncoding' this value will not reflect that change.
|
|
| 575 | -localeEncoding :: TextEncoding
|
|
| 576 | -localeEncoding = initLocaleEncoding
|
|
| 577 | - |
|
| 578 | --- | Computation 'hReady' @hdl@ indicates whether at least one item is
|
|
| 579 | --- available for input from handle @hdl@.
|
|
| 580 | ---
|
|
| 581 | --- This operation may fail with:
|
|
| 582 | ---
|
|
| 583 | --- * 'GHC.Internal.System.IO.Error.isEOFError' if the end of file has been reached.
|
|
| 584 | -hReady :: Handle -> IO Bool
|
|
| 585 | -hReady h = hWaitForInput h 0
|
|
| 586 | - |
|
| 587 | --- | Computation 'hPrint' @hdl t@ writes the string representation of @t@
|
|
| 588 | --- given by the 'show' function to the file or channel managed by @hdl@
|
|
| 589 | --- and appends a newline.
|
|
| 590 | ---
|
|
| 591 | --- This operation may fail with the same errors as 'hPutStrLn'
|
|
| 592 | ---
|
|
| 593 | --- ==== __Examples__
|
|
| 594 | ---
|
|
| 595 | --- >>> hPrint stdout [1,2,3]
|
|
| 596 | --- [1,2,3]
|
|
| 597 | ---
|
|
| 598 | --- >>> hPrint stdin [4,5,6]
|
|
| 599 | --- *** Exception: <stdin>: hPutStr: illegal operation (handle is not open for writing)
|
|
| 600 | -hPrint :: Show a => Handle -> a -> IO ()
|
|
| 601 | -hPrint hdl = hPutStrLn hdl . show
|
|
| 602 | - |
|
| 603 | --- | The function creates a temporary file in ReadWrite mode.
|
|
| 604 | --- The created file isn\'t deleted automatically, so you need to delete it manually.
|
|
| 605 | ---
|
|
| 606 | --- The file is created with permissions such that only the current
|
|
| 607 | --- user can read\/write it.
|
|
| 608 | ---
|
|
| 609 | --- With some exceptions (see below), the file will be created securely
|
|
| 610 | --- in the sense that an attacker should not be able to cause
|
|
| 611 | --- openTempFile to overwrite another file on the filesystem using your
|
|
| 612 | --- credentials, by putting symbolic links (on Unix) in the place where
|
|
| 613 | --- the temporary file is to be created. On Unix the @O_CREAT@ and
|
|
| 614 | --- @O_EXCL@ flags are used to prevent this attack, but note that
|
|
| 615 | --- @O_EXCL@ is sometimes not supported on NFS filesystems, so if you
|
|
| 616 | --- rely on this behaviour it is best to use local filesystems only.
|
|
| 617 | -openTempFile :: FilePath -- ^ Directory in which to create the file
|
|
| 618 | - -> String -- ^ File name template. If the template is \"foo.ext\" then
|
|
| 619 | - -- the created file will be \"fooXXX.ext\" where XXX is some
|
|
| 620 | - -- random number. Note that this should not contain any path
|
|
| 621 | - -- separator characters. On Windows, the template prefix may
|
|
| 622 | - -- be truncated to 3 chars, e.g. \"foobar.ext\" will be
|
|
| 623 | - -- \"fooXXX.ext\".
|
|
| 624 | - -> IO (FilePath, Handle)
|
|
| 625 | -openTempFile tmp_dir template
|
|
| 626 | - = openTempFile' "openTempFile" tmp_dir template False 0o600
|
|
| 627 | - |
|
| 628 | --- | Like 'openTempFile', but opens the file in binary mode. See 'openBinaryFile' for more comments.
|
|
| 629 | -openBinaryTempFile :: FilePath -> String -> IO (FilePath, Handle)
|
|
| 630 | -openBinaryTempFile tmp_dir template
|
|
| 631 | - = openTempFile' "openBinaryTempFile" tmp_dir template True 0o600
|
|
| 632 | - |
|
| 633 | --- | Like 'openTempFile', but uses the default file permissions
|
|
| 634 | -openTempFileWithDefaultPermissions :: FilePath -> String
|
|
| 635 | - -> IO (FilePath, Handle)
|
|
| 636 | -openTempFileWithDefaultPermissions tmp_dir template
|
|
| 637 | - = openTempFile' "openTempFileWithDefaultPermissions" tmp_dir template False 0o666
|
|
| 638 | - |
|
| 639 | --- | Like 'openBinaryTempFile', but uses the default file permissions
|
|
| 640 | -openBinaryTempFileWithDefaultPermissions :: FilePath -> String
|
|
| 641 | - -> IO (FilePath, Handle)
|
|
| 642 | -openBinaryTempFileWithDefaultPermissions tmp_dir template
|
|
| 643 | - = openTempFile' "openBinaryTempFileWithDefaultPermissions" tmp_dir template True 0o666
|
|
| 644 | - |
|
| 645 | -openTempFile' :: String -> FilePath -> String -> Bool -> CMode
|
|
| 646 | - -> IO (FilePath, Handle)
|
|
| 647 | -openTempFile' loc tmp_dir template binary mode
|
|
| 648 | - | pathSeparator template
|
|
| 649 | - = failIO $ "openTempFile': Template string must not contain path separator characters: "++template
|
|
| 650 | - | otherwise = findTempName
|
|
| 651 | - where
|
|
| 652 | - -- We split off the last extension, so we can use .foo.ext files
|
|
| 653 | - -- for temporary files (hidden on Unix OSes). Unfortunately we're
|
|
| 654 | - -- below filepath in the hierarchy here.
|
|
| 655 | - (prefix, suffix) =
|
|
| 656 | - case break (== '.') $ reverse template of
|
|
| 657 | - -- First case: template contains no '.'s. Just re-reverse it.
|
|
| 658 | - (rev_suffix, "") -> (reverse rev_suffix, "")
|
|
| 659 | - -- Second case: template contains at least one '.'. Strip the
|
|
| 660 | - -- dot from the prefix and prepend it to the suffix (if we don't
|
|
| 661 | - -- do this, the unique number will get added after the '.' and
|
|
| 662 | - -- thus be part of the extension, which is wrong.)
|
|
| 663 | - (rev_suffix, '.':rest) -> (reverse rest, '.':reverse rev_suffix)
|
|
| 664 | - -- Otherwise, something is wrong, because (break (== '.')) should
|
|
| 665 | - -- always return a pair with either the empty string or a string
|
|
| 666 | - -- beginning with '.' as the second component.
|
|
| 667 | - _ -> errorWithoutStackTrace "bug in GHC.Internal.System.IO.openTempFile"
|
|
| 668 | -#if defined(mingw32_HOST_OS)
|
|
| 669 | - findTempName = findTempNamePosix <!> findTempNameWinIO
|
|
| 670 | - |
|
| 671 | - findTempNameWinIO = do
|
|
| 672 | - let label = if null prefix then "ghc" else prefix
|
|
| 673 | - withCWString tmp_dir $ \c_tmp_dir ->
|
|
| 674 | - withCWString label $ \c_template ->
|
|
| 675 | - withCWString suffix $ \c_suffix ->
|
|
| 676 | - with nullPtr $ \c_ptr -> do
|
|
| 677 | - res <- c_createUUIDTempFileErrNo c_tmp_dir c_template c_suffix c_ptr
|
|
| 678 | - if not res
|
|
| 679 | - then do errno <- getErrno
|
|
| 680 | - ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 681 | - else do c_p <- peek c_ptr
|
|
| 682 | - filename <- peekCWString c_p
|
|
| 683 | - free c_p
|
|
| 684 | - let flags = fromIntegral mode .&. o_EXCL
|
|
| 685 | - handleResultsWinIO filename (flags == o_EXCL)
|
|
| 686 | - |
|
| 687 | - findTempNamePosix = do
|
|
| 688 | - let label = if null prefix then "ghc" else prefix
|
|
| 689 | - withCWString tmp_dir $ \c_tmp_dir ->
|
|
| 690 | - withCWString label $ \c_template ->
|
|
| 691 | - withCWString suffix $ \c_suffix ->
|
|
| 692 | - allocaBytes (sizeOf (undefined :: CWchar) * 260) $ \c_str -> do
|
|
| 693 | - res <- c_getTempFileNameErrorNo c_tmp_dir c_template c_suffix 0
|
|
| 694 | - c_str
|
|
| 695 | - if not res
|
|
| 696 | - then do errno <- getErrno
|
|
| 697 | - ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 698 | - else do filename <- peekCWString c_str
|
|
| 699 | - handleResultsPosix filename
|
|
| 700 | - |
|
| 701 | - handleResultsPosix filename = do
|
|
| 702 | - let oflags1 = rw_flags .|. o_EXCL
|
|
| 703 | - binary_flags
|
|
| 704 | - | binary = o_BINARY
|
|
| 705 | - | otherwise = 0
|
|
| 706 | - oflags = oflags1 .|. binary_flags
|
|
| 707 | - fd <- withFilePath filename $ \ f -> c_open f oflags mode
|
|
| 708 | - case fd < 0 of
|
|
| 709 | - True -> do errno <- getErrno
|
|
| 710 | - ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 711 | - False ->
|
|
| 712 | - do (fD,fd_type) <- FD.mkFD fd ReadWriteMode Nothing{-no stat-}
|
|
| 713 | - False{-is_socket-}
|
|
| 714 | - True{-is_nonblock-}
|
|
| 715 | - |
|
| 716 | - enc <- getLocaleEncoding
|
|
| 717 | - h <- POSIX.mkHandleFromFD fD fd_type filename ReadWriteMode
|
|
| 718 | - False{-set non-block-} (Just enc)
|
|
| 719 | - |
|
| 720 | - return (filename, h)
|
|
| 721 | - |
|
| 722 | - handleResultsWinIO filename excl = do
|
|
| 723 | - (hwnd, hwnd_type) <- openFileAsTemp filename True excl
|
|
| 724 | - mb_codec <- if binary then return Nothing else fmap Just getLocaleEncoding
|
|
| 725 | - |
|
| 726 | - -- then use it to make a Handle
|
|
| 727 | - h <- mkHandleFromHANDLE hwnd hwnd_type filename ReadWriteMode mb_codec
|
|
| 728 | - `onException` IODevice.close hwnd
|
|
| 729 | - return (filename, h)
|
|
| 730 | - |
|
| 731 | -foreign import ccall "getTempFileNameErrorNo" c_getTempFileNameErrorNo
|
|
| 732 | - :: CWString -> CWString -> CWString -> CUInt -> Ptr CWchar -> IO Bool
|
|
| 733 | - |
|
| 734 | -foreign import ccall "__createUUIDTempFileErrNo" c_createUUIDTempFileErrNo
|
|
| 735 | - :: CWString -> CWString -> CWString -> Ptr CWString -> IO Bool
|
|
| 736 | - |
|
| 737 | -pathSeparator :: String -> Bool
|
|
| 738 | -pathSeparator template = any (\x-> x == '/' || x == '\\') template
|
|
| 739 | - |
|
| 740 | -output_flags = std_flags
|
|
| 741 | -#else /* else mingw32_HOST_OS */
|
|
| 742 | - findTempName = do
|
|
| 743 | - rs <- rand_string
|
|
| 744 | - let filename = prefix ++ rs ++ suffix
|
|
| 745 | - filepath = tmp_dir `combine` filename
|
|
| 746 | - r <- openNewFile filepath binary mode
|
|
| 747 | - case r of
|
|
| 748 | - FileExists -> findTempName
|
|
| 749 | - OpenNewError errno -> ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
|
|
| 750 | - NewFileCreated fd -> do
|
|
| 751 | - (fD,fd_type) <- FD.mkFD fd ReadWriteMode Nothing{-no stat-}
|
|
| 752 | - False{-is_socket-}
|
|
| 753 | - True{-is_nonblock-}
|
|
| 754 | - |
|
| 755 | - enc <- getLocaleEncoding
|
|
| 756 | - h <- POSIX.mkHandleFromFD fD fd_type filepath ReadWriteMode False{-set non-block-} (Just enc)
|
|
| 757 | - |
|
| 758 | - return (filepath, h)
|
|
| 759 | - |
|
| 760 | - where
|
|
| 761 | - -- XXX bits copied from System.FilePath, since that's not available here
|
|
| 762 | - combine a b
|
|
| 763 | - | null b = a
|
|
| 764 | - | null a = b
|
|
| 765 | - | pathSeparator [last a] = a ++ b
|
|
| 766 | - | otherwise = a ++ [pathSeparatorChar] ++ b
|
|
| 767 | - |
|
| 768 | -tempCounter :: IORef Int
|
|
| 769 | -tempCounter = unsafePerformIO $ newIORef 0
|
|
| 770 | -{-# NOINLINE tempCounter #-}
|
|
| 771 | - |
|
| 772 | --- build large digit-alike number
|
|
| 773 | -rand_string :: IO String
|
|
| 774 | -rand_string = do
|
|
| 775 | - r1 <- c_getpid
|
|
| 776 | - (r2, _) <- atomicModifyIORef'_ tempCounter (+1)
|
|
| 777 | - return $ show r1 ++ "-" ++ show r2
|
|
| 778 | - |
|
| 779 | -data OpenNewFileResult
|
|
| 780 | - = NewFileCreated CInt
|
|
| 781 | - | FileExists
|
|
| 782 | - | OpenNewError Errno
|
|
| 783 | - |
|
| 784 | -openNewFile :: FilePath -> Bool -> CMode -> IO OpenNewFileResult
|
|
| 785 | -openNewFile filepath binary mode = do
|
|
| 786 | - let oflags1 = rw_flags .|. o_EXCL
|
|
| 787 | - |
|
| 788 | - binary_flags
|
|
| 789 | - | binary = o_BINARY
|
|
| 790 | - | otherwise = 0
|
|
| 791 | - |
|
| 792 | - oflags = oflags1 .|. binary_flags
|
|
| 793 | - fd <- withFilePath filepath $ \ f ->
|
|
| 794 | - c_open f oflags mode
|
|
| 795 | - if fd < 0
|
|
| 796 | - then do
|
|
| 797 | - errno <- getErrno
|
|
| 798 | - case errno of
|
|
| 799 | - _ | errno == eEXIST -> return FileExists
|
|
| 800 | - _ -> return (OpenNewError errno)
|
|
| 801 | - else return (NewFileCreated fd)
|
|
| 802 | - |
|
| 803 | --- XXX Should use filepath library
|
|
| 804 | -pathSeparatorChar :: Char
|
|
| 805 | -pathSeparatorChar = '/'
|
|
| 806 | - |
|
| 807 | -pathSeparator :: String -> Bool
|
|
| 808 | -pathSeparator template = pathSeparatorChar `elem` template
|
|
| 809 | - |
|
| 810 | -output_flags = std_flags .|. o_CREAT
|
|
| 811 | -#endif /* mingw32_HOST_OS */
|
|
| 812 | - |
|
| 813 | --- XXX Copied from GHC.Handle
|
|
| 814 | -std_flags, output_flags, rw_flags :: CInt
|
|
| 815 | -std_flags = o_NONBLOCK .|. o_NOCTTY
|
|
| 816 | -rw_flags = output_flags .|. o_RDWR |
| ... | ... | @@ -7848,6 +7848,7 @@ module GHC.IO.Handle where |
| 7848 | 7848 | hGetEcho :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7849 | 7849 | hGetEncoding :: Handle -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe GHC.Internal.IO.Encoding.Types.TextEncoding)
|
| 7850 | 7850 | hGetLine :: Handle -> GHC.Internal.Types.IO GHC.Internal.Base.String
|
| 7851 | + hGetNewlineMode :: Handle -> GHC.Internal.Types.IO NewlineMode
|
|
| 7851 | 7852 | hGetPosn :: Handle -> GHC.Internal.Types.IO HandlePosn
|
| 7852 | 7853 | hIsClosed :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7853 | 7854 | hIsEOF :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -9883,7 +9884,7 @@ module System.Exit where |
| 9883 | 9884 | exitWith :: forall a. ExitCode -> GHC.Internal.Types.IO a
|
| 9884 | 9885 | |
| 9885 | 9886 | module System.IO where
|
| 9886 | - -- Safety: Safe
|
|
| 9887 | + -- Safety: Trustworthy
|
|
| 9887 | 9888 | type BufferMode :: *
|
| 9888 | 9889 | data BufferMode = NoBuffering | LineBuffering | BlockBuffering (GHC.Internal.Maybe.Maybe GHC.Internal.Types.Int)
|
| 9889 | 9890 | type FilePath :: *
|
| ... | ... | @@ -7820,6 +7820,7 @@ module GHC.IO.Handle where |
| 7820 | 7820 | hGetEcho :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7821 | 7821 | hGetEncoding :: Handle -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe GHC.Internal.IO.Encoding.Types.TextEncoding)
|
| 7822 | 7822 | hGetLine :: Handle -> GHC.Internal.Types.IO GHC.Internal.Base.String
|
| 7823 | + hGetNewlineMode :: Handle -> GHC.Internal.Types.IO NewlineMode
|
|
| 7823 | 7824 | hGetPosn :: Handle -> GHC.Internal.Types.IO HandlePosn
|
| 7824 | 7825 | hIsClosed :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7825 | 7826 | hIsEOF :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -9921,7 +9922,7 @@ module System.Exit where |
| 9921 | 9922 | exitWith :: forall a. ExitCode -> GHC.Internal.Types.IO a
|
| 9922 | 9923 | |
| 9923 | 9924 | module System.IO where
|
| 9924 | - -- Safety: Safe
|
|
| 9925 | + -- Safety: Trustworthy
|
|
| 9925 | 9926 | type BufferMode :: *
|
| 9926 | 9927 | data BufferMode = NoBuffering | LineBuffering | BlockBuffering (GHC.Internal.Maybe.Maybe GHC.Internal.Types.Int)
|
| 9927 | 9928 | type FilePath :: *
|
| ... | ... | @@ -8012,6 +8012,7 @@ module GHC.IO.Handle where |
| 8012 | 8012 | hGetEcho :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 8013 | 8013 | hGetEncoding :: Handle -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe GHC.Internal.IO.Encoding.Types.TextEncoding)
|
| 8014 | 8014 | hGetLine :: Handle -> GHC.Internal.Types.IO GHC.Internal.Base.String
|
| 8015 | + hGetNewlineMode :: Handle -> GHC.Internal.Types.IO NewlineMode
|
|
| 8015 | 8016 | hGetPosn :: Handle -> GHC.Internal.Types.IO HandlePosn
|
| 8016 | 8017 | hIsClosed :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 8017 | 8018 | hIsEOF :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -10163,7 +10164,7 @@ module System.Exit where |
| 10163 | 10164 | exitWith :: forall a. ExitCode -> GHC.Internal.Types.IO a
|
| 10164 | 10165 | |
| 10165 | 10166 | module System.IO where
|
| 10166 | - -- Safety: Safe
|
|
| 10167 | + -- Safety: Trustworthy
|
|
| 10167 | 10168 | type BufferMode :: *
|
| 10168 | 10169 | data BufferMode = NoBuffering | LineBuffering | BlockBuffering (GHC.Internal.Maybe.Maybe GHC.Internal.Types.Int)
|
| 10169 | 10170 | type FilePath :: *
|
| ... | ... | @@ -7848,6 +7848,7 @@ module GHC.IO.Handle where |
| 7848 | 7848 | hGetEcho :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7849 | 7849 | hGetEncoding :: Handle -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe GHC.Internal.IO.Encoding.Types.TextEncoding)
|
| 7850 | 7850 | hGetLine :: Handle -> GHC.Internal.Types.IO GHC.Internal.Base.String
|
| 7851 | + hGetNewlineMode :: Handle -> GHC.Internal.Types.IO NewlineMode
|
|
| 7851 | 7852 | hGetPosn :: Handle -> GHC.Internal.Types.IO HandlePosn
|
| 7852 | 7853 | hIsClosed :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 7853 | 7854 | hIsEOF :: Handle -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -9883,7 +9884,7 @@ module System.Exit where |
| 9883 | 9884 | exitWith :: forall a. ExitCode -> GHC.Internal.Types.IO a
|
| 9884 | 9885 | |
| 9885 | 9886 | module System.IO where
|
| 9886 | - -- Safety: Safe
|
|
| 9887 | + -- Safety: Trustworthy
|
|
| 9887 | 9888 | type BufferMode :: *
|
| 9888 | 9889 | data BufferMode = NoBuffering | LineBuffering | BlockBuffering (GHC.Internal.Maybe.Maybe GHC.Internal.Types.Int)
|
| 9889 | 9890 | type FilePath :: *
|