{-
Copyright (C) 2004-2005 John Goerzen <jgoerzen@complete.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-}{- |
Module : System.IO.Utils
Copyright : Copyright (C) 2004-2006 John Goerzen
License : GNU GPL, version 2 or above
Maintainer : John Goerzen <jgoerzen@complete.org>
Stability : provisional
Portability: portable
-}moduleSystem.IO.Utils(-- * Entire File Handle Utilities-- ** Opened Handle Data CopyinghCopy,hCopyProgress,hLineCopy,lineCopy,-- ** Disk File Data CopyingcopyFileLinesToFile,-- * Line Processing UtilitieshPutStrLns,hGetLines,-- * Lazy Interaction-- ** Character-basedhInteract,-- ** Line-basedhLineInteract,lineInteract,-- ** Misc. LazylazyMapM,-- * OptimizationsoptimizeForBatch,optimizeForInteraction)whereimportSystem.IO.Unsafe(unsafeInterleaveIO)importSystem.IOimportData.List(genericLength)importSystem.IO.HVIO{- | Given a list of strings, output a line containing each item, adding
newlines as appropriate. The list is not expected to have newlines already.
-}hPutStrLns::HVIOa=>a->[String]->IO()hPutStrLnsh=mapM_$vPutStrLnh{- | Given a handle, returns a list of all the lines in that handle.
Thanks to lazy evaluation, this list does not have to be read all at once.
Combined with 'hPutStrLns', this can make a powerful way to develop
filters. See the 'lineInteract' function for more on that concept.
Example:
> main = do
> l <- hGetLines stdin
> hPutStrLns stdout $ filter (startswith "1") l
-}-- FIXME: does hGetContents h >>= return . lines not work?hGetLines::HVIOa=>a->IO[String]hGetLinesh=unsafeInterleaveIO(doieof<-vIsEOFhif(ieof)thenreturn[]elsedoline<-vGetLinehremainder<-hGetLineshreturn(line:remainder)){- | This is similar to the built-in 'System.IO.interact', but works
on any handle, not just stdin and stdout.
In other words:
> interact = hInteract stdin stdout
-}hInteract::(HVIOa,HVIOb)=>a->b->(String->String)->IO()hInteractfinputfoutputfunc=docontent<-vGetContentsfinputvPutStrfoutput(funccontent){- | Line-based interaction. This is similar to wrapping your
interact functions with 'lines' and 'unlines'. This equality holds:
> lineInteract = hLineInteract stdin stdout
Here's an example:
> main = lineInteract (filter (startswith "1"))
This will act as a simple version of grep -- all lines that start with 1
will be displayed; all others will be ignored.
-}lineInteract::([String]->[String])->IO()lineInteract=hLineInteractstdinstdout{- | Line-based interaction over arbitrary handles. This is similar
to wrapping hInteract with 'lines' and 'unlines'.
One could view this function like this:
> hLineInteract finput foutput func =
> let newf = unlines . func . lines in
> hInteract finput foutput newf
Though the actual implementation is this for efficiency:
> hLineInteract finput foutput func =
> do
> lines <- hGetLines finput
> hPutStrLns foutput (func lines)
-}hLineInteract::(HVIOa,HVIOb)=>a->b->([String]->[String])->IO()hLineInteractfinputfoutputfunc=dolines<-hGetLinesfinputhPutStrLnsfoutput(funclines){- | Copies from one handle to another in raw mode (using
hGetContents).
-}hCopy::(HVIOa,HVIOb)=>a->b->IO()hCopyhinhout=doc<-vGetContentshinvPutStrhoutc{- | Copies from one handle to another in raw mode (using hGetContents).
Takes a function to provide progress updates to the user.
-}hCopyProgress::(HVIOb,HVIOc,Integrala)=>b-- ^ Input handle->c-- ^ Output handle->(Maybea->Integer->Bool->IO())-- ^ Progress function -- the bool is always False unless this is the final call->Int-- Block size->Maybea-- Estimated file size (passed to func)->IOInteger-- Number of bytes copiedhCopyProgresshinhoutfuncbsizeestsize=letcopyFunc::String->Integer->IOIntegercopyFunc[]count=returncountcopyFuncindatacount=letblock=takebsizeindataremainder=dropbsizeindatanewcount=count+(genericLengthblock)indovPutStrhoutblockfuncestsizecountFalsecopyFuncremaindernewcountindoc<-vGetContentshinbytes<-copyFuncc0funcestsizebytesTruereturnbytes{- | Copies from one handle to another in text mode (with lines).
Like 'hBlockCopy', this implementation is nice:
> hLineCopy hin hout = hLineInteract hin hout id
-}hLineCopy::(HVIOa,HVIOb)=>a->b->IO()hLineCopyhinhout=hLineInteracthinhoutid{- | Copies from 'stdin' to 'stdout' using lines. An alias for 'hLineCopy'
over 'stdin' and 'stdout'. -}lineCopy::IO()lineCopy=hLineCopystdinstdout{- | Copies one filename to another in text mode.
Please note that the Unix permission bits are set at a default; you may
need to adjust them after the copy yourself.
This function is implemented using 'hLineCopy' internally. -}copyFileLinesToFile::FilePath->FilePath->IO()copyFileLinesToFileinfnoutfn=dohin<-openFileinfnReadModehout<-openFileoutfnWriteModehLineCopyhinhouthClosehinhClosehoutreturn(){- | Sets stdin and stdout to be block-buffered. This can save a huge amount
of system resources since far fewer syscalls are made, and can make programs
run much faster. -}optimizeForBatch::IO()optimizeForBatch=dohSetBufferingstdin(BlockBuffering(Just4096))hSetBufferingstdout(BlockBuffering(Just4096)){- | Sets stdin and stdout to be line-buffered. This saves resources
on stdout, but not many on stdin, since it it still looking for newlines. -}optimizeForInteraction::IO()optimizeForInteraction=dohSetBufferingstdinLineBufferinghSetBufferingstdoutLineBuffering{- | Applies a given function to every item in a list, and returns
the new list. Unlike the system\'s mapM, items are evaluated lazily. -}lazyMapM::(a->IOb)->[a]->IO[b]lazyMapM_[]=return[]lazyMapMconv(x:xs)=dothis<-convxnext<-unsafeInterleaveIO$lazyMapMconvxsreturn(this:next)