+
+#if defined(__NHC__)
+-- Assume a unix platform, where text and binary I/O are identical.
+openBinaryFile = openFile
+hSetBinaryMode _ _ = return ()
+#endif
+
+#ifndef __NHC__
+-- | The function creates a temporary file in ReadWrite mode.
+-- The created file isn\'t deleted automatically, so you need to delete it manually.
+openTempFile :: FilePath -- ^ Directory in which to create the file
+ -> String -- ^ File name template. If the template is \"foo.ext\" then
+ -- the created file will be \"fooXXX.ext\" where XXX is some
+ -- random number.
+ -> IO (FilePath, Handle)
+openTempFile tmp_dir template = openTempFile' "openTempFile" tmp_dir template False
+
+-- | Like 'openTempFile', but opens the file in binary mode. See 'openBinaryFile' for more comments.
+openBinaryTempFile :: FilePath -> String -> IO (FilePath, Handle)
+openBinaryTempFile tmp_dir template = openTempFile' "openBinaryTempFile" tmp_dir template True
+
+openTempFile' :: String -> FilePath -> String -> Bool -> IO (FilePath, Handle)
+openTempFile' loc tmp_dir template binary = do
+ pid <- c_getpid
+ findTempName pid
+ where
+ -- We split off the last extension, so we can use .foo.ext files
+ -- for temporary files (hidden on Unix OSes). Unfortunately we're
+ -- below filepath in the hierarchy here.
+ (prefix,suffix) =
+ case break (== '.') $ reverse template of
+ -- First case: template contains no '.'s. Just re-reverse it.
+ (rev_suffix, "") -> (reverse rev_suffix, "")
+ -- Second case: template contains at least one '.'. Strip the
+ -- dot from the prefix and prepend it to the suffix (if we don't
+ -- do this, the unique number will get added after the '.' and
+ -- thus be part of the extension, which is wrong.)
+ (rev_suffix, '.':rest) -> (reverse rest, '.':reverse rev_suffix)
+ -- Otherwise, something is wrong, because (break (== '.')) should
+ -- always return a pair with either the empty string or a string
+ -- beginning with '.' as the second component.
+ _ -> error "bug in System.IO.openTempFile"
+
+ oflags1 = rw_flags .|. o_EXCL
+
+ binary_flags
+ | binary = o_BINARY
+ | otherwise = 0
+
+ oflags = oflags1 .|. binary_flags
+
+ findTempName x = do
+ fd <- withCString filepath $ \ f ->
+ c_open f oflags 0o666
+ if fd < 0
+ then do
+ errno <- getErrno
+ if errno == eEXIST
+ then findTempName (x+1)
+ else ioError (errnoToIOError loc errno Nothing (Just tmp_dir))
+ else do
+ -- XXX We want to tell fdToHandle what the filepath is,
+ -- as any exceptions etc will only be able to report the
+ -- fd currently
+ h <- fdToHandle fd
+ `ExceptionBase.catchException` \e -> do c_close fd; throw e
+ return (filepath, h)
+ where
+ filename = prefix ++ show x ++ suffix
+ filepath = tmp_dir ++ [pathSeparator] ++ filename
+#if __HUGS__
+ fdToHandle fd = openFd (fromIntegral fd) False ReadWriteMode binary
+#endif
+
+-- XXX Should use filepath library
+pathSeparator :: Char
+#ifdef mingw32_HOST_OS
+pathSeparator = '\\'
+#else
+pathSeparator = '/'
+#endif
+
+-- XXX Copied from GHC.Handle
+std_flags = o_NONBLOCK .|. o_NOCTTY
+output_flags = std_flags .|. o_CREAT
+read_flags = std_flags .|. o_RDONLY
+write_flags = output_flags .|. o_WRONLY
+rw_flags = output_flags .|. o_RDWR
+append_flags = write_flags .|. o_APPEND
+#endif
+
+-- $locking
+-- Implementations should enforce as far as possible, at least locally to the
+-- Haskell process, multiple-reader single-writer locking on files.
+-- That is, /there may either be many handles on the same file which manage
+-- input, or just one handle on the file which manages output/. If any
+-- open or semi-closed handle is managing a file for output, no new
+-- handle can be allocated for that file. If any open or semi-closed
+-- handle is managing a file for input, new handles can only be allocated
+-- if they do not manage output. Whether two files are the same is
+-- implementation-dependent, but they should normally be the same if they
+-- have the same absolute path name and neither has been renamed, for
+-- example.
+--
+-- /Warning/: the 'readFile' operation holds a semi-closed handle on
+-- the file until the entire contents of the file have been consumed.
+-- It follows that an attempt to write to a file (using 'writeFile', for
+-- example) that was earlier opened by 'readFile' will usually result in
+-- failure with 'System.IO.Error.isAlreadyInUseError'.
+
+-- -----------------------------------------------------------------------------
+-- Utils
+
+#ifdef __GLASGOW_HASKELL__
+-- Copied here to avoid recursive dependency with Control.Exception
+bracket
+ :: IO a -- ^ computation to run first (\"acquire resource\")
+ -> (a -> IO b) -- ^ computation to run last (\"release resource\")
+ -> (a -> IO c) -- ^ computation to run in-between
+ -> IO c -- returns the value from the in-between computation
+bracket before after thing =
+ block (do
+ a <- before
+ r <- catchException
+ (unblock (thing a))
+ (\e -> do { after a; throw e })
+ after a
+ return r
+ )