cabal-install-bundle (empty) → 0.10.2
raw patch · 188 files changed
+50067/−0 lines, 188 filesdep +Cabaldep +arraydep +basesetup-changed
Dependencies added: Cabal, array, base, bytestring, containers, directory, filepath, old-time, pretty, process, time, unix
Files
- Codec/Compression/GZip.hs +130/−0
- Codec/Compression/Zlib.hs +118/−0
- Codec/Compression/Zlib/Internal.hs +425/−0
- Codec/Compression/Zlib/Raw.hs +70/−0
- Codec/Compression/Zlib/Stream.hsc +895/−0
- Control/Applicative/Backwards.hs +49/−0
- Control/Applicative/Lift.hs +68/−0
- Control/Monad/Cont.hs +168/−0
- Control/Monad/Cont/Class.hs +130/−0
- Control/Monad/Error.hs +145/−0
- Control/Monad/Error/Class.hs +151/−0
- Control/Monad/IO/Class.hs +37/−0
- Control/Monad/Identity.hs +44/−0
- Control/Monad/List.hs +25/−0
- Control/Monad/RWS.hs +24/−0
- Control/Monad/RWS/Class.hs +58/−0
- Control/Monad/RWS/Lazy.hs +52/−0
- Control/Monad/RWS/Strict.hs +52/−0
- Control/Monad/Reader.hs +143/−0
- Control/Monad/Reader/Class.hs +164/−0
- Control/Monad/State.hs +25/−0
- Control/Monad/State/Class.hs +152/−0
- Control/Monad/State/Lazy.hs +128/−0
- Control/Monad/State/Strict.hs +128/−0
- Control/Monad/Trans.hs +34/−0
- Control/Monad/Trans/Class.hs +123/−0
- Control/Monad/Trans/Cont.hs +137/−0
- Control/Monad/Trans/Error.hs +267/−0
- Control/Monad/Trans/Identity.hs +89/−0
- Control/Monad/Trans/List.hs +95/−0
- Control/Monad/Trans/Maybe.hs +126/−0
- Control/Monad/Trans/RWS.hs +21/−0
- Control/Monad/Trans/RWS/Lazy.hs +317/−0
- Control/Monad/Trans/RWS/Strict.hs +317/−0
- Control/Monad/Trans/Reader.hs +180/−0
- Control/Monad/Trans/State.hs +29/−0
- Control/Monad/Trans/State/Lazy.hs +366/−0
- Control/Monad/Trans/State/Strict.hs +366/−0
- Control/Monad/Trans/Writer.hs +21/−0
- Control/Monad/Trans/Writer/Lazy.hs +211/−0
- Control/Monad/Trans/Writer/Strict.hs +211/−0
- Control/Monad/Writer.hs +24/−0
- Control/Monad/Writer/Class.hs +162/−0
- Control/Monad/Writer/Lazy.hs +49/−0
- Control/Monad/Writer/Strict.hs +49/−0
- Data/Functor/Compose.hs +40/−0
- Data/Functor/Constant.hs +35/−0
- Data/Functor/Identity.hs +57/−0
- Data/Functor/Product.hs +58/−0
- Data/Functor/Reverse.hs +54/−0
- Distribution/Client/BuildReports/Anonymous.hs +311/−0
- Distribution/Client/BuildReports/Storage.hs +127/−0
- Distribution/Client/BuildReports/Types.hs +44/−0
- Distribution/Client/BuildReports/Upload.hs +74/−0
- Distribution/Client/Check.hs +85/−0
- Distribution/Client/Config.hs +532/−0
- Distribution/Client/Configure.hs +191/−0
- Distribution/Client/Dependency.hs +441/−0
- Distribution/Client/Dependency/Modular.hs +58/−0
- Distribution/Client/Dependency/Modular/Assignment.hs +146/−0
- Distribution/Client/Dependency/Modular/Builder.hs +143/−0
- Distribution/Client/Dependency/Modular/Configured.hs +10/−0
- Distribution/Client/Dependency/Modular/ConfiguredConversion.hs +40/−0
- Distribution/Client/Dependency/Modular/Dependency.hs +194/−0
- Distribution/Client/Dependency/Modular/Explore.hs +148/−0
- Distribution/Client/Dependency/Modular/Flag.hs +69/−0
- Distribution/Client/Dependency/Modular/Index.hs +33/−0
- Distribution/Client/Dependency/Modular/IndexConversion.hs +185/−0
- Distribution/Client/Dependency/Modular/Log.hs +96/−0
- Distribution/Client/Dependency/Modular/Message.hs +97/−0
- Distribution/Client/Dependency/Modular/PSQ.hs +102/−0
- Distribution/Client/Dependency/Modular/Package.hs +113/−0
- Distribution/Client/Dependency/Modular/Preference.hs +258/−0
- Distribution/Client/Dependency/Modular/Solver.hs +52/−0
- Distribution/Client/Dependency/Modular/Tree.hs +146/−0
- Distribution/Client/Dependency/Modular/Validate.hs +232/−0
- Distribution/Client/Dependency/Modular/Version.hs +43/−0
- Distribution/Client/Dependency/TopDown.hs +776/−0
- Distribution/Client/Dependency/TopDown/Constraints.hs +316/−0
- Distribution/Client/Dependency/TopDown/Types.hs +93/−0
- Distribution/Client/Dependency/Types.hs +115/−0
- Distribution/Client/Fetch.hs +171/−0
- Distribution/Client/FetchUtils.hs +193/−0
- Distribution/Client/GZipUtils.hs +44/−0
- Distribution/Client/Haddock.hs +105/−0
- Distribution/Client/HttpUtils.hs +197/−0
- Distribution/Client/IndexUtils.hs +265/−0
- Distribution/Client/Init.hs +556/−0
- Distribution/Client/Init/Heuristics.hs +191/−0
- Distribution/Client/Init/Licenses.hs +1722/−0
- Distribution/Client/Init/Types.hs +152/−0
- Distribution/Client/Install.hs +875/−0
- Distribution/Client/InstallPlan.hs +511/−0
- Distribution/Client/InstallSymlink.hs +238/−0
- Distribution/Client/List.hs +528/−0
- Distribution/Client/PackageIndex.hs +478/−0
- Distribution/Client/PackageUtils.hs +34/−0
- Distribution/Client/Setup.hs +1009/−0
- Distribution/Client/SetupWrapper.hs +320/−0
- Distribution/Client/SrcDist.hs +80/−0
- Distribution/Client/Tar.hs +903/−0
- Distribution/Client/Targets.hs +657/−0
- Distribution/Client/Types.hs +163/−0
- Distribution/Client/Unpack.hs +121/−0
- Distribution/Client/Update.hs +81/−0
- Distribution/Client/Upload.hs +190/−0
- Distribution/Client/Utils.hs +60/−0
- Distribution/Client/Win32SelfUpgrade.hs +222/−0
- Distribution/Client/World.hs +173/−0
- Distribution/Compat/Exception.hs +56/−0
- Distribution/Compat/FilePerms.hs +40/−0
- Main.hs +403/−0
- Network.hs +468/−0
- Network/BSD.hsc +587/−0
- Network/Browser.hs +1062/−0
- Network/BufferType.hs +164/−0
- Network/HTTP.hs +229/−0
- Network/HTTP/Auth.hs +216/−0
- Network/HTTP/Base.hs +895/−0
- Network/HTTP/Base64.hs +282/−0
- Network/HTTP/Cookie.hs +141/−0
- Network/HTTP/HandleStream.hs +246/−0
- Network/HTTP/Headers.hs +306/−0
- Network/HTTP/MD5Aux.hs +342/−0
- Network/HTTP/Proxy.hs +169/−0
- Network/HTTP/Stream.hs +230/−0
- Network/HTTP/Utils.hs +111/−0
- Network/Socket.hsc +2236/−0
- Network/Socket/ByteString.hsc +383/−0
- Network/Socket/ByteString/IOVec.hsc +28/−0
- Network/Socket/ByteString/Internal.hs +62/−0
- Network/Socket/ByteString/Lazy.hsc +154/−0
- Network/Socket/ByteString/MsgHdr.hsc +47/−0
- Network/Socket/Internal.hsc +651/−0
- Network/Stream.hs +91/−0
- Network/StreamDebugger.hs +103/−0
- Network/StreamSocket.hs +93/−0
- Network/TCP.hs +411/−0
- Network/URI.hs +1289/−0
- Setup.hs +6/−0
- System/Random.hs +567/−0
- Text/Parsec.hs +36/−0
- Text/Parsec/ByteString.hs +46/−0
- Text/Parsec/ByteString/Lazy.hs +45/−0
- Text/Parsec/Char.hs +135/−0
- Text/Parsec/Combinator.hs +277/−0
- Text/Parsec/Error.hs +201/−0
- Text/Parsec/Expr.hs +166/−0
- Text/Parsec/Language.hs +150/−0
- Text/Parsec/Perm.hs +181/−0
- Text/Parsec/Pos.hs +130/−0
- Text/Parsec/Prim.hs +740/−0
- Text/Parsec/String.hs +45/−0
- Text/Parsec/Text.hs +31/−0
- Text/Parsec/Text/Lazy.hs +31/−0
- Text/Parsec/Token.hs +722/−0
- Text/ParserCombinators/Parsec.hs +41/−0
- Text/ParserCombinators/Parsec/Char.hs +40/−0
- Text/ParserCombinators/Parsec/Combinator.hs +42/−0
- Text/ParserCombinators/Parsec/Error.hs +40/−0
- Text/ParserCombinators/Parsec/Expr.hs +42/−0
- Text/ParserCombinators/Parsec/Language.hs +28/−0
- Text/ParserCombinators/Parsec/Perm.hs +24/−0
- Text/ParserCombinators/Parsec/Pos.hs +35/−0
- Text/ParserCombinators/Parsec/Prim.hs +65/−0
- Text/ParserCombinators/Parsec/Token.hs +23/−0
- cabal-install-bundle.cabal +205/−0
- cbits/HsNet.c +8/−0
- cbits/ancilData.c +237/−0
- cbits/asyncAccept.c +72/−0
- cbits/crc32.h +441/−0
- cbits/deflate.h +342/−0
- cbits/inffast.h +11/−0
- cbits/inffixed.h +94/−0
- cbits/inflate.h +122/−0
- cbits/inftrees.h +62/−0
- cbits/initWinSock.c +63/−0
- cbits/trees.h +128/−0
- cbits/winSockErr.c +76/−0
- cbits/zutil.h +274/−0
- config.guess +1497/−0
- config.sub +1608/−0
- configure +6297/−0
- configure.ac +176/−0
- include/HsNet.h +195/−0
- include/HsNetworkConfig.h.in +147/−0
- install-sh +295/−0
- network.buildinfo.in +5/−0
+ Codec/Compression/GZip.hs view
@@ -0,0 +1,130 @@+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) 2006-2008 Duncan Coutts+-- License : BSD-style+--+-- Maintainer : duncan@haskell.org+-- Stability : provisional+-- Portability : portable (H98 + FFI)+--+-- Compression and decompression of data streams in the gzip format.+--+-- The format is described in detail in RFC #1952:+-- <http://www.ietf.org/rfc/rfc1952.txt>+--+-- See also the zlib home page: <http://zlib.net/>+--+-----------------------------------------------------------------------------+module Codec.Compression.GZip (++ -- | This module provides pure functions for compressing and decompressing+ -- streams of data in the gzip format and represented by lazy 'ByteString's.+ -- This makes it easy to use either in memory or with disk or network IO.+ --+ -- For example a simple gzip compression program is just:+ --+ -- > import qualified Data.ByteString.Lazy as ByteString+ -- > import qualified Codec.Compression.GZip as GZip+ -- >+ -- > main = ByteString.interact GZip.compress+ --+ -- Or you could lazily read in and decompress a @.gz@ file using:+ --+ -- > content <- fmap GZip.decompress (readFile file)+ --++ -- * Simple compression and decompression+ compress,+ decompress,++ -- * Extended api with control over compression parameters+ compressWith,+ decompressWith,++ CompressParams(..), defaultCompressParams,+ DecompressParams(..), defaultDecompressParams,++ -- ** The compression parameter types+ CompressionLevel(..),+ defaultCompression,+ noCompression,+ bestSpeed,+ bestCompression,+ compressionLevel,+ Method(..),+ deflateMethod,+ WindowBits(..),+ defaultWindowBits,+ windowBits,+ MemoryLevel(..),+ defaultMemoryLevel,+ minMemoryLevel,+ maxMemoryLevel,+ memoryLevel,+ CompressionStrategy(..),+ defaultStrategy,+ filteredStrategy,+ huffmanOnlyStrategy,++ ) where++import Data.ByteString.Lazy (ByteString)++import qualified Codec.Compression.Zlib.Internal as Internal+import Codec.Compression.Zlib.Internal hiding (compress, decompress)+++-- | Decompress a stream of data in the gzip format.+--+-- There are a number of errors that can occur. In each case an exception will+-- be thrown. The possible error conditions are:+--+-- * if the stream does not start with a valid gzip header+--+-- * if the compressed stream is corrupted+--+-- * if the compressed stream ends permaturely+--+-- Note that the decompression is performed /lazily/. Errors in the data stream+-- may not be detected until the end of the stream is demanded (since it is+-- only at the end that the final checksum can be checked). If this is+-- important to you, you must make sure to consume the whole decompressed+-- stream before doing any IO action that depends on it.+--+decompress :: ByteString -> ByteString+decompress = decompressWith defaultDecompressParams+++-- | Like 'decompress' but with the ability to specify various decompression+-- parameters. Typical usage:+--+-- > decompressWith defaultCompressParams { ... }+--+decompressWith :: DecompressParams -> ByteString -> ByteString+decompressWith = Internal.decompress gzipFormat+++-- | Compress a stream of data into the gzip format.+--+-- This uses the default compression parameters. In partiular it uses the+-- default compression level which favours a higher compression ratio over+-- compression speed, though it does not use the maximum compression level.+--+-- Use 'compressWith' to adjust the compression level or other compression+-- parameters.+--+compress :: ByteString -> ByteString+compress = compressWith defaultCompressParams+++-- | Like 'compress' but with the ability to specify various compression+-- parameters. Typical usage:+--+-- > compressWith defaultCompressParams { ... }+--+-- In particular you can set the compression level:+--+-- > compressWith defaultCompressParams { compressLevel = BestCompression }+--+compressWith :: CompressParams -> ByteString -> ByteString+compressWith = Internal.compress gzipFormat
+ Codec/Compression/Zlib.hs view
@@ -0,0 +1,118 @@+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) 2006-2008 Duncan Coutts+-- License : BSD-style+--+-- Maintainer : duncan@haskell.org+-- Stability : provisional+-- Portability : portable (H98 + FFI)+--+-- Compression and decompression of data streams in the zlib format.+--+-- The format is described in detail in RFC #1950:+-- <http://www.ietf.org/rfc/rfc1950.txt>+--+-- See also the zlib home page: <http://zlib.net/>+--+-----------------------------------------------------------------------------+module Codec.Compression.Zlib (++ -- | This module provides pure functions for compressing and decompressing+ -- streams of data in the zlib format and represented by lazy 'ByteString's.+ -- This makes it easy to use either in memory or with disk or network IO.++ -- * Simple compression and decompression+ compress,+ decompress,++ -- * Extended api with control over compression parameters+ compressWith,+ decompressWith,++ CompressParams(..), defaultCompressParams,+ DecompressParams(..), defaultDecompressParams,++ -- ** The compression parameter types+ CompressionLevel(..),+ defaultCompression,+ noCompression,+ bestSpeed,+ bestCompression,+ compressionLevel,+ Method(..),+ deflateMethod,+ WindowBits(..),+ defaultWindowBits,+ windowBits,+ MemoryLevel(..),+ defaultMemoryLevel,+ minMemoryLevel,+ maxMemoryLevel,+ memoryLevel,+ CompressionStrategy(..),+ defaultStrategy,+ filteredStrategy,+ huffmanOnlyStrategy,++ ) where++import Data.ByteString.Lazy (ByteString)++import qualified Codec.Compression.Zlib.Internal as Internal+import Codec.Compression.Zlib.Internal hiding (compress, decompress)+++-- | Decompress a stream of data in the zlib format.+--+-- There are a number of errors that can occur. In each case an exception will+-- be thrown. The possible error conditions are:+--+-- * if the stream does not start with a valid gzip header+--+-- * if the compressed stream is corrupted+--+-- * if the compressed stream ends permaturely+--+-- Note that the decompression is performed /lazily/. Errors in the data stream+-- may not be detected until the end of the stream is demanded (since it is+-- only at the end that the final checksum can be checked). If this is+-- important to you, you must make sure to consume the whole decompressed+-- stream before doing any IO action that depends on it.+--+decompress :: ByteString -> ByteString+decompress = decompressWith defaultDecompressParams+++-- | Like 'decompress' but with the ability to specify various decompression+-- parameters. Typical usage:+--+-- > decompressWith defaultCompressParams { ... }+--+decompressWith :: DecompressParams -> ByteString -> ByteString+decompressWith = Internal.decompress zlibFormat+++-- | Compress a stream of data into the zlib format.+--+-- This uses the default compression parameters. In partiular it uses the+-- default compression level which favours a higher compression ratio over+-- compression speed, though it does not use the maximum compression level.+--+-- Use 'compressWith' to adjust the compression level or other compression+-- parameters.+--+compress :: ByteString -> ByteString+compress = compressWith defaultCompressParams+++-- | Like 'compress' but with the ability to specify various compression+-- parameters. Typical usage:+--+-- > compressWith defaultCompressParams { ... }+--+-- In particular you can set the compression level:+--+-- > compressWith defaultCompressParams { compressLevel = BestCompression }+--+compressWith :: CompressParams -> ByteString -> ByteString+compressWith = Internal.compress zlibFormat
+ Codec/Compression/Zlib/Internal.hs view
@@ -0,0 +1,425 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) 2006-2008 Duncan Coutts+-- License : BSD-style+--+-- Maintainer : duncan@haskell.org+-- Stability : provisional+-- Portability : portable (H98 + FFI)+--+-- Pure stream based interface to lower level zlib wrapper+--+-----------------------------------------------------------------------------+module Codec.Compression.Zlib.Internal (++ -- * Compression+ compress,+ CompressParams(..),+ defaultCompressParams,++ -- * Decompression+ decompress,+ DecompressParams(..),+ defaultDecompressParams,++ -- * The compression parameter types+ Stream.Format(..),+ Stream.gzipFormat,+ Stream.zlibFormat,+ Stream.rawFormat,+ Stream.gzipOrZlibFormat,+ Stream.CompressionLevel(..),+ Stream.defaultCompression,+ Stream.noCompression,+ Stream.bestSpeed,+ Stream.bestCompression,+ Stream.compressionLevel,+ Stream.Method(..),+ Stream.deflateMethod,+ Stream.WindowBits(..),+ Stream.defaultWindowBits,+ Stream.windowBits,+ Stream.MemoryLevel(..),+ Stream.defaultMemoryLevel,+ Stream.minMemoryLevel,+ Stream.maxMemoryLevel,+ Stream.memoryLevel,+ Stream.CompressionStrategy(..),+ Stream.defaultStrategy,+ Stream.filteredStrategy,+ Stream.huffmanOnlyStrategy,++ -- * Low-level API to get explicit error reports+ decompressWithErrors,+ DecompressStream(..),+ DecompressError(..),+ foldDecompressStream,+ fromDecompressStream,+ ) where++import Prelude hiding (length)+import Control.Monad (when)+import Control.Exception (assert)+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Internal as L+import qualified Data.ByteString.Internal as S++import qualified Codec.Compression.Zlib.Stream as Stream+import Codec.Compression.Zlib.Stream (Stream)++-- | The full set of parameters for compression. The defaults are+-- 'defaultCompressParams'.+--+-- The 'compressBufferSize' is the size of the first output buffer containing+-- the compressed data. If you know an approximate upper bound on the size of+-- the compressed data then setting this parameter can save memory. The default+-- compression output buffer size is @16k@. If your extimate is wrong it does+-- not matter too much, the default buffer size will be used for the remaining+-- chunks.+--+data CompressParams = CompressParams {+ compressLevel :: !Stream.CompressionLevel,+ compressMethod :: !Stream.Method,+ compressWindowBits :: !Stream.WindowBits,+ compressMemoryLevel :: !Stream.MemoryLevel,+ compressStrategy :: !Stream.CompressionStrategy,+ compressBufferSize :: !Int+}++-- | The full set of parameters for decompression. The defaults are+-- 'defaultDecompressParams'.+--+-- The 'decompressBufferSize' is the size of the first output buffer,+-- containing the uncompressed data. If you know an exact or approximate upper+-- bound on the size of the decompressed data then setting this parameter can+-- save memory. The default decompression output buffer size is @32k@. If your+-- extimate is wrong it does not matter too much, the default buffer size will+-- be used for the remaining chunks.+--+-- One particular use case for setting the 'decompressBufferSize' is if you+-- know the exact size of the decompressed data and want to produce a strict+-- 'Data.ByteString.ByteString'. The compression and deccompression functions+-- use lazy 'Data.ByteString.Lazy.ByteString's but if you set the+-- 'decompressBufferSize' correctly then you can generate a lazy+-- 'Data.ByteString.Lazy.ByteString' with exactly one chunk, which can be+-- converted to a strict 'Data.ByteString.ByteString' in @O(1)@ time using+-- @'Data.ByteString.concat' . 'Data.ByteString.Lazy.toChunks'@.+--+data DecompressParams = DecompressParams {+ decompressWindowBits :: !Stream.WindowBits,+ decompressBufferSize :: !Int+}++-- | The default set of parameters for compression. This is typically used with+-- the @compressWith@ function with specific parameters overridden.+--+defaultCompressParams :: CompressParams+defaultCompressParams = CompressParams {+ compressLevel = Stream.defaultCompression,+ compressMethod = Stream.deflateMethod,+ compressWindowBits = Stream.defaultWindowBits,+ compressMemoryLevel = Stream.defaultMemoryLevel,+ compressStrategy = Stream.defaultStrategy,+ compressBufferSize = defaultCompressBufferSize+}++-- | The default set of parameters for decompression. This is typically used with+-- the @compressWith@ function with specific parameters overridden.+--+defaultDecompressParams :: DecompressParams+defaultDecompressParams = DecompressParams {+ decompressWindowBits = Stream.defaultWindowBits,+ decompressBufferSize = defaultDecompressBufferSize+}++-- | The default chunk sizes for the output of compression and decompression+-- are 16k and 32k respectively (less a small accounting overhead).+--+defaultCompressBufferSize, defaultDecompressBufferSize :: Int+defaultCompressBufferSize = 16 * 1024 - L.chunkOverhead+defaultDecompressBufferSize = 32 * 1024 - L.chunkOverhead++-- | A sequence of chunks of data produced from decompression.+--+-- The difference from a simple list is that it contains a representation of+-- errors as data rather than as exceptions. This allows you to handle error+-- conditions explicitly.+--+data DecompressStream = StreamEnd+ | StreamChunk S.ByteString DecompressStream++ -- | An error code and a human readable error message.+ | StreamError DecompressError String++-- | The possible error cases when decompressing a stream.+--+data DecompressError =+ -- | The compressed data stream ended prematurely. This may happen if the+ -- input data stream was truncated.+ TruncatedInput++ -- | It is possible to do zlib compression with a custom dictionary. This+ -- allows slightly higher compression ratios for short files. However such+ -- compressed streams require the same dictionary when decompressing. This+ -- zlib binding does not currently support custom dictionaries. This error+ -- is for when we encounter a compressed stream that needs a dictionary.+ | DictionaryRequired++ -- | If the compressed data stream is corrupted in any way then you will+ -- get this error, for example if the input data just isn't a compressed+ -- zlib data stream. In particular if the data checksum turns out to be+ -- wrong then you will get all the decompressed data but this error at the+ -- end, instead of the normal sucessful 'StreamEnd'.+ | DataError++-- | Fold an 'DecompressionStream'. Just like 'foldr' but with an extra error+-- case. For example to convert to a list and translate the errors into+-- exceptions:+--+-- > foldDecompressStream (:) [] (\code msg -> error msg)+--+foldDecompressStream :: (S.ByteString -> a -> a) -> a+ -> (DecompressError -> String -> a)+ -> DecompressStream -> a+foldDecompressStream chunk end err = fold+ where+ fold StreamEnd = end+ fold (StreamChunk bs stream) = chunk bs (fold stream)+ fold (StreamError code msg) = err code msg++-- | Convert a 'DecompressStream' to a lazy 'ByteString'. If any decompression+-- errors are encountered then they are thrown as exceptions.+--+-- This is a special case of 'foldDecompressStream'.+--+fromDecompressStream :: DecompressStream -> L.ByteString+fromDecompressStream =+ foldDecompressStream L.Chunk L.Empty+ (\_code msg -> error ("Codec.Compression.Zlib: " ++ msg))++--TODO: throw DecompressError as an Exception class type and document that it+-- does this.++-- | Compress a data stream.+--+-- There are no expected error conditions. All input data streams are valid. It+-- is possible for unexpected errors to occur, such as running out of memory,+-- or finding the wrong version of the zlib C library, these are thrown as+-- exceptions.+--+compress+ :: Stream.Format+ -> CompressParams+ -> L.ByteString+ -> L.ByteString+compress format+ (CompressParams compLevel method bits memLevel strategy initChunkSize)+ input =+ L.fromChunks $ Stream.run $ do+ Stream.deflateInit format compLevel method bits memLevel strategy+ case L.toChunks input of+ [] -> fillBuffers 20 [] --gzip header is 20 bytes, others even smaller+ S.PS inFPtr offset length : chunks -> do+ Stream.pushInputBuffer inFPtr offset length+ fillBuffers initChunkSize chunks++ where+ -- we flick between two states:+ -- * where one or other buffer is empty+ -- - in which case we refill one or both+ -- * where both buffers are non-empty+ -- - in which case we compress until a buffer is empty++ fillBuffers :: Int+ -> [S.ByteString]+ -> Stream [S.ByteString]+ fillBuffers outChunkSize inChunks = do+#ifdef DEBUG+ Stream.consistencyCheck+#endif++ -- in this state there are two possabilities:+ -- * no outbut buffer space is available+ -- - in which case we must make more available+ -- * no input buffer is available+ -- - in which case we must supply more+ inputBufferEmpty <- Stream.inputBufferEmpty+ outputBufferFull <- Stream.outputBufferFull++ assert (inputBufferEmpty || outputBufferFull) $ return ()++ when outputBufferFull $ do+ outFPtr <- Stream.unsafeLiftIO (S.mallocByteString outChunkSize)+ Stream.pushOutputBuffer outFPtr 0 outChunkSize++ if inputBufferEmpty+ then case inChunks of+ [] -> drainBuffers []+ S.PS inFPtr offset length : inChunks' -> do+ Stream.pushInputBuffer inFPtr offset length+ drainBuffers inChunks'+ else drainBuffers inChunks+++ drainBuffers ::+ [S.ByteString]+ -> Stream [S.ByteString]+ drainBuffers inChunks = do++ inputBufferEmpty' <- Stream.inputBufferEmpty+ outputBufferFull' <- Stream.outputBufferFull+ assert(not outputBufferFull'+ && (null inChunks || not inputBufferEmpty')) $ return ()+ -- this invariant guarantees we can always make forward progress+ -- and that therefore a BufferError is impossible++ let flush = if null inChunks then Stream.Finish else Stream.NoFlush+ status <- Stream.deflate flush++ case status of+ Stream.Ok -> do+ outputBufferFull <- Stream.outputBufferFull+ if outputBufferFull+ then do (outFPtr, offset, length) <- Stream.popOutputBuffer+ outChunks <- Stream.unsafeInterleave+ (fillBuffers defaultCompressBufferSize inChunks)+ return (S.PS outFPtr offset length : outChunks)+ else do fillBuffers defaultCompressBufferSize inChunks++ Stream.StreamEnd -> do+ inputBufferEmpty <- Stream.inputBufferEmpty+ assert inputBufferEmpty $ return ()+ outputBufferBytesAvailable <- Stream.outputBufferBytesAvailable+ if outputBufferBytesAvailable > 0+ then do (outFPtr, offset, length) <- Stream.popOutputBuffer+ Stream.finalise+ return [S.PS outFPtr offset length]+ else do Stream.finalise+ return []++ Stream.Error code msg -> case code of+ Stream.BufferError -> fail "BufferError should be impossible!"+ Stream.NeedDict -> fail "NeedDict is impossible!"+ _ -> fail msg+++-- | Decompress a data stream.+--+-- It will throw an exception if any error is encountered in the input data. If+-- you need more control over error handling then use 'decompressWithErrors'.+--+decompress+ :: Stream.Format+ -> DecompressParams+ -> L.ByteString+ -> L.ByteString+decompress format params = fromDecompressStream+ . decompressWithErrors format params++-- | Like 'decompress' but returns a 'DecompressStream' data structure that+-- contains an explicit representation of the error conditions that one may+-- encounter when decompressing.+--+-- Note that in addition to errors in the input data, it is possible for other+-- unexpected errors to occur, such as out of memory, or finding the wrong+-- version of the zlib C library, these are still thrown as exceptions (because+-- representing them as data would make this function impure).+--+decompressWithErrors+ :: Stream.Format+ -> DecompressParams+ -> L.ByteString+ -> DecompressStream+decompressWithErrors format (DecompressParams bits initChunkSize) input =+ Stream.run $ do+ Stream.inflateInit format bits+ case L.toChunks input of+ [] -> fillBuffers 4 [] --always an error anyway+ S.PS inFPtr offset length : chunks -> do+ Stream.pushInputBuffer inFPtr offset length+ fillBuffers initChunkSize chunks++ where+ -- we flick between two states:+ -- * where one or other buffer is empty+ -- - in which case we refill one or both+ -- * where both buffers are non-empty+ -- - in which case we compress until a buffer is empty++ fillBuffers :: Int+ -> [S.ByteString]+ -> Stream DecompressStream+ fillBuffers outChunkSize inChunks = do+#ifdef DEBUG+ Stream.consistencyCheck+#endif++ -- in this state there are two possabilities:+ -- * no outbut buffer space is available+ -- - in which case we must make more available+ -- * no input buffer is available+ -- - in which case we must supply more+ inputBufferEmpty <- Stream.inputBufferEmpty+ outputBufferFull <- Stream.outputBufferFull++ assert (inputBufferEmpty || outputBufferFull) $ return ()++ when outputBufferFull $ do+ outFPtr <- Stream.unsafeLiftIO (S.mallocByteString outChunkSize)+ Stream.pushOutputBuffer outFPtr 0 outChunkSize++ if inputBufferEmpty+ then case inChunks of+ [] -> drainBuffers []+ S.PS inFPtr offset length : inChunks' -> do+ Stream.pushInputBuffer inFPtr offset length+ drainBuffers inChunks'+ else drainBuffers inChunks+++ drainBuffers ::+ [S.ByteString]+ -> Stream DecompressStream+ drainBuffers inChunks = do++ inputBufferEmpty' <- Stream.inputBufferEmpty+ outputBufferFull' <- Stream.outputBufferFull+ assert(not outputBufferFull'+ && (null inChunks || not inputBufferEmpty')) $ return ()+ -- this invariant guarantees we can always make forward progress or at+ -- least if a BufferError does occur that it must be due to a premature EOF++ status <- Stream.inflate Stream.NoFlush++ case status of+ Stream.Ok -> do+ outputBufferFull <- Stream.outputBufferFull+ if outputBufferFull+ then do (outFPtr, offset, length) <- Stream.popOutputBuffer+ outChunks <- Stream.unsafeInterleave+ (fillBuffers defaultDecompressBufferSize inChunks)+ return $ StreamChunk (S.PS outFPtr offset length) outChunks+ else do fillBuffers defaultDecompressBufferSize inChunks++ Stream.StreamEnd -> finish StreamEnd+ Stream.Error code msg -> case code of+ Stream.BufferError -> finish (StreamError TruncatedInput msg')+ where msg' = "premature end of compressed stream"+ Stream.NeedDict -> finish (StreamError DictionaryRequired msg)+ Stream.DataError -> finish (StreamError DataError msg)+ _ -> fail msg++ -- Note even if we end with an error we still try to flush the last chunk if+ -- there is one. The user just has to decide what they want to trust.+ finish end = do+ -- Note that there may be input bytes still available if the stream+ -- is embeded in some other data stream. Here we just silently discard+ -- any trailing data.+ outputBufferBytesAvailable <- Stream.outputBufferBytesAvailable+ if outputBufferBytesAvailable > 0+ then do (outFPtr, offset, length) <- Stream.popOutputBuffer+ Stream.finalise+ return (StreamChunk (S.PS outFPtr offset length) end)+ else do Stream.finalise+ return end
+ Codec/Compression/Zlib/Raw.hs view
@@ -0,0 +1,70 @@+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) 2006-2008 Duncan Coutts+-- License : BSD-style+--+-- Maintainer : duncan@haskell.org+-- Stability : provisional+-- Portability : portable (H98 + FFI)+--+-- Compression and decompression of data streams in the raw deflate format.+--+-- The format is described in detail in RFC #1951:+-- <http://www.ietf.org/rfc/rfc1951.txt>+--+-- See also the zlib home page: <http://zlib.net/>+--+-----------------------------------------------------------------------------+module Codec.Compression.Zlib.Raw (+ + -- * Simple compression and decompression+ compress,+ decompress,++ -- * Extended api with control over compression parameters+ compressWith,+ decompressWith,++ CompressParams(..), defaultCompressParams,+ DecompressParams(..), defaultDecompressParams,++ -- ** The compression parameter types+ CompressionLevel(..),+ defaultCompression,+ noCompression,+ bestSpeed,+ bestCompression,+ compressionLevel,+ Method(..),+ deflateMethod,+ WindowBits(..),+ defaultWindowBits,+ windowBits,+ MemoryLevel(..),+ defaultMemoryLevel,+ minMemoryLevel,+ maxMemoryLevel,+ memoryLevel,+ CompressionStrategy(..),+ defaultStrategy,+ filteredStrategy,+ huffmanOnlyStrategy,++ ) where++import Data.ByteString.Lazy (ByteString)++import qualified Codec.Compression.Zlib.Internal as Internal+import Codec.Compression.Zlib.Internal hiding (compress, decompress)++decompress :: ByteString -> ByteString+decompress = decompressWith defaultDecompressParams++decompressWith :: DecompressParams -> ByteString -> ByteString+decompressWith = Internal.decompress rawFormat++compress :: ByteString -> ByteString+compress = compressWith defaultCompressParams++compressWith :: CompressParams -> ByteString -> ByteString+compressWith = Internal.compress rawFormat
+ Codec/Compression/Zlib/Stream.hsc view
@@ -0,0 +1,895 @@+{-# LANGUAGE ForeignFunctionInterface #-}+-----------------------------------------------------------------------------+-- |+-- Copyright : (c) 2006-2008 Duncan Coutts+-- License : BSD-style+--+-- Maintainer : duncan@haskell.org+-- Stability : provisional+-- Portability : portable (H98 + FFI)+--+-- Zlib wrapper layer+--+-----------------------------------------------------------------------------+module Codec.Compression.Zlib.Stream (++ -- * The Zlib state monad+ Stream,+ run,+ unsafeInterleave,+ unsafeLiftIO,+ finalise,++ -- * Initialisation+ deflateInit, + inflateInit,++ -- ** Initialisation parameters+ Format(..),+ gzipFormat,+ zlibFormat,+ rawFormat,+ gzipOrZlibFormat,+ CompressionLevel(..),+ defaultCompression,+ noCompression,+ bestSpeed,+ bestCompression,+ compressionLevel,+ Method(..),+ deflateMethod,+ WindowBits(..),+ defaultWindowBits,+ windowBits,+ MemoryLevel(..),+ defaultMemoryLevel,+ minMemoryLevel,+ maxMemoryLevel,+ memoryLevel,+ CompressionStrategy(..),+ defaultStrategy,+ filteredStrategy,+ huffmanOnlyStrategy,++ -- * The buisness+ deflate,+ inflate,+ Status(..),+ Flush(..),+ ErrorCode(..),++ -- * Buffer management+ -- ** Input buffer+ pushInputBuffer,+ inputBufferEmpty,++ -- ** Output buffer+ pushOutputBuffer,+ popOutputBuffer,+ outputBufferBytesAvailable,+ outputBufferSpaceRemaining,+ outputBufferFull,++#ifdef DEBUG+ -- * Debugging+ consistencyCheck,+ dump,+ trace,+#endif++ ) where++import Foreign+ ( Word8, Ptr, nullPtr, plusPtr, peekByteOff, pokeByteOff, mallocBytes+ , ForeignPtr, FinalizerPtr, newForeignPtr_, addForeignPtrFinalizer+ , withForeignPtr, touchForeignPtr )+#if __GLASGOW_HASKELL__ >= 702+import Foreign.ForeignPtr.Unsafe ( unsafeForeignPtrToPtr )+import System.IO.Unsafe ( unsafePerformIO )+#else+import Foreign ( unsafeForeignPtrToPtr, unsafePerformIO )+#endif+#ifdef __GLASGOW_HASKELL__+import Foreign+ ( finalizeForeignPtr )+#endif+import Foreign.C+import Data.ByteString.Internal (nullForeignPtr)+import System.IO.Unsafe (unsafeInterleaveIO)+import Control.Monad (liftM)+import Control.Exception (assert)+#ifdef DEBUG+import System.IO (hPutStrLn, stderr)+#endif++import Prelude hiding (length)++#include "zlib.h"+++pushInputBuffer :: ForeignPtr Word8 -> Int -> Int -> Stream ()+pushInputBuffer inBuf' offset length = do++ -- must not push a new input buffer if the last one is not used up+ inAvail <- getInAvail+ assert (inAvail == 0) $ return ()++ -- Now that we're setting a new input buffer, we can be sure that zlib no+ -- longer has a reference to the old one. Therefore this is the last point+ -- at which the old buffer had to be retained. It's safe to release now.+ inBuf <- getInBuf + unsafeLiftIO $ touchForeignPtr inBuf ++ -- now set the available input buffer ptr and length+ setInBuf inBuf'+ setInAvail length+ setInNext (unsafeForeignPtrToPtr inBuf' `plusPtr` offset)+ -- Note the 'unsafe'. We are passing the raw ptr inside inBuf' to zlib.+ -- To make this safe we need to hold on to the ForeignPtr for at least as+ -- long as zlib is using the underlying raw ptr.+++inputBufferEmpty :: Stream Bool+inputBufferEmpty = getInAvail >>= return . (==0)+++pushOutputBuffer :: ForeignPtr Word8 -> Int -> Int -> Stream ()+pushOutputBuffer outBuf' offset length = do++ --must not push a new buffer if there is still data in the old one+ outAvail <- getOutAvail+ assert (outAvail == 0) $ return ()+ -- Note that there may still be free space in the output buffer, that's ok,+ -- you might not want to bother completely filling the output buffer say if+ -- there's only a few free bytes left.++ outBuf <- getOutBuf+ unsafeLiftIO $ touchForeignPtr outBuf++ -- now set the available input buffer ptr and length+ setOutBuf outBuf'+ setOutFree length+ setOutNext (unsafeForeignPtrToPtr outBuf' `plusPtr` offset)++ setOutOffset offset+ setOutAvail 0+++-- get that part of the output buffer that is currently full+-- (might be 0, use outputBufferBytesAvailable to check)+-- this may leave some space remaining in the buffer, use+-- outputBufferSpaceRemaining to check.+popOutputBuffer :: Stream (ForeignPtr Word8, Int, Int)+popOutputBuffer = do++ outBuf <- getOutBuf+ outOffset <- getOutOffset+ outAvail <- getOutAvail++ -- there really should be something to pop, otherwise it's silly+ assert (outAvail > 0) $ return ()++ setOutOffset (outOffset + outAvail)+ setOutAvail 0++ return (outBuf, outOffset, outAvail)+++-- this is the number of bytes available in the output buffer+outputBufferBytesAvailable :: Stream Int+outputBufferBytesAvailable = getOutAvail+++-- you needen't get all the output immediately, you can continue until+-- there is no more output space available, this tells you that amount+outputBufferSpaceRemaining :: Stream Int+outputBufferSpaceRemaining = getOutFree+++-- you only need to supply a new buffer when there is no more output buffer+-- space remaining+outputBufferFull :: Stream Bool+outputBufferFull = liftM (==0) outputBufferSpaceRemaining+++-- you can only run this when the output buffer is not empty+-- you can run it when the input buffer is empty but it doesn't do anything+-- after running deflate either the output buffer will be full+-- or the input buffer will be empty (or both)+deflate :: Flush -> Stream Status+deflate flush = do++ outFree <- getOutFree++ -- deflate needs free space in the output buffer+ assert (outFree > 0) $ return ()++ result <- deflate_ flush+ outFree' <- getOutFree+ + -- number of bytes of extra output there is available as a result of+ -- the call to deflate:+ let outExtra = outFree - outFree'+ + outAvail <- getOutAvail+ setOutAvail (outAvail + outExtra)+ return result+++inflate :: Flush -> Stream Status+inflate flush = do++ outFree <- getOutFree++ -- inflate needs free space in the output buffer+ assert (outFree > 0) $ return ()++ result <- inflate_ flush+ outFree' <- getOutFree++ -- number of bytes of extra output there is available as a result of+ -- the call to inflate:+ let outExtra = outFree - outFree'++ outAvail <- getOutAvail+ setOutAvail (outAvail + outExtra)+ return result+++----------------------------+-- Stream monad+--++newtype Stream a = Z {+ unZ :: ForeignPtr StreamState+ -> ForeignPtr Word8+ -> ForeignPtr Word8+ -> Int -> Int+ -> IO (ForeignPtr Word8+ ,ForeignPtr Word8+ ,Int, Int, a)+ }++instance Monad Stream where+ (>>=) = thenZ+-- m >>= f = (m `thenZ` \a -> consistencyCheck `thenZ_` returnZ a) `thenZ` f+ (>>) = thenZ_+ return = returnZ+ fail = (finalise >>) . failZ++returnZ :: a -> Stream a+returnZ a = Z $ \_ inBuf outBuf outOffset outLength ->+ return (inBuf, outBuf, outOffset, outLength, a)+{-# INLINE returnZ #-}++thenZ :: Stream a -> (a -> Stream b) -> Stream b+thenZ (Z m) f =+ Z $ \stream inBuf outBuf outOffset outLength ->+ m stream inBuf outBuf outOffset outLength >>=+ \(inBuf', outBuf', outOffset', outLength', a) ->+ unZ (f a) stream inBuf' outBuf' outOffset' outLength'+{-# INLINE thenZ #-}++thenZ_ :: Stream a -> Stream b -> Stream b+thenZ_ (Z m) f =+ Z $ \stream inBuf outBuf outOffset outLength ->+ m stream inBuf outBuf outOffset outLength >>=+ \(inBuf', outBuf', outOffset', outLength', _) ->+ unZ f stream inBuf' outBuf' outOffset' outLength'+{-# INLINE thenZ_ #-}++failZ :: String -> Stream a+failZ msg = Z (\_ _ _ _ _ -> fail ("Codec.Compression.Zlib: " ++ msg))++{-# NOINLINE run #-}+run :: Stream a -> a+run (Z m) = unsafePerformIO $ do+ ptr <- mallocBytes (#{const sizeof(z_stream)})+ #{poke z_stream, msg} ptr nullPtr+ #{poke z_stream, zalloc} ptr nullPtr+ #{poke z_stream, zfree} ptr nullPtr+ #{poke z_stream, opaque} ptr nullPtr+ #{poke z_stream, next_in} ptr nullPtr+ #{poke z_stream, next_out} ptr nullPtr+ #{poke z_stream, avail_in} ptr (0 :: CUInt)+ #{poke z_stream, avail_out} ptr (0 :: CUInt)+ stream <- newForeignPtr_ ptr+ (_,_,_,_,a) <- m stream nullForeignPtr nullForeignPtr 0 0+ return a++-- This is marked as unsafe because run uses unsafePerformIO so anything+-- lifted here will end up being unsafePerformIO'd.+unsafeLiftIO :: IO a -> Stream a+unsafeLiftIO m = Z $ \_stream inBuf outBuf outOffset outLength -> do+ a <- m+ return (inBuf, outBuf, outOffset, outLength, a)++-- It's unsafe because we discard the values here, so if you mutate anything+-- between running this and forcing the result then you'll get an inconsistent+-- stream state.+unsafeInterleave :: Stream a -> Stream a+unsafeInterleave (Z m) = Z $ \stream inBuf outBuf outOffset outLength -> do+ res <- unsafeInterleaveIO (m stream inBuf outBuf outOffset outLength)+ let select (_,_,_,_,a) = a+ return (inBuf, outBuf, outOffset, outLength, select res)++getStreamState :: Stream (ForeignPtr StreamState)+getStreamState = Z $ \stream inBuf outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, stream)++getInBuf :: Stream (ForeignPtr Word8)+getInBuf = Z $ \_stream inBuf outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, inBuf)++getOutBuf :: Stream (ForeignPtr Word8)+getOutBuf = Z $ \_stream inBuf outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, outBuf)++getOutOffset :: Stream Int+getOutOffset = Z $ \_stream inBuf outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, outOffset)++getOutAvail :: Stream Int+getOutAvail = Z $ \_stream inBuf outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, outLength)++setInBuf :: ForeignPtr Word8 -> Stream ()+setInBuf inBuf = Z $ \_stream _ outBuf outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, ())++setOutBuf :: ForeignPtr Word8 -> Stream ()+setOutBuf outBuf = Z $ \_stream inBuf _ outOffset outLength -> do+ return (inBuf, outBuf, outOffset, outLength, ())++setOutOffset :: Int -> Stream ()+setOutOffset outOffset = Z $ \_stream inBuf outBuf _ outLength -> do+ return (inBuf, outBuf, outOffset, outLength, ())++setOutAvail :: Int -> Stream ()+setOutAvail outLength = Z $ \_stream inBuf outBuf outOffset _ -> do+ return (inBuf, outBuf, outOffset, outLength, ())++----------------------------+-- Debug stuff+--++#ifdef DEBUG+trace :: String -> Stream ()+trace = unsafeLiftIO . hPutStrLn stderr++dump :: Stream ()+dump = do+ inNext <- getInNext+ inAvail <- getInAvail++ outNext <- getOutNext+ outFree <- getOutFree+ outAvail <- getOutAvail+ outOffset <- getOutOffset++ unsafeLiftIO $ hPutStrLn stderr $+ "Stream {\n" +++ " inNext = " ++ show inNext ++ ",\n" +++ " inAvail = " ++ show inAvail ++ ",\n" +++ "\n" +++ " outNext = " ++ show outNext ++ ",\n" +++ " outFree = " ++ show outFree ++ ",\n" +++ " outAvail = " ++ show outAvail ++ ",\n" +++ " outOffset = " ++ show outOffset ++ "\n" +++ "}"++ consistencyCheck++consistencyCheck :: Stream ()+consistencyCheck = do++ outBuf <- getOutBuf+ outOffset <- getOutOffset+ outAvail <- getOutAvail+ outNext <- getOutNext++ let outBufPtr = unsafeForeignPtrToPtr outBuf++ assert (outBufPtr `plusPtr` (outOffset + outAvail) == outNext) $ return ()+#endif+++----------------------------+-- zlib wrapper layer+--++data Status =+ Ok+ | StreamEnd+ | Error ErrorCode String++data ErrorCode =+ NeedDict+ | FileError+ | StreamError+ | DataError+ | MemoryError+ | BufferError -- ^ No progress was possible or there was not enough room in+ -- the output buffer when 'Finish' is used. Note that+ -- 'BuferError' is not fatal, and 'inflate' can be called+ -- again with more input and more output space to continue.+ | VersionError+ | Unexpected++toStatus :: CInt -> Stream Status+toStatus errno = case errno of+ (#{const Z_OK}) -> return Ok+ (#{const Z_STREAM_END}) -> return StreamEnd+ (#{const Z_NEED_DICT}) -> err NeedDict "custom dictionary needed"+ (#{const Z_BUF_ERROR}) -> err BufferError "buffer error"+ (#{const Z_ERRNO}) -> err FileError "file error"+ (#{const Z_STREAM_ERROR}) -> err StreamError "stream error"+ (#{const Z_DATA_ERROR}) -> err DataError "data error"+ (#{const Z_MEM_ERROR}) -> err MemoryError "insufficient memory"+ (#{const Z_VERSION_ERROR}) -> err VersionError "incompatible zlib version"+ other -> return $ Error Unexpected+ ("unexpected zlib status: " ++ show other)+ where+ err errCode altMsg = liftM (Error errCode) $ do+ msgPtr <- withStreamPtr (#{peek z_stream, msg})+ if msgPtr /= nullPtr+ then unsafeLiftIO (peekCAString msgPtr)+ else return altMsg++failIfError :: CInt -> Stream ()+failIfError errno = toStatus errno >>= \status -> case status of+ (Error _ msg) -> fail msg+ _ -> return ()+++data Flush =+ NoFlush+ | SyncFlush+ | FullFlush+ | Finish+-- | Block -- only available in zlib 1.2 and later, uncomment if you need it.++fromFlush :: Flush -> CInt+fromFlush NoFlush = #{const Z_NO_FLUSH}+fromFlush SyncFlush = #{const Z_SYNC_FLUSH}+fromFlush FullFlush = #{const Z_FULL_FLUSH}+fromFlush Finish = #{const Z_FINISH}+-- fromFlush Block = #{const Z_BLOCK}+++-- | The format used for compression or decompression. There are three+-- variations.+--+data Format = GZip | Zlib | Raw | GZipOrZlib+ deriving Eq++{-# DEPRECATED GZip "Use gzipFormat. Format constructors will be hidden in version 0.7" #-}+{-# DEPRECATED Zlib "Use zlibFormat. Format constructors will be hidden in version 0.7" #-}+{-# DEPRECATED Raw "Use rawFormat. Format constructors will be hidden in version 0.7" #-}+{-# DEPRECATED GZipOrZlib "Use gzipOrZlibFormat. Format constructors will be hidden in version 0.7" #-}++-- | The gzip format uses a header with a checksum and some optional meta-data+-- about the compressed file. It is intended primarily for compressing+-- individual files but is also sometimes used for network protocols such as+-- HTTP. The format is described in detail in RFC #1952+-- <http://www.ietf.org/rfc/rfc1952.txt>+--+gzipFormat :: Format+gzipFormat = GZip++-- | The zlib format uses a minimal header with a checksum but no other+-- meta-data. It is especially designed for use in network protocols. The+-- format is described in detail in RFC #1950+-- <http://www.ietf.org/rfc/rfc1950.txt>+--+zlibFormat :: Format+zlibFormat = Zlib++-- | The \'raw\' format is just the compressed data stream without any+-- additional header, meta-data or data-integrity checksum. The format is+-- described in detail in RFC #1951 <http://www.ietf.org/rfc/rfc1951.txt>+--+rawFormat :: Format+rawFormat = Raw++-- | This is not a format as such. It enabled zlib or gzip decoding with+-- automatic header detection. This only makes sense for decompression.+--+gzipOrZlibFormat :: Format+gzipOrZlibFormat = GZipOrZlib+++-- | The compression method+--+data Method = Deflated++{-# DEPRECATED Deflated "Use deflateMethod. Method constructors will be hidden in version 0.7" #-}++-- | \'Deflate\' is the only method supported in this version of zlib.+-- Indeed it is likely to be the only method that ever will be supported.+--+deflateMethod :: Method+deflateMethod = Deflated++fromMethod :: Method -> CInt+fromMethod Deflated = #{const Z_DEFLATED}+++-- | The compression level parameter controls the amount of compression. This+-- is a trade-off between the amount of compression and the time required to do+-- the compression.+--+data CompressionLevel = + DefaultCompression+ | NoCompression+ | BestSpeed+ | BestCompression+ | CompressionLevel Int++{-# DEPRECATED DefaultCompression "Use defaultCompression. CompressionLevel constructors will be hidden in version 0.7" #-}+{-# DEPRECATED NoCompression "Use noCompression. CompressionLevel constructors will be hidden in version 0.7" #-}+{-# DEPRECATED BestSpeed "Use bestSpeed. CompressionLevel constructors will be hidden in version 0.7" #-}+{-# DEPRECATED BestCompression "Use bestCompression. CompressionLevel constructors will be hidden in version 0.7" #-}+--FIXME: cannot deprecate constructor named the same as the type+{- DEPRECATED CompressionLevel "Use compressionLevel. CompressionLevel constructors will be hidden in version 0.7" -}++-- | The default compression level is 6 (that is, biased towards higher+-- compression at expense of speed).+defaultCompression :: CompressionLevel+defaultCompression = DefaultCompression++-- | No compression, just a block copy.+noCompression :: CompressionLevel+noCompression = CompressionLevel 0++-- | The fastest compression method (less compression)+bestSpeed :: CompressionLevel+bestSpeed = CompressionLevel 1++-- | The slowest compression method (best compression).+bestCompression :: CompressionLevel+bestCompression = CompressionLevel 9++-- | A specific compression level between 0 and 9.+compressionLevel :: Int -> CompressionLevel+compressionLevel n+ | n >= 0 && n <= 9 = CompressionLevel n+ | otherwise = error "CompressionLevel must be in the range 0..9"++fromCompressionLevel :: CompressionLevel -> CInt+fromCompressionLevel DefaultCompression = -1+fromCompressionLevel NoCompression = 0+fromCompressionLevel BestSpeed = 1+fromCompressionLevel BestCompression = 9+fromCompressionLevel (CompressionLevel n)+ | n >= 0 && n <= 9 = fromIntegral n+ | otherwise = error "CompressLevel must be in the range 1..9"+++-- | This specifies the size of the compression window. Larger values of this+-- parameter result in better compression at the expense of higher memory+-- usage.+--+-- The compression window size is the value of the the window bits raised to+-- the power 2. The window bits must be in the range @8..15@ which corresponds+-- to compression window sizes of 256b to 32Kb. The default is 15 which is also+-- the maximum size.+--+-- The total amount of memory used depends on the window bits and the+-- 'MemoryLevel'. See the 'MemoryLevel' for the details.+--+data WindowBits = WindowBits Int+ | DefaultWindowBits -- This constructor must be last to make+ -- the Ord instance work. The Ord instance+ -- is defined with and used by the tests.+ -- It makse sense because the default value+ -- is is also the max value at 15.++{-# DEPRECATED DefaultWindowBits "Use defaultWindowBits. WindowBits constructors will be hidden in version 0.7" #-}+--FIXME: cannot deprecate constructor named the same as the type+{- DEPRECATED WindowBits "Use windowBits. WindowBits constructors will be hidden in version 0.7" -}++-- | The default 'WindowBits' is 15 which is also the maximum size.+--+defaultWindowBits :: WindowBits+defaultWindowBits = WindowBits 15++-- | A specific compression window size, specified in bits in the range @8..15@+--+windowBits :: Int -> WindowBits+windowBits n+ | n >= 8 && n <= 15 = WindowBits n+ | otherwise = error "WindowBits must be in the range 8..15"++fromWindowBits :: Format -> WindowBits-> CInt+fromWindowBits format bits = (formatModifier format) (checkWindowBits bits)+ where checkWindowBits DefaultWindowBits = 15+ checkWindowBits (WindowBits n)+ | n >= 8 && n <= 15 = fromIntegral n+ | otherwise = error "WindowBits must be in the range 8..15"+ formatModifier Zlib = id+ formatModifier GZip = (+16)+ formatModifier GZipOrZlib = (+32)+ formatModifier Raw = negate+++-- | The 'MemoryLevel' parameter specifies how much memory should be allocated+-- for the internal compression state. It is a tradoff between memory usage,+-- compression ratio and compression speed. Using more memory allows faster+-- compression and a better compression ratio.+--+-- The total amount of memory used for compression depends on the 'WindowBits'+-- and the 'MemoryLevel'. For decompression it depends only on the+-- 'WindowBits'. The totals are given by the functions:+--+-- > compressTotal windowBits memLevel = 4 * 2^windowBits + 512 * 2^memLevel+-- > decompressTotal windowBits = 2^windowBits+--+-- For example, for compression with the default @windowBits = 15@ and+-- @memLevel = 8@ uses @256Kb@. So for example a network server with 100+-- concurrent compressed streams would use @25Mb@. The memory per stream can be+-- halved (at the cost of somewhat degraded and slower compressionby) by+-- reducing the @windowBits@ and @memLevel@ by one.+--+-- Decompression takes less memory, the default @windowBits = 15@ corresponds+-- to just @32Kb@.+--+data MemoryLevel =+ DefaultMemoryLevel+ | MinMemoryLevel+ | MaxMemoryLevel+ | MemoryLevel Int++{-# DEPRECATED DefaultMemoryLevel "Use defaultMemoryLevel. MemoryLevel constructors will be hidden in version 0.7" #-}+{-# DEPRECATED MinMemoryLevel "Use minMemoryLevel. MemoryLevel constructors will be hidden in version 0.7" #-}+{-# DEPRECATED MaxMemoryLevel "Use maxMemoryLevel. MemoryLevel constructors will be hidden in version 0.7" #-}+--FIXME: cannot deprecate constructor named the same as the type+{- DEPRECATED MemoryLevel "Use memoryLevel. MemoryLevel constructors will be hidden in version 0.7" -}++-- | The default memory level. (Equivalent to @'memoryLevel' 8@)+--+defaultMemoryLevel :: MemoryLevel+defaultMemoryLevel = MemoryLevel 8++-- | Use minimum memory. This is slow and reduces the compression ratio.+-- (Equivalent to @'memoryLevel' 1@)+--+minMemoryLevel :: MemoryLevel+minMemoryLevel = MemoryLevel 1++-- | Use maximum memory for optimal compression speed.+-- (Equivalent to @'memoryLevel' 9@)+--+maxMemoryLevel :: MemoryLevel+maxMemoryLevel = MemoryLevel 9++-- | A specific level in the range @1..9@+--+memoryLevel :: Int -> MemoryLevel+memoryLevel n+ | n >= 1 && n <= 9 = MemoryLevel n+ | otherwise = error "MemoryLevel must be in the range 1..9"++fromMemoryLevel :: MemoryLevel -> CInt+fromMemoryLevel DefaultMemoryLevel = 8+fromMemoryLevel MinMemoryLevel = 1+fromMemoryLevel MaxMemoryLevel = 9+fromMemoryLevel (MemoryLevel n)+ | n >= 1 && n <= 9 = fromIntegral n+ | otherwise = error "MemoryLevel must be in the range 1..9"+++-- | The strategy parameter is used to tune the compression algorithm.+--+-- The strategy parameter only affects the compression ratio but not the+-- correctness of the compressed output even if it is not set appropriately.+--+data CompressionStrategy =+ DefaultStrategy+ | Filtered+ | HuffmanOnly++{-+-- -- only available in zlib 1.2 and later, uncomment if you need it.+ | RLE -- ^ Use 'RLE' to limit match distances to one (run-length+ -- encoding). 'RLE' is designed to be almost as fast as+ -- 'HuffmanOnly', but give better compression for PNG+ -- image data.+ | Fixed -- ^ 'Fixed' prevents the use of dynamic Huffman codes,+ -- allowing for a simpler decoder for special applications.+-}++{-# DEPRECATED DefaultStrategy "Use defaultStrategy. CompressionStrategy constructors will be hidden in version 0.7" #-}+{-# DEPRECATED Filtered "Use filteredStrategy. CompressionStrategy constructors will be hidden in version 0.7" #-}+{-# DEPRECATED HuffmanOnly "Use huffmanOnlyStrategy. CompressionStrategy constructors will be hidden in version 0.7" #-}++-- | Use this default compression strategy for normal data.+--+defaultStrategy :: CompressionStrategy+defaultStrategy = DefaultStrategy++-- | Use the filtered compression strategy for data produced by a filter (or+-- predictor). Filtered data consists mostly of small values with a somewhat+-- random distribution. In this case, the compression algorithm is tuned to+-- compress them better. The effect of this strategy is to force more Huffman+-- coding and less string matching; it is somewhat intermediate between+-- 'defaultCompressionStrategy' and 'huffmanOnlyCompressionStrategy'.+--+filteredStrategy :: CompressionStrategy+filteredStrategy = Filtered++-- | Use the Huffman-only compression strategy to force Huffman encoding only+-- (no string match).+--+huffmanOnlyStrategy :: CompressionStrategy+huffmanOnlyStrategy = HuffmanOnly+++fromCompressionStrategy :: CompressionStrategy -> CInt+fromCompressionStrategy DefaultStrategy = #{const Z_DEFAULT_STRATEGY}+fromCompressionStrategy Filtered = #{const Z_FILTERED}+fromCompressionStrategy HuffmanOnly = #{const Z_HUFFMAN_ONLY}+--fromCompressionStrategy RLE = #{const Z_RLE}+--fromCompressionStrategy Fixed = #{const Z_FIXED}+++withStreamPtr :: (Ptr StreamState -> IO a) -> Stream a+withStreamPtr f = do+ stream <- getStreamState+ unsafeLiftIO (withForeignPtr stream f)++withStreamState :: (StreamState -> IO a) -> Stream a+withStreamState f = do+ stream <- getStreamState+ unsafeLiftIO (withForeignPtr stream (f . StreamState))++setInAvail :: Int -> Stream ()+setInAvail val = withStreamPtr $ \ptr ->+ #{poke z_stream, avail_in} ptr (fromIntegral val :: CUInt)++getInAvail :: Stream Int+getInAvail = liftM (fromIntegral :: CUInt -> Int) $+ withStreamPtr (#{peek z_stream, avail_in})++setInNext :: Ptr Word8 -> Stream ()+setInNext val = withStreamPtr (\ptr -> #{poke z_stream, next_in} ptr val)++#ifdef DEBUG+getInNext :: Stream (Ptr Word8)+getInNext = withStreamPtr (#{peek z_stream, next_in})+#endif++setOutFree :: Int -> Stream ()+setOutFree val = withStreamPtr $ \ptr ->+ #{poke z_stream, avail_out} ptr (fromIntegral val :: CUInt)++getOutFree :: Stream Int+getOutFree = liftM (fromIntegral :: CUInt -> Int) $+ withStreamPtr (#{peek z_stream, avail_out})++setOutNext :: Ptr Word8 -> Stream ()+setOutNext val = withStreamPtr (\ptr -> #{poke z_stream, next_out} ptr val)++#ifdef DEBUG+getOutNext :: Stream (Ptr Word8)+getOutNext = withStreamPtr (#{peek z_stream, next_out})+#endif++inflateInit :: Format -> WindowBits -> Stream ()+inflateInit format bits = do+ checkFormatSupported format+ err <- withStreamState $ \zstream ->+ c_inflateInit2 zstream (fromIntegral (fromWindowBits format bits))+ failIfError err+ getStreamState >>= unsafeLiftIO . addForeignPtrFinalizer c_inflateEnd++deflateInit :: Format+ -> CompressionLevel+ -> Method+ -> WindowBits+ -> MemoryLevel+ -> CompressionStrategy+ -> Stream ()+deflateInit format compLevel method bits memLevel strategy = do+ checkFormatSupported format+ err <- withStreamState $ \zstream ->+ c_deflateInit2 zstream+ (fromCompressionLevel compLevel)+ (fromMethod method)+ (fromWindowBits format bits)+ (fromMemoryLevel memLevel)+ (fromCompressionStrategy strategy)+ failIfError err+ getStreamState >>= unsafeLiftIO . addForeignPtrFinalizer c_deflateEnd++inflate_ :: Flush -> Stream Status+inflate_ flush = do+ err <- withStreamState $ \zstream ->+ c_inflate zstream (fromFlush flush)+ toStatus err++deflate_ :: Flush -> Stream Status+deflate_ flush = do+ err <- withStreamState $ \zstream ->+ c_deflate zstream (fromFlush flush)+ toStatus err++-- | This never needs to be used as the stream's resources will be released+-- automatically when no longer needed, however this can be used to release+-- them early. Only use this when you can guarantee that the stream will no+-- longer be needed, for example if an error occurs or if the stream ends.+--+finalise :: Stream ()+#ifdef __GLASGOW_HASKELL__+--TODO: finalizeForeignPtr is ghc-only+finalise = getStreamState >>= unsafeLiftIO . finalizeForeignPtr+#else+finalise = return ()+#endif++checkFormatSupported :: Format -> Stream ()+checkFormatSupported format = do+ version <- unsafeLiftIO (peekCAString =<< c_zlibVersion)+ case version of+ ('1':'.':'1':'.':_)+ | format == GZip+ || format == GZipOrZlib+ -> fail $ "version 1.1.x of the zlib C library does not support the"+ ++ " 'gzip' format via the in-memory api, only the 'raw' and "+ ++ " 'zlib' formats."+ _ -> return ()++----------------------+-- The foreign imports++newtype StreamState = StreamState (Ptr StreamState)++-- inflateInit2 and deflateInit2 are actually defined as macros in zlib.h+-- They are defined in terms of inflateInit2_ and deflateInit2_ passing two+-- additional arguments used to detect compatability problems. They pass the+-- version of zlib as a char * and the size of the z_stream struct.+-- If we compile via C then we can avoid this hassle however thats not really+-- kosher since the Haskell FFI is defined at the C ABI level, not the C+-- language level. There is no requirement to compile via C and pick up C+-- headers. So it's much better if we can make it work properly and that'd+-- also allow compiling via ghc's ncg which is a good thing since the C+-- backend is not going to be around forever.+--+-- So we define c_inflateInit2 and c_deflateInit2 here as wrappers around+-- their _ counterparts and pass the extra args.++foreign import ccall unsafe "zlib.h inflateInit2_"+ c_inflateInit2_ :: StreamState -> CInt -> Ptr CChar -> CInt -> IO CInt++c_inflateInit2 :: StreamState -> CInt -> IO CInt+c_inflateInit2 z n =+ withCAString #{const_str ZLIB_VERSION} $ \versionStr ->+ c_inflateInit2_ z n versionStr (#{const sizeof(z_stream)} :: CInt)++foreign import ccall unsafe "zlib.h inflate"+ c_inflate :: StreamState -> CInt -> IO CInt++foreign import ccall unsafe "zlib.h &inflateEnd"+ c_inflateEnd :: FinalizerPtr StreamState+++foreign import ccall unsafe "zlib.h deflateInit2_"+ c_deflateInit2_ :: StreamState+ -> CInt -> CInt -> CInt -> CInt -> CInt+ -> Ptr CChar -> CInt+ -> IO CInt++c_deflateInit2 :: StreamState+ -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt+c_deflateInit2 z a b c d e =+ withCAString #{const_str ZLIB_VERSION} $ \versionStr ->+ c_deflateInit2_ z a b c d e versionStr (#{const sizeof(z_stream)} :: CInt)++foreign import ccall unsafe "zlib.h deflate"+ c_deflate :: StreamState -> CInt -> IO CInt++foreign import ccall unsafe "zlib.h &deflateEnd"+ c_deflateEnd :: FinalizerPtr StreamState++foreign import ccall unsafe "zlib.h zlibVersion"+ c_zlibVersion :: IO CString
+ Control/Applicative/Backwards.hs view
@@ -0,0 +1,49 @@+-- |+-- Module : Control.Applicative.Backwards+-- Copyright : (c) Russell O'Connor 2009+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : portable+--+-- Making functors with an 'Applicative' instance that performs actions+-- in the reverse order.++module Control.Applicative.Backwards where++import Prelude hiding (foldr, foldr1, foldl, foldl1)+import Control.Applicative+import Data.Foldable+import Data.Traversable++-- | The same functor, but with an 'Applicative' instance that performs+-- actions in the reverse order.+newtype Backwards f a = Backwards { forwards :: f a }++-- | Derived instance.+instance (Functor f) => Functor (Backwards f) where+ fmap f (Backwards a) = Backwards (fmap f a)++-- | Apply @f@-actions in the reverse order.+instance (Applicative f) => Applicative (Backwards f) where+ pure a = Backwards (pure a)+ Backwards f <*> Backwards a = Backwards (a <**> f)++-- | Try alternatives in the same order as @f@.+instance (Alternative f) => Alternative (Backwards f) where+ empty = Backwards empty+ Backwards x <|> Backwards y = Backwards (x <|> y)++-- | Derived instance.+instance (Foldable f) => Foldable (Backwards f) where+ foldMap f (Backwards t) = foldMap f t+ foldr f z (Backwards t) = foldr f z t+ foldl f z (Backwards t) = foldl f z t+ foldr1 f (Backwards t) = foldl1 f t+ foldl1 f (Backwards t) = foldr1 f t++-- | Derived instance.+instance (Traversable f) => Traversable (Backwards f) where+ traverse f (Backwards t) = fmap Backwards (traverse f t)+ sequenceA (Backwards t) = fmap Backwards (sequenceA t)
+ Control/Applicative/Lift.hs view
@@ -0,0 +1,68 @@+-- |+-- Module : Control.Applicative.Lift+-- Copyright : (c) Ross Paterson 2010+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Adding a new kind of pure computation to an applicative functor.++module Control.Applicative.Lift (+ Lift(..), unLift,+ -- * Collecting errors+ Errors, failure+ ) where++import Control.Applicative+import Data.Foldable (Foldable(foldMap))+import Data.Functor.Constant+import Data.Monoid (Monoid(mappend))+import Data.Traversable (Traversable(traverse))++-- | Applicative functor formed by adding pure computations to a given+-- applicative functor.+data Lift f a = Pure a | Other (f a)++instance (Functor f) => Functor (Lift f) where+ fmap f (Pure x) = Pure (f x)+ fmap f (Other y) = Other (fmap f y)++instance (Foldable f) => Foldable (Lift f) where+ foldMap f (Pure x) = f x+ foldMap f (Other y) = foldMap f y++instance (Traversable f) => Traversable (Lift f) where+ traverse f (Pure x) = Pure <$> f x+ traverse f (Other y) = Other <$> traverse f y++-- | A combination is 'Pure' only if both parts are.+instance (Applicative f) => Applicative (Lift f) where+ pure = Pure+ Pure f <*> Pure x = Pure (f x)+ Pure f <*> Other y = Other (f <$> y)+ Other f <*> Pure x = Other (($ x) <$> f)+ Other f <*> Other y = Other (f <*> y)++-- | A combination is 'Pure' only either part is.+instance Alternative f => Alternative (Lift f) where+ empty = Other empty+ Pure x <|> _ = Pure x+ Other _ <|> Pure y = Pure y+ Other x <|> Other y = Other (x <|> y)++-- | Projection to the other functor.+unLift :: Applicative f => Lift f a -> f a+unLift (Pure x) = pure x+unLift (Other e) = e++-- | An applicative functor that collects a monoid (e.g. lists) of errors.+-- A sequence of computations fails if any of its components do, but+-- unlike monads made with 'ErrorT' from "Control.Monad.Trans.Error",+-- these computations continue after an error, collecting all the errors.+type Errors e = Lift (Constant e)++-- | Report an error.+failure :: Monoid e => e -> Errors e a+failure e = Other (Constant e)
+ Control/Monad/Cont.hs view
@@ -0,0 +1,168 @@+{- |+Module : Control.Monad.Cont+Copyright : (c) The University of Glasgow 2001,+ (c) Jeff Newbern 2003-2007,+ (c) Andriy Palamarchuk 2007+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : portable++[Computation type:] Computations which can be interrupted and resumed.++[Binding strategy:] Binding a function to a monadic value creates+a new continuation which uses the function as the continuation of the monadic+computation.++[Useful for:] Complex control structures, error handling,+and creating co-routines.++[Zero and plus:] None.++[Example type:] @'Cont' r a@++The Continuation monad represents computations in continuation-passing style+(CPS).+In continuation-passing style function result is not returned,+but instead is passed to another function,+received as a parameter (continuation).+Computations are built up from sequences+of nested continuations, terminated by a final continuation (often @id@)+which produces the final result.+Since continuations are functions which represent the future of a computation,+manipulation of the continuation functions can achieve complex manipulations+of the future of the computation,+such as interrupting a computation in the middle, aborting a portion+of a computation, restarting a computation, and interleaving execution of+computations.+The Continuation monad adapts CPS to the structure of a monad.++Before using the Continuation monad, be sure that you have+a firm understanding of continuation-passing style+and that continuations represent the best solution to your particular+design problem.+Many algorithms which require continuations in other languages do not require+them in Haskell, due to Haskell's lazy semantics.+Abuse of the Continuation monad can produce code that is impossible+to understand and maintain.+-}++module Control.Monad.Cont (+ -- * MonadCont class+ MonadCont(..),+ -- * The Cont monad+ Cont,+ cont,+ runCont,+ mapCont,+ withCont,+ -- * The ContT monad transformer+ ContT(..),+ mapContT,+ withContT,+ module Control.Monad,+ module Control.Monad.Trans,+ -- * Example 1: Simple Continuation Usage+ -- $simpleContExample++ -- * Example 2: Using @callCC@+ -- $callCCExample+ + -- * Example 3: Using @ContT@ Monad Transformer+ -- $ContTExample+ ) where++import Control.Monad.Cont.Class++import Control.Monad.Trans+import Control.Monad.Trans.Cont++import Control.Monad++{- $simpleContExample+Calculating length of a list continuation-style:++>calculateLength :: [a] -> Cont r Int+>calculateLength l = return (length l)++Here we use @calculateLength@ by making it to pass its result to @print@:++>main = do+> runCont (calculateLength "123") print+> -- result: 3++It is possible to chain 'Cont' blocks with @>>=@.++>double :: Int -> Cont r Int+>double n = return (n * 2)+>+>main = do+> runCont (calculateLength "123" >>= double) print+> -- result: 6+-}++{- $callCCExample+This example gives a taste of how escape continuations work, shows a typical+pattern for their usage.++>-- Returns a string depending on the length of the name parameter.+>-- If the provided string is empty, returns an error.+>-- Otherwise, returns a welcome message.+>whatsYourName :: String -> String+>whatsYourName name =+> (`runCont` id) $ do -- 1+> response <- callCC $ \exit -> do -- 2+> validateName name exit -- 3+> return $ "Welcome, " ++ name ++ "!" -- 4+> return response -- 5+>+>validateName name exit = do+> when (null name) (exit "You forgot to tell me your name!")++Here is what this example does:++(1) Runs an anonymous 'Cont' block and extracts value from it with+@(\`runCont\` id)@. Here @id@ is the continuation, passed to the @Cont@ block.++(1) Binds @response@ to the result of the following 'Control.Monad.Cont.Class.callCC' block,+binds @exit@ to the continuation.++(1) Validates @name@.+This approach illustrates advantage of using 'Control.Monad.Cont.Class.callCC' over @return@.+We pass the continuation to @validateName@,+and interrupt execution of the @Cont@ block from /inside/ of @validateName@.++(1) Returns the welcome message from the 'Control.Monad.Cont.Class.callCC' block.+This line is not executed if @validateName@ fails.++(1) Returns from the @Cont@ block.+-}++{-$ContTExample+'ContT' can be used to add continuation handling to other monads.+Here is an example how to combine it with @IO@ monad:++>import Control.Monad.Cont+>import System.IO+>+>main = do+> hSetBuffering stdout NoBuffering+> runContT (callCC askString) reportResult+>+>askString :: (String -> ContT () IO String) -> ContT () IO String+>askString next = do+> liftIO $ putStrLn "Please enter a string"+> s <- liftIO $ getLine+> next s+>+>reportResult :: String -> IO ()+>reportResult s = do+> putStrLn ("You entered: " ++ s)++Action @askString@ requests user to enter a string,+and passes it to the continuation.+@askString@ takes as a parameter a continuation taking a string parameter,+and returning @IO ()@.+Compare its signature to 'runContT' definition.+-}
+ Control/Monad/Cont/Class.hs view
@@ -0,0 +1,130 @@+{- |+Module : Control.Monad.Cont.Class+Copyright : (c) The University of Glasgow 2001,+ (c) Jeff Newbern 2003-2007,+ (c) Andriy Palamarchuk 2007+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : portable++[Computation type:] Computations which can be interrupted and resumed.++[Binding strategy:] Binding a function to a monadic value creates+a new continuation which uses the function as the continuation of the monadic+computation.++[Useful for:] Complex control structures, error handling,+and creating co-routines.++[Zero and plus:] None.++[Example type:] @'Cont' r a@++The Continuation monad represents computations in continuation-passing style+(CPS).+In continuation-passing style function result is not returned,+but instead is passed to another function,+received as a parameter (continuation).+Computations are built up from sequences+of nested continuations, terminated by a final continuation (often @id@)+which produces the final result.+Since continuations are functions which represent the future of a computation,+manipulation of the continuation functions can achieve complex manipulations+of the future of the computation,+such as interrupting a computation in the middle, aborting a portion+of a computation, restarting a computation, and interleaving execution of+computations.+The Continuation monad adapts CPS to the structure of a monad.++Before using the Continuation monad, be sure that you have+a firm understanding of continuation-passing style+and that continuations represent the best solution to your particular+design problem.+Many algorithms which require continuations in other languages do not require+them in Haskell, due to Haskell's lazy semantics.+Abuse of the Continuation monad can produce code that is impossible+to understand and maintain.+-}++module Control.Monad.Cont.Class (+ MonadCont(..),+ ) where++import Control.Monad.Trans.Cont (ContT)+import qualified Control.Monad.Trans.Cont as ContT+import Control.Monad.Trans.Error as Error+import Control.Monad.Trans.Identity as Identity+import Control.Monad.Trans.List as List+import Control.Monad.Trans.Maybe as Maybe+import Control.Monad.Trans.Reader as Reader+import Control.Monad.Trans.RWS.Lazy as LazyRWS+import Control.Monad.Trans.RWS.Strict as StrictRWS+import Control.Monad.Trans.State.Lazy as LazyState+import Control.Monad.Trans.State.Strict as StrictState+import Control.Monad.Trans.Writer.Lazy as LazyWriter+import Control.Monad.Trans.Writer.Strict as StrictWriter++import Control.Monad+import Data.Monoid++class Monad m => MonadCont m where+ {- | @callCC@ (call-with-current-continuation)+ calls a function with the current continuation as its argument.+ Provides an escape continuation mechanism for use with Continuation monads.+ Escape continuations allow to abort the current computation and return+ a value immediately.+ They achieve a similar effect to 'Control.Monad.Error.throwError'+ and 'Control.Monad.Error.catchError'+ within an 'Control.Monad.Error.Error' monad.+ Advantage of this function over calling @return@ is that it makes+ the continuation explicit,+ allowing more flexibility and better control+ (see examples in "Control.Monad.Cont").++ The standard idiom used with @callCC@ is to provide a lambda-expression+ to name the continuation. Then calling the named continuation anywhere+ within its scope will escape from the computation,+ even if it is many layers deep within nested computations.+ -}+ callCC :: ((a -> m b) -> m a) -> m a++instance MonadCont (ContT r m) where+ callCC = ContT.callCC++-- ---------------------------------------------------------------------------+-- Instances for other mtl transformers++instance (Error e, MonadCont m) => MonadCont (ErrorT e m) where+ callCC = Error.liftCallCC callCC++instance MonadCont m => MonadCont (IdentityT m) where+ callCC = Identity.liftCallCC callCC++instance MonadCont m => MonadCont (ListT m) where+ callCC = List.liftCallCC callCC++instance MonadCont m => MonadCont (MaybeT m) where+ callCC = Maybe.liftCallCC callCC++instance MonadCont m => MonadCont (ReaderT r m) where+ callCC = Reader.liftCallCC callCC++instance (Monoid w, MonadCont m) => MonadCont (LazyRWS.RWST r w s m) where+ callCC = LazyRWS.liftCallCC' callCC++instance (Monoid w, MonadCont m) => MonadCont (StrictRWS.RWST r w s m) where+ callCC = StrictRWS.liftCallCC' callCC++instance MonadCont m => MonadCont (LazyState.StateT s m) where+ callCC = LazyState.liftCallCC' callCC++instance MonadCont m => MonadCont (StrictState.StateT s m) where+ callCC = StrictState.liftCallCC' callCC++instance (Monoid w, MonadCont m) => MonadCont (LazyWriter.WriterT w m) where+ callCC = LazyWriter.liftCallCC callCC++instance (Monoid w, MonadCont m) => MonadCont (StrictWriter.WriterT w m) where+ callCC = StrictWriter.liftCallCC callCC
+ Control/Monad/Error.hs view
@@ -0,0 +1,145 @@+{- |+Module : Control.Monad.Error+Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de> 2001,+ (c) Jeff Newbern 2003-2006,+ (c) Andriy Palamarchuk 2006+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : non-portable (multi-parameter type classes)++[Computation type:] Computations which may fail or throw exceptions.++[Binding strategy:] Failure records information about the cause\/location+of the failure. Failure values bypass the bound function,+other values are used as inputs to the bound function.++[Useful for:] Building computations from sequences of functions that may fail+or using exception handling to structure error handling.++[Zero and plus:] Zero is represented by an empty error and the plus operation+executes its second argument if the first fails.++[Example type:] @'Data.Either' String a@++The Error monad (also called the Exception monad).+-}++{-+ Rendered by Michael Weber <mailto:michael.weber@post.rwth-aachen.de>,+ inspired by the Haskell Monad Template Library from+ Andy Gill (<http://web.cecs.pdx.edu/~andy/>)+-}+module Control.Monad.Error (+ -- * Monads with error handling+ MonadError(..),+ Error(..),+ -- * The ErrorT monad transformer+ ErrorT(..),+ mapErrorT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ -- * Example 1: Custom Error Data Type+ -- $customErrorExample++ -- * Example 2: Using ErrorT Monad Transformer+ -- $ErrorTExample+ ) where++import Control.Monad.Error.Class+import Control.Monad.Trans+import Control.Monad.Trans.Error (ErrorT(..), mapErrorT)++import Control.Monad+import Control.Monad.Fix+import Control.Monad.Instances ()++{- $customErrorExample+Here is an example that demonstrates the use of a custom 'Error' data type with+the 'throwError' and 'catchError' exception mechanism from 'MonadError'.+The example throws an exception if the user enters an empty string+or a string longer than 5 characters. Otherwise it prints length of the string.++>-- This is the type to represent length calculation error.+>data LengthError = EmptyString -- Entered string was empty.+> | StringTooLong Int -- A string is longer than 5 characters.+> -- Records a length of the string.+> | OtherError String -- Other error, stores the problem description.+>+>-- We make LengthError an instance of the Error class+>-- to be able to throw it as an exception.+>instance Error LengthError where+> noMsg = OtherError "A String Error!"+> strMsg s = OtherError s+>+>-- Converts LengthError to a readable message.+>instance Show LengthError where+> show EmptyString = "The string was empty!"+> show (StringTooLong len) =+> "The length of the string (" ++ (show len) ++ ") is bigger than 5!"+> show (OtherError msg) = msg+>+>-- For our monad type constructor, we use Either LengthError+>-- which represents failure using Left LengthError+>-- or a successful result of type a using Right a.+>type LengthMonad = Either LengthError+>+>main = do+> putStrLn "Please enter a string:"+> s <- getLine+> reportResult (calculateLength s)+>+>-- Wraps length calculation to catch the errors.+>-- Returns either length of the string or an error.+>calculateLength :: String -> LengthMonad Int+>calculateLength s = (calculateLengthOrFail s) `catchError` Left+>+>-- Attempts to calculate length and throws an error if the provided string is+>-- empty or longer than 5 characters.+>-- The processing is done in Either monad.+>calculateLengthOrFail :: String -> LengthMonad Int+>calculateLengthOrFail [] = throwError EmptyString+>calculateLengthOrFail s | len > 5 = throwError (StringTooLong len)+> | otherwise = return len+> where len = length s+>+>-- Prints result of the string length calculation.+>reportResult :: LengthMonad Int -> IO ()+>reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len))+>reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e))+-}++{- $ErrorTExample+@'ErrorT'@ monad transformer can be used to add error handling to another monad.+Here is an example how to combine it with an @IO@ monad:++>import Control.Monad.Error+>+>-- An IO monad which can return String failure.+>-- It is convenient to define the monad type of the combined monad,+>-- especially if we combine more monad transformers.+>type LengthMonad = ErrorT String IO+>+>main = do+> -- runErrorT removes the ErrorT wrapper+> r <- runErrorT calculateLength+> reportResult r+>+>-- Asks user for a non-empty string and returns its length.+>-- Throws an error if user enters an empty string.+>calculateLength :: LengthMonad Int+>calculateLength = do+> -- all the IO operations have to be lifted to the IO monad in the monad stack+> liftIO $ putStrLn "Please enter a non-empty string: "+> s <- liftIO getLine+> if null s+> then throwError "The string was empty!"+> else return $ length s+>+>-- Prints result of the string length calculation.+>reportResult :: Either String Int -> IO ()+>reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len))+>reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e))+-}
+ Control/Monad/Error/Class.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE UndecidableInstances #-}++{- |+Module : Control.Monad.Error.Class+Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de> 2001,+ (c) Jeff Newbern 2003-2006,+ (c) Andriy Palamarchuk 2006+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : non-portable (multi-parameter type classes)++[Computation type:] Computations which may fail or throw exceptions.++[Binding strategy:] Failure records information about the cause\/location+of the failure. Failure values bypass the bound function,+other values are used as inputs to the bound function.++[Useful for:] Building computations from sequences of functions that may fail+or using exception handling to structure error handling.++[Zero and plus:] Zero is represented by an empty error and the plus operation+executes its second argument if the first fails.++[Example type:] @'Either' 'String' a@++The Error monad (also called the Exception monad).+-}++{-+ Rendered by Michael Weber <mailto:michael.weber@post.rwth-aachen.de>,+ inspired by the Haskell Monad Template Library from+ Andy Gill (<http://web.cecs.pdx.edu/~andy/>)+-}+module Control.Monad.Error.Class (+ Error(..),+ MonadError(..),+ ) where++import Control.Monad.Trans.Error (Error(..), ErrorT)+import qualified Control.Monad.Trans.Error as ErrorT (throwError, catchError)+import Control.Monad.Trans.Identity as Identity+import Control.Monad.Trans.List as List+import Control.Monad.Trans.Maybe as Maybe+import Control.Monad.Trans.Reader as Reader+import Control.Monad.Trans.RWS.Lazy as LazyRWS+import Control.Monad.Trans.RWS.Strict as StrictRWS+import Control.Monad.Trans.State.Lazy as LazyState+import Control.Monad.Trans.State.Strict as StrictState+import Control.Monad.Trans.Writer.Lazy as LazyWriter+import Control.Monad.Trans.Writer.Strict as StrictWriter++import Control.Monad.Trans.Class (lift)+import Control.Exception (IOException)+import Control.Monad+import Control.Monad.Instances ()+import Data.Monoid++{- |+The strategy of combining computations that can throw exceptions+by bypassing bound functions+from the point an exception is thrown to the point that it is handled.++Is parameterized over the type of error information and+the monad type constructor.+It is common to use @'Data.Either' String@ as the monad type constructor+for an error monad in which error descriptions take the form of strings.+In that case and many other common cases the resulting monad is already defined+as an instance of the 'MonadError' class.+You can also define your own error type and\/or use a monad type constructor+other than @'Either' 'String'@ or @'Either' 'IOError'@.+In these cases you will have to explicitly define instances of the 'Error'+and\/or 'MonadError' classes.+-}+class (Monad m) => MonadError e m | m -> e where+ -- | Is used within a monadic computation to begin exception processing.+ throwError :: e -> m a++ {- |+ A handler function to handle previous errors and return to normal execution.+ A common idiom is:++ > do { action1; action2; action3 } `catchError` handler++ where the @action@ functions can call 'throwError'.+ Note that @handler@ and the do-block must have the same return type.+ -}+ catchError :: m a -> (e -> m a) -> m a++instance MonadError IOException IO where+ throwError = ioError+ catchError = catch++-- ---------------------------------------------------------------------------+-- Our parameterizable error monad++instance Error e => MonadError e (Either e) where+ throwError = Left+ Left l `catchError` h = h l+ Right r `catchError` _ = Right r++instance (Monad m, Error e) => MonadError e (ErrorT e m) where+ throwError = ErrorT.throwError+ catchError = ErrorT.catchError++-- ---------------------------------------------------------------------------+-- Instances for other mtl transformers+--+-- All of these instances need UndecidableInstances,+-- because they do not satisfy the coverage condition.++instance MonadError e m => MonadError e (IdentityT m) where+ throwError = lift . throwError+ catchError = Identity.liftCatch catchError++instance MonadError e m => MonadError e (ListT m) where+ throwError = lift . throwError+ catchError = List.liftCatch catchError++instance MonadError e m => MonadError e (MaybeT m) where+ throwError = lift . throwError+ catchError = Maybe.liftCatch catchError++instance MonadError e m => MonadError e (ReaderT r m) where+ throwError = lift . throwError+ catchError = Reader.liftCatch catchError++instance (Monoid w, MonadError e m) => MonadError e (LazyRWS.RWST r w s m) where+ throwError = lift . throwError+ catchError = LazyRWS.liftCatch catchError++instance (Monoid w, MonadError e m) => MonadError e (StrictRWS.RWST r w s m) where+ throwError = lift . throwError+ catchError = StrictRWS.liftCatch catchError++instance MonadError e m => MonadError e (LazyState.StateT s m) where+ throwError = lift . throwError+ catchError = LazyState.liftCatch catchError++instance MonadError e m => MonadError e (StrictState.StateT s m) where+ throwError = lift . throwError+ catchError = StrictState.liftCatch catchError++instance (Monoid w, MonadError e m) => MonadError e (LazyWriter.WriterT w m) where+ throwError = lift . throwError+ catchError = LazyWriter.liftCatch catchError++instance (Monoid w, MonadError e m) => MonadError e (StrictWriter.WriterT w m) where+ throwError = lift . throwError+ catchError = StrictWriter.liftCatch catchError
+ Control/Monad/IO/Class.hs view
@@ -0,0 +1,37 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.IO.Class+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Class of monads based on @IO@.+-----------------------------------------------------------------------------++module Control.Monad.IO.Class (+ MonadIO(..)+ ) where++import System.IO (IO)++-- | Monads in which 'IO' computations may be embedded.+-- Any monad built by applying a sequence of monad transformers to the+-- 'IO' monad will be an instance of this class.+--+-- Instances should satisfy the following laws, which state that 'liftIO'+-- is a transformer of monads:+--+-- * @'liftIO' . 'return' = 'return'@+--+-- * @'liftIO' (m >>= f) = 'liftIO' m >>= ('liftIO' . f)@++class (Monad m) => MonadIO m where+ -- | Lift a computation from the 'IO' monad.+ liftIO :: IO a -> m a++instance MonadIO IO where+ liftIO = id
+ Control/Monad/Identity.hs view
@@ -0,0 +1,44 @@+{- |+Module : Control.Monad.Identity+Copyright : (c) Andy Gill 2001,+ (c) Oregon Graduate Institute of Science and Technology 2001,+ (c) Jeff Newbern 2003-2006,+ (c) Andriy Palamarchuk 2006+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : portable++[Computation type:] Simple function application.++[Binding strategy:] The bound function is applied to the input value.+@'Identity' x >>= f == 'Identity' (f x)@++[Useful for:] Monads can be derived from monad transformers applied to the+'Identity' monad.++[Zero and plus:] None.++[Example type:] @'Identity' a@++The @Identity@ monad is a monad that does not embody any computational strategy.+It simply applies the bound function to its input without any modification.+Computationally, there is no reason to use the @Identity@ monad+instead of the much simpler act of simply applying functions to their arguments.+The purpose of the @Identity@ monad is its fundamental role in the theory+of monad transformers.+Any monad transformer applied to the @Identity@ monad yields a non-transformer+version of that monad.+-}++module Control.Monad.Identity (+ module Data.Functor.Identity,++ module Control.Monad,+ module Control.Monad.Fix,+ ) where++import Control.Monad+import Control.Monad.Fix+import Data.Functor.Identity
+ Control/Monad/List.hs view
@@ -0,0 +1,25 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.List+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : portable+--+-- The List monad.+--+-----------------------------------------------------------------------------++module Control.Monad.List (+ ListT(..),+ mapListT,+ module Control.Monad,+ module Control.Monad.Trans,+ ) where++import Control.Monad+import Control.Monad.Trans+import Control.Monad.Trans.List
+ Control/Monad/RWS.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.RWS+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Declaration of the MonadRWS class.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.RWS (+ module Control.Monad.RWS.Lazy+ ) where+ +import Control.Monad.RWS.Lazy
+ Control/Monad/RWS/Class.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE UndecidableInstances #-}+-- Search for UndecidableInstances to see why this is needed++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.RWS.Class+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Declaration of the MonadRWS class.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.RWS.Class (+ MonadRWS,+ module Control.Monad.Reader.Class,+ module Control.Monad.State.Class,+ module Control.Monad.Writer.Class,+ ) where++import Control.Monad.Reader.Class+import Control.Monad.State.Class+import Control.Monad.Writer.Class++import Control.Monad.Trans.Class+import Control.Monad.Trans.Error(Error, ErrorT)+import Control.Monad.Trans.Maybe(MaybeT)+import Control.Monad.Trans.Identity(IdentityT)+import Control.Monad.Trans.RWS.Lazy as Lazy (RWST)+import qualified Control.Monad.Trans.RWS.Strict as Strict (RWST)++import Data.Monoid++class (Monoid w, MonadReader r m, MonadWriter w m, MonadState s m)+ => MonadRWS r w s m | m -> r, m -> w, m -> s++instance (Monoid w, Monad m) => MonadRWS r w s (Lazy.RWST r w s m)++instance (Monoid w, Monad m) => MonadRWS r w s (Strict.RWST r w s m)+ +---------------------------------------------------------------------------+-- Instances for other mtl transformers+--+-- All of these instances need UndecidableInstances,+-- because they do not satisfy the coverage condition.++instance (Error e, MonadRWS r w s m) => MonadRWS r w s (ErrorT e m)+instance MonadRWS r w s m => MonadRWS r w s (IdentityT m)+instance MonadRWS r w s m => MonadRWS r w s (MaybeT m)
+ Control/Monad/RWS/Lazy.hs view
@@ -0,0 +1,52 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.RWS.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Lazy RWS monad.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.RWS.Lazy (+ -- * The RWS monad+ RWS,+ rws,+ runRWS,+ evalRWS,+ execRWS,+ mapRWS,+ withRWS,+ -- * The RWST monad transformer+ RWST(..),+ evalRWST,+ execRWST,+ mapRWST,+ withRWST,+ -- * Lazy Reader-writer-state monads+ module Control.Monad.RWS.Class,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ module Data.Monoid,+ ) where++import Control.Monad.RWS.Class++import Control.Monad.Trans+import Control.Monad.Trans.RWS.Lazy (+ RWS, rws, runRWS, evalRWS, execRWS, mapRWS, withRWS,+ RWST(..), evalRWST, execRWST, mapRWST, withRWST)++import Control.Monad+import Control.Monad.Fix+import Data.Monoid
+ Control/Monad/RWS/Strict.hs view
@@ -0,0 +1,52 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.RWS.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Strict RWS monad.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.RWS.Strict (+ -- * The RWS monad+ RWS,+ rws,+ runRWS,+ evalRWS,+ execRWS,+ mapRWS,+ withRWS,+ -- * The RWST monad transformer+ RWST(..),+ evalRWST,+ execRWST,+ mapRWST,+ withRWST,+ -- * Strict Reader-writer-state monads+ module Control.Monad.RWS.Class,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ module Data.Monoid,+ ) where++import Control.Monad.RWS.Class++import Control.Monad.Trans+import Control.Monad.Trans.RWS.Strict (+ RWS, rws, runRWS, evalRWS, execRWS, mapRWS, withRWS,+ RWST(..), evalRWST, execRWST, mapRWST, withRWST)++import Control.Monad+import Control.Monad.Fix+import Data.Monoid
+ Control/Monad/Reader.hs view
@@ -0,0 +1,143 @@+{- |+Module : Control.Monad.Reader+Copyright : (c) Andy Gill 2001,+ (c) Oregon Graduate Institute of Science and Technology 2001,+ (c) Jeff Newbern 2003-2007,+ (c) Andriy Palamarchuk 2007+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies)++[Computation type:] Computations which read values from a shared environment.++[Binding strategy:] Monad values are functions from the environment to a value.+The bound function is applied to the bound value, and both have access+to the shared environment.++[Useful for:] Maintaining variable bindings, or other shared environment.++[Zero and plus:] None.++[Example type:] @'Reader' [(String,Value)] a@++The 'Reader' monad (also called the Environment monad).+Represents a computation, which can read values from+a shared environment, pass values from function to function,+and execute sub-computations in a modified environment.+Using 'Reader' monad for such computations is often clearer and easier+than using the 'Control.Monad.State.State' monad.++ Inspired by the paper+ /Functional Programming with Overloading and Higher-Order Polymorphism/,+ Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+ Advanced School of Functional Programming, 1995.+-}++module Control.Monad.Reader (+ -- * MonadReader class+ MonadReader(..),+ asks,+ -- * The Reader monad+ Reader,+ runReader,+ mapReader,+ withReader,+ -- * The ReaderT monad transformer+ ReaderT(..),+ mapReaderT,+ withReaderT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ -- * Example 1: Simple Reader Usage+ -- $simpleReaderExample++ -- * Example 2: Modifying Reader Content With @local@+ -- $localExample++ -- * Example 3: @ReaderT@ Monad Transformer+ -- $ReaderTExample+ ) where++import Control.Monad.Reader.Class++import Control.Monad.Trans.Reader (+ Reader, runReader, mapReader, withReader,+ ReaderT(..), mapReaderT, withReaderT)+import Control.Monad.Trans++import Control.Monad+import Control.Monad.Fix++{- $simpleReaderExample++In this example the @Reader@ monad provides access to variable bindings.+Bindings are a @Map@ of integer variables.+The variable @count@ contains number of variables in the bindings.+You can see how to run a Reader monad and retrieve data from it+with 'runReader', how to access the Reader data with 'ask' and 'asks'.++> type Bindings = Map String Int;+>+>-- Returns True if the "count" variable contains correct bindings size.+>isCountCorrect :: Bindings -> Bool+>isCountCorrect bindings = runReader calc_isCountCorrect bindings+>+>-- The Reader monad, which implements this complicated check.+>calc_isCountCorrect :: Reader Bindings Bool+>calc_isCountCorrect = do+> count <- asks (lookupVar "count")+> bindings <- ask+> return (count == (Map.size bindings))+>+>-- The selector function to use with 'asks'.+>-- Returns value of the variable with specified name.+>lookupVar :: String -> Bindings -> Int+>lookupVar name bindings = fromJust (Map.lookup name bindings)+>+>sampleBindings = Map.fromList [("count",3), ("1",1), ("b",2)]+>+>main = do+> putStr $ "Count is correct for bindings " ++ (show sampleBindings) ++ ": ";+> putStrLn $ show (isCountCorrect sampleBindings);+-}++{- $localExample++Shows how to modify Reader content with 'local'.++>calculateContentLen :: Reader String Int+>calculateContentLen = do+> content <- ask+> return (length content);+>+>-- Calls calculateContentLen after adding a prefix to the Reader content.+>calculateModifiedContentLen :: Reader String Int+>calculateModifiedContentLen = local ("Prefix " ++) calculateContentLen+>+>main = do+> let s = "12345";+> let modifiedLen = runReader calculateModifiedContentLen s+> let len = runReader calculateContentLen s+> putStrLn $ "Modified 's' length: " ++ (show modifiedLen)+> putStrLn $ "Original 's' length: " ++ (show len)+-}++{- $ReaderTExample++Now you are thinking: 'Wow, what a great monad! I wish I could use+Reader functionality in MyFavoriteComplexMonad!'. Don't worry.+This can be easy done with the 'ReaderT' monad transformer.+This example shows how to combine @ReaderT@ with the IO monad.++>-- The Reader/IO combined monad, where Reader stores a string.+>printReaderContent :: ReaderT String IO ()+>printReaderContent = do+> content <- ask+> liftIO $ putStrLn ("The Reader Content: " ++ content)+>+>main = do+> runReaderT printReaderContent "Some Content"+-}
+ Control/Monad/Reader/Class.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE UndecidableInstances #-}+-- Search for UndecidableInstances to see why this is needed+{- |+Module : Control.Monad.Reader.Class+Copyright : (c) Andy Gill 2001,+ (c) Oregon Graduate Institute of Science and Technology 2001,+ (c) Jeff Newbern 2003-2007,+ (c) Andriy Palamarchuk 2007+License : BSD-style (see the file LICENSE)++Maintainer : libraries@haskell.org+Stability : experimental+Portability : non-portable (multi-param classes, functional dependencies)++[Computation type:] Computations which read values from a shared environment.++[Binding strategy:] Monad values are functions from the environment to a value.+The bound function is applied to the bound value, and both have access+to the shared environment.++[Useful for:] Maintaining variable bindings, or other shared environment.++[Zero and plus:] None.++[Example type:] @'Reader' [(String,Value)] a@++The 'Reader' monad (also called the Environment monad).+Represents a computation, which can read values from+a shared environment, pass values from function to function,+and execute sub-computations in a modified environment.+Using 'Reader' monad for such computations is often clearer and easier+than using the 'Control.Monad.State.State' monad.++ Inspired by the paper+ /Functional Programming with Overloading and Higher-Order Polymorphism/,+ Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+ Advanced School of Functional Programming, 1995.+-}++module Control.Monad.Reader.Class (+ MonadReader(..),+ asks,+ ) where++import Control.Monad.Trans.Cont as Cont+import Control.Monad.Trans.Error+import Control.Monad.Trans.Identity+import Control.Monad.Trans.List+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Reader (ReaderT)+import qualified Control.Monad.Trans.Reader as ReaderT (ask, local, reader)+import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS (RWST, ask, local, reader)+import qualified Control.Monad.Trans.RWS.Strict as StrictRWS (RWST, ask, local, reader)+import Control.Monad.Trans.State.Lazy as Lazy+import Control.Monad.Trans.State.Strict as Strict+import Control.Monad.Trans.Writer.Lazy as Lazy+import Control.Monad.Trans.Writer.Strict as Strict++import Control.Monad.Trans.Class (lift)+import Control.Monad+import Data.Monoid++-- ----------------------------------------------------------------------------+-- class MonadReader+-- asks for the internal (non-mutable) state.++-- | See examples in "Control.Monad.Reader".+-- Note, the partially applied function type @(->) r@ is a simple reader monad.+-- See the @instance@ declaration below.+class Monad m => MonadReader r m | m -> r where+ -- | Retrieves the monad environment.+ ask :: m r++ -- | Executes a computation in a modified environment.+ local :: (r -> r) -- ^ The function to modify the environment.+ -> m a -- ^ @Reader@ to run in the modified environment.+ -> m a++ -- | Retrieves a function of the current environment.+ reader :: (r -> a) -- ^ The selector function to apply to the environment.+ -> m a+ reader f = do+ r <- ask+ return (f r)++-- | Retrieves a function of the current environment.+asks :: MonadReader r m+ => (r -> a) -- ^ The selector function to apply to the environment.+ -> m a+asks = reader++-- ----------------------------------------------------------------------------+-- The partially applied function type is a simple reader monad++instance MonadReader r ((->) r) where+ ask = id+ local f m = m . f+ reader = id++instance Monad m => MonadReader r (ReaderT r m) where+ ask = ReaderT.ask+ local = ReaderT.local+ reader = ReaderT.reader++instance (Monad m, Monoid w) => MonadReader r (LazyRWS.RWST r w s m) where+ ask = LazyRWS.ask+ local = LazyRWS.local+ reader = LazyRWS.reader++instance (Monad m, Monoid w) => MonadReader r (StrictRWS.RWST r w s m) where+ ask = StrictRWS.ask+ local = StrictRWS.local+ reader = StrictRWS.reader++-- ---------------------------------------------------------------------------+-- Instances for other mtl transformers+--+-- All of these instances need UndecidableInstances,+-- because they do not satisfy the coverage condition.++instance MonadReader r' m => MonadReader r' (ContT r m) where+ ask = lift ask+ local = Cont.liftLocal ask local+ reader = lift . reader++instance (Error e, MonadReader r m) => MonadReader r (ErrorT e m) where+ ask = lift ask+ local = mapErrorT . local+ reader = lift . reader++instance MonadReader r m => MonadReader r (IdentityT m) where+ ask = lift ask+ local = mapIdentityT . local+ reader = lift . reader++instance MonadReader r m => MonadReader r (ListT m) where+ ask = lift ask+ local = mapListT . local+ reader = lift . reader++instance MonadReader r m => MonadReader r (MaybeT m) where+ ask = lift ask+ local = mapMaybeT . local+ reader = lift . reader++instance MonadReader r m => MonadReader r (Lazy.StateT s m) where+ ask = lift ask+ local = Lazy.mapStateT . local+ reader = lift . reader++instance MonadReader r m => MonadReader r (Strict.StateT s m) where+ ask = lift ask+ local = Strict.mapStateT . local+ reader = lift . reader++instance (Monoid w, MonadReader r m) => MonadReader r (Lazy.WriterT w m) where+ ask = lift ask+ local = Lazy.mapWriterT . local+ reader = lift . reader++instance (Monoid w, MonadReader r m) => MonadReader r (Strict.WriterT w m) where+ ask = lift ask+ local = Strict.mapWriterT . local+ reader = lift . reader
+ Control/Monad/State.hs view
@@ -0,0 +1,25 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.State+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- State monads.+--+-- This module is inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.++-----------------------------------------------------------------------------++module Control.Monad.State (+ module Control.Monad.State.Lazy+ ) where++import Control.Monad.State.Lazy
+ Control/Monad/State/Class.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE UndecidableInstances #-}+-- Search for UndecidableInstances to see why this is needed++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.State.Class+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- MonadState class.+--+-- This module is inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.++-----------------------------------------------------------------------------++module Control.Monad.State.Class (+ MonadState(..),+ modify,+ gets,+ ) where++import Control.Monad.Trans.Cont+import Control.Monad.Trans.Error+import Control.Monad.Trans.Identity+import Control.Monad.Trans.List+import Control.Monad.Trans.Maybe+import Control.Monad.Trans.Reader+import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS (RWST, get, put, state)+import qualified Control.Monad.Trans.RWS.Strict as StrictRWS (RWST, get, put, state)+import qualified Control.Monad.Trans.State.Lazy as Lazy (StateT, get, put, state)+import qualified Control.Monad.Trans.State.Strict as Strict (StateT, get, put, state)+import Control.Monad.Trans.Writer.Lazy as Lazy+import Control.Monad.Trans.Writer.Strict as Strict++import Control.Monad.Trans.Class (lift)+import Control.Monad+import Data.Monoid++-- ---------------------------------------------------------------------------++-- | Minimal definition is either both of @get@ and @put@ or just @state@+class Monad m => MonadState s m | m -> s where+ -- | Return the state from the internals of the monad.+ get :: m s+ get = state (\s -> (s, s))++ -- | Replace the state inside the monad.+ put :: s -> m ()+ put s = state (\_ -> ((), s))++ -- | Embed a simple state action into the monad.+ state :: (s -> (a, s)) -> m a+ state f = do+ s <- get+ let ~(a, s') = f s+ put s'+ return a++-- | Monadic state transformer.+--+-- Maps an old state to a new state inside a state monad.+-- The old state is thrown away.+--+-- > Main> :t modify ((+1) :: Int -> Int)+-- > modify (...) :: (MonadState Int a) => a ()+--+-- This says that @modify (+1)@ acts over any+-- Monad that is a member of the @MonadState@ class,+-- with an @Int@ state.+modify :: MonadState s m => (s -> s) -> m ()+modify f = state (\s -> ((), f s))++-- | Gets specific component of the state, using a projection function+-- supplied.+gets :: MonadState s m => (s -> a) -> m a+gets f = do+ s <- get+ return (f s)++instance Monad m => MonadState s (Lazy.StateT s m) where+ get = Lazy.get+ put = Lazy.put+ state = Lazy.state++instance Monad m => MonadState s (Strict.StateT s m) where+ get = Strict.get+ put = Strict.put+ state = Strict.state++instance (Monad m, Monoid w) => MonadState s (LazyRWS.RWST r w s m) where+ get = LazyRWS.get+ put = LazyRWS.put+ state = LazyRWS.state++instance (Monad m, Monoid w) => MonadState s (StrictRWS.RWST r w s m) where+ get = StrictRWS.get+ put = StrictRWS.put+ state = StrictRWS.state++-- ---------------------------------------------------------------------------+-- Instances for other mtl transformers+--+-- All of these instances need UndecidableInstances,+-- because they do not satisfy the coverage condition.++instance MonadState s m => MonadState s (ContT r m) where+ get = lift get+ put = lift . put+ state = lift . state++instance (Error e, MonadState s m) => MonadState s (ErrorT e m) where+ get = lift get+ put = lift . put+ state = lift . state++instance MonadState s m => MonadState s (IdentityT m) where+ get = lift get+ put = lift . put+ state = lift . state++instance MonadState s m => MonadState s (ListT m) where+ get = lift get+ put = lift . put+ state = lift . state++instance MonadState s m => MonadState s (MaybeT m) where+ get = lift get+ put = lift . put+ state = lift . state++instance MonadState s m => MonadState s (ReaderT r m) where+ get = lift get+ put = lift . put+ state = lift . state++instance (Monoid w, MonadState s m) => MonadState s (Lazy.WriterT w m) where+ get = lift get+ put = lift . put+ state = lift . state++instance (Monoid w, MonadState s m) => MonadState s (Strict.WriterT w m) where+ get = lift get+ put = lift . put+ state = lift . state
+ Control/Monad/State/Lazy.hs view
@@ -0,0 +1,128 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.State.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Lazy state monads.+--+-- This module is inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.++-----------------------------------------------------------------------------++module Control.Monad.State.Lazy (+ -- * MonadState class+ MonadState(..),+ modify,+ gets,+ -- * The State monad+ State,+ runState,+ evalState,+ execState,+ mapState,+ withState,+ -- * The StateT monad transformer+ StateT(..),+ evalStateT,+ execStateT,+ mapStateT,+ withStateT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ -- * Examples+ -- $examples+ ) where++import Control.Monad.State.Class++import Control.Monad.Trans+import Control.Monad.Trans.State.Lazy+ (State, runState, evalState, execState, mapState, withState,+ StateT(..), evalStateT, execStateT, mapStateT, withStateT)++import Control.Monad+import Control.Monad.Fix++-- ---------------------------------------------------------------------------+-- $examples+-- A function to increment a counter. Taken from the paper+-- /Generalising Monads to Arrows/, John+-- Hughes (<http://www.math.chalmers.se/~rjmh/>), November 1998:+--+-- > tick :: State Int Int+-- > tick = do n <- get+-- > put (n+1)+-- > return n+--+-- Add one to the given number using the state monad:+--+-- > plusOne :: Int -> Int+-- > plusOne n = execState tick n+--+-- A contrived addition example. Works only with positive numbers:+--+-- > plus :: Int -> Int -> Int+-- > plus n x = execState (sequence $ replicate n tick) x+--+-- An example from /The Craft of Functional Programming/, Simon+-- Thompson (<http://www.cs.kent.ac.uk/people/staff/sjt/>),+-- Addison-Wesley 1999: \"Given an arbitrary tree, transform it to a+-- tree of integers in which the original elements are replaced by+-- natural numbers, starting from 0. The same element has to be+-- replaced by the same number at every occurrence, and when we meet+-- an as-yet-unvisited element we have to find a \'new\' number to match+-- it with:\"+--+-- > data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show, Eq)+-- > type Table a = [a]+--+-- > numberTree :: Eq a => Tree a -> State (Table a) (Tree Int)+-- > numberTree Nil = return Nil+-- > numberTree (Node x t1 t2)+-- > = do num <- numberNode x+-- > nt1 <- numberTree t1+-- > nt2 <- numberTree t2+-- > return (Node num nt1 nt2)+-- > where+-- > numberNode :: Eq a => a -> State (Table a) Int+-- > numberNode x+-- > = do table <- get+-- > (newTable, newPos) <- return (nNode x table)+-- > put newTable+-- > return newPos+-- > nNode:: (Eq a) => a -> Table a -> (Table a, Int)+-- > nNode x table+-- > = case (findIndexInList (== x) table) of+-- > Nothing -> (table ++ [x], length table)+-- > Just i -> (table, i)+-- > findIndexInList :: (a -> Bool) -> [a] -> Maybe Int+-- > findIndexInList = findIndexInListHelp 0+-- > findIndexInListHelp _ _ [] = Nothing+-- > findIndexInListHelp count f (h:t)+-- > = if (f h)+-- > then Just count+-- > else findIndexInListHelp (count+1) f t+--+-- numTree applies numberTree with an initial state:+--+-- > numTree :: (Eq a) => Tree a -> Tree Int+-- > numTree t = evalState (numberTree t) []+--+-- > testTree = Node "Zero" (Node "One" (Node "Two" Nil Nil) (Node "One" (Node "Zero" Nil Nil) Nil)) Nil+-- > numTree testTree => Node 0 (Node 1 (Node 2 Nil Nil) (Node 1 (Node 0 Nil Nil) Nil)) Nil+--+-- sumTree is a little helper function that does not use the State monad:+--+-- > sumTree :: (Num a) => Tree a -> a+-- > sumTree Nil = 0+-- > sumTree (Node e t1 t2) = e + (sumTree t1) + (sumTree t2)
+ Control/Monad/State/Strict.hs view
@@ -0,0 +1,128 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.State.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Strict state monads.+--+-- This module is inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/>)+-- Advanced School of Functional Programming, 1995.++-----------------------------------------------------------------------------++module Control.Monad.State.Strict (+ -- * MonadState class+ MonadState(..),+ modify,+ gets,+ -- * The State monad+ State,+ runState,+ evalState,+ execState,+ mapState,+ withState,+ -- * The StateT monad transformer+ StateT(..),+ evalStateT,+ execStateT,+ mapStateT,+ withStateT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ -- * Examples+ -- $examples+ ) where++import Control.Monad.State.Class++import Control.Monad.Trans+import Control.Monad.Trans.State.Strict+ (State, runState, evalState, execState, mapState, withState,+ StateT(..), evalStateT, execStateT, mapStateT, withStateT)++import Control.Monad+import Control.Monad.Fix++-- ---------------------------------------------------------------------------+-- $examples+-- A function to increment a counter. Taken from the paper+-- /Generalising Monads to Arrows/, John+-- Hughes (<http://www.math.chalmers.se/~rjmh/>), November 1998:+--+-- > tick :: State Int Int+-- > tick = do n <- get+-- > put (n+1)+-- > return n+--+-- Add one to the given number using the state monad:+--+-- > plusOne :: Int -> Int+-- > plusOne n = execState tick n+--+-- A contrived addition example. Works only with positive numbers:+--+-- > plus :: Int -> Int -> Int+-- > plus n x = execState (sequence $ replicate n tick) x+--+-- An example from /The Craft of Functional Programming/, Simon+-- Thompson (<http://www.cs.kent.ac.uk/people/staff/sjt/>),+-- Addison-Wesley 1999: \"Given an arbitrary tree, transform it to a+-- tree of integers in which the original elements are replaced by+-- natural numbers, starting from 0. The same element has to be+-- replaced by the same number at every occurrence, and when we meet+-- an as-yet-unvisited element we have to find a \'new\' number to match+-- it with:\"+--+-- > data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show, Eq)+-- > type Table a = [a]+--+-- > numberTree :: Eq a => Tree a -> State (Table a) (Tree Int)+-- > numberTree Nil = return Nil+-- > numberTree (Node x t1 t2)+-- > = do num <- numberNode x+-- > nt1 <- numberTree t1+-- > nt2 <- numberTree t2+-- > return (Node num nt1 nt2)+-- > where+-- > numberNode :: Eq a => a -> State (Table a) Int+-- > numberNode x+-- > = do table <- get+-- > (newTable, newPos) <- return (nNode x table)+-- > put newTable+-- > return newPos+-- > nNode:: (Eq a) => a -> Table a -> (Table a, Int)+-- > nNode x table+-- > = case (findIndexInList (== x) table) of+-- > Nothing -> (table ++ [x], length table)+-- > Just i -> (table, i)+-- > findIndexInList :: (a -> Bool) -> [a] -> Maybe Int+-- > findIndexInList = findIndexInListHelp 0+-- > findIndexInListHelp _ _ [] = Nothing+-- > findIndexInListHelp count f (h:t)+-- > = if (f h)+-- > then Just count+-- > else findIndexInListHelp (count+1) f t+--+-- numTree applies numberTree with an initial state:+--+-- > numTree :: (Eq a) => Tree a -> Tree Int+-- > numTree t = evalState (numberTree t) []+--+-- > testTree = Node "Zero" (Node "One" (Node "Two" Nil Nil) (Node "One" (Node "Zero" Nil Nil) Nil)) Nil+-- > numTree testTree => Node 0 (Node 1 (Node 2 Nil Nil) (Node 1 (Node 0 Nil Nil) Nil)) Nil+--+-- sumTree is a little helper function that does not use the State monad:+--+-- > sumTree :: (Num a) => Tree a -> a+-- > sumTree Nil = 0+-- > sumTree (Node e t1 t2) = e + (sumTree t1) + (sumTree t2)
+ Control/Monad/Trans.hs view
@@ -0,0 +1,34 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : portable+--+-- Classes for monad transformers.+--+-- A monad transformer makes new monad out of an existing monad, such+-- that computations of the old monad may be embedded in the new one.+-- To construct a monad with a desired set of features, one typically+-- starts with a base monad, such as @Identity@, @[]@ or 'IO', and+-- applies a sequence of monad transformers.+--+-- Most monad transformer modules include the special case of applying the+-- transformer to @Identity@. For example, @State s@ is an abbreviation+-- for @StateT s Identity@.+--+-- Each monad transformer also comes with an operation @run@/XXX/ to+-- unwrap the transformer, exposing a computation of the inner monad.+-----------------------------------------------------------------------------++module Control.Monad.Trans (+ module Control.Monad.Trans.Class,+ module Control.Monad.IO.Class+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class
+ Control/Monad/Trans/Class.hs view
@@ -0,0 +1,123 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Class+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Classes for monad transformers.+--+-- A monad transformer makes a new monad out of an existing monad, such+-- that computations of the old monad may be embedded in the new one.+-- To construct a monad with a desired set of features, one typically+-- starts with a base monad, such as @Identity@, @[]@ or 'IO', and+-- applies a sequence of monad transformers.+--+-- Most monad transformer modules include the special case of applying the+-- transformer to @Identity@. For example, @State s@ is an abbreviation+-- for @StateT s Identity@.+--+-- Each monad transformer also comes with an operation @run@/XXX/ to+-- unwrap the transformer, exposing a computation of the inner monad.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Class (+ -- * Transformer class+ MonadTrans(..)++ -- * Examples+ -- ** Parsing+ -- $example1++ -- ** Parsing and counting+ -- $example2+ ) where++-- | The class of monad transformers. Instances should satisfy the+-- following laws, which state that 'lift' is a transformer of monads:+--+-- * @'lift' . 'return' = 'return'@+--+-- * @'lift' (m >>= f) = 'lift' m >>= ('lift' . f)@++class MonadTrans t where+ -- | Lift a computation from the argument monad to the constructed monad.+ lift :: Monad m => m a -> t m a++{- $example1++One might define a parsing monad by adding a state (the 'String' remaining+to be parsed) to the @[]@ monad, which provides non-determinism:++> import Control.Monad.Trans.State+>+> type Parser = StateT String []++Then @Parser@ is an instance of @MonadPlus@: monadic sequencing implements+concatenation of parsers, while @mplus@ provides choice.+To use parsers, we need a primitive to run a constructed parser on an+input string:++> runParser :: Parser a -> String -> [a]+> runParser p s = [x | (x, "") <- runStateT p s]++Finally, we need a primitive parser that matches a single character,+from which arbitrarily complex parsers may be constructed:++> item :: Parser Char+> item = do+> c:cs <- get+> put cs+> return c++In this example we use the operations @get@ and @put@ from+"Control.Monad.Trans.State", which are defined only for monads that are+applications of @StateT@. Alternatively one could use monad classes+from the @mtl@ package or similar, which contain methods @get@ and @put@+with types generalized over all suitable monads.+-}++{- $example2++We can define a parser that also counts by adding a @WriterT@ transformer:++> import Control.Monad.Trans.Class+> import Control.Monad.Trans.State+> import Control.Monad.Trans.Writer+> import Data.Monoid+>+> type Parser = WriterT (Sum Int) (StateT String [])++The function that applies a parser must now unwrap each of the monad+transformers in turn:++> runParser :: Parser a -> String -> [(a, Int)]+> runParser p s = [(x, n) | ((x, Sum n), "") <- runStateT (runWriterT p) s]++To define @item@ parser, we need to lift the @StateT@ operations through+the @WriterT@ transformers.++> item :: Parser Char+> item = do+> c:cs <- lift get+> lift (put cs)+> return c++In this case, we were able to do this with 'lift', but operations with+more complex types require special lifting functions, which are provided+by monad transformers for which they can be implemented. If you use the+monad classes of the @mtl@ package or similar, this lifting is handled+automatically by the instances of the classes, and you need only use+the generalized methods @get@ and @put@.++We can also define a primitive using the Writer:++> tick :: Parser ()+> tick = tell (Sum 1)++Then the parser will keep track of how many @tick@s it executes.+-}
+ Control/Monad/Trans/Cont.hs view
@@ -0,0 +1,137 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Cont+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Continuation monads.+--+-----------------------------------------------------------------------------++module Control.Monad.Trans.Cont (+ -- * The Cont monad+ Cont,+ cont,+ runCont,+ mapCont,+ withCont,+ -- * The ContT monad transformer+ ContT(..),+ mapContT,+ withContT,+ callCC,+ -- * Lifting other operations+ liftLocal,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad++{- |+Continuation monad.+@Cont r a@ is a CPS computation that produces an intermediate result+of type @a@ within a CPS computation whose final result type is @r@.++The @return@ function simply creates a continuation which passes the value on.++The @>>=@ operator adds the bound function into the continuation chain.+-}+type Cont r = ContT r Identity++-- | Construct a continuation-passing computation from a function.+-- (The inverse of 'runCont'.)+cont :: ((a -> r) -> r) -> Cont r a+cont f = ContT (\ k -> Identity (f (runIdentity . k)))++-- | Runs a CPS computation, returns its result after applying the final+-- continuation to it.+-- (The inverse of 'cont'.)+runCont :: Cont r a -- ^ continuation computation (@Cont@).+ -> (a -> r) -- ^ the final continuation, which produces+ -- the final result (often 'id').+ -> r+runCont m k = runIdentity (runContT m (Identity . k))++-- | Apply a function to transform the result of a continuation-passing+-- computation.+--+-- * @'runCont' ('mapCont' f m) = f . 'runCont' m@+mapCont :: (r -> r) -> Cont r a -> Cont r a+mapCont f = mapContT (Identity . f . runIdentity)++-- | Apply a function to transform the continuation passed to a CPS+-- computation.+--+-- * @'runCont' ('withCont' f m) = 'runCont' m . f@+withCont :: ((b -> r) -> (a -> r)) -> Cont r a -> Cont r b+withCont f = withContT ((Identity .) . f . (runIdentity .))++{- |+The continuation monad transformer.+Can be used to add continuation handling to other monads.+-}+newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }++-- | Apply a function to transform the result of a continuation-passing+-- computation.+--+-- * @'runContT' ('mapContT' f m) = f . 'runContT' m@+mapContT :: (m r -> m r) -> ContT r m a -> ContT r m a+mapContT f m = ContT $ f . runContT m++-- | Apply a function to transform the continuation passed to a CPS+-- computation.+--+-- * @'runContT' ('withContT' f m) = 'runContT' m . f@+withContT :: ((b -> m r) -> (a -> m r)) -> ContT r m a -> ContT r m b+withContT f m = ContT $ runContT m . f++instance Functor (ContT r m) where+ fmap f m = ContT $ \c -> runContT m (c . f)++instance Applicative (ContT r m) where+ pure a = ContT ($ a)+ f <*> v = ContT $ \ k -> runContT f $ \ g -> runContT v (k . g)++instance Monad (ContT r m) where+ return a = ContT ($ a)+ m >>= k = ContT $ \c -> runContT m (\a -> runContT (k a) c)++instance MonadTrans (ContT r) where+ lift m = ContT (m >>=)++instance (MonadIO m) => MonadIO (ContT r m) where+ liftIO = lift . liftIO++-- | @callCC@ (call-with-current-continuation) calls its argument+-- function, passing it the current continuation. It provides+-- an escape continuation mechanism for use with continuation+-- monads. Escape continuations one allow to abort the current+-- computation and return a value immediately. They achieve a+-- similar effect to 'Control.Monad.Trans.Error.throwError'+-- and 'Control.Monad.Trans.Error.catchError' within an+-- 'Control.Monad.Trans.Error.ErrorT' monad. The advantage of this+-- function over calling 'return' is that it makes the continuation+-- explicit, allowing more flexibility and better control.+--+-- The standard idiom used with @callCC@ is to provide a lambda-expression+-- to name the continuation. Then calling the named continuation anywhere+-- within its scope will escape from the computation, even if it is many+-- layers deep within nested computations.+callCC :: ((a -> ContT r m b) -> ContT r m a) -> ContT r m a+callCC f = ContT $ \c -> runContT (f (\a -> ContT $ \_ -> c a)) c++-- | @'liftLocal' ask local@ yields a @local@ function for @'ContT' r m@.+liftLocal :: Monad m => m r' -> ((r' -> r') -> m r -> m r) ->+ (r' -> r') -> ContT r m a -> ContT r m a+liftLocal ask local f m = ContT $ \c -> do+ r <- ask+ local f (runContT m (local (const r) . c))
+ Control/Monad/Trans/Error.hs view
@@ -0,0 +1,267 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Error+-- Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de> 2001,+-- (c) Jeff Newbern 2003-2006,+-- (c) Andriy Palamarchuk 2006+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- This monad transformer adds the ability to fail or throw exceptions+-- to a monad.+--+-- A sequence of actions succeeds, producing a value, only if all the+-- actions in the sequence are successful. If one fails with an error,+-- the rest of the sequence is skipped and the composite action fails+-- with that error.+--+-- If the value of the error is not required, the variant in+-- "Control.Monad.Trans.Maybe" may be used instead.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Error (+ -- * The ErrorT monad transformer+ Error(..),+ ErrorList(..),+ ErrorT(..),+ mapErrorT,+ -- * Error operations+ throwError,+ catchError,+ -- * Lifting other operations+ liftCallCC,+ liftListen,+ liftPass,+ -- * Examples+ -- $examples+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class++import Control.Applicative+import Control.Exception (IOException)+import Control.Monad+import Control.Monad.Fix+import Control.Monad.Instances ()+import Data.Foldable (Foldable(foldMap))+import Data.Monoid (mempty)+import Data.Traversable (Traversable(traverse))+import System.IO.Error++instance MonadPlus IO where+ mzero = ioError (userError "mzero")+ m `mplus` n = m `catchIOError` \_ -> n++#if !(MIN_VERSION_base(4,4,0))+-- exported by System.IO.Error from base-4.4+catchIOError :: IO a -> (IOError -> IO a) -> IO a+catchIOError = catch+#endif++-- | An exception to be thrown.+--+-- Minimal complete definition: 'noMsg' or 'strMsg'.+class Error a where+ -- | Creates an exception without a message.+ -- The default implementation is @'strMsg' \"\"@.+ noMsg :: a+ -- | Creates an exception with a message.+ -- The default implementation of @'strMsg' s@ is 'noMsg'.+ strMsg :: String -> a++ noMsg = strMsg ""+ strMsg _ = noMsg++instance Error IOException where+ strMsg = userError++-- | A string can be thrown as an error.+instance ErrorList a => Error [a] where+ strMsg = listMsg++-- | Workaround so that we can have a Haskell 98 instance @'Error' 'String'@.+class ErrorList a where+ listMsg :: String -> [a]++instance ErrorList Char where+ listMsg = id++-- ---------------------------------------------------------------------------+-- Our parameterizable error monad++#if !(MIN_VERSION_base(4,3,0))++-- These instances are in base-4.3++instance Applicative (Either e) where+ pure = Right+ Left e <*> _ = Left e+ Right f <*> r = fmap f r++instance Monad (Either e) where+ return = Right+ Left l >>= _ = Left l+ Right r >>= k = k r++instance MonadFix (Either e) where+ mfix f = let+ a = f $ case a of+ Right r -> r+ _ -> error "empty mfix argument"+ in a++#endif /* base to 4.2.0.x */++instance (Error e) => Alternative (Either e) where+ empty = Left noMsg+ Left _ <|> n = n+ m <|> _ = m++instance (Error e) => MonadPlus (Either e) where+ mzero = Left noMsg+ Left _ `mplus` n = n+ m `mplus` _ = m++-- | The error monad transformer. It can be used to add error handling+-- to other monads.+--+-- The @ErrorT@ Monad structure is parameterized over two things:+--+-- * e - The error type.+--+-- * m - The inner monad.+--+-- The 'return' function yields a successful computation, while @>>=@+-- sequences two subcomputations, failing on the first error.+newtype ErrorT e m a = ErrorT { runErrorT :: m (Either e a) }++-- | Map the unwrapped computation using the given function.+--+-- * @'runErrorT' ('mapErrorT' f m) = f ('runErrorT' m)@+mapErrorT :: (m (Either e a) -> n (Either e' b))+ -> ErrorT e m a+ -> ErrorT e' n b+mapErrorT f m = ErrorT $ f (runErrorT m)++instance (Functor m) => Functor (ErrorT e m) where+ fmap f = ErrorT . fmap (fmap f) . runErrorT++instance (Foldable f) => Foldable (ErrorT e f) where+ foldMap f (ErrorT a) = foldMap (either (const mempty) f) a++instance (Traversable f) => Traversable (ErrorT e f) where+ traverse f (ErrorT a) =+ ErrorT <$> traverse (either (pure . Left) (fmap Right . f)) a++instance (Functor m, Monad m) => Applicative (ErrorT e m) where+ pure a = ErrorT $ return (Right a)+ f <*> v = ErrorT $ do+ mf <- runErrorT f+ case mf of+ Left e -> return (Left e)+ Right k -> do+ mv <- runErrorT v+ case mv of+ Left e -> return (Left e)+ Right x -> return (Right (k x))++instance (Functor m, Monad m, Error e) => Alternative (ErrorT e m) where+ empty = mzero+ (<|>) = mplus++instance (Monad m, Error e) => Monad (ErrorT e m) where+ return a = ErrorT $ return (Right a)+ m >>= k = ErrorT $ do+ a <- runErrorT m+ case a of+ Left l -> return (Left l)+ Right r -> runErrorT (k r)+ fail msg = ErrorT $ return (Left (strMsg msg))++instance (Monad m, Error e) => MonadPlus (ErrorT e m) where+ mzero = ErrorT $ return (Left noMsg)+ m `mplus` n = ErrorT $ do+ a <- runErrorT m+ case a of+ Left _ -> runErrorT n+ Right r -> return (Right r)++instance (MonadFix m, Error e) => MonadFix (ErrorT e m) where+ mfix f = ErrorT $ mfix $ \a -> runErrorT $ f $ case a of+ Right r -> r+ _ -> error "empty mfix argument"++instance (Error e) => MonadTrans (ErrorT e) where+ lift m = ErrorT $ do+ a <- m+ return (Right a)++instance (Error e, MonadIO m) => MonadIO (ErrorT e m) where+ liftIO = lift . liftIO++-- | Signal an error value @e@.+--+-- * @'runErrorT' ('throwError' e) = 'return' ('Left' e)@+--+-- * @'throwError' e >>= m = 'throwError' e@+throwError :: (Monad m, Error e) => e -> ErrorT e m a+throwError l = ErrorT $ return (Left l)++-- | Handle an error.+--+-- * @'catchError' h ('lift' m) = 'lift' m@+--+-- * @'catchError' h ('throwError' e) = h e@+catchError :: (Monad m, Error e) =>+ ErrorT e m a -- ^ the inner computation+ -> (e -> ErrorT e m a) -- ^ a handler for errors in the inner+ -- computation+ -> ErrorT e m a+m `catchError` h = ErrorT $ do+ a <- runErrorT m+ case a of+ Left l -> runErrorT (h l)+ Right r -> return (Right r)++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: (((Either e a -> m (Either e b)) -> m (Either e a)) ->+ m (Either e a)) -> ((a -> ErrorT e m b) -> ErrorT e m a) -> ErrorT e m a+liftCallCC callCC f = ErrorT $+ callCC $ \c ->+ runErrorT (f (\a -> ErrorT $ c (Right a)))++-- | Lift a @listen@ operation to the new monad.+liftListen :: Monad m =>+ (m (Either e a) -> m (Either e a,w)) -> ErrorT e m a -> ErrorT e m (a,w)+liftListen listen = mapErrorT $ \ m -> do+ (a, w) <- listen m+ return $! fmap (\ r -> (r, w)) a++-- | Lift a @pass@ operation to the new monad.+liftPass :: Monad m => (m (Either e a,w -> w) -> m (Either e a)) ->+ ErrorT e m (a,w -> w) -> ErrorT e m a+liftPass pass = mapErrorT $ \ m -> pass $ do+ a <- m+ return $! case a of+ Left l -> (Left l, id)+ Right (r, f) -> (Right r, f)++{- $examples++Wrapping an IO action that can throw an error @e@:++> type ErrorWithIO e a = ErrorT e IO a+> ==> ErrorT (IO (Either e a))++An IO monad wrapped in @StateT@ inside of @ErrorT@:++> type ErrorAndStateWithIO e s a = ErrorT e (StateT s IO) a+> ==> ErrorT (StateT s IO (Either e a))+> ==> ErrorT (StateT (s -> IO (Either e a,s)))++-}
+ Control/Monad/Trans/Identity.hs view
@@ -0,0 +1,89 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Identity+-- Copyright : (c) 2007 Magnus Therning+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The identity monad transformer.+--+-- This is useful for functions parameterized by a monad transformer.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Identity (+ -- * The identity monad transformer+ IdentityT(..),+ mapIdentityT,+ -- * Lifting other operations+ liftCatch,+ liftCallCC,+ ) where++import Control.Applicative+import Control.Monad (MonadPlus(mzero, mplus))+import Control.Monad.Fix (MonadFix(mfix))+import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Trans.Class (MonadTrans(lift))+import Data.Foldable (Foldable(foldMap))+import Data.Traversable (Traversable(traverse))++-- | The trivial monad transformer, which maps a monad to an equivalent monad.+newtype IdentityT m a = IdentityT { runIdentityT :: m a }++instance (Functor m) => Functor (IdentityT m) where+ fmap f = mapIdentityT (fmap f)++instance (Foldable f) => Foldable (IdentityT f) where+ foldMap f (IdentityT a) = foldMap f a++instance (Traversable f) => Traversable (IdentityT f) where+ traverse f (IdentityT a) = IdentityT <$> traverse f a++instance (Applicative m) => Applicative (IdentityT m) where+ pure x = IdentityT (pure x)+ (<*>) = lift2IdentityT (<*>)++instance (Alternative m) => Alternative (IdentityT m) where+ empty = IdentityT empty+ (<|>) = lift2IdentityT (<|>)++instance (Monad m) => Monad (IdentityT m) where+ return = IdentityT . return+ m >>= k = IdentityT $ runIdentityT . k =<< runIdentityT m+ fail msg = IdentityT $ fail msg++instance (MonadPlus m) => MonadPlus (IdentityT m) where+ mzero = IdentityT mzero+ mplus = lift2IdentityT mplus++instance (MonadFix m) => MonadFix (IdentityT m) where+ mfix f = IdentityT (mfix (runIdentityT . f))++instance (MonadIO m) => MonadIO (IdentityT m) where+ liftIO = IdentityT . liftIO++instance MonadTrans IdentityT where+ lift = IdentityT++-- | Lift a unary operation to the new monad.+mapIdentityT :: (m a -> n b) -> IdentityT m a -> IdentityT n b+mapIdentityT f = IdentityT . f . runIdentityT++-- | Lift a binary operation to the new monad.+lift2IdentityT ::+ (m a -> n b -> p c) -> IdentityT m a -> IdentityT n b -> IdentityT p c+lift2IdentityT f a b = IdentityT (f (runIdentityT a) (runIdentityT b))++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: (((a -> m b) -> m a) ->+ m a) -> ((a -> IdentityT m b) -> IdentityT m a) -> IdentityT m a+liftCallCC callCC f =+ IdentityT $ callCC $ \ c -> runIdentityT (f (IdentityT . c))++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m a -> (e -> m a) -> m a) ->+ IdentityT m a -> (e -> IdentityT m a) -> IdentityT m a+liftCatch f m h = IdentityT $ f (runIdentityT m) (runIdentityT . h)
+ Control/Monad/Trans/List.hs view
@@ -0,0 +1,95 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.List+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The ListT monad transformer, adding backtracking to a given monad,+-- which must be commutative.+-----------------------------------------------------------------------------++module Control.Monad.Trans.List (+ -- * The ListT monad transformer+ ListT(..),+ mapListT,+ -- * Lifting other operations+ liftCallCC,+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class++import Control.Applicative+import Control.Monad+import Data.Foldable (Foldable(foldMap))+import Data.Traversable (Traversable(traverse))++-- | Parameterizable list monad, with an inner monad.+--+-- /Note:/ this does not yield a monad unless the argument monad is commutative.+newtype ListT m a = ListT { runListT :: m [a] }++-- | Map between 'ListT' computations.+--+-- * @'runListT' ('mapListT' f m) = f ('runListT' m)@+mapListT :: (m [a] -> n [b]) -> ListT m a -> ListT n b+mapListT f m = ListT $ f (runListT m)++instance (Functor m) => Functor (ListT m) where+ fmap f = mapListT $ fmap $ map f++instance Foldable f => Foldable (ListT f) where+ foldMap f (ListT a) = foldMap (foldMap f) a++instance Traversable f => Traversable (ListT f) where+ traverse f (ListT a) = ListT <$> traverse (traverse f) a++instance (Applicative m) => Applicative (ListT m) where+ pure a = ListT $ pure [a]+ f <*> v = ListT $ (<*>) <$> runListT f <*> runListT v++instance (Applicative m) => Alternative (ListT m) where+ empty = ListT $ pure []+ m <|> n = ListT $ (++) <$> runListT m <*> runListT n++instance (Monad m) => Monad (ListT m) where+ return a = ListT $ return [a]+ m >>= k = ListT $ do+ a <- runListT m+ b <- mapM (runListT . k) a+ return (concat b)+ fail _ = ListT $ return []++instance (Monad m) => MonadPlus (ListT m) where+ mzero = ListT $ return []+ m `mplus` n = ListT $ do+ a <- runListT m+ b <- runListT n+ return (a ++ b)++instance MonadTrans ListT where+ lift m = ListT $ do+ a <- m+ return [a]++instance (MonadIO m) => MonadIO (ListT m) where+ liftIO = lift . liftIO++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: ((([a] -> m [b]) -> m [a]) -> m [a]) ->+ ((a -> ListT m b) -> ListT m a) -> ListT m a+liftCallCC callCC f = ListT $+ callCC $ \c ->+ runListT (f (\a -> ListT $ c [a]))++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m [a] -> (e -> m [a]) -> m [a]) ->+ ListT m a -> (e -> ListT m a) -> ListT m a+liftCatch catchError m h = ListT $ runListT m+ `catchError` \e -> runListT (h e)
+ Control/Monad/Trans/Maybe.hs view
@@ -0,0 +1,126 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Maybe+-- Copyright : (c) 2007 Yitzak Gale, Eric Kidd+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The 'MaybeT' monad transformer adds the ability to fail to a monad.+--+-- A sequence of actions succeeds, producing a value, only if all the+-- actions in the sequence are successful. If one fails, the rest of+-- the sequence is skipped and the composite action fails.+--+-- For a variant allowing a range of error values, see+-- "Control.Monad.Trans.Error".+-----------------------------------------------------------------------------++module Control.Monad.Trans.Maybe (+ -- * The MaybeT monad transformer+ MaybeT(..),+ mapMaybeT,+ -- * Lifting other operations+ liftCallCC,+ liftCatch,+ liftListen,+ liftPass,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class++import Control.Applicative+import Control.Monad (MonadPlus(mzero, mplus), liftM, ap)+import Control.Monad.Fix (MonadFix(mfix))+import Data.Foldable (Foldable(foldMap))+import Data.Maybe (fromMaybe)+import Data.Traversable (Traversable(traverse))++-- | The parameterizable maybe monad, obtained by composing an arbitrary+-- monad with the 'Maybe' monad.+--+-- Computations are actions that may produce a value or fail.+--+-- The 'return' function yields a successful computation, while @>>=@+-- sequences two subcomputations, failing on the first error.+newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }++-- | Transform the computation inside a @MaybeT@.+--+-- * @'runMaybeT' ('mapMaybeT' f m) = f ('runMaybeT' m)@+mapMaybeT :: (m (Maybe a) -> n (Maybe b)) -> MaybeT m a -> MaybeT n b+mapMaybeT f = MaybeT . f . runMaybeT++instance (Functor m) => Functor (MaybeT m) where+ fmap f = mapMaybeT (fmap (fmap f))++instance (Foldable f) => Foldable (MaybeT f) where+ foldMap f (MaybeT a) = foldMap (foldMap f) a++instance (Traversable f) => Traversable (MaybeT f) where+ traverse f (MaybeT a) = MaybeT <$> traverse (traverse f) a++instance (Functor m, Monad m) => Applicative (MaybeT m) where+ pure = return+ (<*>) = ap+ +instance (Functor m, Monad m) => Alternative (MaybeT m) where+ empty = mzero+ (<|>) = mplus++instance (Monad m) => Monad (MaybeT m) where+ fail _ = MaybeT (return Nothing)+ return = lift . return+ x >>= f = MaybeT $ do+ v <- runMaybeT x+ case v of+ Nothing -> return Nothing+ Just y -> runMaybeT (f y)++instance (Monad m) => MonadPlus (MaybeT m) where+ mzero = MaybeT (return Nothing)+ mplus x y = MaybeT $ do+ v <- runMaybeT x+ case v of+ Nothing -> runMaybeT y+ Just _ -> return v++instance (MonadFix m) => MonadFix (MaybeT m) where+ mfix f = MaybeT (mfix (runMaybeT . f . unJust))+ where unJust = fromMaybe (error "mfix MaybeT: Nothing")++instance MonadTrans MaybeT where+ lift = MaybeT . liftM Just++instance (MonadIO m) => MonadIO (MaybeT m) where+ liftIO = lift . liftIO++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: (((Maybe a -> m (Maybe b)) -> m (Maybe a)) ->+ m (Maybe a)) -> ((a -> MaybeT m b) -> MaybeT m a) -> MaybeT m a+liftCallCC callCC f =+ MaybeT $ callCC $ \ c -> runMaybeT (f (MaybeT . c . Just))++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (Maybe a) -> (e -> m (Maybe a)) -> m (Maybe a)) ->+ MaybeT m a -> (e -> MaybeT m a) -> MaybeT m a+liftCatch f m h = MaybeT $ f (runMaybeT m) (runMaybeT . h)++-- | Lift a @listen@ operation to the new monad.+liftListen :: Monad m =>+ (m (Maybe a) -> m (Maybe a,w)) -> MaybeT m a -> MaybeT m (a,w)+liftListen listen = mapMaybeT $ \ m -> do+ (a, w) <- listen m+ return $! fmap (\ r -> (r, w)) a++-- | Lift a @pass@ operation to the new monad.+liftPass :: Monad m => (m (Maybe a,w -> w) -> m (Maybe a)) ->+ MaybeT m (a,w -> w) -> MaybeT m a+liftPass pass = mapMaybeT $ \ m -> pass $ do+ a <- m+ return $! case a of+ Nothing -> (Nothing, id)+ Just (v, f) -> (Just v, f)
+ Control/Monad/Trans/RWS.hs view
@@ -0,0 +1,21 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.RWS+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- A monad transformer that combines 'ReaderT', 'WriterT' and 'StateT'.+-- This version is lazy; for a strict version, see+-- "Control.Monad.Trans.RWS.Strict", which has the same interface.+-----------------------------------------------------------------------------++module Control.Monad.Trans.RWS (+ module Control.Monad.Trans.RWS.Lazy+ ) where++import Control.Monad.Trans.RWS.Lazy
+ Control/Monad/Trans/RWS/Lazy.hs view
@@ -0,0 +1,317 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.RWS.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- A monad transformer that combines 'ReaderT', 'WriterT' and 'StateT'.+-- This version is lazy; for a strict version, see+-- "Control.Monad.Trans.RWS.Strict", which has the same interface.+-----------------------------------------------------------------------------++module Control.Monad.Trans.RWS.Lazy (+ -- * The RWS monad+ RWS,+ rws,+ runRWS,+ evalRWS,+ execRWS,+ mapRWS,+ withRWS,+ -- * The RWST monad transformer+ RWST(..),+ evalRWST,+ execRWST,+ mapRWST,+ withRWST,+ -- * Reader operations+ reader,+ ask,+ local,+ asks,+ -- * Writer operations+ writer,+ tell,+ listen,+ listens,+ pass,+ censor,+ -- * State operations+ state,+ get,+ put,+ modify,+ gets,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Data.Monoid++-- | A monad containing an environment of type @r@, output of type @w@+-- and an updatable state of type @s@.+type RWS r w s = RWST r w s Identity++-- | Construct an RWS computation from a function.+-- (The inverse of 'runRWS'.)+rws :: (r -> s -> (a, s, w)) -> RWS r w s a+rws f = RWST (\ r s -> Identity (f r s))++-- | Unwrap an RWS computation as a function.+-- (The inverse of 'rws'.)+runRWS :: RWS r w s a -> r -> s -> (a, s, w)+runRWS m r s = runIdentity (runRWST m r s)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final value and output, discarding the final state.+evalRWS :: RWS r w s a -- ^RWS computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> (a, w) -- ^final value and output+evalRWS m r s = let+ (a, _, w) = runRWS m r s+ in (a, w)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final state and output, discarding the final value.+execRWS :: RWS r w s a -- ^RWS computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> (s, w) -- ^final state and output+execRWS m r s = let+ (_, s', w) = runRWS m r s+ in (s', w)++-- | Map the return value, final state and output of a computation using+-- the given function.+--+-- * @'runRWS' ('mapRWS' f m) r s = f ('runRWS' m r s)@+mapRWS :: ((a, s, w) -> (b, s, w')) -> RWS r w s a -> RWS r w' s b+mapRWS f = mapRWST (Identity . f . runIdentity)++-- | @'withRWS' f m@ executes action @m@ with an initial environment+-- and state modified by applying @f@.+--+-- * @'runRWS' ('withRWS' f m) r s = 'uncurry' ('runRWS' m) (f r s)@+withRWS :: (r' -> s -> (r, s)) -> RWS r w s a -> RWS r' w s a+withRWS = withRWST++-- ---------------------------------------------------------------------------+-- | A monad transformer adding reading an environment of type @r@,+-- collecting an output of type @w@ and updating a state of type @s@+-- to an inner monad @m@.+newtype RWST r w s m a = RWST { runRWST :: r -> s -> m (a, s, w) }++-- | Evaluate a computation with the given initial state and environment,+-- returning the final value and output, discarding the final state.+evalRWST :: (Monad m)+ => RWST r w s m a -- ^computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> m (a, w) -- ^computation yielding final value and output+evalRWST m r s = do+ ~(a, _, w) <- runRWST m r s+ return (a, w)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final state and output, discarding the final value.+execRWST :: (Monad m)+ => RWST r w s m a -- ^computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> m (s, w) -- ^computation yielding final state and output+execRWST m r s = do+ ~(_, s', w) <- runRWST m r s+ return (s', w)++-- | Map the inner computation using the given function.+--+-- * @'runRWST' ('mapRWST' f m) r s = f ('runRWST' m r s)@+mapRWST :: (m (a, s, w) -> n (b, s, w')) -> RWST r w s m a -> RWST r w' s n b+mapRWST f m = RWST $ \r s -> f (runRWST m r s)++-- | @'withRWST' f m@ executes action @m@ with an initial environment+-- and state modified by applying @f@.+--+-- * @'runRWST' ('withRWST' f m) r s = 'uncurry' ('runRWST' m) (f r s)@+withRWST :: (r' -> s -> (r, s)) -> RWST r w s m a -> RWST r' w s m a+withRWST f m = RWST $ \r s -> uncurry (runRWST m) (f r s)++instance (Functor m) => Functor (RWST r w s m) where+ fmap f m = RWST $ \r s ->+ fmap (\ ~(a, s', w) -> (f a, s', w)) $ runRWST m r s++instance (Monoid w, Functor m, Monad m) => Applicative (RWST r w s m) where+ pure = return+ (<*>) = ap++instance (Monoid w, Functor m, MonadPlus m) => Alternative (RWST r w s m) where+ empty = mzero+ (<|>) = mplus++instance (Monoid w, Monad m) => Monad (RWST r w s m) where+ return a = RWST $ \_ s -> return (a, s, mempty)+ m >>= k = RWST $ \r s -> do+ ~(a, s', w) <- runRWST m r s+ ~(b, s'',w') <- runRWST (k a) r s'+ return (b, s'', w `mappend` w')+ fail msg = RWST $ \_ _ -> fail msg++instance (Monoid w, MonadPlus m) => MonadPlus (RWST r w s m) where+ mzero = RWST $ \_ _ -> mzero+ m `mplus` n = RWST $ \r s -> runRWST m r s `mplus` runRWST n r s++instance (Monoid w, MonadFix m) => MonadFix (RWST r w s m) where+ mfix f = RWST $ \r s -> mfix $ \ ~(a, _, _) -> runRWST (f a) r s++instance (Monoid w) => MonadTrans (RWST r w s) where+ lift m = RWST $ \_ s -> do+ a <- m+ return (a, s, mempty)++instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where+ liftIO = lift . liftIO++-- ---------------------------------------------------------------------------+-- Reader operations++-- | Constructor for computations in the reader monad (equivalent to 'asks').+reader :: (Monoid w, Monad m) => (r -> a) -> RWST r w s m a+reader = asks++-- | Fetch the value of the environment.+ask :: (Monoid w, Monad m) => RWST r w s m r+ask = RWST $ \r s -> return (r, s, mempty)++-- | Execute a computation in a modified environment+--+-- * @'runRWST' ('local' f m) r s = 'runRWST' m (f r) s@+local :: (Monoid w, Monad m) => (r -> r) -> RWST r w s m a -> RWST r w s m a+local f m = RWST $ \r s -> runRWST m (f r) s++-- | Retrieve a function of the current environment.+--+-- * @'asks' f = 'liftM' f 'ask'@+asks :: (Monoid w, Monad m) => (r -> a) -> RWST r w s m a+asks f = RWST $ \r s -> return (f r, s, mempty)++-- ---------------------------------------------------------------------------+-- Writer operations++-- | Construct a writer computation from a (result, output) pair.+writer :: Monad m => (a, w) -> RWST r w s m a+writer (a, w) = RWST $ \_ s -> return (a, s, w)++-- | @'tell' w@ is an action that produces the output @w@.+tell :: (Monoid w, Monad m) => w -> RWST r w s m ()+tell w = RWST $ \_ s -> return ((),s,w)++-- | @'listen' m@ is an action that executes the action @m@ and adds its+-- output to the value of the computation.+--+-- * @'runRWST' ('listen' m) r s = 'liftM' (\\(a, w) -> ((a, w), w)) ('runRWST' m r s)@+listen :: (Monoid w, Monad m) => RWST r w s m a -> RWST r w s m (a, w)+listen m = RWST $ \r s -> do+ ~(a, s', w) <- runRWST m r s+ return ((a, w), s', w)++-- | @'listens' f m@ is an action that executes the action @m@ and adds+-- the result of applying @f@ to the output to the value of the computation.+--+-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@+--+-- * @'runRWST' ('listens' f m) r s = 'liftM' (\\(a, w) -> ((a, f w), w)) ('runRWST' m r s)@+listens :: (Monoid w, Monad m) => (w -> b) -> RWST r w s m a -> RWST r w s m (a, b)+listens f m = RWST $ \r s -> do+ ~(a, s', w) <- runRWST m r s+ return ((a, f w), s', w)++-- | @'pass' m@ is an action that executes the action @m@, which returns+-- a value and a function, and returns the value, applying the function+-- to the output.+--+-- * @'runRWST' ('pass' m) r s = 'liftM' (\\((a, f), w) -> (a, f w)) ('runRWST' m r s)@+pass :: (Monoid w, Monad m) => RWST r w s m (a, w -> w) -> RWST r w s m a+pass m = RWST $ \r s -> do+ ~((a, f), s', w) <- runRWST m r s+ return (a, s', f w)++-- | @'censor' f m@ is an action that executes the action @m@ and+-- applies the function @f@ to its output, leaving the return value+-- unchanged.+--+-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@+--+-- * @'runRWST' ('censor' f m) r s = 'liftM' (\\(a, w) -> (a, f w)) ('runRWST' m r s)@+censor :: (Monoid w, Monad m) => (w -> w) -> RWST r w s m a -> RWST r w s m a+censor f m = RWST $ \r s -> do+ ~(a, s', w) <- runRWST m r s+ return (a, s', f w)++-- ---------------------------------------------------------------------------+-- State operations++-- | Construct a state monad computation from a state transformer function.+state :: (Monoid w, Monad m) => (s -> (a,s)) -> RWST r w s m a+state f = RWST $ \_ s -> let (a,s') = f s in return (a, s', mempty)++-- | Fetch the current value of the state within the monad.+get :: (Monoid w, Monad m) => RWST r w s m s+get = RWST $ \_ s -> return (s, s, mempty)++-- | @'put' s@ sets the state within the monad to @s@.+put :: (Monoid w, Monad m) => s -> RWST r w s m ()+put s = RWST $ \_ _ -> return ((), s, mempty)++-- | @'modify' f@ is an action that updates the state to the result of+-- applying @f@ to the current state.+--+-- * @'modify' f = 'get' >>= ('put' . f)@+modify :: (Monoid w, Monad m) => (s -> s) -> RWST r w s m ()+modify f = RWST $ \_ s -> return ((), f s, mempty)+ +-- | Get a specific component of the state, using a projection function+-- supplied.+--+-- * @'gets' f = 'liftM' f 'get'@+gets :: (Monoid w, Monad m) => (s -> a) -> RWST r w s m a+gets f = RWST $ \_ s -> return (f s, s, mempty)++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: (Monoid w) =>+ ((((a,s,w) -> m (b,s,w)) -> m (a,s,w)) -> m (a,s,w)) ->+ ((a -> RWST r w s m b) -> RWST r w s m a) -> RWST r w s m a+liftCallCC callCC f = RWST $ \r s ->+ callCC $ \c ->+ runRWST (f (\a -> RWST $ \_ _ -> c (a, s, mempty))) r s++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+liftCallCC' :: (Monoid w) =>+ ((((a,s,w) -> m (b,s,w)) -> m (a,s,w)) -> m (a,s,w)) ->+ ((a -> RWST r w s m b) -> RWST r w s m a) -> RWST r w s m a+liftCallCC' callCC f = RWST $ \r s ->+ callCC $ \c ->+ runRWST (f (\a -> RWST $ \_ s' -> c (a, s', mempty))) r s++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,s,w) -> (e -> m (a,s,w)) -> m (a,s,w)) ->+ RWST l w s m a -> (e -> RWST l w s m a) -> RWST l w s m a+liftCatch catchError m h =+ RWST $ \r s -> runRWST m r s `catchError` \e -> runRWST (h e) r s
+ Control/Monad/Trans/RWS/Strict.hs view
@@ -0,0 +1,317 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.RWS.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- A monad transformer that combines 'ReaderT', 'WriterT' and 'StateT'.+-- This version is strict; for a lazy version, see+-- "Control.Monad.Trans.RWS.Lazy", which has the same interface.+-----------------------------------------------------------------------------++module Control.Monad.Trans.RWS.Strict (+ -- * The RWS monad+ RWS,+ rws,+ runRWS,+ evalRWS,+ execRWS,+ mapRWS,+ withRWS,+ -- * The RWST monad transformer+ RWST(..),+ evalRWST,+ execRWST,+ mapRWST,+ withRWST,+ -- * Reader operations+ reader,+ ask,+ local,+ asks,+ -- * Writer operations+ writer,+ tell,+ listen,+ listens,+ pass,+ censor,+ -- * State operations+ state,+ get,+ put,+ modify,+ gets,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Data.Monoid++-- | A monad containing an environment of type @r@, output of type @w@+-- and an updatable state of type @s@.+type RWS r w s = RWST r w s Identity++-- | Construct an RWS computation from a function.+-- (The inverse of 'runRWS'.)+rws :: (r -> s -> (a, s, w)) -> RWS r w s a+rws f = RWST (\ r s -> Identity (f r s))++-- | Unwrap an RWS computation as a function.+-- (The inverse of 'rws'.)+runRWS :: RWS r w s a -> r -> s -> (a, s, w)+runRWS m r s = runIdentity (runRWST m r s)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final value and output, discarding the final state.+evalRWS :: RWS r w s a -- ^RWS computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> (a, w) -- ^final value and output+evalRWS m r s = let+ (a, _, w) = runRWS m r s+ in (a, w)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final state and output, discarding the final value.+execRWS :: RWS r w s a -- ^RWS computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> (s, w) -- ^final state and output+execRWS m r s = let+ (_, s', w) = runRWS m r s+ in (s', w)++-- | Map the return value, final state and output of a computation using+-- the given function.+--+-- * @'runRWS' ('mapRWS' f m) r s = f ('runRWS' m r s)@+mapRWS :: ((a, s, w) -> (b, s, w')) -> RWS r w s a -> RWS r w' s b+mapRWS f = mapRWST (Identity . f . runIdentity)++-- | @'withRWS' f m@ executes action @m@ with an initial environment+-- and state modified by applying @f@.+--+-- * @'runRWS' ('withRWS' f m) r s = 'uncurry' ('runRWS' m) (f r s)@+withRWS :: (r' -> s -> (r, s)) -> RWS r w s a -> RWS r' w s a+withRWS = withRWST++-- ---------------------------------------------------------------------------+-- | A monad transformer adding reading an environment of type @r@,+-- collecting an output of type @w@ and updating a state of type @s@+-- to an inner monad @m@.+newtype RWST r w s m a = RWST { runRWST :: r -> s -> m (a, s, w) }++-- | Evaluate a computation with the given initial state and environment,+-- returning the final value and output, discarding the final state.+evalRWST :: (Monad m)+ => RWST r w s m a -- ^computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> m (a, w) -- ^computation yielding final value and output+evalRWST m r s = do+ (a, _, w) <- runRWST m r s+ return (a, w)++-- | Evaluate a computation with the given initial state and environment,+-- returning the final state and output, discarding the final value.+execRWST :: (Monad m)+ => RWST r w s m a -- ^computation to execute+ -> r -- ^initial environment+ -> s -- ^initial value+ -> m (s, w) -- ^computation yielding final state and output+execRWST m r s = do+ (_, s', w) <- runRWST m r s+ return (s', w)++-- | Map the inner computation using the given function.+--+-- * @'runRWST' ('mapRWST' f m) r s = f ('runRWST' m r s)@+mapRWST :: (m (a, s, w) -> n (b, s, w')) -> RWST r w s m a -> RWST r w' s n b+mapRWST f m = RWST $ \r s -> f (runRWST m r s)++-- | @'withRWST' f m@ executes action @m@ with an initial environment+-- and state modified by applying @f@.+--+-- * @'runRWST' ('withRWST' f m) r s = 'uncurry' ('runRWST' m) (f r s)@+withRWST :: (r' -> s -> (r, s)) -> RWST r w s m a -> RWST r' w s m a+withRWST f m = RWST $ \r s -> uncurry (runRWST m) (f r s)++instance (Functor m) => Functor (RWST r w s m) where+ fmap f m = RWST $ \r s ->+ fmap (\ (a, s', w) -> (f a, s', w)) $ runRWST m r s++instance (Monoid w, Functor m, Monad m) => Applicative (RWST r w s m) where+ pure = return+ (<*>) = ap++instance (Monoid w, Functor m, MonadPlus m) => Alternative (RWST r w s m) where+ empty = mzero+ (<|>) = mplus++instance (Monoid w, Monad m) => Monad (RWST r w s m) where+ return a = RWST $ \_ s -> return (a, s, mempty)+ m >>= k = RWST $ \r s -> do+ (a, s', w) <- runRWST m r s+ (b, s'',w') <- runRWST (k a) r s'+ return (b, s'', w `mappend` w')+ fail msg = RWST $ \_ _ -> fail msg++instance (Monoid w, MonadPlus m) => MonadPlus (RWST r w s m) where+ mzero = RWST $ \_ _ -> mzero+ m `mplus` n = RWST $ \r s -> runRWST m r s `mplus` runRWST n r s++instance (Monoid w, MonadFix m) => MonadFix (RWST r w s m) where+ mfix f = RWST $ \r s -> mfix $ \ ~(a, _, _) -> runRWST (f a) r s++instance (Monoid w) => MonadTrans (RWST r w s) where+ lift m = RWST $ \_ s -> do+ a <- m+ return (a, s, mempty)++instance (Monoid w, MonadIO m) => MonadIO (RWST r w s m) where+ liftIO = lift . liftIO++-- ---------------------------------------------------------------------------+-- Reader operations++-- | Constructor for computations in the reader monad (equivalent to 'asks').+reader :: (Monoid w, Monad m) => (r -> a) -> RWST r w s m a+reader = asks++-- | Fetch the value of the environment.+ask :: (Monoid w, Monad m) => RWST r w s m r+ask = RWST $ \r s -> return (r, s, mempty)++-- | Execute a computation in a modified environment+--+-- * @'runRWST' ('local' f m) r s = 'runRWST' m (f r) s@+local :: (Monoid w, Monad m) => (r -> r) -> RWST r w s m a -> RWST r w s m a+local f m = RWST $ \r s -> runRWST m (f r) s++-- | Retrieve a function of the current environment.+--+-- * @'asks' f = 'liftM' f 'ask'@+asks :: (Monoid w, Monad m) => (r -> a) -> RWST r w s m a+asks f = RWST $ \r s -> return (f r, s, mempty)++-- ---------------------------------------------------------------------------+-- Writer operations++-- | Construct a writer computation from a (result, output) pair.+writer :: Monad m => (a, w) -> RWST r w s m a+writer (a, w) = RWST $ \_ s -> return (a, s, w)++-- | @'tell' w@ is an action that produces the output @w@.+tell :: (Monoid w, Monad m) => w -> RWST r w s m ()+tell w = RWST $ \_ s -> return ((),s,w)++-- | @'listen' m@ is an action that executes the action @m@ and adds its+-- output to the value of the computation.+--+-- * @'runRWST' ('listen' m) r s = 'liftM' (\\(a, w) -> ((a, w), w)) ('runRWST' m r s)@+listen :: (Monoid w, Monad m) => RWST r w s m a -> RWST r w s m (a, w)+listen m = RWST $ \r s -> do+ (a, s', w) <- runRWST m r s+ return ((a, w), s', w)++-- | @'listens' f m@ is an action that executes the action @m@ and adds+-- the result of applying @f@ to the output to the value of the computation.+--+-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@+--+-- * @'runRWST' ('listens' f m) r s = 'liftM' (\\(a, w) -> ((a, f w), w)) ('runRWST' m r s)@+listens :: (Monoid w, Monad m) => (w -> b) -> RWST r w s m a -> RWST r w s m (a, b)+listens f m = RWST $ \r s -> do+ (a, s', w) <- runRWST m r s+ return ((a, f w), s', w)++-- | @'pass' m@ is an action that executes the action @m@, which returns+-- a value and a function, and returns the value, applying the function+-- to the output.+--+-- * @'runRWST' ('pass' m) r s = 'liftM' (\\((a, f), w) -> (a, f w)) ('runRWST' m r s)@+pass :: (Monoid w, Monad m) => RWST r w s m (a, w -> w) -> RWST r w s m a+pass m = RWST $ \r s -> do+ ((a, f), s', w) <- runRWST m r s+ return (a, s', f w)++-- | @'censor' f m@ is an action that executes the action @m@ and+-- applies the function @f@ to its output, leaving the return value+-- unchanged.+--+-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@+--+-- * @'runRWST' ('censor' f m) r s = 'liftM' (\\(a, w) -> (a, f w)) ('runRWST' m r s)@+censor :: (Monoid w, Monad m) => (w -> w) -> RWST r w s m a -> RWST r w s m a+censor f m = RWST $ \r s -> do+ (a, s', w) <- runRWST m r s+ return (a, s', f w)++-- ---------------------------------------------------------------------------+-- State operations++-- | Construct a state monad computation from a state transformer function.+state :: (Monoid w, Monad m) => (s -> (a,s)) -> RWST r w s m a+state f = RWST $ \_ s -> case f s of (a,s') -> return (a, s', mempty)++-- | Fetch the current value of the state within the monad.+get :: (Monoid w, Monad m) => RWST r w s m s+get = RWST $ \_ s -> return (s, s, mempty)++-- | @'put' s@ sets the state within the monad to @s@.+put :: (Monoid w, Monad m) => s -> RWST r w s m ()+put s = RWST $ \_ _ -> return ((), s, mempty)++-- | @'modify' f@ is an action that updates the state to the result of+-- applying @f@ to the current state.+--+-- * @'modify' f = 'get' >>= ('put' . f)@+modify :: (Monoid w, Monad m) => (s -> s) -> RWST r w s m ()+modify f = RWST $ \_ s -> return ((), f s, mempty)+ +-- | Get a specific component of the state, using a projection function+-- supplied.+--+-- * @'gets' f = 'liftM' f 'get'@+gets :: (Monoid w, Monad m) => (s -> a) -> RWST r w s m a+gets f = RWST $ \_ s -> return (f s, s, mempty)++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: (Monoid w) =>+ ((((a,s,w) -> m (b,s,w)) -> m (a,s,w)) -> m (a,s,w)) ->+ ((a -> RWST r w s m b) -> RWST r w s m a) -> RWST r w s m a+liftCallCC callCC f = RWST $ \r s ->+ callCC $ \c ->+ runRWST (f (\a -> RWST $ \_ _ -> c (a, s, mempty))) r s++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+liftCallCC' :: (Monoid w) =>+ ((((a,s,w) -> m (b,s,w)) -> m (a,s,w)) -> m (a,s,w)) ->+ ((a -> RWST r w s m b) -> RWST r w s m a) -> RWST r w s m a+liftCallCC' callCC f = RWST $ \r s ->+ callCC $ \c ->+ runRWST (f (\a -> RWST $ \_ s' -> c (a, s', mempty))) r s++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,s,w) -> (e -> m (a,s,w)) -> m (a,s,w)) ->+ RWST l w s m a -> (e -> RWST l w s m a) -> RWST l w s m a+liftCatch catchError m h =+ RWST $ \r s -> runRWST m r s `catchError` \e -> runRWST (h e) r s
+ Control/Monad/Trans/Reader.hs view
@@ -0,0 +1,180 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Reader+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Declaration of the 'ReaderT' monad transformer, which adds a static+-- environment to a given monad.+--+-- If the computation is to modify the stored information, use+-- "Control.Monad.Trans.State" instead.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Reader (+ -- * The Reader monad+ Reader,+ reader,+ runReader,+ mapReader,+ withReader,+ -- * The ReaderT monad transformer+ ReaderT(..),+ mapReaderT,+ withReaderT,+ -- * Reader operations+ ask,+ local,+ asks,+ -- * Lifting other operations+ liftCallCC,+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Control.Monad.Instances ()++-- | The parameterizable reader monad.+--+-- Computations are functions of a shared environment.+--+-- The 'return' function ignores the environment, while @>>=@ passes+-- the inherited environment to both subcomputations.+type Reader r = ReaderT r Identity++-- | Constructor for computations in the reader monad (equivalent to 'asks').+reader :: Monad m => (r -> a) -> ReaderT r m a+reader f = ReaderT (return . f)++-- | Runs a @Reader@ and extracts the final value from it.+-- (The inverse of 'reader'.)+runReader :: Reader r a -- ^ A @Reader@ to run.+ -> r -- ^ An initial environment.+ -> a+runReader m = runIdentity . runReaderT m++-- | Transform the value returned by a @Reader@.+--+-- * @'runReader' ('mapReader' f m) = f . 'runReader' m@+mapReader :: (a -> b) -> Reader r a -> Reader r b+mapReader f = mapReaderT (Identity . f . runIdentity)++-- | Execute a computation in a modified environment+-- (a specialization of 'withReaderT').+--+-- * @'runReader' ('withReader' f m) = 'runReader' m . f@+withReader+ :: (r' -> r) -- ^ The function to modify the environment.+ -> Reader r a -- ^ Computation to run in the modified environment.+ -> Reader r' a+withReader = withReaderT++-- | The reader monad transformer,+-- which adds a read-only environment to the given monad.+--+-- The 'return' function ignores the environment, while @>>=@ passes+-- the inherited environment to both subcomputations.+newtype ReaderT r m a = ReaderT {+ -- | The underlying computation, as a function of the environment.+ runReaderT :: r -> m a+ }++-- | Transform the computation inside a @ReaderT@.+--+-- * @'runReaderT' ('mapReaderT' f m) = f . 'runReaderT' m@+mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b+mapReaderT f m = ReaderT $ f . runReaderT m++-- | Execute a computation in a modified environment+-- (a more general version of 'local').+--+-- * @'runReaderT' ('withReaderT' f m) = 'runReaderT' m . f@+withReaderT+ :: (r' -> r) -- ^ The function to modify the environment.+ -> ReaderT r m a -- ^ Computation to run in the modified environment.+ -> ReaderT r' m a+withReaderT f m = ReaderT $ runReaderT m . f++instance (Functor m) => Functor (ReaderT r m) where+ fmap f = mapReaderT (fmap f)++instance (Applicative m) => Applicative (ReaderT r m) where+ pure = liftReaderT . pure+ f <*> v = ReaderT $ \ r -> runReaderT f r <*> runReaderT v r++instance (Alternative m) => Alternative (ReaderT r m) where+ empty = liftReaderT empty+ m <|> n = ReaderT $ \ r -> runReaderT m r <|> runReaderT n r++instance (Monad m) => Monad (ReaderT r m) where+ return = lift . return+ m >>= k = ReaderT $ \ r -> do+ a <- runReaderT m r+ runReaderT (k a) r+ fail msg = lift (fail msg)++instance (MonadPlus m) => MonadPlus (ReaderT r m) where+ mzero = lift mzero+ m `mplus` n = ReaderT $ \ r -> runReaderT m r `mplus` runReaderT n r++instance (MonadFix m) => MonadFix (ReaderT r m) where+ mfix f = ReaderT $ \ r -> mfix $ \ a -> runReaderT (f a) r++instance MonadTrans (ReaderT r) where+ lift = liftReaderT++instance (MonadIO m) => MonadIO (ReaderT r m) where+ liftIO = lift . liftIO++liftReaderT :: m a -> ReaderT r m a+liftReaderT m = ReaderT (const m)++-- | Fetch the value of the environment.+ask :: (Monad m) => ReaderT r m r+ask = ReaderT return++-- | Execute a computation in a modified environment+-- (a specialization of 'withReaderT').+--+-- * @'runReaderT' ('local' f m) = 'runReaderT' m . f@+local :: (Monad m)+ => (r -> r) -- ^ The function to modify the environment.+ -> ReaderT r m a -- ^ Computation to run in the modified environment.+ -> ReaderT r m a+local = withReaderT++-- | Retrieve a function of the current environment.+--+-- * @'asks' f = 'liftM' f 'ask'@+asks :: (Monad m)+ => (r -> a) -- ^ The selector function to apply to the environment.+ -> ReaderT r m a+asks f = ReaderT (return . f)++-- | Lift a @callCC@ operation to the new monad.+liftCallCC ::+ (((a -> m b) -> m a) -> m a) -- ^ @callCC@ on the argument monad.+ -> ((a -> ReaderT r m b) -> ReaderT r m a) -> ReaderT r m a+liftCallCC callCC f = ReaderT $ \ r ->+ callCC $ \ c ->+ runReaderT (f (ReaderT . const . c)) r++-- | Lift a @catchError@ operation to the new monad.+liftCatch ::+ (m a -> (e -> m a) -> m a) -- ^ @catch@ on the argument monad.+ -> ReaderT r m a -- ^ Computation to attempt.+ -> (e -> ReaderT r m a) -- ^ Exception handler.+ -> ReaderT r m a+liftCatch f m h =+ ReaderT $ \ r -> f (runReaderT m r) (\ e -> runReaderT (h e) r)
+ Control/Monad/Trans/State.hs view
@@ -0,0 +1,29 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.State+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- State monads, passing an updatable state through a computation.+--+-- Some computations may not require the full power of state transformers:+--+-- * For a read-only state, see "Control.Monad.Trans.Reader".+--+-- * To accumulate a value without using it on the way, see+-- "Control.Monad.Trans.Writer".+--+-- This version is lazy; for a strict version, see+-- "Control.Monad.Trans.State.Strict", which has the same interface.+-----------------------------------------------------------------------------++module Control.Monad.Trans.State (+ module Control.Monad.Trans.State.Lazy+ ) where++import Control.Monad.Trans.State.Lazy
+ Control/Monad/Trans/State/Lazy.hs view
@@ -0,0 +1,366 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.State.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Lazy state monads, passing an updatable state through a computation.+-- See below for examples.+--+-- In this version, sequencing of computations is lazy.+-- For a strict version, see "Control.Monad.Trans.State.Strict", which+-- has the same interface.+--+-- Some computations may not require the full power of state transformers:+--+-- * For a read-only state, see "Control.Monad.Trans.Reader".+--+-- * To accumulate a value without using it on the way, see+-- "Control.Monad.Trans.Writer".+-----------------------------------------------------------------------------++module Control.Monad.Trans.State.Lazy (+ -- * The State monad+ State,+ state,+ runState,+ evalState,+ execState,+ mapState,+ withState,+ -- * The StateT monad transformer+ StateT(..),+ evalStateT,+ execStateT,+ mapStateT,+ withStateT,+ -- * State operations+ get,+ put,+ modify,+ gets,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ liftListen,+ liftPass,+ -- * Examples+ -- ** State monads+ -- $examples++ -- ** Counting+ -- $counting++ -- ** Labelling trees+ -- $labelling+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix++-- ---------------------------------------------------------------------------+-- | A state monad parameterized by the type @s@ of the state to carry.+--+-- The 'return' function leaves the state unchanged, while @>>=@ uses+-- the final state of the first computation as the initial state of+-- the second.+type State s = StateT s Identity++-- | Construct a state monad computation from a function.+-- (The inverse of 'runState'.)+state :: Monad m+ => (s -> (a, s)) -- ^pure state transformer+ -> StateT s m a -- ^equivalent state-passing computation+state f = StateT (return . f)++-- | Unwrap a state monad computation as a function.+-- (The inverse of 'state'.)+runState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial state+ -> (a, s) -- ^return value and final state+runState m = runIdentity . runStateT m++-- | Evaluate a state computation with the given initial state+-- and return the final value, discarding the final state.+--+-- * @'evalState' m s = 'fst' ('runState' m s)@+evalState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial value+ -> a -- ^return value of the state computation+evalState m s = fst (runState m s)++-- | Evaluate a state computation with the given initial state+-- and return the final state, discarding the final value.+--+-- * @'execState' m s = 'snd' ('runState' m s)@+execState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial value+ -> s -- ^final state+execState m s = snd (runState m s)++-- | Map both the return value and final state of a computation using+-- the given function.+--+-- * @'runState' ('mapState' f m) = f . 'runState' m@+mapState :: ((a, s) -> (b, s)) -> State s a -> State s b+mapState f = mapStateT (Identity . f . runIdentity)++-- | @'withState' f m@ executes action @m@ on a state modified by+-- applying @f@.+--+-- * @'withState' f m = 'modify' f >> m@+withState :: (s -> s) -> State s a -> State s a+withState = withStateT++-- ---------------------------------------------------------------------------+-- | A state transformer monad parameterized by:+--+-- * @s@ - The state.+--+-- * @m@ - The inner monad.+--+-- The 'return' function leaves the state unchanged, while @>>=@ uses+-- the final state of the first computation as the initial state of+-- the second.+newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }++-- | Evaluate a state computation with the given initial state+-- and return the final value, discarding the final state.+--+-- * @'evalStateT' m s = 'liftM' 'fst' ('runStateT' m s)@+evalStateT :: (Monad m) => StateT s m a -> s -> m a+evalStateT m s = do+ ~(a, _) <- runStateT m s+ return a++-- | Evaluate a state computation with the given initial state+-- and return the final state, discarding the final value.+--+-- * @'execStateT' m s = 'liftM' 'snd' ('runStateT' m s)@+execStateT :: (Monad m) => StateT s m a -> s -> m s+execStateT m s = do+ ~(_, s') <- runStateT m s+ return s'++-- | Map both the return value and final state of a computation using+-- the given function.+--+-- * @'runStateT' ('mapStateT' f m) = f . 'runStateT' m@+mapStateT :: (m (a, s) -> n (b, s)) -> StateT s m a -> StateT s n b+mapStateT f m = StateT $ f . runStateT m++-- | @'withStateT' f m@ executes action @m@ on a state modified by+-- applying @f@.+--+-- * @'withStateT' f m = 'modify' f >> m@+withStateT :: (s -> s) -> StateT s m a -> StateT s m a+withStateT f m = StateT $ runStateT m . f++instance (Functor m) => Functor (StateT s m) where+ fmap f m = StateT $ \ s ->+ fmap (\ ~(a, s') -> (f a, s')) $ runStateT m s++instance (Functor m, Monad m) => Applicative (StateT s m) where+ pure = return+ (<*>) = ap++instance (Functor m, MonadPlus m) => Alternative (StateT s m) where+ empty = mzero+ (<|>) = mplus++instance (Monad m) => Monad (StateT s m) where+ return a = state $ \s -> (a, s)+ m >>= k = StateT $ \s -> do+ ~(a, s') <- runStateT m s+ runStateT (k a) s'+ fail str = StateT $ \_ -> fail str++instance (MonadPlus m) => MonadPlus (StateT s m) where+ mzero = StateT $ \_ -> mzero+ m `mplus` n = StateT $ \s -> runStateT m s `mplus` runStateT n s++instance (MonadFix m) => MonadFix (StateT s m) where+ mfix f = StateT $ \s -> mfix $ \ ~(a, _) -> runStateT (f a) s++instance MonadTrans (StateT s) where+ lift m = StateT $ \s -> do+ a <- m+ return (a, s)++instance (MonadIO m) => MonadIO (StateT s m) where+ liftIO = lift . liftIO++-- | Fetch the current value of the state within the monad.+get :: (Monad m) => StateT s m s+get = state $ \s -> (s, s)++-- | @'put' s@ sets the state within the monad to @s@.+put :: (Monad m) => s -> StateT s m ()+put s = state $ \_ -> ((), s)++-- | @'modify' f@ is an action that updates the state to the result of+-- applying @f@ to the current state.+--+-- * @'modify' f = 'get' >>= ('put' . f)@+modify :: (Monad m) => (s -> s) -> StateT s m ()+modify f = state $ \s -> ((), f s)++-- | Get a specific component of the state, using a projection function+-- supplied.+--+-- * @'gets' f = 'liftM' f 'get'@+gets :: (Monad m) => (s -> a) -> StateT s m a+gets f = state $ \s -> (f s, s)++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: ((((a,s) -> m (b,s)) -> m (a,s)) -> m (a,s)) ->+ ((a -> StateT s m b) -> StateT s m a) -> StateT s m a+liftCallCC callCC f = StateT $ \s ->+ callCC $ \c ->+ runStateT (f (\a -> StateT $ \ _ -> c (a, s))) s++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+-- It does not satisfy the laws of a monad transformer.+liftCallCC' :: ((((a,s) -> m (b,s)) -> m (a,s)) -> m (a,s)) ->+ ((a -> StateT s m b) -> StateT s m a) -> StateT s m a+liftCallCC' callCC f = StateT $ \s ->+ callCC $ \c ->+ runStateT (f (\a -> StateT $ \s' -> c (a, s'))) s++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,s) -> (e -> m (a,s)) -> m (a,s)) ->+ StateT s m a -> (e -> StateT s m a) -> StateT s m a+liftCatch catchError m h =+ StateT $ \s -> runStateT m s `catchError` \e -> runStateT (h e) s++-- | Lift a @listen@ operation to the new monad.+liftListen :: Monad m =>+ (m (a,s) -> m ((a,s),w)) -> StateT s m a -> StateT s m (a,w)+liftListen listen m = StateT $ \s -> do+ ~((a, s'), w) <- listen (runStateT m s)+ return ((a, w), s')++-- | Lift a @pass@ operation to the new monad.+liftPass :: Monad m =>+ (m ((a,s),b) -> m (a,s)) -> StateT s m (a,b) -> StateT s m a+liftPass pass m = StateT $ \s -> pass $ do+ ~((a, f), s') <- runStateT m s+ return ((a, s'), f)++{- $examples++Parser from ParseLib with Hugs:++> type Parser a = StateT String [] a+> ==> StateT (String -> [(a,String)])++For example, item can be written as:++> item = do (x:xs) <- get+> put xs+> return x+>+> type BoringState s a = StateT s Identity a+> ==> StateT (s -> Identity (a,s))+>+> type StateWithIO s a = StateT s IO a+> ==> StateT (s -> IO (a,s))+>+> type StateWithErr s a = StateT s Maybe a+> ==> StateT (s -> Maybe (a,s))++-}++{- $counting++A function to increment a counter.+Taken from the paper \"Generalising Monads to Arrows\",+John Hughes (<http://www.cse.chalmers.se/~rjmh/>), November 1998:++> tick :: State Int Int+> tick = do n <- get+> put (n+1)+> return n++Add one to the given number using the state monad:++> plusOne :: Int -> Int+> plusOne n = execState tick n++A contrived addition example. Works only with positive numbers:++> plus :: Int -> Int -> Int+> plus n x = execState (sequence $ replicate n tick) x++-}++{- $labelling++An example from /The Craft of Functional Programming/, Simon+Thompson (<http://www.cs.kent.ac.uk/people/staff/sjt/>),+Addison-Wesley 1999: \"Given an arbitrary tree, transform it to a+tree of integers in which the original elements are replaced by+natural numbers, starting from 0. The same element has to be+replaced by the same number at every occurrence, and when we meet+an as-yet-unvisited element we have to find a \'new\' number to match+it with:\"++> data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show, Eq)+> type Table a = [a]++> numberTree :: Eq a => Tree a -> State (Table a) (Tree Int)+> numberTree Nil = return Nil+> numberTree (Node x t1 t2)+> = do num <- numberNode x+> nt1 <- numberTree t1+> nt2 <- numberTree t2+> return (Node num nt1 nt2)+> where+> numberNode :: Eq a => a -> State (Table a) Int+> numberNode x+> = do table <- get+> (newTable, newPos) <- return (nNode x table)+> put newTable+> return newPos+> nNode:: (Eq a) => a -> Table a -> (Table a, Int)+> nNode x table+> = case (findIndexInList (== x) table) of+> Nothing -> (table ++ [x], length table)+> Just i -> (table, i)+> findIndexInList :: (a -> Bool) -> [a] -> Maybe Int+> findIndexInList = findIndexInListHelp 0+> findIndexInListHelp _ _ [] = Nothing+> findIndexInListHelp count f (h:t)+> = if (f h)+> then Just count+> else findIndexInListHelp (count+1) f t++numTree applies numberTree with an initial state:++> numTree :: (Eq a) => Tree a -> Tree Int+> numTree t = evalState (numberTree t) []++> testTree = Node "Zero" (Node "One" (Node "Two" Nil Nil) (Node "One" (Node "Zero" Nil Nil) Nil)) Nil+> numTree testTree => Node 0 (Node 1 (Node 2 Nil Nil) (Node 1 (Node 0 Nil Nil) Nil)) Nil++sumTree is a little helper function that does not use the State monad:++> sumTree :: (Num a) => Tree a -> a+> sumTree Nil = 0+> sumTree (Node e t1 t2) = e + (sumTree t1) + (sumTree t2)++-}
+ Control/Monad/Trans/State/Strict.hs view
@@ -0,0 +1,366 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.State.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Strict state monads, passing an updatable state through a computation.+-- See below for examples.+--+-- In this version, sequencing of computations is strict.+-- For a lazy version, see "Control.Monad.Trans.State.Lazy", which+-- has the same interface.+--+-- Some computations may not require the full power of state transformers:+--+-- * For a read-only state, see "Control.Monad.Trans.Reader".+--+-- * To accumulate a value without using it on the way, see+-- "Control.Monad.Trans.Writer".+-----------------------------------------------------------------------------++module Control.Monad.Trans.State.Strict (+ -- * The State monad+ State,+ state,+ runState,+ evalState,+ execState,+ mapState,+ withState,+ -- * The StateT monad transformer+ StateT(..),+ evalStateT,+ execStateT,+ mapStateT,+ withStateT,+ -- * State operations+ get,+ put,+ modify,+ gets,+ -- * Lifting other operations+ liftCallCC,+ liftCallCC',+ liftCatch,+ liftListen,+ liftPass,+ -- * Examples+ -- ** State monads+ -- $examples++ -- ** Counting+ -- $counting++ -- ** Labelling trees+ -- $labelling+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix++-- ---------------------------------------------------------------------------+-- | A state monad parameterized by the type @s@ of the state to carry.+--+-- The 'return' function leaves the state unchanged, while @>>=@ uses+-- the final state of the first computation as the initial state of+-- the second.+type State s = StateT s Identity++-- | Construct a state monad computation from a function.+-- (The inverse of 'runState'.)+state :: Monad m+ => (s -> (a, s)) -- ^pure state transformer+ -> StateT s m a -- ^equivalent state-passing computation+state f = StateT (return . f)++-- | Unwrap a state monad computation as a function.+-- (The inverse of 'state'.)+runState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial state+ -> (a, s) -- ^return value and final state+runState m = runIdentity . runStateT m++-- | Evaluate a state computation with the given initial state+-- and return the final value, discarding the final state.+--+-- * @'evalState' m s = 'fst' ('runState' m s)@+evalState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial value+ -> a -- ^return value of the state computation+evalState m s = fst (runState m s)++-- | Evaluate a state computation with the given initial state+-- and return the final state, discarding the final value.+--+-- * @'execState' m s = 'snd' ('runState' m s)@+execState :: State s a -- ^state-passing computation to execute+ -> s -- ^initial value+ -> s -- ^final state+execState m s = snd (runState m s)++-- | Map both the return value and final state of a computation using+-- the given function.+--+-- * @'runState' ('mapState' f m) = f . 'runState' m@+mapState :: ((a, s) -> (b, s)) -> State s a -> State s b+mapState f = mapStateT (Identity . f . runIdentity)++-- | @'withState' f m@ executes action @m@ on a state modified by+-- applying @f@.+--+-- * @'withState' f m = 'modify' f >> m@+withState :: (s -> s) -> State s a -> State s a+withState = withStateT++-- ---------------------------------------------------------------------------+-- | A state transformer monad parameterized by:+--+-- * @s@ - The state.+--+-- * @m@ - The inner monad.+--+-- The 'return' function leaves the state unchanged, while @>>=@ uses+-- the final state of the first computation as the initial state of+-- the second.+newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }++-- | Evaluate a state computation with the given initial state+-- and return the final value, discarding the final state.+--+-- * @'evalStateT' m s = 'liftM' 'fst' ('runStateT' m s)@+evalStateT :: (Monad m) => StateT s m a -> s -> m a+evalStateT m s = do+ (a, _) <- runStateT m s+ return a++-- | Evaluate a state computation with the given initial state+-- and return the final state, discarding the final value.+--+-- * @'execStateT' m s = 'liftM' 'snd' ('runStateT' m s)@+execStateT :: (Monad m) => StateT s m a -> s -> m s+execStateT m s = do+ (_, s') <- runStateT m s+ return s'++-- | Map both the return value and final state of a computation using+-- the given function.+--+-- * @'runStateT' ('mapStateT' f m) = f . 'runStateT' m@+mapStateT :: (m (a, s) -> n (b, s)) -> StateT s m a -> StateT s n b+mapStateT f m = StateT $ f . runStateT m++-- | @'withStateT' f m@ executes action @m@ on a state modified by+-- applying @f@.+--+-- * @'withStateT' f m = 'modify' f >> m@+withStateT :: (s -> s) -> StateT s m a -> StateT s m a+withStateT f m = StateT $ runStateT m . f++instance (Functor m) => Functor (StateT s m) where+ fmap f m = StateT $ \ s ->+ fmap (\ (a, s') -> (f a, s')) $ runStateT m s++instance (Functor m, Monad m) => Applicative (StateT s m) where+ pure = return+ (<*>) = ap++instance (Functor m, MonadPlus m) => Alternative (StateT s m) where+ empty = mzero+ (<|>) = mplus++instance (Monad m) => Monad (StateT s m) where+ return a = state $ \s -> (a, s)+ m >>= k = StateT $ \s -> do+ (a, s') <- runStateT m s+ runStateT (k a) s'+ fail str = StateT $ \_ -> fail str++instance (MonadPlus m) => MonadPlus (StateT s m) where+ mzero = StateT $ \_ -> mzero+ m `mplus` n = StateT $ \s -> runStateT m s `mplus` runStateT n s++instance (MonadFix m) => MonadFix (StateT s m) where+ mfix f = StateT $ \s -> mfix $ \ ~(a, _) -> runStateT (f a) s++instance MonadTrans (StateT s) where+ lift m = StateT $ \s -> do+ a <- m+ return (a, s)++instance (MonadIO m) => MonadIO (StateT s m) where+ liftIO = lift . liftIO++-- | Fetch the current value of the state within the monad.+get :: (Monad m) => StateT s m s+get = state $ \s -> (s, s)++-- | @'put' s@ sets the state within the monad to @s@.+put :: (Monad m) => s -> StateT s m ()+put s = state $ \_ -> ((), s)++-- | @'modify' f@ is an action that updates the state to the result of+-- applying @f@ to the current state.+--+-- * @'modify' f = 'get' >>= ('put' . f)@+modify :: (Monad m) => (s -> s) -> StateT s m ()+modify f = state $ \s -> ((), f s)++-- | Get a specific component of the state, using a projection function+-- supplied.+--+-- * @'gets' f = 'liftM' f 'get'@+gets :: (Monad m) => (s -> a) -> StateT s m a+gets f = state $ \s -> (f s, s)++-- | Uniform lifting of a @callCC@ operation to the new monad.+-- This version rolls back to the original state on entering the+-- continuation.+liftCallCC :: ((((a,s) -> m (b,s)) -> m (a,s)) -> m (a,s)) ->+ ((a -> StateT s m b) -> StateT s m a) -> StateT s m a+liftCallCC callCC f = StateT $ \s ->+ callCC $ \c ->+ runStateT (f (\a -> StateT $ \ _ -> c (a, s))) s++-- | In-situ lifting of a @callCC@ operation to the new monad.+-- This version uses the current state on entering the continuation.+-- It does not satisfy the laws of a monad transformer.+liftCallCC' :: ((((a,s) -> m (b,s)) -> m (a,s)) -> m (a,s)) ->+ ((a -> StateT s m b) -> StateT s m a) -> StateT s m a+liftCallCC' callCC f = StateT $ \s ->+ callCC $ \c ->+ runStateT (f (\a -> StateT $ \s' -> c (a, s'))) s++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,s) -> (e -> m (a,s)) -> m (a,s)) ->+ StateT s m a -> (e -> StateT s m a) -> StateT s m a+liftCatch catchError m h =+ StateT $ \s -> runStateT m s `catchError` \e -> runStateT (h e) s++-- | Lift a @listen@ operation to the new monad.+liftListen :: Monad m =>+ (m (a,s) -> m ((a,s),w)) -> StateT s m a -> StateT s m (a,w)+liftListen listen m = StateT $ \s -> do+ ((a, s'), w) <- listen (runStateT m s)+ return ((a, w), s')++-- | Lift a @pass@ operation to the new monad.+liftPass :: Monad m =>+ (m ((a,s),b) -> m (a,s)) -> StateT s m (a,b) -> StateT s m a+liftPass pass m = StateT $ \s -> pass $ do+ ((a, f), s') <- runStateT m s+ return ((a, s'), f)++{- $examples++Parser from ParseLib with Hugs:++> type Parser a = StateT String [] a+> ==> StateT (String -> [(a,String)])++For example, item can be written as:++> item = do (x:xs) <- get+> put xs+> return x+>+> type BoringState s a = StateT s Identity a+> ==> StateT (s -> Identity (a,s))+>+> type StateWithIO s a = StateT s IO a+> ==> StateT (s -> IO (a,s))+>+> type StateWithErr s a = StateT s Maybe a+> ==> StateT (s -> Maybe (a,s))++-}++{- $counting++A function to increment a counter.+Taken from the paper \"Generalising Monads to Arrows\",+John Hughes (<http://www.cse.chalmers.se/~rjmh/>), November 1998:++> tick :: State Int Int+> tick = do n <- get+> put (n+1)+> return n++Add one to the given number using the state monad:++> plusOne :: Int -> Int+> plusOne n = execState tick n++A contrived addition example. Works only with positive numbers:++> plus :: Int -> Int -> Int+> plus n x = execState (sequence $ replicate n tick) x++-}++{- $labelling++An example from /The Craft of Functional Programming/, Simon+Thompson (<http://www.cs.kent.ac.uk/people/staff/sjt/>),+Addison-Wesley 1999: \"Given an arbitrary tree, transform it to a+tree of integers in which the original elements are replaced by+natural numbers, starting from 0. The same element has to be+replaced by the same number at every occurrence, and when we meet+an as-yet-unvisited element we have to find a \'new\' number to match+it with:\"++> data Tree a = Nil | Node a (Tree a) (Tree a) deriving (Show, Eq)+> type Table a = [a]++> numberTree :: Eq a => Tree a -> State (Table a) (Tree Int)+> numberTree Nil = return Nil+> numberTree (Node x t1 t2)+> = do num <- numberNode x+> nt1 <- numberTree t1+> nt2 <- numberTree t2+> return (Node num nt1 nt2)+> where+> numberNode :: Eq a => a -> State (Table a) Int+> numberNode x+> = do table <- get+> (newTable, newPos) <- return (nNode x table)+> put newTable+> return newPos+> nNode:: (Eq a) => a -> Table a -> (Table a, Int)+> nNode x table+> = case (findIndexInList (== x) table) of+> Nothing -> (table ++ [x], length table)+> Just i -> (table, i)+> findIndexInList :: (a -> Bool) -> [a] -> Maybe Int+> findIndexInList = findIndexInListHelp 0+> findIndexInListHelp _ _ [] = Nothing+> findIndexInListHelp count f (h:t)+> = if (f h)+> then Just count+> else findIndexInListHelp (count+1) f t++numTree applies numberTree with an initial state:++> numTree :: (Eq a) => Tree a -> Tree Int+> numTree t = evalState (numberTree t) []++> testTree = Node "Zero" (Node "One" (Node "Two" Nil Nil) (Node "One" (Node "Zero" Nil Nil) Nil)) Nil+> numTree testTree => Node 0 (Node 1 (Node 2 Nil Nil) (Node 1 (Node 0 Nil Nil) Nil)) Nil++sumTree is a little helper function that does not use the State monad:++> sumTree :: (Num a) => Tree a -> a+> sumTree Nil = 0+> sumTree (Node e t1 t2) = e + (sumTree t1) + (sumTree t2)++-}
+ Control/Monad/Trans/Writer.hs view
@@ -0,0 +1,21 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Writer+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The WriterT monad transformer.+-- This version is lazy; for a strict version, see+-- "Control.Monad.Trans.Writer.Strict", which has the same interface.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Writer (+ module Control.Monad.Trans.Writer.Lazy+ ) where++import Control.Monad.Trans.Writer.Lazy
+ Control/Monad/Trans/Writer/Lazy.hs view
@@ -0,0 +1,211 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Writer.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The lazy 'WriterT' monad transformer, which adds collection of+-- outputs (such as a count or string output) to a given monad.+--+-- This version builds its output lazily; for a strict version, see+-- "Control.Monad.Trans.Writer.Strict", which has the same interface.+--+-- This monad transformer provides only limited access to the output+-- during the computation. For more general access, use+-- "Control.Monad.Trans.State" instead.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Writer.Lazy (+ -- * The Writer monad+ Writer,+ writer,+ runWriter,+ execWriter,+ mapWriter,+ -- * The WriterT monad transformer+ WriterT(..),+ execWriterT,+ mapWriterT,+ -- * Writer operations+ tell,+ listen,+ listens,+ pass,+ censor,+ -- * Lifting other operations+ liftCallCC,+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Data.Foldable (Foldable(foldMap))+import Data.Monoid+import Data.Traversable (Traversable(traverse))++-- ---------------------------------------------------------------------------+-- | A writer monad parameterized by the type @w@ of output to accumulate.+--+-- The 'return' function produces the output 'mempty', while @>>=@+-- combines the outputs of the subcomputations using 'mappend'.+type Writer w = WriterT w Identity++-- | Construct a writer computation from a (result, output) pair.+-- (The inverse of 'runWriter'.)+writer :: Monad m => (a, w) -> WriterT w m a+writer = WriterT . return++-- | Unwrap a writer computation as a (result, output) pair.+-- (The inverse of 'writer'.)+runWriter :: Writer w a -> (a, w)+runWriter = runIdentity . runWriterT++-- | Extract the output from a writer computation.+--+-- * @'execWriter' m = 'snd' ('runWriter' m)@+execWriter :: Writer w a -> w+execWriter m = snd (runWriter m)++-- | Map both the return value and output of a computation using+-- the given function.+--+-- * @'runWriter' ('mapWriter' f m) = f ('runWriter' m)@+mapWriter :: ((a, w) -> (b, w')) -> Writer w a -> Writer w' b+mapWriter f = mapWriterT (Identity . f . runIdentity)++-- ---------------------------------------------------------------------------+-- | A writer monad parameterized by:+--+-- * @w@ - the output to accumulate.+--+-- * @m@ - The inner monad.+--+-- The 'return' function produces the output 'mempty', while @>>=@+-- combines the outputs of the subcomputations using 'mappend'.+newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }++-- | Extract the output from a writer computation.+--+-- * @'execWriterT' m = 'liftM' 'snd' ('runWriterT' m)@+execWriterT :: Monad m => WriterT w m a -> m w+execWriterT m = do+ ~(_, w) <- runWriterT m+ return w++-- | Map both the return value and output of a computation using+-- the given function.+--+-- * @'runWriterT' ('mapWriterT' f m) = f ('runWriterT' m)@+mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b+mapWriterT f m = WriterT $ f (runWriterT m)++instance (Functor m) => Functor (WriterT w m) where+ fmap f = mapWriterT $ fmap $ \ ~(a, w) -> (f a, w)++instance (Monoid w, Applicative m) => Applicative (WriterT w m) where+ pure a = WriterT $ pure (a, mempty)+ f <*> v = WriterT $ liftA2 k (runWriterT f) (runWriterT v)+ where k ~(a, w) ~(b, w') = (a b, w `mappend` w')++instance (Monoid w, Alternative m) => Alternative (WriterT w m) where+ empty = WriterT empty+ m <|> n = WriterT $ runWriterT m <|> runWriterT n++instance (Monoid w, Monad m) => Monad (WriterT w m) where+ return a = writer (a, mempty)+ m >>= k = WriterT $ do+ ~(a, w) <- runWriterT m+ ~(b, w') <- runWriterT (k a)+ return (b, w `mappend` w')+ fail msg = WriterT $ fail msg++instance (Monoid w, MonadPlus m) => MonadPlus (WriterT w m) where+ mzero = WriterT mzero+ m `mplus` n = WriterT $ runWriterT m `mplus` runWriterT n++instance (Monoid w, MonadFix m) => MonadFix (WriterT w m) where+ mfix m = WriterT $ mfix $ \ ~(a, _) -> runWriterT (m a)++instance (Monoid w) => MonadTrans (WriterT w) where+ lift m = WriterT $ do+ a <- m+ return (a, mempty)++instance (Monoid w, MonadIO m) => MonadIO (WriterT w m) where+ liftIO = lift . liftIO++instance Foldable f => Foldable (WriterT w f) where+ foldMap f (WriterT a) = foldMap (f . fst) a++instance Traversable f => Traversable (WriterT w f) where+ traverse f (WriterT a) = WriterT <$> traverse f' a where+ f' (a, b) = fmap (\c -> (c, b)) (f a)++-- | @'tell' w@ is an action that produces the output @w@.+tell :: (Monoid w, Monad m) => w -> WriterT w m ()+tell w = writer ((), w)++-- | @'listen' m@ is an action that executes the action @m@ and adds its+-- output to the value of the computation.+--+-- * @'runWriterT' ('listen' m) = 'liftM' (\\(a, w) -> ((a, w), w)) ('runWriterT' m)@+listen :: (Monoid w, Monad m) => WriterT w m a -> WriterT w m (a, w)+listen m = WriterT $ do+ ~(a, w) <- runWriterT m+ return ((a, w), w)++-- | @'listens' f m@ is an action that executes the action @m@ and adds+-- the result of applying @f@ to the output to the value of the computation.+--+-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@+--+-- * @'runWriterT' ('listens' f m) = 'liftM' (\\(a, w) -> ((a, f w), w)) ('runWriterT' m)@+listens :: (Monoid w, Monad m) => (w -> b) -> WriterT w m a -> WriterT w m (a, b)+listens f m = WriterT $ do+ ~(a, w) <- runWriterT m+ return ((a, f w), w)++-- | @'pass' m@ is an action that executes the action @m@, which returns+-- a value and a function, and returns the value, applying the function+-- to the output.+--+-- * @'runWriterT' ('pass' m) = 'liftM' (\\((a, f), w) -> (a, f w)) ('runWriterT' m)@+pass :: (Monoid w, Monad m) => WriterT w m (a, w -> w) -> WriterT w m a+pass m = WriterT $ do+ ~((a, f), w) <- runWriterT m+ return (a, f w)++-- | @'censor' f m@ is an action that executes the action @m@ and+-- applies the function @f@ to its output, leaving the return value+-- unchanged.+--+-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@+--+-- * @'runWriterT' ('censor' f m) = 'liftM' (\\(a, w) -> (a, f w)) ('runWriterT' m)@+censor :: (Monoid w, Monad m) => (w -> w) -> WriterT w m a -> WriterT w m a+censor f m = WriterT $ do+ ~(a, w) <- runWriterT m+ return (a, f w)++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: (Monoid w) => ((((a,w) -> m (b,w)) -> m (a,w)) -> m (a,w)) ->+ ((a -> WriterT w m b) -> WriterT w m a) -> WriterT w m a+liftCallCC callCC f = WriterT $+ callCC $ \c ->+ runWriterT (f (\a -> WriterT $ c (a, mempty)))++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,w) -> (e -> m (a,w)) -> m (a,w)) ->+ WriterT w m a -> (e -> WriterT w m a) -> WriterT w m a+liftCatch catchError m h =+ WriterT $ runWriterT m `catchError` \e -> runWriterT (h e)
+ Control/Monad/Trans/Writer/Strict.hs view
@@ -0,0 +1,211 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Trans.Writer.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The strict 'WriterT' monad transformer, which adds collection of+-- outputs (such as a count or string output) to a given monad.+--+-- This version builds its output strictly; for a lazy version, see+-- "Control.Monad.Trans.Writer.Lazy", which has the same interface.+--+-- This monad transformer provides only limited access to the output+-- during the computation. For more general access, use+-- "Control.Monad.Trans.State" instead.+-----------------------------------------------------------------------------++module Control.Monad.Trans.Writer.Strict (+ -- * The Writer monad+ Writer,+ writer,+ runWriter,+ execWriter,+ mapWriter,+ -- * The WriterT monad transformer+ WriterT(..),+ execWriterT,+ mapWriterT,+ -- * Writer operations+ tell,+ listen,+ listens,+ pass,+ censor,+ -- * Lifting other operations+ liftCallCC,+ liftCatch,+ ) where++import Control.Monad.IO.Class+import Control.Monad.Trans.Class+import Data.Functor.Identity++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Data.Foldable (Foldable(foldMap))+import Data.Monoid+import Data.Traversable (Traversable(traverse))++-- ---------------------------------------------------------------------------+-- | A writer monad parameterized by the type @w@ of output to accumulate.+--+-- The 'return' function produces the output 'mempty', while @>>=@+-- combines the outputs of the subcomputations using 'mappend'.+type Writer w = WriterT w Identity++-- | Construct a writer computation from a (result, output) pair.+-- (The inverse of 'runWriter'.)+writer :: Monad m => (a, w) -> WriterT w m a+writer = WriterT . return++-- | Unwrap a writer computation as a (result, output) pair.+-- (The inverse of 'writer'.)+runWriter :: Writer w a -> (a, w)+runWriter = runIdentity . runWriterT++-- | Extract the output from a writer computation.+--+-- * @'execWriter' m = 'snd' ('runWriter' m)@+execWriter :: Writer w a -> w+execWriter m = snd (runWriter m)++-- | Map both the return value and output of a computation using+-- the given function.+--+-- * @'runWriter' ('mapWriter' f m) = f ('runWriter' m)@+mapWriter :: ((a, w) -> (b, w')) -> Writer w a -> Writer w' b+mapWriter f = mapWriterT (Identity . f . runIdentity)++-- ---------------------------------------------------------------------------+-- | A writer monad parameterized by:+--+-- * @w@ - the output to accumulate.+--+-- * @m@ - The inner monad.+--+-- The 'return' function produces the output 'mempty', while @>>=@+-- combines the outputs of the subcomputations using 'mappend'.+newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }++-- | Extract the output from a writer computation.+--+-- * @'execWriterT' m = 'liftM' 'snd' ('runWriterT' m)@+execWriterT :: Monad m => WriterT w m a -> m w+execWriterT m = do+ (_, w) <- runWriterT m+ return w++-- | Map both the return value and output of a computation using+-- the given function.+--+-- * @'runWriterT' ('mapWriterT' f m) = f ('runWriterT' m)@+mapWriterT :: (m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b+mapWriterT f m = WriterT $ f (runWriterT m)++instance (Functor m) => Functor (WriterT w m) where+ fmap f = mapWriterT $ fmap $ \ (a, w) -> (f a, w)++instance (Foldable f) => Foldable (WriterT w f) where+ foldMap f (WriterT a) = foldMap (f . fst) a++instance (Traversable f) => Traversable (WriterT w f) where+ traverse f (WriterT a) = WriterT <$> traverse f' a where+ f' (a, b) = fmap (\c -> (c, b)) (f a)++instance (Monoid w, Applicative m) => Applicative (WriterT w m) where+ pure a = WriterT $ pure (a, mempty)+ f <*> v = WriterT $ liftA2 k (runWriterT f) (runWriterT v)+ where k (a, w) (b, w') = (a b, w `mappend` w')++instance (Monoid w, Alternative m) => Alternative (WriterT w m) where+ empty = WriterT empty+ m <|> n = WriterT $ runWriterT m <|> runWriterT n++instance (Monoid w, Monad m) => Monad (WriterT w m) where+ return a = WriterT $ return (a, mempty)+ m >>= k = WriterT $ do+ (a, w) <- runWriterT m+ (b, w') <- runWriterT (k a)+ return (b, w `mappend` w')+ fail msg = WriterT $ fail msg++instance (Monoid w, MonadPlus m) => MonadPlus (WriterT w m) where+ mzero = WriterT mzero+ m `mplus` n = WriterT $ runWriterT m `mplus` runWriterT n++instance (Monoid w, MonadFix m) => MonadFix (WriterT w m) where+ mfix m = WriterT $ mfix $ \ ~(a, _) -> runWriterT (m a)++instance (Monoid w) => MonadTrans (WriterT w) where+ lift m = WriterT $ do+ a <- m+ return (a, mempty)++instance (Monoid w, MonadIO m) => MonadIO (WriterT w m) where+ liftIO = lift . liftIO++-- | @'tell' w@ is an action that produces the output @w@.+tell :: (Monoid w, Monad m) => w -> WriterT w m ()+tell w = WriterT $ return ((), w)++-- | @'listen' m@ is an action that executes the action @m@ and adds its+-- output to the value of the computation.+--+-- * @'runWriterT' ('listen' m) = 'liftM' (\\(a, w) -> ((a, w), w)) ('runWriterT' m)@+listen :: (Monoid w, Monad m) => WriterT w m a -> WriterT w m (a, w)+listen m = WriterT $ do+ (a, w) <- runWriterT m+ return ((a, w), w)++-- | @'listens' f m@ is an action that executes the action @m@ and adds+-- the result of applying @f@ to the output to the value of the computation.+--+-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@+--+-- * @'runWriterT' ('listens' f m) = 'liftM' (\\(a, w) -> ((a, f w), w)) ('runWriterT' m)@+listens :: (Monoid w, Monad m) => (w -> b) -> WriterT w m a -> WriterT w m (a, b)+listens f m = WriterT $ do+ (a, w) <- runWriterT m+ return ((a, f w), w)++-- | @'pass' m@ is an action that executes the action @m@, which returns+-- a value and a function, and returns the value, applying the function+-- to the output.+--+-- * @'runWriterT' ('pass' m) = 'liftM' (\\((a, f), w) -> (a, f w)) ('runWriterT' m)@+pass :: (Monoid w, Monad m) => WriterT w m (a, w -> w) -> WriterT w m a+pass m = WriterT $ do+ ((a, f), w) <- runWriterT m+ return (a, f w)++-- | @'censor' f m@ is an action that executes the action @m@ and+-- applies the function @f@ to its output, leaving the return value+-- unchanged.+--+-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@+--+-- * @'runWriterT' ('censor' f m) = 'liftM' (\\(a, w) -> (a, f w)) ('runWriterT' m)@+censor :: (Monoid w, Monad m) => (w -> w) -> WriterT w m a -> WriterT w m a+censor f m = WriterT $ do+ (a, w) <- runWriterT m+ return (a, f w)++-- | Lift a @callCC@ operation to the new monad.+liftCallCC :: (Monoid w) => ((((a,w) -> m (b,w)) -> m (a,w)) -> m (a,w)) ->+ ((a -> WriterT w m b) -> WriterT w m a) -> WriterT w m a+liftCallCC callCC f = WriterT $+ callCC $ \c ->+ runWriterT (f (\a -> WriterT $ c (a, mempty)))++-- | Lift a @catchError@ operation to the new monad.+liftCatch :: (m (a,w) -> (e -> m (a,w)) -> m (a,w)) ->+ WriterT w m a -> (e -> WriterT w m a) -> WriterT w m a+liftCatch catchError m h =+ WriterT $ runWriterT m `catchError` \e -> runWriterT (h e)
+ Control/Monad/Writer.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Writer+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- The MonadWriter class.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.Writer (+ module Control.Monad.Writer.Lazy+ ) where++import Control.Monad.Writer.Lazy
+ Control/Monad/Writer/Class.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE UndecidableInstances #-}+-- Search for UndecidableInstances to see why this is needed++-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Writer.Class+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- The MonadWriter class.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.Writer.Class (+ MonadWriter(..),+ listens,+ censor,+ ) where++import Control.Monad.Trans.Error as Error+import Control.Monad.Trans.Identity as Identity+import Control.Monad.Trans.Maybe as Maybe+import Control.Monad.Trans.Reader+import qualified Control.Monad.Trans.RWS.Lazy as LazyRWS (+ RWST, writer, tell, listen, pass)+import qualified Control.Monad.Trans.RWS.Strict as StrictRWS (+ RWST, writer, tell, listen, pass)+import Control.Monad.Trans.State.Lazy as Lazy+import Control.Monad.Trans.State.Strict as Strict+import qualified Control.Monad.Trans.Writer.Lazy as Lazy (+ WriterT, writer, tell, listen, pass)+import qualified Control.Monad.Trans.Writer.Strict as Strict (+ WriterT, writer, tell, listen, pass)++import Control.Monad.Trans.Class (lift)+import Control.Monad+import Data.Monoid++-- ---------------------------------------------------------------------------+-- MonadWriter class+--+-- tell is like tell on the MUD's it shouts to monad+-- what you want to be heard. The monad carries this 'packet'+-- upwards, merging it if needed (hence the Monoid requirement).+--+-- listen listens to a monad acting, and returns what the monad "said".+--+-- pass lets you provide a writer transformer which changes internals of+-- the written object.++class (Monoid w, Monad m) => MonadWriter w m | m -> w where+ -- | @'writer' (a,w)@ embeds a simple writer action.+ writer :: (a,w) -> m a+ writer ~(a, w) = do+ tell w+ return a++ -- | @'tell' w@ is an action that produces the output @w@.+ tell :: w -> m ()+ tell w = writer ((),w)++ -- | @'listen' m@ is an action that executes the action @m@ and adds+ -- its output to the value of the computation.+ listen :: m a -> m (a, w)+ -- | @'pass' m@ is an action that executes the action @m@, which+ -- returns a value and a function, and returns the value, applying+ -- the function to the output.+ pass :: m (a, w -> w) -> m a++-- | @'listens' f m@ is an action that executes the action @m@ and adds+-- the result of applying @f@ to the output to the value of the computation.+--+-- * @'listens' f m = 'liftM' (id *** f) ('listen' m)@+listens :: MonadWriter w m => (w -> b) -> m a -> m (a, b)+listens f m = do+ ~(a, w) <- listen m+ return (a, f w)++-- | @'censor' f m@ is an action that executes the action @m@ and+-- applies the function @f@ to its output, leaving the return value+-- unchanged.+--+-- * @'censor' f m = 'pass' ('liftM' (\\x -> (x,f)) m)@+censor :: MonadWriter w m => (w -> w) -> m a -> m a+censor f m = pass $ do+ a <- m+ return (a, f)++instance (Monoid w, Monad m) => MonadWriter w (Lazy.WriterT w m) where+ writer = Lazy.writer+ tell = Lazy.tell+ listen = Lazy.listen+ pass = Lazy.pass++instance (Monoid w, Monad m) => MonadWriter w (Strict.WriterT w m) where+ writer = Strict.writer+ tell = Strict.tell+ listen = Strict.listen+ pass = Strict.pass++instance (Monoid w, Monad m) => MonadWriter w (LazyRWS.RWST r w s m) where+ writer = LazyRWS.writer+ tell = LazyRWS.tell+ listen = LazyRWS.listen+ pass = LazyRWS.pass++instance (Monoid w, Monad m) => MonadWriter w (StrictRWS.RWST r w s m) where+ writer = StrictRWS.writer+ tell = StrictRWS.tell+ listen = StrictRWS.listen+ pass = StrictRWS.pass++-- ---------------------------------------------------------------------------+-- Instances for other mtl transformers+--+-- All of these instances need UndecidableInstances,+-- because they do not satisfy the coverage condition.++instance (Error e, MonadWriter w m) => MonadWriter w (ErrorT e m) where+ writer = lift . writer+ tell = lift . tell+ listen = Error.liftListen listen+ pass = Error.liftPass pass++instance MonadWriter w m => MonadWriter w (IdentityT m) where+ writer = lift . writer+ tell = lift . tell+ listen = Identity.mapIdentityT listen+ pass = Identity.mapIdentityT pass++instance MonadWriter w m => MonadWriter w (MaybeT m) where+ writer = lift . writer+ tell = lift . tell+ listen = Maybe.liftListen listen+ pass = Maybe.liftPass pass++instance MonadWriter w m => MonadWriter w (ReaderT r m) where+ writer = lift . writer+ tell = lift . tell+ listen = mapReaderT listen+ pass = mapReaderT pass++instance MonadWriter w m => MonadWriter w (Lazy.StateT s m) where+ writer = lift . writer+ tell = lift . tell+ listen = Lazy.liftListen listen+ pass = Lazy.liftPass pass++instance MonadWriter w m => MonadWriter w (Strict.StateT s m) where+ writer = lift . writer+ tell = lift . tell+ listen = Strict.liftListen listen+ pass = Strict.liftPass pass
+ Control/Monad/Writer/Lazy.hs view
@@ -0,0 +1,49 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Writer.Lazy+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Lazy writer monads.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.Writer.Lazy (+ -- * MonadWriter class+ MonadWriter(..),+ listens,+ censor,+ -- * The Writer monad+ Writer,+ runWriter,+ execWriter,+ mapWriter,+ -- * The WriterT monad transformer+ WriterT(..),+ execWriterT,+ mapWriterT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ module Data.Monoid,+ ) where++import Control.Monad.Writer.Class++import Control.Monad.Trans+import Control.Monad.Trans.Writer.Lazy (+ Writer, runWriter, execWriter, mapWriter,+ WriterT(..), execWriterT, mapWriterT)++import Control.Monad+import Control.Monad.Fix+import Data.Monoid
+ Control/Monad/Writer/Strict.hs view
@@ -0,0 +1,49 @@+-----------------------------------------------------------------------------+-- |+-- Module : Control.Monad.Writer.Strict+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology, 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable (multi-param classes, functional dependencies)+--+-- Strict writer monads.+--+-- Inspired by the paper+-- /Functional Programming with Overloading and Higher-Order Polymorphism/,+-- Mark P Jones (<http://web.cecs.pdx.edu/~mpj/pubs/springschool.html>)+-- Advanced School of Functional Programming, 1995.+-----------------------------------------------------------------------------++module Control.Monad.Writer.Strict (+ -- * MonadWriter class+ MonadWriter(..),+ listens,+ censor,+ -- * The Writer monad+ Writer,+ runWriter,+ execWriter,+ mapWriter,+ -- * The WriterT monad transformer+ WriterT(..),+ execWriterT,+ mapWriterT,+ module Control.Monad,+ module Control.Monad.Fix,+ module Control.Monad.Trans,+ module Data.Monoid,+ ) where++import Control.Monad.Writer.Class++import Control.Monad.Trans+import Control.Monad.Trans.Writer.Strict (+ Writer, runWriter, execWriter, mapWriter,+ WriterT(..), execWriterT, mapWriterT)++import Control.Monad+import Control.Monad.Fix+import Data.Monoid
+ Data/Functor/Compose.hs view
@@ -0,0 +1,40 @@+-- |+-- Module : Data.Functor.Compose+-- Copyright : (c) Ross Paterson 2010+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Composition of functors.++module Data.Functor.Compose (+ Compose(..),+ ) where++import Control.Applicative+import Data.Foldable (Foldable(foldMap))+import Data.Traversable (Traversable(traverse))++-- | Right-to-left composition of functors.+-- The composition of applicative functors is always applicative,+-- but the composition of monads is not always a monad.+newtype Compose f g a = Compose { getCompose :: f (g a) }++instance (Functor f, Functor g) => Functor (Compose f g) where+ fmap f (Compose x) = Compose (fmap (fmap f) x)++instance (Foldable f, Foldable g) => Foldable (Compose f g) where+ foldMap f (Compose t) = foldMap (foldMap f) t++instance (Traversable f, Traversable g) => Traversable (Compose f g) where+ traverse f (Compose t) = Compose <$> traverse (traverse f) t++instance (Applicative f, Applicative g) => Applicative (Compose f g) where+ pure x = Compose (pure (pure x))+ Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)++instance (Alternative f, Applicative g) => Alternative (Compose f g) where+ empty = Compose empty+ Compose x <|> Compose y = Compose (x <|> y)
+ Data/Functor/Constant.hs view
@@ -0,0 +1,35 @@+-- |+-- Module : Data.Functor.Constant+-- Copyright : (c) Ross Paterson 2010+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The constant functor.++module Data.Functor.Constant (+ Constant(..),+ ) where++import Control.Applicative+import Data.Foldable (Foldable(foldMap))+import Data.Monoid (Monoid(..))+import Data.Traversable (Traversable(traverse))++-- | Constant functor.+newtype Constant a b = Constant { getConstant :: a }++instance Functor (Constant a) where+ fmap f (Constant x) = Constant x++instance Foldable (Constant a) where+ foldMap f (Constant x) = mempty++instance Traversable (Constant a) where+ traverse f (Constant x) = pure (Constant x)++instance (Monoid a) => Applicative (Constant a) where+ pure _ = Constant mempty+ Constant x <*> Constant y = Constant (x `mappend` y)
+ Data/Functor/Identity.hs view
@@ -0,0 +1,57 @@+-- |+-- Module : Data.Functor.Identity+-- Copyright : (c) Andy Gill 2001,+-- (c) Oregon Graduate Institute of Science and Technology 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- The identity functor and monad.+--+-- This trivial type constructor serves two purposes:+--+-- * It can be used with functions parameterized by functor or monad classes.+--+-- * It can be used as a base monad to which a series of monad+-- transformers may be applied to construct a composite monad.+-- Most monad transformer modules include the special case of+-- applying the transformer to 'Identity'. For example, @State s@+-- is an abbreviation for @StateT s 'Identity'@.++module Data.Functor.Identity (+ Identity(..),+ ) where++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Data.Foldable (Foldable(foldMap))+import Data.Traversable (Traversable(traverse))++-- | Identity functor and monad.+newtype Identity a = Identity { runIdentity :: a }++-- ---------------------------------------------------------------------------+-- Identity instances for Functor and Monad++instance Functor Identity where+ fmap f m = Identity (f (runIdentity m))++instance Foldable Identity where+ foldMap f (Identity x) = f x++instance Traversable Identity where+ traverse f (Identity x) = Identity <$> f x++instance Applicative Identity where+ pure a = Identity a+ Identity f <*> Identity x = Identity (f x)++instance Monad Identity where+ return a = Identity a+ m >>= k = k (runIdentity m)++instance MonadFix Identity where+ mfix f = Identity (fix (runIdentity . f))
+ Data/Functor/Product.hs view
@@ -0,0 +1,58 @@+-- |+-- Module : Data.Functor.Product+-- Copyright : (c) Ross Paterson 2010+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : ross@soi.city.ac.uk+-- Stability : experimental+-- Portability : portable+--+-- Products, lifted to functors.++module Data.Functor.Product (+ Product(..),+ ) where++import Control.Applicative+import Control.Monad (MonadPlus(..))+import Control.Monad.Fix (MonadFix(..))+import Data.Foldable (Foldable(foldMap))+import Data.Monoid (mappend)+import Data.Traversable (Traversable(traverse))++-- | Lifted product of functors.+data Product f g a = Pair (f a) (g a)++instance (Functor f, Functor g) => Functor (Product f g) where+ fmap f (Pair x y) = Pair (fmap f x) (fmap f y)++instance (Foldable f, Foldable g) => Foldable (Product f g) where+ foldMap f (Pair x y) = foldMap f x `mappend` foldMap f y++instance (Traversable f, Traversable g) => Traversable (Product f g) where+ traverse f (Pair x y) = Pair <$> traverse f x <*> traverse f y++instance (Applicative f, Applicative g) => Applicative (Product f g) where+ pure x = Pair (pure x) (pure x)+ Pair f g <*> Pair x y = Pair (f <*> x) (g <*> y)++instance (Alternative f, Alternative g) => Alternative (Product f g) where+ empty = Pair empty empty+ Pair x1 y1 <|> Pair x2 y2 = Pair (x1 <|> x2) (y1 <|> y2)++instance (Monad f, Monad g) => Monad (Product f g) where+ return x = Pair (return x) (return x)+ Pair m n >>= f = Pair (m >>= fstP . f) (n >>= sndP . f)+ where+ fstP (Pair a _) = a+ sndP (Pair _ b) = b++instance (MonadPlus f, MonadPlus g) => MonadPlus (Product f g) where+ mzero = Pair mzero mzero+ Pair x1 y1 `mplus` Pair x2 y2 = Pair (x1 `mplus` x2) (y1 `mplus` y2)++instance (MonadFix f, MonadFix g) => MonadFix (Product f g) where+ mfix f = Pair (mfix (fstP . f)) (mfix (sndP . f))+ where+ fstP (Pair a _) = a+ sndP (Pair _ b) = b
+ Data/Functor/Reverse.hs view
@@ -0,0 +1,54 @@+-- |+-- Module : Data.Functor.Reverse+-- Copyright : (c) Russell O'Connor 2009+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : portable+--+-- Making functors whose elements are notionally in the reverse order+-- from the original functor.++module Data.Functor.Reverse where++import Control.Applicative.Backwards++import Prelude hiding (foldr, foldr1, foldl, foldl1)+import Control.Applicative+import Data.Foldable+import Data.Traversable+import Data.Monoid++-- | The same functor, but with 'Foldable' and 'Traversable' instances+-- that process the elements in the reverse order.+newtype Reverse f a = Reverse { getReverse :: f a }++-- | Derived instance.+instance (Functor f) => Functor (Reverse f) where+ fmap f (Reverse a) = Reverse (fmap f a)++-- | Derived instance.+instance (Applicative f) => Applicative (Reverse f) where+ pure a = Reverse (pure a)+ Reverse f <*> Reverse a = Reverse (f <*> a)++-- | Derived instance.+instance (Alternative f) => Alternative (Reverse f) where+ empty = Reverse empty+ Reverse x <|> Reverse y = Reverse (x <|> y)++-- | Fold from right to left.+instance (Foldable f) => Foldable (Reverse f) where+ foldMap f (Reverse t) = getDual (foldMap (Dual . f) t)+ foldr f z (Reverse t) = foldl (flip f) z t+ foldl f z (Reverse t) = foldr (flip f) z t+ foldr1 f (Reverse t) = foldl1 (flip f) t+ foldl1 f (Reverse t) = foldr1 (flip f) t++-- | Traverse from right to left.+instance (Traversable f) => Traversable (Reverse f) where+ traverse f (Reverse t) =+ fmap Reverse . forwards $ traverse (Backwards . f) t+ sequenceA (Reverse t) =+ fmap Reverse . forwards $ sequenceA (fmap Backwards t)
+ Distribution/Client/BuildReports/Anonymous.hs view
@@ -0,0 +1,311 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Reporting+-- Copyright : (c) David Waern 2008+-- License : BSD-like+--+-- Maintainer : david.waern@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Anonymous build report data structure, printing and parsing+--+-----------------------------------------------------------------------------+module Distribution.Client.BuildReports.Anonymous (+ BuildReport(..),+ InstallOutcome(..),+ Outcome(..),++ -- * Constructing and writing reports+ new,++ -- * parsing and pretty printing+ parse,+ parseList,+ show,+-- showList,+ ) where++import Distribution.Client.Types+ ( ConfiguredPackage(..) )+import qualified Distribution.Client.Types as BR+ ( BuildResult, BuildFailure(..), BuildSuccess(..)+ , DocsResult(..), TestsResult(..) )+import Distribution.Client.Utils+ ( mergeBy, MergeResult(..) )+import qualified Paths_cabal_install_bundle (version)++import Distribution.Package+ ( PackageIdentifier(..), PackageName(..), Package(packageId) )+import Distribution.PackageDescription+ ( FlagName(..), FlagAssignment )+--import Distribution.Version+-- ( Version )+import Distribution.System+ ( OS, Arch )+import Distribution.Compiler+ ( CompilerId )+import qualified Distribution.Text as Text+ ( Text(disp, parse) )+import Distribution.ParseUtils+ ( FieldDescr(..), ParseResult(..), Field(..)+ , simpleField, listField, ppFields, readFields+ , syntaxError, locatedErrorMsg )+import Distribution.Simple.Utils+ ( comparing )++import qualified Distribution.Compat.ReadP as Parse+ ( ReadP, pfail, munch1, skipSpaces )+import qualified Text.PrettyPrint.HughesPJ as Disp+ ( Doc, render, char, text )+import Text.PrettyPrint.HughesPJ+ ( (<+>), (<>) )++import Data.List+ ( unfoldr, sortBy )+import Data.Char as Char+ ( isAlpha, isAlphaNum )++import Prelude hiding (show)++data BuildReport+ = BuildReport {+ -- | The package this build report is about+ package :: PackageIdentifier,++ -- | The OS and Arch the package was built on+ os :: OS,+ arch :: Arch,++ -- | The Haskell compiler (and hopefully version) used+ compiler :: CompilerId,++ -- | The uploading client, ie cabal-install-x.y.z+ client :: PackageIdentifier,++ -- | Which configurations flags we used+ flagAssignment :: FlagAssignment,++ -- | Which dependent packages we were using exactly+ dependencies :: [PackageIdentifier],++ -- | Did installing work ok?+ installOutcome :: InstallOutcome,++ -- Which version of the Cabal library was used to compile the Setup.hs+-- cabalVersion :: Version,++ -- Which build tools we were using (with versions)+-- tools :: [PackageIdentifier],++ -- | Configure outcome, did configure work ok?+ docsOutcome :: Outcome,++ -- | Configure outcome, did configure work ok?+ testsOutcome :: Outcome+ }++data InstallOutcome+ = DependencyFailed PackageIdentifier+ | DownloadFailed+ | UnpackFailed+ | SetupFailed+ | ConfigureFailed+ | BuildFailed+ | InstallFailed+ | InstallOk+ deriving Eq++data Outcome = NotTried | Failed | Ok+ deriving Eq++new :: OS -> Arch -> CompilerId -- -> Version+ -> ConfiguredPackage -> BR.BuildResult+ -> BuildReport+new os' arch' comp (ConfiguredPackage pkg flags deps) result =+ BuildReport {+ package = packageId pkg,+ os = os',+ arch = arch',+ compiler = comp,+ client = cabalInstallID,+ flagAssignment = flags,+ dependencies = deps,+ installOutcome = convertInstallOutcome,+-- cabalVersion = undefined+ docsOutcome = convertDocsOutcome,+ testsOutcome = convertTestsOutcome+ }+ where+ convertInstallOutcome = case result of+ Left (BR.DependentFailed p) -> DependencyFailed p+ Left (BR.DownloadFailed _) -> DownloadFailed+ Left (BR.UnpackFailed _) -> UnpackFailed+ Left (BR.ConfigureFailed _) -> ConfigureFailed+ Left (BR.BuildFailed _) -> BuildFailed+ Left (BR.InstallFailed _) -> InstallFailed+ Right (BR.BuildOk _ _) -> InstallOk+ convertDocsOutcome = case result of+ Left _ -> NotTried+ Right (BR.BuildOk BR.DocsNotTried _) -> NotTried+ Right (BR.BuildOk BR.DocsFailed _) -> Failed+ Right (BR.BuildOk BR.DocsOk _) -> Ok+ convertTestsOutcome = case result of+ Left _ -> NotTried+ Right (BR.BuildOk _ BR.TestsNotTried) -> NotTried+ Right (BR.BuildOk _ BR.TestsFailed) -> Failed+ Right (BR.BuildOk _ BR.TestsOk) -> Ok++cabalInstallID :: PackageIdentifier+cabalInstallID =+ PackageIdentifier (PackageName "cabal-install") Paths_cabal_install_bundle.version++-- ------------------------------------------------------------+-- * External format+-- ------------------------------------------------------------++initialBuildReport :: BuildReport+initialBuildReport = BuildReport {+ package = requiredField "package",+ os = requiredField "os",+ arch = requiredField "arch",+ compiler = requiredField "compiler",+ client = requiredField "client",+ flagAssignment = [],+ dependencies = [],+ installOutcome = requiredField "install-outcome",+-- cabalVersion = Nothing,+-- tools = [],+ docsOutcome = NotTried,+ testsOutcome = NotTried+ }+ where+ requiredField fname = error ("required field: " ++ fname)++-- -----------------------------------------------------------------------------+-- Parsing++parse :: String -> Either String BuildReport+parse s = case parseFields s of+ ParseFailed perror -> Left msg where (_, msg) = locatedErrorMsg perror+ ParseOk _ report -> Right report++--FIXME: this does not allow for optional or repeated fields+parseFields :: String -> ParseResult BuildReport+parseFields input = do+ fields <- mapM extractField =<< readFields input+ let merged = mergeBy (\desc (_,name,_) -> compare (fieldName desc) name)+ sortedFieldDescrs+ (sortBy (comparing (\(_,name,_) -> name)) fields)+ checkMerged initialBuildReport merged++ where+ extractField :: Field -> ParseResult (Int, String, String)+ extractField (F line name value) = return (line, name, value)+ extractField (Section line _ _ _) = syntaxError line "Unrecognized stanza"+ extractField (IfBlock line _ _ _) = syntaxError line "Unrecognized stanza"++ checkMerged report [] = return report+ checkMerged report (merged:remaining) = case merged of+ InBoth fieldDescr (line, _name, value) -> do+ report' <- fieldSet fieldDescr line value report+ checkMerged report' remaining+ OnlyInRight (line, name, _) ->+ syntaxError line ("Unrecognized field " ++ name)+ OnlyInLeft fieldDescr ->+ fail ("Missing field " ++ fieldName fieldDescr)++parseList :: String -> [BuildReport]+parseList str =+ [ report | Right report <- map parse (split str) ]++ where+ split :: String -> [String]+ split = filter (not . null) . unfoldr chunk . lines+ chunk [] = Nothing+ chunk ls = case break null ls of+ (r, rs) -> Just (unlines r, dropWhile null rs)++-- -----------------------------------------------------------------------------+-- Pretty-printing++show :: BuildReport -> String+show = Disp.render . ppFields fieldDescrs++-- -----------------------------------------------------------------------------+-- Description of the fields, for parsing/printing++fieldDescrs :: [FieldDescr BuildReport]+fieldDescrs =+ [ simpleField "package" Text.disp Text.parse+ package (\v r -> r { package = v })+ , simpleField "os" Text.disp Text.parse+ os (\v r -> r { os = v })+ , simpleField "arch" Text.disp Text.parse+ arch (\v r -> r { arch = v })+ , simpleField "compiler" Text.disp Text.parse+ compiler (\v r -> r { compiler = v })+ , simpleField "client" Text.disp Text.parse+ client (\v r -> r { client = v })+ , listField "flags" dispFlag parseFlag+ flagAssignment (\v r -> r { flagAssignment = v })+ , listField "dependencies" Text.disp Text.parse+ dependencies (\v r -> r { dependencies = v })+ , simpleField "install-outcome" Text.disp Text.parse+ installOutcome (\v r -> r { installOutcome = v })+ , simpleField "docs-outcome" Text.disp Text.parse+ docsOutcome (\v r -> r { docsOutcome = v })+ , simpleField "tests-outcome" Text.disp Text.parse+ testsOutcome (\v r -> r { testsOutcome = v })+ ]++sortedFieldDescrs :: [FieldDescr BuildReport]+sortedFieldDescrs = sortBy (comparing fieldName) fieldDescrs++dispFlag :: (FlagName, Bool) -> Disp.Doc+dispFlag (FlagName name, True) = Disp.text name+dispFlag (FlagName name, False) = Disp.char '-' <> Disp.text name++parseFlag :: Parse.ReadP r (FlagName, Bool)+parseFlag = do+ name <- Parse.munch1 (\c -> Char.isAlphaNum c || c == '_' || c == '-')+ case name of+ ('-':flag) -> return (FlagName flag, False)+ flag -> return (FlagName flag, True)++instance Text.Text InstallOutcome where+ disp (DependencyFailed pkgid) = Disp.text "DependencyFailed" <+> Text.disp pkgid+ disp DownloadFailed = Disp.text "DownloadFailed"+ disp UnpackFailed = Disp.text "UnpackFailed"+ disp SetupFailed = Disp.text "SetupFailed"+ disp ConfigureFailed = Disp.text "ConfigureFailed"+ disp BuildFailed = Disp.text "BuildFailed"+ disp InstallFailed = Disp.text "InstallFailed"+ disp InstallOk = Disp.text "InstallOk"++ parse = do+ name <- Parse.munch1 Char.isAlphaNum+ case name of+ "DependencyFailed" -> do Parse.skipSpaces+ pkgid <- Text.parse+ return (DependencyFailed pkgid)+ "DownloadFailed" -> return DownloadFailed+ "UnpackFailed" -> return UnpackFailed+ "SetupFailed" -> return SetupFailed+ "ConfigureFailed" -> return ConfigureFailed+ "BuildFailed" -> return BuildFailed+ "InstallFailed" -> return InstallFailed+ "InstallOk" -> return InstallOk+ _ -> Parse.pfail++instance Text.Text Outcome where+ disp NotTried = Disp.text "NotTried"+ disp Failed = Disp.text "Failed"+ disp Ok = Disp.text "Ok"+ parse = do+ name <- Parse.munch1 Char.isAlpha+ case name of+ "NotTried" -> return NotTried+ "Failed" -> return Failed+ "Ok" -> return Ok+ _ -> Parse.pfail
+ Distribution/Client/BuildReports/Storage.hs view
@@ -0,0 +1,127 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Reporting+-- Copyright : (c) David Waern 2008+-- License : BSD-like+--+-- Maintainer : david.waern@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Anonymous build report data structure, printing and parsing+--+-----------------------------------------------------------------------------+module Distribution.Client.BuildReports.Storage (++ -- * Storing and retrieving build reports+ storeAnonymous,+ storeLocal,+-- retrieve,++ -- * 'InstallPlan' support+ fromInstallPlan,+ ) where++import qualified Distribution.Client.BuildReports.Anonymous as BuildReport+import Distribution.Client.BuildReports.Anonymous (BuildReport)++import Distribution.Client.Types+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan+ ( InstallPlan )++import Distribution.Simple.InstallDirs+ ( PathTemplate, fromPathTemplate+ , initialPathTemplateEnv, substPathTemplate )+import Distribution.System+ ( Platform(Platform) )+import Distribution.Compiler+ ( CompilerId )+import Distribution.Simple.Utils+ ( comparing, equating )++import Data.List+ ( groupBy, sortBy )+import Data.Maybe+ ( catMaybes )+import System.FilePath+ ( (</>), takeDirectory )+import System.Directory+ ( createDirectoryIfMissing )++storeAnonymous :: [(BuildReport, Repo)] -> IO ()+storeAnonymous reports = sequence_+ [ appendFile file (concatMap format reports')+ | (repo, reports') <- separate reports+ , let file = repoLocalDir repo </> "build-reports.log" ]+ --TODO: make this concurrency safe, either lock the report file or make sure+ -- the writes for each report are atomic (under 4k and flush at boundaries)++ where+ format r = '\n' : BuildReport.show r ++ "\n"+ separate :: [(BuildReport, Repo)]+ -> [(Repo, [BuildReport])]+ separate = map (\rs@((_,repo,_):_) -> (repo, [ r | (r,_,_) <- rs ]))+ . map concat+ . groupBy (equating (repoName . head))+ . sortBy (comparing (repoName . head))+ . groupBy (equating repoName)+ . onlyRemote+ repoName (_,_,rrepo) = remoteRepoName rrepo++ onlyRemote :: [(BuildReport, Repo)] -> [(BuildReport, Repo, RemoteRepo)]+ onlyRemote rs =+ [ (report, repo, remoteRepo)+ | (report, repo@Repo { repoKind = Left remoteRepo }) <- rs ]++storeLocal :: [PathTemplate] -> [(BuildReport, Repo)] -> IO ()+storeLocal templates reports = sequence_+ [ do createDirectoryIfMissing True (takeDirectory file)+ appendFile file output+ --TODO: make this concurrency safe, either lock the report file or make+ -- sure the writes for each report are atomic+ | (file, reports') <- groupByFileName+ [ (reportFileName template report, report)+ | template <- templates+ , (report, _repo) <- reports ]+ , let output = concatMap format reports'+ ]+ where+ format r = '\n' : BuildReport.show r ++ "\n"++ reportFileName template report =+ fromPathTemplate (substPathTemplate env template)+ where env = initialPathTemplateEnv+ (BuildReport.package report)+ (BuildReport.compiler report)++ groupByFileName = map (\grp@((filename,_):_) -> (filename, map snd grp))+ . groupBy (equating fst)+ . sortBy (comparing fst)++-- ------------------------------------------------------------+-- * InstallPlan support+-- ------------------------------------------------------------++fromInstallPlan :: InstallPlan -> [(BuildReport, Repo)]+fromInstallPlan plan = catMaybes+ . map (fromPlanPackage platform comp)+ . InstallPlan.toList+ $ plan+ where platform = InstallPlan.planPlatform plan+ comp = InstallPlan.planCompiler plan++fromPlanPackage :: Platform -> CompilerId+ -> InstallPlan.PlanPackage+ -> Maybe (BuildReport, Repo)+fromPlanPackage (Platform arch os) comp planPackage = case planPackage of++ InstallPlan.Installed pkg@(ConfiguredPackage (AvailablePackage {+ packageSource = RepoTarballPackage repo _ _ }) _ _) result+ -> Just $ (BuildReport.new os arch comp pkg (Right result), repo)++ InstallPlan.Failed pkg@(ConfiguredPackage (AvailablePackage {+ packageSource = RepoTarballPackage repo _ _ }) _ _) result+ -> Just $ (BuildReport.new os arch comp pkg (Left result), repo)++ _ -> Nothing
+ Distribution/Client/BuildReports/Types.hs view
@@ -0,0 +1,44 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.BuildReports.Types+-- Copyright : (c) Duncan Coutts 2009+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Types related to build reporting+--+-----------------------------------------------------------------------------+module Distribution.Client.BuildReports.Types (+ ReportLevel(..),+ ) where++import qualified Distribution.Text as Text+ ( Text(..) )++import qualified Distribution.Compat.ReadP as Parse+ ( pfail, munch1 )+import qualified Text.PrettyPrint.HughesPJ as Disp+ ( text )++import Data.Char as Char+ ( isAlpha, toLower )++data ReportLevel = NoReports | AnonymousReports | DetailedReports+ deriving (Eq, Ord, Show)++instance Text.Text ReportLevel where+ disp NoReports = Disp.text "none"+ disp AnonymousReports = Disp.text "anonymous"+ disp DetailedReports = Disp.text "detailed"+ parse = do+ name <- Parse.munch1 Char.isAlpha+ case lowercase name of+ "none" -> return NoReports+ "anonymous" -> return AnonymousReports+ "detailed" -> return DetailedReports+ _ -> Parse.pfail++lowercase :: String -> String+lowercase = map Char.toLower
+ Distribution/Client/BuildReports/Upload.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE PatternGuards #-}+-- This is a quick hack for uploading build reports to Hackage.++module Distribution.Client.BuildReports.Upload+ ( BuildLog+ , BuildReportId+ , uploadReports+ , postBuildReport+ , putBuildLog+ ) where++import Network.Browser+ ( BrowserAction, request, setAllowRedirects )+import Network.HTTP+ ( Header(..), HeaderName(..)+ , Request(..), RequestMethod(..), Response(..) )+import Network.TCP (HandleStream)+import Network.URI (URI, uriPath, parseRelativeReference, relativeTo)++import Control.Monad+ ( forM_ )+import System.FilePath.Posix+ ( (</>) )+import qualified Distribution.Client.BuildReports.Anonymous as BuildReport+import Distribution.Client.BuildReports.Anonymous (BuildReport)+import Distribution.Text (display)++type BuildReportId = URI+type BuildLog = String++uploadReports :: URI -> [(BuildReport, Maybe BuildLog)]+ -> BrowserAction (HandleStream String) ()+ -> BrowserAction (HandleStream BuildLog) ()+uploadReports uri reports auth = do+ auth+ forM_ reports $ \(report, mbBuildLog) -> do+ buildId <- postBuildReport uri report+ case mbBuildLog of+ Just buildLog -> putBuildLog buildId buildLog+ Nothing -> return ()++postBuildReport :: URI -> BuildReport+ -> BrowserAction (HandleStream BuildLog) BuildReportId+postBuildReport uri buildReport = do+ setAllowRedirects False+ (_, response) <- request Request {+ rqURI = uri { uriPath = "/package" </> display (BuildReport.package buildReport) </> "reports" },+ rqMethod = POST,+ rqHeaders = [Header HdrContentType ("text/plain"),+ Header HdrContentLength (show (length body)),+ Header HdrAccept ("text/plain")],+ rqBody = body+ }+ case rspCode response of+ (3,0,3) | [Just buildId] <- [ do rel <- parseRelativeReference location+ relativeTo rel uri+ | Header HdrLocation location <- rspHeaders response ]+ -> return $ buildId+ _ -> error "Unrecognised response from server."+ where body = BuildReport.show buildReport++putBuildLog :: BuildReportId -> BuildLog+ -> BrowserAction (HandleStream BuildLog) ()+putBuildLog reportId buildLog = do+ --FIXME: do something if the request fails+ (_, response) <- request Request {+ rqURI = reportId{uriPath = uriPath reportId </> "log"},+ rqMethod = PUT,+ rqHeaders = [Header HdrContentType ("text/plain"),+ Header HdrContentLength (show (length buildLog)),+ Header HdrAccept ("text/plain")],+ rqBody = buildLog+ }+ return ()
+ Distribution/Client/Check.hs view
@@ -0,0 +1,85 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Check+-- Copyright : (c) Lennart Kolmodin 2008+-- License : BSD-like+--+-- Maintainer : kolmodin@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Check a package for common mistakes+--+-----------------------------------------------------------------------------+module Distribution.Client.Check (+ check+ ) where++import Control.Monad ( when, unless )++import Distribution.PackageDescription.Parse+ ( readPackageDescription )+import Distribution.PackageDescription.Check+import Distribution.PackageDescription.Configuration+ ( flattenPackageDescription )+import Distribution.Verbosity+ ( Verbosity )+import Distribution.Simple.Utils+ ( defaultPackageDesc, toUTF8, wrapText )++check :: Verbosity -> IO Bool+check verbosity = do+ pdfile <- defaultPackageDesc verbosity+ ppd <- readPackageDescription verbosity pdfile+ -- flatten the generic package description into a regular package+ -- description+ -- TODO: this may give more warnings than it should give;+ -- consider two branches of a condition, one saying+ -- ghc-options: -Wall+ -- and the other+ -- ghc-options: -Werror+ -- joined into+ -- ghc-options: -Wall -Werror+ -- checkPackages will yield a warning on the last line, but it+ -- would not on each individual branch.+ -- Hovever, this is the same way hackage does it, so we will yield+ -- the exact same errors as it will.+ let pkg_desc = flattenPackageDescription ppd+ ioChecks <- checkPackageFiles pkg_desc "."+ let packageChecks = ioChecks ++ checkPackage ppd (Just pkg_desc)+ buildImpossible = [ x | x@PackageBuildImpossible {} <- packageChecks ]+ buildWarning = [ x | x@PackageBuildWarning {} <- packageChecks ]+ distSuspicious = [ x | x@PackageDistSuspicious {} <- packageChecks ]+ distInexusable = [ x | x@PackageDistInexcusable {} <- packageChecks ]++ unless (null buildImpossible) $ do+ putStrLn "The package will not build sanely due to these errors:"+ printCheckMessages buildImpossible++ unless (null buildWarning) $ do+ putStrLn "The following warnings are likely affect your build negatively:"+ printCheckMessages buildWarning++ unless (null distSuspicious) $ do+ putStrLn "These warnings may cause trouble when distributing the package:"+ printCheckMessages distSuspicious++ unless (null distInexusable) $ do+ putStrLn "The following errors will cause portability problems on other environments:"+ printCheckMessages distInexusable++ let isDistError (PackageDistSuspicious {}) = False+ isDistError _ = True+ errors = filter isDistError packageChecks++ unless (null errors) $ do+ putStrLn "Hackage would reject this package."++ when (null packageChecks) $ do+ putStrLn "No errors or warnings could be found in the package."++ return (null packageChecks)++ where+ printCheckMessages = mapM_ (putStrLn . format . explanation)+ format = toUTF8 . wrapText . ("* "++)
+ Distribution/Client/Config.hs view
@@ -0,0 +1,532 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Config+-- Copyright : (c) David Himmelstrup 2005+-- License : BSD-like+--+-- Maintainer : lemmih@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Utilities for handling saved state such as known packages, known servers and downloaded packages.+-----------------------------------------------------------------------------+module Distribution.Client.Config (+ SavedConfig(..),+ loadConfig,++ showConfig,+ showConfigWithComments,+ parseConfig,++ defaultCabalDir,+ defaultConfigFile,+ defaultCacheDir,+ defaultLogsDir,+ ) where+++import Distribution.Client.Types+ ( RemoteRepo(..), Username(..), Password(..) )+import Distribution.Client.BuildReports.Types+ ( ReportLevel(..) )+import Distribution.Client.Setup+ ( GlobalFlags(..), globalCommand+ , ConfigExFlags(..), configureExOptions, defaultConfigExFlags+ , InstallFlags(..), installOptions, defaultInstallFlags+ , UploadFlags(..), uploadCommand+ , ReportFlags(..), reportCommand+ , showRepo, parseRepo )++import Distribution.Simple.Setup+ ( ConfigFlags(..), configureOptions, defaultConfigFlags+ , installDirsOptions+ , Flag, toFlag, flagToMaybe, fromFlagOrDefault )+import Distribution.Simple.InstallDirs+ ( InstallDirs(..), defaultInstallDirs+ , PathTemplate, toPathTemplate )+import Distribution.ParseUtils+ ( FieldDescr(..), liftField+ , ParseResult(..), locatedErrorMsg, showPWarning+ , readFields, warning, lineNo+ , simpleField, listField, parseFilePathQ, parseTokenQ )+import qualified Distribution.ParseUtils as ParseUtils+ ( Field(..) )+import qualified Distribution.Text as Text+ ( Text(..) )+import Distribution.Simple.Command+ ( CommandUI(commandOptions), commandDefaultFlags, ShowOrParseArgs(..)+ , viewAsFieldDescr )+import Distribution.Simple.Program+ ( defaultProgramConfiguration )+import Distribution.Simple.Utils+ ( notice, warn, lowercase )+import Distribution.Compiler+ ( CompilerFlavor(..), defaultCompilerFlavor )+import Distribution.Verbosity+ ( Verbosity, normal )++import Data.List+ ( partition, find )+import Data.Maybe+ ( fromMaybe )+import Data.Monoid+ ( Monoid(..) )+import Control.Monad+ ( when, foldM, liftM )+import qualified Data.Map as Map+import qualified Distribution.Compat.ReadP as Parse+ ( option )+import qualified Text.PrettyPrint.HughesPJ as Disp+ ( Doc, render, text, colon, vcat, empty, isEmpty, nest )+import Text.PrettyPrint.HughesPJ+ ( (<>), (<+>), ($$), ($+$) )+import System.Directory+ ( createDirectoryIfMissing, getAppUserDataDirectory )+import Network.URI+ ( URI(..), URIAuth(..) )+import System.FilePath+ ( (</>), takeDirectory )+import System.Environment+ ( getEnvironment )+import System.IO.Error+ ( isDoesNotExistError )++--+-- * Configuration saved in the config file+--++data SavedConfig = SavedConfig {+ savedGlobalFlags :: GlobalFlags,+ savedInstallFlags :: InstallFlags,+ savedConfigureFlags :: ConfigFlags,+ savedConfigureExFlags :: ConfigExFlags,+ savedUserInstallDirs :: InstallDirs (Flag PathTemplate),+ savedGlobalInstallDirs :: InstallDirs (Flag PathTemplate),+ savedUploadFlags :: UploadFlags,+ savedReportFlags :: ReportFlags+ }++instance Monoid SavedConfig where+ mempty = SavedConfig {+ savedGlobalFlags = mempty,+ savedInstallFlags = mempty,+ savedConfigureFlags = mempty,+ savedConfigureExFlags = mempty,+ savedUserInstallDirs = mempty,+ savedGlobalInstallDirs = mempty,+ savedUploadFlags = mempty,+ savedReportFlags = mempty+ }+ mappend a b = SavedConfig {+ savedGlobalFlags = combine savedGlobalFlags,+ savedInstallFlags = combine savedInstallFlags,+ savedConfigureFlags = combine savedConfigureFlags,+ savedConfigureExFlags = combine savedConfigureExFlags,+ savedUserInstallDirs = combine savedUserInstallDirs,+ savedGlobalInstallDirs = combine savedGlobalInstallDirs,+ savedUploadFlags = combine savedUploadFlags,+ savedReportFlags = combine savedReportFlags+ }+ where combine field = field a `mappend` field b++updateInstallDirs :: Flag Bool -> SavedConfig -> SavedConfig+updateInstallDirs userInstallFlag+ savedConfig@SavedConfig {+ savedConfigureFlags = configureFlags,+ savedUserInstallDirs = userInstallDirs,+ savedGlobalInstallDirs = globalInstallDirs+ } =+ savedConfig {+ savedConfigureFlags = configureFlags {+ configInstallDirs = installDirs+ }+ }+ where+ installDirs | userInstall = userInstallDirs+ | otherwise = globalInstallDirs+ userInstall = fromFlagOrDefault defaultUserInstall $+ configUserInstall configureFlags `mappend` userInstallFlag++--+-- * Default config+--++-- | These are the absolute basic defaults. The fields that must be+-- initialised. When we load the config from the file we layer the loaded+-- values over these ones, so any missing fields in the file take their values+-- from here.+--+baseSavedConfig :: IO SavedConfig+baseSavedConfig = do+ userPrefix <- defaultCabalDir+ logsDir <- defaultLogsDir+ worldFile <- defaultWorldFile+ return mempty {+ savedConfigureFlags = mempty {+ configHcFlavor = toFlag defaultCompiler,+ configUserInstall = toFlag defaultUserInstall,+ configVerbosity = toFlag normal+ },+ savedUserInstallDirs = mempty {+ prefix = toFlag (toPathTemplate userPrefix)+ },+ savedGlobalFlags = mempty {+ globalLogsDir = toFlag logsDir,+ globalWorldFile = toFlag worldFile+ }+ }++-- | This is the initial configuration that we write out to to the config file+-- if the file does not exist (or the config we use if the file cannot be read+-- for some other reason). When the config gets loaded it gets layered on top+-- of 'baseSavedConfig' so we do not need to include it into the initial+-- values we save into the config file.+--+initialSavedConfig :: IO SavedConfig+initialSavedConfig = do+ cacheDir <- defaultCacheDir+ logsDir <- defaultLogsDir+ worldFile <- defaultWorldFile+ return mempty {+ savedGlobalFlags = mempty {+ globalCacheDir = toFlag cacheDir,+ globalRemoteRepos = [defaultRemoteRepo],+ globalWorldFile = toFlag worldFile+ },+ savedInstallFlags = mempty {+ installSummaryFile = [toPathTemplate (logsDir </> "build.log")],+ installBuildReports= toFlag AnonymousReports+ }+ }++--TODO: misleading, there's no way to override this default+-- either make it possible or rename to simply getCabalDir.+defaultCabalDir :: IO FilePath+defaultCabalDir = getAppUserDataDirectory "cabal"++defaultConfigFile :: IO FilePath+defaultConfigFile = do+ dir <- defaultCabalDir+ return $ dir </> "config"++defaultCacheDir :: IO FilePath+defaultCacheDir = do+ dir <- defaultCabalDir+ return $ dir </> "packages"++defaultLogsDir :: IO FilePath+defaultLogsDir = do+ dir <- defaultCabalDir+ return $ dir </> "logs"++-- | Default position of the world file+defaultWorldFile :: IO FilePath+defaultWorldFile = do+ dir <- defaultCabalDir+ return $ dir </> "world"++defaultCompiler :: CompilerFlavor+defaultCompiler = fromMaybe GHC defaultCompilerFlavor++defaultUserInstall :: Bool+defaultUserInstall = True+-- We do per-user installs by default on all platforms. We used to default to+-- global installs on Windows but that no longer works on Windows Vista or 7.++defaultRemoteRepo :: RemoteRepo+defaultRemoteRepo = RemoteRepo name uri+ where+ name = "hackage.haskell.org"+ uri = URI "http:" (Just (URIAuth "" name "")) "/packages/archive" "" ""++--+-- * Config file reading+--++loadConfig :: Verbosity -> Flag FilePath -> Flag Bool -> IO SavedConfig+loadConfig verbosity configFileFlag userInstallFlag = addBaseConf $ do+ let sources = [+ ("commandline option", return . flagToMaybe $ configFileFlag),+ ("env var CABAL_CONFIG", lookup "CABAL_CONFIG" `liftM` getEnvironment),+ ("default config file", Just `liftM` defaultConfigFile) ]++ getSource [] = error "no config file path candidate found."+ getSource ((msg,action): xs) = + action >>= maybe (getSource xs) (return . (,) msg)++ (source, configFile) <- getSource sources+ minp <- readConfigFile mempty configFile+ case minp of+ Nothing -> do+ notice verbosity $ "Config file path source is " ++ source ++ "."+ notice verbosity $ "Config file " ++ configFile ++ " not found."+ notice verbosity $ "Writing default configuration to " ++ configFile+ commentConf <- commentSavedConfig+ initialConf <- initialSavedConfig+ writeConfigFile configFile commentConf initialConf+ return initialConf+ Just (ParseOk ws conf) -> do+ when (not $ null ws) $ warn verbosity $+ unlines (map (showPWarning configFile) ws)+ return conf+ Just (ParseFailed err) -> do+ let (line, msg) = locatedErrorMsg err+ warn verbosity $+ "Error parsing config file " ++ configFile+ ++ maybe "" (\n -> ":" ++ show n) line ++ ":\n" ++ msg+ warn verbosity $ "Using default configuration."+ initialSavedConfig++ where+ addBaseConf body = do+ base <- baseSavedConfig+ extra <- body+ return (updateInstallDirs userInstallFlag (base `mappend` extra))++readConfigFile :: SavedConfig -> FilePath -> IO (Maybe (ParseResult SavedConfig))+readConfigFile initial file = handleNotExists $+ fmap (Just . parseConfig initial) (readFile file)++ where+ handleNotExists action = catch action $ \ioe ->+ if isDoesNotExistError ioe+ then return Nothing+ else ioError ioe++writeConfigFile :: FilePath -> SavedConfig -> SavedConfig -> IO ()+writeConfigFile file comments vals = do+ createDirectoryIfMissing True (takeDirectory file)+ writeFile file $ explanation ++ showConfigWithComments comments vals ++ "\n"+ where+ explanation = unlines+ ["-- This is the configuration file for the 'cabal' command line tool."+ ,""+ ,"-- The available configuration options are listed below."+ ,"-- Some of them have default values listed."+ ,""+ ,"-- Lines (like this one) beginning with '--' are comments."+ ,"-- Be careful with spaces and indentation because they are"+ ,"-- used to indicate layout for nested sections."+ ,"",""+ ]++-- | These are the default values that get used in Cabal if a no value is+-- given. We use these here to include in comments when we write out the+-- initial config file so that the user can see what default value they are+-- overriding.+--+commentSavedConfig :: IO SavedConfig+commentSavedConfig = do+ userInstallDirs <- defaultInstallDirs defaultCompiler True True+ globalInstallDirs <- defaultInstallDirs defaultCompiler False True+ return SavedConfig {+ savedGlobalFlags = commandDefaultFlags globalCommand,+ savedInstallFlags = defaultInstallFlags,+ savedConfigureExFlags = defaultConfigExFlags,+ savedConfigureFlags = (defaultConfigFlags defaultProgramConfiguration) {+ configUserInstall = toFlag defaultUserInstall+ },+ savedUserInstallDirs = fmap toFlag userInstallDirs,+ savedGlobalInstallDirs = fmap toFlag globalInstallDirs,+ savedUploadFlags = commandDefaultFlags uploadCommand,+ savedReportFlags = commandDefaultFlags reportCommand+ }++-- | All config file fields.+--+configFieldDescriptions :: [FieldDescr SavedConfig]+configFieldDescriptions =++ toSavedConfig liftGlobalFlag+ (commandOptions globalCommand ParseArgs)+ ["version", "numeric-version", "config-file"] []++ ++ toSavedConfig liftConfigFlag+ (configureOptions ParseArgs)+ (["builddir", "configure-option"] ++ map fieldName installDirsFields)++ --FIXME: this is only here because viewAsFieldDescr gives us a parser+ -- that only recognises 'ghc' etc, the case-sensitive flag names, not+ -- what the normal case-insensitive parser gives us.+ [simpleField "compiler"+ (fromFlagOrDefault Disp.empty . fmap Text.disp) (optional Text.parse)+ configHcFlavor (\v flags -> flags { configHcFlavor = v })+ ]++ ++ toSavedConfig liftConfigExFlag+ (configureExOptions ParseArgs)+ [] []++ ++ toSavedConfig liftInstallFlag+ (installOptions ParseArgs)+ ["dry-run", "reinstall", "only"] []++ ++ toSavedConfig liftUploadFlag+ (commandOptions uploadCommand ParseArgs)+ ["verbose", "check"] []++ ++ toSavedConfig liftReportFlag+ (commandOptions reportCommand ParseArgs)+ ["verbose"] []++ where+ toSavedConfig lift options exclusions replacements =+ [ lift (fromMaybe field replacement)+ | opt <- options+ , let field = viewAsFieldDescr opt+ name = fieldName field+ replacement = find ((== name) . fieldName) replacements+ , name `notElem` exclusions ]+ optional = Parse.option mempty . fmap toFlag++-- TODO: next step, make the deprecated fields elicit a warning.+--+deprecatedFieldDescriptions :: [FieldDescr SavedConfig]+deprecatedFieldDescriptions =+ [ liftGlobalFlag $+ listField "repos"+ (Disp.text . showRepo) parseRepo+ globalRemoteRepos (\rs cfg -> cfg { globalRemoteRepos = rs })+ , liftGlobalFlag $+ simpleField "cachedir"+ (Disp.text . fromFlagOrDefault "") (optional parseFilePathQ)+ globalCacheDir (\d cfg -> cfg { globalCacheDir = d })+ , liftUploadFlag $+ simpleField "hackage-username"+ (Disp.text . fromFlagOrDefault "" . fmap unUsername)+ (optional (fmap Username parseTokenQ))+ uploadUsername (\d cfg -> cfg { uploadUsername = d })+ , liftUploadFlag $+ simpleField "hackage-password"+ (Disp.text . fromFlagOrDefault "" . fmap unPassword)+ (optional (fmap Password parseTokenQ))+ uploadPassword (\d cfg -> cfg { uploadPassword = d })+ ]+ ++ map (modifyFieldName ("user-"++) . liftUserInstallDirs) installDirsFields+ ++ map (modifyFieldName ("global-"++) . liftGlobalInstallDirs) installDirsFields+ where+ optional = Parse.option mempty . fmap toFlag+ modifyFieldName :: (String -> String) -> FieldDescr a -> FieldDescr a+ modifyFieldName f d = d { fieldName = f (fieldName d) }++liftUserInstallDirs :: FieldDescr (InstallDirs (Flag PathTemplate))+ -> FieldDescr SavedConfig+liftUserInstallDirs = liftField+ savedUserInstallDirs (\flags conf -> conf { savedUserInstallDirs = flags })++liftGlobalInstallDirs :: FieldDescr (InstallDirs (Flag PathTemplate))+ -> FieldDescr SavedConfig+liftGlobalInstallDirs = liftField+ savedGlobalInstallDirs (\flags conf -> conf { savedGlobalInstallDirs = flags })++liftGlobalFlag :: FieldDescr GlobalFlags -> FieldDescr SavedConfig+liftGlobalFlag = liftField+ savedGlobalFlags (\flags conf -> conf { savedGlobalFlags = flags })++liftConfigFlag :: FieldDescr ConfigFlags -> FieldDescr SavedConfig+liftConfigFlag = liftField+ savedConfigureFlags (\flags conf -> conf { savedConfigureFlags = flags })++liftConfigExFlag :: FieldDescr ConfigExFlags -> FieldDescr SavedConfig+liftConfigExFlag = liftField+ savedConfigureExFlags (\flags conf -> conf { savedConfigureExFlags = flags })++liftInstallFlag :: FieldDescr InstallFlags -> FieldDescr SavedConfig+liftInstallFlag = liftField+ savedInstallFlags (\flags conf -> conf { savedInstallFlags = flags })++liftUploadFlag :: FieldDescr UploadFlags -> FieldDescr SavedConfig+liftUploadFlag = liftField+ savedUploadFlags (\flags conf -> conf { savedUploadFlags = flags })++liftReportFlag :: FieldDescr ReportFlags -> FieldDescr SavedConfig+liftReportFlag = liftField+ savedReportFlags (\flags conf -> conf { savedReportFlags = flags })++parseConfig :: SavedConfig -> String -> ParseResult SavedConfig+parseConfig initial = \str -> do+ fields <- readFields str+ let (knownSections, others) = partition isKnownSection fields+ config <- parse others+ let user0 = savedUserInstallDirs config+ global0 = savedGlobalInstallDirs config+ (user, global) <- foldM parseSections (user0, global0) knownSections+ return config {+ savedUserInstallDirs = user,+ savedGlobalInstallDirs = global+ }++ where+ isKnownSection (ParseUtils.Section _ "install-dirs" _ _) = True+ isKnownSection _ = False++ parse = parseFields (configFieldDescriptions+ ++ deprecatedFieldDescriptions) initial++ parseSections accum@(u,g) (ParseUtils.Section _ "install-dirs" name fs)+ | name' == "user" = do u' <- parseFields installDirsFields u fs+ return (u', g)+ | name' == "global" = do g' <- parseFields installDirsFields g fs+ return (u, g')+ | otherwise = do+ warning "The install-paths section should be for 'user' or 'global'"+ return accum+ where name' = lowercase name+ parseSections accum f = do+ warning $ "Unrecognized stanza on line " ++ show (lineNo f)+ return accum++showConfig :: SavedConfig -> String+showConfig = showConfigWithComments mempty++showConfigWithComments :: SavedConfig -> SavedConfig -> String+showConfigWithComments comment vals = Disp.render $+ ppFields configFieldDescriptions comment vals+ $+$ Disp.text ""+ $+$ installDirsSection "user" savedUserInstallDirs+ $+$ Disp.text ""+ $+$ installDirsSection "global" savedGlobalInstallDirs+ where+ installDirsSection name field =+ ppSection "install-dirs" name installDirsFields+ (field comment) (field vals)++------------------------+-- * Parsing utils+--++--FIXME: replace this with something better in Cabal-1.5+parseFields :: [FieldDescr a] -> a -> [ParseUtils.Field] -> ParseResult a+parseFields fields initial = foldM setField initial+ where+ fieldMap = Map.fromList+ [ (name, f) | f@(FieldDescr name _ _) <- fields ]+ setField accum (ParseUtils.F line name value) = case Map.lookup name fieldMap of+ Just (FieldDescr _ _ set) -> set line value accum+ Nothing -> do+ warning $ "Unrecognized field " ++ name ++ " on line " ++ show line+ return accum+ setField accum f = do+ warning $ "Unrecognized stanza on line " ++ show (lineNo f)+ return accum++-- | This is a customised version of the function from Cabal that also prints+-- default values for empty fields as comments.+--+ppFields :: [FieldDescr a] -> a -> a -> Disp.Doc+ppFields fields def cur = Disp.vcat [ ppField name (getter def) (getter cur)+ | FieldDescr name getter _ <- fields]++ppField :: String -> Disp.Doc -> Disp.Doc -> Disp.Doc+ppField name def cur+ | Disp.isEmpty cur = Disp.text "--" <+> Disp.text name <> Disp.colon <+> def+ | otherwise = Disp.text name <> Disp.colon <+> cur++ppSection :: String -> String -> [FieldDescr a] -> a -> a -> Disp.Doc+ppSection name arg fields def cur =+ Disp.text name <+> Disp.text arg+ $$ Disp.nest 2 (ppFields fields def cur)++installDirsFields :: [FieldDescr (InstallDirs (Flag PathTemplate))]+installDirsFields = map viewAsFieldDescr installDirsOptions+
+ Distribution/Client/Configure.hs view
@@ -0,0 +1,191 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Configure+-- Copyright : (c) David Himmelstrup 2005,+-- Duncan Coutts 2005+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- High level interface to configuring a package.+-----------------------------------------------------------------------------+module Distribution.Client.Configure (+ configure,+ ) where++import Distribution.Client.Dependency+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.IndexUtils as IndexUtils+ ( getAvailablePackages, getInstalledPackages )+import Distribution.Client.Setup+ ( ConfigExFlags(..), configureCommand, filterConfigureFlags )+import Distribution.Client.Types as Available+import Distribution.Client.SetupWrapper+ ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )++import Distribution.Simple.Compiler+ ( CompilerId(..), Compiler(compilerId)+ , PackageDB(..), PackageDBStack )+import Distribution.Simple.Program (ProgramConfiguration )+import Distribution.Simple.Setup+ ( ConfigFlags(..), toFlag, flagToMaybe, fromFlagOrDefault )+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Simple.Utils+ ( defaultPackageDesc )+import Distribution.Package+ ( Package(..), packageName, Dependency(..), thisPackageVersion )+import Distribution.PackageDescription.Parse+ ( readPackageDescription )+import Distribution.PackageDescription.Configuration+ ( finalizePackageDescription )+import Distribution.Version+ ( anyVersion, thisVersion )+import Distribution.Simple.Utils as Utils+ ( notice, info, debug, die )+import Distribution.System+ ( Platform, buildPlatform )+import Distribution.Verbosity as Verbosity+ ( Verbosity )++import Data.Monoid (Monoid(..))++-- | Configure the package found in the local directory+configure :: Verbosity+ -> PackageDBStack+ -> [Repo]+ -> Compiler+ -> ProgramConfiguration+ -> ConfigFlags+ -> ConfigExFlags+ -> [String]+ -> IO ()+configure verbosity packageDBs repos comp conf+ configFlags configExFlags extraArgs = do++ installed <- getInstalledPackages verbosity comp packageDBs conf+ available <- getAvailablePackages verbosity repos++ progress <- planLocalPackage verbosity comp configFlags configExFlags+ installed available++ notice verbosity "Resolving dependencies..."+ maybePlan <- foldProgress logMsg (return . Left) (return . Right)+ progress+ case maybePlan of+ Left message -> do+ info verbosity message+ setupWrapper verbosity (setupScriptOptions installed) Nothing+ configureCommand (const configFlags) extraArgs++ Right installPlan -> case InstallPlan.ready installPlan of+ [pkg@(ConfiguredPackage (AvailablePackage _ _ (LocalUnpackedPackage _)) _ _)] ->+ configurePackage verbosity+ (InstallPlan.planPlatform installPlan)+ (InstallPlan.planCompiler installPlan)+ (setupScriptOptions installed)+ configFlags pkg extraArgs++ _ -> die $ "internal error: configure install plan should have exactly "+ ++ "one local ready package."++ where+ setupScriptOptions index = SetupScriptOptions {+ useCabalVersion = maybe anyVersion thisVersion+ (flagToMaybe (configCabalVersion configExFlags)),+ useCompiler = Just comp,+ -- Hack: we typically want to allow the UserPackageDB for finding the+ -- Cabal lib when compiling any Setup.hs even if we're doing a global+ -- install. However we also allow looking in a specific package db.+ usePackageDB = if UserPackageDB `elem` packageDBs+ then packageDBs+ else packageDBs ++ [UserPackageDB],+ usePackageIndex = if UserPackageDB `elem` packageDBs+ then Just index+ else Nothing,+ useProgramConfig = conf,+ useDistPref = fromFlagOrDefault+ (useDistPref defaultSetupScriptOptions)+ (configDistPref configFlags),+ useLoggingHandle = Nothing,+ useWorkingDir = Nothing+ }++ logMsg message rest = debug verbosity message >> rest++-- | Make an 'InstallPlan' for the unpacked package in the current directory,+-- and all its dependencies.+--+planLocalPackage :: Verbosity -> Compiler+ -> ConfigFlags -> ConfigExFlags+ -> PackageIndex InstalledPackage+ -> AvailablePackageDb+ -> IO (Progress String String InstallPlan)+planLocalPackage verbosity comp configFlags configExFlags installed+ (AvailablePackageDb _ availablePrefs) = do+ pkg <- readPackageDescription verbosity =<< defaultPackageDesc verbosity++ let -- We create a local package and ask to resolve a dependency on it+ localPkg = AvailablePackage {+ packageInfoId = packageId pkg,+ Available.packageDescription = pkg,+ packageSource = LocalUnpackedPackage "."+ }++ resolverParams =++ addPreferences+ -- preferences from the config file or command line+ [ PackageVersionPreference name ver+ | Dependency name ver <- configPreferences configExFlags ]++ . addConstraints+ -- version constraints from the config file or command line+ [ PackageVersionConstraint name ver+ | Dependency name ver <- configConstraints configFlags ]++ . addConstraints+ -- package flags from the config file or command line+ [ PackageFlagsConstraint (packageName pkg)+ (configConfigurationsFlags configFlags) ]++ $ standardInstallPolicy+ installed+ (AvailablePackageDb mempty availablePrefs)+ [SpecificSourcePackage localPkg]++ return (resolveDependencies buildPlatform (compilerId comp) resolverParams)+++-- | Call an installer for an 'AvailablePackage' but override the configure+-- flags with the ones given by the 'ConfiguredPackage'. In particular the+-- 'ConfiguredPackage' specifies an exact 'FlagAssignment' and exactly+-- versioned package dependencies. So we ignore any previous partial flag+-- assignment or dependency constraints and use the new ones.+--+configurePackage :: Verbosity+ -> Platform -> CompilerId+ -> SetupScriptOptions+ -> ConfigFlags+ -> ConfiguredPackage+ -> [String]+ -> IO ()+configurePackage verbosity platform comp scriptOptions configFlags+ (ConfiguredPackage (AvailablePackage _ gpkg _) flags deps) extraArgs =++ setupWrapper verbosity+ scriptOptions (Just pkg) configureCommand configureFlags extraArgs++ where+ configureFlags = filterConfigureFlags configFlags {+ configConfigurationsFlags = flags,+ configConstraints = map thisPackageVersion deps,+ configVerbosity = toFlag verbosity+ }++ pkg = case finalizePackageDescription flags+ (const True)+ platform comp [] gpkg of+ Left _ -> error "finalizePackageDescription ConfiguredPackage failed"+ Right (desc, _) -> desc
+ Distribution/Client/Dependency.hs view
@@ -0,0 +1,441 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Dependency+-- Copyright : (c) David Himmelstrup 2005,+-- Bjorn Bringert 2007+-- Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Top level interface to dependency resolution.+-----------------------------------------------------------------------------+module Distribution.Client.Dependency (+ -- * The main package dependency resolver+ resolveDependencies,+ Progress(..),+ foldProgress,++ -- * Alternate, simple resolver that does not do dependencies recursively+ resolveWithoutDependencies,++ -- * Constructing resolver policies+ DepResolverParams(..),+ PackageConstraint(..),+ PackagesPreferenceDefault(..),+ PackagePreference(..),+ InstalledPreference(..),++ -- ** Standard policy+ standardInstallPolicy,+ PackageSpecifier(..),++ -- ** Extra policy options+ dontUpgradeBasePackage,+ hideBrokenInstalledPackages,+ upgradeDependencies,+ reinstallTargets,++ -- ** Policy utils+ addConstraints,+ addPreferences,+ setPreferenceDefault,+ addAvailablePackages,+ hideInstalledPackagesSpecific,+ hideInstalledPackagesAllVersions,+ ) where++import Distribution.Client.Dependency.TopDown (topDownResolver)+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.Types+ ( AvailablePackageDb(AvailablePackageDb)+ , AvailablePackage(..), InstalledPackage )+import Distribution.Client.Dependency.Types+ ( DependencyResolver, PackageConstraint(..)+ , PackagePreferences(..), InstalledPreference(..)+ , Progress(..), foldProgress )+import Distribution.Client.Targets+import Distribution.Package+ ( PackageName(..), PackageId, Package(..), packageVersion+ , Dependency(Dependency))+import Distribution.Version+ ( VersionRange, anyVersion, withinRange, simplifyVersionRange )+import Distribution.Compiler+ ( CompilerId(..) )+import Distribution.System+ ( Platform )+import Distribution.Simple.Utils (comparing)+import Distribution.Text+ ( display )++import Data.List (maximumBy, foldl')+import Data.Maybe (fromMaybe, isJust)+import qualified Data.Map as Map+import qualified Data.Set as Set+import Data.Set (Set)+++-- ------------------------------------------------------------+-- * High level planner policy+-- ------------------------------------------------------------++-- | The set of parameters to the dependency resolver. These parameters are+-- relatively low level but many kinds of high level policies can be+-- implemented in terms of adjustments to the parameters.+--+data DepResolverParams = DepResolverParams {+ depResolverTargets :: [PackageName],+ depResolverConstraints :: [PackageConstraint],+ depResolverPreferences :: [PackagePreference],+ depResolverPreferenceDefault :: PackagesPreferenceDefault,+ depResolverInstalled :: PackageIndex InstalledPackage,+ depResolverAvailable :: PackageIndex AvailablePackage+ }+++-- | Global policy for all packages to say if we prefer package versions that+-- are already installed locally or if we just prefer the latest available.+--+data PackagesPreferenceDefault =++ -- | Always prefer the latest version irrespective of any existing+ -- installed version.+ --+ -- * This is the standard policy for upgrade.+ --+ PreferAllLatest++ -- | Always prefer the installed versions over ones that would need to be+ -- installed. Secondarily, prefer latest versions (eg the latest installed+ -- version or if there are none then the latest available version).+ | PreferAllInstalled++ -- | Prefer the latest version for packages that are explicitly requested+ -- but prefers the installed version for any other packages.+ --+ -- * This is the standard policy for install.+ --+ | PreferLatestForSelected+++-- | A package selection preference for a particular package.+--+-- Preferences are soft constraints that the dependency resolver should try to+-- respect where possible. It is not specified if preferences on some packages+-- are more important than others.+--+data PackagePreference =++ -- | A suggested constraint on the version number.+ PackageVersionPreference PackageName VersionRange++ -- | If we prefer versions of packages that are already installed.+ | PackageInstalledPreference PackageName InstalledPreference++basicDepResolverParams :: PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> DepResolverParams+basicDepResolverParams installed available =+ DepResolverParams {+ depResolverTargets = [],+ depResolverConstraints = [],+ depResolverPreferences = [],+ depResolverPreferenceDefault = PreferLatestForSelected,+ depResolverInstalled = installed,+ depResolverAvailable = available+ }++addTargets :: [PackageName]+ -> DepResolverParams -> DepResolverParams+addTargets extraTargets params =+ params {+ depResolverTargets = extraTargets ++ depResolverTargets params+ }++addConstraints :: [PackageConstraint]+ -> DepResolverParams -> DepResolverParams+addConstraints extraConstraints params =+ params {+ depResolverConstraints = extraConstraints+ ++ depResolverConstraints params+ }++addPreferences :: [PackagePreference]+ -> DepResolverParams -> DepResolverParams+addPreferences extraPreferences params =+ params {+ depResolverPreferences = extraPreferences+ ++ depResolverPreferences params+ }++setPreferenceDefault :: PackagesPreferenceDefault+ -> DepResolverParams -> DepResolverParams+setPreferenceDefault preferenceDefault params =+ params {+ depResolverPreferenceDefault = preferenceDefault+ }++dontUpgradeBasePackage :: DepResolverParams -> DepResolverParams+dontUpgradeBasePackage params =+ addConstraints extraConstraints params+ where+ extraConstraints =+ [ PackageInstalledConstraint pkgname+ | all (/=PackageName "base") (depResolverTargets params)+ , pkgname <- [ PackageName "base", PackageName "ghc-prim" ]+ , isInstalled pkgname ]+ -- TODO: the top down resolver chokes on the base constraints+ -- below when there are no targets and thus no dep on base.+ -- Need to refactor contraints separate from needing packages.+ isInstalled = not . null+ . PackageIndex.lookupPackageName (depResolverInstalled params)++addAvailablePackages :: [AvailablePackage]+ -> DepResolverParams -> DepResolverParams+addAvailablePackages pkgs params =+ params {+ depResolverAvailable = foldl (flip PackageIndex.insert)+ (depResolverAvailable params) pkgs+ }++hideInstalledPackagesSpecific :: [PackageId]+ -> DepResolverParams -> DepResolverParams+hideInstalledPackagesSpecific pkgids params =+ --TODO: this should work using exclude constraints instead+ params {+ depResolverInstalled = foldl' (flip PackageIndex.deletePackageId)+ (depResolverInstalled params) pkgids+ }++hideInstalledPackagesAllVersions :: [PackageName]+ -> DepResolverParams -> DepResolverParams+hideInstalledPackagesAllVersions pkgnames params =+ --TODO: this should work using exclude constraints instead+ params {+ depResolverInstalled =+ foldl' (flip PackageIndex.deletePackageName)+ (depResolverInstalled params) pkgnames+ }+++hideBrokenInstalledPackages :: DepResolverParams -> DepResolverParams+hideBrokenInstalledPackages params =+ hideInstalledPackagesSpecific pkgids params+ where+ pkgids = map packageId+ . PackageIndex.reverseDependencyClosure (depResolverInstalled params)+ . map (packageId . fst)+ . PackageIndex.brokenPackages+ $ depResolverInstalled params+++upgradeDependencies :: DepResolverParams -> DepResolverParams+upgradeDependencies = setPreferenceDefault PreferAllLatest+++reinstallTargets :: DepResolverParams -> DepResolverParams+reinstallTargets params =+ hideInstalledPackagesAllVersions (depResolverTargets params) params+++standardInstallPolicy :: PackageIndex InstalledPackage+ -> AvailablePackageDb+ -> [PackageSpecifier AvailablePackage]+ -> DepResolverParams+standardInstallPolicy+ installed (AvailablePackageDb available availablePrefs) pkgSpecifiers++ = addPreferences+ [ PackageVersionPreference name ver+ | (name, ver) <- Map.toList availablePrefs ]++ . addConstraints+ (concatMap pkgSpecifierConstraints pkgSpecifiers)++ . addTargets+ (map pkgSpecifierTarget pkgSpecifiers)++ . hideInstalledPackagesSpecific+ [ packageId pkg | SpecificSourcePackage pkg <- pkgSpecifiers ]++ . addAvailablePackages+ [ pkg | SpecificSourcePackage pkg <- pkgSpecifiers ]++ $ basicDepResolverParams+ installed available+++-- ------------------------------------------------------------+-- * Interface to the standard resolver+-- ------------------------------------------------------------++defaultResolver :: DependencyResolver+defaultResolver = topDownResolver++-- | Run the dependency solver.+--+-- Since this is potentially an expensive operation, the result is wrapped in a+-- a 'Progress' structure that can be unfolded to provide progress information,+-- logging messages and the final result or an error.+--+resolveDependencies :: Platform+ -> CompilerId+ -> DepResolverParams+ -> Progress String String InstallPlan++ --TODO: is this needed here? see dontUpgradeBasePackage+resolveDependencies platform comp params+ | null (depResolverTargets params)+ = return (mkInstallPlan platform comp [])++resolveDependencies platform comp params =++ fmap (mkInstallPlan platform comp)+ $ defaultResolver platform comp installed available+ preferences constraints targets+ where+ DepResolverParams+ targets constraints+ prefs defpref+ installed available = dontUpgradeBasePackage+ . hideBrokenInstalledPackages+ $ params++ preferences = interpretPackagesPreference+ (Set.fromList targets) defpref prefs+++-- | Make an install plan from the output of the dep resolver.+-- It checks that the plan is valid, or it's an error in the dep resolver.+--+mkInstallPlan :: Platform+ -> CompilerId+ -> [InstallPlan.PlanPackage] -> InstallPlan+mkInstallPlan platform comp pkgs =+ case InstallPlan.new platform comp (PackageIndex.fromList pkgs) of+ Right plan -> plan+ Left problems -> error $ unlines $+ "internal error: could not construct a valid install plan."+ : "The proposed (invalid) plan contained the following problems:"+ : map InstallPlan.showPlanProblem problems+++-- | Give an interpretation to the global 'PackagesPreference' as+-- specific per-package 'PackageVersionPreference'.+--+interpretPackagesPreference :: Set PackageName+ -> PackagesPreferenceDefault+ -> [PackagePreference]+ -> (PackageName -> PackagePreferences)+interpretPackagesPreference selected defaultPref prefs =+ \pkgname -> PackagePreferences (versionPref pkgname) (installPref pkgname)++ where+ versionPref pkgname =+ fromMaybe anyVersion (Map.lookup pkgname versionPrefs)+ versionPrefs = Map.fromList+ [ (pkgname, pref)+ | PackageVersionPreference pkgname pref <- prefs ]++ installPref pkgname =+ fromMaybe (installPrefDefault pkgname) (Map.lookup pkgname installPrefs)+ installPrefs = Map.fromList+ [ (pkgname, pref)+ | PackageInstalledPreference pkgname pref <- prefs ]+ installPrefDefault = case defaultPref of+ PreferAllLatest -> \_ -> PreferLatest+ PreferAllInstalled -> \_ -> PreferInstalled+ PreferLatestForSelected -> \pkgname ->+ -- When you say cabal install foo, what you really mean is, prefer the+ -- latest version of foo, but the installed version of everything else+ if pkgname `Set.member` selected then PreferLatest+ else PreferInstalled++-- ------------------------------------------------------------+-- * Simple resolver that ignores dependencies+-- ------------------------------------------------------------++-- | A simplistic method of resolving a list of target package names to+-- available packages.+--+-- Specifically, it does not consider package dependencies at all. Unlike+-- 'resolveDependencies', no attempt is made to ensure that the selected+-- packages have dependencies that are satisfiable or consistent with+-- each other.+--+-- It is suitable for tasks such as selecting packages to download for user+-- inspection. It is not suitable for selecting packages to install.+--+-- Note: if no installed package index is available, it is ok to pass 'mempty'.+-- It simply means preferences for installed packages will be ignored.+--+resolveWithoutDependencies :: DepResolverParams+ -> Either [ResolveNoDepsError] [AvailablePackage]+resolveWithoutDependencies (DepResolverParams targets constraints+ prefs defpref installed available) =+ collectEithers (map selectPackage targets)+ where+ selectPackage :: PackageName -> Either ResolveNoDepsError AvailablePackage+ selectPackage pkgname+ | null choices = Left $! ResolveUnsatisfiable pkgname requiredVersions+ | otherwise = Right $! maximumBy bestByPrefs choices++ where+ -- Constraints+ requiredVersions = packageConstraints pkgname+ pkgDependency = Dependency pkgname requiredVersions+ choices = PackageIndex.lookupDependency available pkgDependency++ -- Preferences+ PackagePreferences preferredVersions preferInstalled+ = packagePreferences pkgname++ bestByPrefs = comparing $ \pkg ->+ (installPref pkg, versionPref pkg, packageVersion pkg)+ installPref = case preferInstalled of+ PreferLatest -> const False+ PreferInstalled -> isJust . PackageIndex.lookupPackageId installed+ . packageId+ versionPref pkg = packageVersion pkg `withinRange` preferredVersions++ packageConstraints :: PackageName -> VersionRange+ packageConstraints pkgname =+ Map.findWithDefault anyVersion pkgname packageVersionConstraintMap+ packageVersionConstraintMap =+ Map.fromList [ (name, range)+ | PackageVersionConstraint name range <- constraints ]++ packagePreferences :: PackageName -> PackagePreferences+ packagePreferences = interpretPackagesPreference+ (Set.fromList targets) defpref prefs+++collectEithers :: [Either a b] -> Either [a] [b]+collectEithers = collect . partitionEithers+ where+ collect ([], xs) = Right xs+ collect (errs,_) = Left errs+ partitionEithers :: [Either a b] -> ([a],[b])+ partitionEithers = foldr (either left right) ([],[])+ where+ left a (l, r) = (a:l, r)+ right a (l, r) = (l, a:r)++-- | Errors for 'resolveWithoutDependencies'.+--+data ResolveNoDepsError =++ -- | A package name which cannot be resolved to a specific package.+ -- Also gives the constraint on the version and whether there was+ -- a constraint on the package being installed.+ ResolveUnsatisfiable PackageName VersionRange++instance Show ResolveNoDepsError where+ show (ResolveUnsatisfiable name ver) =+ "There is no available version of " ++ display name+ ++ " that satisfies " ++ display (simplifyVersionRange ver)
+ Distribution/Client/Dependency/Modular.hs view
@@ -0,0 +1,58 @@+module Distribution.Client.Dependency.Modular+ ( modularResolver, SolverConfig(..)) where++-- Here, we try to map between the external cabal-install solver+-- interface and the internal interface that the solver actually+-- expects. There are a number of type conversions to perform: we+-- have to convert the package indices to the uniform index used+-- by the solver; we also have to convert the initial constraints;+-- and finally, we have to convert back the resulting install+-- plan.++import Data.Map as M+ ( fromListWith )+import Distribution.Client.Dependency.Modular.Assignment+ ( Assignment, toCPs )+import Distribution.Client.Dependency.Modular.Dependency+ ( RevDepMap )+import Distribution.Client.Dependency.Modular.ConfiguredConversion+ ( convCP )+import Distribution.Client.Dependency.Modular.IndexConversion+ ( convPIs )+import Distribution.Client.Dependency.Modular.Log+ ( logToProgress )+import Distribution.Client.Dependency.Modular.Package+ ( PN )+import Distribution.Client.Dependency.Modular.Solver+ ( SolverConfig(..), solve )+import Distribution.Client.Dependency.Types+ ( DependencyResolver, PackageConstraint(..) )+import Distribution.Client.InstallPlan+ ( PlanPackage )+import Distribution.System+ ( Platform(..) )++-- | Ties the two worlds together: classic cabal-install vs. the modular+-- solver. Performs the necessary translations before and after.+modularResolver :: SolverConfig -> DependencyResolver+modularResolver sc (Platform arch os) cid iidx sidx pprefs pcs pns =+ fmap (uncurry postprocess) $ -- convert install plan+ logToProgress (maxBackjumps sc) $ -- convert log format into progress format+ solve sc idx pprefs gcs pns+ where+ -- Indices have to be converted into solver-specific uniform index.+ idx = convPIs os arch cid (shadowPkgs sc) iidx sidx+ -- Constraints have to be converted into a finite map indexed by PN.+ gcs = M.fromListWith (++) (map (\ pc -> (pcName pc, [pc])) pcs)++ -- Results have to be converted into an install plan.+ postprocess :: Assignment -> RevDepMap -> [PlanPackage]+ postprocess a rdm = map (convCP iidx sidx) (toCPs a rdm)++ -- Helper function to extract the PN from a constraint.+ pcName :: PackageConstraint -> PN+ pcName (PackageConstraintVersion pn _) = pn+ pcName (PackageConstraintInstalled pn ) = pn+ pcName (PackageConstraintSource pn ) = pn+ pcName (PackageConstraintFlags pn _) = pn+ pcName (PackageConstraintStanzas pn _) = pn
+ Distribution/Client/Dependency/Modular/Assignment.hs view
@@ -0,0 +1,146 @@+module Distribution.Client.Dependency.Modular.Assignment where++import Control.Applicative+import Control.Monad+import Data.Array as A+import Data.List as L+import Data.Map as M+import Data.Maybe+import Data.Graph+import Prelude hiding (pi)++import Distribution.PackageDescription (FlagAssignment) -- from Cabal+import Distribution.Client.Types (OptionalStanza)++import Distribution.Client.Dependency.Modular.Configured+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Version++-- | A (partial) package assignment. Qualified package names+-- are associated with instances.+type PAssignment = Map QPN I++-- | A (partial) package preassignment. Qualified package names+-- are associated with constrained instances. Constrained instances+-- record constraints about the instances that can still be chosen,+-- and in the extreme case fix a concrete instance.+type PPreAssignment = Map QPN (CI QPN)+type FAssignment = Map QFN Bool+type SAssignment = Map QSN Bool++-- | A (partial) assignment of variables.+data Assignment = A PAssignment FAssignment SAssignment+ deriving (Show, Eq)++-- | A preassignment comprises knowledge about variables, but not+-- necessarily fixed values.+data PreAssignment = PA PPreAssignment FAssignment SAssignment++-- | Extend a package preassignment.+--+-- Either returns a witness of the conflict that would arise during the merge,+-- or the successfully extended assignment.+extend :: Var QPN -> PPreAssignment -> [Dep QPN] -> Either (ConflictSet QPN, [Dep QPN]) PPreAssignment+extend var pa qa = foldM (\ a (Dep qpn ci) ->+ let ci' = M.findWithDefault (Constrained []) qpn a+ in case (\ x -> M.insert qpn x a) <$> merge ci' ci of+ Left (c, (d, d')) -> Left (c, L.map (Dep qpn) (simplify (P qpn) d d'))+ Right x -> Right x)+ pa qa+ where+ -- We're trying to remove trivial elements of the conflict. If we're just+ -- making a choice pkg == instance, and pkg => pkg == instance is a part+ -- of the conflict, then this info is clear from the context and does not+ -- have to be repeated.+ simplify v (Fixed _ (Goal var' _)) c | v == var && var' == var = [c]+ simplify v c (Fixed _ (Goal var' _)) | v == var && var' == var = [c]+ simplify _ c d = [c, d]++-- | Delivers an ordered list of fully configured packages.+--+-- TODO: This function is (sort of) ok. However, there's an open bug+-- w.r.t. unqualification. There might be several different instances+-- of one package version chosen by the solver, which will lead to+-- clashes.+toCPs :: Assignment -> RevDepMap -> [CP QPN]+toCPs (A pa fa sa) rdm =+ let+ -- get hold of the graph+ g :: Graph+ vm :: Vertex -> ((), QPN, [QPN])+ cvm :: QPN -> Maybe Vertex+ -- Note that the RevDepMap contains duplicate dependencies. Therefore the nub.+ (g, vm, cvm) = graphFromEdges (L.map (\ (x, xs) -> ((), x, nub xs))+ (M.toList rdm))+ tg :: Graph+ tg = transposeG g+ -- Topsort the dependency graph, yielding a list of pkgs in the right order.+ -- The graph will still contain all the installed packages, and it might+ -- contain duplicates, because several variables might actually resolve to+ -- the same package in the presence of qualified package names.+ ps :: [PI QPN]+ ps = L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) $+ topSort g+ -- Determine the flags per package, by walking over and regrouping the+ -- complete flag assignment by package.+ fapp :: Map QPN FlagAssignment+ fapp = M.fromListWith (++) $+ L.map (\ ((FN (PI qpn _) fn), b) -> (qpn, [(fn, b)])) $+ M.toList $+ fa+ -- Stanzas per package.+ sapp :: Map QPN [OptionalStanza]+ sapp = M.fromListWith (++) $+ L.map (\ ((SN (PI qpn _) sn), b) -> (qpn, if b then [sn] else [])) $+ M.toList $+ sa+ -- Dependencies per package.+ depp :: QPN -> [PI QPN]+ depp qpn = let v :: Vertex+ v = fromJust (cvm qpn)+ dvs :: [Vertex]+ dvs = tg A.! v+ in L.map (\ dv -> case vm dv of (_, x, _) -> PI x (pa M.! x)) dvs+ in+ L.map (\ pi@(PI qpn _) -> CP pi+ (M.findWithDefault [] qpn fapp)+ (M.findWithDefault [] qpn sapp)+ (depp qpn))+ ps++-- | Finalize an assignment and a reverse dependency map.+--+-- This is preliminary, and geared towards output right now.+finalize :: Index -> Assignment -> RevDepMap -> IO ()+finalize idx (A pa fa _) rdm =+ let+ -- get hold of the graph+ g :: Graph+ vm :: Vertex -> ((), QPN, [QPN])+ (g, vm) = graphFromEdges' (L.map (\ (x, xs) -> ((), x, xs)) (M.toList rdm))+ -- topsort the dependency graph, yielding a list of pkgs in the right order+ f :: [PI QPN]+ f = L.filter (not . instPI) (L.map ((\ (_, x, _) -> PI x (pa M.! x)) . vm) (topSort g))+ fapp :: Map QPN [(QFN, Bool)] -- flags per package+ fapp = M.fromListWith (++) $+ L.map (\ (qfn@(FN (PI qpn _) _), b) -> (qpn, [(qfn, b)])) $ M.toList $ fa+ -- print one instance+ ppi pi@(PI qpn _) = showPI pi ++ status pi ++ " " ++ pflags (M.findWithDefault [] qpn fapp)+ -- print install status+ status :: PI QPN -> String+ status (PI (Q _ pn) _) =+ case insts of+ [] -> " (new)"+ vs -> " (" ++ intercalate ", " (L.map showVer vs) ++ ")"+ where insts = L.map (\ (I v _) -> v) $ L.filter isInstalled $+ M.keys (M.findWithDefault M.empty pn idx)+ isInstalled (I _ (Inst _ )) = True+ isInstalled _ = False+ -- print flag assignment+ pflags = unwords . L.map (uncurry showFBool)+ in+ -- show packages with associated flag assignments+ putStr (unlines (L.map ppi f))
+ Distribution/Client/Dependency/Modular/Builder.hs view
@@ -0,0 +1,143 @@+module Distribution.Client.Dependency.Modular.Builder where++-- Building the search tree.+--+-- In this phase, we build a search tree that is too large, i.e, it contains+-- invalid solutions. We keep track of the open goals at each point. We+-- nondeterministically pick an open goal (via a goal choice node), create+-- subtrees according to the index and the available solutions, and extend the+-- set of open goals by superficially looking at the dependencies recorded in+-- the index.+--+-- For each goal, we keep track of all the *reasons* why it is being+-- introduced. These are for debugging and error messages, mainly. A little bit+-- of care has to be taken due to the way we treat flags. If a package has+-- flag-guarded dependencies, we cannot introduce them immediately. Instead, we+-- store the entire dependency.++import Control.Monad.Reader hiding (sequence, mapM)+import Data.List as L+import Data.Map as M+import Prelude hiding (sequence, mapM)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- | The state needed during the build phase of the search tree.+data BuildState = BS {+ index :: Index, -- ^ information about packages and their dependencies+ scope :: Scope, -- ^ information about encapsulations+ rdeps :: RevDepMap, -- ^ set of all package goals, completed and open, with reverse dependencies+ open :: PSQ OpenGoal (), -- ^ set of still open goals (flag and package goals)+ next :: BuildType -- ^ kind of node to generate next+}++-- | Extend the set of open goals with the new goals listed.+--+-- We also adjust the map of overall goals, and keep track of the+-- reverse dependencies of each of the goals.+extendOpen :: QPN -> [OpenGoal] -> BuildState -> BuildState+extendOpen qpn' gs s@(BS { rdeps = gs', open = o' }) = go gs' o' gs+ where+ go g o [] = s { rdeps = g, open = o }+ go g o (ng@(OpenGoal (Flagged _ _ _ _) _gr) : ngs) = go g (cons ng () o) ngs+ go g o (ng@(OpenGoal (Stanza _ _ ) _gr) : ngs) = go g (cons ng () o) ngs+ go g o (ng@(OpenGoal (Simple (Dep qpn _)) _gr) : ngs)+ | qpn == qpn' = go g o ngs+ -- we ignore self-dependencies at this point; TODO: more care may be needed+ | qpn `M.member` g = go (M.adjust (qpn':) qpn g) o ngs+ | otherwise = go (M.insert qpn [qpn'] g) (cons ng () o) ngs+ -- code above is correct; insert/adjust have different arg order++-- | Update the current scope by taking into account the encapsulations that+-- are defined for the current package.+establishScope :: QPN -> Encaps -> BuildState -> BuildState+establishScope (Q pp pn) ecs s =+ s { scope = L.foldl (\ m e -> M.insert e pp' m) (scope s) ecs }+ where+ pp' = pn : pp -- new path++-- | Given the current scope, qualify all the package names in the given set of+-- dependencies and then extend the set of open goals accordingly.+scopedExtendOpen :: QPN -> I -> QGoalReasons -> FlaggedDeps PN -> FlagDefaults ->+ BuildState -> BuildState+scopedExtendOpen qpn i gr fdeps fdefs s = extendOpen qpn gs s+ where+ sc = scope s+ qfdeps = L.map (fmap (qualify sc)) fdeps -- qualify all the package names+ qfdefs = L.map (\ (fn, b) -> Flagged (FN (PI qpn i) fn) b [] []) $ M.toList fdefs+ gs = L.map (flip OpenGoal gr) (qfdeps ++ qfdefs)++data BuildType = Goals | OneGoal OpenGoal | Instance QPN I PInfo QGoalReasons++build :: BuildState -> Tree (QGoalReasons, Scope)+build = ana go+ where+ go :: BuildState -> TreeF (QGoalReasons, Scope) BuildState++ -- If we have a choice between many goals, we just record the choice in+ -- the tree. We select each open goal in turn, and before we descend, remove+ -- it from the queue of open goals.+ go bs@(BS { rdeps = rds, open = gs, next = Goals })+ | P.null gs = DoneF rds+ | otherwise = GoalChoiceF (P.mapWithKey (\ g (_sc, gs') -> bs { next = OneGoal g, open = gs' })+ (P.splits gs))++ -- If we have already picked a goal, then the choice depends on the kind+ -- of goal.+ --+ -- For a package, we look up the instances available in the global info,+ -- and then handle each instance in turn.+ go bs@(BS { index = idx, scope = sc, next = OneGoal (OpenGoal (Simple (Dep qpn@(Q _ pn) _)) gr) }) =+ case M.lookup pn idx of+ Nothing -> FailF (toConflictSet (Goal (P qpn) gr)) (BuildFailureNotInIndex pn)+ Just pis -> PChoiceF qpn (gr, sc) (P.fromList (L.map (\ (i, info) ->+ (i, bs { next = Instance qpn i info gr }))+ (M.toList pis)))+ -- TODO: data structure conversion is rather ugly here++ -- For a flag, we create only two subtrees, and we create them in the order+ -- that is indicated by the flag default.+ --+ -- TODO: Should we include the flag default in the tree?+ go bs@(BS { scope = sc, next = OneGoal (OpenGoal (Flagged qfn@(FN (PI qpn _) _) b t f) gr) }) =+ FChoiceF qfn (gr, sc) trivial (P.fromList (reorder b+ [(True, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn True : gr)) t) bs) { next = Goals }),+ (False, (extendOpen qpn (L.map (flip OpenGoal (FDependency qfn False : gr)) f) bs) { next = Goals })]))+ where+ reorder True = id+ reorder False = reverse+ trivial = L.null t && L.null f++ go bs@(BS { scope = sc, next = OneGoal (OpenGoal (Stanza qsn@(SN (PI qpn _) _) t) gr) }) =+ SChoiceF qsn (gr, sc) trivial (P.fromList+ [(False, bs { next = Goals }),+ (True, (extendOpen qpn (L.map (flip OpenGoal (SDependency qsn : gr)) t) bs) { next = Goals })])+ where+ trivial = L.null t++ -- For a particular instance, we change the state: we update the scope,+ -- and furthermore we update the set of goals.+ --+ -- TODO: We could inline this above.+ go bs@(BS { next = Instance qpn i (PInfo fdeps fdefs ecs _) gr }) =+ go ((establishScope qpn ecs+ (scopedExtendOpen qpn i (PDependency (PI qpn i) : gr) fdeps fdefs bs))+ { next = Goals })++-- | Interface to the tree builder. Just takes an index and a list of package names,+-- and computes the initial state and then the tree from there.+buildTree :: Index -> Bool -> [PN] -> Tree (QGoalReasons, Scope)+buildTree idx ind igs =+ build (BS idx sc+ (M.fromList (L.map (\ qpn -> (qpn, [])) qpns))+ (P.fromList (L.map (\ qpn -> (OpenGoal (Simple (Dep qpn (Constrained []))) [UserGoal], ())) qpns))+ Goals)+ where+ sc | ind = makeIndependent igs+ | otherwise = emptyScope+ qpns = L.map (qualify sc) igs
+ Distribution/Client/Dependency/Modular/Configured.hs view
@@ -0,0 +1,10 @@+module Distribution.Client.Dependency.Modular.Configured where++import Distribution.PackageDescription (FlagAssignment) -- from Cabal+import Distribution.Client.Types (OptionalStanza)++import Distribution.Client.Dependency.Modular.Package++-- | A configured package is a package instance together with+-- a flag assignment and complete dependencies.+data CP qpn = CP (PI qpn) FlagAssignment [OptionalStanza] [PI qpn]
+ Distribution/Client/Dependency/Modular/ConfiguredConversion.hs view
@@ -0,0 +1,40 @@+module Distribution.Client.Dependency.Modular.ConfiguredConversion where++import Data.Maybe+import Prelude hiding (pi)++import Distribution.Client.InstallPlan+import Distribution.Client.Types+import Distribution.Compiler+import qualified Distribution.Client.PackageIndex as CI+import qualified Distribution.Simple.PackageIndex as SI+import Distribution.System++import Distribution.Client.Dependency.Modular.Configured+import Distribution.Client.Dependency.Modular.Package++mkPlan :: Platform -> CompilerId ->+ SI.PackageIndex -> CI.PackageIndex SourcePackage ->+ [CP QPN] -> Either [PlanProblem] InstallPlan+mkPlan plat comp iidx sidx cps =+ new plat comp (CI.fromList (map (convCP iidx sidx) cps))++convCP :: SI.PackageIndex -> CI.PackageIndex SourcePackage ->+ CP QPN -> PlanPackage+convCP iidx sidx (CP qpi fa es ds) =+ case convPI qpi of+ Left pi -> PreExisting $ InstalledPackage+ (fromJust $ SI.lookupInstalledPackageId iidx pi)+ (map convPI' ds)+ Right pi -> Configured $ ConfiguredPackage+ (fromJust $ CI.lookupPackageId sidx pi)+ fa+ es+ (map convPI' ds)++convPI :: PI QPN -> Either InstalledPackageId PackageId+convPI (PI _ (I _ (Inst pi))) = Left pi+convPI qpi = Right $ convPI' qpi++convPI' :: PI QPN -> PackageId+convPI' (PI (Q _ pn) (I v _)) = PackageIdentifier pn v
+ Distribution/Client/Dependency/Modular/Dependency.hs view
@@ -0,0 +1,194 @@+module Distribution.Client.Dependency.Modular.Dependency where++import Prelude hiding (pi)++import Data.List as L+import Data.Map as M+import Data.Set as S++import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Version++-- | The type of variables that play a role in the solver.+-- Note that the tree currently does not use this type directly,+-- and rather has separate tree nodes for the different types of+-- variables. This fits better with the fact that in most cases,+-- these have to be treated differently.+--+-- TODO: This isn't the ideal location to declare the type,+-- but we need them for constrained instances.+data Var qpn = P qpn | F (FN qpn) | S (SN qpn)+ deriving (Eq, Ord, Show)++showVar :: Var QPN -> String+showVar (P qpn) = showQPN qpn+showVar (F qfn) = showQFN qfn+showVar (S qsn) = showQSN qsn++instance Functor Var where+ fmap f (P n) = P (f n)+ fmap f (F fn) = F (fmap f fn)+ fmap f (S sn) = S (fmap f sn)++type ConflictSet qpn = Set (Var qpn)++showCS :: ConflictSet QPN -> String+showCS = intercalate ", " . L.map showVar . S.toList++-- | Constrained instance. If the choice has already been made, this is+-- a fixed instance, and we record the package name for which the choice+-- is for convenience. Otherwise, it is a list of version ranges paired with+-- the goals / variables that introduced them.+data CI qpn = Fixed I (Goal qpn) | Constrained [VROrigin qpn]+ deriving (Eq, Show)++instance Functor CI where+ fmap f (Fixed i g) = Fixed i (fmap f g)+ fmap f (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, fmap f y)) vrs)++instance ResetGoal CI where+ resetGoal g (Fixed i _) = Fixed i g+ resetGoal g (Constrained vrs) = Constrained (L.map (\ (x, y) -> (x, resetGoal g y)) vrs)++type VROrigin qpn = (VR, Goal qpn)++-- | Helper function to collapse a list of version ranges with origins into+-- a single, simplified, version range.+collapse :: [VROrigin qpn] -> VR+collapse = simplifyVR . L.foldr (.&&.) anyVR . L.map fst++showCI :: CI QPN -> String+showCI (Fixed i _) = "==" ++ showI i+showCI (Constrained vr) = showVR (collapse vr)++-- | Merge constrained instances. We currently adopt a lazy strategy for+-- merging, i.e., we only perform actual checking if one of the two choices+-- is fixed. If the merge fails, we return a conflict set indicating the+-- variables responsible for the failure, as well as the two conflicting+-- fragments.+--+-- Note that while there may be more than one conflicting pair of version+-- ranges, we only return the first we find.+--+-- TODO: Different pairs might have different conflict sets. We're+-- obviously interested to return a conflict that has a "better" conflict+-- set in the sense the it contains variables that allow us to backjump+-- further. We might apply some heuristics here, such as to change the+-- order in which we check the constraints.+merge :: Ord qpn => CI qpn -> CI qpn -> Either (ConflictSet qpn, (CI qpn, CI qpn)) (CI qpn)+merge c@(Fixed i g1) d@(Fixed j g2)+ | i == j = Right c+ | otherwise = Left (S.union (toConflictSet g1) (toConflictSet g2), (c, d))+merge c@(Fixed (I v _) g1) (Constrained rs) = go rs -- I tried "reverse rs" here, but it seems to slow things down ...+ where+ go [] = Right c+ go (d@(vr, g2) : vrs)+ | checkVR vr v = go vrs+ | otherwise = Left (S.union (toConflictSet g1) (toConflictSet g2), (c, Constrained [d]))+merge c@(Constrained _) d@(Fixed _ _) = merge d c+merge (Constrained rs) (Constrained ss) = Right (Constrained (rs ++ ss))+++type FlaggedDeps qpn = [FlaggedDep qpn]++-- | Flagged dependencies can either be plain dependency constraints,+-- or flag-dependent dependency trees.+data FlaggedDep qpn =+ Flagged (FN qpn) FDefault (TrueFlaggedDeps qpn) (FalseFlaggedDeps qpn)+ | Stanza (SN qpn) (TrueFlaggedDeps qpn)+ | Simple (Dep qpn)+ deriving (Eq, Show)++instance Functor FlaggedDep where+ fmap f (Flagged x y tt ff) = Flagged (fmap f x) y+ (fmap (fmap f) tt) (fmap (fmap f) ff)+ fmap f (Stanza x tt) = Stanza (fmap f x) (fmap (fmap f) tt)+ fmap f (Simple d) = Simple (fmap f d)++type TrueFlaggedDeps qpn = FlaggedDeps qpn+type FalseFlaggedDeps qpn = FlaggedDeps qpn++-- | A dependency (constraint) associates a package name with a+-- constrained instance.+data Dep qpn = Dep qpn (CI qpn)+ deriving (Eq, Show)++showDep :: Dep QPN -> String+showDep (Dep qpn (Fixed i (Goal v _)) ) =+ (if P qpn /= v then showVar v ++ " => " else "") +++ showQPN qpn ++ "==" ++ showI i+showDep (Dep qpn (Constrained [(vr, Goal v _)])) =+ showVar v ++ " => " ++ showQPN qpn ++ showVR vr+showDep (Dep qpn ci ) =+ showQPN qpn ++ showCI ci++instance Functor Dep where+ fmap f (Dep x y) = Dep (f x) (fmap f y)++instance ResetGoal Dep where+ resetGoal g (Dep qpn ci) = Dep qpn (resetGoal g ci)++-- | A map containing reverse dependencies between qualified+-- package names.+type RevDepMap = Map QPN [QPN]++-- | Goals are solver variables paired with information about+-- why they have been introduced.+data Goal qpn = Goal (Var qpn) (GoalReasons qpn)+ deriving (Eq, Show)++instance Functor Goal where+ fmap f (Goal v gr) = Goal (fmap f v) (fmap (fmap f) gr)++class ResetGoal f where+ resetGoal :: Goal qpn -> f qpn -> f qpn++instance ResetGoal Goal where+ resetGoal = const++-- | For open goals as they occur during the build phase, we need to store+-- additional information about flags.+data OpenGoal = OpenGoal (FlaggedDep QPN) QGoalReasons+ deriving (Eq, Show)++-- | Reasons why a goal can be added to a goal set.+data GoalReason qpn =+ UserGoal+ | PDependency (PI qpn)+ | FDependency (FN qpn) Bool+ | SDependency (SN qpn)+ deriving (Eq, Show)++instance Functor GoalReason where+ fmap _ UserGoal = UserGoal+ fmap f (PDependency pi) = PDependency (fmap f pi)+ fmap f (FDependency fn b) = FDependency (fmap f fn) b+ fmap f (SDependency sn) = SDependency (fmap f sn)++-- | The first element is the immediate reason. The rest are the reasons+-- for the reasons ...+type GoalReasons qpn = [GoalReason qpn]++type QGoalReasons = GoalReasons QPN++goalReasonToVars :: GoalReason qpn -> ConflictSet qpn+goalReasonToVars UserGoal = S.empty+goalReasonToVars (PDependency (PI qpn _)) = S.singleton (P qpn)+goalReasonToVars (FDependency qfn _) = S.singleton (F qfn)+goalReasonToVars (SDependency qsn) = S.singleton (S qsn)++goalReasonsToVars :: Ord qpn => GoalReasons qpn -> ConflictSet qpn+goalReasonsToVars = S.unions . L.map goalReasonToVars++-- | Closes a goal, i.e., removes all the extraneous information that we+-- need only during the build phase.+close :: OpenGoal -> Goal QPN+close (OpenGoal (Simple (Dep qpn _)) gr) = Goal (P qpn) gr+close (OpenGoal (Flagged qfn _ _ _ ) gr) = Goal (F qfn) gr+close (OpenGoal (Stanza qsn _) gr) = Goal (S qsn) gr++-- | Compute a conflic set from a goal. The conflict set contains the+-- closure of goal reasons as well as the variable of the goal itself.+toConflictSet :: Ord qpn => Goal qpn -> ConflictSet qpn+toConflictSet (Goal g gr) = S.insert g (goalReasonsToVars gr)
+ Distribution/Client/Dependency/Modular/Explore.hs view
@@ -0,0 +1,148 @@+module Distribution.Client.Dependency.Modular.Explore where++import Control.Applicative as A+import Data.Foldable+import Data.List as L+import Data.Map as M+import Data.Set as S++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Log+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- | Backjumping.+--+-- A tree traversal that tries to propagate conflict sets+-- up the tree from the leaves, and thereby cut branches.+-- All the tricky things are done in the function 'combine'.+backjump :: Tree a -> Tree (Maybe (ConflictSet QPN))+backjump = snd . cata go+ where+ go (FailF c fr) = (Just c, Fail c fr)+ go (DoneF rdm ) = (Nothing, Done rdm)+ go (PChoiceF qpn _ ts) = (c, PChoice qpn c (P.fromList ts'))+ where+ ~(c, ts') = combine (P qpn) (P.toList ts) S.empty+ go (FChoiceF qfn _ b ts) = (c, FChoice qfn c b (P.fromList ts'))+ where+ ~(c, ts') = combine (F qfn) (P.toList ts) S.empty+ go (SChoiceF qsn _ b ts) = (c, SChoice qsn c b (P.fromList ts'))+ where+ ~(c, ts') = combine (S qsn) (P.toList ts) S.empty+ go (GoalChoiceF ts) = (c, GoalChoice (P.fromList ts'))+ where+ ~(cs, ts') = unzip $ L.map (\ (k, (x, v)) -> (x, (k, v))) $ P.toList ts+ c = case cs of [] -> Nothing+ d : _ -> d++-- | The 'combine' function is at the heart of backjumping. It takes+-- the variable we're currently considering, and a list of children+-- annotated with their respective conflict sets, and an accumulator+-- for the result conflict set. It returns a combined conflict set+-- for the parent node, and a (potentially shortened) list of children+-- with the annotations removed.+--+-- It is *essential* that we produce the results as early as possible.+-- In particular, we have to produce the list of children prior to+-- traversing the entire list -- otherwise we lose the desired behaviour+-- of being able to traverse the tree from left to right incrementally.+--+-- We can shorten the list of children if we find an individual conflict+-- set that does not contain the current variable. In this case, we can+-- just lift the conflict set to the current level, because the current+-- level cannot possibly have contributed to this conflict, so no other+-- choice at the current level would avoid the conflict.+--+-- If any of the children might contain a successful solution+-- (indicated by Nothing), then Nothing will be the combined+-- conflict set. If all children contain conflict sets, we can+-- take the union as the combined conflict set.+combine :: Var QPN -> [(a, (Maybe (ConflictSet QPN), b))] ->+ ConflictSet QPN -> (Maybe (ConflictSet QPN), [(a, b)])+combine _ [] c = (Just c, [])+combine var ((k, ( d, v)) : xs) c = (\ ~(e, ys) -> (e, (k, v) : ys)) $+ case d of+ Just e | not (var `S.member` e) -> (Just e, [])+ | otherwise -> combine var xs (e `S.union` c)+ Nothing -> (Nothing, snd $ combine var xs S.empty)++-- | Naive backtracking exploration of the search tree. This will yield correct+-- assignments only once the tree itself is validated.+explore :: Alternative m => Tree a -> (Assignment -> m (Assignment, RevDepMap))+explore = cata go+ where+ go (FailF _ _) _ = A.empty+ go (DoneF rdm) a = pure (a, rdm)+ go (PChoiceF qpn _ ts) (A pa fa sa) =+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> r (A (M.insert qpn k pa) fa sa)) $ -- record the pkg choice+ ts+ go (FChoiceF qfn _ _ ts) (A pa fa sa) =+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> r (A pa (M.insert qfn k fa) sa)) $ -- record the flag choice+ ts+ go (SChoiceF qsn _ _ ts) (A pa fa sa) =+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> r (A pa fa (M.insert qsn k sa))) $ -- record the flag choice+ ts+ go (GoalChoiceF ts) a =+ casePSQ ts A.empty -- empty goal choice is an internal error+ (\ _k v _xs -> v a) -- commit to the first goal choice++-- | Version of 'explore' that returns a 'Log'.+exploreLog :: Tree (Maybe (ConflictSet QPN)) -> (Assignment -> Log Message (Assignment, RevDepMap))+exploreLog = cata go+ where+ go (FailF c fr) _ = failWith (Failure c fr)+ go (DoneF rdm) a = succeedWith Success (a, rdm)+ go (PChoiceF qpn c ts) (A pa fa sa) =+ backjumpInfo c $+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> tryWith (TryP (PI qpn k)) $ -- log and ...+ r (A (M.insert qpn k pa) fa sa)) -- record the pkg choice+ ts+ go (FChoiceF qfn c _ ts) (A pa fa sa) =+ backjumpInfo c $+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> tryWith (TryF qfn k) $ -- log and ...+ r (A pa (M.insert qfn k fa) sa)) -- record the pkg choice+ ts+ go (SChoiceF qsn c _ ts) (A pa fa sa) =+ backjumpInfo c $+ asum $ -- try children in order,+ P.mapWithKey -- when descending ...+ (\ k r -> tryWith (TryS qsn k) $ -- log and ...+ r (A pa fa (M.insert qsn k sa))) -- record the pkg choice+ ts+ go (GoalChoiceF ts) a =+ casePSQ ts+ (failWith (Failure S.empty EmptyGoalChoice)) -- empty goal choice is an internal error+ (\ k v _xs -> continueWith (Next (close k)) (v a)) -- commit to the first goal choice++-- | Add in information about pruned trees.+--+-- TODO: This isn't quite optimal, because we do not merely report the shape of the+-- tree, but rather make assumptions about where that shape originated from. It'd be+-- better if the pruning itself would leave information that we could pick up at this+-- point.+backjumpInfo :: Maybe (ConflictSet QPN) -> Log Message a -> Log Message a+backjumpInfo c m = m <|> case c of -- important to produce 'm' before matching on 'c'!+ Nothing -> A.empty+ Just cs -> failWith (Failure cs Backjump)++-- | Interface.+exploreTree :: Alternative m => Tree a -> m (Assignment, RevDepMap)+exploreTree t = explore t (A M.empty M.empty M.empty)++-- | Interface.+exploreTreeLog :: Tree (Maybe (ConflictSet QPN)) -> Log Message (Assignment, RevDepMap)+exploreTreeLog t = exploreLog t (A M.empty M.empty M.empty)
+ Distribution/Client/Dependency/Modular/Flag.hs view
@@ -0,0 +1,69 @@+module Distribution.Client.Dependency.Modular.Flag where++import Data.Map as M+import Prelude hiding (pi)++import Distribution.PackageDescription hiding (Flag) -- from Cabal++import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Types (OptionalStanza(..))++-- | Flag name. Consists of a package instance and the flag identifier itself.+data FN qpn = FN (PI qpn) Flag+ deriving (Eq, Ord, Show)++-- | Extract the package name from a flag name.+getPN :: FN qpn -> qpn+getPN (FN (PI qpn _) _) = qpn++instance Functor FN where+ fmap f (FN x y) = FN (fmap f x) y++-- | Flag identifier. Just a string.+type Flag = FlagName++unFlag :: Flag -> String+unFlag (FlagName fn) = fn++-- | Flag default. Just a bool.+type FDefault = Bool++-- | Flag defaults.+type FlagDefaults = Map Flag FDefault++-- | Qualified flag name.+type QFN = FN QPN++-- | Stanza name. Paired with a package name, much like a flag.+data SN qpn = SN (PI qpn) OptionalStanza+ deriving (Eq, Ord, Show)++instance Functor SN where+ fmap f (SN x y) = SN (fmap f x) y++-- | Qualified stanza name.+type QSN = SN QPN++unStanza :: OptionalStanza -> String+unStanza TestStanzas = "test"+unStanza BenchStanzas = "bench"++showQFNBool :: QFN -> Bool -> String+showQFNBool qfn@(FN pi _f) b = showPI pi ++ ":" ++ showFBool qfn b++showQSNBool :: QSN -> Bool -> String+showQSNBool qsn@(SN pi _f) b = showPI pi ++ ":" ++ showSBool qsn b++showFBool :: FN qpn -> Bool -> String+showFBool (FN _ f) True = "+" ++ unFlag f+showFBool (FN _ f) False = "-" ++ unFlag f++showSBool :: SN qpn -> Bool -> String+showSBool (SN _ s) True = "*" ++ unStanza s+showSBool (SN _ s) False = "!" ++ unStanza s++showQFN :: QFN -> String+showQFN (FN pi f) = showPI pi ++ ":" ++ unFlag f++showQSN :: QSN -> String+showQSN (SN pi f) = showPI pi ++ ":" ++ unStanza f
+ Distribution/Client/Dependency/Modular/Index.hs view
@@ -0,0 +1,33 @@+module Distribution.Client.Dependency.Modular.Index where++import Data.List as L+import Data.Map as M+import Prelude hiding (pi)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree++-- | An index contains information about package instances. This is a nested+-- dictionary. Package names are mapped to instances, which in turn is mapped+-- to info.+type Index = Map PN (Map I PInfo)++-- | Info associated with a package instance.+-- Currently, dependencies, flags, encapsulations and failure reasons.+-- Packages that have a failure reason recorded for them are disabled+-- globally, for reasons external to the solver. We currently use this+-- for shadowing which essentially is a GHC limitation, and for+-- installed packages that are broken.+data PInfo = PInfo (FlaggedDeps PN) FlagDefaults Encaps (Maybe FailReason)+ deriving (Show)++-- | Encapsulations. A list of package names.+type Encaps = [PN]++mkIndex :: [(PN, I, PInfo)] -> Index+mkIndex xs = M.map M.fromList (groupMap (L.map (\ (pn, i, pi) -> (pn, (i, pi))) xs))++groupMap :: Ord a => [(a, b)] -> Map a [b]+groupMap xs = M.fromListWith (flip (++)) (L.map (\ (x, y) -> (x, [y])) xs)
+ Distribution/Client/Dependency/Modular/IndexConversion.hs view
@@ -0,0 +1,185 @@+module Distribution.Client.Dependency.Modular.IndexConversion where++import Data.List as L+import Data.Map as M+import Data.Maybe+import Prelude hiding (pi)++import qualified Distribution.Client.PackageIndex as CI+import Distribution.Client.Types+import Distribution.Compiler+import Distribution.InstalledPackageInfo as IPI+import Distribution.Package -- from Cabal+import Distribution.PackageDescription as PD -- from Cabal+import qualified Distribution.Simple.PackageIndex as SI+import Distribution.Simple.Utils (equating)+import Distribution.System++import Distribution.Client.Dependency.Modular.Dependency as D+import Distribution.Client.Dependency.Modular.Flag as F+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree+import Distribution.Client.Dependency.Modular.Version++-- | Convert both the installed package index and the source package+-- index into one uniform solver index.+--+-- We use 'allPackagesByName' for the installed package index because+-- that returns us several instances of the same package and version+-- in order of preference. This allows us in principle to "shadow"+-- packages if there are several installed packages of the same version.+-- There are currently some shortcomings in both GHC and Cabal in+-- resolving these situations. However, the right thing to do is to+-- fix the problem there, so for now, shadowing is only activated if+-- explicitly requested.+convPIs :: OS -> Arch -> CompilerId -> Bool ->+ SI.PackageIndex -> CI.PackageIndex SourcePackage -> Index+convPIs os arch cid sip iidx sidx =+ mkIndex (convIPI' sip iidx ++ convSPI' os arch cid sidx)++-- | Convert a Cabal installed package index to the simpler,+-- more uniform index format of the solver.+convIPI' :: Bool -> SI.PackageIndex -> [(PN, I, PInfo)]+convIPI' sip idx = combine (convIP idx) . versioned . SI.allPackagesByName $ idx+ where+ -- group installed packages by version+ versioned = L.map (groupBy (equating packageVersion))+ -- apply shadowing whenever there are multple installed packages with+ -- the same version+ combine f pkgs = [ g (f p) | pbn <- pkgs, pbv <- pbn,+ (g, p) <- zip (id : repeat shadow) pbv ]+ -- shadowing is recorded in the package info+ shadow (pn, i, PInfo fdeps fds encs _) | sip = (pn, i, PInfo fdeps fds encs (Just Shadowed))+ shadow x = x++convIPI :: Bool -> SI.PackageIndex -> Index+convIPI sip = mkIndex . convIPI' sip++-- | Convert a single installed package into the solver-specific format.+convIP :: SI.PackageIndex -> InstalledPackageInfo -> (PN, I, PInfo)+convIP idx ipi =+ let ipid = installedPackageId ipi+ i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)+ pn = pkgName (sourcePackageId ipi)+ in case mapM (convIPId pn idx) (IPI.depends ipi) of+ Nothing -> (pn, i, PInfo [] M.empty [] (Just Broken))+ Just fds -> (pn, i, PInfo fds M.empty [] Nothing)+-- TODO: Installed packages should also store their encapsulations!++-- | Convert dependencies specified by an installed package id into+-- flagged dependencies of the solver.+--+-- May return Nothing if the package can't be found in the index. That+-- indicates that the original package having this dependency is broken+-- and should be ignored.+convIPId :: PN -> SI.PackageIndex -> InstalledPackageId -> Maybe (FlaggedDep PN)+convIPId pn' idx ipid =+ case SI.lookupInstalledPackageId idx ipid of+ Nothing -> Nothing+ Just ipi -> let i = I (pkgVersion (sourcePackageId ipi)) (Inst ipid)+ pn = pkgName (sourcePackageId ipi)+ in Just (D.Simple (Dep pn (Fixed i (Goal (P pn') []))))++-- | Convert a cabal-install source package index to the simpler,+-- more uniform index format of the solver.+convSPI' :: OS -> Arch -> CompilerId ->+ CI.PackageIndex SourcePackage -> [(PN, I, PInfo)]+convSPI' os arch cid = L.map (convSP os arch cid) . CI.allPackages++convSPI :: OS -> Arch -> CompilerId ->+ CI.PackageIndex SourcePackage -> Index+convSPI os arch cid = mkIndex . convSPI' os arch cid++-- | Convert a single source package into the solver-specific format.+convSP :: OS -> Arch -> CompilerId -> SourcePackage -> (PN, I, PInfo)+convSP os arch cid (SourcePackage (PackageIdentifier pn pv) gpd _pl) =+ let i = I pv InRepo+ in (pn, i, convGPD os arch cid (PI pn i) gpd)++-- We do not use 'flattenPackageDescription' or 'finalizePackageDescription'+-- from 'Distribution.PackageDescription.Configuration' here, because we+-- want to keep the condition tree, but simplify much of the test.++-- | Convert a generic package description to a solver-specific 'PInfo'.+--+-- TODO: We currently just take all dependencies from all specified library,+-- executable and test components. This does not quite seem fair.+convGPD :: OS -> Arch -> CompilerId ->+ PI PN -> GenericPackageDescription -> PInfo+convGPD os arch cid pi+ (GenericPackageDescription _ flags libs exes tests benchs) =+ let+ fds = flagDefaults flags+ in+ PInfo+ (maybe [] (convCondTree os arch cid pi fds (const True) ) libs +++ concatMap (convCondTree os arch cid pi fds (const True) . snd) exes +++ (prefix (Stanza (SN pi TestStanzas))+ (concatMap (convCondTree os arch cid pi fds (const True) . snd) tests)) +++ (prefix (Stanza (SN pi BenchStanzas))+ (concatMap (convCondTree os arch cid pi fds (const True) . snd) benchs)))+ fds+ [] -- TODO: add encaps+ Nothing++prefix :: (FlaggedDeps qpn -> FlaggedDep qpn) -> FlaggedDeps qpn -> FlaggedDeps qpn+prefix _ [] = []+prefix f fds = [f fds]++-- | Convert flag information.+flagDefaults :: [PD.Flag] -> FlagDefaults+flagDefaults = M.fromList . L.map (\ (MkFlag fn _ b _) -> (fn, b))++-- | Convert condition trees to flagged dependencies.+convCondTree :: OS -> Arch -> CompilerId -> PI PN -> FlagDefaults ->+ (a -> Bool) -> -- how to detect if a branch is active+ CondTree ConfVar [Dependency] a -> FlaggedDeps PN+convCondTree os arch cid pi@(PI pn _) fds p (CondNode info ds branches)+ | p info = L.map (D.Simple . convDep pn) ds -- unconditional dependencies+ ++ concatMap (convBranch os arch cid pi fds p) branches+ | otherwise = []++-- | Branch interpreter.+--+-- Here, we try to simplify one of Cabal's condition tree branches into the+-- solver's flagged dependency format, which is weaker. Condition trees can+-- contain complex logical expression composed from flag choices and special+-- flags (such as architecture, or compiler flavour). We try to evaluate the+-- special flags and subsequently simplify to a tree that only depends on+-- simple flag choices.+convBranch :: OS -> Arch -> CompilerId ->+ PI PN -> FlagDefaults ->+ (a -> Bool) -> -- how to detect if a branch is active+ (Condition ConfVar,+ CondTree ConfVar [Dependency] a,+ Maybe (CondTree ConfVar [Dependency] a)) -> FlaggedDeps PN+convBranch os arch cid@(CompilerId cf cv) pi fds p (c', t', mf') =+ go c' ( convCondTree os arch cid pi fds p t')+ (maybe [] (convCondTree os arch cid pi fds p) mf')+ where+ go :: Condition ConfVar ->+ FlaggedDeps PN -> FlaggedDeps PN -> FlaggedDeps PN+ go (Lit True) t _ = t+ go (Lit False) _ f = f+ go (CNot c) t f = go c f t+ go (CAnd c d) t f = go c (go d t f) f+ go (COr c d) t f = go c t (go d t f)+ go (Var (Flag fn)) t f = [Flagged (FN pi fn) (fds ! fn) t f]+ go (Var (OS os')) t f+ | os == os' = t+ | otherwise = f+ go (Var (Arch arch')) t f+ | arch == arch' = t+ | otherwise = f+ go (Var (Impl cf' cvr')) t f+ | cf == cf' && checkVR cvr' cv = t+ | otherwise = f++-- | Convert a Cabal dependency to a solver-specific dependency.+convDep :: PN -> Dependency -> Dep PN+convDep pn' (Dependency pn vr) = Dep pn (Constrained [(vr, Goal (P pn') [])])++-- | Convert a Cabal package identifier to a solver-specific dependency.+convPI :: PN -> PackageIdentifier -> Dep PN+convPI pn' (PackageIdentifier pn v) = Dep pn (Constrained [(eqVR v, Goal (P pn') [])])
+ Distribution/Client/Dependency/Modular/Log.hs view
@@ -0,0 +1,96 @@+module Distribution.Client.Dependency.Modular.Log where++import Control.Applicative+import Data.List as L+import Data.Set as S++import Distribution.Client.Dependency.Types -- from Cabal++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree (FailReason(..))++-- | The 'Log' datatype.+--+-- Represents the progress of a computation lazily.+--+-- Parameterized over the type of actual messages and the final result.+type Log m a = Progress m () a++runLog :: Log m a -> ([m], Maybe a)+runLog (Done x) = ([], Just x)+runLog (Fail _) = ([], Nothing)+runLog (Step m p) = let+ (ms, r) = runLog p+ in+ (m : ms, r)++-- | Postprocesses a log file. Takes as an argument a limit on allowed backjumps.+-- If the limit is 'Nothing', then infinitely many backjumps are allowed. If the+-- limit is 'Just 0', backtracking is completely disabled.+logToProgress :: Maybe Int -> Log Message a -> Progress String String a+logToProgress mbj l = let+ (ms, s) = runLog l+ (es, e) = proc 0 ms -- catch first error (always)+ (ns, t) = case mbj of+ Nothing -> (ms, Nothing)+ Just n -> proc n ms+ -- prefer first error over later error+ r = case t of+ Nothing -> case s of+ Nothing -> e+ Just _ -> Nothing+ Just _ -> e+ in go es -- trace for first error+ (showMessages (const True) True ns) -- shortened run+ r s+ where+ proc :: Int -> [Message] -> ([Message], Maybe (ConflictSet QPN))+ proc _ [] = ([], Nothing)+ proc n ( Failure cs Backjump : xs@(Leave : Failure cs' Backjump : _))+ | cs == cs' = proc n xs -- repeated backjumps count as one+ proc 0 ( Failure cs Backjump : _ ) = ([], Just cs)+ proc n (x@(Failure _ Backjump) : xs) = (\ ~(ys, r) -> (x : ys, r)) (proc (n - 1) xs)+ proc n (x : xs) = (\ ~(ys, r) -> (x : ys, r)) (proc n xs)++ -- The order of arguments is important! In particular 's' must not be evaluated+ -- unless absolutely necessary. It contains the final result, and if we shortcut+ -- with an error due to backjumping, evaluating 's' would still require traversing+ -- the entire tree.+ go ms (x : xs) r s = Step x (go ms xs r s)+ go ms [] (Just cs) _ = Fail ("Could not resolve dependencies:\n" +++ unlines (showMessages (L.foldr (\ v _ -> v `S.member` cs) True) False ms))+ go _ [] _ (Just s) = Done s+ go _ [] _ _ = Fail ("Could not resolve dependencies.") -- should not happen++logToProgress' :: Log Message a -> Progress String String a+logToProgress' l = let+ (ms, r) = runLog l+ xs = showMessages (const True) True ms+ in go xs r+ where+ go [x] Nothing = Fail x+ go [] Nothing = Fail ""+ go [] (Just r) = Done r+ go (x:xs) r = Step x (go xs r)+++runLogIO :: Log Message a -> IO (Maybe a)+runLogIO x =+ do+ let (ms, r) = runLog x+ putStr (unlines $ showMessages (const True) True ms)+ return r++failWith :: m -> Log m a+failWith m = Step m (Fail ())++succeedWith :: m -> a -> Log m a+succeedWith m x = Step m (Done x)++continueWith :: m -> Log m a -> Log m a+continueWith = Step++tryWith :: Message -> Log Message a -> Log Message a+tryWith m x = Step m (Step Enter x) <|> failWith Leave
+ Distribution/Client/Dependency/Modular/Message.hs view
@@ -0,0 +1,97 @@+module Distribution.Client.Dependency.Modular.Message where++import qualified Data.List as L+import Prelude hiding (pi)++import Distribution.Text -- from Cabal++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.Tree++data Message =+ Enter -- ^ increase indentation level+ | Leave -- ^ decrease indentation level+ | TryP (PI QPN)+ | TryF QFN Bool+ | TryS QSN Bool+ | Next (Goal QPN)+ | Success+ | Failure (ConflictSet QPN) FailReason++-- | Transforms the structured message type to actual messages (strings).+--+-- Takes an additional relevance predicate. The predicate gets a stack of goal+-- variables and can decide whether messages regarding these goals are relevant.+-- You can plug in 'const True' if you're interested in a full trace. If you+-- want a slice of the trace concerning a particular conflict set, then plug in+-- a predicate returning 'True' on the empty stack and if the head is in the+-- conflict set.+--+-- The second argument indicates if the level numbers should be shown. This is+-- recommended for any trace that involves backtracking, because only the level+-- numbers will allow to keep track of backjumps.+showMessages :: ([Var QPN] -> Bool) -> Bool -> [Message] -> [String]+showMessages p sl = go [] 0+ where+ go :: [Var QPN] -> Int -> [Message] -> [String]+ go _ _ [] = []+ -- complex patterns+ go v l (TryP (PI qpn i) : Enter : Failure c fr : Leave : ms) = goPReject v l qpn [i] c fr ms+ go v l (TryF qfn b : Enter : Failure c fr : Leave : ms) = (atLevel (F qfn : v) l $ "rejecting: " ++ showQFNBool qfn b ++ showFR c fr) (go v l ms)+ go v l (TryS qsn b : Enter : Failure c fr : Leave : ms) = (atLevel (S qsn : v) l $ "rejecting: " ++ showQSNBool qsn b ++ showFR c fr) (go v l ms)+ go v l (Next (Goal (P qpn) gr) : TryP pi : ms@(Enter : Next _ : _)) = (atLevel (P qpn : v) l $ "trying: " ++ showPI pi ++ showGRs gr) (go (P qpn : v) l ms)+ go v l (Failure c Backjump : ms@(Leave : Failure c' Backjump : _)) | c == c' = go v l ms+ -- standard display+ go v l (Enter : ms) = go v (l+1) ms+ go v l (Leave : ms) = go (drop 1 v) (l-1) ms+ go v l (TryP pi@(PI qpn _) : ms) = (atLevel (P qpn : v) l $ "trying: " ++ showPI pi) (go (P qpn : v) l ms)+ go v l (TryF qfn b : ms) = (atLevel (F qfn : v) l $ "trying: " ++ showQFNBool qfn b) (go (F qfn : v) l ms)+ go v l (TryS qsn b : ms) = (atLevel (S qsn : v) l $ "trying: " ++ showQSNBool qsn b) (go (S qsn : v) l ms)+ go v l (Next (Goal (P qpn) gr) : ms) = (atLevel (P qpn : v) l $ "next goal: " ++ showQPN qpn ++ showGRs gr) (go v l ms)+ go v l (Next _ : ms) = go v l ms -- ignore flag goals in the log+ go v l (Success : ms) = (atLevel v l $ "done") (go v l ms)+ go v l (Failure c fr : ms) = (atLevel v l $ "fail" ++ showFR c fr) (go v l ms)++ -- special handler for many subsequent package rejections+ goPReject :: [Var QPN] -> Int -> QPN -> [I] -> ConflictSet QPN -> FailReason -> [Message] -> [String]+ goPReject v l qpn is c fr (TryP (PI qpn' i) : Enter : Failure _ fr' : Leave : ms) | qpn == qpn' && fr == fr' = goPReject v l qpn (i : is) c fr ms+ goPReject v l qpn is c fr ms = (atLevel (P qpn : v) l $ "rejecting: " ++ showQPN qpn ++ "-" ++ L.intercalate ", " (map showI (reverse is)) ++ showFR c fr) (go v l ms)++ -- write a message, but only if it's relevant; we can also enable or disable the display of the current level+ atLevel v l x xs+ | sl && p v = let s = show l+ in ("[" ++ replicate (3 - length s) '_' ++ s ++ "] " ++ x) : xs+ | p v = x : xs+ | otherwise = xs++showGRs :: QGoalReasons -> String+showGRs (gr : _) = showGR gr+showGRs [] = ""++showGR :: GoalReason QPN -> String+showGR UserGoal = " (user goal)"+showGR (PDependency pi) = " (dependency of " ++ showPI pi ++ ")"+showGR (FDependency qfn b) = " (dependency of " ++ showQFNBool qfn b ++ ")"+showGR (SDependency qsn) = " (dependency of " ++ showQSNBool qsn True ++ ")"++showFR :: ConflictSet QPN -> FailReason -> String+showFR _ InconsistentInitialConstraints = " (inconsistent initial constraints)"+showFR _ (Conflicting ds) = " (conflict: " ++ L.intercalate ", " (map showDep ds) ++ ")"+showFR _ CannotInstall = " (only already installed instances can be used)"+showFR _ CannotReinstall = " (avoiding to reinstall a package with same version but new dependencies)"+showFR _ Shadowed = " (shadowed by another installed package with same version)"+showFR _ Broken = " (package is broken)"+showFR _ (GlobalConstraintVersion vr) = " (global constraint requires " ++ display vr ++ ")"+showFR _ GlobalConstraintInstalled = " (global constraint requires installed instance)"+showFR _ GlobalConstraintSource = " (global constraint requires source instance)"+showFR _ GlobalConstraintFlag = " (global constraint requires opposite flag selection)"+showFR _ (BuildFailureNotInIndex pn) = " (unknown package: " ++ display pn ++ ")"+showFR c Backjump = " (backjumping, conflict set: " ++ showCS c ++ ")"+-- The following are internal failures. They should not occur. In the+-- interest of not crashing unnecessarily, we still just print an error+-- message though.+showFR _ (MalformedFlagChoice qfn) = " (INTERNAL ERROR: MALFORMED FLAG CHOICE: " ++ showQFN qfn ++ ")"+showFR _ (MalformedStanzaChoice qsn) = " (INTERNAL ERROR: MALFORMED STANZA CHOICE: " ++ showQSN qsn ++ ")"+showFR _ EmptyGoalChoice = " (INTERNAL ERROR: EMPTY GOAL CHOICE)"
+ Distribution/Client/Dependency/Modular/PSQ.hs view
@@ -0,0 +1,102 @@+module Distribution.Client.Dependency.Modular.PSQ where++-- Priority search queues.+--+-- I am not yet sure what exactly is needed. But we need a datastructure with+-- key-based lookup that can be sorted. We're using a sequence right now with+-- (inefficiently implemented) lookup, because I think that queue-based+-- opertions and sorting turn out to be more efficiency-critical in practice.++import Control.Applicative+import Data.Foldable+import Data.Function+import Data.List as S hiding (foldr)+import Data.Traversable+import Prelude hiding (foldr)++newtype PSQ k v = PSQ [(k, v)]+ deriving (Eq, Show)++instance Functor (PSQ k) where+ fmap f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f v)) xs)++instance Foldable (PSQ k) where+ foldr op e (PSQ xs) = foldr op e (fmap snd xs)++instance Traversable (PSQ k) where+ traverse f (PSQ xs) = PSQ <$> traverse (\ (k, v) -> (\ x -> (k, x)) <$> f v) xs++keys :: PSQ k v -> [k]+keys (PSQ xs) = fmap fst xs++lookup :: Eq k => k -> PSQ k v -> Maybe v+lookup k (PSQ xs) = S.lookup k xs++map :: (v1 -> v2) -> PSQ k v1 -> PSQ k v2+map f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f v)) xs)++mapKeys :: (k1 -> k2) -> PSQ k1 v -> PSQ k2 v+mapKeys f (PSQ xs) = PSQ (fmap (\ (k, v) -> (f k, v)) xs)++mapWithKey :: (k -> a -> b) -> PSQ k a -> PSQ k b+mapWithKey f (PSQ xs) = PSQ (fmap (\ (k, v) -> (k, f k v)) xs)++mapWithKeyState :: (s -> k -> a -> (b, s)) -> PSQ k a -> s -> PSQ k b+mapWithKeyState p (PSQ xs) s0 =+ PSQ (foldr (\ (k, v) r s -> case p s k v of+ (w, n) -> (k, w) : (r n))+ (const []) xs s0)++delete :: Eq k => k -> PSQ k a -> PSQ k a+delete k (PSQ xs) = PSQ (snd (partition ((== k) . fst) xs))++fromList :: [(k, a)] -> PSQ k a+fromList = PSQ++cons :: k -> a -> PSQ k a -> PSQ k a+cons k x (PSQ xs) = PSQ ((k, x) : xs)++snoc :: PSQ k a -> k -> a -> PSQ k a+snoc (PSQ xs) k x = PSQ (xs ++ [(k, x)])++casePSQ :: PSQ k a -> r -> (k -> a -> PSQ k a -> r) -> r+casePSQ (PSQ xs) n c =+ case xs of+ [] -> n+ (k, v) : ys -> c k v (PSQ ys)++splits :: PSQ k a -> PSQ k (a, PSQ k a)+splits xs =+ casePSQ xs+ (PSQ [])+ (\ k v ys -> cons k (v, ys) (fmap (\ (w, zs) -> (w, cons k v zs)) (splits ys)))++sortBy :: (a -> a -> Ordering) -> PSQ k a -> PSQ k a+sortBy cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` snd) xs)++sortByKeys :: (k -> k -> Ordering) -> PSQ k a -> PSQ k a+sortByKeys cmp (PSQ xs) = PSQ (S.sortBy (cmp `on` fst) xs)++filterKeys :: (k -> Bool) -> PSQ k a -> PSQ k a+filterKeys p (PSQ xs) = PSQ (S.filter (p . fst) xs)++filter :: (a -> Bool) -> PSQ k a -> PSQ k a+filter p (PSQ xs) = PSQ (S.filter (p . snd) xs)++length :: PSQ k a -> Int+length (PSQ xs) = S.length xs++-- | "Lazy length".+--+-- Only approximates the length, but doesn't force the list.+llength :: PSQ k a -> Int+llength (PSQ []) = 0+llength (PSQ (_:[])) = 1+llength (PSQ (_:_:[])) = 2+llength (PSQ _) = 3++null :: PSQ k a -> Bool+null (PSQ xs) = S.null xs++toList :: PSQ k a -> [(k, a)]+toList (PSQ xs) = xs
+ Distribution/Client/Dependency/Modular/Package.hs view
@@ -0,0 +1,113 @@+module Distribution.Client.Dependency.Modular.Package+ (module Distribution.Client.Dependency.Modular.Package,+ module Distribution.Package) where++import Data.List as L+import Data.Map as M++import Distribution.Package -- from Cabal+import Distribution.Text -- from Cabal++import Distribution.Client.Dependency.Modular.Version++-- | A package name.+type PN = PackageName++-- | Unpacking a package name.+unPN :: PN -> String+unPN (PackageName pn) = pn++-- | Package version. A package name plus a version number.+type PV = PackageId++-- | Qualified package version.+type QPV = Q PV++-- | Package id. Currently just a black-box string.+type PId = InstalledPackageId++-- | Location. Info about whether a package is installed or not, and where+-- exactly it is located. For installed packages, uniquely identifies the+-- package instance via its 'PId'.+--+-- TODO: More information is needed about the repo.+data Loc = Inst PId | InRepo+ deriving (Eq, Ord, Show)++-- | Instance. A version number and a location.+data I = I Ver Loc+ deriving (Eq, Ord, Show)++-- | String representation of an instance.+showI :: I -> String+showI (I v InRepo) = showVer v+showI (I v (Inst (InstalledPackageId i))) = showVer v ++ "/installed" ++ shortId i+ where+ -- A hack to extract the beginning of the package ABI hash+ shortId = snip (splitAt 4) (++ "...") .+ snip ((\ (x, y) -> (reverse x, y)) . break (=='-') . reverse) ('-':)+ snip p f xs = case p xs of+ (ys, zs) -> (if L.null zs then id else f) ys++-- | Package instance. A package name and an instance.+data PI qpn = PI qpn I+ deriving (Eq, Ord, Show)++-- | String representation of a package instance.+showPI :: PI QPN -> String+showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i++-- | Checks if a package instance corresponds to an installed package.+instPI :: PI qpn -> Bool+instPI (PI _ (I _ (Inst _))) = True+instPI _ = False++instI :: I -> Bool+instI (I _ (Inst _)) = True+instI _ = False++instance Functor PI where+ fmap f (PI x y) = PI (f x) y++-- | Package path. (Stored in "reverse" order.)+type PP = [PN]++-- | String representation of a package path.+showPP :: PP -> String+showPP = intercalate "." . L.map display . reverse+++-- | A qualified entity. Pairs a package path with the entity.+data Q a = Q PP a+ deriving (Eq, Ord, Show)++-- | Standard string representation of a qualified entity.+showQ :: (a -> String) -> (Q a -> String)+showQ showa (Q [] x) = showa x+showQ showa (Q pp x) = showPP pp ++ "." ++ showa x++-- | Qualified package name.+type QPN = Q PN++-- | String representation of a qualified package path.+showQPN :: QPN -> String+showQPN = showQ display++-- | The scope associates every package with a path. The convention is that packages+-- not in the data structure have an empty path associated with them.+type Scope = Map PN PP++-- | An empty scope structure, for initialization.+emptyScope :: Scope+emptyScope = M.empty++-- | Create artificial parents for each of the package names, making+-- them all independent.+makeIndependent :: [PN] -> Scope+makeIndependent ps = L.foldl (\ sc (n, p) -> M.insert p [PackageName (show n)] sc) emptyScope (zip ([0..] :: [Int]) ps)++qualify :: Scope -> PN -> QPN+qualify sc pn = Q (findWithDefault [] pn sc) pn++unQualify :: Q a -> a+unQualify (Q _ x) = x
+ Distribution/Client/Dependency/Modular/Preference.hs view
@@ -0,0 +1,258 @@+module Distribution.Client.Dependency.Modular.Preference where++-- Reordering or pruning the tree in order to prefer or make certain choices.++import qualified Data.List as L+import qualified Data.Map as M+import Data.Monoid+import Data.Ord++import Distribution.Client.Dependency.Types+ ( PackageConstraint(..), PackagePreferences(..), InstalledPreference(..) )+import Distribution.Client.Types+ ( OptionalStanza(..) )++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree+import Distribution.Client.Dependency.Modular.Version++-- | Generic abstraction for strategies that just rearrange the package order.+-- Only packages that match the given predicate are reordered.+packageOrderFor :: (PN -> Bool) -> (PN -> I -> I -> Ordering) -> Tree a -> Tree a+packageOrderFor p cmp = trav go+ where+ go (PChoiceF v@(Q _ pn) r cs)+ | p pn = PChoiceF v r (P.sortByKeys (flip (cmp pn)) cs)+ | otherwise = PChoiceF v r cs+ go x = x++-- | Ordering that treats preferred versions as greater than non-preferred+-- versions.+preferredVersionsOrdering :: VR -> Ver -> Ver -> Ordering+preferredVersionsOrdering vr v1 v2 =+ compare (checkVR vr v1) (checkVR vr v2)++-- | Traversal that tries to establish package preferences (not constraints).+-- Works by reordering choice nodes.+preferPackagePreferences :: (PN -> PackagePreferences) -> Tree a -> Tree a+preferPackagePreferences pcs = packageOrderFor (const True) preference+ where+ preference pn i1@(I v1 _) i2@(I v2 _) =+ let PackagePreferences vr ipref = pcs pn+ in preferredVersionsOrdering vr v1 v2 `mappend` -- combines lexically+ locationsOrdering ipref i1 i2++ -- Note that we always rank installed before uninstalled, and later+ -- versions before earlier, but we can change the priority of the+ -- two orderings.+ locationsOrdering PreferInstalled v1 v2 =+ preferInstalledOrdering v1 v2 `mappend` preferLatestOrdering v1 v2+ locationsOrdering PreferLatest v1 v2 =+ preferLatestOrdering v1 v2 `mappend` preferInstalledOrdering v1 v2++-- | Ordering that treats installed instances as greater than uninstalled ones.+preferInstalledOrdering :: I -> I -> Ordering+preferInstalledOrdering (I _ (Inst _)) (I _ (Inst _)) = EQ+preferInstalledOrdering (I _ (Inst _)) _ = GT+preferInstalledOrdering _ (I _ (Inst _)) = LT+preferInstalledOrdering _ _ = EQ++-- | Compare instances by their version numbers.+preferLatestOrdering :: I -> I -> Ordering+preferLatestOrdering (I v1 _) (I v2 _) = compare v1 v2++-- | Helper function that tries to enforce a single package constraint on a+-- given instance for a P-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintP :: ConflictSet QPN -> I -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintP c (I v _) (PackageConstraintVersion _ vr) r+ | checkVR vr v = r+ | otherwise = Fail c (GlobalConstraintVersion vr)+processPackageConstraintP c i (PackageConstraintInstalled _) r+ | instI i = r+ | otherwise = Fail c GlobalConstraintInstalled+processPackageConstraintP c i (PackageConstraintSource _) r+ | not (instI i) = r+ | otherwise = Fail c GlobalConstraintSource+processPackageConstraintP _ _ _ r = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintF :: Flag -> ConflictSet QPN -> Bool -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintF f c b' (PackageConstraintFlags _ fa) r =+ case L.lookup f fa of+ Nothing -> r+ Just b | b == b' -> r+ | otherwise -> Fail c GlobalConstraintFlag+processPackageConstraintF _ _ _ _ r = r++-- | Helper function that tries to enforce a single package constraint on a+-- given flag setting for an F-node. Translates the constraint into a+-- tree-transformer that either leaves the subtree untouched, or replaces it+-- with an appropriate failure node.+processPackageConstraintS :: OptionalStanza -> ConflictSet QPN -> Bool -> PackageConstraint -> Tree a -> Tree a+processPackageConstraintS s c b' (PackageConstraintStanzas _ ss) r =+ if not b' && s `elem` ss then Fail c GlobalConstraintFlag+ else r+processPackageConstraintS _ _ _ _ r = r++-- | Traversal that tries to establish various kinds of user constraints. Works+-- by selectively disabling choices that have been ruled out by global user+-- constraints.+enforcePackageConstraints :: M.Map PN [PackageConstraint] -> Tree QGoalReasons -> Tree QGoalReasons+enforcePackageConstraints pcs = trav go+ where+ go (PChoiceF qpn@(Q _ pn) gr ts) =+ let c = toConflictSet (Goal (P qpn) gr)+ -- compose the transformation functions for each of the relevant constraint+ g = \ i -> foldl (\ h pc -> h . processPackageConstraintP c i pc) id+ (M.findWithDefault [] pn pcs)+ in PChoiceF qpn gr (P.mapWithKey g ts)+ go (FChoiceF qfn@(FN (PI (Q _ pn) _) f) gr tr ts) =+ let c = toConflictSet (Goal (F qfn) gr)+ -- compose the transformation functions for each of the relevant constraint+ g = \ b -> foldl (\ h pc -> h . processPackageConstraintF f c b pc) id+ (M.findWithDefault [] pn pcs)+ in FChoiceF qfn gr tr (P.mapWithKey g ts)+ go (SChoiceF qsn@(SN (PI (Q _ pn) _) f) gr tr ts) =+ let c = toConflictSet (Goal (S qsn) gr)+ -- compose the transformation functions for each of the relevant constraint+ g = \ b -> foldl (\ h pc -> h . processPackageConstraintS f c b pc) id+ (M.findWithDefault [] pn pcs)+ in SChoiceF qsn gr tr (P.mapWithKey g ts)+ go x = x++-- | Prefer installed packages over non-installed packages, generally.+-- All installed packages or non-installed packages are treated as+-- equivalent.+preferInstalled :: Tree a -> Tree a+preferInstalled = packageOrderFor (const True) (const preferInstalledOrdering)++-- | Prefer packages with higher version numbers over packages with+-- lower version numbers, for certain packages.+preferLatestFor :: (PN -> Bool) -> Tree a -> Tree a+preferLatestFor p = packageOrderFor p (const preferLatestOrdering)++-- | Prefer packages with higher version numbers over packages with+-- lower version numbers, for all packages.+preferLatest :: Tree a -> Tree a+preferLatest = preferLatestFor (const True)++-- | Require installed packages.+requireInstalled :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)+requireInstalled p = trav go+ where+ go (PChoiceF v@(Q _ pn) i@(gr, _) cs)+ | p pn = PChoiceF v i (P.mapWithKey installed cs)+ | otherwise = PChoiceF v i cs+ where+ installed (I _ (Inst _)) x = x+ installed _ _ = Fail (toConflictSet (Goal (P v) gr)) CannotInstall+ go x = x++-- | Avoid reinstalls.+--+-- This is a tricky strategy. If a package version is installed already and the+-- same version is available from a repo, the repo version will never be chosen.+-- This would result in a reinstall (either destructively, or potentially,+-- shadowing). The old instance won't be visible or even present anymore, but+-- other packages might have depended on it.+--+-- TODO: It would be better to actually check the reverse dependencies of installed+-- packages. If they're not depended on, then reinstalling should be fine. Even if+-- they are, perhaps this should just result in trying to reinstall those other+-- packages as well. However, doing this all neatly in one pass would require to+-- change the builder, or at least to change the goal set after building.+avoidReinstalls :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)+avoidReinstalls p = trav go+ where+ go (PChoiceF qpn@(Q _ pn) i@(gr, _) cs)+ | p pn = PChoiceF qpn i disableReinstalls+ | otherwise = PChoiceF qpn i cs+ where+ disableReinstalls =+ let installed = [ v | (I v (Inst _), _) <- toList cs ]+ in P.mapWithKey (notReinstall installed) cs++ notReinstall vs (I v InRepo) _+ | v `elem` vs = Fail (toConflictSet (Goal (P qpn) gr)) CannotReinstall+ notReinstall _ _ x = x+ go x = x++-- | Always choose the first goal in the list next, abandoning all+-- other choices.+--+-- This is unnecessary for the default search strategy, because+-- it descends only into the first goal choice anyway,+-- but may still make sense to just reduce the tree size a bit.+firstGoal :: Tree a -> Tree a+firstGoal = trav go+ where+ go (GoalChoiceF xs) = casePSQ xs (GoalChoiceF xs) (\ _ t _ -> out t)+ go x = x+ -- Note that we keep empty choice nodes, because they mean success.++-- | Transformation that tries to make a decision on base as early as+-- possible. In nearly all cases, there's a single choice for the base+-- package. Also, fixing base early should lead to better error messages.+preferBaseGoalChoice :: Tree a -> Tree a+preferBaseGoalChoice = trav go+ where+ go (GoalChoiceF xs) = GoalChoiceF (P.sortByKeys preferBase xs)+ go x = x++ preferBase :: OpenGoal -> OpenGoal -> Ordering+ preferBase (OpenGoal (Simple (Dep (Q [] pn) _)) _) _ | unPN pn == "base" = LT+ preferBase _ (OpenGoal (Simple (Dep (Q [] pn) _)) _) | unPN pn == "base" = GT+ preferBase _ _ = EQ++-- | Transformation that sorts choice nodes so that+-- child nodes with a small branching degree are preferred. As a+-- special case, choices with 0 branches will be preferred (as they+-- are immediately considered inconsistent), and choices with 1+-- branch will also be preferred (as they don't involve choice).+preferEasyGoalChoices :: Tree a -> Tree a+preferEasyGoalChoices = trav go+ where+ go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing choices) xs)+ go x = x++-- | Transformation that tries to avoid making inconsequential+-- flag choices early.+deferDefaultFlagChoices :: Tree a -> Tree a+deferDefaultFlagChoices = trav go+ where+ go (GoalChoiceF xs) = GoalChoiceF (P.sortBy defer xs)+ go x = x++ defer :: Tree a -> Tree a -> Ordering+ defer (FChoice _ _ True _) _ = GT+ defer _ (FChoice _ _ True _) = LT+ defer _ _ = EQ++-- | Variant of 'preferEasyGoalChoices'.+--+-- Only approximates the number of choices in the branches. Less accurate,+-- more efficient.+lpreferEasyGoalChoices :: Tree a -> Tree a+lpreferEasyGoalChoices = trav go+ where+ go (GoalChoiceF xs) = GoalChoiceF (P.sortBy (comparing lchoices) xs)+ go x = x++-- | Variant of 'preferEasyGoalChoices'.+--+-- I first thought that using a paramorphism might be faster here,+-- but it doesn't seem to make any difference.+preferEasyGoalChoices' :: Tree a -> Tree a+preferEasyGoalChoices' = para (inn . go)+ where+ go (GoalChoiceF xs) = GoalChoiceF (P.map fst (P.sortBy (comparing (choices . snd)) xs))+ go x = fmap fst x+
+ Distribution/Client/Dependency/Modular/Solver.hs view
@@ -0,0 +1,52 @@+module Distribution.Client.Dependency.Modular.Solver where++import Data.Map as M++import Distribution.Client.Dependency.Types++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Builder+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Explore+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Log+import Distribution.Client.Dependency.Modular.Message+import Distribution.Client.Dependency.Modular.Package+import qualified Distribution.Client.Dependency.Modular.Preference as P+import Distribution.Client.Dependency.Modular.Validate++-- | Various options for the modular solver.+data SolverConfig = SolverConfig {+ preferEasyGoalChoices :: Bool,+ independentGoals :: Bool,+ avoidReinstalls :: Bool,+ shadowPkgs :: Bool,+ maxBackjumps :: Maybe Int+}++solve :: SolverConfig -> -- solver parameters+ Index -> -- all available packages as an index+ (PN -> PackagePreferences) -> -- preferences+ Map PN [PackageConstraint] -> -- global constraints+ [PN] -> -- global goals+ Log Message (Assignment, RevDepMap)+solve sc idx userPrefs userConstraints userGoals =+ explorePhase $+ heuristicsPhase $+ preferencesPhase $+ validationPhase $+ prunePhase $+ buildPhase+ where+ explorePhase = exploreTreeLog . backjump+ heuristicsPhase = if preferEasyGoalChoices sc+ then P.preferBaseGoalChoice . P.deferDefaultFlagChoices . P.lpreferEasyGoalChoices+ else P.preferBaseGoalChoice+ preferencesPhase = P.preferPackagePreferences userPrefs+ validationPhase = P.enforcePackageConstraints userConstraints .+ validateTree idx+ prunePhase = (if avoidReinstalls sc then P.avoidReinstalls (const True) else id) .+ -- packages that can never be "upgraded":+ P.requireInstalled (`elem` [PackageName "base",+ PackageName "ghc-prim"])+ buildPhase = buildTree idx (independentGoals sc) userGoals
+ Distribution/Client/Dependency/Modular/Tree.hs view
@@ -0,0 +1,146 @@+module Distribution.Client.Dependency.Modular.Tree where++import Control.Applicative+import Control.Monad hiding (mapM)+import Data.Foldable+import Data.Traversable+import Prelude hiding (foldr, mapM)++import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Version++-- | Type of the search tree. Inlining the choice nodes for now.+data Tree a =+ PChoice QPN a (PSQ I (Tree a))+ | FChoice QFN a Bool (PSQ Bool (Tree a)) -- Bool indicates whether it's trivial+ | SChoice QSN a Bool (PSQ Bool (Tree a)) -- Bool indicates whether it's trivial+ | GoalChoice (PSQ OpenGoal (Tree a)) -- PSQ should never be empty+ | Done RevDepMap+ | Fail (ConflictSet QPN) FailReason+ deriving (Eq, Show)+ -- Above, a choice is called trivial if it clearly does not matter. The+ -- special case of triviality we actually consider is if there are no new+ -- dependencies introduced by this node.++instance Functor Tree where+ fmap f (PChoice qpn i xs) = PChoice qpn (f i) (fmap (fmap f) xs)+ fmap f (FChoice qfn i b xs) = FChoice qfn (f i) b (fmap (fmap f) xs)+ fmap f (SChoice qsn i b xs) = SChoice qsn (f i) b (fmap (fmap f) xs)+ fmap f (GoalChoice xs) = GoalChoice (fmap (fmap f) xs)+ fmap _f (Done rdm ) = Done rdm+ fmap _f (Fail cs fr ) = Fail cs fr++data FailReason = InconsistentInitialConstraints+ | Conflicting [Dep QPN]+ | CannotInstall+ | CannotReinstall+ | Shadowed+ | Broken+ | GlobalConstraintVersion VR+ | GlobalConstraintInstalled+ | GlobalConstraintSource+ | GlobalConstraintFlag+ | BuildFailureNotInIndex PN+ | MalformedFlagChoice QFN+ | MalformedStanzaChoice QSN+ | EmptyGoalChoice+ | Backjump+ deriving (Eq, Show)++-- | Functor for the tree type.+data TreeF a b =+ PChoiceF QPN a (PSQ I b)+ | FChoiceF QFN a Bool (PSQ Bool b)+ | SChoiceF QSN a Bool (PSQ Bool b)+ | GoalChoiceF (PSQ OpenGoal b)+ | DoneF RevDepMap+ | FailF (ConflictSet QPN) FailReason++out :: Tree a -> TreeF a (Tree a)+out (PChoice p i ts) = PChoiceF p i ts+out (FChoice p i b ts) = FChoiceF p i b ts+out (SChoice p i b ts) = SChoiceF p i b ts+out (GoalChoice ts) = GoalChoiceF ts+out (Done x ) = DoneF x+out (Fail c x ) = FailF c x++inn :: TreeF a (Tree a) -> Tree a+inn (PChoiceF p i ts) = PChoice p i ts+inn (FChoiceF p i b ts) = FChoice p i b ts+inn (SChoiceF p i b ts) = SChoice p i b ts+inn (GoalChoiceF ts) = GoalChoice ts+inn (DoneF x ) = Done x+inn (FailF c x ) = Fail c x++instance Functor (TreeF a) where+ fmap f (PChoiceF p i ts) = PChoiceF p i (fmap f ts)+ fmap f (FChoiceF p i b ts) = FChoiceF p i b (fmap f ts)+ fmap f (SChoiceF p i b ts) = SChoiceF p i b (fmap f ts)+ fmap f (GoalChoiceF ts) = GoalChoiceF (fmap f ts)+ fmap _ (DoneF x ) = DoneF x+ fmap _ (FailF c x ) = FailF c x++instance Foldable (TreeF a) where+ foldr op e (PChoiceF _ _ ts) = foldr op e ts+ foldr op e (FChoiceF _ _ _ ts) = foldr op e ts+ foldr op e (SChoiceF _ _ _ ts) = foldr op e ts+ foldr op e (GoalChoiceF ts) = foldr op e ts+ foldr _ e (DoneF _ ) = e+ foldr _ e (FailF _ _ ) = e++instance Traversable (TreeF a) where+ traverse f (PChoiceF p i ts) = PChoiceF <$> pure p <*> pure i <*> traverse f ts+ traverse f (FChoiceF p i b ts) = FChoiceF <$> pure p <*> pure i <*> pure b <*> traverse f ts+ traverse f (SChoiceF p i b ts) = SChoiceF <$> pure p <*> pure i <*> pure b <*> traverse f ts+ traverse f (GoalChoiceF ts) = GoalChoiceF <$> traverse f ts+ traverse _ (DoneF x ) = DoneF <$> pure x+ traverse _ (FailF c x ) = FailF <$> pure c <*> pure x++-- | Determines whether a tree is active, i.e., isn't a failure node.+active :: Tree a -> Bool+active (Fail _ _) = False+active _ = True++-- | Determines how many active choices are available in a node. Note that we+-- count goal choices as having one choice, always.+choices :: Tree a -> Int+choices (PChoice _ _ ts) = P.length (P.filter active ts)+choices (FChoice _ _ _ ts) = P.length (P.filter active ts)+choices (SChoice _ _ _ ts) = P.length (P.filter active ts)+choices (GoalChoice _ ) = 1+choices (Done _ ) = 1+choices (Fail _ _ ) = 0++-- | Variant of 'choices' that only approximates the number of choices,+-- using 'llength'.+lchoices :: Tree a -> Int+lchoices (PChoice _ _ ts) = P.llength (P.filter active ts)+lchoices (FChoice _ _ _ ts) = P.llength (P.filter active ts)+lchoices (SChoice _ _ _ ts) = P.llength (P.filter active ts)+lchoices (GoalChoice _ ) = 1+lchoices (Done _ ) = 1+lchoices (Fail _ _ ) = 0++-- | Catamorphism on trees.+cata :: (TreeF a b -> b) -> Tree a -> b+cata phi x = (phi . fmap (cata phi) . out) x++trav :: (TreeF a (Tree b) -> TreeF b (Tree b)) -> Tree a -> Tree b+trav psi x = cata (inn . psi) x++-- | Paramorphism on trees.+para :: (TreeF a (b, Tree a) -> b) -> Tree a -> b+para phi = phi . fmap (\ x -> (para phi x, x)) . out++cataM :: Monad m => (TreeF a b -> m b) -> Tree a -> m b+cataM phi = phi <=< mapM (cataM phi) <=< return . out++-- | Anamorphism on trees.+ana :: (b -> TreeF a b) -> b -> Tree a+ana psi = inn . fmap (ana psi) . psi++anaM :: Monad m => (b -> m (TreeF a b)) -> b -> m (Tree a)+anaM psi = return . inn <=< mapM (anaM psi) <=< psi
+ Distribution/Client/Dependency/Modular/Validate.hs view
@@ -0,0 +1,232 @@+module Distribution.Client.Dependency.Modular.Validate where++-- Validation of the tree.+--+-- The task here is to make sure all constraints hold. After validation, any+-- assignment returned by exploration of the tree should be a complete valid+-- assignment, i.e., actually constitute a solution.++import Control.Applicative+import Control.Monad.Reader hiding (sequence)+import Data.List as L+import Data.Map as M+import Data.Traversable+import Prelude hiding (sequence)++import Distribution.Client.Dependency.Modular.Assignment+import Distribution.Client.Dependency.Modular.Dependency+import Distribution.Client.Dependency.Modular.Flag+import Distribution.Client.Dependency.Modular.Index+import Distribution.Client.Dependency.Modular.Package+import Distribution.Client.Dependency.Modular.PSQ as P+import Distribution.Client.Dependency.Modular.Tree++-- In practice, most constraints are implication constraints (IF we have made+-- a number of choices, THEN we also have to ensure that). We call constraints+-- that for which the precondiditions are fulfilled ACTIVE. We maintain a set+-- of currently active constraints that we pass down the node.+--+-- We aim at detecting inconsistent states as early as possible.+--+-- Whenever we make a choice, there are two things that need to happen:+--+-- (1) We must check that the choice is consistent with the currently+-- active constraints.+--+-- (2) The choice increases the set of active constraints. For the new+-- active constraints, we must check that they are consistent with+-- the current state.+--+-- We can actually merge (1) and (2) by saying the the current choice is+-- a new active constraint, fixing the choice.+--+-- If a test fails, we have detected an inconsistent state. We can+-- disable the current subtree and do not have to traverse it any further.+--+-- We need a good way to represent the current state, i.e., the current+-- set of active constraints. Since the main situation where we have to+-- search in it is (1), it seems best to store the state by package: for+-- every package, we store which versions are still allowed. If for any+-- package, we have inconsistent active constraints, we can also stop.+-- This is a particular way to read task (2):+--+-- (2, weak) We only check if the new constraints are consistent with+-- the choices we've already made, and add them to the active set.+--+-- (2, strong) We check if the new constraints are consistent with the+-- choices we've already made, and the constraints we already have.+--+-- It currently seems as if we're implementing the weak variant. However,+-- when used together with 'preferEasyGoalChoices', we will find an+-- inconsistent state in the very next step.+--+-- What do we do about flags?+--+-- Like for packages, we store the flag choices we have already made.+-- Now, regarding (1), we only have to test whether we've decided the+-- current flag before. Regarding (2), the interesting bit is in discovering+-- the new active constraints. To this end, we look up the constraints for+-- the package the flag belongs to, and traverse its flagged dependencies.+-- Wherever we find the flag in question, we start recording dependencies+-- underneath as new active dependencies. If we encounter other flags, we+-- check if we've chosen them already and either proceed or stop.++-- | The state needed during validation.+data ValidateState = VS {+ index :: Index,+ saved :: Map QPN (FlaggedDeps QPN), -- saved, scoped, dependencies+ pa :: PreAssignment+}++type Validate = Reader ValidateState++validate :: Tree (QGoalReasons, Scope) -> Validate (Tree QGoalReasons)+validate = cata go+ where+ go :: TreeF (QGoalReasons, Scope) (Validate (Tree QGoalReasons)) -> Validate (Tree QGoalReasons)++ go (PChoiceF qpn (gr, sc) ts) = PChoice qpn gr <$> sequence (P.mapWithKey (goP qpn gr sc) ts)+ go (FChoiceF qfn (gr, _sc) b ts) =+ do+ -- Flag choices may occur repeatedly (because they can introduce new constraints+ -- in various places). However, subsequent choices must be consistent. We thereby+ -- collapse repeated flag choice nodes.+ PA _ pfa _ <- asks pa -- obtain current flag-preassignment+ case M.lookup qfn pfa of+ Just rb -> -- flag has already been assigned; collapse choice to the correct branch+ case P.lookup rb ts of+ Just t -> goF qfn gr rb t+ Nothing -> return $ Fail (toConflictSet (Goal (F qfn) gr)) (MalformedFlagChoice qfn)+ Nothing -> -- flag choice is new, follow both branches+ FChoice qfn gr b <$> sequence (P.mapWithKey (goF qfn gr) ts)+ go (SChoiceF qsn (gr, _sc) b ts) =+ do+ -- Optional stanza choices are very similar to flag choices.+ PA _ _ psa <- asks pa -- obtain current stanza-preassignment+ case M.lookup qsn psa of+ Just rb -> -- stanza choice has already been made; collapse choice to the correct branch+ case P.lookup rb ts of+ Just t -> goS qsn gr rb t+ Nothing -> return $ Fail (toConflictSet (Goal (S qsn) gr)) (MalformedStanzaChoice qsn)+ Nothing -> -- stanza choice is new, follow both branches+ SChoice qsn gr b <$> sequence (P.mapWithKey (goS qsn gr) ts)++ -- We don't need to do anything for goal choices or failure nodes.+ go (GoalChoiceF ts) = GoalChoice <$> sequence ts+ go (DoneF rdm ) = pure (Done rdm)+ go (FailF c fr ) = pure (Fail c fr)++ -- What to do for package nodes ...+ goP :: QPN -> QGoalReasons -> Scope -> I -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+ goP qpn@(Q _pp pn) gr sc i r = do+ PA ppa pfa psa <- asks pa -- obtain current preassignment+ idx <- asks index -- obtain the index+ svd <- asks saved -- obtain saved dependencies+ let (PInfo deps _ _ mfr) = idx ! pn ! i -- obtain dependencies and index-dictated exclusions introduced by the choice+ let qdeps = L.map (fmap (qualify sc)) deps -- qualify the deps in the current scope+ -- the new active constraints are given by the instance we have chosen,+ -- plus the dependency information we have for that instance+ let goal = Goal (P qpn) gr+ let newactives = Dep qpn (Fixed i goal) : L.map (resetGoal goal) (extractDeps pfa psa qdeps)+ -- We now try to extend the partial assignment with the new active constraints.+ let mnppa = extend (P qpn) ppa newactives+ -- In case we continue, we save the scoped dependencies+ let nsvd = M.insert qpn qdeps svd+ case mfr of+ Just fr -> -- The index marks this as an invalid choice. We can stop.+ return (Fail (toConflictSet goal) fr)+ _ -> case mnppa of+ Left (c, d) -> -- We have an inconsistency. We can stop.+ return (Fail c (Conflicting d))+ Right nppa -> -- We have an updated partial assignment for the recursive validation.+ local (\ s -> s { pa = PA nppa pfa psa, saved = nsvd }) r++ -- What to do for flag nodes ...+ goF :: QFN -> QGoalReasons -> Bool -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+ goF qfn@(FN (PI qpn _i) _f) gr b r = do+ PA ppa pfa psa <- asks pa -- obtain current preassignment+ svd <- asks saved -- obtain saved dependencies+ -- Note that there should be saved dependencies for the package in question,+ -- because while building, we do not choose flags before we see the packages+ -- that define them.+ let qdeps = svd ! qpn+ -- We take the *saved* dependencies, because these have been qualified in the+ -- correct scope.+ --+ -- Extend the flag assignment+ let npfa = M.insert qfn b pfa+ -- We now try to get the new active dependencies we might learn about because+ -- we have chosen a new flag.+ let newactives = extractNewDeps (F qfn) gr b npfa psa qdeps+ -- As in the package case, we try to extend the partial assignment.+ case extend (F qfn) ppa newactives of+ Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+ Right nppa -> local (\ s -> s { pa = PA nppa npfa psa }) r++ -- What to do for stanza nodes (similar to flag nodes) ...+ goS :: QSN -> QGoalReasons -> Bool -> Validate (Tree QGoalReasons) -> Validate (Tree QGoalReasons)+ goS qsn@(SN (PI qpn _i) _f) gr b r = do+ PA ppa pfa psa <- asks pa -- obtain current preassignment+ svd <- asks saved -- obtain saved dependencies+ -- Note that there should be saved dependencies for the package in question,+ -- because while building, we do not choose flags before we see the packages+ -- that define them.+ let qdeps = svd ! qpn+ -- We take the *saved* dependencies, because these have been qualified in the+ -- correct scope.+ --+ -- Extend the flag assignment+ let npsa = M.insert qsn b psa+ -- We now try to get the new active dependencies we might learn about because+ -- we have chosen a new flag.+ let newactives = extractNewDeps (S qsn) gr b pfa npsa qdeps+ -- As in the package case, we try to extend the partial assignment.+ case extend (S qsn) ppa newactives of+ Left (c, d) -> return (Fail c (Conflicting d)) -- inconsistency found+ Right nppa -> local (\ s -> s { pa = PA nppa pfa npsa }) r++-- | We try to extract as many concrete dependencies from the given flagged+-- dependencies as possible. We make use of all the flag knowledge we have+-- already acquired.+extractDeps :: FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN]+extractDeps fa sa deps = do+ d <- deps+ case d of+ Simple sd -> return sd+ Flagged qfn _ td fd -> case M.lookup qfn fa of+ Nothing -> mzero+ Just True -> extractDeps fa sa td+ Just False -> extractDeps fa sa fd+ Stanza qsn td -> case M.lookup qsn sa of+ Nothing -> mzero+ Just True -> extractDeps fa sa td+ Just False -> []++-- | We try to find new dependencies that become available due to the given+-- flag or stanza choice. We therefore look for the choice in question, and then call+-- 'extractDeps' for everything underneath.+extractNewDeps :: Var QPN -> QGoalReasons -> Bool -> FAssignment -> SAssignment -> FlaggedDeps QPN -> [Dep QPN]+extractNewDeps v gr b fa sa = go+ where+ go deps = do+ d <- deps+ case d of+ Simple _ -> mzero+ Flagged qfn' _ td fd+ | v == F qfn' -> L.map (resetGoal (Goal v gr)) $+ if b then extractDeps fa sa td else extractDeps fa sa fd+ | otherwise -> case M.lookup qfn' fa of+ Nothing -> mzero+ Just True -> go td+ Just False -> go fd+ Stanza qsn' td+ | v == S qsn' -> L.map (resetGoal (Goal v gr)) $+ if b then extractDeps fa sa td else []+ | otherwise -> case M.lookup qsn' sa of+ Nothing -> mzero+ Just True -> go td+ Just False -> []++-- | Interface.+validateTree :: Index -> Tree (QGoalReasons, Scope) -> Tree QGoalReasons+validateTree idx t = runReader (validate t) (VS idx M.empty (PA M.empty M.empty M.empty))
+ Distribution/Client/Dependency/Modular/Version.hs view
@@ -0,0 +1,43 @@+module Distribution.Client.Dependency.Modular.Version where++import qualified Distribution.Version as CV -- from Cabal+import Distribution.Text -- from Cabal++-- | Preliminary type for versions.+type Ver = CV.Version++-- | String representation of a version.+showVer :: Ver -> String+showVer = display++-- | Version range. Consists of a lower and upper bound.+type VR = CV.VersionRange++-- | String representation of a version range.+showVR :: VR -> String+showVR = display++-- | Unconstrained version range.+anyVR :: VR+anyVR = CV.anyVersion++-- | Version range fixing a single version.+eqVR :: Ver -> VR+eqVR = CV.thisVersion++-- | Intersect two version ranges.+(.&&.) :: VR -> VR -> VR+(.&&.) = CV.intersectVersionRanges++-- | Simplify a version range.+simplifyVR :: VR -> VR+simplifyVR = CV.simplifyVersionRange++-- | Checking a version against a version range.+checkVR :: VR -> Ver -> Bool+checkVR = flip CV.withinRange++-- | Make a version number.+mkV :: [Int] -> Ver+mkV xs = CV.Version xs []+
+ Distribution/Client/Dependency/TopDown.hs view
@@ -0,0 +1,776 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Dependency.Types+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Common types for dependency resolution.+-----------------------------------------------------------------------------+module Distribution.Client.Dependency.TopDown (+ topDownResolver+ ) where++import Distribution.Client.Dependency.TopDown.Types+import qualified Distribution.Client.Dependency.TopDown.Constraints as Constraints+import Distribution.Client.Dependency.TopDown.Constraints+ ( Satisfiable(..) )+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan+ ( PlanPackage(..) )+import Distribution.Client.Types+ ( AvailablePackage(..), ConfiguredPackage(..), InstalledPackage(..) )+import Distribution.Client.Dependency.Types+ ( DependencyResolver, PackageConstraint(..)+ , PackagePreferences(..), InstalledPreference(..)+ , Progress(..), foldProgress )++import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Package+ ( PackageName(..), PackageIdentifier, Package(packageId), packageVersion, packageName+ , Dependency(Dependency), thisPackageVersion, notThisPackageVersion+ , PackageFixedDeps(depends) )+import Distribution.PackageDescription+ ( PackageDescription(buildDepends) )+import Distribution.Client.PackageUtils+ ( externalBuildDepends )+import Distribution.PackageDescription.Configuration+ ( finalizePackageDescription, flattenPackageDescription )+import Distribution.Version+ ( VersionRange, anyVersion, withinRange, simplifyVersionRange+ , UpperBound(..), asVersionIntervals )+import Distribution.Compiler+ ( CompilerId )+import Distribution.System+ ( Platform )+import Distribution.Simple.Utils+ ( equating, comparing )+import Distribution.Text+ ( display )++import Data.List+ ( foldl', maximumBy, minimumBy, nub, sort, groupBy )+import Data.Maybe+ ( fromJust, fromMaybe, catMaybes )+import Data.Monoid+ ( Monoid(mempty) )+import Control.Monad+ ( guard )+import qualified Data.Set as Set+import Data.Set (Set)+import qualified Data.Map as Map+import qualified Data.Graph as Graph+import qualified Data.Array as Array+import Control.Exception+ ( assert )++-- ------------------------------------------------------------+-- * Search state types+-- ------------------------------------------------------------++type Constraints = Constraints.Constraints+ InstalledPackageEx UnconfiguredPackage ExclusionReason+type SelectedPackages = PackageIndex SelectedPackage++-- ------------------------------------------------------------+-- * The search tree type+-- ------------------------------------------------------------++data SearchSpace inherited pkg+ = ChoiceNode inherited [[(pkg, SearchSpace inherited pkg)]]+ | Failure Failure++-- ------------------------------------------------------------+-- * Traverse a search tree+-- ------------------------------------------------------------++explore :: (PackageName -> PackagePreferences)+ -> SearchSpace (SelectedPackages, Constraints, SelectionChanges)+ SelectablePackage+ -> Progress Log Failure (SelectedPackages, Constraints)++explore _ (Failure failure) = Fail failure+explore _ (ChoiceNode (s,c,_) []) = Done (s,c)+explore pref (ChoiceNode _ choices) =+ case [ choice | [choice] <- choices ] of+ ((_, node'):_) -> Step (logInfo node') (explore pref node')+ [] -> Step (logInfo node') (explore pref node')+ where+ choice = minimumBy (comparing topSortNumber) choices+ pkgname = packageName . fst . head $ choice+ (_, node') = maximumBy (bestByPref pkgname) choice+ where+ topSortNumber choice = case fst (head choice) of+ InstalledOnly (InstalledPackageEx _ i _) -> i+ AvailableOnly (UnconfiguredPackage _ i _) -> i+ InstalledAndAvailable _ (UnconfiguredPackage _ i _) -> i++ bestByPref pkgname = case packageInstalledPreference of+ PreferLatest ->+ comparing (\(p,_) -> ( isPreferred p, packageId p))+ PreferInstalled ->+ comparing (\(p,_) -> (isInstalled p, isPreferred p, packageId p))+ where+ isInstalled (AvailableOnly _) = False+ isInstalled _ = True+ isPreferred p = packageVersion p `withinRange` preferredVersions+ (PackagePreferences preferredVersions packageInstalledPreference)+ = pref pkgname++ logInfo node = Select selected discarded+ where (selected, discarded) = case node of+ Failure _ -> ([], [])+ ChoiceNode (_,_,changes) _ -> changes++-- ------------------------------------------------------------+-- * Generate a search tree+-- ------------------------------------------------------------++type ConfigurePackage = PackageIndex SelectablePackage+ -> SelectablePackage+ -> Either [Dependency] SelectedPackage++-- | (packages selected, packages discarded)+type SelectionChanges = ([SelectedPackage], [PackageIdentifier])++searchSpace :: ConfigurePackage+ -> Constraints+ -> SelectedPackages+ -> SelectionChanges+ -> Set PackageName+ -> SearchSpace (SelectedPackages, Constraints, SelectionChanges)+ SelectablePackage+searchSpace configure constraints selected changes next =+ ChoiceNode (selected, constraints, changes)+ [ [ (pkg, select name pkg)+ | pkg <- PackageIndex.lookupPackageName available name ]+ | name <- Set.elems next ]+ where+ available = Constraints.choices constraints++ select name pkg = case configure available pkg of+ Left missing -> Failure $ ConfigureFailed pkg+ [ (dep, Constraints.conflicting constraints dep)+ | dep <- missing ]+ Right pkg' -> case constrainDeps pkg' newDeps constraints [] of+ Left failure -> Failure failure+ Right (constraints', newDiscarded) ->+ searchSpace configure+ constraints' selected' (newSelected, newDiscarded) next'+ where+ selected' = foldl' (flip PackageIndex.insert) selected newSelected+ newSelected =+ case Constraints.isPaired constraints (packageId pkg) of+ Nothing -> [pkg']+ Just pkgid' -> [pkg', pkg'']+ where+ Just pkg'' = fmap (\(InstalledOnly p) -> InstalledOnly p)+ (PackageIndex.lookupPackageId available pkgid')++ newPkgs = [ name'+ | dep <- newDeps+ , let (Dependency name' _) = untagDependency dep+ , null (PackageIndex.lookupPackageName selected' name') ]+ newDeps = concatMap packageConstraints newSelected+ next' = Set.delete name+ $ foldl' (flip Set.insert) next newPkgs++packageConstraints :: SelectedPackage -> [TaggedDependency]+packageConstraints = either installedConstraints availableConstraints+ . preferAvailable+ where+ preferAvailable (InstalledOnly pkg) = Left pkg+ preferAvailable (AvailableOnly pkg) = Right pkg+ preferAvailable (InstalledAndAvailable _ pkg) = Right pkg+ installedConstraints (InstalledPackageEx _ _ deps) =+ [ TaggedDependency InstalledConstraint (thisPackageVersion dep)+ | dep <- deps ]+ availableConstraints (SemiConfiguredPackage _ _ deps) =+ [ TaggedDependency NoInstalledConstraint dep | dep <- deps ]++constrainDeps :: SelectedPackage -> [TaggedDependency] -> Constraints+ -> [PackageIdentifier]+ -> Either Failure (Constraints, [PackageIdentifier])+constrainDeps pkg [] cs discard =+ case addPackageSelectConstraint (packageId pkg) cs of+ Satisfiable cs' discard' -> Right (cs', discard' ++ discard)+ _ -> impossible+constrainDeps pkg (dep:deps) cs discard =+ case addPackageDependencyConstraint (packageId pkg) dep cs of+ Satisfiable cs' discard' -> constrainDeps pkg deps cs' (discard' ++ discard)+ Unsatisfiable -> impossible+ ConflictsWith conflicts ->+ Left (DependencyConflict pkg dep conflicts)++-- ------------------------------------------------------------+-- * The main algorithm+-- ------------------------------------------------------------++search :: ConfigurePackage+ -> (PackageName -> PackagePreferences)+ -> Constraints+ -> Set PackageName+ -> Progress Log Failure (SelectedPackages, Constraints)+search configure pref constraints =+ explore pref . searchSpace configure constraints mempty ([], [])++-- ------------------------------------------------------------+-- * The top level resolver+-- ------------------------------------------------------------++-- | The main exported resolver, with string logging and failure types to fit+-- the standard 'DependencyResolver' interface.+--+topDownResolver :: DependencyResolver+topDownResolver = ((((((mapMessages .).).).).).) . topDownResolver'+ where+ mapMessages :: Progress Log Failure a -> Progress String String a+ mapMessages = foldProgress (Step . showLog) (Fail . showFailure) Done++-- | The native resolver with detailed structured logging and failure types.+--+topDownResolver' :: Platform -> CompilerId+ -> PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> (PackageName -> PackagePreferences)+ -> [PackageConstraint]+ -> [PackageName]+ -> Progress Log Failure [PlanPackage]+topDownResolver' platform comp installed available+ preferences constraints targets =+ fmap (uncurry finalise)+ . (\cs -> search configure preferences cs initialPkgNames)+ =<< addTopLevelConstraints constraints constraintSet++ where+ configure = configurePackage platform comp+ constraintSet :: Constraints+ constraintSet = Constraints.empty+ (annotateInstalledPackages topSortNumber installed')+ (annotateAvailablePackages constraints topSortNumber available')+ (installed', available') = selectNeededSubset installed available+ initialPkgNames+ topSortNumber = topologicalSortNumbering installed' available'++ initialPkgNames = Set.fromList targets++ finalise selected' constraints' =+ PackageIndex.allPackages+ . fst . improvePlan installed' constraints'+ . PackageIndex.fromList+ $ finaliseSelectedPackages preferences selected' constraints'++addTopLevelConstraints :: [PackageConstraint] -> Constraints+ -> Progress a Failure Constraints+addTopLevelConstraints [] cs = Done cs+addTopLevelConstraints (PackageFlagsConstraint _ _ :deps) cs =+ addTopLevelConstraints deps cs++addTopLevelConstraints (PackageVersionConstraint pkg ver:deps) cs =+ case addTopLevelVersionConstraint pkg ver cs of+ Satisfiable cs' _ ->+ addTopLevelConstraints deps cs'++ Unsatisfiable ->+ Fail (TopLevelVersionConstraintUnsatisfiable pkg ver)++ ConflictsWith conflicts ->+ Fail (TopLevelVersionConstraintConflict pkg ver conflicts)++addTopLevelConstraints (PackageInstalledConstraint pkg:deps) cs =+ case addTopLevelInstalledConstraint pkg cs of+ Satisfiable cs' _ -> addTopLevelConstraints deps cs'++ Unsatisfiable ->+ Fail (TopLevelInstallConstraintUnsatisfiable pkg)++ ConflictsWith conflicts ->+ Fail (TopLevelInstallConstraintConflict pkg conflicts)++configurePackage :: Platform -> CompilerId -> ConfigurePackage+configurePackage platform comp available spkg = case spkg of+ InstalledOnly ipkg -> Right (InstalledOnly ipkg)+ AvailableOnly apkg -> fmap AvailableOnly (configure apkg)+ InstalledAndAvailable ipkg apkg -> fmap (InstalledAndAvailable ipkg)+ (configure apkg)+ where+ configure (UnconfiguredPackage apkg@(AvailablePackage _ p _) _ flags) =+ case finalizePackageDescription flags dependencySatisfiable+ platform comp [] p of+ Left missing -> Left missing+ Right (pkg, flags') -> Right $+ SemiConfiguredPackage apkg flags' (externalBuildDepends pkg)++ dependencySatisfiable = not . null . PackageIndex.lookupDependency available++-- | Annotate each installed packages with its set of transative dependencies+-- and its topological sort number.+--+annotateInstalledPackages :: (PackageName -> TopologicalSortNumber)+ -> PackageIndex InstalledPackage+ -> PackageIndex InstalledPackageEx+annotateInstalledPackages dfsNumber installed = PackageIndex.fromList+ [ InstalledPackageEx pkg (dfsNumber (packageName pkg)) (transitiveDepends pkg)+ | pkg <- PackageIndex.allPackages installed ]+ where+ transitiveDepends :: InstalledPackage -> [PackageIdentifier]+ transitiveDepends = map (packageId . toPkg) . tail . Graph.reachable graph+ . fromJust . toVertex . packageId+ (graph, toPkg, toVertex) = PackageIndex.dependencyGraph installed+++-- | Annotate each available packages with its topological sort number and any+-- user-supplied partial flag assignment.+--+annotateAvailablePackages :: [PackageConstraint]+ -> (PackageName -> TopologicalSortNumber)+ -> PackageIndex AvailablePackage+ -> PackageIndex UnconfiguredPackage+annotateAvailablePackages constraints dfsNumber available = PackageIndex.fromList+ [ UnconfiguredPackage pkg (dfsNumber name) (flagsFor name)+ | pkg <- PackageIndex.allPackages available+ , let name = packageName pkg ]+ where+ flagsFor = fromMaybe [] . flip Map.lookup flagsMap+ flagsMap = Map.fromList+ [ (name, flags)+ | PackageFlagsConstraint name flags <- constraints ]++-- | One of the heuristics we use when guessing which path to take in the+-- search space is an ordering on the choices we make. It's generally better+-- to make decisions about packages higer in the dep graph first since they+-- place constraints on packages lower in the dep graph.+--+-- To pick them in that order we annotate each package with its topological+-- sort number. So if package A depends on package B then package A will have+-- a lower topological sort number than B and we'll make a choice about which+-- version of A to pick before we make a choice about B (unless there is only+-- one possible choice for B in which case we pick that immediately).+--+-- To construct these topological sort numbers we combine and flatten the+-- installed and available package sets. We consider only dependencies between+-- named packages, not including versions and for not-yet-configured packages+-- we look at all the possible dependencies, not just those under any single+-- flag assignment. This means we can actually get impossible combinations of+-- edges and even cycles, but that doesn't really matter here, it's only a+-- heuristic.+--+topologicalSortNumbering :: PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> (PackageName -> TopologicalSortNumber)+topologicalSortNumbering installed available =+ \pkgname -> let Just vertex = toVertex pkgname+ in topologicalSortNumbers Array.! vertex+ where+ topologicalSortNumbers = Array.array (Array.bounds graph)+ (zip (Graph.topSort graph) [0..])+ (graph, _, toVertex) = Graph.graphFromEdges $+ [ ((), packageName pkg, nub deps)+ | pkgs@(pkg:_) <- PackageIndex.allPackagesByName installed+ , let deps = [ packageName dep+ | pkg' <- pkgs+ , dep <- depends pkg' ] ]+ ++ [ ((), packageName pkg, nub deps)+ | pkgs@(pkg:_) <- PackageIndex.allPackagesByName available+ , let deps = [ depName+ | AvailablePackage _ pkg' _ <- pkgs+ , Dependency depName _ <-+ buildDepends (flattenPackageDescription pkg') ] ]++-- | We don't need the entire index (which is rather large and costly if we+-- force it by examining the whole thing). So trace out the maximul subset of+-- each index that we could possibly ever need. Do this by flattening packages+-- and looking at the names of all possible dependencies.+--+selectNeededSubset :: PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> Set PackageName+ -> (PackageIndex InstalledPackage+ ,PackageIndex AvailablePackage)+selectNeededSubset installed available = select mempty mempty+ where+ select :: PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> Set PackageName+ -> (PackageIndex InstalledPackage+ ,PackageIndex AvailablePackage)+ select installed' available' remaining+ | Set.null remaining = (installed', available')+ | otherwise = select installed'' available'' remaining''+ where+ (next, remaining') = Set.deleteFindMin remaining+ moreInstalled = PackageIndex.lookupPackageName installed next+ moreAvailable = PackageIndex.lookupPackageName available next+ moreRemaining = -- we filter out packages already included in the indexes+ -- this avoids an infinite loop if a package depends on itself+ -- like base-3.0.3.0 with base-4.0.0.0+ filter notAlreadyIncluded+ $ [ packageName dep+ | pkg <- moreInstalled+ , dep <- depends pkg ]+ ++ [ name+ | AvailablePackage _ pkg _ <- moreAvailable+ , Dependency name _ <-+ buildDepends (flattenPackageDescription pkg) ]+ installed'' = foldl' (flip PackageIndex.insert) installed' moreInstalled+ available'' = foldl' (flip PackageIndex.insert) available' moreAvailable+ remaining'' = foldl' (flip Set.insert) remaining' moreRemaining+ notAlreadyIncluded name = null (PackageIndex.lookupPackageName installed' name)+ && null (PackageIndex.lookupPackageName available' name)++-- ------------------------------------------------------------+-- * Post processing the solution+-- ------------------------------------------------------------++finaliseSelectedPackages :: (PackageName -> PackagePreferences)+ -> SelectedPackages+ -> Constraints+ -> [PlanPackage]+finaliseSelectedPackages pref selected constraints =+ map finaliseSelected (PackageIndex.allPackages selected)+ where+ remainingChoices = Constraints.choices constraints+ finaliseSelected (InstalledOnly ipkg ) = finaliseInstalled ipkg+ finaliseSelected (AvailableOnly apkg) = finaliseAvailable Nothing apkg+ finaliseSelected (InstalledAndAvailable ipkg apkg) =+ case PackageIndex.lookupPackageId remainingChoices (packageId ipkg) of+ Nothing -> impossible --picked package not in constraints+ Just (AvailableOnly _) -> impossible --to constrain to avail only+ Just (InstalledOnly _) -> finaliseInstalled ipkg+ Just (InstalledAndAvailable _ _) -> finaliseAvailable (Just ipkg) apkg++ finaliseInstalled (InstalledPackageEx pkg _ _) = InstallPlan.PreExisting pkg+ finaliseAvailable mipkg (SemiConfiguredPackage pkg flags deps) =+ InstallPlan.Configured (ConfiguredPackage pkg flags deps')+ where+ deps' = map (packageId . pickRemaining mipkg) deps++ pickRemaining mipkg dep@(Dependency _name versionRange) =+ case PackageIndex.lookupDependency remainingChoices dep of+ [] -> impossible+ [pkg'] -> pkg'+ remaining -> assert (checkIsPaired remaining)+ $ maximumBy bestByPref remaining+ where+ -- We order candidate packages to pick for a dependency by these+ -- three factors. The last factor is just highest version wins.+ bestByPref =+ comparing (\p -> (isCurrent p, isPreferred p, packageVersion p))+ -- Is the package already used by the installed version of this+ -- package? If so we should pick that first. This stops us from doing+ -- silly things like deciding to rebuild haskell98 against base 3.+ isCurrent = case mipkg :: Maybe InstalledPackageEx of+ Nothing -> \_ -> False+ Just ipkg -> \p -> packageId p `elem` depends ipkg+ -- If there is no upper bound on the version range then we apply a+ -- preferred version according to the hackage or user's suggested+ -- version constraints. TODO: distinguish hacks from prefs+ bounded = boundedAbove versionRange+ isPreferred p+ | bounded = True -- any constant will do+ | otherwise = packageVersion p `withinRange` preferredVersions+ where (PackagePreferences preferredVersions _) = pref (packageName p)++ boundedAbove :: VersionRange -> Bool+ boundedAbove vr = case asVersionIntervals vr of+ [] -> True -- this is the inconsistent version range.+ intervals -> case last intervals of+ (_, UpperBound _ _) -> True+ (_, NoUpperBound ) -> False++ -- We really only expect to find more than one choice remaining when+ -- we're finalising a dependency on a paired package.+ checkIsPaired [p1, p2] =+ case Constraints.isPaired constraints (packageId p1) of+ Just p2' -> packageId p2' == packageId p2+ Nothing -> False+ checkIsPaired _ = False++-- | Improve an existing installation plan by, where possible, swapping+-- packages we plan to install with ones that are already installed.+-- This may add additional constraints due to the dependencies of installed+-- packages on other installed packages.+--+improvePlan :: PackageIndex InstalledPackage+ -> Constraints+ -> PackageIndex PlanPackage+ -> (PackageIndex PlanPackage, Constraints)+improvePlan installed constraints0 selected0 =+ foldl' improve (selected0, constraints0) (reverseTopologicalOrder selected0)+ where+ improve (selected, constraints) = fromMaybe (selected, constraints)+ . improvePkg selected constraints++ -- The idea is to improve the plan by swapping a configured package for+ -- an equivalent installed one. For a particular package the condition is+ -- that the package be in a configured state, that a the same version be+ -- already installed with the exact same dependencies and all the packages+ -- in the plan that it depends on are in the installed state+ improvePkg selected constraints pkgid = do+ Configured pkg <- PackageIndex.lookupPackageId selected pkgid+ ipkg <- PackageIndex.lookupPackageId installed pkgid+ guard $ all (isInstalled selected) (depends pkg)+ tryInstalled selected constraints [ipkg]++ isInstalled selected pkgid =+ case PackageIndex.lookupPackageId selected pkgid of+ Just (PreExisting _) -> True+ _ -> False++ tryInstalled :: PackageIndex PlanPackage -> Constraints+ -> [InstalledPackage]+ -> Maybe (PackageIndex PlanPackage, Constraints)+ tryInstalled selected constraints [] = Just (selected, constraints)+ tryInstalled selected constraints (pkg:pkgs) =+ case constraintsOk (packageId pkg) (depends pkg) constraints of+ Nothing -> Nothing+ Just constraints' -> tryInstalled selected' constraints' pkgs'+ where+ selected' = PackageIndex.insert (PreExisting pkg) selected+ pkgs' = catMaybes (map notSelected (depends pkg)) ++ pkgs+ notSelected pkgid =+ case (PackageIndex.lookupPackageId installed pkgid+ ,PackageIndex.lookupPackageId selected pkgid) of+ (Just pkg', Nothing) -> Just pkg'+ _ -> Nothing++ constraintsOk _ [] constraints = Just constraints+ constraintsOk pkgid (pkgid':pkgids) constraints =+ case addPackageDependencyConstraint pkgid dep constraints of+ Satisfiable constraints' _ -> constraintsOk pkgid pkgids constraints'+ _ -> Nothing+ where+ dep = TaggedDependency InstalledConstraint (thisPackageVersion pkgid')++ reverseTopologicalOrder :: PackageFixedDeps pkg+ => PackageIndex pkg -> [PackageIdentifier]+ reverseTopologicalOrder index = map (packageId . toPkg)+ . Graph.topSort+ . Graph.transposeG+ $ graph+ where (graph, toPkg, _) = PackageIndex.dependencyGraph index++-- ------------------------------------------------------------+-- * Adding and recording constraints+-- ------------------------------------------------------------++addPackageSelectConstraint :: PackageIdentifier -> Constraints+ -> Satisfiable Constraints+ [PackageIdentifier] ExclusionReason+addPackageSelectConstraint pkgid constraints =+ Constraints.constrain dep reason constraints+ where+ dep = TaggedDependency NoInstalledConstraint (thisPackageVersion pkgid)+ reason = SelectedOther pkgid++addPackageExcludeConstraint :: PackageIdentifier -> Constraints+ -> Satisfiable Constraints+ [PackageIdentifier] ExclusionReason+addPackageExcludeConstraint pkgid constraints =+ Constraints.constrain dep reason constraints+ where+ dep = TaggedDependency NoInstalledConstraint+ (notThisPackageVersion pkgid)+ reason = ExcludedByConfigureFail++addPackageDependencyConstraint :: PackageIdentifier -> TaggedDependency -> Constraints+ -> Satisfiable Constraints+ [PackageIdentifier] ExclusionReason+addPackageDependencyConstraint pkgid dep constraints =+ Constraints.constrain dep reason constraints+ where+ reason = ExcludedByPackageDependency pkgid dep++addTopLevelVersionConstraint :: PackageName -> VersionRange+ -> Constraints+ -> Satisfiable Constraints+ [PackageIdentifier] ExclusionReason+addTopLevelVersionConstraint pkg ver constraints =+ Constraints.constrain taggedDep reason constraints+ where+ dep = Dependency pkg ver+ taggedDep = TaggedDependency NoInstalledConstraint dep+ reason = ExcludedByTopLevelDependency dep++addTopLevelInstalledConstraint :: PackageName+ -> Constraints+ -> Satisfiable Constraints+ [PackageIdentifier] ExclusionReason+addTopLevelInstalledConstraint pkg constraints =+ Constraints.constrain taggedDep reason constraints+ where+ dep = Dependency pkg anyVersion+ taggedDep = TaggedDependency InstalledConstraint dep+ reason = ExcludedByTopLevelDependency dep++-- ------------------------------------------------------------+-- * Reasons for constraints+-- ------------------------------------------------------------++-- | For every constraint we record we also record the reason that constraint+-- is needed. So if we end up failing due to conflicting constraints then we+-- can give an explnanation as to what was conflicting and why.+--+data ExclusionReason =++ -- | We selected this other version of the package. That means we exclude+ -- all the other versions.+ SelectedOther PackageIdentifier++ -- | We excluded this version of the package because it failed to+ -- configure probably because of unsatisfiable deps.+ | ExcludedByConfigureFail++ -- | We excluded this version of the package because another package that+ -- we selected imposed a dependency which this package did not satisfy.+ | ExcludedByPackageDependency PackageIdentifier TaggedDependency++ -- | We excluded this version of the package because it did not satisfy+ -- a dependency given as an original top level input.+ --+ | ExcludedByTopLevelDependency Dependency++-- | Given an excluded package and the reason it was excluded, produce a human+-- readable explanation.+--+showExclusionReason :: PackageIdentifier -> ExclusionReason -> String+showExclusionReason pkgid (SelectedOther pkgid') =+ display pkgid ++ " was excluded because " +++ display pkgid' ++ " was selected instead"+showExclusionReason pkgid ExcludedByConfigureFail =+ display pkgid ++ " was excluded because it could not be configured"+showExclusionReason pkgid (ExcludedByPackageDependency pkgid' dep) =+ display pkgid ++ " was excluded because " +++ display pkgid' ++ " requires " ++ displayDep (untagDependency dep)+showExclusionReason pkgid (ExcludedByTopLevelDependency dep) =+ display pkgid ++ " was excluded because of the top level dependency " +++ displayDep dep+++-- ------------------------------------------------------------+-- * Logging progress and failures+-- ------------------------------------------------------------++data Log = Select [SelectedPackage] [PackageIdentifier]+data Failure+ = ConfigureFailed+ SelectablePackage+ [(Dependency, [(PackageIdentifier, [ExclusionReason])])]+ | DependencyConflict+ SelectedPackage TaggedDependency+ [(PackageIdentifier, [ExclusionReason])]+ | TopLevelVersionConstraintConflict+ PackageName VersionRange+ [(PackageIdentifier, [ExclusionReason])]+ | TopLevelVersionConstraintUnsatisfiable+ PackageName VersionRange+ | TopLevelInstallConstraintConflict+ PackageName+ [(PackageIdentifier, [ExclusionReason])]+ | TopLevelInstallConstraintUnsatisfiable+ PackageName++showLog :: Log -> String+showLog (Select selected discarded) = case (selectedMsg, discardedMsg) of+ ("", y) -> y+ (x, "") -> x+ (x, y) -> x ++ " and " ++ y++ where+ selectedMsg = "selecting " ++ case selected of+ [] -> ""+ [s] -> display (packageId s) ++ " " ++ kind s+ (s:ss) -> listOf id+ $ (display (packageId s) ++ " " ++ kind s)+ : [ display (packageVersion s') ++ " " ++ kind s'+ | s' <- ss ]++ kind (InstalledOnly _) = "(installed)"+ kind (AvailableOnly _) = "(hackage)"+ kind (InstalledAndAvailable _ _) = "(installed or hackage)"++ discardedMsg = case discarded of+ [] -> ""+ _ -> "discarding " ++ listOf id+ [ element+ | (pkgid:pkgids) <- groupBy (equating packageName) (sort discarded)+ , element <- display pkgid : map (display . packageVersion) pkgids ]++showFailure :: Failure -> String+showFailure (ConfigureFailed pkg missingDeps) =+ "cannot configure " ++ displayPkg pkg ++ ". It requires "+ ++ listOf (displayDep . fst) missingDeps+ ++ '\n' : unlines (map (uncurry whyNot) missingDeps)++ where+ whyNot (Dependency name ver) [] =+ "There is no available version of " ++ display name+ ++ " that satisfies " ++ displayVer ver++ whyNot dep conflicts =+ "For the dependency on " ++ displayDep dep+ ++ " there are these packages: " ++ listOf display pkgs+ ++ ". However none of them are available.\n"+ ++ unlines [ showExclusionReason (packageId pkg') reason+ | (pkg', reasons) <- conflicts, reason <- reasons ]++ where pkgs = map fst conflicts++showFailure (DependencyConflict pkg (TaggedDependency _ dep) conflicts) =+ "dependencies conflict: "+ ++ displayPkg pkg ++ " requires " ++ displayDep dep ++ " however\n"+ ++ unlines [ showExclusionReason (packageId pkg') reason+ | (pkg', reasons) <- conflicts, reason <- reasons ]++showFailure (TopLevelVersionConstraintConflict name ver conflicts) =+ "constraints conflict: "+ ++ "top level constraint " ++ displayDep (Dependency name ver) ++ " however\n"+ ++ unlines [ showExclusionReason (packageId pkg') reason+ | (pkg', reasons) <- conflicts, reason <- reasons ]++showFailure (TopLevelVersionConstraintUnsatisfiable name ver) =+ "There is no available version of " ++ display name+ ++ " that satisfies " ++ displayVer ver++showFailure (TopLevelInstallConstraintConflict name conflicts) =+ "constraints conflict: "+ ++ "top level constraint " ++ display name ++ "-installed however\n"+ ++ unlines [ showExclusionReason (packageId pkg') reason+ | (pkg', reasons) <- conflicts, reason <- reasons ]++showFailure (TopLevelInstallConstraintUnsatisfiable name) =+ "There is no installed version of " ++ display name++displayVer :: VersionRange -> String+displayVer = display . simplifyVersionRange++displayDep :: Dependency -> String+displayDep = display . simplifyDependency++simplifyDependency :: Dependency -> Dependency+simplifyDependency (Dependency name range) =+ Dependency name (simplifyVersionRange range)++-- ------------------------------------------------------------+-- * Utils+-- ------------------------------------------------------------++impossible :: a+impossible = internalError "impossible"++internalError :: String -> a+internalError msg = error $ "internal error: " ++ msg++displayPkg :: Package pkg => pkg -> String+displayPkg = display . packageId++listOf :: (a -> String) -> [a] -> String+listOf _ [] = []+listOf disp [x0] = disp x0+listOf disp (x0:x1:xs) = disp x0 ++ go x1 xs+ where go x [] = " and " ++ disp x+ go x (x':xs') = ", " ++ disp x ++ go x' xs'
+ Distribution/Client/Dependency/TopDown/Constraints.hs view
@@ -0,0 +1,316 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Dependency.TopDown.Constraints+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : duncan@community.haskell.org+-- Stability : provisional+-- Portability : portable+--+-- A set of satisfiable dependencies (package version constraints).+-----------------------------------------------------------------------------+module Distribution.Client.Dependency.TopDown.Constraints (+ Constraints,+ empty,+ choices,+ isPaired,++ constrain,+ Satisfiable(..),+ conflicting,+ ) where++import Distribution.Client.Dependency.TopDown.Types+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Package+ ( PackageName, PackageIdentifier(..)+ , Package(packageId), packageName, packageVersion+ , PackageFixedDeps(depends)+ , Dependency(Dependency) )+import Distribution.Version+ ( Version, withinRange )+import Distribution.Client.Utils+ ( mergeBy, MergeResult(..) )++import Data.List+ ( foldl' )+import Data.Monoid+ ( Monoid(mempty) )+import Data.Maybe+ ( catMaybes )+import qualified Data.Map as Map+import Data.Map (Map)+import Control.Exception+ ( assert )++-- | A set of constraints on package versions. For each package name we record+-- what other packages depends on it and what constraints they impose on the+-- version of the package.+--+data (Package installed, Package available)+ => Constraints installed available reason+ = Constraints++ -- Remaining available choices+ (PackageIndex (InstalledOrAvailable installed available))++ -- Paired choices+ (Map PackageName (Version, Version))++ -- Choices that we have excluded for some reason+ -- usually by applying constraints+ (PackageIndex (ExcludedPackage PackageIdentifier reason))++ -- Purely for the invariant, we keep a copy of the original index+ (PackageIndex (InstalledOrAvailable installed available))+++data ExcludedPackage pkg reason+ = ExcludedPackage pkg [reason] -- reasons for excluding just the available+ [reason] -- reasons for excluding installed and avail++instance Package pkg => Package (ExcludedPackage pkg reason) where+ packageId (ExcludedPackage p _ _) = packageId p++-- | There is a conservation of packages property. Packages are never gained or+-- lost, they just transfer from the remaining pot to the excluded pot.+--+invariant :: (Package installed, Package available)+ => Constraints installed available a -> Bool+invariant (Constraints available _ excluded original) = all check merged+ where+ merged = mergeBy (\a b -> packageId a `compare` mergedPackageId b)+ (PackageIndex.allPackages original)+ (mergeBy (\a b -> packageId a `compare` packageId b)+ (PackageIndex.allPackages available)+ (PackageIndex.allPackages excluded))+ where+ mergedPackageId (OnlyInLeft p ) = packageId p+ mergedPackageId (OnlyInRight p) = packageId p+ mergedPackageId (InBoth p _) = packageId p++ check (InBoth (InstalledOnly _) cur) = case cur of+ -- If the package was originally installed only then+ -- now it's either still remaining as installed only+ -- or it has been excluded in which case we excluded both+ -- installed and available since it was only installed+ OnlyInLeft (InstalledOnly _) -> True+ OnlyInRight (ExcludedPackage _ [] (_:_)) -> True+ _ -> False++ check (InBoth (AvailableOnly _) cur) = case cur of+ -- If the package was originally available only then+ -- now it's either still remaining as available only+ -- or it has been excluded in which case we excluded both+ -- installed and available since it was only available+ OnlyInLeft (AvailableOnly _) -> True+ OnlyInRight (ExcludedPackage _ [] (_:_)) -> True+ _ -> True++ -- If the package was originally installed and available+ -- then there are three cases.+ check (InBoth (InstalledAndAvailable _ _) cur) = case cur of+ -- We can have both remaining:+ OnlyInLeft (InstalledAndAvailable _ _) -> True+ -- both excluded, in particular it can have had the available excluded+ -- and later had both excluded so we do not mind if the available excluded+ -- is empty or non-empty.+ OnlyInRight (ExcludedPackage _ _ (_:_)) -> True+ -- the installed remaining and the available excluded:+ InBoth (InstalledOnly _) (ExcludedPackage _ (_:_) []) -> True+ _ -> False++ check _ = False++-- | An update to the constraints can move packages between the two piles+-- but not gain or loose packages.+transitionsTo :: (Package installed, Package available)+ => Constraints installed available a+ -> Constraints installed available a -> Bool+transitionsTo constraints @(Constraints available _ excluded _)+ constraints'@(Constraints available' _ excluded' _) =+ invariant constraints && invariant constraints'+ && null availableGained && null excludedLost+ && map packageId availableLost == map packageId excludedGained++ where+ availableLost = foldr lost [] availableChange where+ lost (OnlyInLeft pkg) rest = pkg : rest+ lost (InBoth (InstalledAndAvailable _ pkg)+ (InstalledOnly _)) rest = AvailableOnly pkg : rest+ lost _ rest = rest+ availableGained = [ pkg | OnlyInRight pkg <- availableChange ]+ excludedLost = [ pkg | OnlyInLeft pkg <- excludedChange ]+ excludedGained = [ pkg | OnlyInRight pkg <- excludedChange ]+ ++ [ pkg | InBoth (ExcludedPackage _ (_:_) [])+ pkg@(ExcludedPackage _ (_:_) (_:_))+ <- excludedChange ]+ availableChange = mergeBy (\a b -> packageId a `compare` packageId b)+ (PackageIndex.allPackages available)+ (PackageIndex.allPackages available')+ excludedChange = mergeBy (\a b -> packageId a `compare` packageId b)+ (PackageIndex.allPackages excluded)+ (PackageIndex.allPackages excluded')++-- | We construct 'Constraints' with an initial 'PackageIndex' of all the+-- packages available.+--+empty :: (PackageFixedDeps installed, Package available)+ => PackageIndex installed+ -> PackageIndex available+ -> Constraints installed available reason+empty installed available = Constraints pkgs pairs mempty pkgs+ where+ pkgs = PackageIndex.fromList+ . map toInstalledOrAvailable+ $ mergeBy (\a b -> packageId a `compare` packageId b)+ (PackageIndex.allPackages installed)+ (PackageIndex.allPackages available)+ toInstalledOrAvailable (OnlyInLeft i ) = InstalledOnly i+ toInstalledOrAvailable (OnlyInRight a) = AvailableOnly a+ toInstalledOrAvailable (InBoth i a) = InstalledAndAvailable i a++ -- pick up cases like base-3 and 4 where one version depends on the other:+ pairs = Map.fromList+ [ (name, (packageVersion pkgid1, packageVersion pkgid2))+ | [pkg1, pkg2] <- PackageIndex.allPackagesByName installed+ , let name = packageName pkg1+ pkgid1 = packageId pkg1+ pkgid2 = packageId pkg2+ , any ((pkgid1==) . packageId) (depends pkg2)+ || any ((pkgid2==) . packageId) (depends pkg1) ]++-- | The package choices that are still available.+--+choices :: (Package installed, Package available)+ => Constraints installed available reason+ -> PackageIndex (InstalledOrAvailable installed available)+choices (Constraints available _ _ _) = available++isPaired :: (Package installed, Package available)+ => Constraints installed available reason+ -> PackageIdentifier -> Maybe PackageIdentifier+isPaired (Constraints _ pairs _ _) (PackageIdentifier name version) =+ case Map.lookup name pairs of+ Just (v1, v2)+ | version == v1 -> Just (PackageIdentifier name v2)+ | version == v2 -> Just (PackageIdentifier name v1)+ _ -> Nothing++data Satisfiable constraints discarded reason+ = Satisfiable constraints discarded+ | Unsatisfiable+ | ConflictsWith [(PackageIdentifier, [reason])]++constrain :: (Package installed, Package available)+ => TaggedDependency+ -> reason+ -> Constraints installed available reason+ -> Satisfiable (Constraints installed available reason)+ [PackageIdentifier] reason+constrain (TaggedDependency installedConstraint (Dependency name versionRange))+ reason constraints@(Constraints available paired excluded original)++ | not anyRemaining+ = if null conflicts then Unsatisfiable+ else ConflictsWith conflicts++ | otherwise+ = let constraints' = Constraints available' paired excluded' original+ in assert (constraints `transitionsTo` constraints') $+ Satisfiable constraints' (map packageId newExcluded)++ where+ -- This tells us if any packages would remain at all for this package name if+ -- we applied this constraint. This amounts to checking if any package+ -- satisfies the given constraint, including version range and installation+ -- status.+ --+ anyRemaining = any satisfiesConstraint availableChoices++ conflicts = [ (packageId pkg, reasonsAvail ++ reasonsAll)+ | ExcludedPackage pkg reasonsAvail reasonsAll <- excludedChoices+ , satisfiesVersionConstraint pkg ]++ -- Applying this constraint may involve deleting some choices for this+ -- package name, or restricting which install states are available.+ available' = updateAvailable available+ updateAvailable = flip (foldl' (flip update)) availableChoices where+ update pkg | not (satisfiesVersionConstraint pkg)+ = PackageIndex.deletePackageId (packageId pkg)+ update _ | installedConstraint == NoInstalledConstraint+ = id+ update pkg = case pkg of+ InstalledOnly _ -> id+ AvailableOnly _ -> PackageIndex.deletePackageId (packageId pkg)+ InstalledAndAvailable i _ -> PackageIndex.insert (InstalledOnly i)++ -- Applying the constraint means adding exclusions for the packages that+ -- we're just freshly excluding, ie the ones we're removing from available.+ excluded' = foldl' (flip PackageIndex.insert) excluded+ (newExcluded ++ oldExcluded)++ newExcluded = catMaybes (map exclude availableChoices) where+ exclude pkg+ | not (satisfiesVersionConstraint pkg)+ = Just (ExcludedPackage pkgid [] [reason])+ | installedConstraint == NoInstalledConstraint+ = Nothing+ | otherwise = case pkg of+ InstalledOnly _ -> Nothing+ AvailableOnly _ -> Just (ExcludedPackage pkgid [] [reason])+ InstalledAndAvailable _ _ ->+ case PackageIndex.lookupPackageId excluded pkgid of+ Just (ExcludedPackage _ avail both)+ -> Just (ExcludedPackage pkgid (reason:avail) both)+ Nothing -> Just (ExcludedPackage pkgid [reason] [])+ where pkgid = packageId pkg++ -- Additionally we have to add extra exclusions for any already-excluded+ -- packages that happen to be covered by the (inverse of the) constraint.+ oldExcluded = catMaybes (map exclude excludedChoices) where+ exclude (ExcludedPackage pkgid avail both)+ -- if it doesn't satisfy the version constraint then we exclude the+ -- package as a whole, the available or the installed instances or both.+ | not (satisfiesVersionConstraint pkgid)+ = Just (ExcludedPackage pkgid avail (reason:both))+ -- if on the other hand it does satisfy the constraint and we were also+ -- constraining to just the installed version then we exclude just the+ -- available instance.+ | installedConstraint == InstalledConstraint+ = Just (ExcludedPackage pkgid (reason:avail) both)+ | otherwise = Nothing++ -- util definitions+ availableChoices = PackageIndex.lookupPackageName available name+ excludedChoices = PackageIndex.lookupPackageName excluded name++ satisfiesConstraint pkg = satisfiesVersionConstraint pkg+ && satisfiesInstallStateConstraint pkg++ satisfiesVersionConstraint :: Package pkg => pkg -> Bool+ satisfiesVersionConstraint = case Map.lookup name paired of+ Nothing -> \pkg ->+ packageVersion pkg `withinRange` versionRange+ Just (v1, v2) -> \pkg -> case packageVersion pkg of+ v | v == v1+ || v == v2 -> v1 `withinRange` versionRange+ || v2 `withinRange` versionRange+ | otherwise -> v `withinRange` versionRange++ satisfiesInstallStateConstraint = case installedConstraint of+ NoInstalledConstraint -> \_ -> True+ InstalledConstraint -> \pkg -> case pkg of+ AvailableOnly _ -> False+ _ -> True++conflicting :: (Package installed, Package available)+ => Constraints installed available reason+ -> Dependency+ -> [(PackageIdentifier, [reason])]+conflicting (Constraints _ _ excluded _) dep =+ [ (pkgid, reasonsAvail ++ reasonsAll) --TODO+ | ExcludedPackage pkgid reasonsAvail reasonsAll <-+ PackageIndex.lookupDependency excluded dep ]
+ Distribution/Client/Dependency/TopDown/Types.hs view
@@ -0,0 +1,93 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Dependency.TopDown.Types+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Types for the top-down dependency resolver.+-----------------------------------------------------------------------------+module Distribution.Client.Dependency.TopDown.Types where++import Distribution.Client.Types+ ( AvailablePackage(..), InstalledPackage )++import Distribution.Package+ ( PackageIdentifier, Dependency+ , Package(packageId), PackageFixedDeps(depends) )+import Distribution.PackageDescription+ ( FlagAssignment )++-- ------------------------------------------------------------+-- * The various kinds of packages+-- ------------------------------------------------------------++type SelectablePackage+ = InstalledOrAvailable InstalledPackageEx UnconfiguredPackage++type SelectedPackage+ = InstalledOrAvailable InstalledPackageEx SemiConfiguredPackage++data InstalledOrAvailable installed available+ = InstalledOnly installed+ | AvailableOnly available+ | InstalledAndAvailable installed available++type TopologicalSortNumber = Int++data InstalledPackageEx+ = InstalledPackageEx+ InstalledPackage+ !TopologicalSortNumber+ [PackageIdentifier] -- transative closure of installed deps++data UnconfiguredPackage+ = UnconfiguredPackage+ AvailablePackage+ !TopologicalSortNumber+ FlagAssignment++data SemiConfiguredPackage+ = SemiConfiguredPackage+ AvailablePackage -- package info+ FlagAssignment -- total flag assignment for the package+ [Dependency] -- dependencies we end up with when we apply+ -- the flag assignment++instance Package InstalledPackageEx where+ packageId (InstalledPackageEx p _ _) = packageId p++instance PackageFixedDeps InstalledPackageEx where+ depends (InstalledPackageEx _ _ deps) = deps++instance Package UnconfiguredPackage where+ packageId (UnconfiguredPackage p _ _) = packageId p++instance Package SemiConfiguredPackage where+ packageId (SemiConfiguredPackage p _ _) = packageId p++instance (Package installed, Package available)+ => Package (InstalledOrAvailable installed available) where+ packageId (InstalledOnly p ) = packageId p+ packageId (AvailableOnly p ) = packageId p+ packageId (InstalledAndAvailable p _) = packageId p++-- ------------------------------------------------------------+-- * Tagged Dependency type+-- ------------------------------------------------------------++-- | Installed packages can only depend on other installed packages while+-- packages that are not yet installed but which we plan to install can depend+-- on installed or other not-yet-installed packages.+--+-- This makes life more complex as we have to remember these constraints.+--+data TaggedDependency = TaggedDependency InstalledConstraint Dependency+data InstalledConstraint = InstalledConstraint | NoInstalledConstraint+ deriving Eq++untagDependency :: TaggedDependency -> Dependency+untagDependency (TaggedDependency _ dep) = dep
+ Distribution/Client/Dependency/Types.hs view
@@ -0,0 +1,115 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Dependency.Types+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Common types for dependency resolution.+-----------------------------------------------------------------------------+module Distribution.Client.Dependency.Types (+ DependencyResolver,++ PackageConstraint(..),+ PackagePreferences(..),+ InstalledPreference(..),++ Progress(..),+ foldProgress,+ ) where++import Distribution.Client.Types+ ( AvailablePackage(..), InstalledPackage )+import qualified Distribution.Client.InstallPlan as InstallPlan++import Distribution.PackageDescription+ ( FlagAssignment )+import Distribution.Client.PackageIndex+ ( PackageIndex )+import Distribution.Package+ ( PackageName )+import Distribution.Version+ ( VersionRange )+import Distribution.Compiler+ ( CompilerId )+import Distribution.System+ ( Platform )++import Prelude hiding (fail)++-- | A dependency resolver is a function that works out an installation plan+-- given the set of installed and available packages and a set of deps to+-- solve for.+--+-- The reason for this interface is because there are dozens of approaches to+-- solving the package dependency problem and we want to make it easy to swap+-- in alternatives.+--+type DependencyResolver = Platform+ -> CompilerId+ -> PackageIndex InstalledPackage+ -> PackageIndex AvailablePackage+ -> (PackageName -> PackagePreferences)+ -> [PackageConstraint]+ -> [PackageName]+ -> Progress String String [InstallPlan.PlanPackage]++-- | Per-package constraints. Package constraints must be respected by the+-- solver. Multiple constraints for each package can be given, though obviously+-- it is possible to construct conflicting constraints (eg impossible version+-- range or inconsistent flag assignment).+--+data PackageConstraint+ = PackageVersionConstraint PackageName VersionRange+ | PackageInstalledConstraint PackageName+ | PackageFlagsConstraint PackageName FlagAssignment+ deriving (Show,Eq)++-- | A per-package preference on the version. It is a soft constraint that the+-- 'DependencyResolver' should try to respect where possible. It consists of+-- a 'InstalledPreference' which says if we prefer versions of packages+-- that are already installed. It also hase a 'PackageVersionPreference' which+-- is a suggested constraint on the version number. The resolver should try to+-- use package versions that satisfy the suggested version constraint.+--+-- It is not specified if preferences on some packages are more important than+-- others.+--+data PackagePreferences = PackagePreferences VersionRange InstalledPreference++-- | Wether we prefer an installed version of a package or simply the latest+-- version.+--+data InstalledPreference = PreferInstalled | PreferLatest++-- | A type to represent the unfolding of an expensive long running+-- calculation that may fail. We may get intermediate steps before the final+-- retult which may be used to indicate progress and\/or logging messages.+--+data Progress step fail done = Step step (Progress step fail done)+ | Fail fail+ | Done done++-- | Consume a 'Progres' calculation. Much like 'foldr' for lists but with+-- two base cases, one for a final result and one for failure.+--+-- Eg to convert into a simple 'Either' result use:+--+-- > foldProgress (flip const) Left Right+--+foldProgress :: (step -> a -> a) -> (fail -> a) -> (done -> a)+ -> Progress step fail done -> a+foldProgress step fail done = fold+ where fold (Step s p) = step s (fold p)+ fold (Fail f) = fail f+ fold (Done r) = done r++instance Functor (Progress step fail) where+ fmap f = foldProgress Step Fail (Done . f)++instance Monad (Progress step fail) where+ return a = Done a+ p >>= f = foldProgress Step Fail f p
+ Distribution/Client/Fetch.hs view
@@ -0,0 +1,171 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Fetch+-- Copyright : (c) David Himmelstrup 2005+-- Duncan Coutts 2011+-- License : BSD-like+--+-- Maintainer : cabal-devel@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- The cabal fetch command+-----------------------------------------------------------------------------+module Distribution.Client.Fetch (+ fetch,+ ) where++import Distribution.Client.Types+import Distribution.Client.Targets+import Distribution.Client.FetchUtils hiding (fetchPackage)+import Distribution.Client.Dependency+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Client.IndexUtils as IndexUtils+ ( getAvailablePackages, getInstalledPackages )+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.Setup+ ( GlobalFlags(..), FetchFlags(..) )++import Distribution.Package+ ( packageId )+import Distribution.Simple.Compiler+ ( Compiler(compilerId), PackageDBStack )+import Distribution.Simple.Program+ ( ProgramConfiguration )+import Distribution.Simple.Setup+ ( fromFlag )+import Distribution.Simple.Utils+ ( die, notice, debug )+import Distribution.System+ ( buildPlatform )+import Distribution.Text+ ( display )+import Distribution.Verbosity+ ( Verbosity )++import Control.Monad+ ( filterM )++-- ------------------------------------------------------------+-- * The fetch command+-- ------------------------------------------------------------++--TODO:+-- * add fetch -o support+-- * support tarball URLs via ad-hoc download cache (or in -o mode?)+-- * suggest using --no-deps, unpack or fetch -o if deps cannot be satisfied+-- * Port various flags from install:+-- * --updage-dependencies+-- * --constraint and --preference+-- * --only-dependencies, but note it conflicts with --no-deps+++-- | Fetch a list of packages and their dependencies.+--+fetch :: Verbosity+ -> PackageDBStack+ -> [Repo]+ -> Compiler+ -> ProgramConfiguration+ -> GlobalFlags+ -> FetchFlags+ -> [UserTarget]+ -> IO ()+fetch verbosity _ _ _ _ _ _ [] =+ notice verbosity "No packages requested. Nothing to do."++fetch verbosity packageDBs repos comp conf+ globalFlags fetchFlags userTargets = do++ mapM_ checkTarget userTargets++ installed <- getInstalledPackages verbosity comp packageDBs conf+ availableDb <- getAvailablePackages verbosity repos++ pkgSpecifiers <- resolveUserTargets verbosity+ globalFlags (packageIndex availableDb) userTargets++ pkgs <- planPackages+ verbosity comp fetchFlags+ installed availableDb pkgSpecifiers++ pkgs' <- filterM (fmap not . isFetched . packageSource) pkgs+ if null pkgs'+ --TODO: when we add support for remote tarballs then this message+ -- will need to be changed because for remote tarballs we fetch them+ -- at the earlier phase.+ then notice verbosity $ "No packages need to be fetched. "+ ++ "All the requested packages are already local "+ ++ "or cached locally."+ else if dryRun+ then notice verbosity $ unlines $+ "The following packages would be fetched:"+ : map (display . packageId) pkgs'++ else mapM_ (fetchPackage verbosity . packageSource) pkgs'++ where+ dryRun = fromFlag (fetchDryRun fetchFlags)++planPackages :: Verbosity+ -> Compiler+ -> FetchFlags+ -> PackageIndex InstalledPackage+ -> AvailablePackageDb+ -> [PackageSpecifier AvailablePackage]+ -> IO [AvailablePackage]+planPackages verbosity comp fetchFlags+ installed availableDb pkgSpecifiers++ | includeDependencies = do+ notice verbosity "Resolving dependencies..."+ installPlan <- foldProgress logMsg die return $+ resolveDependencies+ buildPlatform (compilerId comp)+ resolverParams++ -- The packages we want to fetch are those packages the 'InstallPlan'+ -- that are in the 'InstallPlan.Configured' state.+ return+ [ pkg+ | (InstallPlan.Configured (InstallPlan.ConfiguredPackage pkg _ _))+ <- InstallPlan.toList installPlan ]++ | otherwise =+ either (die . unlines . map show) return $+ resolveWithoutDependencies resolverParams++ where+ resolverParams =++ -- Reinstall the targets given on the command line so that the dep+ -- resolver will decide that they need fetching, even if they're+ -- already installed. Sicne we want to get the source packages of+ -- things we might have installed (but not have the sources for).+ reinstallTargets++ $ standardInstallPolicy installed availableDb pkgSpecifiers++ includeDependencies = fromFlag (fetchDeps fetchFlags)+ logMsg message rest = debug verbosity message >> rest+++checkTarget :: UserTarget -> IO ()+checkTarget target = case target of+ UserTargetRemoteTarball _uri+ -> die $ "The 'fetch' command does not yet support remote tarballs. "+ ++ "In the meantime you can use the 'unpack' commands."+ _ -> return ()++fetchPackage :: Verbosity -> PackageLocation a -> IO ()+fetchPackage verbosity pkgsrc = case pkgsrc of+ LocalUnpackedPackage _dir -> return ()+ LocalTarballPackage _file -> return ()++ RemoteTarballPackage _uri _ ->+ die $ "The 'fetch' command does not yet support remote tarballs. "+ ++ "In the meantime you can use the 'unpack' commands."++ RepoTarballPackage repo pkgid _ -> do+ _ <- fetchRepoTarball verbosity repo pkgid+ return ()
+ Distribution/Client/FetchUtils.hs view
@@ -0,0 +1,193 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.FetchUtils+-- Copyright : (c) David Himmelstrup 2005+-- Duncan Coutts 2011+-- License : BSD-like+--+-- Maintainer : cabal-devel@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Functions for fetching packages+-----------------------------------------------------------------------------+module Distribution.Client.FetchUtils (++ -- * fetching packages+ fetchPackage,+ isFetched,+ checkFetched,++ -- ** specifically for repo packages+ fetchRepoTarball,++ -- * fetching other things+ downloadIndex,+ ) where++import Distribution.Client.Types+import Distribution.Client.HttpUtils+ ( downloadURI, isOldHackageURI )++import Distribution.Package+ ( PackageId, packageName, packageVersion )+import Distribution.Simple.Utils+ ( notice, info, setupMessage )+import Distribution.Text+ ( display )+import Distribution.Verbosity+ ( Verbosity )++import Data.Maybe+import System.Directory+ ( doesFileExist, createDirectoryIfMissing, getTemporaryDirectory )+import System.IO+ ( openTempFile, hClose )+import System.FilePath+ ( (</>), (<.>) )+import qualified System.FilePath.Posix as FilePath.Posix+ ( combine, joinPath )+import Network.URI+ ( URI(uriPath) )++-- ------------------------------------------------------------+-- * Actually fetch things+-- ------------------------------------------------------------++-- | Returns @True@ if the package has already been fetched+-- or does not need fetching.+--+isFetched :: PackageLocation (Maybe FilePath) -> IO Bool+isFetched loc = case loc of+ LocalUnpackedPackage _dir -> return True+ LocalTarballPackage _file -> return True+ RemoteTarballPackage _uri local -> return (isJust local)+ RepoTarballPackage repo pkgid _ -> doesFileExist (packageFile repo pkgid)+++checkFetched :: PackageLocation (Maybe FilePath)+ -> IO (Maybe (PackageLocation FilePath))+checkFetched loc = case loc of+ LocalUnpackedPackage dir ->+ return (Just $ LocalUnpackedPackage dir)+ LocalTarballPackage file ->+ return (Just $ LocalTarballPackage file)+ RemoteTarballPackage uri (Just file) ->+ return (Just $ RemoteTarballPackage uri file)+ RepoTarballPackage repo pkgid (Just file) ->+ return (Just $ RepoTarballPackage repo pkgid file)++ RemoteTarballPackage _uri Nothing -> return Nothing+ RepoTarballPackage repo pkgid Nothing -> do+ let file = packageFile repo pkgid+ exists <- doesFileExist file+ if exists+ then return (Just $ RepoTarballPackage repo pkgid file)+ else return Nothing+++-- | Fetch a package if we don't have it already.+--+fetchPackage :: Verbosity+ -> PackageLocation (Maybe FilePath)+ -> IO (PackageLocation FilePath)+fetchPackage verbosity loc = case loc of+ LocalUnpackedPackage dir ->+ return (LocalUnpackedPackage dir)+ LocalTarballPackage file ->+ return (LocalTarballPackage file)+ RemoteTarballPackage uri (Just file) ->+ return (RemoteTarballPackage uri file)+ RepoTarballPackage repo pkgid (Just file) ->+ return (RepoTarballPackage repo pkgid file)++ RemoteTarballPackage uri Nothing -> do+ path <- downloadTarballPackage uri+ return (RemoteTarballPackage uri path)+ RepoTarballPackage repo pkgid Nothing -> do+ local <- fetchRepoTarball verbosity repo pkgid+ return (RepoTarballPackage repo pkgid local)+ where+ downloadTarballPackage uri = do+ notice verbosity ("Downloading " ++ show uri)+ tmpdir <- getTemporaryDirectory+ (path, hnd) <- openTempFile tmpdir "cabal-.tar.gz"+ hClose hnd+ downloadURI verbosity uri path+ return path+++-- | Fetch a repo package if we don't have it already.+--+fetchRepoTarball :: Verbosity -> Repo -> PackageId -> IO FilePath+fetchRepoTarball verbosity repo pkgid = do+ fetched <- doesFileExist (packageFile repo pkgid)+ if fetched+ then do info verbosity $ display pkgid ++ " has already been downloaded."+ return (packageFile repo pkgid)+ else do setupMessage verbosity "Downloading" pkgid+ downloadRepoPackage+ where+ downloadRepoPackage = case repoKind repo of+ Right LocalRepo -> return (packageFile repo pkgid)++ Left remoteRepo -> do+ let uri = packageURI remoteRepo pkgid+ dir = packageDir repo pkgid+ path = packageFile repo pkgid+ createDirectoryIfMissing True dir+ downloadURI verbosity uri path+ return path++-- | Downloads an index file to [config-dir/packages/serv-id].+--+downloadIndex :: Verbosity -> RemoteRepo -> FilePath -> IO FilePath+downloadIndex verbosity repo cacheDir = do+ let uri = (remoteRepoURI repo) {+ uriPath = uriPath (remoteRepoURI repo)+ `FilePath.Posix.combine` "00-index.tar.gz"+ }+ path = cacheDir </> "00-index" <.> "tar.gz"+ createDirectoryIfMissing True cacheDir+ downloadURI verbosity uri path+ return path+++-- ------------------------------------------------------------+-- * Path utilities+-- ------------------------------------------------------------++-- | Generate the full path to the locally cached copy of+-- the tarball for a given @PackageIdentifer@.+--+packageFile :: Repo -> PackageId -> FilePath+packageFile repo pkgid = packageDir repo pkgid+ </> display pkgid+ <.> "tar.gz"++-- | Generate the full path to the directory where the local cached copy of+-- the tarball for a given @PackageIdentifer@ is stored.+--+packageDir :: Repo -> PackageId -> FilePath+packageDir repo pkgid = repoLocalDir repo+ </> display (packageName pkgid)+ </> display (packageVersion pkgid)++-- | Generate the URI of the tarball for a given package.+--+packageURI :: RemoteRepo -> PackageId -> URI+packageURI repo pkgid | isOldHackageURI (remoteRepoURI repo) =+ (remoteRepoURI repo) {+ uriPath = FilePath.Posix.joinPath+ [uriPath (remoteRepoURI repo)+ ,display (packageName pkgid)+ ,display (packageVersion pkgid)+ ,display pkgid <.> "tar.gz"]+ }+packageURI repo pkgid =+ (remoteRepoURI repo) {+ uriPath = FilePath.Posix.joinPath+ [uriPath (remoteRepoURI repo)+ ,"package"+ ,display pkgid <.> "tar.gz"]+ }
+ Distribution/Client/GZipUtils.hs view
@@ -0,0 +1,44 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.GZipUtils+-- Copyright : (c) Dmitry Astapov 2010+-- License : BSD-like+--+-- Maintainer : cabal-devel@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Provides a convenience functions for working with files that may or may not+-- be zipped.+-----------------------------------------------------------------------------+module Distribution.Client.GZipUtils (+ maybeDecompress,+ ) where++import qualified Data.ByteString.Lazy.Internal as BS (ByteString(..))+import Data.ByteString.Lazy (ByteString)+import Codec.Compression.GZip+import Codec.Compression.Zlib.Internal++-- | Attempts to decompress the `bytes' under the assumption that+-- "data format" error at the very beginning of the stream means+-- that it is already decompressed. Caller should make sanity checks+-- to verify that it is not, in fact, garbage.+--+-- This is to deal with http proxies that lie to us and transparently+-- decompress without removing the content-encoding header. See:+-- <http://hackage.haskell.org/trac/hackage/ticket/686>+--+maybeDecompress :: ByteString -> ByteString+maybeDecompress bytes = foldStream $ decompressWithErrors gzipOrZlibFormat defaultDecompressParams bytes+ where+ -- DataError at the beginning of the stream probably means that stream is not compressed.+ -- Returning it as-is.+ -- TODO: alternatively, we might consider looking for the two magic bytes+ -- at the beginning of the gzip header.+ foldStream (StreamError DataError _) = bytes+ foldStream somethingElse = doFold somethingElse++ doFold StreamEnd = BS.Empty+ doFold (StreamChunk bs stream) = BS.Chunk bs (doFold stream)+ doFold (StreamError _ msg) = error $ "Codec.Compression.Zlib: " ++ msg
+ Distribution/Client/Haddock.hs view
@@ -0,0 +1,105 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Haddock+-- Copyright : (c) Andrea Vezzosi 2009+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Interfacing with Haddock+--+-----------------------------------------------------------------------------+module Distribution.Client.Haddock + (+ regenerateHaddockIndex+ )+ where++import Data.Maybe (listToMaybe)+import Data.List (maximumBy)+import Control.Monad (guard)+import System.Directory (createDirectoryIfMissing, doesFileExist,+ renameFile)+import System.FilePath ((</>), splitFileName)+import Distribution.Package (Package(..))+import Distribution.Simple.Program (haddockProgram, ProgramConfiguration+ , rawSystemProgram, requireProgramVersion)+import Distribution.Version (Version(Version), orLaterVersion)+import Distribution.Verbosity (Verbosity)+import Distribution.Text (display)+import Distribution.Client.PackageIndex(PackageIndex, allPackages,+ allPackagesByName, fromList)+import Distribution.Simple.Utils+ ( comparing, intercalate, debug+ , installDirectoryContents, withTempDirectory )+import Distribution.InstalledPackageInfo as InstalledPackageInfo + ( InstalledPackageInfo+ , InstalledPackageInfo_(haddockHTMLs, haddockInterfaces, exposed) )+import Distribution.Client.Types+ ( InstalledPackage(..) )++regenerateHaddockIndex :: Verbosity -> PackageIndex InstalledPackage -> ProgramConfiguration -> FilePath -> IO ()+regenerateHaddockIndex verbosity pkgs conf index = do+ (paths,warns) <- haddockPackagePaths pkgs'+ case warns of+ Nothing -> return ()+ Just m -> debug verbosity m+ + (confHaddock, _, _) <-+ requireProgramVersion verbosity haddockProgram+ (orLaterVersion (Version [0,6] [])) conf++ createDirectoryIfMissing True destDir++ withTempDirectory verbosity destDir "tmphaddock" $ \tempDir -> do++ let flags = [ "--gen-contents"+ , "--gen-index"+ , "--odir=" ++ tempDir+ , "--title=Haskell modules on this system" ]+ ++ [ "--read-interface=" ++ html ++ "," ++ interface+ | (interface, html) <- paths ]+ rawSystemProgram verbosity confHaddock flags+ renameFile (tempDir </> "index.html") (tempDir </> destFile)+ installDirectoryContents verbosity tempDir destDir+ + where + (destDir,destFile) = splitFileName index+ pkgs' = map (maximumBy $ comparing packageId) + . allPackagesByName + . fromList+ . filter exposed+ . map (\(InstalledPackage pkg _) -> pkg)+ . allPackages+ $ pkgs++haddockPackagePaths :: [InstalledPackageInfo]+ -> IO ([(FilePath, FilePath)], Maybe String)+haddockPackagePaths pkgs = do+ interfaces <- sequence+ [ case interfaceAndHtmlPath pkg of+ Just (interface, html) -> do+ exists <- doesFileExist interface+ if exists+ then return (pkgid, Just (interface, html))+ else return (pkgid, Nothing)+ Nothing -> return (pkgid, Nothing)+ | pkg <- pkgs, let pkgid = packageId pkg ]++ let missing = [ pkgid | (pkgid, Nothing) <- interfaces ]++ warning = "The documentation for the following packages are not "+ ++ "installed. No links will be generated to these packages: "+ ++ intercalate ", " (map display missing)++ flags = [ x | (_, Just x) <- interfaces ]++ return (flags, if null missing then Nothing else Just warning)++ where+ interfaceAndHtmlPath pkg = do+ interface <- listToMaybe (InstalledPackageInfo.haddockInterfaces pkg)+ html <- listToMaybe (InstalledPackageInfo.haddockHTMLs pkg)+ guard (not . null $ html)+ return (interface, html)
+ Distribution/Client/HttpUtils.hs view
@@ -0,0 +1,197 @@+{-# OPTIONS -cpp #-}+-----------------------------------------------------------------------------+-- | Separate module for HTTP actions, using a proxy server if one exists+-----------------------------------------------------------------------------+module Distribution.Client.HttpUtils (+ downloadURI,+ getHTTP,+ proxy,+ isOldHackageURI+ ) where++import Network.HTTP+ ( Request (..), Response (..), RequestMethod (..)+ , Header(..), HeaderName(..) )+import Network.URI+ ( URI (..), URIAuth (..), parseAbsoluteURI )+import Network.Stream+ ( Result, ConnError(..) )+import Network.Browser+ ( Proxy (..), Authority (..), browse+ , setOutHandler, setErrHandler, setProxy, request)+import Control.Monad+ ( mplus, join, liftM2 )+import qualified Data.ByteString.Lazy.Char8 as ByteString+import Data.ByteString.Lazy (ByteString)+#ifdef WIN32+import System.Win32.Types+ ( DWORD, HKEY )+import System.Win32.Registry+ ( hKEY_CURRENT_USER, regOpenKey, regCloseKey+ , regQueryValue, regQueryValueEx )+import Control.Exception+ ( bracket )+import Distribution.Compat.Exception+ ( handleIO )+import Foreign+ ( toBool, Storable(peek, sizeOf), castPtr, alloca )+#endif+import System.Environment (getEnvironment)++import qualified Paths_cabal_install_bundle (version)+import Distribution.Verbosity (Verbosity)+import Distribution.Simple.Utils+ ( die, info, warn, debug+ , copyFileVerbose, writeFileAtomic )+import Distribution.Text+ ( display )+import qualified System.FilePath.Posix as FilePath.Posix+ ( splitDirectories )++-- FIXME: all this proxy stuff is far too complicated, especially parsing+-- the proxy strings. Network.Browser should have a way to pick up the+-- proxy settings hiding all this system-dependent stuff below.++-- try to read the system proxy settings on windows or unix+proxyString, envProxyString, registryProxyString :: IO (Maybe String)+#ifdef WIN32+-- read proxy settings from the windows registry+registryProxyString = handleIO (\_ -> return Nothing) $+ bracket (regOpenKey hive path) regCloseKey $ \hkey -> do+ enable <- fmap toBool $ regQueryValueDWORD hkey "ProxyEnable"+ if enable+ then fmap Just $ regQueryValue hkey (Just "ProxyServer")+ else return Nothing+ where+ -- some sources say proxy settings should be at+ -- HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows+ -- \CurrentVersion\Internet Settings\ProxyServer+ -- but if the user sets them with IE connection panel they seem to+ -- end up in the following place:+ hive = hKEY_CURRENT_USER+ path = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"++ regQueryValueDWORD :: HKEY -> String -> IO DWORD+ regQueryValueDWORD hkey name = alloca $ \ptr -> do+ regQueryValueEx hkey name (castPtr ptr) (sizeOf (undefined :: DWORD))+ peek ptr+#else+registryProxyString = return Nothing+#endif++-- read proxy settings by looking for an env var+envProxyString = do+ env <- getEnvironment+ return (lookup "http_proxy" env `mplus` lookup "HTTP_PROXY" env)++proxyString = liftM2 mplus envProxyString registryProxyString+++-- |Get the local proxy settings+proxy :: Verbosity -> IO Proxy+proxy verbosity = do+ mstr <- proxyString+ case mstr of+ Nothing -> return NoProxy+ Just str -> case parseHttpProxy str of+ Nothing -> do+ warn verbosity $ "invalid http proxy uri: " ++ show str+ warn verbosity $ "proxy uri must be http with a hostname"+ warn verbosity $ "ignoring http proxy, trying a direct connection"+ return NoProxy+ Just p -> return p+--TODO: print info message when we're using a proxy++-- | We need to be able to parse non-URIs like @\"wwwcache.example.com:80\"@+-- which lack the @\"http://\"@ URI scheme. The problem is that+-- @\"wwwcache.example.com:80\"@ is in fact a valid URI but with scheme+-- @\"wwwcache.example.com:\"@, no authority part and a path of @\"80\"@.+--+-- So our strategy is to try parsing as normal uri first and if it lacks the+-- 'uriAuthority' then we try parsing again with a @\"http://\"@ prefix.+--+parseHttpProxy :: String -> Maybe Proxy+parseHttpProxy str = join+ . fmap uri2proxy+ $ parseHttpURI str+ `mplus` parseHttpURI ("http://" ++ str)+ where+ parseHttpURI str' = case parseAbsoluteURI str' of+ Just uri@URI { uriAuthority = Just _ }+ -> Just (fixUserInfo uri)+ _ -> Nothing++fixUserInfo :: URI -> URI+fixUserInfo uri = uri{ uriAuthority = f `fmap` uriAuthority uri }+ where+ f a@URIAuth{ uriUserInfo = s } =+ a{ uriUserInfo = case reverse s of+ '@':s' -> reverse s'+ _ -> s+ }+uri2proxy :: URI -> Maybe Proxy+uri2proxy uri@URI{ uriScheme = "http:"+ , uriAuthority = Just (URIAuth auth' host port)+ } = Just (Proxy (host ++ port) auth)+ where auth = if null auth'+ then Nothing+ else Just (AuthBasic "" usr pwd uri)+ (usr,pwd') = break (==':') auth'+ pwd = case pwd' of+ ':':cs -> cs+ _ -> pwd'+uri2proxy _ = Nothing++mkRequest :: URI -> Request ByteString+mkRequest uri = Request{ rqURI = uri+ , rqMethod = GET+ , rqHeaders = [Header HdrUserAgent userAgent]+ , rqBody = ByteString.empty }+ where userAgent = "cabal-install/" ++ display Paths_cabal_install_bundle.version++-- |Carry out a GET request, using the local proxy settings+getHTTP :: Verbosity -> URI -> IO (Result (Response ByteString))+getHTTP verbosity uri = do+ p <- proxy verbosity+ let req = mkRequest uri+ (_, resp) <- browse $ do+ setErrHandler (warn verbosity . ("http error: "++))+ setOutHandler (debug verbosity)+ setProxy p+ request req+ return (Right resp)++downloadURI :: Verbosity+ -> URI -- ^ What to download+ -> FilePath -- ^ Where to put it+ -> IO ()+downloadURI verbosity uri path | uriScheme uri == "file:" =+ copyFileVerbose verbosity (uriPath uri) path+downloadURI verbosity uri path = do+ result <- getHTTP verbosity uri+ let result' = case result of+ Left err -> Left err+ Right rsp -> case rspCode rsp of+ (2,0,0) -> Right (rspBody rsp)+ (a,b,c) -> Left err+ where+ err = ErrorMisc $ "Unsucessful HTTP code: "+ ++ concatMap show [a,b,c]++ case result' of+ Left err -> die $ "Failed to download " ++ show uri ++ " : " ++ show err+ Right body -> do+ info verbosity ("Downloaded to " ++ path)+ writeFileAtomic path (ByteString.unpack body)+ --FIXME: check the content-length header matches the body length.+ --TODO: stream the download into the file rather than buffering the whole+ -- thing in memory.+ -- remember the ETag so we can not re-download if nothing changed.++-- Utility function for legacy support.+isOldHackageURI :: URI -> Bool+isOldHackageURI uri+ = case uriAuthority uri of+ Just (URIAuth {uriRegName = "hackage.haskell.org"}) ->+ FilePath.Posix.splitDirectories (uriPath uri) == ["/","packages","archive"]+ _ -> False
+ Distribution/Client/IndexUtils.hs view
@@ -0,0 +1,265 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.IndexUtils+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : duncan@community.haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Extra utils related to the package indexes.+-----------------------------------------------------------------------------+module Distribution.Client.IndexUtils (+ getInstalledPackages,+ getAvailablePackages,++ readPackageIndexFile,+ parseRepoIndex,+ ) where++import qualified Distribution.Client.Tar as Tar+import Distribution.Client.Types++import Distribution.Package+ ( PackageId, PackageIdentifier(..), PackageName(..)+ , Package(..), packageVersion+ , Dependency(Dependency), InstalledPackageId(..) )+import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Client.PackageIndex as PackageIndex+import qualified Distribution.Simple.PackageIndex as InstalledPackageIndex+import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo+import Distribution.PackageDescription+ ( GenericPackageDescription )+import Distribution.PackageDescription.Parse+ ( parsePackageDescription )+import Distribution.Simple.Compiler+ ( Compiler, PackageDBStack )+import Distribution.Simple.Program+ ( ProgramConfiguration )+import qualified Distribution.Simple.Configure as Configure+ ( getInstalledPackages )+import Distribution.ParseUtils+ ( ParseResult(..) )+import Distribution.Version+ ( Version(Version), intersectVersionRanges )+import Distribution.Text+ ( simpleParse )+import Distribution.Verbosity+ ( Verbosity, lessVerbose )+import Distribution.Simple.Utils+ ( warn, info, fromUTF8, equating )++import Data.Maybe (catMaybes, fromMaybe)+import Data.List (isPrefixOf, groupBy)+import Data.Monoid (Monoid(..))+import qualified Data.Map as Map+import Control.Monad (MonadPlus(mplus), when)+import Control.Exception (evaluate)+import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as BS.Char8+import Data.ByteString.Lazy (ByteString)+import Distribution.Client.GZipUtils (maybeDecompress)+import System.FilePath ((</>), takeExtension, splitDirectories, normalise)+import System.FilePath.Posix as FilePath.Posix+ ( takeFileName )+import System.IO.Error (isDoesNotExistError)+import System.Directory+ ( getModificationTime )+import System.Time+ ( getClockTime, diffClockTimes, normalizeTimeDiff, TimeDiff(tdDay) )++getInstalledPackages :: Verbosity -> Compiler+ -> PackageDBStack -> ProgramConfiguration+ -> IO (PackageIndex InstalledPackage)+getInstalledPackages verbosity comp packageDbs conf =+ fmap convert (Configure.getInstalledPackages verbosity'+ comp packageDbs conf)+ where+ --FIXME: make getInstalledPackages use sensible verbosity in the first place+ verbosity' = lessVerbose verbosity++ convert :: InstalledPackageIndex.PackageIndex -> PackageIndex InstalledPackage+ convert index = PackageIndex.fromList+ -- There can be multiple installed instances of each package version,+ -- like when the same package is installed in the global & user dbs.+ -- InstalledPackageIndex.allPackagesByName gives us the installed+ -- packages with the most preferred instances first, so by picking the+ -- first we should get the user one. This is almost but not quite the+ -- same as what ghc does.+ [ InstalledPackage ipkg (sourceDeps index ipkg)+ | ipkgs <- InstalledPackageIndex.allPackagesByName index+ , (ipkg:_) <- groupBy (equating packageVersion) ipkgs ]++ -- The InstalledPackageInfo only lists dependencies by the+ -- InstalledPackageId, which means we do not directly know the corresponding+ -- source dependency. The only way to find out is to lookup the+ -- InstalledPackageId to get the InstalledPackageInfo and look at its+ -- source PackageId. But if the package is broken because it depends on+ -- other packages that do not exist then we have a problem we cannot find+ -- the original source package id. Instead we make up a bogus package id.+ -- This should have the same effect since it should be a dependency on a+ -- non-existant package.+ sourceDeps index ipkg =+ [ maybe (brokenPackageId depid) packageId mdep+ | let depids = InstalledPackageInfo.depends ipkg+ getpkg = InstalledPackageIndex.lookupInstalledPackageId index+ , (depid, mdep) <- zip depids (map getpkg depids) ]++ brokenPackageId (InstalledPackageId str) =+ PackageIdentifier (PackageName (str ++ "-broken")) (Version [] [])++-- | Read a repository index from disk, from the local files specified by+-- a list of 'Repo's.+--+-- All the 'AvailablePackage's are marked as having come from the appropriate+-- 'Repo'.+--+-- This is a higher level wrapper used internally in cabal-install.+--+getAvailablePackages :: Verbosity -> [Repo] -> IO AvailablePackageDb+getAvailablePackages verbosity [] = do+ warn verbosity $ "No remote package servers have been specified. Usually "+ ++ "you would have one specified in the config file."+ return AvailablePackageDb {+ packageIndex = mempty,+ packagePreferences = mempty+ }+getAvailablePackages verbosity repos = do+ info verbosity "Reading available packages..."+ pkgss <- mapM (readRepoIndex verbosity) repos+ let (pkgs, prefs) = mconcat pkgss+ prefs' = Map.fromListWith intersectVersionRanges+ [ (name, range) | Dependency name range <- prefs ]+ _ <- evaluate pkgs+ _ <- evaluate prefs'+ return AvailablePackageDb {+ packageIndex = pkgs,+ packagePreferences = prefs'+ }++-- | Read a repository index from disk, from the local file specified by+-- the 'Repo'.+--+-- All the 'AvailablePackage's are marked as having come from the given 'Repo'.+--+-- This is a higher level wrapper used internally in cabal-install.+--+readRepoIndex :: Verbosity -> Repo+ -> IO (PackageIndex AvailablePackage, [Dependency])+readRepoIndex verbosity repo = handleNotFound $ do+ let indexFile = repoLocalDir repo </> "00-index.tar"+ (pkgs, prefs) <- either fail return+ . foldlTarball extract ([], [])+ =<< BS.readFile indexFile++ pkgIndex <- evaluate $ PackageIndex.fromList+ [ AvailablePackage {+ packageInfoId = pkgid,+ packageDescription = pkg,+ packageSource = RepoTarballPackage repo pkgid Nothing+ }+ | (pkgid, pkg) <- pkgs]++ warnIfIndexIsOld indexFile+ return (pkgIndex, prefs)++ where+ extract (pkgs, prefs) entry = fromMaybe (pkgs, prefs) $+ (do pkg <- extractPkg entry; return (pkg:pkgs, prefs))+ `mplus` (do prefs' <- extractPrefs entry; return (pkgs, prefs'++prefs))++ extractPrefs :: Tar.Entry -> Maybe [Dependency]+ extractPrefs entry = case Tar.entryContent entry of+ Tar.NormalFile content _+ | takeFileName (Tar.entryPath entry) == "preferred-versions"+ -> Just . parsePreferredVersions+ . BS.Char8.unpack $ content+ _ -> Nothing++ handleNotFound action = catch action $ \e -> if isDoesNotExistError e+ then do+ case repoKind repo of+ Left remoteRepo -> warn verbosity $+ "The package list for '" ++ remoteRepoName remoteRepo+ ++ "' does not exist. Run 'cabal update' to download it."+ Right _localRepo -> warn verbosity $+ "The package list for the local repo '" ++ repoLocalDir repo+ ++ "' is missing. The repo is invalid."+ return mempty+ else ioError e++ isOldThreshold = 15 --days+ warnIfIndexIsOld indexFile = do+ indexTime <- getModificationTime indexFile+ currentTime <- getClockTime+ let diff = normalizeTimeDiff (diffClockTimes currentTime indexTime)+ when (tdDay diff >= isOldThreshold) $ case repoKind repo of+ Left remoteRepo -> warn verbosity $+ "The package list for '" ++ remoteRepoName remoteRepo+ ++ "' is " ++ show (tdDay diff) ++ " days old.\nRun "+ ++ "'cabal update' to get the latest list of available packages."+ Right _localRepo -> return ()++parsePreferredVersions :: String -> [Dependency]+parsePreferredVersions = catMaybes+ . map simpleParse+ . filter (not . isPrefixOf "--")+ . lines++-- | Read a compressed \"00-index.tar.gz\" file into a 'PackageIndex'.+--+-- This is supposed to be an \"all in one\" way to easily get at the info in+-- the hackage package index.+--+-- It takes a function to map a 'GenericPackageDescription' into any more+-- specific instance of 'Package' that you might want to use. In the simple+-- case you can just use @\_ p -> p@ here.+--+readPackageIndexFile :: Package pkg+ => (PackageId -> GenericPackageDescription -> pkg)+ -> FilePath -> IO (PackageIndex pkg)+readPackageIndexFile mkPkg indexFile = do+ pkgs <- either fail return+ . parseRepoIndex+ . maybeDecompress+ =<< BS.readFile indexFile+ + evaluate $ PackageIndex.fromList+ [ mkPkg pkgid pkg | (pkgid, pkg) <- pkgs]++-- | Parse an uncompressed \"00-index.tar\" repository index file represented+-- as a 'ByteString'.+--+parseRepoIndex :: ByteString+ -> Either String [(PackageId, GenericPackageDescription)]+parseRepoIndex = foldlTarball (\pkgs -> maybe pkgs (:pkgs) . extractPkg) []++extractPkg :: Tar.Entry -> Maybe (PackageId, GenericPackageDescription)+extractPkg entry = case Tar.entryContent entry of+ Tar.NormalFile content _+ | takeExtension fileName == ".cabal"+ -> case splitDirectories (normalise fileName) of+ [pkgname,vers,_] -> case simpleParse vers of+ Just ver -> Just (pkgid, descr)+ where+ pkgid = PackageIdentifier (PackageName pkgname) ver+ parsed = parsePackageDescription . fromUTF8 . BS.Char8.unpack+ $ content+ descr = case parsed of+ ParseOk _ d -> d+ _ -> error $ "Couldn't read cabal file "+ ++ show fileName+ _ -> Nothing+ _ -> Nothing+ _ -> Nothing+ where+ fileName = Tar.entryPath entry++foldlTarball :: (a -> Tar.Entry -> a) -> a+ -> ByteString -> Either String a+foldlTarball f z = either Left (Right . foldl f z) . check [] . Tar.read+ where+ check _ (Tar.Fail err) = Left err+ check ok Tar.Done = Right ok+ check ok (Tar.Next e es) = check (e:ok) es
+ Distribution/Client/Init.hs view
@@ -0,0 +1,556 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Init+-- Copyright : (c) Brent Yorgey 2009+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Implementation of the 'cabal init' command, which creates an initial .cabal+-- file for a project.+--+-----------------------------------------------------------------------------++module Distribution.Client.Init (++ -- * Commands+ initCabal++ ) where++import System.IO+ ( hSetBuffering, stdout, BufferMode(..) )+import System.Directory+ ( getCurrentDirectory )+import Data.Time+ ( getCurrentTime, utcToLocalTime, toGregorian, localDay, getCurrentTimeZone )++import Data.List+ ( intersperse )+import Data.Maybe+ ( fromMaybe, isJust )+import Data.Traversable+ ( traverse )+import Control.Monad+ ( when )+#if MIN_VERSION_base(3,0,0)+import Control.Monad+ ( (>=>) )+#endif++import Text.PrettyPrint.HughesPJ hiding (mode, cat)++import Data.Version+ ( Version(..) )+import Distribution.Version+ ( orLaterVersion )++import Distribution.Client.Init.Types+ ( InitFlags(..), PackageType(..), Category(..) )+import Distribution.Client.Init.Licenses+ ( bsd3, gplv2, gplv3, lgpl2, lgpl3 )+import Distribution.Client.Init.Heuristics+ ( guessPackageName, guessAuthorNameMail, SourceFileEntry(..), scanForModules, neededBuildPrograms )++import Distribution.License+ ( License(..), knownLicenses )+import Distribution.ModuleName+ ( ) -- for the Text instance++import Distribution.ReadE+ ( runReadE, readP_to_E )+import Distribution.Simple.Setup+ ( Flag(..), flagToMaybe )+import Distribution.Text+ ( display, Text(..) )++initCabal :: InitFlags -> IO ()+initCabal initFlags = do+ hSetBuffering stdout NoBuffering++ initFlags' <- extendFlags initFlags++ writeLicense initFlags'+ writeSetupFile initFlags'+ success <- writeCabalFile initFlags'++ when success $ generateWarnings initFlags'++---------------------------------------------------------------------------+-- Flag acquisition -----------------------------------------------------+---------------------------------------------------------------------------++-- | Fill in more details by guessing, discovering, or prompting the+-- user.+extendFlags :: InitFlags -> IO InitFlags+extendFlags = getPackageName+ >=> getVersion+ >=> getLicense+ >=> getAuthorInfo+ >=> getHomepage+ >=> getSynopsis+ >=> getCategory+ >=> getLibOrExec+ >=> getSrcDir+ >=> getModulesAndBuildTools++-- | Combine two actions which may return a value, preferring the first. That+-- is, run the second action only if the first doesn't return a value.+infixr 1 ?>>+(?>>) :: IO (Maybe a) -> IO (Maybe a) -> IO (Maybe a)+f ?>> g = do+ ma <- f+ if isJust ma+ then return ma+ else g++-- | Witness the isomorphism between Maybe and Flag.+maybeToFlag :: Maybe a -> Flag a+maybeToFlag = maybe NoFlag Flag++-- | Get the package name: use the package directory (supplied, or the current+-- directory by default) as a guess.+getPackageName :: InitFlags -> IO InitFlags+getPackageName flags = do+ guess <- traverse guessPackageName (flagToMaybe $ packageDir flags)+ ?>> Just `fmap` (getCurrentDirectory >>= guessPackageName)++ pkgName' <- return (flagToMaybe $ packageName flags)+ ?>> maybePrompt flags (promptStr "Package name" guess)+ ?>> return guess++ return $ flags { packageName = maybeToFlag pkgName' }++-- | Package version: use 0.1 as a last resort, but try prompting the user if+-- possible.+getVersion :: InitFlags -> IO InitFlags+getVersion flags = do+ let v = Just $ Version { versionBranch = [0,1], versionTags = [] }+ v' <- return (flagToMaybe $ version flags)+ ?>> maybePrompt flags (prompt "Package version" v)+ ?>> return v+ return $ flags { version = maybeToFlag v' }++-- | Choose a license.+getLicense :: InitFlags -> IO InitFlags+getLicense flags = do+ lic <- return (flagToMaybe $ license flags)+ ?>> fmap (fmap (either UnknownLicense id))+ (maybePrompt flags+ (promptList "Please choose a license"+ knownLicenses (Just BSD3) True))+ return $ flags { license = maybeToFlag lic }++-- | The author's name and email. Prompt, or try to guess from an existing+-- darcs repo.+getAuthorInfo :: InitFlags -> IO InitFlags+getAuthorInfo flags = do+ (authorName, authorEmail) <- (\(a,e) -> (flagToMaybe a, flagToMaybe e)) `fmap` guessAuthorNameMail+ authorName' <- return (flagToMaybe $ author flags)+ ?>> maybePrompt flags (promptStr "Author name" authorName)+ ?>> return authorName++ authorEmail' <- return (flagToMaybe $ email flags)+ ?>> maybePrompt flags (promptStr "Maintainer email" authorEmail)+ ?>> return authorEmail++ return $ flags { author = maybeToFlag authorName'+ , email = maybeToFlag authorEmail'+ }++-- | Prompt for a homepage URL.+getHomepage :: InitFlags -> IO InitFlags+getHomepage flags = do+ hp <- queryHomepage+ hp' <- return (flagToMaybe $ homepage flags)+ ?>> maybePrompt flags (promptStr "Project homepage/repo URL" hp)+ ?>> return hp++ return $ flags { homepage = maybeToFlag hp' }++-- | Right now this does nothing, but it could be changed to do some+-- intelligent guessing.+queryHomepage :: IO (Maybe String)+queryHomepage = return Nothing -- get default remote darcs repo?++-- | Prompt for a project synopsis.+getSynopsis :: InitFlags -> IO InitFlags+getSynopsis flags = do+ syn <- return (flagToMaybe $ synopsis flags)+ ?>> maybePrompt flags (promptStr "Project synopsis" Nothing)++ return $ flags { synopsis = maybeToFlag syn }++-- | Prompt for a package category.+-- Note that it should be possible to do some smarter guessing here too, i.e.+-- look at the name of the top level source directory.+getCategory :: InitFlags -> IO InitFlags+getCategory flags = do+ cat <- return (flagToMaybe $ category flags)+ ?>> maybePrompt flags (promptList "Project category" [Codec ..]+ Nothing True)+ return $ flags { category = maybeToFlag cat }++-- | Ask whether the project builds a library or executable.+getLibOrExec :: InitFlags -> IO InitFlags+getLibOrExec flags = do+ isLib <- return (flagToMaybe $ packageType flags)+ ?>> maybePrompt flags (either (const Library) id `fmap`+ (promptList "What does the package build"+ [Library, Executable]+ Nothing False))+ ?>> return (Just Library)++ return $ flags { packageType = maybeToFlag isLib }++-- | Try to guess the source root directory (don't prompt the user).+getSrcDir :: InitFlags -> IO InitFlags+getSrcDir flags = do+ srcDirs <- return (sourceDirs flags)+ ?>> guessSourceDirs++ return $ flags { sourceDirs = srcDirs }++-- XXX+-- | Try to guess source directories.+guessSourceDirs :: IO (Maybe [String])+guessSourceDirs = return Nothing++-- | Get the list of exposed modules and extra tools needed to build them.+getModulesAndBuildTools :: InitFlags -> IO InitFlags+getModulesAndBuildTools flags = do+ dir <- fromMaybe getCurrentDirectory+ (fmap return . flagToMaybe $ packageDir flags)++ -- XXX really should use guessed source roots.+ sourceFiles <- scanForModules dir++ mods <- return (exposedModules flags)+ ?>> (return . Just . map moduleName $ sourceFiles)++ tools <- return (buildTools flags)+ ?>> (return . Just . neededBuildPrograms $ sourceFiles)++ return $ flags { exposedModules = mods+ , buildTools = tools }++---------------------------------------------------------------------------+-- Prompting/user interaction -------------------------------------------+---------------------------------------------------------------------------++-- | Run a prompt or not based on the nonInteractive flag of the+-- InitFlags structure.+maybePrompt :: InitFlags -> IO t -> IO (Maybe t)+maybePrompt flags p =+ case nonInteractive flags of+ Flag True -> return Nothing+ _ -> Just `fmap` p++-- | Create a prompt with optional default value that returns a+-- String.+promptStr :: String -> Maybe String -> IO String+promptStr = promptDefault' Just id++-- | Create a prompt with optional default value that returns a value+-- of some Text instance.+prompt :: Text t => String -> Maybe t -> IO t+prompt = promptDefault'+ (either (const Nothing) Just . runReadE (readP_to_E id parse))+ display++-- | Create a prompt with an optional default value.+promptDefault' :: (String -> Maybe t) -- ^ parser+ -> (t -> String) -- ^ pretty-printer+ -> String -- ^ prompt message+ -> Maybe t -- ^ optional default value+ -> IO t+promptDefault' parser pretty pr def = do+ putStr $ mkDefPrompt pr (pretty `fmap` def)+ inp <- getLine+ case (inp, def) of+ ("", Just d) -> return d+ _ -> case parser inp of+ Just t -> return t+ Nothing -> do putStrLn $ "Couldn't parse " ++ inp ++ ", please try again!"+ promptDefault' parser pretty pr def++-- | Create a prompt from a prompt string and a String representation+-- of an optional default value.+mkDefPrompt :: String -> Maybe String -> String+mkDefPrompt pr def = pr ++ defStr def ++ "? "+ where defStr Nothing = ""+ defStr (Just s) = " [default \"" ++ s ++ "\"]"++-- | Create a prompt from a list of items.+promptList :: (Text t, Eq t)+ => String -- ^ prompt+ -> [t] -- ^ choices+ -> Maybe t -- ^ optional default value+ -> Bool -- ^ whether to allow an 'other' option+ -> IO (Either String t)+promptList pr choices def other = do+ putStrLn $ pr ++ ":"+ let options1 = map (\c -> (Just c == def, display c)) choices+ options2 = zip ([1..]::[Int])+ (options1 ++ if other then [(False, "Other (specify)")]+ else [])+ mapM_ (putStrLn . \(n,(i,s)) -> showOption n i ++ s) options2+ promptList' (length options2) choices def other+ where showOption n i | n < 10 = " " ++ star i ++ " " ++ rest+ | otherwise = " " ++ star i ++ rest+ where rest = show n ++ ") "+ star True = "*"+ star False = " "++promptList' :: Text t => Int -> [t] -> Maybe t -> Bool -> IO (Either String t)+promptList' numChoices choices def other = do+ putStr $ mkDefPrompt "Your choice" (display `fmap` def)+ inp <- getLine+ case (inp, def) of+ ("", Just d) -> return $ Right d+ _ -> case readMaybe inp of+ Nothing -> invalidChoice inp+ Just n -> getChoice n+ where invalidChoice inp = do putStrLn $ inp ++ " is not a valid choice."+ promptList' numChoices choices def other+ getChoice n | n < 1 || n > numChoices = invalidChoice (show n)+ | n < numChoices ||+ (n == numChoices && not other)+ = return . Right $ choices !! (n-1)+ | otherwise = Left `fmap` promptStr "Please specify" Nothing++readMaybe :: (Read a) => String -> Maybe a+readMaybe s = case reads s of+ [(a,"")] -> Just a+ _ -> Nothing++---------------------------------------------------------------------------+-- File generation ------------------------------------------------------+---------------------------------------------------------------------------++writeLicense :: InitFlags -> IO ()+writeLicense flags = do+ message flags "Generating LICENSE..."+ year <- getYear+ let licenseFile =+ case license flags of+ Flag BSD3 -> Just $ bsd3 (fromMaybe "???"+ . flagToMaybe+ . author+ $ flags)+ (show year)++ Flag (GPL (Just (Version {versionBranch = [2]})))+ -> Just gplv2++ Flag (GPL (Just (Version {versionBranch = [3]})))+ -> Just gplv3++ Flag (LGPL (Just (Version {versionBranch = [2]})))+ -> Just lgpl2++ Flag (LGPL (Just (Version {versionBranch = [3]})))+ -> Just lgpl3++ _ -> Nothing++ case licenseFile of+ Just licenseText -> writeFile "LICENSE" licenseText+ Nothing -> message flags "Warning: unknown license type, you must put a copy in LICENSE yourself."++getYear :: IO Integer+getYear = do+ u <- getCurrentTime+ z <- getCurrentTimeZone+ let l = utcToLocalTime z u+ (y, _, _) = toGregorian $ localDay l+ return y++writeSetupFile :: InitFlags -> IO ()+writeSetupFile flags = do+ message flags "Generating Setup.hs..."+ writeFile "Setup.hs" setupFile+ where+ setupFile = unlines+ [ "import Distribution.Simple"+ , "main = defaultMain"+ ]++writeCabalFile :: InitFlags -> IO Bool+writeCabalFile flags@(InitFlags{packageName = NoFlag}) = do+ message flags "Error: no package name provided."+ return False+writeCabalFile flags@(InitFlags{packageName = Flag p}) = do+ let cabalFileName = p ++ ".cabal"+ message flags $ "Generating " ++ cabalFileName ++ "..."+ writeFile cabalFileName (generateCabalFile cabalFileName flags)+ return True++-- | Generate a .cabal file from an InitFlags structure. NOTE: this+-- is rather ad-hoc! What we would REALLY like is to have a+-- standard low-level AST type representing .cabal files, which+-- preserves things like comments, and to write an *inverse*+-- parser/pretty-printer pair between .cabal files and this AST.+-- Then instead of this ad-hoc code we could just map an InitFlags+-- structure onto a low-level AST structure and use the existing+-- pretty-printing code to generate the file.+generateCabalFile :: String -> InitFlags -> String+generateCabalFile fileName c = render $+ (if (minimal c /= Flag True)+ then showComment (Just $ fileName ++ " auto-generated by cabal init. For additional options, see http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.")+ else empty)+ $$+ vcat [ fieldS "Name" (packageName c)+ (Just "The name of the package.")+ True++ , field "Version" (version c)+ (Just "The package version. See the Haskell package versioning policy (http://www.haskell.org/haskellwiki/Package_versioning_policy) for standards guiding when and how versions should be incremented.")+ True++ , fieldS "Synopsis" (synopsis c)+ (Just "A short (one-line) description of the package.")+ True++ , fieldS "Description" NoFlag+ (Just "A longer description of the package.")+ True++ , fieldS "Homepage" (homepage c)+ (Just "URL for the project homepage or repository.")+ False++ , fieldS "Bug-reports" NoFlag+ (Just "A URL where users can report bugs.")+ False++ , field "License" (license c)+ (Just "The license under which the package is released.")+ True++ , fieldS "License-file" (Flag "LICENSE")+ (Just "The file containing the license text.")+ True++ , fieldS "Author" (author c)+ (Just "The package author(s).")+ True++ , fieldS "Maintainer" (email c)+ (Just "An email address to which users can send suggestions, bug reports, and patches.")+ True++ , fieldS "Copyright" NoFlag+ (Just "A copyright notice.")+ True++ , fieldS "Category" (either id display `fmap` category c)+ Nothing+ True++ , fieldS "Build-type" (Flag "Simple")+ Nothing+ True++ , fieldS "Extra-source-files" NoFlag+ (Just "Extra files to be distributed with the package, such as examples or a README.")+ True++ , field "Cabal-version" (Flag $ orLaterVersion (Version [1,2] []))+ (Just "Constraint on the version of Cabal needed to build this package.")+ False++ , case packageType c of+ Flag Executable ->+ text "\nExecutable" <+> text (fromMaybe "" . flagToMaybe $ packageName c) $$ (nest 2 $ vcat+ [ fieldS "Main-is" NoFlag (Just ".hs or .lhs file containing the Main module.") True++ , generateBuildInfo c+ ])+ Flag Library -> text "\nLibrary" $$ (nest 2 $ vcat+ [ fieldS "Exposed-modules" (listField (exposedModules c))+ (Just "Modules exported by the library.")+ True++ , generateBuildInfo c+ ])+ _ -> empty+ ]+ where+ generateBuildInfo :: InitFlags -> Doc+ generateBuildInfo c' = vcat+ [ fieldS "Build-depends" (listField (dependencies c'))+ (Just "Packages needed in order to build this package.")+ True++ , fieldS "Other-modules" (listField (otherModules c'))+ (Just "Modules not exported by this package.")+ True++ , fieldS "hs-source-dirs" (listFieldS (sourceDirs c'))+ (Just "Directories other than the root containing source files.")+ False++ , fieldS "Build-tools" (listFieldS (buildTools c'))+ (Just "Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.")+ True+ ]++ listField :: Text s => Maybe [s] -> Flag String+ listField = listFieldS . fmap (map display)++ listFieldS :: Maybe [String] -> Flag String+ listFieldS = Flag . maybe "" (concat . intersperse ", ")++ field :: Text t => String -> Flag t -> Maybe String -> Bool -> Doc+ field s f = fieldS s (fmap display f)++ fieldS :: String -- ^ Name of the field+ -> Flag String -- ^ Field contents+ -> Maybe String -- ^ Comment to explain the field+ -> Bool -- ^ Should the field be included (commented out) even if blank?+ -> Doc+ fieldS _ NoFlag _ inc | not inc || (minimal c == Flag True) = empty+ fieldS _ (Flag "") _ inc | not inc || (minimal c == Flag True) = empty+ fieldS s f com _ = case (isJust com, noComments c, minimal c) of+ (_, _, Flag True) -> id+ (_, Flag True, _) -> id+ (True, _, _) -> (showComment com $$) . ($$ text "")+ (False, _, _) -> ($$ text "")+ $+ comment f <> text s <> colon+ <> text (take (20 - length s) (repeat ' '))+ <> text (fromMaybe "" . flagToMaybe $ f)+ comment NoFlag = text "-- "+ comment (Flag "") = text "-- "+ comment _ = text ""++ showComment :: Maybe String -> Doc+ showComment (Just t) = vcat . map text+ . map ("-- "++) . lines+ . render . fsep . map text . words $ t+ showComment Nothing = text ""++-- | Generate warnings for missing fields etc.+generateWarnings :: InitFlags -> IO ()+generateWarnings flags = do+ message flags ""+ when (synopsis flags `elem` [NoFlag, Flag ""])+ (message flags "Warning: no synopsis given. You should edit the .cabal file and add one.")++ message flags "You may want to edit the .cabal file and add a Description field."++-- | Possibly generate a message to stdout, taking into account the+-- --quiet flag.+message :: InitFlags -> String -> IO ()+message (InitFlags{quiet = Flag True}) _ = return ()+message _ s = putStrLn s++#if MIN_VERSION_base(3,0,0)+#else+(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)+f >=> g = \x -> f x >>= g+#endif
+ Distribution/Client/Init/Heuristics.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Init.Heuristics+-- Copyright : (c) Benedikt Huber 2009+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Heuristics for creating initial cabal files.+--+-----------------------------------------------------------------------------+module Distribution.Client.Init.Heuristics (+ guessPackageName,+ scanForModules, SourceFileEntry(..),+ neededBuildPrograms,+ guessAuthorNameMail,+ knownCategories,+) where+import Distribution.Simple.Setup(Flag(..))+import Distribution.ModuleName ( ModuleName, fromString )+import Distribution.Client.PackageIndex+ ( allPackagesByName )+import qualified Distribution.PackageDescription as PD+ ( category, packageDescription )+import Distribution.Simple.Utils+ ( intercalate )++import Distribution.Client.Types ( packageDescription, AvailablePackageDb(..) )+import Control.Monad (liftM )+import Data.Char ( isUpper, isLower, isSpace )+#if MIN_VERSION_base(3,0,3)+import Data.Either ( partitionEithers )+#endif+import Data.Maybe ( catMaybes )+import Data.Monoid ( mempty, mappend )+import qualified Data.Set as Set ( fromList, toList )+import System.Directory ( getDirectoryContents, doesDirectoryExist, doesFileExist,+ getHomeDirectory, canonicalizePath )+import System.Environment ( getEnvironment )+import System.FilePath ( takeExtension, takeBaseName, dropExtension,+ (</>), splitDirectories, makeRelative )++-- |Guess the package name based on the given root directory+guessPackageName :: FilePath -> IO String+guessPackageName = liftM (last . splitDirectories) . canonicalizePath++-- |Data type of source files found in the working directory+data SourceFileEntry = SourceFileEntry+ { relativeSourcePath :: FilePath+ , moduleName :: ModuleName+ , fileExtension :: String+ } deriving Show++-- |Search for source files in the given directory+-- and return pairs of guessed haskell source path and+-- module names.+scanForModules :: FilePath -> IO [SourceFileEntry]+scanForModules rootDir = scanForModulesIn rootDir rootDir++scanForModulesIn :: FilePath -> FilePath -> IO [SourceFileEntry]+scanForModulesIn projectRoot srcRoot = scan srcRoot []+ where+ scan dir hierarchy = do+ entries <- getDirectoryContents (projectRoot </> dir)+ (files, dirs) <- liftM partitionEithers (mapM (tagIsDir dir) entries)+ let modules = catMaybes [ guessModuleName hierarchy file+ | file <- files+ , isUpper (head file) ]+ recMods <- mapM (scanRecursive dir hierarchy) dirs+ return $ concat (modules : recMods)+ tagIsDir parent entry = do+ isDir <- doesDirectoryExist (parent </> entry)+ return $ (if isDir then Right else Left) entry+ guessModuleName hierarchy entry+ | takeBaseName entry == "Setup" = Nothing+ | ext `elem` sourceExtensions = Just $ SourceFileEntry relRoot modName ext+ | otherwise = Nothing+ where+ relRoot = makeRelative projectRoot srcRoot+ unqualModName = dropExtension entry+ modName = fromString $ intercalate "." . reverse $ (unqualModName : hierarchy)+ ext = case takeExtension entry of '.':e -> e; e -> e+ scanRecursive parent hierarchy entry+ | isUpper (head entry) = scan (parent </> entry) (entry : hierarchy)+ | isLower (head entry) && not (ignoreDir entry) =+ scanForModulesIn projectRoot $ foldl (</>) srcRoot (entry : hierarchy)+ | otherwise = return []+ ignoreDir ('.':_) = True+ ignoreDir dir = dir `elem` ["dist", "_darcs"]++-- Unfortunately we cannot use the version exported by Distribution.Simple.Program+knownSuffixHandlers :: [(String,String)]+knownSuffixHandlers =+ [ ("gc", "greencard")+ , ("chs", "chs")+ , ("hsc", "hsc2hs")+ , ("x", "alex")+ , ("y", "happy")+ , ("ly", "happy")+ , ("cpphs", "cpp")+ ]++sourceExtensions :: [String]+sourceExtensions = "hs" : "lhs" : map fst knownSuffixHandlers++neededBuildPrograms :: [SourceFileEntry] -> [String]+neededBuildPrograms entries =+ [ handler+ | ext <- nubSet (map fileExtension entries)+ , handler <- maybe [] (:[]) (lookup ext knownSuffixHandlers)+ ]++-- |Guess author and email+guessAuthorNameMail :: IO (Flag String, Flag String)+guessAuthorNameMail =+ update (readFromFile authorRepoFile) mempty >>=+ update (getAuthorHome >>= readFromFile) >>=+ update readFromEnvironment+ where+ update _ info@(Flag _, Flag _) = return info+ update extract info = liftM (`mappend` info) extract -- prefer info+ readFromFile file = do+ exists <- doesFileExist file+ if exists then liftM nameAndMail (readFile file) else return mempty+ readFromEnvironment = fmap extractFromEnvironment getEnvironment+ extractFromEnvironment env =+ let darcsEmailEnv = maybe mempty nameAndMail (lookup "DARCS_EMAIL" env)+ emailEnv = maybe mempty (\e -> (mempty, Flag e)) (lookup "EMAIL" env)+ in darcsEmailEnv `mappend` emailEnv+ getAuthorHome = liftM (</> (".darcs" </> "author")) getHomeDirectory+ authorRepoFile = "_darcs" </> "prefs" </> "author"++-- |Get list of categories used in hackage. NOTE: Very slow, needs to be cached+knownCategories :: AvailablePackageDb -> [String]+knownCategories (AvailablePackageDb available _) = nubSet $+ [ cat | pkg <- map head (allPackagesByName available)+ , let catList = (PD.category . PD.packageDescription . packageDescription) pkg+ , cat <- splitString ',' catList+ ]++-- Parse name and email, from darcs pref files or environment variable+nameAndMail :: String -> (Flag String, Flag String)+nameAndMail str+ | all isSpace nameOrEmail = mempty+ | null erest = (mempty, Flag $ trim nameOrEmail)+ | otherwise = (Flag $ trim nameOrEmail, Flag email)+ where+ (nameOrEmail,erest) = break (== '<') str+ (email,_) = break (== '>') (tail erest)+ trim = removeLeadingSpace . reverse . removeLeadingSpace . reverse+ removeLeadingSpace = dropWhile isSpace++-- split string at given character, and remove whitespaces+splitString :: Char -> String -> [String]+splitString sep str = go str where+ go s = if null s' then [] else tok : go rest where+ s' = dropWhile (\c -> c == sep || isSpace c) s+ (tok,rest) = break (==sep) s'++nubSet :: (Ord a) => [a] -> [a]+nubSet = Set.toList . Set.fromList++{-+test db testProjectRoot = do+ putStrLn "Guessed package name"+ (guessPackageName >=> print) testProjectRoot+ putStrLn "Guessed name and email"+ guessAuthorNameMail >>= print++ mods <- scanForModules testProjectRoot++ putStrLn "Guessed modules"+ mapM_ print mods+ putStrLn "Needed build programs"+ print (neededBuildPrograms mods)++ putStrLn "List of known categories"+ print $ knownCategories db+-}++#if MIN_VERSION_base(3,0,3)+#else+partitionEithers :: [Either a b] -> ([a],[b])+partitionEithers = foldr (either left right) ([],[])+ where+ left a (l, r) = (a:l, r)+ right a (l, r) = (l, a:r)+#endif
+ Distribution/Client/Init/Licenses.hs view
@@ -0,0 +1,1722 @@+module Distribution.Client.Init.Licenses+ ( License+ , bsd3+ , gplv2+ , gplv3+ , lgpl2+ , lgpl3++ ) where++type License = String++bsd3 :: String -> String -> License+bsd3 authors year = unlines+ [ "Copyright (c)" ++ year ++ ", " ++ authors+ , ""+ , "All rights reserved."+ , ""+ , "Redistribution and use in source and binary forms, with or without"+ , "modification, are permitted provided that the following conditions are met:"+ , ""+ , " * Redistributions of source code must retain the above copyright"+ , " notice, this list of conditions and the following disclaimer."+ , ""+ , " * Redistributions in binary form must reproduce the above"+ , " copyright notice, this list of conditions and the following"+ , " disclaimer in the documentation and/or other materials provided"+ , " with the distribution."+ , ""+ , " * Neither the name of " ++ authors ++ " nor the names of other"+ , " contributors may be used to endorse or promote products derived"+ , " from this software without specific prior written permission."+ , ""+ , "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS"+ , "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT"+ , "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR"+ , "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT"+ , "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,"+ , "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT"+ , "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,"+ , "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY"+ , "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT"+ , "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE"+ , "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."+ ]++gplv2 :: License+gplv2 = unlines+ [ " GNU GENERAL PUBLIC LICENSE"+ , " Version 2, June 1991"+ , ""+ , " Copyright (C) 1989, 1991 Free Software Foundation, Inc.,"+ , " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"+ , " Everyone is permitted to copy and distribute verbatim copies"+ , " of this license document, but changing it is not allowed."+ , ""+ , " Preamble"+ , ""+ , " The licenses for most software are designed to take away your"+ , "freedom to share and change it. By contrast, the GNU General Public"+ , "License is intended to guarantee your freedom to share and change free"+ , "software--to make sure the software is free for all its users. This"+ , "General Public License applies to most of the Free Software"+ , "Foundation's software and to any other program whose authors commit to"+ , "using it. (Some other Free Software Foundation software is covered by"+ , "the GNU Lesser General Public License instead.) You can apply it to"+ , "your programs, too."+ , ""+ , " When we speak of free software, we are referring to freedom, not"+ , "price. Our General Public Licenses are designed to make sure that you"+ , "have the freedom to distribute copies of free software (and charge for"+ , "this service if you wish), that you receive source code or can get it"+ , "if you want it, that you can change the software or use pieces of it"+ , "in new free programs; and that you know you can do these things."+ , ""+ , " To protect your rights, we need to make restrictions that forbid"+ , "anyone to deny you these rights or to ask you to surrender the rights."+ , "These restrictions translate to certain responsibilities for you if you"+ , "distribute copies of the software, or if you modify it."+ , ""+ , " For example, if you distribute copies of such a program, whether"+ , "gratis or for a fee, you must give the recipients all the rights that"+ , "you have. You must make sure that they, too, receive or can get the"+ , "source code. And you must show them these terms so they know their"+ , "rights."+ , ""+ , " We protect your rights with two steps: (1) copyright the software, and"+ , "(2) offer you this license which gives you legal permission to copy,"+ , "distribute and/or modify the software."+ , ""+ , " Also, for each author's protection and ours, we want to make certain"+ , "that everyone understands that there is no warranty for this free"+ , "software. If the software is modified by someone else and passed on, we"+ , "want its recipients to know that what they have is not the original, so"+ , "that any problems introduced by others will not reflect on the original"+ , "authors' reputations."+ , ""+ , " Finally, any free program is threatened constantly by software"+ , "patents. We wish to avoid the danger that redistributors of a free"+ , "program will individually obtain patent licenses, in effect making the"+ , "program proprietary. To prevent this, we have made it clear that any"+ , "patent must be licensed for everyone's free use or not licensed at all."+ , ""+ , " The precise terms and conditions for copying, distribution and"+ , "modification follow."+ , ""+ , " GNU GENERAL PUBLIC LICENSE"+ , " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION"+ , ""+ , " 0. This License applies to any program or other work which contains"+ , "a notice placed by the copyright holder saying it may be distributed"+ , "under the terms of this General Public License. The \"Program\", below,"+ , "refers to any such program or work, and a \"work based on the Program\""+ , "means either the Program or any derivative work under copyright law:"+ , "that is to say, a work containing the Program or a portion of it,"+ , "either verbatim or with modifications and/or translated into another"+ , "language. (Hereinafter, translation is included without limitation in"+ , "the term \"modification\".) Each licensee is addressed as \"you\"."+ , ""+ , "Activities other than copying, distribution and modification are not"+ , "covered by this License; they are outside its scope. The act of"+ , "running the Program is not restricted, and the output from the Program"+ , "is covered only if its contents constitute a work based on the"+ , "Program (independent of having been made by running the Program)."+ , "Whether that is true depends on what the Program does."+ , ""+ , " 1. You may copy and distribute verbatim copies of the Program's"+ , "source code as you receive it, in any medium, provided that you"+ , "conspicuously and appropriately publish on each copy an appropriate"+ , "copyright notice and disclaimer of warranty; keep intact all the"+ , "notices that refer to this License and to the absence of any warranty;"+ , "and give any other recipients of the Program a copy of this License"+ , "along with the Program."+ , ""+ , "You may charge a fee for the physical act of transferring a copy, and"+ , "you may at your option offer warranty protection in exchange for a fee."+ , ""+ , " 2. You may modify your copy or copies of the Program or any portion"+ , "of it, thus forming a work based on the Program, and copy and"+ , "distribute such modifications or work under the terms of Section 1"+ , "above, provided that you also meet all of these conditions:"+ , ""+ , " a) You must cause the modified files to carry prominent notices"+ , " stating that you changed the files and the date of any change."+ , ""+ , " b) You must cause any work that you distribute or publish, that in"+ , " whole or in part contains or is derived from the Program or any"+ , " part thereof, to be licensed as a whole at no charge to all third"+ , " parties under the terms of this License."+ , ""+ , " c) If the modified program normally reads commands interactively"+ , " when run, you must cause it, when started running for such"+ , " interactive use in the most ordinary way, to print or display an"+ , " announcement including an appropriate copyright notice and a"+ , " notice that there is no warranty (or else, saying that you provide"+ , " a warranty) and that users may redistribute the program under"+ , " these conditions, and telling the user how to view a copy of this"+ , " License. (Exception: if the Program itself is interactive but"+ , " does not normally print such an announcement, your work based on"+ , " the Program is not required to print an announcement.)"+ , ""+ , "These requirements apply to the modified work as a whole. If"+ , "identifiable sections of that work are not derived from the Program,"+ , "and can be reasonably considered independent and separate works in"+ , "themselves, then this License, and its terms, do not apply to those"+ , "sections when you distribute them as separate works. But when you"+ , "distribute the same sections as part of a whole which is a work based"+ , "on the Program, the distribution of the whole must be on the terms of"+ , "this License, whose permissions for other licensees extend to the"+ , "entire whole, and thus to each and every part regardless of who wrote it."+ , ""+ , "Thus, it is not the intent of this section to claim rights or contest"+ , "your rights to work written entirely by you; rather, the intent is to"+ , "exercise the right to control the distribution of derivative or"+ , "collective works based on the Program."+ , ""+ , "In addition, mere aggregation of another work not based on the Program"+ , "with the Program (or with a work based on the Program) on a volume of"+ , "a storage or distribution medium does not bring the other work under"+ , "the scope of this License."+ , ""+ , " 3. You may copy and distribute the Program (or a work based on it,"+ , "under Section 2) in object code or executable form under the terms of"+ , "Sections 1 and 2 above provided that you also do one of the following:"+ , ""+ , " a) Accompany it with the complete corresponding machine-readable"+ , " source code, which must be distributed under the terms of Sections"+ , " 1 and 2 above on a medium customarily used for software interchange; or,"+ , ""+ , " b) Accompany it with a written offer, valid for at least three"+ , " years, to give any third party, for a charge no more than your"+ , " cost of physically performing source distribution, a complete"+ , " machine-readable copy of the corresponding source code, to be"+ , " distributed under the terms of Sections 1 and 2 above on a medium"+ , " customarily used for software interchange; or,"+ , ""+ , " c) Accompany it with the information you received as to the offer"+ , " to distribute corresponding source code. (This alternative is"+ , " allowed only for noncommercial distribution and only if you"+ , " received the program in object code or executable form with such"+ , " an offer, in accord with Subsection b above.)"+ , ""+ , "The source code for a work means the preferred form of the work for"+ , "making modifications to it. For an executable work, complete source"+ , "code means all the source code for all modules it contains, plus any"+ , "associated interface definition files, plus the scripts used to"+ , "control compilation and installation of the executable. However, as a"+ , "special exception, the source code distributed need not include"+ , "anything that is normally distributed (in either source or binary"+ , "form) with the major components (compiler, kernel, and so on) of the"+ , "operating system on which the executable runs, unless that component"+ , "itself accompanies the executable."+ , ""+ , "If distribution of executable or object code is made by offering"+ , "access to copy from a designated place, then offering equivalent"+ , "access to copy the source code from the same place counts as"+ , "distribution of the source code, even though third parties are not"+ , "compelled to copy the source along with the object code."+ , ""+ , " 4. You may not copy, modify, sublicense, or distribute the Program"+ , "except as expressly provided under this License. Any attempt"+ , "otherwise to copy, modify, sublicense or distribute the Program is"+ , "void, and will automatically terminate your rights under this License."+ , "However, parties who have received copies, or rights, from you under"+ , "this License will not have their licenses terminated so long as such"+ , "parties remain in full compliance."+ , ""+ , " 5. You are not required to accept this License, since you have not"+ , "signed it. However, nothing else grants you permission to modify or"+ , "distribute the Program or its derivative works. These actions are"+ , "prohibited by law if you do not accept this License. Therefore, by"+ , "modifying or distributing the Program (or any work based on the"+ , "Program), you indicate your acceptance of this License to do so, and"+ , "all its terms and conditions for copying, distributing or modifying"+ , "the Program or works based on it."+ , ""+ , " 6. Each time you redistribute the Program (or any work based on the"+ , "Program), the recipient automatically receives a license from the"+ , "original licensor to copy, distribute or modify the Program subject to"+ , "these terms and conditions. You may not impose any further"+ , "restrictions on the recipients' exercise of the rights granted herein."+ , "You are not responsible for enforcing compliance by third parties to"+ , "this License."+ , ""+ , " 7. If, as a consequence of a court judgment or allegation of patent"+ , "infringement or for any other reason (not limited to patent issues),"+ , "conditions are imposed on you (whether by court order, agreement or"+ , "otherwise) that contradict the conditions of this License, they do not"+ , "excuse you from the conditions of this License. If you cannot"+ , "distribute so as to satisfy simultaneously your obligations under this"+ , "License and any other pertinent obligations, then as a consequence you"+ , "may not distribute the Program at all. For example, if a patent"+ , "license would not permit royalty-free redistribution of the Program by"+ , "all those who receive copies directly or indirectly through you, then"+ , "the only way you could satisfy both it and this License would be to"+ , "refrain entirely from distribution of the Program."+ , ""+ , "If any portion of this section is held invalid or unenforceable under"+ , "any particular circumstance, the balance of the section is intended to"+ , "apply and the section as a whole is intended to apply in other"+ , "circumstances."+ , ""+ , "It is not the purpose of this section to induce you to infringe any"+ , "patents or other property right claims or to contest validity of any"+ , "such claims; this section has the sole purpose of protecting the"+ , "integrity of the free software distribution system, which is"+ , "implemented by public license practices. Many people have made"+ , "generous contributions to the wide range of software distributed"+ , "through that system in reliance on consistent application of that"+ , "system; it is up to the author/donor to decide if he or she is willing"+ , "to distribute software through any other system and a licensee cannot"+ , "impose that choice."+ , ""+ , "This section is intended to make thoroughly clear what is believed to"+ , "be a consequence of the rest of this License."+ , ""+ , " 8. If the distribution and/or use of the Program is restricted in"+ , "certain countries either by patents or by copyrighted interfaces, the"+ , "original copyright holder who places the Program under this License"+ , "may add an explicit geographical distribution limitation excluding"+ , "those countries, so that distribution is permitted only in or among"+ , "countries not thus excluded. In such case, this License incorporates"+ , "the limitation as if written in the body of this License."+ , ""+ , " 9. The Free Software Foundation may publish revised and/or new versions"+ , "of the General Public License from time to time. Such new versions will"+ , "be similar in spirit to the present version, but may differ in detail to"+ , "address new problems or concerns."+ , ""+ , "Each version is given a distinguishing version number. If the Program"+ , "specifies a version number of this License which applies to it and \"any"+ , "later version\", you have the option of following the terms and conditions"+ , "either of that version or of any later version published by the Free"+ , "Software Foundation. If the Program does not specify a version number of"+ , "this License, you may choose any version ever published by the Free Software"+ , "Foundation."+ , ""+ , " 10. If you wish to incorporate parts of the Program into other free"+ , "programs whose distribution conditions are different, write to the author"+ , "to ask for permission. For software which is copyrighted by the Free"+ , "Software Foundation, write to the Free Software Foundation; we sometimes"+ , "make exceptions for this. Our decision will be guided by the two goals"+ , "of preserving the free status of all derivatives of our free software and"+ , "of promoting the sharing and reuse of software generally."+ , ""+ , " NO WARRANTY"+ , ""+ , " 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY"+ , "FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN"+ , "OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES"+ , "PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED"+ , "OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF"+ , "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS"+ , "TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE"+ , "PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,"+ , "REPAIR OR CORRECTION."+ , ""+ , " 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING"+ , "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR"+ , "REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,"+ , "INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING"+ , "OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED"+ , "TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY"+ , "YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER"+ , "PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE"+ , "POSSIBILITY OF SUCH DAMAGES."+ , ""+ , " END OF TERMS AND CONDITIONS"+ , ""+ , " How to Apply These Terms to Your New Programs"+ , ""+ , " If you develop a new program, and you want it to be of the greatest"+ , "possible use to the public, the best way to achieve this is to make it"+ , "free software which everyone can redistribute and change under these terms."+ , ""+ , " To do so, attach the following notices to the program. It is safest"+ , "to attach them to the start of each source file to most effectively"+ , "convey the exclusion of warranty; and each file should have at least"+ , "the \"copyright\" line and a pointer to where the full notice is found."+ , ""+ , " <one line to give the program's name and a brief idea of what it does.>"+ , " Copyright (C) <year> <name of author>"+ , ""+ , " 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.,"+ , " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."+ , ""+ , "Also add information on how to contact you by electronic and paper mail."+ , ""+ , "If the program is interactive, make it output a short notice like this"+ , "when it starts in an interactive mode:"+ , ""+ , " Gnomovision version 69, Copyright (C) year name of author"+ , " Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'."+ , " This is free software, and you are welcome to redistribute it"+ , " under certain conditions; type `show c' for details."+ , ""+ , "The hypothetical commands `show w' and `show c' should show the appropriate"+ , "parts of the General Public License. Of course, the commands you use may"+ , "be called something other than `show w' and `show c'; they could even be"+ , "mouse-clicks or menu items--whatever suits your program."+ , ""+ , "You should also get your employer (if you work as a programmer) or your"+ , "school, if any, to sign a \"copyright disclaimer\" for the program, if"+ , "necessary. Here is a sample; alter the names:"+ , ""+ , " Yoyodyne, Inc., hereby disclaims all copyright interest in the program"+ , " `Gnomovision' (which makes passes at compilers) written by James Hacker."+ , ""+ , " <signature of Ty Coon>, 1 April 1989"+ , " Ty Coon, President of Vice"+ , ""+ , "This General Public License does not permit incorporating your program into"+ , "proprietary programs. If your program is a subroutine library, you may"+ , "consider it more useful to permit linking proprietary applications with the"+ , "library. If this is what you want to do, use the GNU Lesser General"+ , "Public License instead of this License."+ ]++gplv3 :: License+gplv3 = unlines+ [ " GNU GENERAL PUBLIC LICENSE"+ , " Version 3, 29 June 2007"+ , ""+ , " Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>"+ , " Everyone is permitted to copy and distribute verbatim copies"+ , " of this license document, but changing it is not allowed."+ , ""+ , " Preamble"+ , ""+ , " The GNU General Public License is a free, copyleft license for"+ , "software and other kinds of works."+ , ""+ , " The licenses for most software and other practical works are designed"+ , "to take away your freedom to share and change the works. By contrast,"+ , "the GNU General Public License is intended to guarantee your freedom to"+ , "share and change all versions of a program--to make sure it remains free"+ , "software for all its users. We, the Free Software Foundation, use the"+ , "GNU General Public License for most of our software; it applies also to"+ , "any other work released this way by its authors. You can apply it to"+ , "your programs, too."+ , ""+ , " When we speak of free software, we are referring to freedom, not"+ , "price. Our General Public Licenses are designed to make sure that you"+ , "have the freedom to distribute copies of free software (and charge for"+ , "them if you wish), that you receive source code or can get it if you"+ , "want it, that you can change the software or use pieces of it in new"+ , "free programs, and that you know you can do these things."+ , ""+ , " To protect your rights, we need to prevent others from denying you"+ , "these rights or asking you to surrender the rights. Therefore, you have"+ , "certain responsibilities if you distribute copies of the software, or if"+ , "you modify it: responsibilities to respect the freedom of others."+ , ""+ , " For example, if you distribute copies of such a program, whether"+ , "gratis or for a fee, you must pass on to the recipients the same"+ , "freedoms that you received. You must make sure that they, too, receive"+ , "or can get the source code. And you must show them these terms so they"+ , "know their rights."+ , ""+ , " Developers that use the GNU GPL protect your rights with two steps:"+ , "(1) assert copyright on the software, and (2) offer you this License"+ , "giving you legal permission to copy, distribute and/or modify it."+ , ""+ , " For the developers' and authors' protection, the GPL clearly explains"+ , "that there is no warranty for this free software. For both users' and"+ , "authors' sake, the GPL requires that modified versions be marked as"+ , "changed, so that their problems will not be attributed erroneously to"+ , "authors of previous versions."+ , ""+ , " Some devices are designed to deny users access to install or run"+ , "modified versions of the software inside them, although the manufacturer"+ , "can do so. This is fundamentally incompatible with the aim of"+ , "protecting users' freedom to change the software. The systematic"+ , "pattern of such abuse occurs in the area of products for individuals to"+ , "use, which is precisely where it is most unacceptable. Therefore, we"+ , "have designed this version of the GPL to prohibit the practice for those"+ , "products. If such problems arise substantially in other domains, we"+ , "stand ready to extend this provision to those domains in future versions"+ , "of the GPL, as needed to protect the freedom of users."+ , ""+ , " Finally, every program is threatened constantly by software patents."+ , "States should not allow patents to restrict development and use of"+ , "software on general-purpose computers, but in those that do, we wish to"+ , "avoid the special danger that patents applied to a free program could"+ , "make it effectively proprietary. To prevent this, the GPL assures that"+ , "patents cannot be used to render the program non-free."+ , ""+ , " The precise terms and conditions for copying, distribution and"+ , "modification follow."+ , ""+ , " TERMS AND CONDITIONS"+ , ""+ , " 0. Definitions."+ , ""+ , " \"This License\" refers to version 3 of the GNU General Public License."+ , ""+ , " \"Copyright\" also means copyright-like laws that apply to other kinds of"+ , "works, such as semiconductor masks."+ , " "+ , " \"The Program\" refers to any copyrightable work licensed under this"+ , "License. Each licensee is addressed as \"you\". \"Licensees\" and"+ , "\"recipients\" may be individuals or organizations."+ , ""+ , " To \"modify\" a work means to copy from or adapt all or part of the work"+ , "in a fashion requiring copyright permission, other than the making of an"+ , "exact copy. The resulting work is called a \"modified version\" of the"+ , "earlier work or a work \"based on\" the earlier work."+ , ""+ , " A \"covered work\" means either the unmodified Program or a work based"+ , "on the Program."+ , ""+ , " To \"propagate\" a work means to do anything with it that, without"+ , "permission, would make you directly or secondarily liable for"+ , "infringement under applicable copyright law, except executing it on a"+ , "computer or modifying a private copy. Propagation includes copying,"+ , "distribution (with or without modification), making available to the"+ , "public, and in some countries other activities as well."+ , ""+ , " To \"convey\" a work means any kind of propagation that enables other"+ , "parties to make or receive copies. Mere interaction with a user through"+ , "a computer network, with no transfer of a copy, is not conveying."+ , ""+ , " An interactive user interface displays \"Appropriate Legal Notices\""+ , "to the extent that it includes a convenient and prominently visible"+ , "feature that (1) displays an appropriate copyright notice, and (2)"+ , "tells the user that there is no warranty for the work (except to the"+ , "extent that warranties are provided), that licensees may convey the"+ , "work under this License, and how to view a copy of this License. If"+ , "the interface presents a list of user commands or options, such as a"+ , "menu, a prominent item in the list meets this criterion."+ , ""+ , " 1. Source Code."+ , ""+ , " The \"source code\" for a work means the preferred form of the work"+ , "for making modifications to it. \"Object code\" means any non-source"+ , "form of a work."+ , ""+ , " A \"Standard Interface\" means an interface that either is an official"+ , "standard defined by a recognized standards body, or, in the case of"+ , "interfaces specified for a particular programming language, one that"+ , "is widely used among developers working in that language."+ , ""+ , " The \"System Libraries\" of an executable work include anything, other"+ , "than the work as a whole, that (a) is included in the normal form of"+ , "packaging a Major Component, but which is not part of that Major"+ , "Component, and (b) serves only to enable use of the work with that"+ , "Major Component, or to implement a Standard Interface for which an"+ , "implementation is available to the public in source code form. A"+ , "\"Major Component\", in this context, means a major essential component"+ , "(kernel, window system, and so on) of the specific operating system"+ , "(if any) on which the executable work runs, or a compiler used to"+ , "produce the work, or an object code interpreter used to run it."+ , ""+ , " The \"Corresponding Source\" for a work in object code form means all"+ , "the source code needed to generate, install, and (for an executable"+ , "work) run the object code and to modify the work, including scripts to"+ , "control those activities. However, it does not include the work's"+ , "System Libraries, or general-purpose tools or generally available free"+ , "programs which are used unmodified in performing those activities but"+ , "which are not part of the work. For example, Corresponding Source"+ , "includes interface definition files associated with source files for"+ , "the work, and the source code for shared libraries and dynamically"+ , "linked subprograms that the work is specifically designed to require,"+ , "such as by intimate data communication or control flow between those"+ , "subprograms and other parts of the work."+ , ""+ , " The Corresponding Source need not include anything that users"+ , "can regenerate automatically from other parts of the Corresponding"+ , "Source."+ , ""+ , " The Corresponding Source for a work in source code form is that"+ , "same work."+ , ""+ , " 2. Basic Permissions."+ , ""+ , " All rights granted under this License are granted for the term of"+ , "copyright on the Program, and are irrevocable provided the stated"+ , "conditions are met. This License explicitly affirms your unlimited"+ , "permission to run the unmodified Program. The output from running a"+ , "covered work is covered by this License only if the output, given its"+ , "content, constitutes a covered work. This License acknowledges your"+ , "rights of fair use or other equivalent, as provided by copyright law."+ , ""+ , " You may make, run and propagate covered works that you do not"+ , "convey, without conditions so long as your license otherwise remains"+ , "in force. You may convey covered works to others for the sole purpose"+ , "of having them make modifications exclusively for you, or provide you"+ , "with facilities for running those works, provided that you comply with"+ , "the terms of this License in conveying all material for which you do"+ , "not control copyright. Those thus making or running the covered works"+ , "for you must do so exclusively on your behalf, under your direction"+ , "and control, on terms that prohibit them from making any copies of"+ , "your copyrighted material outside their relationship with you."+ , ""+ , " Conveying under any other circumstances is permitted solely under"+ , "the conditions stated below. Sublicensing is not allowed; section 10"+ , "makes it unnecessary."+ , ""+ , " 3. Protecting Users' Legal Rights From Anti-Circumvention Law."+ , ""+ , " No covered work shall be deemed part of an effective technological"+ , "measure under any applicable law fulfilling obligations under article"+ , "11 of the WIPO copyright treaty adopted on 20 December 1996, or"+ , "similar laws prohibiting or restricting circumvention of such"+ , "measures."+ , ""+ , " When you convey a covered work, you waive any legal power to forbid"+ , "circumvention of technological measures to the extent such circumvention"+ , "is effected by exercising rights under this License with respect to"+ , "the covered work, and you disclaim any intention to limit operation or"+ , "modification of the work as a means of enforcing, against the work's"+ , "users, your or third parties' legal rights to forbid circumvention of"+ , "technological measures."+ , ""+ , " 4. Conveying Verbatim Copies."+ , ""+ , " You may convey verbatim copies of the Program's source code as you"+ , "receive it, in any medium, provided that you conspicuously and"+ , "appropriately publish on each copy an appropriate copyright notice;"+ , "keep intact all notices stating that this License and any"+ , "non-permissive terms added in accord with section 7 apply to the code;"+ , "keep intact all notices of the absence of any warranty; and give all"+ , "recipients a copy of this License along with the Program."+ , ""+ , " You may charge any price or no price for each copy that you convey,"+ , "and you may offer support or warranty protection for a fee."+ , ""+ , " 5. Conveying Modified Source Versions."+ , ""+ , " You may convey a work based on the Program, or the modifications to"+ , "produce it from the Program, in the form of source code under the"+ , "terms of section 4, provided that you also meet all of these conditions:"+ , ""+ , " a) The work must carry prominent notices stating that you modified"+ , " it, and giving a relevant date."+ , ""+ , " b) The work must carry prominent notices stating that it is"+ , " released under this License and any conditions added under section"+ , " 7. This requirement modifies the requirement in section 4 to"+ , " \"keep intact all notices\"."+ , ""+ , " c) You must license the entire work, as a whole, under this"+ , " License to anyone who comes into possession of a copy. This"+ , " License will therefore apply, along with any applicable section 7"+ , " additional terms, to the whole of the work, and all its parts,"+ , " regardless of how they are packaged. This License gives no"+ , " permission to license the work in any other way, but it does not"+ , " invalidate such permission if you have separately received it."+ , ""+ , " d) If the work has interactive user interfaces, each must display"+ , " Appropriate Legal Notices; however, if the Program has interactive"+ , " interfaces that do not display Appropriate Legal Notices, your"+ , " work need not make them do so."+ , ""+ , " A compilation of a covered work with other separate and independent"+ , "works, which are not by their nature extensions of the covered work,"+ , "and which are not combined with it such as to form a larger program,"+ , "in or on a volume of a storage or distribution medium, is called an"+ , "\"aggregate\" if the compilation and its resulting copyright are not"+ , "used to limit the access or legal rights of the compilation's users"+ , "beyond what the individual works permit. Inclusion of a covered work"+ , "in an aggregate does not cause this License to apply to the other"+ , "parts of the aggregate."+ , ""+ , " 6. Conveying Non-Source Forms."+ , ""+ , " You may convey a covered work in object code form under the terms"+ , "of sections 4 and 5, provided that you also convey the"+ , "machine-readable Corresponding Source under the terms of this License,"+ , "in one of these ways:"+ , ""+ , " a) Convey the object code in, or embodied in, a physical product"+ , " (including a physical distribution medium), accompanied by the"+ , " Corresponding Source fixed on a durable physical medium"+ , " customarily used for software interchange."+ , ""+ , " b) Convey the object code in, or embodied in, a physical product"+ , " (including a physical distribution medium), accompanied by a"+ , " written offer, valid for at least three years and valid for as"+ , " long as you offer spare parts or customer support for that product"+ , " model, to give anyone who possesses the object code either (1) a"+ , " copy of the Corresponding Source for all the software in the"+ , " product that is covered by this License, on a durable physical"+ , " medium customarily used for software interchange, for a price no"+ , " more than your reasonable cost of physically performing this"+ , " conveying of source, or (2) access to copy the"+ , " Corresponding Source from a network server at no charge."+ , ""+ , " c) Convey individual copies of the object code with a copy of the"+ , " written offer to provide the Corresponding Source. This"+ , " alternative is allowed only occasionally and noncommercially, and"+ , " only if you received the object code with such an offer, in accord"+ , " with subsection 6b."+ , ""+ , " d) Convey the object code by offering access from a designated"+ , " place (gratis or for a charge), and offer equivalent access to the"+ , " Corresponding Source in the same way through the same place at no"+ , " further charge. You need not require recipients to copy the"+ , " Corresponding Source along with the object code. If the place to"+ , " copy the object code is a network server, the Corresponding Source"+ , " may be on a different server (operated by you or a third party)"+ , " that supports equivalent copying facilities, provided you maintain"+ , " clear directions next to the object code saying where to find the"+ , " Corresponding Source. Regardless of what server hosts the"+ , " Corresponding Source, you remain obligated to ensure that it is"+ , " available for as long as needed to satisfy these requirements."+ , ""+ , " e) Convey the object code using peer-to-peer transmission, provided"+ , " you inform other peers where the object code and Corresponding"+ , " Source of the work are being offered to the general public at no"+ , " charge under subsection 6d."+ , ""+ , " A separable portion of the object code, whose source code is excluded"+ , "from the Corresponding Source as a System Library, need not be"+ , "included in conveying the object code work."+ , ""+ , " A \"User Product\" is either (1) a \"consumer product\", which means any"+ , "tangible personal property which is normally used for personal, family,"+ , "or household purposes, or (2) anything designed or sold for incorporation"+ , "into a dwelling. In determining whether a product is a consumer product,"+ , "doubtful cases shall be resolved in favor of coverage. For a particular"+ , "product received by a particular user, \"normally used\" refers to a"+ , "typical or common use of that class of product, regardless of the status"+ , "of the particular user or of the way in which the particular user"+ , "actually uses, or expects or is expected to use, the product. A product"+ , "is a consumer product regardless of whether the product has substantial"+ , "commercial, industrial or non-consumer uses, unless such uses represent"+ , "the only significant mode of use of the product."+ , ""+ , " \"Installation Information\" for a User Product means any methods,"+ , "procedures, authorization keys, or other information required to install"+ , "and execute modified versions of a covered work in that User Product from"+ , "a modified version of its Corresponding Source. The information must"+ , "suffice to ensure that the continued functioning of the modified object"+ , "code is in no case prevented or interfered with solely because"+ , "modification has been made."+ , ""+ , " If you convey an object code work under this section in, or with, or"+ , "specifically for use in, a User Product, and the conveying occurs as"+ , "part of a transaction in which the right of possession and use of the"+ , "User Product is transferred to the recipient in perpetuity or for a"+ , "fixed term (regardless of how the transaction is characterized), the"+ , "Corresponding Source conveyed under this section must be accompanied"+ , "by the Installation Information. But this requirement does not apply"+ , "if neither you nor any third party retains the ability to install"+ , "modified object code on the User Product (for example, the work has"+ , "been installed in ROM)."+ , ""+ , " The requirement to provide Installation Information does not include a"+ , "requirement to continue to provide support service, warranty, or updates"+ , "for a work that has been modified or installed by the recipient, or for"+ , "the User Product in which it has been modified or installed. Access to a"+ , "network may be denied when the modification itself materially and"+ , "adversely affects the operation of the network or violates the rules and"+ , "protocols for communication across the network."+ , ""+ , " Corresponding Source conveyed, and Installation Information provided,"+ , "in accord with this section must be in a format that is publicly"+ , "documented (and with an implementation available to the public in"+ , "source code form), and must require no special password or key for"+ , "unpacking, reading or copying."+ , ""+ , " 7. Additional Terms."+ , ""+ , " \"Additional permissions\" are terms that supplement the terms of this"+ , "License by making exceptions from one or more of its conditions."+ , "Additional permissions that are applicable to the entire Program shall"+ , "be treated as though they were included in this License, to the extent"+ , "that they are valid under applicable law. If additional permissions"+ , "apply only to part of the Program, that part may be used separately"+ , "under those permissions, but the entire Program remains governed by"+ , "this License without regard to the additional permissions."+ , ""+ , " When you convey a copy of a covered work, you may at your option"+ , "remove any additional permissions from that copy, or from any part of"+ , "it. (Additional permissions may be written to require their own"+ , "removal in certain cases when you modify the work.) You may place"+ , "additional permissions on material, added by you to a covered work,"+ , "for which you have or can give appropriate copyright permission."+ , ""+ , " Notwithstanding any other provision of this License, for material you"+ , "add to a covered work, you may (if authorized by the copyright holders of"+ , "that material) supplement the terms of this License with terms:"+ , ""+ , " a) Disclaiming warranty or limiting liability differently from the"+ , " terms of sections 15 and 16 of this License; or"+ , ""+ , " b) Requiring preservation of specified reasonable legal notices or"+ , " author attributions in that material or in the Appropriate Legal"+ , " Notices displayed by works containing it; or"+ , ""+ , " c) Prohibiting misrepresentation of the origin of that material, or"+ , " requiring that modified versions of such material be marked in"+ , " reasonable ways as different from the original version; or"+ , ""+ , " d) Limiting the use for publicity purposes of names of licensors or"+ , " authors of the material; or"+ , ""+ , " e) Declining to grant rights under trademark law for use of some"+ , " trade names, trademarks, or service marks; or"+ , ""+ , " f) Requiring indemnification of licensors and authors of that"+ , " material by anyone who conveys the material (or modified versions of"+ , " it) with contractual assumptions of liability to the recipient, for"+ , " any liability that these contractual assumptions directly impose on"+ , " those licensors and authors."+ , ""+ , " All other non-permissive additional terms are considered \"further"+ , "restrictions\" within the meaning of section 10. If the Program as you"+ , "received it, or any part of it, contains a notice stating that it is"+ , "governed by this License along with a term that is a further"+ , "restriction, you may remove that term. If a license document contains"+ , "a further restriction but permits relicensing or conveying under this"+ , "License, you may add to a covered work material governed by the terms"+ , "of that license document, provided that the further restriction does"+ , "not survive such relicensing or conveying."+ , ""+ , " If you add terms to a covered work in accord with this section, you"+ , "must place, in the relevant source files, a statement of the"+ , "additional terms that apply to those files, or a notice indicating"+ , "where to find the applicable terms."+ , ""+ , " Additional terms, permissive or non-permissive, may be stated in the"+ , "form of a separately written license, or stated as exceptions;"+ , "the above requirements apply either way."+ , ""+ , " 8. Termination."+ , ""+ , " You may not propagate or modify a covered work except as expressly"+ , "provided under this License. Any attempt otherwise to propagate or"+ , "modify it is void, and will automatically terminate your rights under"+ , "this License (including any patent licenses granted under the third"+ , "paragraph of section 11)."+ , ""+ , " However, if you cease all violation of this License, then your"+ , "license from a particular copyright holder is reinstated (a)"+ , "provisionally, unless and until the copyright holder explicitly and"+ , "finally terminates your license, and (b) permanently, if the copyright"+ , "holder fails to notify you of the violation by some reasonable means"+ , "prior to 60 days after the cessation."+ , ""+ , " Moreover, your license from a particular copyright holder is"+ , "reinstated permanently if the copyright holder notifies you of the"+ , "violation by some reasonable means, this is the first time you have"+ , "received notice of violation of this License (for any work) from that"+ , "copyright holder, and you cure the violation prior to 30 days after"+ , "your receipt of the notice."+ , ""+ , " Termination of your rights under this section does not terminate the"+ , "licenses of parties who have received copies or rights from you under"+ , "this License. If your rights have been terminated and not permanently"+ , "reinstated, you do not qualify to receive new licenses for the same"+ , "material under section 10."+ , ""+ , " 9. Acceptance Not Required for Having Copies."+ , ""+ , " You are not required to accept this License in order to receive or"+ , "run a copy of the Program. Ancillary propagation of a covered work"+ , "occurring solely as a consequence of using peer-to-peer transmission"+ , "to receive a copy likewise does not require acceptance. However,"+ , "nothing other than this License grants you permission to propagate or"+ , "modify any covered work. These actions infringe copyright if you do"+ , "not accept this License. Therefore, by modifying or propagating a"+ , "covered work, you indicate your acceptance of this License to do so."+ , ""+ , " 10. Automatic Licensing of Downstream Recipients."+ , ""+ , " Each time you convey a covered work, the recipient automatically"+ , "receives a license from the original licensors, to run, modify and"+ , "propagate that work, subject to this License. You are not responsible"+ , "for enforcing compliance by third parties with this License."+ , ""+ , " An \"entity transaction\" is a transaction transferring control of an"+ , "organization, or substantially all assets of one, or subdividing an"+ , "organization, or merging organizations. If propagation of a covered"+ , "work results from an entity transaction, each party to that"+ , "transaction who receives a copy of the work also receives whatever"+ , "licenses to the work the party's predecessor in interest had or could"+ , "give under the previous paragraph, plus a right to possession of the"+ , "Corresponding Source of the work from the predecessor in interest, if"+ , "the predecessor has it or can get it with reasonable efforts."+ , ""+ , " You may not impose any further restrictions on the exercise of the"+ , "rights granted or affirmed under this License. For example, you may"+ , "not impose a license fee, royalty, or other charge for exercise of"+ , "rights granted under this License, and you may not initiate litigation"+ , "(including a cross-claim or counterclaim in a lawsuit) alleging that"+ , "any patent claim is infringed by making, using, selling, offering for"+ , "sale, or importing the Program or any portion of it."+ , ""+ , " 11. Patents."+ , ""+ , " A \"contributor\" is a copyright holder who authorizes use under this"+ , "License of the Program or a work on which the Program is based. The"+ , "work thus licensed is called the contributor's \"contributor version\"."+ , ""+ , " A contributor's \"essential patent claims\" are all patent claims"+ , "owned or controlled by the contributor, whether already acquired or"+ , "hereafter acquired, that would be infringed by some manner, permitted"+ , "by this License, of making, using, or selling its contributor version,"+ , "but do not include claims that would be infringed only as a"+ , "consequence of further modification of the contributor version. For"+ , "purposes of this definition, \"control\" includes the right to grant"+ , "patent sublicenses in a manner consistent with the requirements of"+ , "this License."+ , ""+ , " Each contributor grants you a non-exclusive, worldwide, royalty-free"+ , "patent license under the contributor's essential patent claims, to"+ , "make, use, sell, offer for sale, import and otherwise run, modify and"+ , "propagate the contents of its contributor version."+ , ""+ , " In the following three paragraphs, a \"patent license\" is any express"+ , "agreement or commitment, however denominated, not to enforce a patent"+ , "(such as an express permission to practice a patent or covenant not to"+ , "sue for patent infringement). To \"grant\" such a patent license to a"+ , "party means to make such an agreement or commitment not to enforce a"+ , "patent against the party."+ , ""+ , " If you convey a covered work, knowingly relying on a patent license,"+ , "and the Corresponding Source of the work is not available for anyone"+ , "to copy, free of charge and under the terms of this License, through a"+ , "publicly available network server or other readily accessible means,"+ , "then you must either (1) cause the Corresponding Source to be so"+ , "available, or (2) arrange to deprive yourself of the benefit of the"+ , "patent license for this particular work, or (3) arrange, in a manner"+ , "consistent with the requirements of this License, to extend the patent"+ , "license to downstream recipients. \"Knowingly relying\" means you have"+ , "actual knowledge that, but for the patent license, your conveying the"+ , "covered work in a country, or your recipient's use of the covered work"+ , "in a country, would infringe one or more identifiable patents in that"+ , "country that you have reason to believe are valid."+ , " "+ , " If, pursuant to or in connection with a single transaction or"+ , "arrangement, you convey, or propagate by procuring conveyance of, a"+ , "covered work, and grant a patent license to some of the parties"+ , "receiving the covered work authorizing them to use, propagate, modify"+ , "or convey a specific copy of the covered work, then the patent license"+ , "you grant is automatically extended to all recipients of the covered"+ , "work and works based on it."+ , ""+ , " A patent license is \"discriminatory\" if it does not include within"+ , "the scope of its coverage, prohibits the exercise of, or is"+ , "conditioned on the non-exercise of one or more of the rights that are"+ , "specifically granted under this License. You may not convey a covered"+ , "work if you are a party to an arrangement with a third party that is"+ , "in the business of distributing software, under which you make payment"+ , "to the third party based on the extent of your activity of conveying"+ , "the work, and under which the third party grants, to any of the"+ , "parties who would receive the covered work from you, a discriminatory"+ , "patent license (a) in connection with copies of the covered work"+ , "conveyed by you (or copies made from those copies), or (b) primarily"+ , "for and in connection with specific products or compilations that"+ , "contain the covered work, unless you entered into that arrangement,"+ , "or that patent license was granted, prior to 28 March 2007."+ , ""+ , " Nothing in this License shall be construed as excluding or limiting"+ , "any implied license or other defenses to infringement that may"+ , "otherwise be available to you under applicable patent law."+ , ""+ , " 12. No Surrender of Others' Freedom."+ , ""+ , " If conditions are imposed on you (whether by court order, agreement or"+ , "otherwise) that contradict the conditions of this License, they do not"+ , "excuse you from the conditions of this License. If you cannot convey a"+ , "covered work so as to satisfy simultaneously your obligations under this"+ , "License and any other pertinent obligations, then as a consequence you may"+ , "not convey it at all. For example, if you agree to terms that obligate you"+ , "to collect a royalty for further conveying from those to whom you convey"+ , "the Program, the only way you could satisfy both those terms and this"+ , "License would be to refrain entirely from conveying the Program."+ , ""+ , " 13. Use with the GNU Affero General Public License."+ , ""+ , " Notwithstanding any other provision of this License, you have"+ , "permission to link or combine any covered work with a work licensed"+ , "under version 3 of the GNU Affero General Public License into a single"+ , "combined work, and to convey the resulting work. The terms of this"+ , "License will continue to apply to the part which is the covered work,"+ , "but the special requirements of the GNU Affero General Public License,"+ , "section 13, concerning interaction through a network will apply to the"+ , "combination as such."+ , ""+ , " 14. Revised Versions of this License."+ , ""+ , " The Free Software Foundation may publish revised and/or new versions of"+ , "the GNU General Public License from time to time. Such new versions will"+ , "be similar in spirit to the present version, but may differ in detail to"+ , "address new problems or concerns."+ , ""+ , " Each version is given a distinguishing version number. If the"+ , "Program specifies that a certain numbered version of the GNU General"+ , "Public License \"or any later version\" applies to it, you have the"+ , "option of following the terms and conditions either of that numbered"+ , "version or of any later version published by the Free Software"+ , "Foundation. If the Program does not specify a version number of the"+ , "GNU General Public License, you may choose any version ever published"+ , "by the Free Software Foundation."+ , ""+ , " If the Program specifies that a proxy can decide which future"+ , "versions of the GNU General Public License can be used, that proxy's"+ , "public statement of acceptance of a version permanently authorizes you"+ , "to choose that version for the Program."+ , ""+ , " Later license versions may give you additional or different"+ , "permissions. However, no additional obligations are imposed on any"+ , "author or copyright holder as a result of your choosing to follow a"+ , "later version."+ , ""+ , " 15. Disclaimer of Warranty."+ , ""+ , " THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY"+ , "APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT"+ , "HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY"+ , "OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,"+ , "THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR"+ , "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM"+ , "IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF"+ , "ALL NECESSARY SERVICING, REPAIR OR CORRECTION."+ , ""+ , " 16. Limitation of Liability."+ , ""+ , " IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING"+ , "WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS"+ , "THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY"+ , "GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE"+ , "USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF"+ , "DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD"+ , "PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),"+ , "EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF"+ , "SUCH DAMAGES."+ , ""+ , " 17. Interpretation of Sections 15 and 16."+ , ""+ , " If the disclaimer of warranty and limitation of liability provided"+ , "above cannot be given local legal effect according to their terms,"+ , "reviewing courts shall apply local law that most closely approximates"+ , "an absolute waiver of all civil liability in connection with the"+ , "Program, unless a warranty or assumption of liability accompanies a"+ , "copy of the Program in return for a fee."+ , ""+ , " END OF TERMS AND CONDITIONS"+ , ""+ , " How to Apply These Terms to Your New Programs"+ , ""+ , " If you develop a new program, and you want it to be of the greatest"+ , "possible use to the public, the best way to achieve this is to make it"+ , "free software which everyone can redistribute and change under these terms."+ , ""+ , " To do so, attach the following notices to the program. It is safest"+ , "to attach them to the start of each source file to most effectively"+ , "state the exclusion of warranty; and each file should have at least"+ , "the \"copyright\" line and a pointer to where the full notice is found."+ , ""+ , " <one line to give the program's name and a brief idea of what it does.>"+ , " Copyright (C) <year> <name of author>"+ , ""+ , " 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 3 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, see <http://www.gnu.org/licenses/>."+ , ""+ , "Also add information on how to contact you by electronic and paper mail."+ , ""+ , " If the program does terminal interaction, make it output a short"+ , "notice like this when it starts in an interactive mode:"+ , ""+ , " <program> Copyright (C) <year> <name of author>"+ , " This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'."+ , " This is free software, and you are welcome to redistribute it"+ , " under certain conditions; type `show c' for details."+ , ""+ , "The hypothetical commands `show w' and `show c' should show the appropriate"+ , "parts of the General Public License. Of course, your program's commands"+ , "might be different; for a GUI interface, you would use an \"about box\"."+ , ""+ , " You should also get your employer (if you work as a programmer) or school,"+ , "if any, to sign a \"copyright disclaimer\" for the program, if necessary."+ , "For more information on this, and how to apply and follow the GNU GPL, see"+ , "<http://www.gnu.org/licenses/>."+ , ""+ , " The GNU General Public License does not permit incorporating your program"+ , "into proprietary programs. If your program is a subroutine library, you"+ , "may consider it more useful to permit linking proprietary applications with"+ , "the library. If this is what you want to do, use the GNU Lesser General"+ , "Public License instead of this License. But first, please read"+ , "<http://www.gnu.org/philosophy/why-not-lgpl.html>."+ , ""+ ]++lgpl2 :: License+lgpl2 = unlines+ [ " GNU LIBRARY GENERAL PUBLIC LICENSE"+ , " Version 2, June 1991"+ , ""+ , " Copyright (C) 1991 Free Software Foundation, Inc."+ , " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"+ , " Everyone is permitted to copy and distribute verbatim copies"+ , " of this license document, but changing it is not allowed."+ , ""+ , "[This is the first released version of the library GPL. It is"+ , " numbered 2 because it goes with version 2 of the ordinary GPL.]"+ , ""+ , " Preamble"+ , ""+ , " The licenses for most software are designed to take away your"+ , "freedom to share and change it. By contrast, the GNU General Public"+ , "Licenses are intended to guarantee your freedom to share and change"+ , "free software--to make sure the software is free for all its users."+ , ""+ , " This license, the Library General Public License, applies to some"+ , "specially designated Free Software Foundation software, and to any"+ , "other libraries whose authors decide to use it. You can use it for"+ , "your libraries, too."+ , ""+ , " When we speak of free software, we are referring to freedom, not"+ , "price. Our General Public Licenses are designed to make sure that you"+ , "have the freedom to distribute copies of free software (and charge for"+ , "this service if you wish), that you receive source code or can get it"+ , "if you want it, that you can change the software or use pieces of it"+ , "in new free programs; and that you know you can do these things."+ , ""+ , " To protect your rights, we need to make restrictions that forbid"+ , "anyone to deny you these rights or to ask you to surrender the rights."+ , "These restrictions translate to certain responsibilities for you if"+ , "you distribute copies of the library, or if you modify it."+ , ""+ , " For example, if you distribute copies of the library, whether gratis"+ , "or for a fee, you must give the recipients all the rights that we gave"+ , "you. You must make sure that they, too, receive or can get the source"+ , "code. If you link a program with the library, you must provide"+ , "complete object files to the recipients so that they can relink them"+ , "with the library, after making changes to the library and recompiling"+ , "it. And you must show them these terms so they know their rights."+ , ""+ , " Our method of protecting your rights has two steps: (1) copyright"+ , "the library, and (2) offer you this license which gives you legal"+ , "permission to copy, distribute and/or modify the library."+ , ""+ , " Also, for each distributor's protection, we want to make certain"+ , "that everyone understands that there is no warranty for this free"+ , "library. If the library is modified by someone else and passed on, we"+ , "want its recipients to know that what they have is not the original"+ , "version, so that any problems introduced by others will not reflect on"+ , "the original authors' reputations."+ , ""+ , " Finally, any free program is threatened constantly by software"+ , "patents. We wish to avoid the danger that companies distributing free"+ , "software will individually obtain patent licenses, thus in effect"+ , "transforming the program into proprietary software. To prevent this,"+ , "we have made it clear that any patent must be licensed for everyone's"+ , "free use or not licensed at all."+ , ""+ , " Most GNU software, including some libraries, is covered by the ordinary"+ , "GNU General Public License, which was designed for utility programs. This"+ , "license, the GNU Library General Public License, applies to certain"+ , "designated libraries. This license is quite different from the ordinary"+ , "one; be sure to read it in full, and don't assume that anything in it is"+ , "the same as in the ordinary license."+ , ""+ , " The reason we have a separate public license for some libraries is that"+ , "they blur the distinction we usually make between modifying or adding to a"+ , "program and simply using it. Linking a program with a library, without"+ , "changing the library, is in some sense simply using the library, and is"+ , "analogous to running a utility program or application program. However, in"+ , "a textual and legal sense, the linked executable is a combined work, a"+ , "derivative of the original library, and the ordinary General Public License"+ , "treats it as such."+ , ""+ , " Because of this blurred distinction, using the ordinary General"+ , "Public License for libraries did not effectively promote software"+ , "sharing, because most developers did not use the libraries. We"+ , "concluded that weaker conditions might promote sharing better."+ , ""+ , " However, unrestricted linking of non-free programs would deprive the"+ , "users of those programs of all benefit from the free status of the"+ , "libraries themselves. This Library General Public License is intended to"+ , "permit developers of non-free programs to use free libraries, while"+ , "preserving your freedom as a user of such programs to change the free"+ , "libraries that are incorporated in them. (We have not seen how to achieve"+ , "this as regards changes in header files, but we have achieved it as regards"+ , "changes in the actual functions of the Library.) The hope is that this"+ , "will lead to faster development of free libraries."+ , ""+ , " The precise terms and conditions for copying, distribution and"+ , "modification follow. Pay close attention to the difference between a"+ , "\"work based on the library\" and a \"work that uses the library\". The"+ , "former contains code derived from the library, while the latter only"+ , "works together with the library."+ , ""+ , " Note that it is possible for a library to be covered by the ordinary"+ , "General Public License rather than by this special one."+ , ""+ , " GNU LIBRARY GENERAL PUBLIC LICENSE"+ , " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION"+ , ""+ , " 0. This License Agreement applies to any software library which"+ , "contains a notice placed by the copyright holder or other authorized"+ , "party saying it may be distributed under the terms of this Library"+ , "General Public License (also called \"this License\"). Each licensee is"+ , "addressed as \"you\"."+ , ""+ , " A \"library\" means a collection of software functions and/or data"+ , "prepared so as to be conveniently linked with application programs"+ , "(which use some of those functions and data) to form executables."+ , ""+ , " The \"Library\", below, refers to any such software library or work"+ , "which has been distributed under these terms. A \"work based on the"+ , "Library\" means either the Library or any derivative work under"+ , "copyright law: that is to say, a work containing the Library or a"+ , "portion of it, either verbatim or with modifications and/or translated"+ , "straightforwardly into another language. (Hereinafter, translation is"+ , "included without limitation in the term \"modification\".)"+ , ""+ , " \"Source code\" for a work means the preferred form of the work for"+ , "making modifications to it. For a library, complete source code means"+ , "all the source code for all modules it contains, plus any associated"+ , "interface definition files, plus the scripts used to control compilation"+ , "and installation of the library."+ , ""+ , " Activities other than copying, distribution and modification are not"+ , "covered by this License; they are outside its scope. The act of"+ , "running a program using the Library is not restricted, and output from"+ , "such a program is covered only if its contents constitute a work based"+ , "on the Library (independent of the use of the Library in a tool for"+ , "writing it). Whether that is true depends on what the Library does"+ , "and what the program that uses the Library does."+ , " "+ , " 1. You may copy and distribute verbatim copies of the Library's"+ , "complete source code as you receive it, in any medium, provided that"+ , "you conspicuously and appropriately publish on each copy an"+ , "appropriate copyright notice and disclaimer of warranty; keep intact"+ , "all the notices that refer to this License and to the absence of any"+ , "warranty; and distribute a copy of this License along with the"+ , "Library."+ , ""+ , " You may charge a fee for the physical act of transferring a copy,"+ , "and you may at your option offer warranty protection in exchange for a"+ , "fee."+ , ""+ , " 2. You may modify your copy or copies of the Library or any portion"+ , "of it, thus forming a work based on the Library, and copy and"+ , "distribute such modifications or work under the terms of Section 1"+ , "above, provided that you also meet all of these conditions:"+ , ""+ , " a) The modified work must itself be a software library."+ , ""+ , " b) You must cause the files modified to carry prominent notices"+ , " stating that you changed the files and the date of any change."+ , ""+ , " c) You must cause the whole of the work to be licensed at no"+ , " charge to all third parties under the terms of this License."+ , ""+ , " d) If a facility in the modified Library refers to a function or a"+ , " table of data to be supplied by an application program that uses"+ , " the facility, other than as an argument passed when the facility"+ , " is invoked, then you must make a good faith effort to ensure that,"+ , " in the event an application does not supply such function or"+ , " table, the facility still operates, and performs whatever part of"+ , " its purpose remains meaningful."+ , ""+ , " (For example, a function in a library to compute square roots has"+ , " a purpose that is entirely well-defined independent of the"+ , " application. Therefore, Subsection 2d requires that any"+ , " application-supplied function or table used by this function must"+ , " be optional: if the application does not supply it, the square"+ , " root function must still compute square roots.)"+ , ""+ , "These requirements apply to the modified work as a whole. If"+ , "identifiable sections of that work are not derived from the Library,"+ , "and can be reasonably considered independent and separate works in"+ , "themselves, then this License, and its terms, do not apply to those"+ , "sections when you distribute them as separate works. But when you"+ , "distribute the same sections as part of a whole which is a work based"+ , "on the Library, the distribution of the whole must be on the terms of"+ , "this License, whose permissions for other licensees extend to the"+ , "entire whole, and thus to each and every part regardless of who wrote"+ , "it."+ , ""+ , "Thus, it is not the intent of this section to claim rights or contest"+ , "your rights to work written entirely by you; rather, the intent is to"+ , "exercise the right to control the distribution of derivative or"+ , "collective works based on the Library."+ , ""+ , "In addition, mere aggregation of another work not based on the Library"+ , "with the Library (or with a work based on the Library) on a volume of"+ , "a storage or distribution medium does not bring the other work under"+ , "the scope of this License."+ , ""+ , " 3. You may opt to apply the terms of the ordinary GNU General Public"+ , "License instead of this License to a given copy of the Library. To do"+ , "this, you must alter all the notices that refer to this License, so"+ , "that they refer to the ordinary GNU General Public License, version 2,"+ , "instead of to this License. (If a newer version than version 2 of the"+ , "ordinary GNU General Public License has appeared, then you can specify"+ , "that version instead if you wish.) Do not make any other change in"+ , "these notices."+ , ""+ , " Once this change is made in a given copy, it is irreversible for"+ , "that copy, so the ordinary GNU General Public License applies to all"+ , "subsequent copies and derivative works made from that copy."+ , ""+ , " This option is useful when you wish to copy part of the code of"+ , "the Library into a program that is not a library."+ , ""+ , " 4. You may copy and distribute the Library (or a portion or"+ , "derivative of it, under Section 2) in object code or executable form"+ , "under the terms of Sections 1 and 2 above provided that you accompany"+ , "it with the complete corresponding machine-readable source code, which"+ , "must be distributed under the terms of Sections 1 and 2 above on a"+ , "medium customarily used for software interchange."+ , ""+ , " If distribution of object code is made by offering access to copy"+ , "from a designated place, then offering equivalent access to copy the"+ , "source code from the same place satisfies the requirement to"+ , "distribute the source code, even though third parties are not"+ , "compelled to copy the source along with the object code."+ , ""+ , " 5. A program that contains no derivative of any portion of the"+ , "Library, but is designed to work with the Library by being compiled or"+ , "linked with it, is called a \"work that uses the Library\". Such a"+ , "work, in isolation, is not a derivative work of the Library, and"+ , "therefore falls outside the scope of this License."+ , ""+ , " However, linking a \"work that uses the Library\" with the Library"+ , "creates an executable that is a derivative of the Library (because it"+ , "contains portions of the Library), rather than a \"work that uses the"+ , "library\". The executable is therefore covered by this License."+ , "Section 6 states terms for distribution of such executables."+ , ""+ , " When a \"work that uses the Library\" uses material from a header file"+ , "that is part of the Library, the object code for the work may be a"+ , "derivative work of the Library even though the source code is not."+ , "Whether this is true is especially significant if the work can be"+ , "linked without the Library, or if the work is itself a library. The"+ , "threshold for this to be true is not precisely defined by law."+ , ""+ , " If such an object file uses only numerical parameters, data"+ , "structure layouts and accessors, and small macros and small inline"+ , "functions (ten lines or less in length), then the use of the object"+ , "file is unrestricted, regardless of whether it is legally a derivative"+ , "work. (Executables containing this object code plus portions of the"+ , "Library will still fall under Section 6.)"+ , ""+ , " Otherwise, if the work is a derivative of the Library, you may"+ , "distribute the object code for the work under the terms of Section 6."+ , "Any executables containing that work also fall under Section 6,"+ , "whether or not they are linked directly with the Library itself."+ , ""+ , " 6. As an exception to the Sections above, you may also compile or"+ , "link a \"work that uses the Library\" with the Library to produce a"+ , "work containing portions of the Library, and distribute that work"+ , "under terms of your choice, provided that the terms permit"+ , "modification of the work for the customer's own use and reverse"+ , "engineering for debugging such modifications."+ , ""+ , " You must give prominent notice with each copy of the work that the"+ , "Library is used in it and that the Library and its use are covered by"+ , "this License. You must supply a copy of this License. If the work"+ , "during execution displays copyright notices, you must include the"+ , "copyright notice for the Library among them, as well as a reference"+ , "directing the user to the copy of this License. Also, you must do one"+ , "of these things:"+ , ""+ , " a) Accompany the work with the complete corresponding"+ , " machine-readable source code for the Library including whatever"+ , " changes were used in the work (which must be distributed under"+ , " Sections 1 and 2 above); and, if the work is an executable linked"+ , " with the Library, with the complete machine-readable \"work that"+ , " uses the Library\", as object code and/or source code, so that the"+ , " user can modify the Library and then relink to produce a modified"+ , " executable containing the modified Library. (It is understood"+ , " that the user who changes the contents of definitions files in the"+ , " Library will not necessarily be able to recompile the application"+ , " to use the modified definitions.)"+ , ""+ , " b) Accompany the work with a written offer, valid for at"+ , " least three years, to give the same user the materials"+ , " specified in Subsection 6a, above, for a charge no more"+ , " than the cost of performing this distribution."+ , ""+ , " c) If distribution of the work is made by offering access to copy"+ , " from a designated place, offer equivalent access to copy the above"+ , " specified materials from the same place."+ , ""+ , " d) Verify that the user has already received a copy of these"+ , " materials or that you have already sent this user a copy."+ , ""+ , " For an executable, the required form of the \"work that uses the"+ , "Library\" must include any data and utility programs needed for"+ , "reproducing the executable from it. However, as a special exception,"+ , "the source code distributed need not include anything that is normally"+ , "distributed (in either source or binary form) with the major"+ , "components (compiler, kernel, and so on) of the operating system on"+ , "which the executable runs, unless that component itself accompanies"+ , "the executable."+ , ""+ , " It may happen that this requirement contradicts the license"+ , "restrictions of other proprietary libraries that do not normally"+ , "accompany the operating system. Such a contradiction means you cannot"+ , "use both them and the Library together in an executable that you"+ , "distribute."+ , ""+ , " 7. You may place library facilities that are a work based on the"+ , "Library side-by-side in a single library together with other library"+ , "facilities not covered by this License, and distribute such a combined"+ , "library, provided that the separate distribution of the work based on"+ , "the Library and of the other library facilities is otherwise"+ , "permitted, and provided that you do these two things:"+ , ""+ , " a) Accompany the combined library with a copy of the same work"+ , " based on the Library, uncombined with any other library"+ , " facilities. This must be distributed under the terms of the"+ , " Sections above."+ , ""+ , " b) Give prominent notice with the combined library of the fact"+ , " that part of it is a work based on the Library, and explaining"+ , " where to find the accompanying uncombined form of the same work."+ , ""+ , " 8. You may not copy, modify, sublicense, link with, or distribute"+ , "the Library except as expressly provided under this License. Any"+ , "attempt otherwise to copy, modify, sublicense, link with, or"+ , "distribute the Library is void, and will automatically terminate your"+ , "rights under this License. However, parties who have received copies,"+ , "or rights, from you under this License will not have their licenses"+ , "terminated so long as such parties remain in full compliance."+ , ""+ , " 9. You are not required to accept this License, since you have not"+ , "signed it. However, nothing else grants you permission to modify or"+ , "distribute the Library or its derivative works. These actions are"+ , "prohibited by law if you do not accept this License. Therefore, by"+ , "modifying or distributing the Library (or any work based on the"+ , "Library), you indicate your acceptance of this License to do so, and"+ , "all its terms and conditions for copying, distributing or modifying"+ , "the Library or works based on it."+ , ""+ , " 10. Each time you redistribute the Library (or any work based on the"+ , "Library), the recipient automatically receives a license from the"+ , "original licensor to copy, distribute, link with or modify the Library"+ , "subject to these terms and conditions. You may not impose any further"+ , "restrictions on the recipients' exercise of the rights granted herein."+ , "You are not responsible for enforcing compliance by third parties to"+ , "this License."+ , ""+ , " 11. If, as a consequence of a court judgment or allegation of patent"+ , "infringement or for any other reason (not limited to patent issues),"+ , "conditions are imposed on you (whether by court order, agreement or"+ , "otherwise) that contradict the conditions of this License, they do not"+ , "excuse you from the conditions of this License. If you cannot"+ , "distribute so as to satisfy simultaneously your obligations under this"+ , "License and any other pertinent obligations, then as a consequence you"+ , "may not distribute the Library at all. For example, if a patent"+ , "license would not permit royalty-free redistribution of the Library by"+ , "all those who receive copies directly or indirectly through you, then"+ , "the only way you could satisfy both it and this License would be to"+ , "refrain entirely from distribution of the Library."+ , ""+ , "If any portion of this section is held invalid or unenforceable under any"+ , "particular circumstance, the balance of the section is intended to apply,"+ , "and the section as a whole is intended to apply in other circumstances."+ , ""+ , "It is not the purpose of this section to induce you to infringe any"+ , "patents or other property right claims or to contest validity of any"+ , "such claims; this section has the sole purpose of protecting the"+ , "integrity of the free software distribution system which is"+ , "implemented by public license practices. Many people have made"+ , "generous contributions to the wide range of software distributed"+ , "through that system in reliance on consistent application of that"+ , "system; it is up to the author/donor to decide if he or she is willing"+ , "to distribute software through any other system and a licensee cannot"+ , "impose that choice."+ , ""+ , "This section is intended to make thoroughly clear what is believed to"+ , "be a consequence of the rest of this License."+ , ""+ , " 12. If the distribution and/or use of the Library is restricted in"+ , "certain countries either by patents or by copyrighted interfaces, the"+ , "original copyright holder who places the Library under this License may add"+ , "an explicit geographical distribution limitation excluding those countries,"+ , "so that distribution is permitted only in or among countries not thus"+ , "excluded. In such case, this License incorporates the limitation as if"+ , "written in the body of this License."+ , ""+ , " 13. The Free Software Foundation may publish revised and/or new"+ , "versions of the Library General Public License from time to time."+ , "Such new versions will be similar in spirit to the present version,"+ , "but may differ in detail to address new problems or concerns."+ , ""+ , "Each version is given a distinguishing version number. If the Library"+ , "specifies a version number of this License which applies to it and"+ , "\"any later version\", you have the option of following the terms and"+ , "conditions either of that version or of any later version published by"+ , "the Free Software Foundation. If the Library does not specify a"+ , "license version number, you may choose any version ever published by"+ , "the Free Software Foundation."+ , ""+ , " 14. If you wish to incorporate parts of the Library into other free"+ , "programs whose distribution conditions are incompatible with these,"+ , "write to the author to ask for permission. For software which is"+ , "copyrighted by the Free Software Foundation, write to the Free"+ , "Software Foundation; we sometimes make exceptions for this. Our"+ , "decision will be guided by the two goals of preserving the free status"+ , "of all derivatives of our free software and of promoting the sharing"+ , "and reuse of software generally."+ , ""+ , " NO WARRANTY"+ , ""+ , " 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO"+ , "WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW."+ , "EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR"+ , "OTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY"+ , "KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE"+ , "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR"+ , "PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE"+ , "LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME"+ , "THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION."+ , ""+ , " 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN"+ , "WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY"+ , "AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU"+ , "FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR"+ , "CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE"+ , "LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING"+ , "RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A"+ , "FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF"+ , "SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH"+ , "DAMAGES."+ , ""+ , " END OF TERMS AND CONDITIONS"+ , ""+ , " How to Apply These Terms to Your New Libraries"+ , ""+ , " If you develop a new library, and you want it to be of the greatest"+ , "possible use to the public, we recommend making it free software that"+ , "everyone can redistribute and change. You can do so by permitting"+ , "redistribution under these terms (or, alternatively, under the terms of the"+ , "ordinary General Public License)."+ , ""+ , " To apply these terms, attach the following notices to the library. It is"+ , "safest to attach them to the start of each source file to most effectively"+ , "convey the exclusion of warranty; and each file should have at least the"+ , "\"copyright\" line and a pointer to where the full notice is found."+ , ""+ , " <one line to give the library's name and a brief idea of what it does.>"+ , " Copyright (C) <year> <name of author>"+ , ""+ , " This library is free software; you can redistribute it and/or"+ , " modify it under the terms of the GNU Library General Public"+ , " License as published by the Free Software Foundation; either"+ , " version 2 of the License, or (at your option) any later version."+ , ""+ , " This library 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"+ , " Library General Public License for more details."+ , ""+ , " You should have received a copy of the GNU Library General Public"+ , " License along with this library; if not, write to the Free"+ , " Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA"+ , ""+ , "Also add information on how to contact you by electronic and paper mail."+ , ""+ , "You should also get your employer (if you work as a programmer) or your"+ , "school, if any, to sign a \"copyright disclaimer\" for the library, if"+ , "necessary. Here is a sample; alter the names:"+ , ""+ , " Yoyodyne, Inc., hereby disclaims all copyright interest in the"+ , " library `Frob' (a library for tweaking knobs) written by James Random Hacker."+ , ""+ , " <signature of Ty Coon>, 1 April 1990"+ , " Ty Coon, President of Vice"+ , ""+ , "That's all there is to it!"+ ]++lgpl3 :: License+lgpl3 = unlines+ [ " GNU LESSER GENERAL PUBLIC LICENSE"+ , " Version 3, 29 June 2007"+ , ""+ , " Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>"+ , " Everyone is permitted to copy and distribute verbatim copies"+ , " of this license document, but changing it is not allowed."+ , ""+ , ""+ , " This version of the GNU Lesser General Public License incorporates"+ , "the terms and conditions of version 3 of the GNU General Public"+ , "License, supplemented by the additional permissions listed below."+ , ""+ , " 0. Additional Definitions. "+ , ""+ , " As used herein, \"this License\" refers to version 3 of the GNU Lesser"+ , "General Public License, and the \"GNU GPL\" refers to version 3 of the GNU"+ , "General Public License."+ , ""+ , " \"The Library\" refers to a covered work governed by this License,"+ , "other than an Application or a Combined Work as defined below."+ , ""+ , " An \"Application\" is any work that makes use of an interface provided"+ , "by the Library, but which is not otherwise based on the Library."+ , "Defining a subclass of a class defined by the Library is deemed a mode"+ , "of using an interface provided by the Library."+ , ""+ , " A \"Combined Work\" is a work produced by combining or linking an"+ , "Application with the Library. The particular version of the Library"+ , "with which the Combined Work was made is also called the \"Linked"+ , "Version\"."+ , ""+ , " The \"Minimal Corresponding Source\" for a Combined Work means the"+ , "Corresponding Source for the Combined Work, excluding any source code"+ , "for portions of the Combined Work that, considered in isolation, are"+ , "based on the Application, and not on the Linked Version."+ , ""+ , " The \"Corresponding Application Code\" for a Combined Work means the"+ , "object code and/or source code for the Application, including any data"+ , "and utility programs needed for reproducing the Combined Work from the"+ , "Application, but excluding the System Libraries of the Combined Work."+ , ""+ , " 1. Exception to Section 3 of the GNU GPL."+ , ""+ , " You may convey a covered work under sections 3 and 4 of this License"+ , "without being bound by section 3 of the GNU GPL."+ , ""+ , " 2. Conveying Modified Versions."+ , ""+ , " If you modify a copy of the Library, and, in your modifications, a"+ , "facility refers to a function or data to be supplied by an Application"+ , "that uses the facility (other than as an argument passed when the"+ , "facility is invoked), then you may convey a copy of the modified"+ , "version:"+ , ""+ , " a) under this License, provided that you make a good faith effort to"+ , " ensure that, in the event an Application does not supply the"+ , " function or data, the facility still operates, and performs"+ , " whatever part of its purpose remains meaningful, or"+ , ""+ , " b) under the GNU GPL, with none of the additional permissions of"+ , " this License applicable to that copy."+ , ""+ , " 3. Object Code Incorporating Material from Library Header Files."+ , ""+ , " The object code form of an Application may incorporate material from"+ , "a header file that is part of the Library. You may convey such object"+ , "code under terms of your choice, provided that, if the incorporated"+ , "material is not limited to numerical parameters, data structure"+ , "layouts and accessors, or small macros, inline functions and templates"+ , "(ten or fewer lines in length), you do both of the following:"+ , ""+ , " a) Give prominent notice with each copy of the object code that the"+ , " Library is used in it and that the Library and its use are"+ , " covered by this License."+ , ""+ , " b) Accompany the object code with a copy of the GNU GPL and this license"+ , " document."+ , ""+ , " 4. Combined Works."+ , ""+ , " You may convey a Combined Work under terms of your choice that,"+ , "taken together, effectively do not restrict modification of the"+ , "portions of the Library contained in the Combined Work and reverse"+ , "engineering for debugging such modifications, if you also do each of"+ , "the following:"+ , ""+ , " a) Give prominent notice with each copy of the Combined Work that"+ , " the Library is used in it and that the Library and its use are"+ , " covered by this License."+ , ""+ , " b) Accompany the Combined Work with a copy of the GNU GPL and this license"+ , " document."+ , ""+ , " c) For a Combined Work that displays copyright notices during"+ , " execution, include the copyright notice for the Library among"+ , " these notices, as well as a reference directing the user to the"+ , " copies of the GNU GPL and this license document."+ , ""+ , " d) Do one of the following:"+ , ""+ , " 0) Convey the Minimal Corresponding Source under the terms of this"+ , " License, and the Corresponding Application Code in a form"+ , " suitable for, and under terms that permit, the user to"+ , " recombine or relink the Application with a modified version of"+ , " the Linked Version to produce a modified Combined Work, in the"+ , " manner specified by section 6 of the GNU GPL for conveying"+ , " Corresponding Source."+ , ""+ , " 1) Use a suitable shared library mechanism for linking with the"+ , " Library. A suitable mechanism is one that (a) uses at run time"+ , " a copy of the Library already present on the user's computer"+ , " system, and (b) will operate properly with a modified version"+ , " of the Library that is interface-compatible with the Linked"+ , " Version. "+ , ""+ , " e) Provide Installation Information, but only if you would otherwise"+ , " be required to provide such information under section 6 of the"+ , " GNU GPL, and only to the extent that such information is"+ , " necessary to install and execute a modified version of the"+ , " Combined Work produced by recombining or relinking the"+ , " Application with a modified version of the Linked Version. (If"+ , " you use option 4d0, the Installation Information must accompany"+ , " the Minimal Corresponding Source and Corresponding Application"+ , " Code. If you use option 4d1, you must provide the Installation"+ , " Information in the manner specified by section 6 of the GNU GPL"+ , " for conveying Corresponding Source.)"+ , ""+ , " 5. Combined Libraries."+ , ""+ , " You may place library facilities that are a work based on the"+ , "Library side by side in a single library together with other library"+ , "facilities that are not Applications and are not covered by this"+ , "License, and convey such a combined library under terms of your"+ , "choice, if you do both of the following:"+ , ""+ , " a) Accompany the combined library with a copy of the same work based"+ , " on the Library, uncombined with any other library facilities,"+ , " conveyed under the terms of this License."+ , ""+ , " b) Give prominent notice with the combined library that part of it"+ , " is a work based on the Library, and explaining where to find the"+ , " accompanying uncombined form of the same work."+ , ""+ , " 6. Revised Versions of the GNU Lesser General Public License."+ , ""+ , " The Free Software Foundation may publish revised and/or new versions"+ , "of the GNU Lesser General Public License from time to time. Such new"+ , "versions will be similar in spirit to the present version, but may"+ , "differ in detail to address new problems or concerns."+ , ""+ , " Each version is given a distinguishing version number. If the"+ , "Library as you received it specifies that a certain numbered version"+ , "of the GNU Lesser General Public License \"or any later version\""+ , "applies to it, you have the option of following the terms and"+ , "conditions either of that published version or of any later version"+ , "published by the Free Software Foundation. If the Library as you"+ , "received it does not specify a version number of the GNU Lesser"+ , "General Public License, you may choose any version of the GNU Lesser"+ , "General Public License ever published by the Free Software Foundation."+ , ""+ , " If the Library as you received it specifies that a proxy can decide"+ , "whether future versions of the GNU Lesser General Public License shall"+ , "apply, that proxy's public statement of acceptance of any version is"+ , "permanent authorization for you to choose that version for the"+ , "Library."+ ]+
+ Distribution/Client/Init/Types.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Init.Types+-- Copyright : (c) Brent Yorgey, Benedikt Huber 2009+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Some types used by the 'cabal init' command.+--+-----------------------------------------------------------------------------+module Distribution.Client.Init.Types where++import Distribution.Simple.Setup+ ( Flag(..) )++import Distribution.Version+import qualified Distribution.Package as P+import Distribution.License+import Distribution.ModuleName++import qualified Text.PrettyPrint as Disp+import qualified Distribution.Compat.ReadP as Parse+import Distribution.Text++import Data.Monoid++-- | InitFlags is really just a simple type to represent certain+-- portions of a .cabal file. Rather than have a flag for EVERY+-- possible field, we just have one for each field that the user is+-- likely to want and/or that we are likely to be able to+-- intelligently guess.+data InitFlags =+ InitFlags { nonInteractive :: Flag Bool+ , quiet :: Flag Bool+ , packageDir :: Flag FilePath+ , noComments :: Flag Bool+ , minimal :: Flag Bool++ , packageName :: Flag String+ , version :: Flag Version+ , cabalVersion :: Flag VersionRange+ , license :: Flag License+ , author :: Flag String+ , email :: Flag String+ , homepage :: Flag String++ , synopsis :: Flag String+ , category :: Flag (Either String Category)++ , packageType :: Flag PackageType++ , exposedModules :: Maybe [ModuleName]+ , otherModules :: Maybe [ModuleName]++ , dependencies :: Maybe [P.Dependency]+ , sourceDirs :: Maybe [String]+ , buildTools :: Maybe [String]+ }+ deriving (Show)++data PackageType = Library | Executable+ deriving (Show, Read, Eq)++instance Text PackageType where+ disp = Disp.text . show+ parse = Parse.choice $ map (fmap read . Parse.string . show) [Library, Executable]++instance Monoid InitFlags where+ mempty = InitFlags+ { nonInteractive = mempty+ , quiet = mempty+ , packageDir = mempty+ , noComments = mempty+ , minimal = mempty+ , packageName = mempty+ , version = mempty+ , cabalVersion = mempty+ , license = mempty+ , author = mempty+ , email = mempty+ , homepage = mempty+ , synopsis = mempty+ , category = mempty+ , packageType = mempty+ , exposedModules = mempty+ , otherModules = mempty+ , dependencies = mempty+ , sourceDirs = mempty+ , buildTools = mempty+ }+ mappend a b = InitFlags+ { nonInteractive = combine nonInteractive+ , quiet = combine quiet+ , packageDir = combine packageDir+ , noComments = combine noComments+ , minimal = combine minimal+ , packageName = combine packageName+ , version = combine version+ , cabalVersion = combine cabalVersion+ , license = combine license+ , author = combine author+ , email = combine email+ , homepage = combine homepage+ , synopsis = combine synopsis+ , category = combine category+ , packageType = combine packageType+ , exposedModules = combine exposedModules+ , otherModules = combine otherModules+ , dependencies = combine dependencies+ , sourceDirs = combine sourceDirs+ , buildTools = combine buildTools+ }+ where combine field = field a `mappend` field b++-- | Some common package categories.+data Category+ = Codec+ | Concurrency+ | Control+ | Data+ | Database+ | Development+ | Distribution+ | Game+ | Graphics+ | Language+ | Math+ | Network+ | Sound+ | System+ | Testing+ | Text+ | Web+ deriving (Read, Show, Eq, Ord, Bounded, Enum)++instance Text Category where+ disp = Disp.text . show+ parse = Parse.choice $ map (fmap read . Parse.string . show) [Codec .. ]++#if MIN_VERSION_base(3,0,0)+#else+-- Compat instance for ghc-6.6 era+instance Monoid a => Monoid (Maybe a) where+ mempty = Nothing+ Nothing `mappend` m = m+ m `mappend` Nothing = m+ Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)+#endif
+ Distribution/Client/Install.hs view
@@ -0,0 +1,875 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Install+-- Copyright : (c) 2005 David Himmelstrup+-- 2007 Bjorn Bringert+-- 2007-2010 Duncan Coutts+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- High level interface to package installation.+-----------------------------------------------------------------------------+module Distribution.Client.Install (+ install,+ upgrade,+ ) where++import Data.List+ ( unfoldr, find, nub, sort )+import Data.Maybe+ ( isJust, fromMaybe )+import Control.Exception as Exception+ ( handleJust )+#if MIN_VERSION_base(4,0,0)+import Control.Exception as Exception+ ( Exception(toException), catches, Handler(Handler), IOException )+import System.Exit+ ( ExitCode )+#else+import Control.Exception as Exception+ ( Exception(IOException, ExitException) )+#endif+import Distribution.Compat.Exception+ ( SomeException, catchIO, catchExit )+import Control.Monad+ ( when, unless )+import System.Directory+ ( getTemporaryDirectory, doesFileExist, createDirectoryIfMissing )+import System.FilePath+ ( (</>), (<.>), takeDirectory )+import System.IO+ ( openFile, IOMode(AppendMode) )+import System.IO.Error+ ( isDoesNotExistError, ioeGetFileName )++import Distribution.Client.Targets+import Distribution.Client.Dependency+import Distribution.Client.FetchUtils+import qualified Distribution.Client.Haddock as Haddock (regenerateHaddockIndex)+-- import qualified Distribution.Client.Info as Info+import Distribution.Client.IndexUtils as IndexUtils+ ( getAvailablePackages, getInstalledPackages )+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.Setup+ ( GlobalFlags(..)+ , ConfigFlags(..), configureCommand, filterConfigureFlags+ , ConfigExFlags(..), InstallFlags(..) )+import Distribution.Client.Config+ ( defaultCabalDir )+import Distribution.Client.Tar (extractTarGzFile)+import Distribution.Client.Types as Available+import Distribution.Client.BuildReports.Types+ ( ReportLevel(..) )+import Distribution.Client.SetupWrapper+ ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )+import qualified Distribution.Client.BuildReports.Anonymous as BuildReports+import qualified Distribution.Client.BuildReports.Storage as BuildReports+ ( storeAnonymous, storeLocal, fromInstallPlan )+import qualified Distribution.Client.InstallSymlink as InstallSymlink+ ( symlinkBinaries )+import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade+import qualified Distribution.Client.World as World+import Paths_cabal_install_bundle (getBinDir)++import Distribution.Simple.Compiler+ ( CompilerId(..), Compiler(compilerId), compilerFlavor+ , PackageDB(..), PackageDBStack )+import Distribution.Simple.Program (ProgramConfiguration, defaultProgramConfiguration)+import qualified Distribution.Simple.InstallDirs as InstallDirs+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Simple.Setup+ ( haddockCommand, HaddockFlags(..), emptyHaddockFlags+ , buildCommand, BuildFlags(..), emptyBuildFlags+ , toFlag, fromFlag, fromFlagOrDefault, flagToMaybe )+import qualified Distribution.Simple.Setup as Cabal+ ( installCommand, InstallFlags(..), emptyInstallFlags )+import Distribution.Simple.Utils+ ( rawSystemExit, comparing )+import Distribution.Simple.InstallDirs as InstallDirs+ ( PathTemplate, fromPathTemplate, toPathTemplate, substPathTemplate+ , initialPathTemplateEnv, installDirsTemplateEnv )+import Distribution.Package+ ( PackageIdentifier, packageName, packageVersion+ , Package(..), PackageFixedDeps(..)+ , Dependency(..), thisPackageVersion )+import qualified Distribution.PackageDescription as PackageDescription+import Distribution.PackageDescription+ ( PackageDescription )+import Distribution.PackageDescription.Configuration+ ( finalizePackageDescription )+import Distribution.Version+ ( Version, anyVersion, thisVersion )+import Distribution.Simple.Utils as Utils+ ( notice, info, debug, warn, die, intercalate, withTempDirectory )+import Distribution.Client.Utils+ ( inDir, mergeBy, MergeResult(..) )+import Distribution.System+ ( Platform, buildPlatform, OS(Windows), buildOS )+import Distribution.Text+ ( display )+import Distribution.Verbosity as Verbosity+ ( Verbosity, showForCabal, verbose )+import Distribution.Simple.BuildPaths ( exeExtension )++--TODO:+-- * assign flags to packages individually+-- * complain about flags that do not apply to any package given as target+-- so flags do not apply to dependencies, only listed, can use flag+-- constraints for dependencies+-- * only record applicable flags in world file+-- * allow flag constraints+-- * allow installed constraints+-- * allow flag and installed preferences+-- * change world file to use cabal section syntax+-- * allow persistent configure flags for each package individually++-- ------------------------------------------------------------+-- * Top level user actions+-- ------------------------------------------------------------++-- | Installs the packages needed to satisfy a list of dependencies.+--+install, upgrade+ :: Verbosity+ -> PackageDBStack+ -> [Repo]+ -> Compiler+ -> ProgramConfiguration+ -> GlobalFlags+ -> ConfigFlags+ -> ConfigExFlags+ -> InstallFlags+ -> [UserTarget]+ -> IO ()+install verbosity packageDBs repos comp conf+ globalFlags configFlags configExFlags installFlags userTargets0 = do++ installed <- getInstalledPackages verbosity comp packageDBs conf+ availableDb <- getAvailablePackages verbosity repos++ let -- For install, if no target is given it means we use the+ -- current directory as the single target+ userTargets | null userTargets0 = [UserTargetLocalDir "."]+ | otherwise = userTargets0++ pkgSpecifiers <- resolveUserTargets verbosity+ globalFlags (packageIndex availableDb) userTargets++ notice verbosity "Resolving dependencies..."+ installPlan <- foldProgress logMsg die return $+ planPackages+ comp configFlags configExFlags installFlags+ installed availableDb pkgSpecifiers++ printPlanMessages verbosity installed installPlan dryRun++ unless dryRun $ do+ installPlan' <- performInstallations verbosity+ context installed installPlan+ postInstallActions verbosity context userTargets installPlan'++ where+ context :: InstallContext+ context = (packageDBs, repos, comp, conf,+ globalFlags, configFlags, configExFlags, installFlags)++ dryRun = fromFlag (installDryRun installFlags)+ logMsg message rest = debug verbosity message >> rest+++upgrade _ _ _ _ _ _ _ _ _ _ = die $+ "Use the 'cabal install' command instead of 'cabal upgrade'.\n"+ ++ "You can install the latest version of a package using 'cabal install'. "+ ++ "The 'cabal upgrade' command has been removed because people found it "+ ++ "confusing and it often led to broken packages.\n"+ ++ "If you want the old upgrade behaviour then use the install command "+ ++ "with the --upgrade-dependencies flag (but check first with --dry-run "+ ++ "to see what would happen). This will try to pick the latest versions "+ ++ "of all dependencies, rather than the usual behaviour of trying to pick "+ ++ "installed versions of all dependencies. If you do use "+ ++ "--upgrade-dependencies, it is recommended that you do not upgrade core "+ ++ "packages (e.g. by using appropriate --constraint= flags)."++type InstallContext = ( PackageDBStack+ , [Repo]+ , Compiler+ , ProgramConfiguration+ , GlobalFlags+ , ConfigFlags+ , ConfigExFlags+ , InstallFlags )++-- ------------------------------------------------------------+-- * Installation planning+-- ------------------------------------------------------------++planPackages :: Compiler+ -> ConfigFlags+ -> ConfigExFlags+ -> InstallFlags+ -> PackageIndex InstalledPackage+ -> AvailablePackageDb+ -> [PackageSpecifier AvailablePackage]+ -> Progress String String InstallPlan+planPackages comp configFlags configExFlags installFlags+ installed availableDb pkgSpecifiers =++ resolveDependencies+ buildPlatform (compilerId comp)+ resolverParams++ >>= if onlyDeps then adjustPlanOnlyDeps else return++ where+ resolverParams =++ setPreferenceDefault (if upgradeDeps then PreferAllLatest+ else PreferLatestForSelected)++ . addPreferences+ -- preferences from the config file or command line+ [ PackageVersionPreference name ver+ | Dependency name ver <- configPreferences configExFlags ]++ . addConstraints+ -- version constraints from the config file or command line+ [ PackageVersionConstraint name ver+ | Dependency name ver <- configConstraints configFlags ]++ . addConstraints+ --FIXME: this just applies all flags to all targets which+ -- is silly. We should check if the flags are appropriate+ [ PackageFlagsConstraint (pkgSpecifierTarget pkgSpecifier) flags+ | let flags = configConfigurationsFlags configFlags+ , not (null flags)+ , pkgSpecifier <- pkgSpecifiers ]++ . (if reinstall then reinstallTargets else id)++ $ standardInstallPolicy installed availableDb pkgSpecifiers++ --TODO: this is a general feature and should be moved to D.C.Dependency+ -- Also, the InstallPlan.remove should return info more precise to the+ -- problem, rather than the very general PlanProblem type.+ adjustPlanOnlyDeps :: InstallPlan -> Progress String String InstallPlan+ adjustPlanOnlyDeps =+ either (Fail . explain) Done+ . InstallPlan.remove isTarget+ where+ isTarget pkg = packageName pkg `elem` targetnames+ targetnames = map pkgSpecifierTarget pkgSpecifiers++ explain :: [InstallPlan.PlanProblem] -> String+ explain problems =+ "Cannot select only the dependencies (as requested by the "+ ++ "'--only-dependencies' flag), "+ ++ (case pkgids of+ [pkgid] -> "the package " ++ display pkgid ++ " is "+ _ -> "the packages "+ ++ intercalate ", " (map display pkgids) ++ " are ")+ ++ "required by a dependency of one of the other targets."+ where+ pkgids =+ nub [ depid+ | InstallPlan.PackageMissingDeps _ depids <- problems+ , depid <- depids+ , packageName depid `elem` targetnames ]++ reinstall = fromFlag (installReinstall installFlags)+ upgradeDeps = fromFlag (installUpgradeDeps installFlags)+ onlyDeps = fromFlag (installOnlyDeps installFlags)++-- ------------------------------------------------------------+-- * Informational messages+-- ------------------------------------------------------------++printPlanMessages :: Verbosity+ -> PackageIndex InstalledPackage+ -> InstallPlan+ -> Bool+ -> IO ()+printPlanMessages verbosity installed installPlan dryRun = do++ when nothingToInstall $+ notice verbosity $+ "No packages to be installed. All the requested packages are "+ ++ "already installed.\n If you want to reinstall anyway then use "+ ++ "the --reinstall flag."++ when (dryRun || verbosity >= verbose) $+ printDryRun verbosity installed installPlan++ where+ nothingToInstall = null (InstallPlan.ready installPlan)+++printDryRun :: Verbosity+ -> PackageIndex InstalledPackage+ -> InstallPlan+ -> IO ()+printDryRun verbosity installed plan = case unfoldr next plan of+ [] -> return ()+ pkgs+ | verbosity >= Verbosity.verbose -> notice verbosity $ unlines $+ "In order, the following would be installed:"+ : map showPkgAndReason pkgs+ | otherwise -> notice verbosity $ unlines $+ "In order, the following would be installed (use -v for more details):"+ : map (display . packageId) pkgs+ where+ next plan' = case InstallPlan.ready plan' of+ [] -> Nothing+ (pkg:_) -> Just (pkg, InstallPlan.completed pkgid result plan')+ where pkgid = packageId pkg+ result = BuildOk DocsNotTried TestsNotTried+ --FIXME: This is a bit of a hack,+ -- pretending that each package is installed++ showPkgAndReason pkg' = display (packageId pkg') ++ " " +++ case PackageIndex.lookupPackageName installed (packageName pkg') of+ [] -> "(new package)"+ ps -> case find ((==packageId pkg') . packageId) ps of+ Nothing -> "(new version)"+ Just pkg -> "(reinstall)" ++ case changes pkg pkg' of+ [] -> ""+ diff -> " changes: " ++ intercalate ", " diff+ changes pkg pkg' = map change . filter changed+ $ mergeBy (comparing packageName)+ (nub . sort . depends $ pkg)+ (nub . sort . depends $ pkg')+ change (OnlyInLeft pkgid) = display pkgid ++ " removed"+ change (InBoth pkgid pkgid') = display pkgid ++ " -> "+ ++ display (packageVersion pkgid')+ change (OnlyInRight pkgid') = display pkgid' ++ " added"+ changed (InBoth pkgid pkgid') = pkgid /= pkgid'+ changed _ = True++-- ------------------------------------------------------------+-- * Post installation stuff+-- ------------------------------------------------------------++-- | Various stuff we do after successful or unsuccessfully installing a bunch+-- of packages. This includes:+--+-- * build reporting, local and remote+-- * symlinking binaries+-- * updating indexes+-- * updating world file+-- * error reporting+--+postInstallActions :: Verbosity+ -> InstallContext+ -> [UserTarget]+ -> InstallPlan+ -> IO ()+postInstallActions verbosity+ (packageDBs, _, comp, conf, globalFlags, configFlags, _, installFlags)+ targets installPlan = do++ unless oneShot $+ World.insert verbosity worldFile+ --FIXME: does not handle flags+ [ World.WorldPkgInfo dep []+ | UserTargetNamed dep <- targets ]++ let buildReports = BuildReports.fromInstallPlan installPlan+ BuildReports.storeLocal (installSummaryFile installFlags) buildReports+ when (reportingLevel >= AnonymousReports) $+ BuildReports.storeAnonymous buildReports+ when (reportingLevel == DetailedReports) $+ storeDetailedBuildReports verbosity logsDir buildReports++ regenerateHaddockIndex verbosity packageDBs comp conf+ configFlags installFlags installPlan++ symlinkBinaries verbosity configFlags installFlags installPlan++ printBuildFailures installPlan++ where+ reportingLevel = fromFlag (installBuildReports installFlags)+ logsDir = fromFlag (globalLogsDir globalFlags)+ oneShot = fromFlag (installOneShot installFlags)+ worldFile = fromFlag $ globalWorldFile globalFlags++storeDetailedBuildReports :: Verbosity -> FilePath+ -> [(BuildReports.BuildReport, Repo)] -> IO ()+storeDetailedBuildReports verbosity logsDir reports = sequence_+ [ do dotCabal <- defaultCabalDir+ let logFileName = display (BuildReports.package report) <.> "log"+ logFile = logsDir </> logFileName+ reportsDir = dotCabal </> "reports" </> remoteRepoName remoteRepo+ reportFile = reportsDir </> logFileName++ handleMissingLogFile $ do+ buildLog <- readFile logFile+ createDirectoryIfMissing True reportsDir -- FIXME+ writeFile reportFile (show (BuildReports.show report, buildLog))++ | (report, Repo { repoKind = Left remoteRepo }) <- reports+ , isLikelyToHaveLogFile (BuildReports.installOutcome report) ]++ where+ isLikelyToHaveLogFile BuildReports.ConfigureFailed {} = True+ isLikelyToHaveLogFile BuildReports.BuildFailed {} = True+ isLikelyToHaveLogFile BuildReports.InstallFailed {} = True+ isLikelyToHaveLogFile BuildReports.InstallOk {} = True+ isLikelyToHaveLogFile _ = False++ handleMissingLogFile = Exception.handleJust missingFile $ \ioe ->+ warn verbosity $ "Missing log file for build report: "+ ++ fromMaybe "" (ioeGetFileName ioe)++#if MIN_VERSION_base(4,0,0)+ missingFile ioe+#else+ missingFile (IOException ioe)+#endif+ | isDoesNotExistError ioe = Just ioe+ missingFile _ = Nothing+++regenerateHaddockIndex :: Verbosity+ -> [PackageDB]+ -> Compiler+ -> ProgramConfiguration+ -> ConfigFlags+ -> InstallFlags+ -> InstallPlan+ -> IO ()+regenerateHaddockIndex verbosity packageDBs comp conf+ configFlags installFlags installPlan+ | haddockIndexFileIsRequested && shouldRegenerateHaddockIndex = do++ defaultDirs <- InstallDirs.defaultInstallDirs+ (compilerFlavor comp)+ (fromFlag (configUserInstall configFlags))+ True+ let indexFileTemplate = fromFlag (installHaddockIndex installFlags)+ indexFile = substHaddockIndexFileName defaultDirs indexFileTemplate++ notice verbosity $+ "Updating documentation index " ++ indexFile++ --TODO: might be nice if the install plan gave us the new InstalledPackageInfo+ installed <- getInstalledPackages verbosity comp packageDBs conf+ Haddock.regenerateHaddockIndex verbosity installed conf indexFile++ | otherwise = return ()+ where+ haddockIndexFileIsRequested =+ fromFlag (installDocumentation installFlags)+ && isJust (flagToMaybe (installHaddockIndex installFlags))++ -- We want to regenerate the index if some new documentation was actually+ -- installed. Since the index is per-user, we don't do it for global+ -- installs or special cases where we're installing into a specific db.+ shouldRegenerateHaddockIndex = normalUserInstall+ && someDocsWereInstalled installPlan+ where+ someDocsWereInstalled = any installedDocs . InstallPlan.toList+ normalUserInstall = (UserPackageDB `elem` packageDBs)+ && all (not . isSpecificPackageDB) packageDBs++ installedDocs (InstallPlan.Installed _ (BuildOk DocsOk _)) = True+ installedDocs _ = False+ isSpecificPackageDB (SpecificPackageDB _) = True+ isSpecificPackageDB _ = False++ substHaddockIndexFileName defaultDirs = fromPathTemplate+ . substPathTemplate env+ where+ env = env0 ++ installDirsTemplateEnv absoluteDirs+ env0 = InstallDirs.compilerTemplateEnv (compilerId comp)+ ++ InstallDirs.platformTemplateEnv (buildPlatform)+ absoluteDirs = InstallDirs.substituteInstallDirTemplates+ env0 templateDirs+ templateDirs = InstallDirs.combineInstallDirs fromFlagOrDefault+ defaultDirs (configInstallDirs configFlags)+++symlinkBinaries :: Verbosity+ -> ConfigFlags+ -> InstallFlags+ -> InstallPlan -> IO ()+symlinkBinaries verbosity configFlags installFlags plan = do+ failed <- InstallSymlink.symlinkBinaries configFlags installFlags plan+ case failed of+ [] -> return ()+ [(_, exe, path)] ->+ warn verbosity $+ "could not create a symlink in " ++ bindir ++ " for "+ ++ exe ++ " because the file exists there already but is not "+ ++ "managed by cabal. You can create a symlink for this executable "+ ++ "manually if you wish. The executable file has been installed at "+ ++ path+ exes ->+ warn verbosity $+ "could not create symlinks in " ++ bindir ++ " for "+ ++ intercalate ", " [ exe | (_, exe, _) <- exes ]+ ++ " because the files exist there already and are not "+ ++ "managed by cabal. You can create symlinks for these executables "+ ++ "manually if you wish. The executable files have been installed at "+ ++ intercalate ", " [ path | (_, _, path) <- exes ]+ where+ bindir = fromFlag (installSymlinkBinDir installFlags)+++printBuildFailures :: InstallPlan -> IO ()+printBuildFailures plan =+ case [ (pkg, reason)+ | InstallPlan.Failed pkg reason <- InstallPlan.toList plan ] of+ [] -> return ()+ failed -> die . unlines+ $ "Error: some packages failed to install:"+ : [ display (packageId pkg) ++ printFailureReason reason+ | (pkg, reason) <- failed ]+ where+ printFailureReason reason = case reason of+ DependentFailed pkgid -> " depends on " ++ display pkgid+ ++ " which failed to install."+ DownloadFailed e -> " failed while downloading the package."+ ++ " The exception was:\n " ++ show e+ UnpackFailed e -> " failed while unpacking the package."+ ++ " The exception was:\n " ++ show e+ ConfigureFailed e -> " failed during the configure step."+ ++ " The exception was:\n " ++ show e+ BuildFailed e -> " failed during the building phase."+ ++ " The exception was:\n " ++ show e+ InstallFailed e -> " failed during the final install step."+ ++ " The exception was:\n " ++ show e+++-- ------------------------------------------------------------+-- * Actually do the installations+-- ------------------------------------------------------------++data InstallMisc = InstallMisc {+ rootCmd :: Maybe FilePath,+ libVersion :: Maybe Version+ }++performInstallations :: Verbosity+ -> InstallContext+ -> PackageIndex InstalledPackage+ -> InstallPlan+ -> IO InstallPlan+performInstallations verbosity+ (packageDBs, _, comp, conf,+ globalFlags, configFlags, configExFlags, installFlags)+ installed installPlan = do++ executeInstallPlan installPlan $ \cpkg ->+ installConfiguredPackage platform compid configFlags+ cpkg $ \configFlags' src pkg ->+ fetchAvailablePackage verbosity src $ \src' ->+ installLocalPackage verbosity (packageId pkg) src' $ \mpath ->+ installUnpackedPackage verbosity (setupScriptOptions installed)+ miscOptions configFlags' installFlags+ compid pkg mpath useLogFile++ where+ platform = InstallPlan.planPlatform installPlan+ compid = InstallPlan.planCompiler installPlan++ setupScriptOptions index = SetupScriptOptions {+ useCabalVersion = maybe anyVersion thisVersion (libVersion miscOptions),+ useCompiler = Just comp,+ -- Hack: we typically want to allow the UserPackageDB for finding the+ -- Cabal lib when compiling any Setup.hs even if we're doing a global+ -- install. However we also allow looking in a specific package db.+ usePackageDB = if UserPackageDB `elem` packageDBs+ then packageDBs+ else let (db@GlobalPackageDB:dbs) = packageDBs+ in db : UserPackageDB : dbs,+ --TODO: use Ord instance:+ -- insert UserPackageDB packageDBs+ usePackageIndex = if UserPackageDB `elem` packageDBs+ then Just index+ else Nothing,+ useProgramConfig = conf,+ useDistPref = fromFlagOrDefault+ (useDistPref defaultSetupScriptOptions)+ (configDistPref configFlags),+ useLoggingHandle = Nothing,+ useWorkingDir = Nothing+ }+ reportingLevel = fromFlag (installBuildReports installFlags)+ logsDir = fromFlag (globalLogsDir globalFlags)+ useLogFile :: Maybe (PackageIdentifier -> FilePath)+ useLogFile = fmap substLogFileName logFileTemplate+ where+ logFileTemplate :: Maybe PathTemplate+ logFileTemplate --TODO: separate policy from mechanism+ | reportingLevel == DetailedReports+ = Just $ toPathTemplate $ logsDir </> "$pkgid" <.> "log"+ | otherwise+ = flagToMaybe (installLogFile installFlags)+ substLogFileName template pkg = fromPathTemplate+ . substPathTemplate env+ $ template+ where env = initialPathTemplateEnv (packageId pkg) (compilerId comp)+ miscOptions = InstallMisc {+ rootCmd = if fromFlag (configUserInstall configFlags)+ then Nothing -- ignore --root-cmd if --user.+ else flagToMaybe (installRootCmd installFlags),+ libVersion = flagToMaybe (configCabalVersion configExFlags)+ }+++executeInstallPlan :: Monad m+ => InstallPlan+ -> (ConfiguredPackage -> m BuildResult)+ -> m InstallPlan+executeInstallPlan plan installPkg = case InstallPlan.ready plan of+ [] -> return plan+ (pkg: _) -> do buildResult <- installPkg pkg+ let plan' = updatePlan (packageId pkg) buildResult plan+ executeInstallPlan plan' installPkg+ where+ updatePlan pkgid (Right buildSuccess) =+ InstallPlan.completed pkgid buildSuccess++ updatePlan pkgid (Left buildFailure) =+ InstallPlan.failed pkgid buildFailure depsFailure+ where+ depsFailure = DependentFailed pkgid+ -- So this first pkgid failed for whatever reason (buildFailure).+ -- All the other packages that depended on this pkgid, which we+ -- now cannot build, we mark as failing due to 'DependentFailed'+ -- which kind of means it was not their fault.+++-- | Call an installer for an 'AvailablePackage' but override the configure+-- flags with the ones given by the 'ConfiguredPackage'. In particular the+-- 'ConfiguredPackage' specifies an exact 'FlagAssignment' and exactly+-- versioned package dependencies. So we ignore any previous partial flag+-- assignment or dependency constraints and use the new ones.+--+installConfiguredPackage :: Platform -> CompilerId+ -> ConfigFlags -> ConfiguredPackage+ -> (ConfigFlags -> PackageLocation (Maybe FilePath)+ -> PackageDescription -> a)+ -> a+installConfiguredPackage platform comp configFlags+ (ConfiguredPackage (AvailablePackage _ gpkg source) flags deps)+ installPkg = installPkg configFlags {+ configConfigurationsFlags = flags,+ configConstraints = map thisPackageVersion deps+ } source pkg+ where+ pkg = case finalizePackageDescription flags+ (const True)+ platform comp [] gpkg of+ Left _ -> error "finalizePackageDescription ConfiguredPackage failed"+ Right (desc, _) -> desc++fetchAvailablePackage+ :: Verbosity+ -> PackageLocation (Maybe FilePath)+ -> (PackageLocation FilePath -> IO BuildResult)+ -> IO BuildResult+fetchAvailablePackage verbosity src installPkg = do+ fetched <- checkFetched src+ case fetched of+ Just src' -> installPkg src'+ Nothing -> onFailure DownloadFailed $+ fetchPackage verbosity src >>= installPkg+++installLocalPackage+ :: Verbosity -> PackageIdentifier -> PackageLocation FilePath+ -> (Maybe FilePath -> IO BuildResult)+ -> IO BuildResult+installLocalPackage verbosity pkgid location installPkg = case location of++ LocalUnpackedPackage dir ->+ installPkg (Just dir)++ LocalTarballPackage tarballPath ->+ installLocalTarballPackage verbosity pkgid tarballPath installPkg++ RemoteTarballPackage _ tarballPath ->+ installLocalTarballPackage verbosity pkgid tarballPath installPkg++ RepoTarballPackage _ _ tarballPath ->+ installLocalTarballPackage verbosity pkgid tarballPath installPkg+++installLocalTarballPackage+ :: Verbosity -> PackageIdentifier -> FilePath+ -> (Maybe FilePath -> IO BuildResult)+ -> IO BuildResult+installLocalTarballPackage verbosity pkgid tarballPath installPkg = do+ tmp <- getTemporaryDirectory+ withTempDirectory verbosity tmp (display pkgid) $ \tmpDirPath ->+ onFailure UnpackFailed $ do+ info verbosity $ "Extracting " ++ tarballPath+ ++ " to " ++ tmpDirPath ++ "..."+ let relUnpackedPath = display pkgid+ absUnpackedPath = tmpDirPath </> relUnpackedPath+ descFilePath = absUnpackedPath+ </> display (packageName pkgid) <.> "cabal"+ extractTarGzFile tmpDirPath relUnpackedPath tarballPath+ exists <- doesFileExist descFilePath+ when (not exists) $+ die $ "Package .cabal file not found: " ++ show descFilePath+ installPkg (Just absUnpackedPath)+++installUnpackedPackage :: Verbosity+ -> SetupScriptOptions+ -> InstallMisc+ -> ConfigFlags+ -> InstallFlags+ -> CompilerId+ -> PackageDescription+ -> Maybe FilePath -- ^ Directory to change to before starting the installation.+ -> Maybe (PackageIdentifier -> FilePath) -- ^ File to log output to (if any)+ -> IO BuildResult+installUnpackedPackage verbosity scriptOptions miscOptions+ configFlags installConfigFlags+ compid pkg workingDir useLogFile =++ -- Configure phase+ onFailure ConfigureFailed $ do+ setup configureCommand configureFlags++ -- Build phase+ onFailure BuildFailed $ do+ setup buildCommand' buildFlags++ -- Doc generation phase+ docsResult <- if shouldHaddock+ then (do setup haddockCommand haddockFlags+ return DocsOk)+ `catchIO` (\_ -> return DocsFailed)+ `catchExit` (\_ -> return DocsFailed)+ else return DocsNotTried++ -- Tests phase+ testsResult <- return TestsNotTried --TODO: add optional tests++ -- Install phase+ onFailure InstallFailed $+ withWin32SelfUpgrade verbosity configFlags compid pkg $ do+ case rootCmd miscOptions of+ (Just cmd) -> reexec cmd+ Nothing -> setup Cabal.installCommand installFlags+ return (Right (BuildOk docsResult testsResult))++ where+ configureFlags = filterConfigureFlags configFlags {+ configVerbosity = toFlag verbosity'+ }+ buildCommand' = buildCommand defaultProgramConfiguration+ buildFlags _ = emptyBuildFlags {+ buildDistPref = configDistPref configFlags,+ buildVerbosity = toFlag verbosity'+ }+ shouldHaddock = fromFlag (installDocumentation installConfigFlags)+ haddockFlags _ = emptyHaddockFlags {+ haddockDistPref = configDistPref configFlags,+ haddockVerbosity = toFlag verbosity'+ }+ installFlags _ = Cabal.emptyInstallFlags {+ Cabal.installDistPref = configDistPref configFlags,+ Cabal.installVerbosity = toFlag verbosity'+ }+ verbosity' | isJust useLogFile = max Verbosity.verbose verbosity+ | otherwise = verbosity+ setup cmd flags = do+ logFileHandle <- case useLogFile of+ Nothing -> return Nothing+ Just mkLogFileName -> do+ let logFileName = mkLogFileName (packageId pkg)+ logDir = takeDirectory logFileName+ unless (null logDir) $ createDirectoryIfMissing True logDir+ logFile <- openFile logFileName AppendMode+ return (Just logFile)++ setupWrapper verbosity+ scriptOptions { useLoggingHandle = logFileHandle+ , useWorkingDir = workingDir }+ (Just pkg)+ cmd flags []+ reexec cmd = do+ -- look for our on executable file and re-exec ourselves using+ -- a helper program like sudo to elevate priviledges:+ bindir <- getBinDir+ let self = bindir </> "cabal" <.> exeExtension+ weExist <- doesFileExist self+ if weExist+ then inDir workingDir $+ rawSystemExit verbosity cmd+ [self, "install", "--only"+ ,"--verbose=" ++ showForCabal verbosity]+ else die $ "Unable to find cabal executable at: " ++ self+++-- helper+onFailure :: (SomeException -> BuildFailure) -> IO BuildResult -> IO BuildResult+onFailure result action =+#if MIN_VERSION_base(4,0,0)+ action `catches`+ [ Handler $ \ioe -> handler (ioe :: IOException)+ , Handler $ \exit -> handler (exit :: ExitCode)+ ]+ where+ handler :: Exception e => e -> IO BuildResult+ handler = return . Left . result . toException+#else+ action+ `catchIO` (return . Left . result . IOException)+ `catchExit` (return . Left . result . ExitException)+#endif+++-- ------------------------------------------------------------+-- * Wierd windows hacks+-- ------------------------------------------------------------++withWin32SelfUpgrade :: Verbosity+ -> ConfigFlags+ -> CompilerId+ -> PackageDescription+ -> IO a -> IO a+withWin32SelfUpgrade _ _ _ _ action | buildOS /= Windows = action+withWin32SelfUpgrade verbosity configFlags compid pkg action = do++ defaultDirs <- InstallDirs.defaultInstallDirs+ compFlavor+ (fromFlag (configUserInstall configFlags))+ (PackageDescription.hasLibs pkg)++ Win32SelfUpgrade.possibleSelfUpgrade verbosity+ (exeInstallPaths defaultDirs) action++ where+ pkgid = packageId pkg+ (CompilerId compFlavor _) = compid++ exeInstallPaths defaultDirs =+ [ InstallDirs.bindir absoluteDirs </> exeName <.> exeExtension+ | exe <- PackageDescription.executables pkg+ , PackageDescription.buildable (PackageDescription.buildInfo exe)+ , let exeName = prefix ++ PackageDescription.exeName exe ++ suffix+ prefix = substTemplate prefixTemplate+ suffix = substTemplate suffixTemplate ]+ where+ fromFlagTemplate = fromFlagOrDefault (InstallDirs.toPathTemplate "")+ prefixTemplate = fromFlagTemplate (configProgPrefix configFlags)+ suffixTemplate = fromFlagTemplate (configProgSuffix configFlags)+ templateDirs = InstallDirs.combineInstallDirs fromFlagOrDefault+ defaultDirs (configInstallDirs configFlags)+ absoluteDirs = InstallDirs.absoluteInstallDirs+ pkgid compid InstallDirs.NoCopyDest templateDirs+ substTemplate = InstallDirs.fromPathTemplate+ . InstallDirs.substPathTemplate env+ where env = InstallDirs.initialPathTemplateEnv pkgid compid
+ Distribution/Client/InstallPlan.hs view
@@ -0,0 +1,511 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.InstallPlan+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : duncan@community.haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Package installation plan+--+-----------------------------------------------------------------------------+module Distribution.Client.InstallPlan (+ InstallPlan,+ ConfiguredPackage(..),+ PlanPackage(..),++ -- * Operations on 'InstallPlan's+ new,+ toList,+ ready,+ completed,+ failed,+ remove,++ -- ** Query functions+ planPlatform,+ planCompiler,++ -- * Checking valididy of plans+ valid,+ closed,+ consistent,+ acyclic,+ configuredPackageValid,++ -- ** Details on invalid plans+ PlanProblem(..),+ showPlanProblem,+ PackageProblem(..),+ showPackageProblem,+ problems,+ configuredPackageProblems+ ) where++import Distribution.Client.Types+ ( AvailablePackage(packageDescription), ConfiguredPackage(..)+ , InstalledPackage+ , BuildFailure, BuildSuccess )+import Distribution.Package+ ( PackageIdentifier(..), PackageName(..), Package(..), packageName+ , PackageFixedDeps(..), Dependency(..) )+import Distribution.Version+ ( Version, withinRange )+import Distribution.PackageDescription+ ( GenericPackageDescription(genPackageFlags)+ , Flag(flagName), FlagName(..) )+import Distribution.Client.PackageUtils+ ( externalBuildDepends )+import Distribution.PackageDescription.Configuration+ ( finalizePackageDescription )+import Distribution.Client.PackageIndex+ ( PackageIndex )+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Text+ ( display )+import Distribution.System+ ( Platform )+import Distribution.Compiler+ ( CompilerId(..) )+import Distribution.Client.Utils+ ( duplicates, duplicatesBy, mergeBy, MergeResult(..) )+import Distribution.Simple.Utils+ ( comparing, intercalate )++import Data.List+ ( sort, sortBy )+import Data.Maybe+ ( fromMaybe )+import qualified Data.Graph as Graph+import Data.Graph (Graph)+import Control.Exception+ ( assert )++-- When cabal tries to install a number of packages, including all their+-- dependencies it has a non-trivial problem to solve.+--+-- The Problem:+--+-- In general we start with a set of installed packages and a set of available+-- packages.+--+-- Installed packages have fixed dependencies. They have already been built and+-- we know exactly what packages they were built against, including their exact+-- versions. +--+-- Available package have somewhat flexible dependencies. They are specified as+-- version ranges, though really they're predicates. To make matters worse they+-- have conditional flexible dependencies. Configuration flags can affect which+-- packages are required and can place additional constraints on their+-- versions.+--+-- These two sets of package can and usually do overlap. There can be installed+-- packages that are also available which means they could be re-installed if+-- required, though there will also be packages which are not available and+-- cannot be re-installed. Very often there will be extra versions available+-- than are installed. Sometimes we may like to prefer installed packages over+-- available ones or perhaps always prefer the latest available version whether+-- installed or not.+--+-- The goal is to calculate an installation plan that is closed, acyclic and+-- consistent and where every configured package is valid.+--+-- An installation plan is a set of packages that are going to be used+-- together. It will consist of a mixture of installed packages and available+-- packages along with their exact version dependencies. An installation plan+-- is closed if for every package in the set, all of its dependencies are+-- also in the set. It is consistent if for every package in the set, all+-- dependencies which target that package have the same version.++-- Note that plans do not necessarily compose. You might have a valid plan for+-- package A and a valid plan for package B. That does not mean the composition+-- is simultaniously valid for A and B. In particular you're most likely to+-- have problems with inconsistent dependencies.+-- On the other hand it is true that every closed sub plan is valid.++data PlanPackage = PreExisting InstalledPackage+ | Configured ConfiguredPackage+ | Installed ConfiguredPackage BuildSuccess+ | Failed ConfiguredPackage BuildFailure++instance Package PlanPackage where+ packageId (PreExisting pkg) = packageId pkg+ packageId (Configured pkg) = packageId pkg+ packageId (Installed pkg _) = packageId pkg+ packageId (Failed pkg _) = packageId pkg++instance PackageFixedDeps PlanPackage where+ depends (PreExisting pkg) = depends pkg+ depends (Configured pkg) = depends pkg+ depends (Installed pkg _) = depends pkg+ depends (Failed pkg _) = depends pkg++data InstallPlan = InstallPlan {+ planIndex :: PackageIndex PlanPackage,+ planGraph :: Graph,+ planGraphRev :: Graph,+ planPkgOf :: Graph.Vertex -> PlanPackage,+ planVertexOf :: PackageIdentifier -> Graph.Vertex,+ planPlatform :: Platform,+ planCompiler :: CompilerId+ }++invariant :: InstallPlan -> Bool+invariant plan =+ valid (planPlatform plan) (planCompiler plan) (planIndex plan)++internalError :: String -> a+internalError msg = error $ "InstallPlan: internal error: " ++ msg++-- | Build an installation plan from a valid set of resolved packages.+--+new :: Platform -> CompilerId -> PackageIndex PlanPackage+ -> Either [PlanProblem] InstallPlan+new platform compiler index =+ case problems platform compiler index of+ [] -> Right InstallPlan {+ planIndex = index,+ planGraph = graph,+ planGraphRev = Graph.transposeG graph,+ planPkgOf = vertexToPkgId,+ planVertexOf = fromMaybe noSuchPkgId . pkgIdToVertex,+ planPlatform = platform,+ planCompiler = compiler+ }+ where (graph, vertexToPkgId, pkgIdToVertex) =+ PackageIndex.dependencyGraph index+ noSuchPkgId = internalError "package is not in the graph"+ probs -> Left probs++toList :: InstallPlan -> [PlanPackage]+toList = PackageIndex.allPackages . planIndex++-- | Remove packages from the install plan. This will result in an+-- error if there are remaining packages that depend on any matching+-- package. This is primarily useful for obtaining an install plan for+-- the dependencies of a package or set of packages without actually+-- installing the package itself, as when doing development.+--+remove :: (PlanPackage -> Bool)+ -> InstallPlan+ -> Either [PlanProblem] InstallPlan+remove shouldRemove plan =+ new (planPlatform plan) (planCompiler plan) newIndex+ where+ newIndex = PackageIndex.fromList $+ filter (not . shouldRemove) (toList plan)++-- | The packages that are ready to be installed. That is they are in the+-- configured state and have all their dependencies installed already.+-- The plan is complete if the result is @[]@.+--+ready :: InstallPlan -> [ConfiguredPackage]+ready plan = assert check readyPackages+ where+ check = if null readyPackages then null configuredPackages else True+ configuredPackages =+ [ pkg | Configured pkg <- PackageIndex.allPackages (planIndex plan) ]+ readyPackages = filter (all isInstalled . depends) configuredPackages+ isInstalled pkg =+ case PackageIndex.lookupPackageId (planIndex plan) pkg of+ Just (Configured _) -> False+ Just (Failed _ _) -> internalError depOnFailed+ Just (PreExisting _) -> True+ Just (Installed _ _) -> True+ Nothing -> internalError incomplete+ incomplete = "install plan is not closed"+ depOnFailed = "configured package depends on failed package"++-- | Marks a package in the graph as completed. Also saves the build result for+-- the completed package in the plan.+--+-- * The package must exist in the graph.+-- * The package must have had no uninstalled dependent packages.+--+completed :: PackageIdentifier+ -> BuildSuccess+ -> InstallPlan -> InstallPlan+completed pkgid buildResult plan = assert (invariant plan') plan'+ where+ plan' = plan {+ planIndex = PackageIndex.insert installed (planIndex plan)+ }+ installed = Installed (lookupConfiguredPackage plan pkgid) buildResult++-- | Marks a package in the graph as having failed. It also marks all the+-- packages that depended on it as having failed.+--+-- * The package must exist in the graph and be in the configured state.+--+failed :: PackageIdentifier -- ^ The id of the package that failed to install+ -> BuildFailure -- ^ The build result to use for the failed package+ -> BuildFailure -- ^ The build result to use for its dependencies+ -> InstallPlan+ -> InstallPlan+failed pkgid buildResult buildResult' plan = assert (invariant plan') plan'+ where+ plan' = plan {+ planIndex = PackageIndex.merge (planIndex plan) failures+ }+ pkg = lookupConfiguredPackage plan pkgid+ failures = PackageIndex.fromList+ $ Failed pkg buildResult+ : [ Failed pkg' buildResult'+ | Just pkg' <- map checkConfiguredPackage+ $ packagesThatDependOn plan pkgid ]++-- | lookup the reachable packages in the reverse dependency graph+--+packagesThatDependOn :: InstallPlan+ -> PackageIdentifier -> [PlanPackage]+packagesThatDependOn plan = map (planPkgOf plan)+ . tail+ . Graph.reachable (planGraphRev plan)+ . planVertexOf plan++-- | lookup a package that we expect to be in the configured state+--+lookupConfiguredPackage :: InstallPlan+ -> PackageIdentifier -> ConfiguredPackage+lookupConfiguredPackage plan pkgid =+ case PackageIndex.lookupPackageId (planIndex plan) pkgid of+ Just (Configured pkg) -> pkg+ _ -> internalError $ "not configured or no such pkg " ++ display pkgid++-- | check a package that we expect to be in the configured or failed state+--+checkConfiguredPackage :: PlanPackage -> Maybe ConfiguredPackage+checkConfiguredPackage (Configured pkg) = Just pkg+checkConfiguredPackage (Failed _ _) = Nothing+checkConfiguredPackage pkg =+ internalError $ "not configured or no such pkg " ++ display (packageId pkg)++-- ------------------------------------------------------------+-- * Checking valididy of plans+-- ------------------------------------------------------------++-- | A valid installation plan is a set of packages that is 'acyclic',+-- 'closed' and 'consistent'. Also, every 'ConfiguredPackage' in the+-- plan has to have a valid configuration (see 'configuredPackageValid').+--+-- * if the result is @False@ use 'problems' to get a detailed list.+--+valid :: Platform -> CompilerId -> PackageIndex PlanPackage -> Bool+valid platform comp index = null (problems platform comp index)++data PlanProblem =+ PackageInvalid ConfiguredPackage [PackageProblem]+ | PackageMissingDeps PlanPackage [PackageIdentifier]+ | PackageCycle [PlanPackage]+ | PackageInconsistency PackageName [(PackageIdentifier, Version)]+ | PackageStateInvalid PlanPackage PlanPackage++showPlanProblem :: PlanProblem -> String+showPlanProblem (PackageInvalid pkg packageProblems) =+ "Package " ++ display (packageId pkg)+ ++ " has an invalid configuration, in particular:\n"+ ++ unlines [ " " ++ showPackageProblem problem+ | problem <- packageProblems ]++showPlanProblem (PackageMissingDeps pkg missingDeps) =+ "Package " ++ display (packageId pkg)+ ++ " depends on the following packages which are missing from the plan "+ ++ intercalate ", " (map display missingDeps)++showPlanProblem (PackageCycle cycleGroup) =+ "The following packages are involved in a dependency cycle "+ ++ intercalate ", " (map (display.packageId) cycleGroup)++showPlanProblem (PackageInconsistency name inconsistencies) =+ "Package " ++ display name+ ++ " is required by several packages,"+ ++ " but they require inconsistent versions:\n"+ ++ unlines [ " package " ++ display pkg ++ " requires "+ ++ display (PackageIdentifier name ver)+ | (pkg, ver) <- inconsistencies ]++showPlanProblem (PackageStateInvalid pkg pkg') =+ "Package " ++ display (packageId pkg)+ ++ " is in the " ++ showPlanState pkg+ ++ " state but it depends on package " ++ display (packageId pkg')+ ++ " which is in the " ++ showPlanState pkg'+ ++ " state"+ where+ showPlanState (PreExisting _) = "pre-existing"+ showPlanState (Configured _) = "configured"+ showPlanState (Installed _ _) = "installed"+ showPlanState (Failed _ _) = "failed"++-- | For an invalid plan, produce a detailed list of problems as human readable+-- error messages. This is mainly intended for debugging purposes.+-- Use 'showPlanProblem' for a human readable explanation.+--+problems :: Platform -> CompilerId+ -> PackageIndex PlanPackage -> [PlanProblem]+problems platform comp index =+ [ PackageInvalid pkg packageProblems+ | Configured pkg <- PackageIndex.allPackages index+ , let packageProblems = configuredPackageProblems platform comp pkg+ , not (null packageProblems) ]++ ++ [ PackageMissingDeps pkg missingDeps+ | (pkg, missingDeps) <- PackageIndex.brokenPackages index ]++ ++ [ PackageCycle cycleGroup+ | cycleGroup <- PackageIndex.dependencyCycles index ]++ ++ [ PackageInconsistency name inconsistencies+ | (name, inconsistencies) <- PackageIndex.dependencyInconsistencies index ]++ ++ [ PackageStateInvalid pkg pkg'+ | pkg <- PackageIndex.allPackages index+ , Just pkg' <- map (PackageIndex.lookupPackageId index) (depends pkg)+ , not (stateDependencyRelation pkg pkg') ]++-- | The graph of packages (nodes) and dependencies (edges) must be acyclic.+--+-- * if the result is @False@ use 'PackageIndex.dependencyCycles' to find out+-- which packages are involved in dependency cycles.+--+acyclic :: PackageIndex PlanPackage -> Bool+acyclic = null . PackageIndex.dependencyCycles++-- | An installation plan is closed if for every package in the set, all of+-- its dependencies are also in the set. That is, the set is closed under the+-- dependency relation.+--+-- * if the result is @False@ use 'PackageIndex.brokenPackages' to find out+-- which packages depend on packages not in the index.+--+closed :: PackageIndex PlanPackage -> Bool+closed = null . PackageIndex.brokenPackages++-- | An installation plan is consistent if all dependencies that target a+-- single package name, target the same version.+--+-- This is slightly subtle. It is not the same as requiring that there be at+-- most one version of any package in the set. It only requires that of+-- packages which have more than one other package depending on them. We could+-- actually make the condition even more precise and say that different+-- versions are ok so long as they are not both in the transative closure of+-- any other package (or equivalently that their inverse closures do not+-- intersect). The point is we do not want to have any packages depending+-- directly or indirectly on two different versions of the same package. The+-- current definition is just a safe aproximation of that.+--+-- * if the result is @False@ use 'PackageIndex.dependencyInconsistencies' to+-- find out which packages are.+--+consistent :: PackageIndex PlanPackage -> Bool+consistent = null . PackageIndex.dependencyInconsistencies++-- | The states of packages have that depend on each other must respect+-- this relation. That is for very case where package @a@ depends on+-- package @b@ we require that @dependencyStatesOk a b = True@.+--+stateDependencyRelation :: PlanPackage -> PlanPackage -> Bool+stateDependencyRelation (PreExisting _) (PreExisting _) = True++stateDependencyRelation (Configured _) (PreExisting _) = True+stateDependencyRelation (Configured _) (Configured _) = True+stateDependencyRelation (Configured _) (Installed _ _) = True++stateDependencyRelation (Installed _ _) (PreExisting _) = True+stateDependencyRelation (Installed _ _) (Installed _ _) = True++stateDependencyRelation (Failed _ _) (PreExisting _) = True+-- failed can depends on configured because a package can depend on+-- several other packages and if one of the deps fail then we fail+-- but we still depend on the other ones that did not fail:+stateDependencyRelation (Failed _ _) (Configured _) = True+stateDependencyRelation (Failed _ _) (Installed _ _) = True+stateDependencyRelation (Failed _ _) (Failed _ _) = True++stateDependencyRelation _ _ = False++-- | A 'ConfiguredPackage' is valid if the flag assignment is total and if+-- in the configuration given by the flag assignment, all the package+-- dependencies are satisfied by the specified packages.+--+configuredPackageValid :: Platform -> CompilerId -> ConfiguredPackage -> Bool+configuredPackageValid platform comp pkg =+ null (configuredPackageProblems platform comp pkg)++data PackageProblem = DuplicateFlag FlagName+ | MissingFlag FlagName+ | ExtraFlag FlagName+ | DuplicateDeps [PackageIdentifier]+ | MissingDep Dependency+ | ExtraDep PackageIdentifier+ | InvalidDep Dependency PackageIdentifier++showPackageProblem :: PackageProblem -> String+showPackageProblem (DuplicateFlag (FlagName flag)) =+ "duplicate flag in the flag assignment: " ++ flag++showPackageProblem (MissingFlag (FlagName flag)) =+ "missing an assignment for the flag: " ++ flag++showPackageProblem (ExtraFlag (FlagName flag)) =+ "extra flag given that is not used by the package: " ++ flag++showPackageProblem (DuplicateDeps pkgids) =+ "duplicate packages specified as selected dependencies: "+ ++ intercalate ", " (map display pkgids)++showPackageProblem (MissingDep dep) =+ "the package has a dependency " ++ display dep+ ++ " but no package has been selected to satisfy it."++showPackageProblem (ExtraDep pkgid) =+ "the package configuration specifies " ++ display pkgid+ ++ " but (with the given flag assignment) the package does not actually"+ ++ " depend on any version of that package."++showPackageProblem (InvalidDep dep pkgid) =+ "the package depends on " ++ display dep+ ++ " but the configuration specifies " ++ display pkgid+ ++ " which does not satisfy the dependency."++configuredPackageProblems :: Platform -> CompilerId+ -> ConfiguredPackage -> [PackageProblem]+configuredPackageProblems platform comp+ (ConfiguredPackage pkg specifiedFlags specifiedDeps) =+ [ DuplicateFlag flag | ((flag,_):_) <- duplicates specifiedFlags ]+ ++ [ MissingFlag flag | OnlyInLeft flag <- mergedFlags ]+ ++ [ ExtraFlag flag | OnlyInRight flag <- mergedFlags ]+ ++ [ DuplicateDeps pkgs+ | pkgs <- duplicatesBy (comparing packageName) specifiedDeps ]+ ++ [ MissingDep dep | OnlyInLeft dep <- mergedDeps ]+ ++ [ ExtraDep pkgid | OnlyInRight pkgid <- mergedDeps ]+ ++ [ InvalidDep dep pkgid | InBoth dep pkgid <- mergedDeps+ , not (packageSatisfiesDependency pkgid dep) ]+ where+ mergedFlags = mergeBy compare+ (sort $ map flagName (genPackageFlags (packageDescription pkg)))+ (sort $ map fst specifiedFlags)++ mergedDeps = mergeBy+ (\dep pkgid -> dependencyName dep `compare` packageName pkgid)+ (sortBy (comparing dependencyName) requiredDeps)+ (sortBy (comparing packageName) specifiedDeps)++ packageSatisfiesDependency+ (PackageIdentifier name version)+ (Dependency name' versionRange) = assert (name == name') $+ version `withinRange` versionRange++ dependencyName (Dependency name _) = name++ requiredDeps :: [Dependency]+ requiredDeps =+ --TODO: use something lower level than finalizePackageDescription+ case finalizePackageDescription specifiedFlags+ (const True)+ platform comp+ []+ (packageDescription pkg) of+ Right (resolvedPkg, _) -> externalBuildDepends resolvedPkg+ Left _ -> error "configuredPackageInvalidDeps internal error"
+ Distribution/Client/InstallSymlink.hs view
@@ -0,0 +1,238 @@+{-# OPTIONS -cpp #-}+-- OPTIONS required for ghc-6.4.x compat, and must appear first+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -cpp #-}+{-# OPTIONS_NHC98 -cpp #-}+{-# OPTIONS_JHC -fcpp #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.InstallSymlink+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Managing installing binaries with symlinks.+-----------------------------------------------------------------------------+module Distribution.Client.InstallSymlink (+ symlinkBinaries,+ symlinkBinary,+ ) where++#if mingw32_HOST_OS || mingw32_TARGET_OS++import Distribution.Package (PackageIdentifier)+import Distribution.Client.InstallPlan (InstallPlan)+import Distribution.Client.Setup (InstallFlags)+import Distribution.Simple.Setup (ConfigFlags)++symlinkBinaries :: ConfigFlags+ -> InstallFlags+ -> InstallPlan+ -> IO [(PackageIdentifier, String, FilePath)]+symlinkBinaries _ _ _ = return []++symlinkBinary :: FilePath -> FilePath -> String -> String -> IO Bool+symlinkBinary _ _ _ _ = fail "Symlinking feature not available on Windows"++#else++import Distribution.Client.Types+ ( AvailablePackage(..), ConfiguredPackage(..) )+import Distribution.Client.Setup+ ( InstallFlags(installSymlinkBinDir) )+import qualified Distribution.Client.InstallPlan as InstallPlan+import Distribution.Client.InstallPlan (InstallPlan)++import Distribution.Package+ ( PackageIdentifier, Package(packageId) )+import Distribution.Compiler+ ( CompilerId(..) )+import qualified Distribution.PackageDescription as PackageDescription+import Distribution.PackageDescription+ ( PackageDescription )+import Distribution.PackageDescription.Configuration+ ( finalizePackageDescription )+import Distribution.Simple.Setup+ ( ConfigFlags(..), fromFlag, fromFlagOrDefault, flagToMaybe )+import qualified Distribution.Simple.InstallDirs as InstallDirs++import System.Posix.Files+ ( getSymbolicLinkStatus, isSymbolicLink, createSymbolicLink+ , removeLink )+import System.Directory+ ( canonicalizePath )+import System.FilePath+ ( (</>), splitPath, joinPath, isAbsolute )++import Prelude hiding (catch, ioError)+import System.IO.Error+ ( catch, isDoesNotExistError, ioError )+import Control.Exception+ ( assert )+import Data.Maybe+ ( catMaybes )++-- | We would like by default to install binaries into some location that is on+-- the user's PATH. For per-user installations on Unix systems that basically+-- means the @~/bin/@ directory. On the majority of platforms the @~/bin/@+-- directory will be on the user's PATH. However some people are a bit nervous+-- about letting a package manager install programs into @~/bin/@.+--+-- A comprimise solution is that instead of installing binaries directly into+-- @~/bin/@, we could install them in a private location under @~/.cabal/bin@+-- and then create symlinks in @~/bin/@. We can be careful when setting up the+-- symlinks that we do not overwrite any binary that the user installed. We can+-- check if it was a symlink we made because it would point to the private dir+-- where we install our binaries. This means we can install normally without+-- worrying and in a later phase set up symlinks, and if that fails then we+-- report it to the user, but even in this case the package is still in an ok+-- installed state.+--+-- This is an optional feature that users can choose to use or not. It is+-- controlled from the config file. Of course it only works on posix systems+-- with symlinks so is not available to Windows users.+--+symlinkBinaries :: ConfigFlags+ -> InstallFlags+ -> InstallPlan+ -> IO [(PackageIdentifier, String, FilePath)]+symlinkBinaries configFlags installFlags plan =+ case flagToMaybe (installSymlinkBinDir installFlags) of+ Nothing -> return []+ Just symlinkBinDir+ | null exes -> return []+ | otherwise -> do+ publicBinDir <- canonicalizePath symlinkBinDir+-- TODO: do we want to do this here? :+-- createDirectoryIfMissing True publicBinDir+ fmap catMaybes $ sequence+ [ do privateBinDir <- pkgBinDir pkg+ ok <- symlinkBinary+ publicBinDir privateBinDir+ publicExeName privateExeName+ if ok+ then return Nothing+ else return (Just (pkgid, publicExeName,+ privateBinDir </> privateExeName))+ | (pkg, exe) <- exes+ , let publicExeName = PackageDescription.exeName exe+ privateExeName = prefix ++ publicExeName ++ suffix+ pkgid = packageId pkg+ prefix = substTemplate pkgid prefixTemplate+ suffix = substTemplate pkgid suffixTemplate ]+ where+ exes =+ [ (pkg, exe)+ | InstallPlan.Installed cpkg _ <- InstallPlan.toList plan+ , let pkg = pkgDescription cpkg+ , exe <- PackageDescription.executables pkg+ , PackageDescription.buildable (PackageDescription.buildInfo exe) ]++ pkgDescription :: ConfiguredPackage -> PackageDescription+ pkgDescription (ConfiguredPackage (AvailablePackage _ pkg _) flags _) =+ case finalizePackageDescription flags+ (const True)+ platform compilerId [] pkg of+ Left _ -> error "finalizePackageDescription ConfiguredPackage failed"+ Right (desc, _) -> desc++ -- This is sadly rather complicated. We're kind of re-doing part of the+ -- configuration for the package. :-(+ pkgBinDir :: PackageDescription -> IO FilePath+ pkgBinDir pkg = do+ defaultDirs <- InstallDirs.defaultInstallDirs+ compilerFlavor+ (fromFlag (configUserInstall configFlags))+ (PackageDescription.hasLibs pkg)+ let templateDirs = InstallDirs.combineInstallDirs fromFlagOrDefault+ defaultDirs (configInstallDirs configFlags)+ absoluteDirs = InstallDirs.absoluteInstallDirs+ (packageId pkg) compilerId InstallDirs.NoCopyDest+ templateDirs+ canonicalizePath (InstallDirs.bindir absoluteDirs)++ substTemplate pkgid = InstallDirs.fromPathTemplate+ . InstallDirs.substPathTemplate env+ where env = InstallDirs.initialPathTemplateEnv pkgid compilerId++ fromFlagTemplate = fromFlagOrDefault (InstallDirs.toPathTemplate "")+ prefixTemplate = fromFlagTemplate (configProgPrefix configFlags)+ suffixTemplate = fromFlagTemplate (configProgSuffix configFlags)+ platform = InstallPlan.planPlatform plan+ compilerId@(CompilerId compilerFlavor _) = InstallPlan.planCompiler plan++symlinkBinary :: FilePath -- ^ The canonical path of the public bin dir+ -- eg @/home/user/bin@+ -> FilePath -- ^ The canonical path of the private bin dir+ -- eg @/home/user/.cabal/bin@+ -> String -- ^ The name of the executable to go in the public+ -- bin dir, eg @foo@+ -> String -- ^ The name of the executable to in the private bin+ -- dir, eg @foo-1.0@+ -> IO Bool -- ^ If creating the symlink was sucessful. @False@+ -- if there was another file there already that we+ -- did not own. Other errors like permission errors+ -- just propagate as exceptions.+symlinkBinary publicBindir privateBindir publicName privateName = do+ ok <- targetOkToOverwrite (publicBindir </> publicName)+ (privateBindir </> privateName)+ case ok of+ NotOurFile -> return False+ NotExists -> mkLink >> return True+ OkToOverwrite -> rmLink >> mkLink >> return True+ where+ relativeBindir = makeRelative publicBindir privateBindir+ mkLink = createSymbolicLink (relativeBindir </> privateName)+ (publicBindir </> publicName)+ rmLink = removeLink (publicBindir </> publicName)++-- | Check a filepath of a symlink that we would like to create to see if it+-- is ok. For it to be ok to overwrite it must either not already exist yet or+-- be a symlink to our target (in which case we can assume ownership).+--+targetOkToOverwrite :: FilePath -- ^ The filepath of the symlink to the private+ -- binary that we would like to create+ -> FilePath -- ^ The canonical path of the private binary.+ -- Use 'canonicalizePath' to make this.+ -> IO SymlinkStatus+targetOkToOverwrite symlink target = handleNotExist $ do+ status <- getSymbolicLinkStatus symlink+ if not (isSymbolicLink status)+ then return NotOurFile+ else do target' <- canonicalizePath symlink+ -- This relies on canonicalizePath handling symlinks+ if target == target'+ then return OkToOverwrite+ else return NotOurFile++ where+ handleNotExist action = catch action $ \ioexception ->+ -- If the target doesn't exist then there's no problem overwriting it!+ if isDoesNotExistError ioexception+ then return NotExists+ else ioError ioexception++data SymlinkStatus+ = NotExists -- ^ The file doesn't exist so we can make a symlink.+ | OkToOverwrite -- ^ A symlink already exists, though it is ours. We'll+ -- have to delete it first bemore we make a new symlink.+ | NotOurFile -- ^ A file already exists and it is not one of our existing+ -- symlinks (either because it is not a symlink or because+ -- it points somewhere other than our managed space).+ deriving Show++-- | Take two canonical paths and produce a relative path to get from the first+-- to the second, even if it means adding @..@ path components.+--+makeRelative :: FilePath -> FilePath -> FilePath+makeRelative a b = assert (isAbsolute a && isAbsolute b) $+ let as = splitPath a+ bs = splitPath b+ commonLen = length $ takeWhile id $ zipWith (==) as bs+ in joinPath $ [ ".." | _ <- drop commonLen as ]+ ++ [ b' | b' <- drop commonLen bs ]++#endif
+ Distribution/Client/List.hs view
@@ -0,0 +1,528 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.List+-- Copyright : (c) David Himmelstrup 2005+-- Duncan Coutts 2008-2011+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+--+-- Search for and print information about packages+-----------------------------------------------------------------------------+module Distribution.Client.List (+ list, info+ ) where++import Distribution.Package+ ( PackageName(..), Package(..), packageName, packageVersion+ , Dependency(..), thisPackageVersion, depends, simplifyDependency )+import Distribution.ModuleName (ModuleName)+import Distribution.License (License)+import qualified Distribution.InstalledPackageInfo as Installed+import qualified Distribution.PackageDescription as Available+import Distribution.PackageDescription+ ( Flag(..), FlagName(..) )+import Distribution.PackageDescription.Configuration+ ( flattenPackageDescription )++import Distribution.Simple.Compiler+ ( Compiler, PackageDBStack )+import Distribution.Simple.Program (ProgramConfiguration)+import Distribution.Simple.Utils+ ( equating, comparing, die, notice )+import Distribution.Simple.Setup (fromFlag)+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Version+ ( Version(..), VersionRange, withinRange, anyVersion+ , intersectVersionRanges, simplifyVersionRange )+import Distribution.Verbosity (Verbosity)+import Distribution.Text+ ( Text(disp), display )++import Distribution.Client.Types+ ( AvailablePackage(..), Repo, AvailablePackageDb(..)+ , InstalledPackage(..) )+import Distribution.Client.Dependency.Types+ ( PackageConstraint(..) )+import Distribution.Client.Targets+ ( UserTarget, resolveUserTargets, PackageSpecifier(..) )+import Distribution.Client.Setup+ ( GlobalFlags(..), ListFlags(..), InfoFlags(..) )+import Distribution.Client.Utils+ ( mergeBy, MergeResult(..) )+import Distribution.Client.IndexUtils as IndexUtils+ ( getAvailablePackages, getInstalledPackages )+import Distribution.Client.FetchUtils+ ( isFetched )++import Data.List+ ( sortBy, groupBy, sort, nub, intersperse, maximumBy, partition )+import Data.Maybe+ ( listToMaybe, fromJust, fromMaybe, isJust )+import qualified Data.Map as Map+import Data.Tree as Tree+import Control.Monad+ ( MonadPlus(mplus), join )+import Control.Exception+ ( assert )+import Text.PrettyPrint.HughesPJ as Disp+import System.Directory+ ( doesDirectoryExist )+++-- |Show information about packages+list :: Verbosity+ -> PackageDBStack+ -> [Repo]+ -> Compiler+ -> ProgramConfiguration+ -> ListFlags+ -> [String]+ -> IO ()+list verbosity packageDBs repos comp conf listFlags pats = do++ installed <- getInstalledPackages verbosity comp packageDBs conf+ availableDb <- getAvailablePackages verbosity repos+ let available = packageIndex availableDb+ prefs name = fromMaybe anyVersion+ (Map.lookup name (packagePreferences availableDb))++ pkgsInfo :: [(PackageName, [InstalledPackage], [AvailablePackage])]+ pkgsInfo+ -- gather info for all packages+ | null pats = mergePackages (PackageIndex.allPackages installed)+ (PackageIndex.allPackages available)++ -- gather info for packages matching search term+ | otherwise = mergePackages (matchingPackages installed)+ (matchingPackages available)++ matches :: [PackageDisplayInfo]+ matches = [ mergePackageInfo pref+ installedPkgs availablePkgs selectedPkg False+ | (pkgname, installedPkgs, availablePkgs) <- pkgsInfo+ , not onlyInstalled || not (null installedPkgs)+ , let pref = prefs pkgname+ selectedPkg = latestWithPref pref availablePkgs ]++ if simpleOutput+ then putStr $ unlines+ [ display (pkgName pkg) ++ " " ++ display version+ | pkg <- matches+ , version <- if onlyInstalled+ then installedVersions pkg+ else nub . sort $ installedVersions pkg+ ++ availableVersions pkg ]+ -- Note: this only works because for 'list', one cannot currently+ -- specify any version constraints, so listing all installed+ -- and available ones works.+ else+ if null matches+ then notice verbosity "No matches found."+ else putStr $ unlines (map showPackageSummaryInfo matches)+ where+ onlyInstalled = fromFlag (listInstalled listFlags)+ simpleOutput = fromFlag (listSimpleOutput listFlags)++ matchingPackages index =+ [ pkg+ | pat <- pats+ , (_, pkgs) <- PackageIndex.searchByNameSubstring index pat+ , pkg <- pkgs ]++info :: Verbosity+ -> PackageDBStack+ -> [Repo]+ -> Compiler+ -> ProgramConfiguration+ -> GlobalFlags+ -> InfoFlags+ -> [UserTarget]+ -> IO ()+info verbosity packageDBs repos comp conf+ globalFlags _listFlags userTargets = do++ installed <- getInstalledPackages verbosity comp packageDBs conf+ availableDb <- getAvailablePackages verbosity repos+ let available = packageIndex availableDb+ prefs name = fromMaybe anyVersion+ (Map.lookup name (packagePreferences availableDb))++ -- Users may specify names of packages that are only installed, not+ -- just available source packages, so we must resolve targets using+ -- the combination of installed and available packages.+ let available' = PackageIndex.fromList+ $ map packageId (PackageIndex.allPackages installed)+ ++ map packageId (PackageIndex.allPackages available)+ pkgSpecifiers <- resolveUserTargets verbosity+ globalFlags available' userTargets++ pkgsinfo <- sequence+ [ do pkginfo <- either die return $+ gatherPkgInfo prefs+ installed available pkgSpecifier+ updateFileSystemPackageDetails pkginfo+ | pkgSpecifier <- pkgSpecifiers ]++ putStr $ unlines (map showPackageDetailedInfo pkgsinfo)++ where+ gatherPkgInfo prefs installed available (NamedPackage name constraints)+ | null (selectedInstalledPkgs) && null (selectedAvailablePkgs)+ = Left $ "There is no available version of " ++ display name+ ++ " that satisfies "+ ++ display (simplifyVersionRange verConstraint)++ | otherwise+ = Right $ mergePackageInfo pref installedPkgs+ availablePkgs selectedAvailablePkg+ showPkgVersion+ where+ pref = prefs name+ installedPkgs = PackageIndex.lookupPackageName installed name+ availablePkgs = PackageIndex.lookupPackageName available name++ selectedInstalledPkgs = PackageIndex.lookupDependency installed+ (Dependency name verConstraint)+ selectedAvailablePkgs = PackageIndex.lookupDependency available+ (Dependency name verConstraint)+ selectedAvailablePkg = latestWithPref pref selectedAvailablePkgs++ -- display a specific package version if the user+ -- supplied a non-trivial version constraint+ showPkgVersion = not (null verConstraints)+ verConstraint = foldr intersectVersionRanges anyVersion verConstraints+ verConstraints = [ vr | PackageVersionConstraint _ vr <- constraints ]++ gatherPkgInfo prefs installed available (SpecificSourcePackage pkg) =+ Right $ mergePackageInfo pref installedPkgs availablePkgs+ selectedPkg True+ where+ name = packageName pkg+ pref = prefs name+ installedPkgs = PackageIndex.lookupPackageName installed name+ availablePkgs = PackageIndex.lookupPackageName available name+ selectedPkg = Just pkg+++-- | The info that we can display for each package. It is information per+-- package name and covers all installed and avilable versions.+--+data PackageDisplayInfo = PackageDisplayInfo {+ pkgName :: PackageName,+ selectedVersion :: Maybe Version,+ selectedAvailable :: Maybe AvailablePackage,+ installedVersions :: [Version],+ availableVersions :: [Version],+ preferredVersions :: VersionRange,+ homepage :: String,+ bugReports :: String,+ sourceRepo :: String,+ synopsis :: String,+ description :: String,+ category :: String,+ license :: License,+ author :: String,+ maintainer :: String,+ dependencies :: [Dependency],+ flags :: [Flag],+ hasLib :: Bool,+ hasExe :: Bool,+ executables :: [String],+ modules :: [ModuleName],+ haddockHtml :: FilePath,+ haveTarball :: Bool+ }++showPackageSummaryInfo :: PackageDisplayInfo -> String+showPackageSummaryInfo pkginfo =+ renderStyle (style {lineLength = 80, ribbonsPerLine = 1}) $+ char '*' <+> disp (pkgName pkginfo)+ $+$+ (nest 4 $ vcat [+ maybeShow (synopsis pkginfo) "Synopsis:" reflowParagraphs+ , text "Default available version:" <+>+ case selectedAvailable pkginfo of+ Nothing -> text "[ Not available from any configured repository ]"+ Just pkg -> disp (packageVersion pkg)+ , text "Installed versions:" <+>+ case installedVersions pkginfo of+ [] | hasLib pkginfo -> text "[ Not installed ]"+ | otherwise -> text "[ Unknown ]"+ versions -> dispTopVersions 4+ (preferredVersions pkginfo) versions+ , maybeShow (homepage pkginfo) "Homepage:" text+ , text "License: " <+> text (display (license pkginfo))+ ])+ $+$ text ""+ where+ maybeShow [] _ _ = empty+ maybeShow l s f = text s <+> (f l)++showPackageDetailedInfo :: PackageDisplayInfo -> String+showPackageDetailedInfo pkginfo =+ renderStyle (style {lineLength = 80, ribbonsPerLine = 1}) $+ char '*' <+> disp (pkgName pkginfo)+ <> maybe empty (\v -> char '-' <> disp v) (selectedVersion pkginfo)+ <+> text (replicate (16 - length (display (pkgName pkginfo))) ' ')+ <> parens pkgkind+ $+$+ (nest 4 $ vcat [+ entry "Synopsis" synopsis hideIfNull reflowParagraphs+ , entry "Versions available" availableVersions+ (altText null "[ Not available from server ]")+ (dispTopVersions 9 (preferredVersions pkginfo))+ , entry "Versions installed" installedVersions+ (altText null (if hasLib pkginfo then "[ Not installed ]"+ else "[ Unknown ]"))+ (dispTopVersions 4 (preferredVersions pkginfo))+ , entry "Homepage" homepage orNotSpecified text+ , entry "Bug reports" bugReports orNotSpecified text+ , entry "Description" description hideIfNull reflowParagraphs+ , entry "Category" category hideIfNull text+ , entry "License" license alwaysShow disp+ , entry "Author" author hideIfNull reflowLines+ , entry "Maintainer" maintainer hideIfNull reflowLines+ , entry "Source repo" sourceRepo orNotSpecified text+ , entry "Executables" executables hideIfNull (commaSep text)+ , entry "Flags" flags hideIfNull (commaSep dispFlag)+ , entry "Dependencies" dependencies hideIfNull (commaSep disp)+ , entry "Documentation" haddockHtml showIfInstalled text+ , entry "Cached" haveTarball alwaysShow dispYesNo+ , if not (hasLib pkginfo) then empty else+ text "Modules:" $+$ nest 4 (vcat (map disp . sort . modules $ pkginfo))+ ])+ $+$ text ""+ where+ entry fname field cond format = case cond (field pkginfo) of+ Nothing -> label <+> format (field pkginfo)+ Just Nothing -> empty+ Just (Just other) -> label <+> text other+ where+ label = text fname <> char ':' <> padding+ padding = text (replicate (13 - length fname ) ' ')++ normal = Nothing+ hide = Just Nothing+ replace msg = Just (Just msg)++ alwaysShow = const normal+ hideIfNull v = if null v then hide else normal+ showIfInstalled v+ | not isInstalled = hide+ | null v = replace "[ Not installed ]"+ | otherwise = normal+ altText nul msg v = if nul v then replace msg else normal+ orNotSpecified = altText null "[ Not specified ]"++ commaSep f = Disp.fsep . Disp.punctuate (Disp.char ',') . map f+ dispFlag f = case flagName f of FlagName n -> text n+ dispYesNo True = text "Yes"+ dispYesNo False = text "No"++ isInstalled = not (null (installedVersions pkginfo))+ hasExes = length (executables pkginfo) >= 2+ --TODO: exclude non-buildable exes+ pkgkind | hasLib pkginfo && hasExes = text "programs and library"+ | hasLib pkginfo && hasExe pkginfo = text "program and library"+ | hasLib pkginfo = text "library"+ | hasExes = text "programs"+ | hasExe pkginfo = text "program"+ | otherwise = empty+++reflowParagraphs :: String -> Doc+reflowParagraphs =+ vcat+ . intersperse (text "") -- re-insert blank lines+ . map (fsep . map text . concatMap words) -- reflow paragraphs+ . filter (/= [""])+ . groupBy (\x y -> "" `notElem` [x,y]) -- break on blank lines+ . lines++reflowLines :: String -> Doc+reflowLines = vcat . map text . lines++-- | We get the 'PackageDisplayInfo' by combining the info for the installed+-- and available versions of a package.+--+-- * We're building info about a various versions of a single named package so+-- the input package info records are all supposed to refer to the same+-- package name.+--+mergePackageInfo :: VersionRange+ -> [InstalledPackage]+ -> [AvailablePackage]+ -> Maybe AvailablePackage+ -> Bool+ -> PackageDisplayInfo+mergePackageInfo versionPref installedPkgs availablePkgs selectedPkg showVer =+ assert (length installedPkgs + length availablePkgs > 0) $+ PackageDisplayInfo {+ pkgName = combine packageName available+ packageName installed,+ selectedVersion = if showVer then fmap packageVersion selectedPkg+ else Nothing,+ selectedAvailable = availableSelected,+ installedVersions = map packageVersion installedPkgs,+ availableVersions = map packageVersion availablePkgs,+ preferredVersions = versionPref,++ license = combine Available.license available+ Installed.license installed,+ maintainer = combine Available.maintainer available+ Installed.maintainer installed,+ author = combine Available.author available+ Installed.author installed,+ homepage = combine Available.homepage available+ Installed.homepage installed,+ bugReports = maybe "" Available.bugReports available,+ sourceRepo = fromMaybe "" . join+ . fmap (uncons Nothing Available.repoLocation+ . sortBy (comparing Available.repoKind)+ . Available.sourceRepos)+ $ available,+ --TODO: installed package info is missing synopsis+ synopsis = maybe "" Available.synopsis available,+ description = combine Available.description available+ Installed.description installed,+ category = combine Available.category available+ Installed.category installed,+ flags = maybe [] Available.genPackageFlags availableGeneric,+ hasLib = isJust installed+ || fromMaybe False+ (fmap (isJust . Available.condLibrary) availableGeneric),+ hasExe = fromMaybe False+ (fmap (not . null . Available.condExecutables) availableGeneric),+ executables = map fst (maybe [] Available.condExecutables availableGeneric),+ modules = combine Installed.exposedModules installed+ (maybe [] Available.exposedModules+ . Available.library) available,+ dependencies = map simplifyDependency+ $ combine Available.buildDepends available+ (map thisPackageVersion . depends) installed',+ haddockHtml = fromMaybe "" . join+ . fmap (listToMaybe . Installed.haddockHTMLs)+ $ installed,+ haveTarball = False+ }+ where+ combine f x g y = fromJust (fmap f x `mplus` fmap g y)+ installed' = latestWithPref versionPref installedPkgs+ installed = fmap (\(InstalledPackage p _) -> p) installed'++ availableSelected+ | isJust selectedPkg = selectedPkg+ | otherwise = latestWithPref versionPref availablePkgs+ availableGeneric = fmap packageDescription availableSelected+ available = fmap flattenPackageDescription availableGeneric++ uncons :: b -> (a -> b) -> [a] -> b+ uncons z _ [] = z+ uncons _ f (x:_) = f x+++-- | Not all the info is pure. We have to check if the docs really are+-- installed, because the registered package info lies. Similarly we have to+-- check if the tarball has indeed been fetched.+--+updateFileSystemPackageDetails :: PackageDisplayInfo -> IO PackageDisplayInfo+updateFileSystemPackageDetails pkginfo = do+ fetched <- maybe (return False) (isFetched . packageSource)+ (selectedAvailable pkginfo)+ docsExist <- doesDirectoryExist (haddockHtml pkginfo)+ return pkginfo {+ haveTarball = fetched,+ haddockHtml = if docsExist then haddockHtml pkginfo else ""+ }++latestWithPref :: Package pkg => VersionRange -> [pkg] -> Maybe pkg+latestWithPref _ [] = Nothing+latestWithPref pref pkgs = Just (maximumBy (comparing prefThenVersion) pkgs)+ where+ prefThenVersion pkg = let ver = packageVersion pkg+ in (withinRange ver pref, ver)+++-- | Rearrange installed and available packages into groups referring to the+-- same package by name. In the result pairs, the lists are guaranteed to not+-- both be empty.+--+mergePackages :: [InstalledPackage]+ -> [AvailablePackage]+ -> [( PackageName+ , [InstalledPackage]+ , [AvailablePackage] )]+mergePackages installed available =+ map collect+ $ mergeBy (\i a -> fst i `compare` fst a)+ (groupOn packageName installed)+ (groupOn packageName available)+ where+ collect (OnlyInLeft (name,is) ) = (name, is, [])+ collect ( InBoth (_,is) (name,as)) = (name, is, as)+ collect (OnlyInRight (name,as)) = (name, [], as)++groupOn :: Ord key => (a -> key) -> [a] -> [(key,[a])]+groupOn key = map (\xs -> (key (head xs), xs))+ . groupBy (equating key)+ . sortBy (comparing key)++dispTopVersions :: Int -> VersionRange -> [Version] -> Doc+dispTopVersions n pref vs =+ (Disp.fsep . Disp.punctuate (Disp.char ',')+ . map (\ver -> if ispref ver then disp ver else parens (disp ver))+ . sort . take n . interestingVersions ispref+ $ vs)+ <+> trailingMessage++ where+ ispref ver = withinRange ver pref+ extra = length vs - n+ trailingMessage+ | extra <= 0 = Disp.empty+ | otherwise = Disp.parens $ Disp.text "and"+ <+> Disp.int (length vs - n)+ <+> if extra == 1 then Disp.text "other"+ else Disp.text "others"++-- | Reorder a bunch of versions to put the most interesting / significant+-- versions first. A preferred version range is taken into account.+--+-- This may be used in a user interface to select a small number of versions+-- to present to the user, e.g.+--+-- > let selectVersions = sort . take 5 . interestingVersions pref+--+interestingVersions :: (Version -> Bool) -> [Version] -> [Version]+interestingVersions pref =+ map ((\ns -> Version ns []) . fst) . filter snd+ . concat . Tree.levels+ . swizzleTree+ . reorderTree (\(Node (v,_) _) -> pref (Version v []))+ . reverseTree+ . mkTree+ . map versionBranch++ where+ swizzleTree = unfoldTree (spine [])+ where+ spine ts' (Node x []) = (x, ts')+ spine ts' (Node x (t:ts)) = spine (Node x ts:ts') t++ reorderTree _ (Node x []) = Node x []+ reorderTree p (Node x ts) = Node x (ts' ++ ts'')+ where+ (ts',ts'') = partition p (map (reorderTree p) ts)++ reverseTree (Node x cs) = Node x (reverse (map reverseTree cs))++ mkTree xs = unfoldTree step (False, [], xs)+ where+ step (node,ns,vs) =+ ( (reverse ns, node)+ , [ (any null vs', n:ns, filter (not . null) vs')+ | (n, vs') <- groups vs ]+ )+ groups = map (\g -> (head (head g), map tail g))+ . groupBy (equating head)
+ Distribution/Client/PackageIndex.hs view
@@ -0,0 +1,478 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.PackageIndex+-- Copyright : (c) David Himmelstrup 2005,+-- Bjorn Bringert 2007,+-- Duncan Coutts 2008+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- An index of packages.+--+module Distribution.Client.PackageIndex (+ -- * Package index data type+ PackageIndex,++ -- * Creating an index+ fromList,++ -- * Updates+ merge,+ insert,+ deletePackageName,+ deletePackageId,+ deleteDependency,++ -- * Queries++ -- ** Precise lookups+ lookupPackageName,+ lookupPackageId,+ lookupDependency,++ -- ** Case-insensitive searches+ searchByName,+ SearchResult(..),+ searchByNameSubstring,++ -- ** Bulk queries+ allPackages,+ allPackagesByName,++ -- ** Special queries+ brokenPackages,+ dependencyClosure,+ reverseDependencyClosure,+ topologicalOrder,+ reverseTopologicalOrder,+ dependencyInconsistencies,+ dependencyCycles,+ dependencyGraph,+ ) where++import Prelude hiding (lookup)+import Control.Exception (assert)+import qualified Data.Map as Map+import Data.Map (Map)+import qualified Data.Tree as Tree+import qualified Data.Graph as Graph+import qualified Data.Array as Array+import Data.Array ((!))+import Data.List (groupBy, sortBy, nub, isInfixOf)+import Data.Monoid (Monoid(..))+import Data.Maybe (isNothing, fromMaybe)++import Distribution.Package+ ( PackageName(..), PackageIdentifier(..)+ , Package(..), packageName, packageVersion+ , Dependency(Dependency), PackageFixedDeps(..) )+import Distribution.Version+ ( Version, withinRange )+import Distribution.Simple.Utils (lowercase, equating, comparing)+++-- | The collection of information about packages from one or more 'PackageDB's.+--+-- It can be searched effeciently by package name and version.+--+newtype Package pkg => PackageIndex pkg = PackageIndex+ -- This index package names to all the package records matching that package+ -- name case-sensitively. It includes all versions.+ --+ -- This allows us to find all versions satisfying a dependency.+ -- Most queries are a map lookup followed by a linear scan of the bucket.+ --+ (Map PackageName [pkg])++ deriving (Show, Read)++instance Package pkg => Monoid (PackageIndex pkg) where+ mempty = PackageIndex (Map.empty)+ mappend = merge+ --save one mappend with empty in the common case:+ mconcat [] = mempty+ mconcat xs = foldr1 mappend xs++invariant :: Package pkg => PackageIndex pkg -> Bool+invariant (PackageIndex m) = all (uncurry goodBucket) (Map.toList m)+ where+ goodBucket _ [] = False+ goodBucket name (pkg0:pkgs0) = check (packageId pkg0) pkgs0+ where+ check pkgid [] = packageName pkgid == name+ check pkgid (pkg':pkgs) = packageName pkgid == name+ && pkgid < pkgid'+ && check pkgid' pkgs+ where pkgid' = packageId pkg'++--+-- * Internal helpers+--++mkPackageIndex :: Package pkg => Map PackageName [pkg] -> PackageIndex pkg+mkPackageIndex index = assert (invariant (PackageIndex index))+ (PackageIndex index)++internalError :: String -> a+internalError name = error ("PackageIndex." ++ name ++ ": internal error")++-- | Lookup a name in the index to get all packages that match that name+-- case-sensitively.+--+lookup :: Package pkg => PackageIndex pkg -> PackageName -> [pkg]+lookup (PackageIndex m) name = fromMaybe [] $ Map.lookup name m++--+-- * Construction+--++-- | Build an index out of a bunch of packages.+--+-- If there are duplicates, later ones mask earlier ones.+--+fromList :: Package pkg => [pkg] -> PackageIndex pkg+fromList pkgs = mkPackageIndex+ . Map.map fixBucket+ . Map.fromListWith (++)+ $ [ (packageName pkg, [pkg])+ | pkg <- pkgs ]+ where+ fixBucket = -- out of groups of duplicates, later ones mask earlier ones+ -- but Map.fromListWith (++) constructs groups in reverse order+ map head+ -- Eq instance for PackageIdentifier is wrong, so use Ord:+ . groupBy (\a b -> EQ == comparing packageId a b)+ -- relies on sortBy being a stable sort so we+ -- can pick consistently among duplicates+ . sortBy (comparing packageId)++--+-- * Updates+--++-- | Merge two indexes.+--+-- Packages from the second mask packages of the same exact name+-- (case-sensitively) from the first.+--+merge :: Package pkg => PackageIndex pkg -> PackageIndex pkg -> PackageIndex pkg+merge i1@(PackageIndex m1) i2@(PackageIndex m2) =+ assert (invariant i1 && invariant i2) $+ mkPackageIndex (Map.unionWith mergeBuckets m1 m2)++-- | Elements in the second list mask those in the first.+mergeBuckets :: Package pkg => [pkg] -> [pkg] -> [pkg]+mergeBuckets [] ys = ys+mergeBuckets xs [] = xs+mergeBuckets xs@(x:xs') ys@(y:ys') =+ case packageId x `compare` packageId y of+ GT -> y : mergeBuckets xs ys'+ EQ -> y : mergeBuckets xs' ys'+ LT -> x : mergeBuckets xs' ys++-- | Inserts a single package into the index.+--+-- This is equivalent to (but slightly quicker than) using 'mappend' or+-- 'merge' with a singleton index.+--+insert :: Package pkg => pkg -> PackageIndex pkg -> PackageIndex pkg+insert pkg (PackageIndex index) = mkPackageIndex $+ Map.insertWith (\_ -> insertNoDup) (packageName pkg) [pkg] index+ where+ pkgid = packageId pkg+ insertNoDup [] = [pkg]+ insertNoDup pkgs@(pkg':pkgs') = case compare pkgid (packageId pkg') of+ LT -> pkg : pkgs+ EQ -> pkg : pkgs'+ GT -> pkg' : insertNoDup pkgs'++-- | Internal delete helper.+--+delete :: Package pkg => PackageName -> (pkg -> Bool) -> PackageIndex pkg -> PackageIndex pkg+delete name p (PackageIndex index) = mkPackageIndex $+ Map.update filterBucket name index+ where+ filterBucket = deleteEmptyBucket+ . filter (not . p)+ deleteEmptyBucket [] = Nothing+ deleteEmptyBucket remaining = Just remaining++-- | Removes a single package from the index.+--+deletePackageId :: Package pkg => PackageIdentifier -> PackageIndex pkg -> PackageIndex pkg+deletePackageId pkgid =+ delete (packageName pkgid) (\pkg -> packageId pkg == pkgid)++-- | Removes all packages with this (case-sensitive) name from the index.+--+deletePackageName :: Package pkg => PackageName -> PackageIndex pkg -> PackageIndex pkg+deletePackageName name =+ delete name (\pkg -> packageName pkg == name)++-- | Removes all packages satisfying this dependency from the index.+--+deleteDependency :: Package pkg => Dependency -> PackageIndex pkg -> PackageIndex pkg+deleteDependency (Dependency name verstionRange) =+ delete name (\pkg -> packageVersion pkg `withinRange` verstionRange)++--+-- * Bulk queries+--++-- | Get all the packages from the index.+--+allPackages :: Package pkg => PackageIndex pkg -> [pkg]+allPackages (PackageIndex m) = concat (Map.elems m)++-- | Get all the packages from the index.+--+-- They are grouped by package name, case-sensitively.+--+allPackagesByName :: Package pkg => PackageIndex pkg -> [[pkg]]+allPackagesByName (PackageIndex m) = Map.elems m++--+-- * Lookups+--++-- | Does a lookup by package id (name & version).+--+-- Since multiple package DBs mask each other case-sensitively by package name,+-- then we get back at most one package.+--+lookupPackageId :: Package pkg => PackageIndex pkg -> PackageIdentifier -> Maybe pkg+lookupPackageId index pkgid =+ case [ pkg | pkg <- lookup index (packageName pkgid)+ , packageId pkg == pkgid ] of+ [] -> Nothing+ [pkg] -> Just pkg+ _ -> internalError "lookupPackageIdentifier"++-- | Does a case-sensitive search by package name.+--+lookupPackageName :: Package pkg => PackageIndex pkg -> PackageName -> [pkg]+lookupPackageName index name =+ [ pkg | pkg <- lookup index name+ , packageName pkg == name ]++-- | Does a case-sensitive search by package name and a range of versions.+--+-- We get back any number of versions of the specified package name, all+-- satisfying the version range constraint.+--+lookupDependency :: Package pkg => PackageIndex pkg -> Dependency -> [pkg]+lookupDependency index (Dependency name versionRange) =+ [ pkg | pkg <- lookup index name+ , packageName pkg == name+ , packageVersion pkg `withinRange` versionRange ]++--+-- * Case insensitive name lookups+--++-- | Does a case-insensitive search by package name.+--+-- If there is only one package that compares case-insentiviely to this name+-- then the search is unambiguous and we get back all versions of that package.+-- If several match case-insentiviely but one matches exactly then it is also+-- unambiguous.+--+-- If however several match case-insentiviely and none match exactly then we+-- have an ambiguous result, and we get back all the versions of all the+-- packages. The list of ambiguous results is split by exact package name. So+-- it is a non-empty list of non-empty lists.+--+searchByName :: Package pkg => PackageIndex pkg+ -> String -> [(PackageName, [pkg])]+searchByName (PackageIndex m) name =+ [ pkgs+ | pkgs@(PackageName name',_) <- Map.toList m+ , lowercase name' == lname ]+ where+ lname = lowercase name++data SearchResult a = None | Unambiguous a | Ambiguous [a]++-- | Does a case-insensitive substring search by package name.+--+-- That is, all packages that contain the given string in their name.+--+searchByNameSubstring :: Package pkg => PackageIndex pkg+ -> String -> [(PackageName, [pkg])]+searchByNameSubstring (PackageIndex m) searchterm =+ [ pkgs+ | pkgs@(PackageName name, _) <- Map.toList m+ , lsearchterm `isInfixOf` lowercase name ]+ where+ lsearchterm = lowercase searchterm++--+-- * Special queries+--++-- | All packages that have dependencies that are not in the index.+--+-- Returns such packages along with the dependencies that they're missing.+--+brokenPackages :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> [(pkg, [PackageIdentifier])]+brokenPackages index =+ [ (pkg, missing)+ | pkg <- allPackages index+ , let missing = [ pkg' | pkg' <- depends pkg+ , isNothing (lookupPackageId index pkg') ]+ , not (null missing) ]++-- | Tries to take the transative closure of the package dependencies.+--+-- If the transative closure is complete then it returns that subset of the+-- index. Otherwise it returns the broken packages as in 'brokenPackages'.+--+-- * Note that if the result is @Right []@ it is because at least one of+-- the original given 'PackageIdentifier's do not occur in the index.+--+dependencyClosure :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> [PackageIdentifier]+ -> Either (PackageIndex pkg)+ [(pkg, [PackageIdentifier])]+dependencyClosure index pkgids0 = case closure mempty [] pkgids0 of+ (completed, []) -> Left completed+ (completed, _) -> Right (brokenPackages completed)+ where+ closure completed failed [] = (completed, failed)+ closure completed failed (pkgid:pkgids) = case lookupPackageId index pkgid of+ Nothing -> closure completed (pkgid:failed) pkgids+ Just pkg -> case lookupPackageId completed (packageId pkg) of+ Just _ -> closure completed failed pkgids+ Nothing -> closure completed' failed pkgids'+ where completed' = insert pkg completed+ pkgids' = depends pkg ++ pkgids++-- | Takes the transative closure of the packages reverse dependencies.+--+-- * The given 'PackageIdentifier's must be in the index.+--+reverseDependencyClosure :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> [PackageIdentifier]+ -> [pkg]+reverseDependencyClosure index =+ map vertexToPkg+ . concatMap Tree.flatten+ . Graph.dfs reverseDepGraph+ . map (fromMaybe noSuchPkgId . pkgIdToVertex)++ where+ (depGraph, vertexToPkg, pkgIdToVertex) = dependencyGraph index+ reverseDepGraph = Graph.transposeG depGraph+ noSuchPkgId = error "reverseDependencyClosure: package is not in the graph"++topologicalOrder :: PackageFixedDeps pkg => PackageIndex pkg -> [pkg]+topologicalOrder index = map toPkgId+ . Graph.topSort+ $ graph+ where (graph, toPkgId, _) = dependencyGraph index++reverseTopologicalOrder :: PackageFixedDeps pkg => PackageIndex pkg -> [pkg]+reverseTopologicalOrder index = map toPkgId+ . Graph.topSort+ . Graph.transposeG+ $ graph+ where (graph, toPkgId, _) = dependencyGraph index++-- | Given a package index where we assume we want to use all the packages+-- (use 'dependencyClosure' if you need to get such a index subset) find out+-- if the dependencies within it use consistent versions of each package.+-- Return all cases where multiple packages depend on different versions of+-- some other package.+--+-- Each element in the result is a package name along with the packages that+-- depend on it and the versions they require. These are guaranteed to be+-- distinct.+--+dependencyInconsistencies :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> [(PackageName, [(PackageIdentifier, Version)])]+dependencyInconsistencies index =+ [ (name, inconsistencies)+ | (name, uses) <- Map.toList inverseIndex+ , let inconsistencies = duplicatesBy uses+ versions = map snd inconsistencies+ , reallyIsInconsistent name (nub versions) ]++ where inverseIndex = Map.fromListWith (++)+ [ (packageName dep, [(packageId pkg, packageVersion dep)])+ | pkg <- allPackages index+ , dep <- depends pkg ]++ duplicatesBy = (\groups -> if length groups == 1+ then []+ else concat groups)+ . groupBy (equating snd)+ . sortBy (comparing snd)++ reallyIsInconsistent :: PackageName -> [Version] -> Bool+ reallyIsInconsistent _ [] = False+ reallyIsInconsistent name [v1, v2] =+ case (mpkg1, mpkg2) of+ (Just pkg1, Just pkg2) -> pkgid1 `notElem` depends pkg2+ && pkgid2 `notElem` depends pkg1+ _ -> True+ where+ pkgid1 = PackageIdentifier name v1+ pkgid2 = PackageIdentifier name v2+ mpkg1 = lookupPackageId index pkgid1+ mpkg2 = lookupPackageId index pkgid2++ reallyIsInconsistent _ _ = True++-- | Find if there are any cycles in the dependency graph. If there are no+-- cycles the result is @[]@.+--+-- This actually computes the strongly connected components. So it gives us a+-- list of groups of packages where within each group they all depend on each+-- other, directly or indirectly.+--+dependencyCycles :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> [[pkg]]+dependencyCycles index =+ [ vs | Graph.CyclicSCC vs <- Graph.stronglyConnComp adjacencyList ]+ where+ adjacencyList = [ (pkg, packageId pkg, depends pkg)+ | pkg <- allPackages index ]++-- | Builds a graph of the package dependencies.+--+-- Dependencies on other packages that are not in the index are discarded.+-- You can check if there are any such dependencies with 'brokenPackages'.+--+dependencyGraph :: PackageFixedDeps pkg+ => PackageIndex pkg+ -> (Graph.Graph,+ Graph.Vertex -> pkg,+ PackageIdentifier -> Maybe Graph.Vertex)+dependencyGraph index = (graph, vertexToPkg, pkgIdToVertex)+ where+ graph = Array.listArray bounds+ [ [ v | Just v <- map pkgIdToVertex (depends pkg) ]+ | pkg <- pkgs ]+ vertexToPkg vertex = pkgTable ! vertex+ pkgIdToVertex = binarySearch 0 topBound++ pkgTable = Array.listArray bounds pkgs+ pkgIdTable = Array.listArray bounds (map packageId pkgs)+ pkgs = sortBy (comparing packageId) (allPackages index)+ topBound = length pkgs - 1+ bounds = (0, topBound)++ binarySearch a b key+ | a > b = Nothing+ | otherwise = case compare key (pkgIdTable ! mid) of+ LT -> binarySearch a (mid-1) key+ EQ -> Just mid+ GT -> binarySearch (mid+1) b key+ where mid = (a + b) `div` 2
+ Distribution/Client/PackageUtils.hs view
@@ -0,0 +1,34 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.PackageUtils+-- Copyright : (c) Duncan Coutts 2010+-- License : BSD-like+--+-- Maintainer : cabal-devel@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Various package description utils that should be in the Cabal lib+-----------------------------------------------------------------------------+module Distribution.Client.PackageUtils (+ externalBuildDepends,+ ) where++import Distribution.Package+ ( packageVersion, packageName, Dependency(..) )+import Distribution.PackageDescription+ ( PackageDescription(..) )+import Distribution.Version+ ( withinRange )++-- | The list of dependencies that refer to external packages+-- rather than internal package components.+--+externalBuildDepends :: PackageDescription -> [Dependency]+externalBuildDepends pkg = filter (not . internal) (buildDepends pkg)+ where+ -- True if this dependency is an internal one (depends on a library+ -- defined in the same package).+ internal (Dependency depName versionRange) =+ depName == packageName pkg &&+ packageVersion pkg `withinRange` versionRange
+ Distribution/Client/Setup.hs view
@@ -0,0 +1,1009 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Setup+-- Copyright : (c) David Himmelstrup 2005+-- License : BSD-like+--+-- Maintainer : lemmih@gmail.com+-- Stability : provisional+-- Portability : portable+--+--+-----------------------------------------------------------------------------+module Distribution.Client.Setup+ ( globalCommand, GlobalFlags(..), globalRepos+ , configureCommand, ConfigFlags(..), filterConfigureFlags+ , configureExCommand, ConfigExFlags(..), defaultConfigExFlags+ , configureExOptions+ , installCommand, InstallFlags(..), installOptions, defaultInstallFlags+ , listCommand, ListFlags(..)+ , updateCommand+ , upgradeCommand+ , infoCommand, InfoFlags(..)+ , fetchCommand, FetchFlags(..)+ , checkCommand+ , uploadCommand, UploadFlags(..)+ , reportCommand, ReportFlags(..)+ , unpackCommand, UnpackFlags(..)+ , initCommand, IT.InitFlags(..)++ , parsePackageArgs+ --TODO: stop exporting these:+ , showRepo+ , parseRepo+ ) where++import Distribution.Client.Types+ ( Username(..), Password(..), Repo(..), RemoteRepo(..), LocalRepo(..) )+import Distribution.Client.BuildReports.Types+ ( ReportLevel(..) )+import qualified Distribution.Client.Init.Types as IT+ ( InitFlags(..), PackageType(..) )++import Distribution.Simple.Program+ ( defaultProgramConfiguration )+import Distribution.Simple.Command hiding (boolOpt)+import qualified Distribution.Simple.Command as Command+import qualified Distribution.Simple.Setup as Cabal+ ( configureCommand )+import Distribution.Simple.Setup+ ( ConfigFlags(..) )+import Distribution.Simple.Setup+ ( Flag(..), toFlag, fromFlag, flagToList, flagToMaybe+ , optionVerbosity, trueArg, falseArg )+import Distribution.Simple.InstallDirs+ ( PathTemplate, toPathTemplate, fromPathTemplate )+import Distribution.Version+ ( Version(Version), anyVersion, thisVersion )+import Distribution.Package+ ( PackageIdentifier, packageName, packageVersion, Dependency(..) )+import Distribution.Text+ ( Text(parse), display )+import Distribution.ReadE+ ( readP_to_E, succeedReadE )+import qualified Distribution.Compat.ReadP as Parse+ ( ReadP, readP_to_S, char, munch1, pfail, (+++) )+import Distribution.Verbosity+ ( Verbosity, normal )+import Distribution.Simple.Utils+ ( wrapText )++import Data.Char+ ( isSpace, isAlphaNum )+import Data.Maybe+ ( listToMaybe, maybeToList, fromMaybe )+import Data.Monoid+ ( Monoid(..) )+import Control.Monad+ ( liftM )+import System.FilePath+ ( (</>) )+import Network.URI+ ( parseAbsoluteURI, uriToString )++-- ------------------------------------------------------------+-- * Global flags+-- ------------------------------------------------------------++-- | Flags that apply at the top level, not to any sub-command.+data GlobalFlags = GlobalFlags {+ globalVersion :: Flag Bool,+ globalNumericVersion :: Flag Bool,+ globalConfigFile :: Flag FilePath,+ globalRemoteRepos :: [RemoteRepo], -- ^Available Hackage servers.+ globalCacheDir :: Flag FilePath,+ globalLocalRepos :: [FilePath],+ globalLogsDir :: Flag FilePath,+ globalWorldFile :: Flag FilePath+ }++defaultGlobalFlags :: GlobalFlags+defaultGlobalFlags = GlobalFlags {+ globalVersion = Flag False,+ globalNumericVersion = Flag False,+ globalConfigFile = mempty,+ globalRemoteRepos = [],+ globalCacheDir = mempty,+ globalLocalRepos = mempty,+ globalLogsDir = mempty,+ globalWorldFile = mempty+ }++globalCommand :: CommandUI GlobalFlags+globalCommand = CommandUI {+ commandName = "",+ commandSynopsis = "",+ commandUsage = \_ ->+ "This program is the command line interface "+ ++ "to the Haskell Cabal infrastructure.\n"+ ++ "See http://www.haskell.org/cabal/ for more information.\n",+ commandDescription = Just $ \pname ->+ "For more information about a command use:\n"+ ++ " " ++ pname ++ " COMMAND --help\n\n"+ ++ "To install Cabal packages from hackage use:\n"+ ++ " " ++ pname ++ " install foo [--dry-run]\n\n"+ ++ "Occasionally you need to update the list of available packages:\n"+ ++ " " ++ pname ++ " update\n",+ commandDefaultFlags = defaultGlobalFlags,+ commandOptions = \showOrParseArgs ->+ (case showOrParseArgs of ShowArgs -> take 2; ParseArgs -> id)+ [option ['V'] ["version"]+ "Print version information"+ globalVersion (\v flags -> flags { globalVersion = v })+ trueArg++ ,option [] ["numeric-version"]+ "Print just the version number"+ globalNumericVersion (\v flags -> flags { globalNumericVersion = v })+ trueArg++ ,option [] ["config-file"]+ "Set an alternate location for the config file"+ globalConfigFile (\v flags -> flags { globalConfigFile = v })+ (reqArgFlag "FILE")++ ,option [] ["remote-repo"]+ "The name and url for a remote repository"+ globalRemoteRepos (\v flags -> flags { globalRemoteRepos = v })+ (reqArg' "NAME:URL" (maybeToList . readRepo) (map showRepo))++ ,option [] ["remote-repo-cache"]+ "The location where downloads from all remote repos are cached"+ globalCacheDir (\v flags -> flags { globalCacheDir = v })+ (reqArgFlag "DIR")++ ,option [] ["local-repo"]+ "The location of a local repository"+ globalLocalRepos (\v flags -> flags { globalLocalRepos = v })+ (reqArg' "DIR" (\x -> [x]) id)++ ,option [] ["logs-dir"]+ "The location to put log files"+ globalLogsDir (\v flags -> flags { globalLogsDir = v })+ (reqArgFlag "DIR")++ ,option [] ["world-file"]+ "The location of the world file"+ globalWorldFile (\v flags -> flags { globalWorldFile = v })+ (reqArgFlag "FILE")+ ]+ }++instance Monoid GlobalFlags where+ mempty = GlobalFlags {+ globalVersion = mempty,+ globalNumericVersion = mempty,+ globalConfigFile = mempty,+ globalRemoteRepos = mempty,+ globalCacheDir = mempty,+ globalLocalRepos = mempty,+ globalLogsDir = mempty,+ globalWorldFile = mempty+ }+ mappend a b = GlobalFlags {+ globalVersion = combine globalVersion,+ globalNumericVersion = combine globalNumericVersion,+ globalConfigFile = combine globalConfigFile,+ globalRemoteRepos = combine globalRemoteRepos,+ globalCacheDir = combine globalCacheDir,+ globalLocalRepos = combine globalLocalRepos,+ globalLogsDir = combine globalLogsDir,+ globalWorldFile = combine globalWorldFile+ }+ where combine field = field a `mappend` field b++globalRepos :: GlobalFlags -> [Repo]+globalRepos globalFlags = remoteRepos ++ localRepos+ where+ remoteRepos =+ [ Repo (Left remote) cacheDir+ | remote <- globalRemoteRepos globalFlags+ , let cacheDir = fromFlag (globalCacheDir globalFlags)+ </> remoteRepoName remote ]+ localRepos =+ [ Repo (Right LocalRepo) local+ | local <- globalLocalRepos globalFlags ]++-- ------------------------------------------------------------+-- * Config flags+-- ------------------------------------------------------------++configureCommand :: CommandUI ConfigFlags+configureCommand = (Cabal.configureCommand defaultProgramConfiguration) {+ commandDefaultFlags = mempty+ }++configureOptions :: ShowOrParseArgs -> [OptionField ConfigFlags]+configureOptions = commandOptions configureCommand++filterConfigureFlags :: ConfigFlags -> Version -> ConfigFlags+filterConfigureFlags flags cabalLibVersion+ | cabalLibVersion >= Version [1,3,10] [] = flags+ -- older Cabal does not grok the constraints flag:+ | otherwise = flags { configConstraints = [] }+++-- ------------------------------------------------------------+-- * Config extra flags+-- ------------------------------------------------------------++-- | cabal configure takes some extra flags beyond runghc Setup configure+--+data ConfigExFlags = ConfigExFlags {+ configCabalVersion :: Flag Version,+ configPreferences :: [Dependency]+ }++defaultConfigExFlags :: ConfigExFlags+defaultConfigExFlags = mempty++configureExCommand :: CommandUI (ConfigFlags, ConfigExFlags)+configureExCommand = configureCommand {+ commandDefaultFlags = (mempty, defaultConfigExFlags),+ commandOptions = \showOrParseArgs ->+ liftOptions fst setFst (configureOptions showOrParseArgs)+ ++ liftOptions snd setSnd (configureExOptions showOrParseArgs)+ }+ where+ setFst a (_,b) = (a,b)+ setSnd b (a,_) = (a,b)++configureExOptions :: ShowOrParseArgs -> [OptionField ConfigExFlags]+configureExOptions _showOrParseArgs =+ [ option [] ["cabal-lib-version"]+ ("Select which version of the Cabal lib to use to build packages "+ ++ "(useful for testing).")+ configCabalVersion (\v flags -> flags { configCabalVersion = v })+ (reqArg "VERSION" (readP_to_E ("Cannot parse cabal lib version: "++)+ (fmap toFlag parse))+ (map display . flagToList))++ , option [] ["preference"]+ "Specify preferences (soft constraints) on the version of a package"+ configPreferences (\v flags -> flags { configPreferences = v })+ (reqArg "DEPENDENCY"+ (readP_to_E (const "dependency expected") ((\x -> [x]) `fmap` parse))+ (map (\x -> display x)))+ ]++instance Monoid ConfigExFlags where+ mempty = ConfigExFlags {+ configCabalVersion = mempty,+ configPreferences = mempty+ }+ mappend a b = ConfigExFlags {+ configCabalVersion = combine configCabalVersion,+ configPreferences = combine configPreferences+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Fetch command+-- ------------------------------------------------------------++data FetchFlags = FetchFlags {+-- fetchOutput :: Flag FilePath,+ fetchDeps :: Flag Bool,+ fetchDryRun :: Flag Bool,+ fetchVerbosity :: Flag Verbosity+ }++defaultFetchFlags :: FetchFlags+defaultFetchFlags = FetchFlags {+-- fetchOutput = mempty,+ fetchDeps = toFlag True,+ fetchDryRun = toFlag False,+ fetchVerbosity = toFlag normal+ }++fetchCommand :: CommandUI FetchFlags+fetchCommand = CommandUI {+ commandName = "fetch",+ commandSynopsis = "Downloads packages for later installation.",+ commandDescription = Nothing,+ commandUsage = usagePackages "fetch",+ commandDefaultFlags = defaultFetchFlags,+ commandOptions = \_ -> [+ optionVerbosity fetchVerbosity (\v flags -> flags { fetchVerbosity = v })++-- , option "o" ["output"]+-- "Put the package(s) somewhere specific rather than the usual cache."+-- fetchOutput (\v flags -> flags { fetchOutput = v })+-- (reqArgFlag "PATH")++ , option [] ["dependencies", "deps"]+ "Resolve and fetch dependencies (default)"+ fetchDeps (\v flags -> flags { fetchDeps = v })+ trueArg++ , option [] ["no-dependencies", "no-deps"]+ "Ignore dependencies"+ fetchDeps (\v flags -> flags { fetchDeps = v })+ falseArg++ , option [] ["dry-run"]+ "Do not install anything, only print what would be installed."+ fetchDryRun (\v flags -> flags { fetchDryRun = v })+ trueArg+ ]+ }++-- ------------------------------------------------------------+-- * Other commands+-- ------------------------------------------------------------++updateCommand :: CommandUI (Flag Verbosity)+updateCommand = CommandUI {+ commandName = "update",+ commandSynopsis = "Updates list of known packages",+ commandDescription = Nothing,+ commandUsage = usagePackages "update",+ commandDefaultFlags = toFlag normal,+ commandOptions = \_ -> [optionVerbosity id const]+ }++upgradeCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags)+upgradeCommand = configureCommand {+ commandName = "upgrade",+ commandSynopsis = "(command disabled, use install instead)",+ commandDescription = Nothing,+ commandUsage = usagePackages "upgrade",+ commandDefaultFlags = (mempty, mempty, mempty),+ commandOptions = commandOptions installCommand+ }++{-+cleanCommand :: CommandUI ()+cleanCommand = makeCommand name shortDesc longDesc emptyFlags options+ where+ name = "clean"+ shortDesc = "Removes downloaded files"+ longDesc = Nothing+ emptyFlags = ()+ options _ = []+-}++checkCommand :: CommandUI (Flag Verbosity)+checkCommand = CommandUI {+ commandName = "check",+ commandSynopsis = "Check the package for common mistakes",+ commandDescription = Nothing,+ commandUsage = \pname -> "Usage: " ++ pname ++ " check\n",+ commandDefaultFlags = toFlag normal,+ commandOptions = \_ -> []+ }++-- ------------------------------------------------------------+-- * Report flags+-- ------------------------------------------------------------++data ReportFlags = ReportFlags {+ reportUsername :: Flag Username,+ reportPassword :: Flag Password,+ reportVerbosity :: Flag Verbosity+ }++defaultReportFlags :: ReportFlags+defaultReportFlags = ReportFlags {+ reportUsername = mempty,+ reportPassword = mempty,+ reportVerbosity = toFlag normal+ }++reportCommand :: CommandUI ReportFlags+reportCommand = CommandUI {+ commandName = "report",+ commandSynopsis = "Upload build reports to a remote server.",+ commandDescription = Just $ \_ ->+ "You can store your Hackage login in the ~/.cabal/config file\n",+ commandUsage = \pname -> "Usage: " ++ pname ++ " report [FLAGS]\n\n"+ ++ "Flags for upload:",+ commandDefaultFlags = defaultReportFlags,+ commandOptions = \_ ->+ [optionVerbosity reportVerbosity (\v flags -> flags { reportVerbosity = v })++ ,option ['u'] ["username"]+ "Hackage username."+ reportUsername (\v flags -> flags { reportUsername = v })+ (reqArg' "USERNAME" (toFlag . Username)+ (flagToList . fmap unUsername))++ ,option ['p'] ["password"]+ "Hackage password."+ reportPassword (\v flags -> flags { reportPassword = v })+ (reqArg' "PASSWORD" (toFlag . Password)+ (flagToList . fmap unPassword))+ ]+ }++instance Monoid ReportFlags where+ mempty = ReportFlags {+ reportUsername = mempty,+ reportPassword = mempty,+ reportVerbosity = mempty+ }+ mappend a b = ReportFlags {+ reportUsername = combine reportUsername,+ reportPassword = combine reportPassword,+ reportVerbosity = combine reportVerbosity+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Unpack flags+-- ------------------------------------------------------------++data UnpackFlags = UnpackFlags {+ unpackDestDir :: Flag FilePath,+ unpackVerbosity :: Flag Verbosity+ }++defaultUnpackFlags :: UnpackFlags+defaultUnpackFlags = UnpackFlags {+ unpackDestDir = mempty,+ unpackVerbosity = toFlag normal+ }++unpackCommand :: CommandUI UnpackFlags+unpackCommand = CommandUI {+ commandName = "unpack",+ commandSynopsis = "Unpacks packages for user inspection.",+ commandDescription = Nothing,+ commandUsage = usagePackages "unpack",+ commandDefaultFlags = mempty,+ commandOptions = \_ -> [+ optionVerbosity unpackVerbosity (\v flags -> flags { unpackVerbosity = v })++ ,option "d" ["destdir"]+ "where to unpack the packages, defaults to the current directory."+ unpackDestDir (\v flags -> flags { unpackDestDir = v })+ (reqArgFlag "PATH")+ ]+ }++instance Monoid UnpackFlags where+ mempty = defaultUnpackFlags+ mappend a b = UnpackFlags {+ unpackDestDir = combine unpackDestDir+ ,unpackVerbosity = combine unpackVerbosity+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * List flags+-- ------------------------------------------------------------++data ListFlags = ListFlags {+ listInstalled :: Flag Bool,+ listSimpleOutput :: Flag Bool,+ listVerbosity :: Flag Verbosity+ }++defaultListFlags :: ListFlags+defaultListFlags = ListFlags {+ listInstalled = Flag False,+ listSimpleOutput = Flag False,+ listVerbosity = toFlag normal+ }++listCommand :: CommandUI ListFlags+listCommand = CommandUI {+ commandName = "list",+ commandSynopsis = "List packages matching a search string.",+ commandDescription = Nothing,+ commandUsage = usagePackages "list",+ commandDefaultFlags = defaultListFlags,+ commandOptions = \_ -> [+ optionVerbosity listVerbosity (\v flags -> flags { listVerbosity = v })++ , option [] ["installed"]+ "Only print installed packages"+ listInstalled (\v flags -> flags { listInstalled = v })+ trueArg++ , option [] ["simple-output"]+ "Print in a easy-to-parse format"+ listSimpleOutput (\v flags -> flags { listSimpleOutput = v })+ trueArg++ ]+ }++instance Monoid ListFlags where+ mempty = defaultListFlags+ mappend a b = ListFlags {+ listInstalled = combine listInstalled,+ listSimpleOutput = combine listSimpleOutput,+ listVerbosity = combine listVerbosity+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Info flags+-- ------------------------------------------------------------++data InfoFlags = InfoFlags {+ infoVerbosity :: Flag Verbosity+ }++defaultInfoFlags :: InfoFlags+defaultInfoFlags = InfoFlags {+ infoVerbosity = toFlag normal+ }++infoCommand :: CommandUI InfoFlags+infoCommand = CommandUI {+ commandName = "info",+ commandSynopsis = "Display detailed information about a particular package.",+ commandDescription = Nothing,+ commandUsage = usagePackages "info",+ commandDefaultFlags = defaultInfoFlags,+ commandOptions = \_ -> [+ optionVerbosity infoVerbosity (\v flags -> flags { infoVerbosity = v })+ ]+ }++instance Monoid InfoFlags where+ mempty = defaultInfoFlags+ mappend a b = InfoFlags {+ infoVerbosity = combine infoVerbosity+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Install flags+-- ------------------------------------------------------------++-- | Install takes the same flags as configure along with a few extras.+--+data InstallFlags = InstallFlags {+ installDocumentation:: Flag Bool,+ installHaddockIndex :: Flag PathTemplate,+ installDryRun :: Flag Bool,+ installReinstall :: Flag Bool,+ installUpgradeDeps :: Flag Bool,+ installOnly :: Flag Bool,+ installOnlyDeps :: Flag Bool,+ installRootCmd :: Flag String,+ installSummaryFile :: [PathTemplate],+ installLogFile :: Flag PathTemplate,+ installBuildReports :: Flag ReportLevel,+ installSymlinkBinDir:: Flag FilePath,+ installOneShot :: Flag Bool+ }++defaultInstallFlags :: InstallFlags+defaultInstallFlags = InstallFlags {+ installDocumentation= Flag False,+ installHaddockIndex = Flag docIndexFile,+ installDryRun = Flag False,+ installReinstall = Flag False,+ installUpgradeDeps = Flag False,+ installOnly = Flag False,+ installOnlyDeps = Flag False,+ installRootCmd = mempty,+ installSummaryFile = mempty,+ installLogFile = mempty,+ installBuildReports = Flag NoReports,+ installSymlinkBinDir= mempty,+ installOneShot = Flag False+ }+ where+ docIndexFile = toPathTemplate ("$datadir" </> "doc" </> "index.html")++installCommand :: CommandUI (ConfigFlags, ConfigExFlags, InstallFlags)+installCommand = CommandUI {+ commandName = "install",+ commandSynopsis = "Installs a list of packages.",+ commandUsage = usagePackages "install",+ commandDescription = Just $ \pname ->+ let original = case commandDescription configureCommand of+ Just desc -> desc pname ++ "\n"+ Nothing -> ""+ in original+ ++ "Examples:\n"+ ++ " " ++ pname ++ " install "+ ++ " Package in the current directory\n"+ ++ " " ++ pname ++ " install foo "+ ++ " Package from the hackage server\n"+ ++ " " ++ pname ++ " install foo-1.0 "+ ++ " Specific version of a package\n"+ ++ " " ++ pname ++ " install 'foo < 2' "+ ++ " Constrained package version\n",+ commandDefaultFlags = (mempty, mempty, mempty),+ commandOptions = \showOrParseArgs ->+ liftOptions get1 set1 (configureOptions showOrParseArgs)+ ++ liftOptions get2 set2 (configureExOptions showOrParseArgs)+ ++ liftOptions get3 set3 (installOptions showOrParseArgs)+ }+ where+ get1 (a,_,_) = a; set1 a (_,b,c) = (a,b,c)+ get2 (_,b,_) = b; set2 b (a,_,c) = (a,b,c)+ get3 (_,_,c) = c; set3 c (a,b,_) = (a,b,c)++installOptions :: ShowOrParseArgs -> [OptionField InstallFlags]+installOptions showOrParseArgs =+ [ option "" ["documentation"]+ "building of documentation"+ installDocumentation (\v flags -> flags { installDocumentation = v })+ (boolOpt [] [])++ , option [] ["doc-index-file"]+ "A central index of haddock API documentation (template cannot use $pkgid)"+ installHaddockIndex (\v flags -> flags { installHaddockIndex = v })+ (reqArg' "TEMPLATE" (toFlag.toPathTemplate)+ (flagToList . fmap fromPathTemplate))++ , option [] ["dry-run"]+ "Do not install anything, only print what would be installed."+ installDryRun (\v flags -> flags { installDryRun = v })+ trueArg++ , option [] ["reinstall"]+ "Install even if it means installing the same version again."+ installReinstall (\v flags -> flags { installReinstall = v })+ trueArg++ , option [] ["upgrade-dependencies"]+ "Pick the latest version for all dependencies, rather than trying to pick an installed version."+ installUpgradeDeps (\v flags -> flags { installUpgradeDeps = v })+ trueArg+++ , option [] ["only-dependencies"]+ "Install only the dependencies necessary to build the given packages"+ installOnlyDeps (\v flags -> flags { installOnlyDeps = v })+ trueArg+++ , option [] ["root-cmd"]+ "Command used to gain root privileges, when installing with --global."+ installRootCmd (\v flags -> flags { installRootCmd = v })+ (reqArg' "COMMAND" toFlag flagToList)++ , option [] ["symlink-bindir"]+ "Add symlinks to installed executables into this directory."+ installSymlinkBinDir (\v flags -> flags { installSymlinkBinDir = v })+ (reqArgFlag "DIR")++ , option [] ["build-summary"]+ "Save build summaries to file (name template can use $pkgid, $compiler, $os, $arch)"+ installSummaryFile (\v flags -> flags { installSummaryFile = v })+ (reqArg' "TEMPLATE" (\x -> [toPathTemplate x]) (map fromPathTemplate))++ , option [] ["build-log"]+ "Log all builds to file (name template can use $pkgid, $compiler, $os, $arch)"+ installLogFile (\v flags -> flags { installLogFile = v })+ (reqArg' "TEMPLATE" (toFlag.toPathTemplate)+ (flagToList . fmap fromPathTemplate))++ , option [] ["remote-build-reporting"]+ "Generate build reports to send to a remote server (none, anonymous or detailed)."+ installBuildReports (\v flags -> flags { installBuildReports = v })+ (reqArg "LEVEL" (readP_to_E (const $ "report level must be 'none', "+ ++ "'anonymous' or 'detailed'")+ (toFlag `fmap` parse))+ (flagToList . fmap display))++ , option [] ["one-shot"]+ "Do not record the packages in the world file."+ installOneShot (\v flags -> flags { installOneShot = v })+ trueArg+ ] ++ case showOrParseArgs of -- TODO: remove when "cabal install" avoids+ ParseArgs ->+ option [] ["only"]+ "Only installs the package in the current directory."+ installOnly (\v flags -> flags { installOnly = v })+ trueArg+ : []+ _ -> []++instance Monoid InstallFlags where+ mempty = InstallFlags {+ installDocumentation= mempty,+ installHaddockIndex = mempty,+ installDryRun = mempty,+ installReinstall = mempty,+ installUpgradeDeps = mempty,+ installOnly = mempty,+ installOnlyDeps = mempty,+ installRootCmd = mempty,+ installSummaryFile = mempty,+ installLogFile = mempty,+ installBuildReports = mempty,+ installSymlinkBinDir= mempty,+ installOneShot = mempty+ }+ mappend a b = InstallFlags {+ installDocumentation= combine installDocumentation,+ installHaddockIndex = combine installHaddockIndex,+ installDryRun = combine installDryRun,+ installReinstall = combine installReinstall,+ installUpgradeDeps = combine installUpgradeDeps,+ installOnly = combine installOnly,+ installOnlyDeps = combine installOnlyDeps,+ installRootCmd = combine installRootCmd,+ installSummaryFile = combine installSummaryFile,+ installLogFile = combine installLogFile,+ installBuildReports = combine installBuildReports,+ installSymlinkBinDir= combine installSymlinkBinDir,+ installOneShot = combine installOneShot+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Upload flags+-- ------------------------------------------------------------++data UploadFlags = UploadFlags {+ uploadCheck :: Flag Bool,+ uploadUsername :: Flag Username,+ uploadPassword :: Flag Password,+ uploadVerbosity :: Flag Verbosity+ }++defaultUploadFlags :: UploadFlags+defaultUploadFlags = UploadFlags {+ uploadCheck = toFlag False,+ uploadUsername = mempty,+ uploadPassword = mempty,+ uploadVerbosity = toFlag normal+ }++uploadCommand :: CommandUI UploadFlags+uploadCommand = CommandUI {+ commandName = "upload",+ commandSynopsis = "Uploads source packages to Hackage",+ commandDescription = Just $ \_ ->+ "You can store your Hackage login in the ~/.cabal/config file\n",+ commandUsage = \pname ->+ "Usage: " ++ pname ++ " upload [FLAGS] [TARFILES]\n\n"+ ++ "Flags for upload:",+ commandDefaultFlags = defaultUploadFlags,+ commandOptions = \_ ->+ [optionVerbosity uploadVerbosity (\v flags -> flags { uploadVerbosity = v })++ ,option ['c'] ["check"]+ "Do not upload, just do QA checks."+ uploadCheck (\v flags -> flags { uploadCheck = v })+ trueArg++ ,option ['u'] ["username"]+ "Hackage username."+ uploadUsername (\v flags -> flags { uploadUsername = v })+ (reqArg' "USERNAME" (toFlag . Username)+ (flagToList . fmap unUsername))++ ,option ['p'] ["password"]+ "Hackage password."+ uploadPassword (\v flags -> flags { uploadPassword = v })+ (reqArg' "PASSWORD" (toFlag . Password)+ (flagToList . fmap unPassword))+ ]+ }++instance Monoid UploadFlags where+ mempty = UploadFlags {+ uploadCheck = mempty,+ uploadUsername = mempty,+ uploadPassword = mempty,+ uploadVerbosity = mempty+ }+ mappend a b = UploadFlags {+ uploadCheck = combine uploadCheck,+ uploadUsername = combine uploadUsername,+ uploadPassword = combine uploadPassword,+ uploadVerbosity = combine uploadVerbosity+ }+ where combine field = field a `mappend` field b++-- ------------------------------------------------------------+-- * Init flags+-- ------------------------------------------------------------++emptyInitFlags :: IT.InitFlags+emptyInitFlags = mempty++defaultInitFlags :: IT.InitFlags+defaultInitFlags = emptyInitFlags++initCommand :: CommandUI IT.InitFlags+initCommand = CommandUI {+ commandName = "init",+ commandSynopsis = "Interactively create a .cabal file.",+ commandDescription = Just $ \_ -> wrapText $+ "Cabalise a project by creating a .cabal, Setup.hs, and "+ ++ "optionally a LICENSE file.\n\n"+ ++ "Calling init with no arguments (recommended) uses an "+ ++ "interactive mode, which will try to guess as much as "+ ++ "possible and prompt you for the rest. Command-line "+ ++ "arguments are provided for scripting purposes. "+ ++ "If you don't want interactive mode, be sure to pass "+ ++ "the -n flag.\n",+ commandUsage = \pname ->+ "Usage: " ++ pname ++ " init [FLAGS]\n\n"+ ++ "Flags for init:",+ commandDefaultFlags = defaultInitFlags,+ commandOptions = \_ ->+ [ option ['n'] ["non-interactive"]+ "Non-interactive mode."+ IT.nonInteractive (\v flags -> flags { IT.nonInteractive = v })+ trueArg++ , option ['q'] ["quiet"]+ "Do not generate log messages to stdout."+ IT.quiet (\v flags -> flags { IT.quiet = v })+ trueArg++ , option [] ["no-comments"]+ "Do not generate explanatory comments in the .cabal file."+ IT.noComments (\v flags -> flags { IT.noComments = v })+ trueArg++ , option ['m'] ["minimal"]+ "Generate a minimal .cabal file, that is, do not include extra empty fields. Also implies --no-comments."+ IT.minimal (\v flags -> flags { IT.minimal = v })+ trueArg++ , option [] ["package-dir"]+ "Root directory of the package (default = current directory)."+ IT.packageDir (\v flags -> flags { IT.packageDir = v })+ (reqArgFlag "DIRECTORY")++ , option ['p'] ["package-name"]+ "Name of the Cabal package to create."+ IT.packageName (\v flags -> flags { IT.packageName = v })+ (reqArgFlag "PACKAGE")++ , option [] ["version"]+ "Initial version of the package."+ IT.version (\v flags -> flags { IT.version = v })+ (reqArg "VERSION" (readP_to_E ("Cannot parse package version: "++)+ (toFlag `fmap` parse))+ (flagToList . fmap display))++ , option [] ["cabal-version"]+ "Required version of the Cabal library."+ IT.cabalVersion (\v flags -> flags { IT.cabalVersion = v })+ (reqArg "VERSION_RANGE" (readP_to_E ("Cannot parse Cabal version range: "++)+ (toFlag `fmap` parse))+ (flagToList . fmap display))++ , option ['l'] ["license"]+ "Project license."+ IT.license (\v flags -> flags { IT.license = v })+ (reqArg "LICENSE" (readP_to_E ("Cannot parse license: "++)+ (toFlag `fmap` parse))+ (flagToList . fmap display))++ , option ['a'] ["author"]+ "Name of the project's author."+ IT.author (\v flags -> flags { IT.author = v })+ (reqArgFlag "NAME")++ , option ['e'] ["email"]+ "Email address of the maintainer."+ IT.email (\v flags -> flags { IT.email = v })+ (reqArgFlag "EMAIL")++ , option ['u'] ["homepage"]+ "Project homepage and/or repository."+ IT.homepage (\v flags -> flags { IT.homepage = v })+ (reqArgFlag "URL")++ , option ['s'] ["synopsis"]+ "Short project synopsis."+ IT.synopsis (\v flags -> flags { IT.synopsis = v })+ (reqArgFlag "TEXT")++ , option ['c'] ["category"]+ "Project category."+ IT.category (\v flags -> flags { IT.category = v })+ (reqArg' "CATEGORY" (\s -> toFlag $ maybe (Left s) Right (readMaybe s))+ (flagToList . fmap (either id show)))++ , option [] ["is-library"]+ "Build a library."+ IT.packageType (\v flags -> flags { IT.packageType = v })+ (noArg (Flag IT.Library))++ , option [] ["is-executable"]+ "Build an executable."+ IT.packageType+ (\v flags -> flags { IT.packageType = v })+ (noArg (Flag IT.Executable))++ , option ['o'] ["expose-module"]+ "Export a module from the package."+ IT.exposedModules+ (\v flags -> flags { IT.exposedModules = v })+ (reqArg "MODULE" (readP_to_E ("Cannot parse module name: "++)+ ((Just . (:[])) `fmap` parse))+ (fromMaybe [] . fmap (fmap display)))++ , option ['d'] ["dependency"]+ "Package dependency."+ IT.dependencies (\v flags -> flags { IT.dependencies = v })+ (reqArg "PACKAGE" (readP_to_E ("Cannot parse dependency: "++)+ ((Just . (:[])) `fmap` parse))+ (fromMaybe [] . fmap (fmap display)))++ , option [] ["source-dir"]+ "Directory containing package source."+ IT.sourceDirs (\v flags -> flags { IT.sourceDirs = v })+ (reqArg' "DIR" (Just . (:[]))+ (fromMaybe []))++ , option [] ["build-tool"]+ "Required external build tool."+ IT.buildTools (\v flags -> flags { IT.buildTools = v })+ (reqArg' "TOOL" (Just . (:[]))+ (fromMaybe []))+ ]+ }+ where readMaybe s = case reads s of+ [(x,"")] -> Just x+ _ -> Nothing++-- ------------------------------------------------------------+-- * GetOpt Utils+-- ------------------------------------------------------------++boolOpt :: SFlags -> SFlags -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a+boolOpt = Command.boolOpt flagToMaybe Flag++reqArgFlag :: ArgPlaceHolder -> SFlags -> LFlags -> Description ->+ (b -> Flag String) -> (Flag String -> b -> b) -> OptDescr b+reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList++liftOptions :: (b -> a) -> (a -> b -> b)+ -> [OptionField a] -> [OptionField b]+liftOptions get set = map (liftOption get set)++usagePackages :: String -> String -> String+usagePackages name pname =+ "Usage: " ++ pname ++ " " ++ name ++ " [FLAGS]\n"+ ++ " or: " ++ pname ++ " " ++ name ++ " [PACKAGES]\n\n"+ ++ "Flags for " ++ name ++ ":"++--TODO: do we want to allow per-package flags?+parsePackageArgs :: [String] -> Either String [Dependency]+parsePackageArgs = parsePkgArgs []+ where+ parsePkgArgs ds [] = Right (reverse ds)+ parsePkgArgs ds (arg:args) =+ case readPToMaybe parseDependencyOrPackageId arg of+ Just dep -> parsePkgArgs (dep:ds) args+ Nothing -> Left $+ show arg ++ " is not valid syntax for a package name or"+ ++ " package dependency."++readPToMaybe :: Parse.ReadP a a -> String -> Maybe a+readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str+ , all isSpace s ]++parseDependencyOrPackageId :: Parse.ReadP r Dependency+parseDependencyOrPackageId = parse Parse.+++ liftM pkgidToDependency parse+ where+ pkgidToDependency :: PackageIdentifier -> Dependency+ pkgidToDependency p = case packageVersion p of+ Version [] _ -> Dependency (packageName p) anyVersion+ version -> Dependency (packageName p) (thisVersion version)++showRepo :: RemoteRepo -> String+showRepo repo = remoteRepoName repo ++ ":"+ ++ uriToString id (remoteRepoURI repo) []++readRepo :: String -> Maybe RemoteRepo+readRepo = readPToMaybe parseRepo++parseRepo :: Parse.ReadP r RemoteRepo+parseRepo = do+ name <- Parse.munch1 (\c -> isAlphaNum c || c `elem` "_-.")+ _ <- Parse.char ':'+ uriStr <- Parse.munch1 (\c -> isAlphaNum c || c `elem` "+-=._/*()@'$:;&!?~")+ uri <- maybe Parse.pfail return (parseAbsoluteURI uriStr)+ return $ RemoteRepo {+ remoteRepoName = name,+ remoteRepoURI = uri+ }
+ Distribution/Client/SetupWrapper.hs view
@@ -0,0 +1,320 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.SetupWrapper+-- Copyright : (c) The University of Glasgow 2006,+-- Duncan Coutts 2008+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : alpha+-- Portability : portable+--+-- An interface to building and installing Cabal packages.+-- If the @Built-Type@ field is specified as something other than+-- 'Custom', and the current version of Cabal is acceptable, this performs+-- setup actions directly. Otherwise it builds the setup script and+-- runs it with the given arguments.++module Distribution.Client.SetupWrapper (+ setupWrapper,+ SetupScriptOptions(..),+ defaultSetupScriptOptions,+ ) where++import Distribution.Client.Types+ ( InstalledPackage )++import qualified Distribution.Make as Make+import qualified Distribution.Simple as Simple+import Distribution.Version+ ( Version(..), VersionRange, anyVersion+ , intersectVersionRanges, orLaterVersion+ , withinRange )+import Distribution.Package+ ( PackageIdentifier(..), PackageName(..), Package(..), packageName+ , packageVersion, Dependency(..) )+import Distribution.PackageDescription+ ( GenericPackageDescription(packageDescription)+ , PackageDescription(..), specVersion, BuildType(..) )+import Distribution.PackageDescription.Parse+ ( readPackageDescription )+import Distribution.Simple.Configure+ ( configCompiler )+import Distribution.Simple.Compiler+ ( CompilerFlavor(GHC), Compiler, PackageDB(..), PackageDBStack )+import Distribution.Simple.Program+ ( ProgramConfiguration, emptyProgramConfiguration+ , rawSystemProgramConf, ghcProgram )+import Distribution.Simple.BuildPaths+ ( defaultDistPref, exeExtension )+import Distribution.Simple.Command+ ( CommandUI(..), commandShowOptions )+import Distribution.Simple.GHC+ ( ghcVerbosityOptions )+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.PackageIndex (PackageIndex)+import Distribution.Client.IndexUtils+ ( getInstalledPackages )+import Distribution.Simple.Utils+ ( die, debug, info, cabalVersion, findPackageDesc, comparing+ , createDirectoryIfMissingVerbose, rewriteFile )+import Distribution.Client.Utils+ ( moreRecentFile, inDir )+import Distribution.Text+ ( display )+import Distribution.Verbosity+ ( Verbosity )++import System.Directory ( doesFileExist, getCurrentDirectory )+import System.FilePath ( (</>), (<.>) )+import System.IO ( Handle )+import System.Exit ( ExitCode(..), exitWith )+import System.Process ( runProcess, waitForProcess )+import Control.Monad ( when, unless )+import Data.List ( maximumBy )+import Data.Maybe ( fromMaybe, isJust )+import Data.Char ( isSpace )++data SetupScriptOptions = SetupScriptOptions {+ useCabalVersion :: VersionRange,+ useCompiler :: Maybe Compiler,+ usePackageDB :: PackageDBStack,+ usePackageIndex :: Maybe (PackageIndex InstalledPackage),+ useProgramConfig :: ProgramConfiguration,+ useDistPref :: FilePath,+ useLoggingHandle :: Maybe Handle,+ useWorkingDir :: Maybe FilePath+ }++defaultSetupScriptOptions :: SetupScriptOptions+defaultSetupScriptOptions = SetupScriptOptions {+ useCabalVersion = anyVersion,+ useCompiler = Nothing,+ usePackageDB = [GlobalPackageDB, UserPackageDB],+ usePackageIndex = Nothing,+ useProgramConfig = emptyProgramConfiguration,+ useDistPref = defaultDistPref,+ useLoggingHandle = Nothing,+ useWorkingDir = Nothing+ }++setupWrapper :: Verbosity+ -> SetupScriptOptions+ -> Maybe PackageDescription+ -> CommandUI flags+ -> (Version -> flags)+ -> [String]+ -> IO ()+setupWrapper verbosity options mpkg cmd flags extraArgs = do+ pkg <- maybe getPkg return mpkg+ let setupMethod = determineSetupMethod options' buildType'+ options' = options {+ useCabalVersion = intersectVersionRanges+ (useCabalVersion options)+ (orLaterVersion (specVersion pkg))+ }+ buildType' = fromMaybe Custom (buildType pkg)+ mkArgs cabalLibVersion = commandName cmd+ : commandShowOptions cmd (flags cabalLibVersion)+ ++ extraArgs+ setupMethod verbosity options' (packageId pkg) buildType' mkArgs+ where+ getPkg = findPackageDesc (fromMaybe "." (useWorkingDir options))+ >>= readPackageDescription verbosity+ >>= return . packageDescription++-- | Decide if we're going to be able to do a direct internal call to the+-- entry point in the Cabal library or if we're going to have to compile+-- and execute an external Setup.hs script.+--+determineSetupMethod :: SetupScriptOptions -> BuildType -> SetupMethod+determineSetupMethod options buildType'+ | isJust (useLoggingHandle options)+ || buildType' == Custom = externalSetupMethod+ | cabalVersion `withinRange`+ useCabalVersion options = internalSetupMethod+ | otherwise = externalSetupMethod++type SetupMethod = Verbosity+ -> SetupScriptOptions+ -> PackageIdentifier+ -> BuildType+ -> (Version -> [String]) -> IO ()++-- ------------------------------------------------------------+-- * Internal SetupMethod+-- ------------------------------------------------------------++internalSetupMethod :: SetupMethod+internalSetupMethod verbosity options _ bt mkargs = do+ let args = mkargs cabalVersion+ debug verbosity $ "Using internal setup method with build-type " ++ show bt+ ++ " and args:\n " ++ show args+ inDir (useWorkingDir options) $+ buildTypeAction bt args++buildTypeAction :: BuildType -> ([String] -> IO ())+buildTypeAction Simple = Simple.defaultMainArgs+buildTypeAction Configure = Simple.defaultMainWithHooksArgs+ Simple.autoconfUserHooks+buildTypeAction Make = Make.defaultMainArgs+buildTypeAction Custom = error "buildTypeAction Custom"+buildTypeAction (UnknownBuildType _) = error "buildTypeAction UnknownBuildType"++-- ------------------------------------------------------------+-- * External SetupMethod+-- ------------------------------------------------------------++externalSetupMethod :: SetupMethod+externalSetupMethod verbosity options pkg bt mkargs = do+ debug verbosity $ "Using external setup method with build-type " ++ show bt+ createDirectoryIfMissingVerbose verbosity True setupDir+ (cabalLibVersion, options') <- cabalLibVersionToUse+ debug verbosity $ "Using Cabal library version " ++ display cabalLibVersion+ setupHs <- updateSetupScript cabalLibVersion bt+ debug verbosity $ "Using " ++ setupHs ++ " as setup script."+ compileSetupExecutable options' cabalLibVersion setupHs+ invokeSetupScript (mkargs cabalLibVersion)++ where+ workingDir = case fromMaybe "" (useWorkingDir options) of+ [] -> "."+ dir -> dir+ setupDir = workingDir </> useDistPref options </> "setup"+ setupVersionFile = setupDir </> "setup" <.> "version"+ setupProgFile = setupDir </> "setup" <.> exeExtension++ cabalLibVersionToUse :: IO (Version, SetupScriptOptions)+ cabalLibVersionToUse = do+ savedVersion <- savedCabalVersion+ case savedVersion of+ Just version | version `withinRange` useCabalVersion options+ -> return (version, options)+ _ -> do (comp, conf, options') <- configureCompiler options+ version <- installedCabalVersion options comp conf+ writeFile setupVersionFile (show version ++ "\n")+ return (version, options')++ savedCabalVersion = do+ versionString <- readFile setupVersionFile `catch` \_ -> return ""+ case reads versionString of+ [(version,s)] | all isSpace s -> return (Just version)+ _ -> return Nothing++ installedCabalVersion :: SetupScriptOptions -> Compiler+ -> ProgramConfiguration -> IO Version+ installedCabalVersion _ _ _ | packageName pkg == PackageName "Cabal" =+ return (packageVersion pkg)+ installedCabalVersion options' comp conf = do+ index <- case usePackageIndex options' of+ Just index -> return index+ Nothing -> getInstalledPackages verbosity+ comp (usePackageDB options') conf++ let cabalDep = Dependency (PackageName "Cabal") (useCabalVersion options)+ case PackageIndex.lookupDependency index cabalDep of+ [] -> die $ "The package requires Cabal library version "+ ++ display (useCabalVersion options)+ ++ " but no suitable version is installed."+ pkgs -> return $ bestVersion (map packageVersion pkgs)+ where+ bestVersion = maximumBy (comparing preference)+ preference version = (sameVersion, sameMajorVersion+ ,stableVersion, latestVersion)+ where+ sameVersion = version == cabalVersion+ sameMajorVersion = majorVersion version == majorVersion cabalVersion+ majorVersion = take 2 . versionBranch+ stableVersion = case versionBranch version of+ (_:x:_) -> even x+ _ -> False+ latestVersion = version++ configureCompiler :: SetupScriptOptions+ -> IO (Compiler, ProgramConfiguration, SetupScriptOptions)+ configureCompiler options' = do+ (comp, conf) <- case useCompiler options' of+ Just comp -> return (comp, useProgramConfig options')+ Nothing -> configCompiler (Just GHC) Nothing Nothing+ (useProgramConfig options') verbosity+ return (comp, conf, options' { useCompiler = Just comp,+ useProgramConfig = conf })++ -- | Decide which Setup.hs script to use, creating it if necessary.+ --+ updateSetupScript :: Version -> BuildType -> IO FilePath+ updateSetupScript _ Custom = do+ useHs <- doesFileExist setupHs+ useLhs <- doesFileExist setupLhs+ unless (useHs || useLhs) $ die+ "Using 'build-type: Custom' but there is no Setup.hs or Setup.lhs script."+ return (if useHs then setupHs else setupLhs)+ where+ setupHs = workingDir </> "Setup.hs"+ setupLhs = workingDir </> "Setup.lhs"++ updateSetupScript cabalLibVersion _ = do+ rewriteFile setupHs (buildTypeScript cabalLibVersion)+ return setupHs+ where+ setupHs = setupDir </> "setup.hs"++ buildTypeScript :: Version -> String+ buildTypeScript cabalLibVersion = case bt of+ Simple -> "import Distribution.Simple; main = defaultMain\n"+ Configure -> "import Distribution.Simple; main = defaultMainWithHooks "+ ++ if cabalLibVersion >= Version [1,3,10] []+ then "autoconfUserHooks\n"+ else "defaultUserHooks\n"+ Make -> "import Distribution.Make; main = defaultMain\n"+ Custom -> error "buildTypeScript Custom"+ UnknownBuildType _ -> error "buildTypeScript UnknownBuildType"++ -- | If the Setup.hs is out of date wrt the executable then recompile it.+ -- Currently this is GHC only. It should really be generalised.+ --+ compileSetupExecutable :: SetupScriptOptions -> Version -> FilePath -> IO ()+ compileSetupExecutable options' cabalLibVersion setupHsFile = do+ setupHsNewer <- setupHsFile `moreRecentFile` setupProgFile+ cabalVersionNewer <- setupVersionFile `moreRecentFile` setupProgFile+ let outOfDate = setupHsNewer || cabalVersionNewer+ when outOfDate $ do+ debug verbosity "Setup script is out of date, compiling..."+ (_, conf, _) <- configureCompiler options'+ --TODO: get Cabal's GHC module to export a GhcOptions type and render func+ rawSystemProgramConf verbosity ghcProgram conf $+ ghcVerbosityOptions verbosity+ ++ ["--make", setupHsFile, "-o", setupProgFile+ ,"-odir", setupDir, "-hidir", setupDir+ ,"-i", "-i" ++ workingDir ]+ ++ ghcPackageDbOptions (usePackageDB options')+ ++ if packageName pkg == PackageName "Cabal"+ then []+ else ["-package", display cabalPkgid]+ where+ cabalPkgid = PackageIdentifier (PackageName "Cabal") cabalLibVersion++ ghcPackageDbOptions :: PackageDBStack -> [String]+ ghcPackageDbOptions dbstack = case dbstack of+ (GlobalPackageDB:UserPackageDB:dbs) -> concatMap specific dbs+ (GlobalPackageDB:dbs) -> "-no-user-package-conf"+ : concatMap specific dbs+ _ -> ierror+ where+ specific (SpecificPackageDB db) = [ "-package-conf", db ]+ specific _ = ierror+ ierror = error "internal error: unexpected package db stack"+++ invokeSetupScript :: [String] -> IO ()+ invokeSetupScript args = do+ info verbosity $ unwords (setupProgFile : args)+ case useLoggingHandle options of+ Nothing -> return ()+ Just logHandle -> info verbosity $ "Redirecting build log to "+ ++ show logHandle+ currentDir <- getCurrentDirectory+ process <- runProcess (currentDir </> setupProgFile) args+ (useWorkingDir options) Nothing+ Nothing (useLoggingHandle options) (useLoggingHandle options)+ exitCode <- waitForProcess process+ unless (exitCode == ExitSuccess) $ exitWith exitCode
+ Distribution/Client/SrcDist.hs view
@@ -0,0 +1,80 @@+-- Implements the \"@.\/cabal sdist@\" command, which creates a source+-- distribution for this package. That is, packs up the source code+-- into a tarball, making use of the corresponding Cabal module.+module Distribution.Client.SrcDist (+ sdist+ ) where+import Distribution.Simple.SrcDist+ ( printPackageProblems, prepareTree+ , prepareSnapshotTree, snapshotPackage )+import Distribution.Client.Tar (createTarGzFile)++import Distribution.Package+ ( Package(..) )+import Distribution.PackageDescription+ ( PackageDescription )+import Distribution.PackageDescription.Parse+ ( readPackageDescription )+import Distribution.Simple.Utils+ ( defaultPackageDesc, warn, notice, setupMessage+ , createDirectoryIfMissingVerbose, withTempDirectory )+import Distribution.Simple.Setup (SDistFlags(..), fromFlag)+import Distribution.Verbosity (Verbosity)+import Distribution.Simple.PreProcess (knownSuffixHandlers)+import Distribution.Simple.BuildPaths ( srcPref)+import Distribution.Simple.Configure(maybeGetPersistBuildConfig)+import Distribution.PackageDescription.Configuration ( flattenPackageDescription )+import Distribution.Text+ ( display )++import System.Time (getClockTime, toCalendarTime)+import System.FilePath ((</>), (<.>))+import Control.Monad (when)+import Data.Maybe (isNothing)++-- |Create a source distribution.+sdist :: SDistFlags -> IO ()+sdist flags = do+ pkg <- return . flattenPackageDescription+ =<< readPackageDescription verbosity+ =<< defaultPackageDesc verbosity+ mb_lbi <- maybeGetPersistBuildConfig distPref+ let tmpTargetDir = srcPref distPref++ -- do some QA+ printPackageProblems verbosity pkg++ when (isNothing mb_lbi) $+ warn verbosity "Cannot run preprocessors. Run 'configure' command first."++ createDirectoryIfMissingVerbose verbosity True tmpTargetDir+ withTempDirectory verbosity tmpTargetDir "sdist." $ \tmpDir -> do++ date <- toCalendarTime =<< getClockTime+ let pkg' | snapshot = snapshotPackage date pkg+ | otherwise = pkg+ setupMessage verbosity "Building source dist for" (packageId pkg')++ _ <- if snapshot+ then prepareSnapshotTree verbosity pkg' mb_lbi distPref tmpDir pps+ else prepareTree verbosity pkg' mb_lbi distPref tmpDir pps+ targzFile <- createArchive verbosity pkg' tmpDir distPref+ notice verbosity $ "Source tarball created: " ++ targzFile++ where+ verbosity = fromFlag (sDistVerbosity flags)+ snapshot = fromFlag (sDistSnapshot flags)+ distPref = fromFlag (sDistDistPref flags)+ pps = knownSuffixHandlers++-- |Create an archive from a tree of source files, and clean up the tree.+createArchive :: Verbosity+ -> PackageDescription+ -> FilePath+ -> FilePath+ -> IO FilePath+createArchive _verbosity pkg tmpDir targetPref = do+ let tarBallName = display (packageId pkg)+ tarBallFilePath = targetPref </> tarBallName <.> "tar.gz"+ createTarGzFile tarBallFilePath tmpDir tarBallName+ return tarBallFilePath
+ Distribution/Client/Tar.hs view
@@ -0,0 +1,903 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Tar+-- Copyright : (c) 2007 Bjorn Bringert,+-- 2008 Andrea Vezzosi,+-- 2008-2009 Duncan Coutts+-- License : BSD3+--+-- Maintainer : duncan@community.haskell.org+-- Portability : portable+--+-- Reading, writing and manipulating \"@.tar@\" archive files.+--+-----------------------------------------------------------------------------+module Distribution.Client.Tar (+ -- * High level \"all in one\" operations+ createTarGzFile,+ extractTarGzFile,++ -- * Converting between internal and external representation+ read,+ write,++ -- * Packing and unpacking files to\/from internal representation+ pack,+ unpack,++ -- * Tar entry and associated types+ Entry(..),+ entryPath,+ EntryContent(..),+ Ownership(..),+ FileSize,+ Permissions,+ EpochTime,+ DevMajor,+ DevMinor,+ TypeCode,+ Format(..),++ -- * Constructing simple entry values+ simpleEntry,+ fileEntry,+ directoryEntry,++ -- * TarPath type+ TarPath,+ toTarPath,+ fromTarPath,++ -- ** Sequences of tar entries+ Entries(..),+ foldrEntries,+ foldlEntries,+ unfoldrEntries,+ mapEntries,+ filterEntries,+ entriesIndex,++ ) where++import Data.Char (ord)+import Data.Int (Int64)+import Data.Bits (Bits, shiftL, testBit)+import Data.List (foldl')+import Numeric (readOct, showOct)+import Control.Monad (MonadPlus(mplus), when)+import qualified Data.Map as Map+import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as BS.Char8+import Data.ByteString.Lazy (ByteString)+import qualified Codec.Compression.GZip as GZip+import qualified Distribution.Client.GZipUtils as GZipUtils++import System.FilePath+ ( (</>) )+import qualified System.FilePath as FilePath.Native+import qualified System.FilePath.Windows as FilePath.Windows+import qualified System.FilePath.Posix as FilePath.Posix+import System.Directory+ ( getDirectoryContents, doesDirectoryExist, getModificationTime+ , getPermissions, createDirectoryIfMissing, copyFile )+import qualified System.Directory as Permissions+ ( Permissions(executable) )+import Distribution.Compat.FilePerms+ ( setFileExecutable )+import System.Posix.Types+ ( FileMode )+import System.Time+ ( ClockTime(..) )+import System.IO+ ( IOMode(ReadMode), openBinaryFile, hFileSize )+import System.IO.Unsafe (unsafeInterleaveIO)++import Prelude hiding (read)+++--+-- * High level operations+--++createTarGzFile :: FilePath -- ^ Full Tarball path+ -> FilePath -- ^ Base directory+ -> FilePath -- ^ Directory to archive, relative to base dir+ -> IO ()+createTarGzFile tar base dir =+ BS.writeFile tar . GZip.compress . write =<< pack base [dir]++extractTarGzFile :: FilePath -- ^ Destination directory+ -> FilePath -- ^ Expected subdir (to check for tarbombs)+ -> FilePath -- ^ Tarball+ -> IO ()+extractTarGzFile dir expected tar = do+ unpack dir . checkTarbomb expected . read . GZipUtils.maybeDecompress =<< BS.readFile tar++--+-- * Entry type+--++type FileSize = Int64+-- | The number of seconds since the UNIX epoch+type EpochTime = Int64+type DevMajor = Int+type DevMinor = Int+type TypeCode = Char+type Permissions = FileMode++-- | Tar archive entry.+--+data Entry = Entry {++ -- | The path of the file or directory within the archive. This is in a+ -- tar-specific form. Use 'entryPath' to get a native 'FilePath'.+ entryTarPath :: !TarPath,++ -- | The real content of the entry. For 'NormalFile' this includes the+ -- file data. An entry usually contains a 'NormalFile' or a 'Directory'.+ entryContent :: !EntryContent,++ -- | File permissions (Unix style file mode).+ entryPermissions :: !Permissions,++ -- | The user and group to which this file belongs.+ entryOwnership :: !Ownership,++ -- | The time the file was last modified.+ entryTime :: !EpochTime,++ -- | The tar format the archive is using.+ entryFormat :: !Format+ }++-- | Native 'FilePath' of the file or directory within the archive.+--+entryPath :: Entry -> FilePath+entryPath = fromTarPath . entryTarPath++-- | The content of a tar archive entry, which depends on the type of entry.+--+-- Portable archives should contain only 'NormalFile' and 'Directory'.+--+data EntryContent = NormalFile ByteString !FileSize+ | Directory+ | SymbolicLink !LinkTarget+ | HardLink !LinkTarget+ | CharacterDevice !DevMajor !DevMinor+ | BlockDevice !DevMajor !DevMinor+ | NamedPipe+ | OtherEntryType !TypeCode ByteString !FileSize++data Ownership = Ownership {+ -- | The owner user name. Should be set to @\"\"@ if unknown.+ ownerName :: String,++ -- | The owner group name. Should be set to @\"\"@ if unknown.+ groupName :: String,++ -- | Numeric owner user id. Should be set to @0@ if unknown.+ ownerId :: !Int,++ -- | Numeric owner group id. Should be set to @0@ if unknown.+ groupId :: !Int+ }++-- | There have been a number of extensions to the tar file format over the+-- years. They all share the basic entry fields and put more meta-data in+-- different extended headers.+--+data Format =++ -- | This is the classic Unix V7 tar format. It does not support owner and+ -- group names, just numeric Ids. It also does not support device numbers.+ V7Format++ -- | The \"USTAR\" format is an extension of the classic V7 format. It was+ -- later standardised by POSIX. It has some restructions but is the most+ -- portable format.+ --+ | UstarFormat++ -- | The GNU tar implementation also extends the classic V7 format, though+ -- in a slightly different way from the USTAR format. In general for new+ -- archives the standard USTAR/POSIX should be used.+ --+ | GnuFormat+ deriving Eq++-- | @rw-r--r--@ for normal files+ordinaryFilePermissions :: Permissions+ordinaryFilePermissions = 0o0644++-- | @rwxr-xr-x@ for executable files+executableFilePermissions :: Permissions+executableFilePermissions = 0o0755++-- | @rwxr-xr-x@ for directories+directoryPermissions :: Permissions+directoryPermissions = 0o0755++isExecutable :: Permissions -> Bool+isExecutable p = testBit p 0 || testBit p 6 -- user or other exectuable++-- | An 'Entry' with all default values except for the file name and type. It+-- uses the portable USTAR/POSIX format (see 'UstarHeader').+--+-- You can use this as a basis and override specific fields, eg:+--+-- > (emptyEntry name HardLink) { linkTarget = target }+--+simpleEntry :: TarPath -> EntryContent -> Entry+simpleEntry tarpath content = Entry {+ entryTarPath = tarpath,+ entryContent = content,+ entryPermissions = case content of+ Directory -> directoryPermissions+ _ -> ordinaryFilePermissions,+ entryOwnership = Ownership "" "" 0 0,+ entryTime = 0,+ entryFormat = UstarFormat+ }++-- | A tar 'Entry' for a file.+--+-- Entry fields such as file permissions and ownership have default values.+--+-- You can use this as a basis and override specific fields. For example if you+-- need an executable file you could use:+--+-- > (fileEntry name content) { fileMode = executableFileMode }+--+fileEntry :: TarPath -> ByteString -> Entry+fileEntry name fileContent =+ simpleEntry name (NormalFile fileContent (BS.length fileContent))++-- | A tar 'Entry' for a directory.+--+-- Entry fields such as file permissions and ownership have default values.+--+directoryEntry :: TarPath -> Entry+directoryEntry name = simpleEntry name Directory++--+-- * Tar paths+--++-- | The classic tar format allowed just 100 charcters for the file name. The+-- USTAR format extended this with an extra 155 characters, however it uses a+-- complex method of splitting the name between the two sections.+--+-- Instead of just putting any overflow into the extended area, it uses the+-- extended area as a prefix. The agrevating insane bit however is that the+-- prefix (if any) must only contain a directory prefix. That is the split+-- between the two areas must be on a directory separator boundary. So there is+-- no simple calculation to work out if a file name is too long. Instead we+-- have to try to find a valid split that makes the name fit in the two areas.+--+-- The rationale presumably was to make it a bit more compatible with old tar+-- programs that only understand the classic format. A classic tar would be+-- able to extract the file name and possibly some dir prefix, but not the+-- full dir prefix. So the files would end up in the wrong place, but that's+-- probably better than ending up with the wrong names too.+--+-- So it's understandable but rather annoying.+--+-- * Tar paths use posix format (ie @\'/\'@ directory separators), irrespective+-- of the local path conventions.+--+-- * The directory separator between the prefix and name is /not/ stored.+--+data TarPath = TarPath FilePath -- path name, 100 characters max.+ FilePath -- path prefix, 155 characters max.+ deriving (Eq, Ord)++-- | Convert a 'TarPath' to a native 'FilePath'.+--+-- The native 'FilePath' will use the native directory separator but it is not+-- otherwise checked for validity or sanity. In particular:+--+-- * The tar path may be invalid as a native path, eg the filename @\"nul\"@ is+-- not valid on Windows.+--+-- * The tar path may be an absolute path or may contain @\"..\"@ components.+-- For security reasons this should not usually be allowed, but it is your+-- responsibility to check for these conditions (eg using 'checkSecurity').+--+fromTarPath :: TarPath -> FilePath+fromTarPath (TarPath name prefix) = adjustDirectory $+ FilePath.Native.joinPath $ FilePath.Posix.splitDirectories prefix+ ++ FilePath.Posix.splitDirectories name+ where+ adjustDirectory | FilePath.Posix.hasTrailingPathSeparator name+ = FilePath.Native.addTrailingPathSeparator+ | otherwise = id++-- | Convert a native 'FilePath' to a 'TarPath'.+--+-- The conversion may fail if the 'FilePath' is too long. See 'TarPath' for a+-- description of the problem with splitting long 'FilePath's.+--+toTarPath :: Bool -- ^ Is the path for a directory? This is needed because for+ -- directories a 'TarPath' must always use a trailing @\/@.+ -> FilePath -> Either String TarPath+toTarPath isDir = splitLongPath+ . addTrailingSep+ . FilePath.Posix.joinPath+ . FilePath.Native.splitDirectories+ where+ addTrailingSep | isDir = FilePath.Posix.addTrailingPathSeparator+ | otherwise = id++-- | Take a sanitized path, split on directory separators and try to pack it+-- into the 155 + 100 tar file name format.+--+-- The stragey is this: take the name-directory components in reverse order+-- and try to fit as many components into the 100 long name area as possible.+-- If all the remaining components fit in the 155 name area then we win.+--+splitLongPath :: FilePath -> Either String TarPath+splitLongPath path =+ case packName nameMax (reverse (FilePath.Posix.splitPath path)) of+ Left err -> Left err+ Right (name, []) -> Right (TarPath name "")+ Right (name, first:rest) -> case packName prefixMax remainder of+ Left err -> Left err+ Right (_ , (_:_)) -> Left "File name too long (cannot split)"+ Right (prefix, []) -> Right (TarPath name prefix)+ where+ -- drop the '/' between the name and prefix:+ remainder = init first : rest++ where+ nameMax, prefixMax :: Int+ nameMax = 100+ prefixMax = 155++ packName _ [] = Left "File name empty"+ packName maxLen (c:cs)+ | n > maxLen = Left "File name too long"+ | otherwise = Right (packName' maxLen n [c] cs)+ where n = length c++ packName' maxLen n ok (c:cs)+ | n' <= maxLen = packName' maxLen n' (c:ok) cs+ where n' = n + length c+ packName' _ _ ok cs = (FilePath.Posix.joinPath ok, cs)++-- | The tar format allows just 100 ASCII charcters for the 'SymbolicLink' and+-- 'HardLink' entry types.+--+newtype LinkTarget = LinkTarget FilePath+ deriving (Eq, Ord)++-- | Convert a tar 'LinkTarget' to a native 'FilePath'.+--+fromLinkTarget :: LinkTarget -> FilePath+fromLinkTarget (LinkTarget path) = adjustDirectory $+ FilePath.Native.joinPath $ FilePath.Posix.splitDirectories path+ where+ adjustDirectory | FilePath.Posix.hasTrailingPathSeparator path+ = FilePath.Native.addTrailingPathSeparator+ | otherwise = id++--+-- * Entries type+--++-- | A tar archive is a sequence of entries.+data Entries = Next Entry Entries+ | Done+ | Fail String++unfoldrEntries :: (a -> Either String (Maybe (Entry, a))) -> a -> Entries+unfoldrEntries f = unfold+ where+ unfold x = case f x of+ Left err -> Fail err+ Right Nothing -> Done+ Right (Just (e, x')) -> Next e (unfold x')++foldrEntries :: (Entry -> a -> a) -> a -> (String -> a) -> Entries -> a+foldrEntries next done fail' = fold+ where+ fold (Next e es) = next e (fold es)+ fold Done = done+ fold (Fail err) = fail' err++foldlEntries :: (a -> Entry -> a) -> a -> Entries -> Either String a+foldlEntries f = fold+ where+ fold a (Next e es) = (fold $! f a e) es+ fold a Done = Right a+ fold _ (Fail err) = Left err++mapEntries :: (Entry -> Entry) -> Entries -> Entries+mapEntries f = foldrEntries (Next . f) Done Fail++filterEntries :: (Entry -> Bool) -> Entries -> Entries+filterEntries p =+ foldrEntries+ (\entry rest -> if p entry+ then Next entry rest+ else rest)+ Done Fail++checkEntries :: (Entry -> Maybe String) -> Entries -> Entries+checkEntries checkEntry =+ foldrEntries+ (\entry rest -> case checkEntry entry of+ Nothing -> Next entry rest+ Just err -> Fail err)+ Done Fail++entriesIndex :: Entries -> Either String (Map.Map TarPath Entry)+entriesIndex = foldlEntries (\m e -> Map.insert (entryTarPath e) e m) Map.empty++--+-- * Checking+--++-- | This function checks a sequence of tar entries for file name security+-- problems. It checks that:+--+-- * file paths are not absolute+--+-- * file paths do not contain any path components that are \"@..@\"+--+-- * file names are valid+--+-- These checks are from the perspective of the current OS. That means we check+-- for \"@C:\blah@\" files on Windows and \"\/blah\" files on unix. For archive+-- entry types 'HardLink' and 'SymbolicLink' the same checks are done for the+-- link target. A failure in any entry terminates the sequence of entries with+-- an error.+--+checkSecurity :: Entries -> Entries+checkSecurity = checkEntries checkEntrySecurity++checkTarbomb :: FilePath -> Entries -> Entries+checkTarbomb expectedTopDir = checkEntries (checkEntryTarbomb expectedTopDir)++checkEntrySecurity :: Entry -> Maybe String+checkEntrySecurity entry = case entryContent entry of+ HardLink link -> check (entryPath entry)+ `mplus` check (fromLinkTarget link)+ SymbolicLink link -> check (entryPath entry)+ `mplus` check (fromLinkTarget link)+ _ -> check (entryPath entry)++ where+ check name+ | not (FilePath.Native.isRelative name)+ = Just $ "Absolute file name in tar archive: " ++ show name++ | not (FilePath.Native.isValid name)+ = Just $ "Invalid file name in tar archive: " ++ show name++ | any (=="..") (FilePath.Native.splitDirectories name)+ = Just $ "Invalid file name in tar archive: " ++ show name++ | otherwise = Nothing++checkEntryTarbomb :: FilePath -> Entry -> Maybe String+checkEntryTarbomb _ entry | nonFilesystemEntry = Nothing+ where+ -- Ignore some special entries we will not unpack anyway+ nonFilesystemEntry =+ case entryContent entry of+ OtherEntryType 'g' _ _ -> True --PAX global header+ OtherEntryType 'x' _ _ -> True --PAX individual header+ _ -> False++checkEntryTarbomb expectedTopDir entry =+ case FilePath.Native.splitDirectories (entryPath entry) of+ (topDir:_) | topDir == expectedTopDir -> Nothing+ _ -> Just $ "File in tar archive is not in the expected directory "+ ++ show expectedTopDir+++--+-- * Reading+--++read :: ByteString -> Entries+read = unfoldrEntries getEntry++getEntry :: ByteString -> Either String (Maybe (Entry, ByteString))+getEntry bs+ | BS.length header < 512 = Left "truncated tar archive"++ -- Tar files end with at least two blocks of all '0'. Checking this serves+ -- two purposes. It checks the format but also forces the tail of the data+ -- which is necessary to close the file if it came from a lazily read file.+ | BS.head bs == 0 = case BS.splitAt 1024 bs of+ (end, trailing)+ | BS.length end /= 1024 -> Left "short tar trailer"+ | not (BS.all (== 0) end) -> Left "bad tar trailer"+ | not (BS.all (== 0) trailing) -> Left "tar file has trailing junk"+ | otherwise -> Right Nothing++ | otherwise = partial $ do++ case (chksum_, format_) of+ (Ok chksum, _ ) | correctChecksum header chksum -> return ()+ (Ok _, Ok _) -> fail "tar checksum error"+ _ -> fail "data is not in tar format"++ -- These fields are partial, have to check them+ format <- format_; mode <- mode_;+ uid <- uid_; gid <- gid_;+ size <- size_; mtime <- mtime_;+ devmajor <- devmajor_; devminor <- devminor_;++ let content = BS.take size (BS.drop 512 bs)+ padding = (512 - size) `mod` 512+ bs' = BS.drop (512 + size + padding) bs++ entry = Entry {+ entryTarPath = TarPath name prefix,+ entryContent = case typecode of+ '\0' -> NormalFile content size+ '0' -> NormalFile content size+ '1' -> HardLink (LinkTarget linkname)+ '2' -> SymbolicLink (LinkTarget linkname)+ '3' -> CharacterDevice devmajor devminor+ '4' -> BlockDevice devmajor devminor+ '5' -> Directory+ '6' -> NamedPipe+ '7' -> NormalFile content size+ _ -> OtherEntryType typecode content size,+ entryPermissions = mode,+ entryOwnership = Ownership uname gname uid gid,+ entryTime = mtime,+ entryFormat = format+ }++ return (Just (entry, bs'))++ where+ header = BS.take 512 bs++ name = getString 0 100 header+ mode_ = getOct 100 8 header+ uid_ = getOct 108 8 header+ gid_ = getOct 116 8 header+ size_ = getOct 124 12 header+ mtime_ = getOct 136 12 header+ chksum_ = getOct 148 8 header+ typecode = getByte 156 header+ linkname = getString 157 100 header+ magic = getChars 257 8 header+ uname = getString 265 32 header+ gname = getString 297 32 header+ devmajor_ = getOct 329 8 header+ devminor_ = getOct 337 8 header+ prefix = getString 345 155 header+-- trailing = getBytes 500 12 header++ format_ = case magic of+ "\0\0\0\0\0\0\0\0" -> return V7Format+ "ustar\NUL00" -> return UstarFormat+ "ustar \NUL" -> return GnuFormat+ _ -> fail "tar entry not in a recognised format"++correctChecksum :: ByteString -> Int -> Bool+correctChecksum header checksum = checksum == checksum'+ where+ -- sum of all 512 bytes in the header block,+ -- treating each byte as an 8-bit unsigned value+ checksum' = BS.Char8.foldl' (\x y -> x + ord y) 0 header'+ -- treating the 8 bytes of chksum as blank characters.+ header' = BS.concat [BS.take 148 header,+ BS.Char8.replicate 8 ' ',+ BS.drop 156 header]++-- * TAR format primitive input++getOct :: (Integral a, Bits a) => Int64 -> Int64 -> ByteString -> Partial a+getOct off len header+ | BS.head bytes == 128 = parseBinInt (BS.unpack (BS.tail bytes))+ | null octstr = return 0+ | otherwise = case readOct octstr of+ [(x,[])] -> return x+ _ -> fail "tar header is malformed (bad numeric encoding)"+ where+ bytes = getBytes off len header+ octstr = BS.Char8.unpack+ . BS.Char8.takeWhile (\c -> c /= '\NUL' && c /= ' ')+ . BS.Char8.dropWhile (== ' ')+ $ bytes++ -- Some tar programs switch into a binary format when they try to represent+ -- field values that will not fit in the required width when using the text+ -- octal format. In particular, the UID/GID fields can only hold up to 2^21+ -- while in the binary format can hold up to 2^32. The binary format uses+ -- '\128' as the header which leaves 7 bytes. Only the last 4 are used.+ parseBinInt [0, 0, 0, byte3, byte2, byte1, byte0] =+ return $! shiftL (fromIntegral byte3) 24+ + shiftL (fromIntegral byte2) 16+ + shiftL (fromIntegral byte1) 8+ + shiftL (fromIntegral byte0) 0+ parseBinInt _ = fail "tar header uses non-standard number encoding"++getBytes :: Int64 -> Int64 -> ByteString -> ByteString+getBytes off len = BS.take len . BS.drop off++getByte :: Int64 -> ByteString -> Char+getByte off bs = BS.Char8.index bs off++getChars :: Int64 -> Int64 -> ByteString -> String+getChars off len = BS.Char8.unpack . getBytes off len++getString :: Int64 -> Int64 -> ByteString -> String+getString off len = BS.Char8.unpack . BS.Char8.takeWhile (/='\0') . getBytes off len++data Partial a = Error String | Ok a++partial :: Partial a -> Either String a+partial (Error msg) = Left msg+partial (Ok x) = Right x++instance Monad Partial where+ return = Ok+ Error m >>= _ = Error m+ Ok x >>= k = k x+ fail = Error++--+-- * Writing+--++-- | Create the external representation of a tar archive by serialising a list+-- of tar entries.+--+-- * The conversion is done lazily.+--+write :: [Entry] -> ByteString+write es = BS.concat $ map putEntry es ++ [BS.replicate (512*2) 0]++putEntry :: Entry -> ByteString+putEntry entry = case entryContent entry of+ NormalFile content size -> BS.concat [ header, content, padding size ]+ OtherEntryType _ content size -> BS.concat [ header, content, padding size ]+ _ -> header+ where+ header = putHeader entry+ padding size = BS.replicate paddingSize 0+ where paddingSize = fromIntegral (negate size `mod` 512)++putHeader :: Entry -> ByteString+putHeader entry =+ BS.Char8.pack $ take 148 block+ ++ putOct 7 checksum+ ++ ' ' : drop 156 block+ where+ block = putHeaderNoChkSum entry+ checksum = foldl' (\x y -> x + ord y) 0 block++putHeaderNoChkSum :: Entry -> String+putHeaderNoChkSum Entry {+ entryTarPath = TarPath name prefix,+ entryContent = content,+ entryPermissions = permissions,+ entryOwnership = ownership,+ entryTime = modTime,+ entryFormat = format+ } =++ concat+ [ putString 100 $ name+ , putOct 8 $ permissions+ , putOct 8 $ ownerId ownership+ , putOct 8 $ groupId ownership+ , putOct 12 $ contentSize+ , putOct 12 $ modTime+ , fill 8 $ ' ' -- dummy checksum+ , putChar8 $ typeCode+ , putString 100 $ linkTarget+ ] +++ case format of+ V7Format ->+ fill 255 '\NUL'+ UstarFormat -> concat+ [ putString 8 $ "ustar\NUL00"+ , putString 32 $ ownerName ownership+ , putString 32 $ groupName ownership+ , putOct 8 $ deviceMajor+ , putOct 8 $ deviceMinor+ , putString 155 $ prefix+ , fill 12 $ '\NUL'+ ]+ GnuFormat -> concat+ [ putString 8 $ "ustar \NUL"+ , putString 32 $ ownerName ownership+ , putString 32 $ groupName ownership+ , putGnuDev 8 $ deviceMajor+ , putGnuDev 8 $ deviceMinor+ , putString 155 $ prefix+ , fill 12 $ '\NUL'+ ]+ where+ (typeCode, contentSize, linkTarget,+ deviceMajor, deviceMinor) = case content of+ NormalFile _ size -> ('0' , size, [], 0, 0)+ Directory -> ('5' , 0, [], 0, 0)+ SymbolicLink (LinkTarget link) -> ('2' , 0, link, 0, 0)+ HardLink (LinkTarget link) -> ('1' , 0, link, 0, 0)+ CharacterDevice major minor -> ('3' , 0, [], major, minor)+ BlockDevice major minor -> ('4' , 0, [], major, minor)+ NamedPipe -> ('6' , 0, [], 0, 0)+ OtherEntryType code _ size -> (code, size, [], 0, 0)++ putGnuDev w n = case content of+ CharacterDevice _ _ -> putOct w n+ BlockDevice _ _ -> putOct w n+ _ -> replicate w '\NUL'++-- * TAR format primitive output++type FieldWidth = Int++putString :: FieldWidth -> String -> String+putString n s = take n s ++ fill (n - length s) '\NUL'++--TODO: check integer widths, eg for large file sizes+putOct :: Integral a => FieldWidth -> a -> String+putOct n x =+ let octStr = take (n-1) $ showOct x ""+ in fill (n - length octStr - 1) '0'+ ++ octStr+ ++ putChar8 '\NUL'++putChar8 :: Char -> String+putChar8 c = [c]++fill :: FieldWidth -> Char -> String+fill n c = replicate n c++--+-- * Unpacking+--++unpack :: FilePath -> Entries -> IO ()+unpack baseDir entries = unpackEntries [] (checkSecurity entries)+ >>= emulateLinks++ where+ -- We're relying here on 'checkSecurity' to make sure we're not scribbling+ -- files all over the place.++ unpackEntries _ (Fail err) = fail err+ unpackEntries links Done = return links+ unpackEntries links (Next entry es) = case entryContent entry of+ NormalFile file _ -> extractFile entry path file+ >> unpackEntries links es+ Directory -> extractDir path+ >> unpackEntries links es+ HardLink link -> (unpackEntries $! saveLink path link links) es+ SymbolicLink link -> (unpackEntries $! saveLink path link links) es+ _ -> unpackEntries links es --ignore other file types+ where+ path = entryPath entry++ extractFile entry path content = do+ -- Note that tar archives do not make sure each directory is created+ -- before files they contain, indeed we may have to create several+ -- levels of directory.+ createDirectoryIfMissing True absDir+ BS.writeFile absPath content+ when (isExecutable (entryPermissions entry))+ (setFileExecutable absPath)+ where+ absDir = baseDir </> FilePath.Native.takeDirectory path+ absPath = baseDir </> path++ extractDir path = createDirectoryIfMissing True (baseDir </> path)++ saveLink path link links = seq (length path)+ $ seq (length link')+ $ (path, link'):links+ where link' = fromLinkTarget link++ emulateLinks = mapM_ $ \(relPath, relLinkTarget) ->+ let absPath = baseDir </> relPath+ absTarget = FilePath.Native.takeDirectory absPath </> relLinkTarget+ in copyFile absTarget absPath++--+-- * Packing+--++pack :: FilePath -- ^ Base directory+ -> [FilePath] -- ^ Files and directories to pack, relative to the base dir+ -> IO [Entry]+pack baseDir paths0 = preparePaths baseDir paths0 >>= packPaths baseDir++preparePaths :: FilePath -> [FilePath] -> IO [FilePath]+preparePaths baseDir paths =+ fmap concat $ interleave+ [ do isDir <- doesDirectoryExist (baseDir </> path)+ if isDir+ then do entries <- getDirectoryContentsRecursive (baseDir </> path)+ return (FilePath.Native.addTrailingPathSeparator path+ : map (path </>) entries)+ else return [path]+ | path <- paths ]++packPaths :: FilePath -> [FilePath] -> IO [Entry]+packPaths baseDir paths =+ interleave+ [ do tarpath <- either fail return (toTarPath isDir relpath)+ if isDir then packDirectoryEntry filepath tarpath+ else packFileEntry filepath tarpath+ | relpath <- paths+ , let isDir = FilePath.Native.hasTrailingPathSeparator filepath+ filepath = baseDir </> relpath ]++interleave :: [IO a] -> IO [a]+interleave = unsafeInterleaveIO . go+ where+ go [] = return []+ go (x:xs) = do+ x' <- x+ xs' <- interleave xs+ return (x':xs')++packFileEntry :: FilePath -- ^ Full path to find the file on the local disk+ -> TarPath -- ^ Path to use for the tar Entry in the archive+ -> IO Entry+packFileEntry filepath tarpath = do+ mtime <- getModTime filepath+ perms <- getPermissions filepath+ file <- openBinaryFile filepath ReadMode+ size <- hFileSize file+ content <- BS.hGetContents file+ return (simpleEntry tarpath (NormalFile content (fromIntegral size))) {+ entryPermissions = if Permissions.executable perms+ then executableFilePermissions+ else ordinaryFilePermissions,+ entryTime = mtime+ }++packDirectoryEntry :: FilePath -- ^ Full path to find the file on the local disk+ -> TarPath -- ^ Path to use for the tar Entry in the archive+ -> IO Entry+packDirectoryEntry filepath tarpath = do+ mtime <- getModTime filepath+ return (directoryEntry tarpath) {+ entryTime = mtime+ }++getDirectoryContentsRecursive :: FilePath -> IO [FilePath]+getDirectoryContentsRecursive dir0 =+ fmap tail (recurseDirectories dir0 [""])++recurseDirectories :: FilePath -> [FilePath] -> IO [FilePath]+recurseDirectories _ [] = return []+recurseDirectories base (dir:dirs) = unsafeInterleaveIO $ do+ (files, dirs') <- collect [] [] =<< getDirectoryContents (base </> dir)++ files' <- recurseDirectories base (dirs' ++ dirs)+ return (dir : files ++ files')++ where+ collect files dirs' [] = return (reverse files, reverse dirs')+ collect files dirs' (entry:entries) | ignore entry+ = collect files dirs' entries+ collect files dirs' (entry:entries) = do+ let dirEntry = dir </> entry+ dirEntry' = FilePath.Native.addTrailingPathSeparator dirEntry+ isDirectory <- doesDirectoryExist (base </> dirEntry)+ if isDirectory+ then collect files (dirEntry':dirs') entries+ else collect (dirEntry:files) dirs' entries++ ignore ['.'] = True+ ignore ['.', '.'] = True+ ignore _ = False++getModTime :: FilePath -> IO EpochTime+getModTime path = do+ (TOD s _) <- getModificationTime path+ return $! fromIntegral s
+ Distribution/Client/Targets.hs view
@@ -0,0 +1,657 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Targets+-- Copyright : (c) Duncan Coutts 2011+-- License : BSD-like+--+-- Maintainer : duncan@community.haskell.org+--+-- Handling for user-specified targets+-----------------------------------------------------------------------------+module Distribution.Client.Targets (+ -- * User targets+ UserTarget(..),+ readUserTargets,++ -- * Package specifiers+ PackageSpecifier(..),+ pkgSpecifierTarget,+ pkgSpecifierConstraints,++ -- * Resolving user targets to package specifiers+ resolveUserTargets,++ -- ** Detailed interface+ UserTargetProblem(..),+ readUserTarget,+ reportUserTargetProblems,+ expandUserTarget,++ PackageTarget(..),+ fetchPackageTarget,+ readPackageTarget,++ PackageTargetProblem(..),+ reportPackageTargetProblems,++ disambiguatePackageTargets,+ disambiguatePackageName,++ ) where++import Distribution.Package+ ( Package(..), PackageName(..)+ , PackageIdentifier(..), packageName, packageVersion+ , Dependency(Dependency) )+import Distribution.Client.Types+ ( AvailablePackage(..), PackageLocation(..) )+import Distribution.Client.Dependency.Types+ ( PackageConstraint(..) )++import qualified Distribution.Client.World as World+import Distribution.Client.PackageIndex (PackageIndex)+import qualified Distribution.Client.PackageIndex as PackageIndex+import qualified Distribution.Client.Tar as Tar+import Distribution.Client.FetchUtils++import Distribution.PackageDescription+ ( GenericPackageDescription )+import Distribution.PackageDescription.Parse+ ( readPackageDescription, parsePackageDescription, ParseResult(..) )+import Distribution.Simple.Setup+ ( fromFlag )+import Distribution.Client.Setup+ ( GlobalFlags(..) )+import Distribution.Version+ ( Version(Version), thisVersion, anyVersion, isAnyVersion )+import Distribution.Text+ ( Text(parse), display )+import Distribution.Verbosity (Verbosity)+import Distribution.Simple.Utils+ ( die, warn, intercalate, findPackageDesc, fromUTF8, lowercase )++import Data.List+ ( find, nub )+import Data.Maybe+ ( listToMaybe )+import Data.Either+ ( partitionEithers )+import Data.Monoid+ ( Monoid(..) )+import qualified Data.Map as Map+import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as BS.Char8+import qualified Distribution.Client.GZipUtils as GZipUtils+import Control.Monad (liftM)+import qualified Distribution.Compat.ReadP as Parse+ ( ReadP, readP_to_S, (+++) )+import Data.Char+ ( isSpace )+import System.FilePath+ ( takeExtension, dropExtension, takeDirectory, splitPath )+import System.Directory+ ( doesFileExist, doesDirectoryExist )+import Network.URI+ ( URI(..), URIAuth(..), parseAbsoluteURI )++-- ------------------------------------------------------------+-- * User targets+-- ------------------------------------------------------------++-- | Various ways that a user may specify a package or package collection.+--+data UserTarget =++ -- | A partially specified package, identified by name and possibly with+ -- an exact version or a version constraint.+ --+ -- > cabal install foo+ -- > cabal install foo-1.0+ -- > cabal install 'foo < 2'+ --+ UserTargetNamed Dependency++ -- | A special virtual package that refers to the collection of packages+ -- recorded in the world file that the user specifically installed.+ --+ -- > cabal install world+ --+ | UserTargetWorld++ -- | A specific package that is unpacked in a local directory, often the+ -- current directory.+ --+ -- > cabal install .+ -- > cabal install ../lib/other+ --+ -- * Note: in future, if multiple @.cabal@ files are allowed in a single+ -- directory then this will refer to the collection of packages.+ --+ | UserTargetLocalDir FilePath++ -- | A specific local unpacked package, identified by its @.cabal@ file.+ --+ -- > cabal install foo.cabal+ -- > cabal install ../lib/other/bar.cabal+ --+ | UserTargetLocalCabalFile FilePath++ -- | A specific package that is available as a local tarball file+ --+ -- > cabal install dist/foo-1.0.tar.gz+ -- > cabal install ../build/baz-1.0.tar.gz+ --+ | UserTargetLocalTarball FilePath++ -- | A specific package that is available as a remote tarball file+ --+ -- > cabal install http://code.haskell.org/~user/foo/foo-0.9.tar.gz+ --+ | UserTargetRemoteTarball URI+ deriving (Show,Eq)+++-- ------------------------------------------------------------+-- * Package specifier+-- ------------------------------------------------------------++-- | A fully or partially resolved reference to a package.+--+data PackageSpecifier pkg =++ -- | A partially specified reference to a package (either source or+ -- installed). It is specified by package name and optionally some+ -- additional constraints. Use a dependency resolver to pick a specific+ -- package satisfying these constraints.+ --+ NamedPackage PackageName [PackageConstraint]++ -- | A fully specified source package.+ --+ | SpecificSourcePackage pkg+ deriving Show+++pkgSpecifierTarget :: Package pkg => PackageSpecifier pkg -> PackageName+pkgSpecifierTarget (NamedPackage name _) = name+pkgSpecifierTarget (SpecificSourcePackage pkg) = packageName pkg++pkgSpecifierConstraints :: Package pkg+ => PackageSpecifier pkg -> [PackageConstraint]+pkgSpecifierConstraints (NamedPackage _ constraints) = constraints+pkgSpecifierConstraints (SpecificSourcePackage pkg) =+ [PackageVersionConstraint (packageName pkg)+ (thisVersion (packageVersion pkg))]+++-- ------------------------------------------------------------+-- * Parsing and checking user targets+-- ------------------------------------------------------------++readUserTargets :: Verbosity -> [String] -> IO [UserTarget]+readUserTargets _verbosity targetStrs = do+ (problems, targets) <- liftM partitionEithers+ (mapM readUserTarget targetStrs)+ reportUserTargetProblems problems+ return targets+++data UserTargetProblem+ = UserTargetUnexpectedFile String+ | UserTargetNonexistantFile String+ | UserTargetUnexpectedUriScheme String+ | UserTargetUnrecognisedUri String+ | UserTargetUnrecognised String+ | UserTargetBadWorldPkg+ deriving Show++readUserTarget :: String -> IO (Either UserTargetProblem UserTarget)+readUserTarget targetstr =+ case testNamedTargets targetstr of+ Just (Dependency (PackageName "world") verrange)+ | verrange == anyVersion -> return (Right UserTargetWorld)+ | otherwise -> return (Left UserTargetBadWorldPkg)+ Just dep -> return (Right (UserTargetNamed dep))+ Nothing -> do+ fileTarget <- testFileTargets targetstr+ case fileTarget of+ Just target -> return target+ Nothing ->+ case testUriTargets targetstr of+ Just target -> return target+ Nothing -> return (Left (UserTargetUnrecognised targetstr))+ where+ testNamedTargets = readPToMaybe parseDependencyOrPackageId++ testFileTargets filename = do+ isDir <- doesDirectoryExist filename+ isFile <- doesFileExist filename+ parentDirExists <- case takeDirectory filename of+ [] -> return False+ dir -> doesDirectoryExist dir+ let result+ | isDir+ = Just (Right (UserTargetLocalDir filename))++ | isFile && extensionIsTarGz filename+ = Just (Right (UserTargetLocalTarball filename))++ | isFile && takeExtension filename == ".cabal"+ = Just (Right (UserTargetLocalCabalFile filename))++ | isFile+ = Just (Left (UserTargetUnexpectedFile filename))++ | parentDirExists+ = Just (Left (UserTargetNonexistantFile filename))++ | otherwise+ = Nothing+ return result++ testUriTargets str =+ case parseAbsoluteURI str of+ Just uri@URI {+ uriScheme = scheme,+ uriAuthority = Just URIAuth { uriRegName = host }+ }+ | scheme /= "http:" ->+ Just (Left (UserTargetUnexpectedUriScheme targetstr))++ | null host ->+ Just (Left (UserTargetUnrecognisedUri targetstr))++ | otherwise ->+ Just (Right (UserTargetRemoteTarball uri))+ _ -> Nothing++ extensionIsTarGz f = takeExtension f == ".gz"+ && takeExtension (dropExtension f) == ".tar"++ parseDependencyOrPackageId :: Parse.ReadP r Dependency+ parseDependencyOrPackageId = parse Parse.+++ liftM pkgidToDependency parse+ where+ pkgidToDependency :: PackageIdentifier -> Dependency+ pkgidToDependency p = case packageVersion p of+ Version [] _ -> Dependency (packageName p) anyVersion+ version -> Dependency (packageName p) (thisVersion version)++ readPToMaybe :: Parse.ReadP a a -> String -> Maybe a+ readPToMaybe p str = listToMaybe [ r | (r,s) <- Parse.readP_to_S p str+ , all isSpace s ]+++reportUserTargetProblems :: [UserTargetProblem] -> IO ()+reportUserTargetProblems problems = do+ case [ target | UserTargetUnrecognised target <- problems ] of+ [] -> return ()+ target -> die+ $ unlines+ [ "Unrecognised target '" ++ name ++ "'."+ | name <- target ]+ ++ "Targets can be:\n"+ ++ " - package names, e.g. 'pkgname', 'pkgname-1.0.1', 'pkgname < 2.0'\n"+ ++ " - the special 'world' target\n"+ ++ " - cabal files 'pkgname.cabal' or package directories 'pkgname/'\n"+ ++ " - package tarballs 'pkgname.tar.gz' or 'http://example.com/pkgname.tar.gz'"++ case [ () | UserTargetBadWorldPkg <- problems ] of+ [] -> return ()+ _ -> die "The special 'world' target does not take any version."++ case [ target | UserTargetNonexistantFile target <- problems ] of+ [] -> return ()+ target -> die+ $ unlines+ [ "The file does not exist '" ++ name ++ "'."+ | name <- target ]++ case [ target | UserTargetUnexpectedFile target <- problems ] of+ [] -> return ()+ target -> die+ $ unlines+ [ "Unrecognised file target '" ++ name ++ "'."+ | name <- target ]+ ++ "File targets can be either package tarballs 'pkgname.tar.gz' "+ ++ "or cabal files 'pkgname.cabal'."++ case [ target | UserTargetUnexpectedUriScheme target <- problems ] of+ [] -> return ()+ target -> die+ $ unlines+ [ "URL target not supported '" ++ name ++ "'."+ | name <- target ]+ ++ "Only 'http://' URLs are supported."++ case [ target | UserTargetUnrecognisedUri target <- problems ] of+ [] -> return ()+ target -> die+ $ unlines+ [ "Unrecognise URL target '" ++ name ++ "'."+ | name <- target ]+++-- ------------------------------------------------------------+-- * Resolving user targets to package specifiers+-- ------------------------------------------------------------++-- | Given a bunch of user-specified targets, try to resolve what it is they+-- refer to. They can either be specific packages (local dirs, tarballs etc)+-- or they can be named packages (with or without version info).+--+resolveUserTargets :: Package pkg+ => Verbosity+ -> GlobalFlags+ -> PackageIndex pkg+ -> [UserTarget]+ -> IO [PackageSpecifier AvailablePackage]+resolveUserTargets verbosity globalFlags available userTargets = do++ -- given the user targets, get a list of fully or partially resolved+ -- package references+ packageTargets <- mapM (readPackageTarget verbosity)+ =<< mapM (fetchPackageTarget verbosity) . concat+ =<< mapM (expandUserTarget globalFlags) userTargets++ -- users are allowed to give package names case-insensitively, so we must+ -- disambiguate named package references+ let (problems, packageSpecifiers) =+ disambiguatePackageTargets available availableExtra packageTargets++ -- use any extra specific available packages to help us disambiguate+ availableExtra = [ packageName pkg+ | PackageTargetLocation pkg <- packageTargets ]++ reportPackageTargetProblems verbosity problems++ return packageSpecifiers+++-- ------------------------------------------------------------+-- * Package targets+-- ------------------------------------------------------------++-- | An intermediate between a 'UserTarget' and a resolved 'PackageSpecifier'.+-- Unlike a 'UserTarget', a 'PackageTarget' refers only to a single package.+--+data PackageTarget pkg =+ PackageTargetNamed PackageName [PackageConstraint] UserTarget++ -- | A package identified by name, but case insensitively, so it needs+ -- to be resolved to the right case-sensitive name.+ | PackageTargetNamedFuzzy PackageName [PackageConstraint] UserTarget+ | PackageTargetLocation pkg+ deriving Show+++-- ------------------------------------------------------------+-- * Converting user targets to package targets+-- ------------------------------------------------------------++-- | Given a user-specified target, expand it to a bunch of package targets+-- (each of which refers to only one package).+--+expandUserTarget :: GlobalFlags+ -> UserTarget+ -> IO [PackageTarget (PackageLocation ())]+expandUserTarget globalFlags userTarget = case userTarget of++ UserTargetNamed (Dependency name vrange) ->+ let constraints = [ PackageVersionConstraint name vrange+ | not (isAnyVersion vrange) ]+ in return [PackageTargetNamedFuzzy name constraints userTarget]++ UserTargetWorld -> do+ worldPkgs <- World.getContents worldFile+ --TODO: should we warn if there are no world targets?+ return [ PackageTargetNamed name constraints userTarget+ | World.WorldPkgInfo (Dependency name vrange) flags <- worldPkgs+ , let constraints = [ PackageVersionConstraint name vrange+ | not (isAnyVersion vrange) ]+ ++ [ PackageFlagsConstraint name flags+ | not (null flags) ] ]++ UserTargetLocalDir dir ->+ return [ PackageTargetLocation (LocalUnpackedPackage dir) ]++ UserTargetLocalCabalFile file -> do+ let dir = takeDirectory file+ _ <- findPackageDesc dir -- just as a check+ return [ PackageTargetLocation (LocalUnpackedPackage dir) ]++ UserTargetLocalTarball tarballFile ->+ return [ PackageTargetLocation (LocalTarballPackage tarballFile) ]++ UserTargetRemoteTarball tarballURL ->+ return [ PackageTargetLocation (RemoteTarballPackage tarballURL ()) ]+ where+ worldFile = fromFlag $ globalWorldFile globalFlags+++-- ------------------------------------------------------------+-- * Fetching and reading package targets+-- ------------------------------------------------------------+++-- | Fetch any remote targets so that they can be read.+--+fetchPackageTarget :: Verbosity+ -> PackageTarget (PackageLocation ())+ -> IO (PackageTarget (PackageLocation FilePath))+fetchPackageTarget verbosity target = case target of+ PackageTargetNamed n cs ut -> return (PackageTargetNamed n cs ut)+ PackageTargetNamedFuzzy n cs ut -> return (PackageTargetNamedFuzzy n cs ut)+ PackageTargetLocation location -> do+ location' <- fetchPackage verbosity (fmap (const Nothing) location)+ return (PackageTargetLocation location')+++-- | Given a package target that has been fetched, read the .cabal file.+--+-- This only affects targets given by location, named targets are unaffected.+--+readPackageTarget :: Verbosity+ -> PackageTarget (PackageLocation FilePath)+ -> IO (PackageTarget AvailablePackage)+readPackageTarget verbosity target = case target of++ PackageTargetNamed pkgname constraints userTarget ->+ return (PackageTargetNamed pkgname constraints userTarget)++ PackageTargetNamedFuzzy pkgname constraints userTarget ->+ return (PackageTargetNamedFuzzy pkgname constraints userTarget)++ PackageTargetLocation location -> case location of++ LocalUnpackedPackage dir -> do+ pkg <- readPackageDescription verbosity =<< findPackageDesc dir+ return $ PackageTargetLocation $+ AvailablePackage {+ packageInfoId = packageId pkg,+ packageDescription = pkg,+ packageSource = fmap Just location+ }++ LocalTarballPackage tarballFile ->+ readTarballPackageTarget location tarballFile tarballFile++ RemoteTarballPackage tarballURL tarballFile ->+ readTarballPackageTarget location tarballFile (show tarballURL)++ RepoTarballPackage _repo _pkgid _ ->+ error "TODO: readPackageTarget RepoTarballPackage"+ -- For repo tarballs this info should be obtained from the index.++ where+ readTarballPackageTarget location tarballFile tarballOriginalLoc = do+ (filename, content) <- extractTarballPackageCabalFile+ tarballFile tarballOriginalLoc+ case parsePackageDescription' content of+ Nothing -> die $ "Could not parse the cabal file "+ ++ filename ++ " in " ++ tarballFile+ Just pkg ->+ return $ PackageTargetLocation $+ AvailablePackage {+ packageInfoId = packageId pkg,+ packageDescription = pkg,+ packageSource = fmap Just location+ }++ extractTarballPackageCabalFile :: FilePath -> String+ -> IO (FilePath, BS.ByteString)+ extractTarballPackageCabalFile tarballFile tarballOriginalLoc =+ either (die . formatErr) return+ . check+ . Tar.entriesIndex+ . Tar.filterEntries isCabalFile+ . Tar.read+ . GZipUtils.maybeDecompress+ =<< BS.readFile tarballFile+ where+ formatErr msg = "Error reading " ++ tarballOriginalLoc ++ ": " ++ msg++ check (Left e) = Left e+ check (Right m) = case Map.elems m of+ [] -> Left noCabalFile+ [file] -> case Tar.entryContent file of+ Tar.NormalFile content _ -> Right (Tar.entryPath file, content)+ _ -> Left noCabalFile+ _files -> Left multipleCabalFiles+ where+ noCabalFile = "No cabal file found"+ multipleCabalFiles = "Multiple cabal files found"++ isCabalFile e = case splitPath (Tar.entryPath e) of+ [ _dir, file] -> takeExtension file == ".cabal"+ [".", _dir, file] -> takeExtension file == ".cabal"+ _ -> False++ parsePackageDescription' :: BS.ByteString -> Maybe GenericPackageDescription+ parsePackageDescription' content =+ case parsePackageDescription . fromUTF8 . BS.Char8.unpack $ content of+ ParseOk _ pkg -> Just pkg+ _ -> Nothing+++-- ------------------------------------------------------------+-- * Checking package targets+-- ------------------------------------------------------------++data PackageTargetProblem+ = PackageNameUnknown PackageName UserTarget+ | PackageNameAmbigious PackageName [PackageName] UserTarget+ deriving Show+++-- | Users are allowed to give package names case-insensitively, so we must+-- disambiguate named package references.+--+disambiguatePackageTargets :: Package pkg'+ => PackageIndex pkg'+ -> [PackageName]+ -> [PackageTarget pkg]+ -> ( [PackageTargetProblem]+ , [PackageSpecifier pkg] )+disambiguatePackageTargets available availableExtra targets =+ partitionEithers (map disambiguatePackageTarget targets)+ where+ disambiguatePackageTarget packageTarget = case packageTarget of+ PackageTargetLocation pkg -> Right (SpecificSourcePackage pkg)++ PackageTargetNamed pkgname constraints userTarget+ | null (PackageIndex.lookupPackageName available pkgname)+ -> Left (PackageNameUnknown pkgname userTarget)+ | otherwise -> Right (NamedPackage pkgname constraints)++ PackageTargetNamedFuzzy pkgname constraints userTarget ->+ case disambiguatePackageName packageNameEnv pkgname of+ None -> Left (PackageNameUnknown+ pkgname userTarget)+ Ambiguous pkgnames -> Left (PackageNameAmbigious+ pkgname pkgnames userTarget)+ Unambiguous pkgname' -> Right (NamedPackage pkgname' constraints)++ -- use any extra specific available packages to help us disambiguate+ packageNameEnv :: PackageNameEnv+ packageNameEnv = mappend (indexPackageNameEnv available)+ (extraPackageNameEnv availableExtra)+++-- | Report problems to the user. That is, if there are any problems+-- then raise an exception.+reportPackageTargetProblems :: Verbosity+ -> [PackageTargetProblem] -> IO ()+reportPackageTargetProblems verbosity problems = do+ case [ pkg | PackageNameUnknown pkg originalTarget <- problems+ , not (isUserTagetWorld originalTarget) ] of+ [] -> return ()+ pkgs -> die $ unlines+ [ "There is no package named '" ++ display name ++ "'. "+ | name <- pkgs ]+ ++ "You may need to run 'cabal update' to get the latest "+ ++ "list of available packages."++ case [ (pkg, matches) | PackageNameAmbigious pkg matches _ <- problems ] of+ [] -> return ()+ ambiguities -> die $ unlines+ [ "The package name '" ++ display name+ ++ "' is ambigious. It could be: "+ ++ intercalate ", " (map display matches)+ | (name, matches) <- ambiguities ]++ case [ pkg | PackageNameUnknown pkg UserTargetWorld <- problems ] of+ [] -> return ()+ pkgs -> warn verbosity $+ "The following 'world' packages will be ignored because "+ ++ "they refer to packages that cannot be found: "+ ++ intercalate ", " (map display pkgs) ++ "\n"+ ++ "You can surpress this warning by correcting the world file."+ where+ isUserTagetWorld UserTargetWorld = True; isUserTagetWorld _ = False+++-- ------------------------------------------------------------+-- * Disambiguating package names+-- ------------------------------------------------------------++data MaybeAmbigious a = None | Unambiguous a | Ambiguous [a]++-- | Given a package name and a list of matching names, figure out which one it+-- might be referring to. If there is an exact case-sensitive match then that's+-- ok. If it matches just one package case-insensitively then that's also ok.+-- The only problem is if it matches multiple packages case-insensitively, in+-- that case it is ambigious.+--+disambiguatePackageName :: PackageNameEnv+ -> PackageName+ -> MaybeAmbigious PackageName+disambiguatePackageName (PackageNameEnv pkgNameLookup) name =+ case nub (pkgNameLookup name) of+ [] -> None+ [name'] -> Unambiguous name'+ names -> case find (name==) names of+ Just name' -> Unambiguous name'+ Nothing -> Ambiguous names+++newtype PackageNameEnv = PackageNameEnv (PackageName -> [PackageName])++instance Monoid PackageNameEnv where+ mempty = PackageNameEnv (const [])+ mappend (PackageNameEnv lookupA) (PackageNameEnv lookupB) =+ PackageNameEnv (\name -> lookupA name ++ lookupB name)++indexPackageNameEnv :: Package pkg => PackageIndex pkg -> PackageNameEnv+indexPackageNameEnv index = PackageNameEnv pkgNameLookup+ where+ pkgNameLookup (PackageName name) =+ map fst (PackageIndex.searchByName index name)++extraPackageNameEnv :: [PackageName] -> PackageNameEnv+extraPackageNameEnv names = PackageNameEnv pkgNameLookup+ where+ pkgNameLookup (PackageName name) =+ [ PackageName name'+ | let lname = lowercase name+ , PackageName name' <- names+ , lowercase name' == lname ]
+ Distribution/Client/Types.hs view
@@ -0,0 +1,163 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Types+-- Copyright : (c) David Himmelstrup 2005+-- Duncan Coutts 2011+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Various common data types for the entire cabal-install system+-----------------------------------------------------------------------------+module Distribution.Client.Types where++import Distribution.Package+ ( PackageName, PackageId, Package(..), PackageFixedDeps(..) )+import Distribution.InstalledPackageInfo+ ( InstalledPackageInfo )+import Distribution.PackageDescription+ ( GenericPackageDescription, FlagAssignment )+import Distribution.Client.PackageIndex+ ( PackageIndex )+import Distribution.Version+ ( VersionRange )++import Data.Map (Map)+import Network.URI (URI)+import Distribution.Compat.Exception+ ( SomeException )++newtype Username = Username { unUsername :: String }+newtype Password = Password { unPassword :: String }++-- | This is the information we get from a @00-index.tar.gz@ hackage index.+--+data AvailablePackageDb = AvailablePackageDb {+ packageIndex :: PackageIndex AvailablePackage,+ packagePreferences :: Map PackageName VersionRange+}++-- ------------------------------------------------------------+-- * Various kinds of information about packages+-- ------------------------------------------------------------++-- | TODO: This is a hack to help us transition from Cabal-1.6 to 1.8.+-- What is new in 1.8 is that installed packages and dependencies between+-- installed packages are now identified by an opaque InstalledPackageId+-- rather than a source PackageId.+--+-- We should use simply an 'InstalledPackageInfo' here but to ease the+-- transition we are temporarily using this variant where we pretend that+-- installed packages still specify their deps in terms of PackageIds.+--+-- Crucially this means that 'InstalledPackage' can be an instance of+-- 'PackageFixedDeps' where as 'InstalledPackageInfo' is no longer an instance+-- of that class. This means we can make 'PackageIndex'es of InstalledPackage+-- where as the InstalledPackageInfo now has its own monomorphic index type.+--+data InstalledPackage = InstalledPackage+ InstalledPackageInfo+ [PackageId]++instance Package InstalledPackage where+ packageId (InstalledPackage pkg _) = packageId pkg+instance PackageFixedDeps InstalledPackage where+ depends (InstalledPackage _ deps) = deps++-- | A 'ConfiguredPackage' is a not-yet-installed package along with the+-- total configuration information. The configuration information is total in+-- the sense that it provides all the configuration information and so the+-- final configure process will be independent of the environment.+--+data ConfiguredPackage = ConfiguredPackage+ AvailablePackage -- package info, including repo+ FlagAssignment -- complete flag assignment for the package+ [PackageId] -- set of exact dependencies. These must be+ -- consistent with the 'buildDepends' in the+ -- 'PackageDescrption' that you'd get by applying+ -- the flag assignment.+ deriving Show++instance Package ConfiguredPackage where+ packageId (ConfiguredPackage pkg _ _) = packageId pkg++instance PackageFixedDeps ConfiguredPackage where+ depends (ConfiguredPackage _ _ deps) = deps+++-- | We re-use @GenericPackageDescription@ and use the @package-url@+-- field to store the tarball URI.+data AvailablePackage = AvailablePackage {+ packageInfoId :: PackageId,+ packageDescription :: GenericPackageDescription,+ packageSource :: PackageLocation (Maybe FilePath)+ }+ deriving Show++instance Package AvailablePackage where packageId = packageInfoId++-- ------------------------------------------------------------+-- * Package locations and repositories+-- ------------------------------------------------------------++data PackageLocation local =++ -- | An unpacked package in the given dir, or current dir+ LocalUnpackedPackage FilePath++ -- | A package as a tarball that's available as a local tarball+ | LocalTarballPackage FilePath++ -- | A package as a tarball from a remote URI+ | RemoteTarballPackage URI local++ -- | A package available as a tarball from a repository.+ --+ -- It may be from a local repository or from a remote repository, with a+ -- locally cached copy. ie a package available from hackage+ | RepoTarballPackage Repo PackageId local++--TODO:+-- * add support for darcs and other SCM style remote repos with a local cache+-- | ScmPackage+ deriving Show++instance Functor PackageLocation where+ fmap _ (LocalUnpackedPackage dir) = LocalUnpackedPackage dir+ fmap _ (LocalTarballPackage file) = LocalTarballPackage file+ fmap f (RemoteTarballPackage uri x) = RemoteTarballPackage uri (f x)+ fmap f (RepoTarballPackage repo pkg x) = RepoTarballPackage repo pkg (f x)+++data LocalRepo = LocalRepo+ deriving (Show,Eq)++data RemoteRepo = RemoteRepo {+ remoteRepoName :: String,+ remoteRepoURI :: URI+ }+ deriving (Show,Eq)++data Repo = Repo {+ repoKind :: Either RemoteRepo LocalRepo,+ repoLocalDir :: FilePath+ }+ deriving (Show,Eq)++-- ------------------------------------------------------------+-- * Build results+-- ------------------------------------------------------------++type BuildResult = Either BuildFailure BuildSuccess+data BuildFailure = DependentFailed PackageId+ | DownloadFailed SomeException+ | UnpackFailed SomeException+ | ConfigureFailed SomeException+ | BuildFailed SomeException+ | InstallFailed SomeException+data BuildSuccess = BuildOk DocsResult TestsResult++data DocsResult = DocsNotTried | DocsFailed | DocsOk+data TestsResult = TestsNotTried | TestsFailed | TestsOk
+ Distribution/Client/Unpack.hs view
@@ -0,0 +1,121 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Unpack+-- Copyright : (c) Andrea Vezzosi 2008+-- Duncan Coutts 2011+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+--+-----------------------------------------------------------------------------+module Distribution.Client.Unpack (++ -- * Commands+ unpack,++ ) where++import Distribution.Package+ ( PackageId, packageId )+import Distribution.Simple.Setup+ ( fromFlagOrDefault )+import Distribution.Simple.Utils+ ( notice, die )+import Distribution.Verbosity+ ( Verbosity )+import Distribution.Text(display)++import Distribution.Client.Setup+ ( GlobalFlags(..), UnpackFlags(..) )+import Distribution.Client.Types+import Distribution.Client.Targets+import Distribution.Client.Dependency+import Distribution.Client.FetchUtils+import qualified Distribution.Client.Tar as Tar (extractTarGzFile)+import Distribution.Client.IndexUtils as IndexUtils+ ( getAvailablePackages )++import System.Directory+ ( createDirectoryIfMissing, doesDirectoryExist, doesFileExist )+import Control.Monad+ ( unless, when )+import Data.Monoid+ ( mempty )+import System.FilePath+ ( (</>), addTrailingPathSeparator )+++unpack :: Verbosity+ -> [Repo]+ -> GlobalFlags+ -> UnpackFlags+ -> [UserTarget] + -> IO ()+unpack verbosity _ _ _ [] =+ notice verbosity "No packages requested. Nothing to do."++unpack verbosity repos globalFlags unpackFlags userTargets = do+ mapM_ checkTarget userTargets++ availableDb <- getAvailablePackages verbosity repos++ pkgSpecifiers <- resolveUserTargets verbosity+ globalFlags (packageIndex availableDb) userTargets++ pkgs <- either (die . unlines . map show) return $+ resolveWithoutDependencies+ (resolverParams availableDb pkgSpecifiers)++ unless (null prefix) $+ createDirectoryIfMissing True prefix++ flip mapM_ pkgs $ \pkg -> do+ location <- fetchPackage verbosity (packageSource pkg)+ let pkgid = packageId pkg+ case location of+ LocalTarballPackage tarballPath ->+ unpackPackage verbosity prefix pkgid tarballPath++ RemoteTarballPackage _tarballURL tarballPath ->+ unpackPackage verbosity prefix pkgid tarballPath++ RepoTarballPackage _repo _pkgid tarballPath ->+ unpackPackage verbosity prefix pkgid tarballPath++ LocalUnpackedPackage _ ->+ error "Distribution.Client.Unpack.unpack: the impossible happened."++ where+ resolverParams availableDb pkgSpecifiers =+ --TODO: add commandline constraint and preference args for unpack++ standardInstallPolicy mempty availableDb pkgSpecifiers++ prefix = fromFlagOrDefault "" (unpackDestDir unpackFlags)++checkTarget :: UserTarget -> IO ()+checkTarget target = case target of+ UserTargetLocalDir dir -> die (notTarball dir)+ UserTargetLocalCabalFile file -> die (notTarball file)+ _ -> return ()+ where+ notTarball t =+ "The 'unpack' command is for tarball packages. "+ ++ "The target '" ++ t ++ "' is not a tarball."++unpackPackage :: Verbosity -> FilePath -> PackageId -> FilePath -> IO ()+unpackPackage verbosity prefix pkgid pkgPath = do+ let pkgdirname = display pkgid+ pkgdir = prefix </> pkgdirname+ pkgdir' = addTrailingPathSeparator pkgdir+ existsDir <- doesDirectoryExist pkgdir+ when existsDir $ die $+ "The directory \"" ++ pkgdir' ++ "\" already exists, not unpacking."+ existsFile <- doesFileExist pkgdir+ when existsFile $ die $+ "A file \"" ++ pkgdir ++ "\" is in the way, not unpacking."+ notice verbosity $ "Unpacking to " ++ pkgdir'+ Tar.extractTarGzFile prefix pkgdirname pkgPath
+ Distribution/Client/Update.hs view
@@ -0,0 +1,81 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Update+-- Copyright : (c) David Himmelstrup 2005+-- License : BSD-like+--+-- Maintainer : lemmih@gmail.com+-- Stability : provisional+-- Portability : portable+--+--+-----------------------------------------------------------------------------+module Distribution.Client.Update+ ( update+ ) where++import Distribution.Client.Types+ ( Repo(..), RemoteRepo(..), LocalRepo(..), AvailablePackageDb(..) )+import Distribution.Client.FetchUtils+ ( downloadIndex )+import qualified Distribution.Client.PackageIndex as PackageIndex+import Distribution.Client.IndexUtils+ ( getAvailablePackages )+import qualified Paths_cabal_install_bundle+ ( version )++import Distribution.Package+ ( PackageName(..), packageVersion )+import Distribution.Version+ ( anyVersion, withinRange )+import Distribution.Simple.Utils+ ( warn, notice, writeFileAtomic )+import Distribution.Verbosity+ ( Verbosity )++import qualified Data.ByteString.Lazy as BS+import qualified Data.ByteString.Lazy.Char8 as BS.Char8+import Distribution.Client.GZipUtils (maybeDecompress)+import qualified Data.Map as Map+import System.FilePath (dropExtension)+import Data.Maybe (fromMaybe)+import Control.Monad (when)++-- | 'update' downloads the package list from all known servers+update :: Verbosity -> [Repo] -> IO ()+update verbosity [] = do+ warn verbosity $ "No remote package servers have been specified. Usually "+ ++ "you would have one specified in the config file."+update verbosity repos = do+ mapM_ (updateRepo verbosity) repos+ checkForSelfUpgrade verbosity repos++updateRepo :: Verbosity -> Repo -> IO ()+updateRepo verbosity repo = case repoKind repo of+ Right LocalRepo -> return ()+ Left remoteRepo -> do+ notice verbosity $ "Downloading the latest package list from "+ ++ remoteRepoName remoteRepo+ indexPath <- downloadIndex verbosity remoteRepo (repoLocalDir repo)+ writeFileAtomic (dropExtension indexPath) . BS.Char8.unpack+ . maybeDecompress+ =<< BS.readFile indexPath++checkForSelfUpgrade :: Verbosity -> [Repo] -> IO ()+checkForSelfUpgrade verbosity repos = do+ AvailablePackageDb available prefs <- getAvailablePackages verbosity repos++ let self = PackageName "cabal-install"+ preferredVersionRange = fromMaybe anyVersion (Map.lookup self prefs)+ currentVersion = Paths_cabal_install_bundle.version+ laterPreferredVersions =+ [ packageVersion pkg+ | pkg <- PackageIndex.lookupPackageName available self+ , let version = packageVersion pkg+ , version > currentVersion+ , version `withinRange` preferredVersionRange ]++ when (not (null laterPreferredVersions)) $+ notice verbosity $+ "Note: there is a new version of cabal-install available.\n"+ ++ "To upgrade, run: cabal install cabal-install"
+ Distribution/Client/Upload.hs view
@@ -0,0 +1,190 @@+-- This is a quick hack for uploading packages to Hackage.+-- See http://hackage.haskell.org/trac/hackage/wiki/CabalUpload++module Distribution.Client.Upload (check, upload, report) where++import Distribution.Client.Types (Username(..), Password(..),Repo(..),RemoteRepo(..))+import Distribution.Client.HttpUtils (proxy, isOldHackageURI)++import Distribution.Simple.Utils (debug, notice, warn, info)+import Distribution.Verbosity (Verbosity)+import Distribution.Text (display)+import Distribution.Client.Config++import qualified Distribution.Client.BuildReports.Anonymous as BuildReport+import qualified Distribution.Client.BuildReports.Upload as BuildReport++import Network.Browser+ ( BrowserAction, browse, request+ , Authority(..), addAuthority, setAuthorityGen+ , setOutHandler, setErrHandler, setProxy )+import Network.HTTP+ ( Header(..), HeaderName(..), findHeader+ , Request(..), RequestMethod(..), Response(..) )+import Network.TCP (HandleStream)+import Network.URI (URI(uriPath), parseURI)++import Data.Char (intToDigit)+import Numeric (showHex)+import System.IO (hFlush, stdin, stdout, hGetEcho, hSetEcho+ ,openBinaryFile, IOMode(ReadMode), hGetContents)+import Control.Exception (bracket)+import System.Random (randomRIO)+import System.FilePath ((</>), takeExtension, takeFileName)+import qualified System.FilePath.Posix as FilePath.Posix (combine)+import System.Directory+import Control.Monad (forM_)+++--FIXME: how do we find this path for an arbitrary hackage server?+-- is it always at some fixed location relative to the server root?+legacyUploadURI :: URI+Just legacyUploadURI = parseURI "http://hackage.haskell.org/cgi-bin/hackage-scripts/protected/upload-pkg"++checkURI :: URI+Just checkURI = parseURI "http://hackage.haskell.org/cgi-bin/hackage-scripts/check-pkg"+++upload :: Verbosity -> [Repo] -> Maybe Username -> Maybe Password -> [FilePath] -> IO ()+upload verbosity repos mUsername mPassword paths = do+ let uploadURI = if isOldHackageURI targetRepoURI+ then legacyUploadURI+ else targetRepoURI{uriPath = uriPath targetRepoURI `FilePath.Posix.combine` "upload"}+ Username username <- maybe promptUsername return mUsername+ Password password <- maybe promptPassword return mPassword+ let auth = addAuthority AuthBasic {+ auRealm = "Hackage",+ auUsername = username,+ auPassword = password,+ auSite = uploadURI+ }+ flip mapM_ paths $ \path -> do+ notice verbosity $ "Uploading " ++ path ++ "... "+ handlePackage verbosity uploadURI auth path+ where+ targetRepoURI = remoteRepoURI $ last [ remoteRepo | Left remoteRepo <- map repoKind repos ] --FIXME: better error message when no repos are given++promptUsername :: IO Username+promptUsername = do+ putStr "Hackage username: "+ hFlush stdout+ fmap Username getLine++promptPassword :: IO Password+promptPassword = do+ putStr "Hackage password: "+ hFlush stdout+ -- save/restore the terminal echoing status+ passwd <- bracket (hGetEcho stdin) (hSetEcho stdin) $ \_ -> do+ hSetEcho stdin False -- no echoing for entering the password+ fmap Password getLine+ putStrLn ""+ return passwd++report :: Verbosity -> [Repo] -> Maybe Username -> Maybe Password -> IO ()+report verbosity repos mUsername mPassword = do+ let uploadURI = if isOldHackageURI targetRepoURI+ then legacyUploadURI+ else targetRepoURI{uriPath = ""}+ Username username <- maybe promptUsername return mUsername+ Password password <- maybe promptPassword return mPassword+ let auth = addAuthority AuthBasic {+ auRealm = "Hackage",+ auUsername = username,+ auPassword = password,+ auSite = uploadURI+ }+ forM_ repos $ \repo -> case repoKind repo of+ Left remoteRepo+ -> do dotCabal <- defaultCabalDir+ let srcDir = dotCabal </> "reports" </> remoteRepoName remoteRepo+ contents <- getDirectoryContents srcDir+ forM_ (filter (\c -> takeExtension c == ".log") contents) $ \logFile ->+ do inp <- readFile (srcDir </> logFile)+ let (reportStr, buildLog) = read inp :: (String,String)+ case BuildReport.parse reportStr of+ Left errs -> do warn verbosity $ "Errors: " ++ errs -- FIXME+ Right report' ->+ do info verbosity $ "Uploading report for " ++ display (BuildReport.package report')+ browse $ BuildReport.uploadReports (remoteRepoURI remoteRepo) [(report', Just buildLog)] auth+ return ()+ Right{} -> return ()+ where+ targetRepoURI = remoteRepoURI $ last [ remoteRepo | Left remoteRepo <- map repoKind repos ] --FIXME: better error message when no repos are given++check :: Verbosity -> [FilePath] -> IO ()+check verbosity paths = do+ flip mapM_ paths $ \path -> do+ notice verbosity $ "Checking " ++ path ++ "... "+ handlePackage verbosity checkURI (return ()) path++handlePackage :: Verbosity -> URI -> BrowserAction (HandleStream String) ()+ -> FilePath -> IO ()+handlePackage verbosity uri auth path =+ do req <- mkRequest uri path+ p <- proxy verbosity+ debug verbosity $ "\n" ++ show req+ (_,resp) <- browse $ do+ setProxy p+ setErrHandler (warn verbosity . ("http error: "++))+ setOutHandler (debug verbosity)+ auth+ setAuthorityGen (\_ _ -> return Nothing)+ request req+ debug verbosity $ show resp+ case rspCode resp of+ (2,0,0) -> do notice verbosity "Ok"+ (x,y,z) -> do notice verbosity $ "Error: " ++ path ++ ": "+ ++ map intToDigit [x,y,z] ++ " "+ ++ rspReason resp+ case findHeader HdrContentType resp of+ Just contenttype+ | takeWhile (/= ';') contenttype == "text/plain"+ -> notice verbosity $ rspBody resp+ _ -> debug verbosity $ rspBody resp++mkRequest :: URI -> FilePath -> IO (Request String)+mkRequest uri path = + do pkg <- readBinaryFile path+ boundary <- genBoundary+ let body = printMultiPart boundary (mkFormData path pkg)+ return $ Request {+ rqURI = uri,+ rqMethod = POST,+ rqHeaders = [Header HdrContentType ("multipart/form-data; boundary="++boundary),+ Header HdrContentLength (show (length body)),+ Header HdrAccept ("text/plain")],+ rqBody = body+ }++readBinaryFile :: FilePath -> IO String+readBinaryFile path = openBinaryFile path ReadMode >>= hGetContents++genBoundary :: IO String+genBoundary = do i <- randomRIO (0x10000000000000,0xFFFFFFFFFFFFFF) :: IO Integer+ return $ showHex i ""++mkFormData :: FilePath -> String -> [BodyPart]+mkFormData path pkg =+ -- yes, web browsers are that stupid (re quoting)+ [BodyPart [Header hdrContentDisposition $+ "form-data; name=package; filename=\""++takeFileName path++"\"",+ Header HdrContentType "application/x-gzip"]+ pkg]++hdrContentDisposition :: HeaderName+hdrContentDisposition = HdrCustom "Content-disposition"++-- * Multipart, partly stolen from the cgi package.++data BodyPart = BodyPart [Header] String++printMultiPart :: String -> [BodyPart] -> String+printMultiPart boundary xs = + concatMap (printBodyPart boundary) xs ++ crlf ++ "--" ++ boundary ++ "--" ++ crlf++printBodyPart :: String -> BodyPart -> String+printBodyPart boundary (BodyPart hs c) = crlf ++ "--" ++ boundary ++ crlf ++ concatMap show hs ++ crlf ++ c++crlf :: String+crlf = "\r\n"
+ Distribution/Client/Utils.hs view
@@ -0,0 +1,60 @@+module Distribution.Client.Utils where++import Data.List+ ( sortBy, groupBy )+import System.Directory+ ( doesFileExist, getModificationTime+ , getCurrentDirectory, setCurrentDirectory )+import qualified Control.Exception as Exception+ ( finally )++-- | Generic merging utility. For sorted input lists this is a full outer join.+--+-- * The result list never contains @(Nothing, Nothing)@.+--+mergeBy :: (a -> b -> Ordering) -> [a] -> [b] -> [MergeResult a b]+mergeBy cmp = merge+ where+ merge [] ys = [ OnlyInRight y | y <- ys]+ merge xs [] = [ OnlyInLeft x | x <- xs]+ merge (x:xs) (y:ys) =+ case x `cmp` y of+ GT -> OnlyInRight y : merge (x:xs) ys+ EQ -> InBoth x y : merge xs ys+ LT -> OnlyInLeft x : merge xs (y:ys)++data MergeResult a b = OnlyInLeft a | InBoth a b | OnlyInRight b++duplicates :: Ord a => [a] -> [[a]]+duplicates = duplicatesBy compare++duplicatesBy :: (a -> a -> Ordering) -> [a] -> [[a]]+duplicatesBy cmp = filter moreThanOne . groupBy eq . sortBy cmp+ where+ eq a b = case cmp a b of+ EQ -> True+ _ -> False+ moreThanOne (_:_:_) = True+ moreThanOne _ = False++-- | Compare the modification times of two files to see if the first is newer+-- than the second. The first file must exist but the second need not.+-- The expected use case is when the second file is generated using the first.+-- In this use case, if the result is True then the second file is out of date.+--+moreRecentFile :: FilePath -> FilePath -> IO Bool+moreRecentFile a b = do+ exists <- doesFileExist b+ if not exists+ then return True+ else do tb <- getModificationTime b+ ta <- getModificationTime a+ return (ta > tb)++-- | Executes the action in the specified directory.+inDir :: Maybe FilePath -> IO () -> IO ()+inDir Nothing m = m+inDir (Just d) m = do+ old <- getCurrentDirectory+ setCurrentDirectory d+ m `Exception.finally` setCurrentDirectory old
+ Distribution/Client/Win32SelfUpgrade.hs view
@@ -0,0 +1,222 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}+{-# OPTIONS_NHC98 -cpp #-}+{-# OPTIONS_JHC -fcpp -fffi #-}+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.Win32SelfUpgrade+-- Copyright : (c) Duncan Coutts 2008+-- License : BSD-like+--+-- Maintainer : cabal-devel@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Support for self-upgrading executables on Windows platforms.+-----------------------------------------------------------------------------+module Distribution.Client.Win32SelfUpgrade (+-- * Explanation+--+-- | Windows inherited a design choice from DOS that while initially innocuous+-- has rather unfortunate consequences. It maintains the invariant that every+-- open file has a corresponding name on disk. One positive consequence of this+-- is that an executable can always find it's own executable file. The downside+-- is that a program cannot be deleted or upgraded while it is running without+-- hideous workarounds. This module implements one such hideous workaround.+--+-- The basic idea is:+--+-- * Move our own exe file to a new name+-- * Copy a new exe file to the previous name+-- * Run the new exe file, passing our own pid and new path+-- * Wait for the new process to start+-- * Close the new exe file+-- * Exit old process+--+-- Then in the new process:+--+-- * Inform the old process that we've started+-- * Wait for the old process to die+-- * Delete the old exe file+-- * Exit new process+--++ possibleSelfUpgrade,+ deleteOldExeFile,+ ) where++#if mingw32_HOST_OS || mingw32_TARGET_OS++import qualified System.Win32 as Win32+import qualified System.Win32.DLL as Win32+import System.Win32 (DWORD, BOOL, HANDLE, LPCTSTR)+import Foreign.Ptr (Ptr, nullPtr)+import System.Process (runProcess)+import System.Directory (canonicalizePath)+import System.FilePath (takeBaseName, replaceBaseName, equalFilePath)++import Distribution.Verbosity as Verbosity (Verbosity, showForCabal)+import Distribution.Simple.Utils (debug, info)++import Prelude hiding (log)++-- | If one of the given files is our own exe file then we arrange things such+-- that the nested action can replace our own exe file.+--+-- We require that the new process accepts a command line invocation that+-- calls 'deleteOldExeFile', passing in the pid and exe file.+--+possibleSelfUpgrade :: Verbosity+ -> [FilePath]+ -> IO a -> IO a+possibleSelfUpgrade verbosity newPaths action = do+ dstPath <- canonicalizePath =<< Win32.getModuleFileName Win32.nullHANDLE++ newPaths' <- mapM canonicalizePath newPaths+ let doingSelfUpgrade = any (equalFilePath dstPath) newPaths'++ if not doingSelfUpgrade+ then action+ else do+ info verbosity $ "cabal-install does the replace-own-exe-file dance..."+ tmpPath <- moveOurExeOutOfTheWay verbosity+ result <- action+ scheduleOurDemise verbosity dstPath tmpPath+ (\pid path -> ["win32selfupgrade", pid, path+ ,"--verbose=" ++ Verbosity.showForCabal verbosity])+ return result++-- | The name of a Win32 Event object that we use to synchronise between the+-- old and new processes. We need to synchronise to make sure that the old+-- process has not yet terminated by the time the new one starts up and looks+-- for the old process. Otherwise the old one might have already terminated+-- and we could not wait on it terminating reliably (eg the pid might get+-- re-used).+--+syncEventName :: String+syncEventName = "Local\\cabal-install-upgrade"++-- | The first part of allowing our exe file to be replaced is to move the+-- existing exe file out of the way. Although we cannot delete our exe file+-- while we're still running, fortunately we can rename it, at least within+-- the same directory.+--+moveOurExeOutOfTheWay :: Verbosity -> IO FilePath+moveOurExeOutOfTheWay verbosity = do+ ourPID <- getCurrentProcessId+ dstPath <- Win32.getModuleFileName Win32.nullHANDLE++ let tmpPath = replaceBaseName dstPath (takeBaseName dstPath ++ show ourPID)++ debug verbosity $ "moving " ++ dstPath ++ " to " ++ tmpPath+ Win32.moveFile dstPath tmpPath+ return tmpPath++-- | Assuming we've now installed the new exe file in the right place, we+-- launch it and ask it to delete our exe file when we eventually terminate.+--+scheduleOurDemise :: Verbosity -> FilePath -> FilePath+ -> (String -> FilePath -> [String]) -> IO ()+scheduleOurDemise verbosity dstPath tmpPath mkArgs = do+ ourPID <- getCurrentProcessId+ event <- createEvent syncEventName++ let args = mkArgs (show ourPID) tmpPath+ log $ "launching child " ++ unwords (dstPath : map show args)+ runProcess dstPath args Nothing Nothing Nothing Nothing Nothing++ log $ "waiting for the child to start up"+ waitForSingleObject event (10*1000) -- wait at most 10 sec+ log $ "child started ok"++ where+ log msg = debug verbosity ("Win32Reinstall.parent: " ++ msg)++-- | Assuming we're now in the new child process, we've been asked by the old+-- process to wait for it to terminate and then we can remove the old exe file+-- that it renamted itself to.+--+deleteOldExeFile :: Verbosity -> Int -> FilePath -> IO ()+deleteOldExeFile verbosity oldPID tmpPath = do+ log $ "process started. Will delete exe file of process "+ ++ show oldPID ++ " at path " ++ tmpPath++ log $ "getting handle of parent process " ++ show oldPID+ oldPHANDLE <- Win32.openProcess Win32.sYNCHORNIZE False (fromIntegral oldPID)++ log $ "synchronising with parent"+ event <- openEvent syncEventName+ setEvent event++ log $ "waiting for parent process to terminate"+ waitForSingleObject oldPHANDLE Win32.iNFINITE+ log $ "parent process terminated"++ log $ "deleting parent's old .exe file"+ Win32.deleteFile tmpPath++ where+ log msg = debug verbosity ("Win32Reinstall.child: " ++ msg)++------------------------+-- Win32 foreign imports+--++-- A bunch of functions sadly not provided by the Win32 package.++foreign import stdcall unsafe "windows.h GetCurrentProcessId"+ getCurrentProcessId :: IO DWORD++foreign import stdcall unsafe "windows.h WaitForSingleObject"+ waitForSingleObject_ :: HANDLE -> DWORD -> IO DWORD++waitForSingleObject :: HANDLE -> DWORD -> IO ()+waitForSingleObject handle timeout =+ Win32.failIf_ bad "WaitForSingleObject" $+ waitForSingleObject_ handle timeout+ where+ bad result = not (result == 0 || result == wAIT_TIMEOUT)+ wAIT_TIMEOUT = 0x00000102++foreign import stdcall unsafe "windows.h CreateEventW"+ createEvent_ :: Ptr () -> BOOL -> BOOL -> LPCTSTR -> IO HANDLE++createEvent :: String -> IO HANDLE+createEvent name = do+ Win32.failIfNull "CreateEvent" $+ Win32.withTString name $+ createEvent_ nullPtr False False++foreign import stdcall unsafe "windows.h OpenEventW"+ openEvent_ :: DWORD -> BOOL -> LPCTSTR -> IO HANDLE++openEvent :: String -> IO HANDLE+openEvent name = do+ Win32.failIfNull "OpenEvent" $+ Win32.withTString name $+ openEvent_ eVENT_MODIFY_STATE False+ where+ eVENT_MODIFY_STATE :: DWORD+ eVENT_MODIFY_STATE = 0x0002++foreign import stdcall unsafe "windows.h SetEvent"+ setEvent_ :: HANDLE -> IO BOOL++setEvent :: HANDLE -> IO ()+setEvent handle =+ Win32.failIfFalse_ "SetEvent" $+ setEvent_ handle++#else++import Distribution.Verbosity (Verbosity)+import Distribution.Simple.Utils (die)++possibleSelfUpgrade :: Verbosity+ -> [FilePath]+ -> IO a -> IO a+possibleSelfUpgrade _ _ action = action++deleteOldExeFile :: Verbosity -> Int -> FilePath -> IO ()+deleteOldExeFile _ _ _ = die "win32selfupgrade not needed except on win32"++#endif
+ Distribution/Client/World.hs view
@@ -0,0 +1,173 @@+-----------------------------------------------------------------------------+-- |+-- Module : Distribution.Client.World+-- Copyright : (c) Peter Robinson 2009+-- License : BSD-like+--+-- Maintainer : thaldyron@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Interface to the world-file that contains a list of explicitly+-- requested packages. Meant to be imported qualified.+--+-- A world file entry stores the package-name, package-version, and+-- user flags.+-- For example, the entry generated by+-- # cabal install stm-io-hooks --flags="-debug"+-- looks like this:+-- # stm-io-hooks -any --flags="-debug"+-- To rebuild/upgrade the packages in world (e.g. when updating the compiler)+-- use+-- # cabal install world+--+-----------------------------------------------------------------------------+module Distribution.Client.World (+ WorldPkgInfo(..),+ insert,+ delete,+ getContents,+ ) where++import Distribution.Package+ ( Dependency(..) )+import Distribution.PackageDescription+ ( FlagAssignment, FlagName(FlagName) )+import Distribution.Verbosity+ ( Verbosity )+import Distribution.Simple.Utils+ ( die, info, chattyTry, writeFileAtomic )+import Distribution.Text+ ( Text(..), display, simpleParse )+import qualified Distribution.Compat.ReadP as Parse+import qualified Text.PrettyPrint as Disp+import Text.PrettyPrint ( (<>), (<+>) )+++import Data.Char as Char++import Data.List+ ( unionBy, deleteFirstsBy, nubBy )+import Data.Maybe+ ( isJust, fromJust )+import System.IO.Error+ ( isDoesNotExistError )+import qualified Data.ByteString.Lazy.Char8 as B+import Prelude hiding (getContents)+++data WorldPkgInfo = WorldPkgInfo Dependency FlagAssignment+ deriving (Show,Eq)++-- | Adds packages to the world file; creates the file if it doesn't+-- exist yet. Version constraints and flag assignments for a package are+-- updated if already present. IO errors are non-fatal.+insert :: Verbosity -> FilePath -> [WorldPkgInfo] -> IO ()+insert = modifyWorld $ unionBy equalUDep++-- | Removes packages from the world file.+-- Note: Currently unused as there is no mechanism in Cabal (yet) to+-- handle uninstalls. IO errors are non-fatal.+delete :: Verbosity -> FilePath -> [WorldPkgInfo] -> IO ()+delete = modifyWorld $ flip (deleteFirstsBy equalUDep)++-- | WorldPkgInfo values are considered equal if they refer to+-- the same package, i.e., we don't care about differing versions or flags.+equalUDep :: WorldPkgInfo -> WorldPkgInfo -> Bool+equalUDep (WorldPkgInfo (Dependency pkg1 _) _)+ (WorldPkgInfo (Dependency pkg2 _) _) = pkg1 == pkg2++-- | Modifies the world file by applying an update-function ('unionBy'+-- for 'insert', 'deleteFirstsBy' for 'delete') to the given list of+-- packages. IO errors are considered non-fatal.+modifyWorld :: ([WorldPkgInfo] -> [WorldPkgInfo]+ -> [WorldPkgInfo])+ -- ^ Function that defines how+ -- the list of user packages are merged with+ -- existing world packages.+ -> Verbosity+ -> FilePath -- ^ Location of the world file+ -> [WorldPkgInfo] -- ^ list of user supplied packages+ -> IO ()+modifyWorld _ _ _ [] = return ()+modifyWorld f verbosity world pkgs =+ chattyTry "Error while updating world-file. " $ do+ pkgsOldWorld <- getContents world+ -- Filter out packages that are not in the world file:+ let pkgsNewWorld = nubBy equalUDep $ f pkgs pkgsOldWorld+ -- 'Dependency' is not an Ord instance, so we need to check for+ -- equivalence the awkward way:+ if not (all (`elem` pkgsOldWorld) pkgsNewWorld &&+ all (`elem` pkgsNewWorld) pkgsOldWorld)+ then do+ info verbosity "Updating world file..."+ writeFileAtomic world $ unlines+ [ (display pkg) | pkg <- pkgsNewWorld]+ else+ info verbosity "World file is already up to date."+++-- | Returns the content of the world file as a list+getContents :: FilePath -> IO [WorldPkgInfo]+getContents world = do+ content <- safelyReadFile world+ let result = map simpleParse (lines $ B.unpack content)+ if all isJust result+ then return $ map fromJust result+ else die "Could not parse world file."+ where+ safelyReadFile :: FilePath -> IO B.ByteString+ safelyReadFile file = B.readFile file `catch` handler+ where+ handler e | isDoesNotExistError e = return B.empty+ | otherwise = ioError e+++instance Text WorldPkgInfo where+ disp (WorldPkgInfo dep flags) = disp dep <+> dispFlags flags+ where+ dispFlags [] = Disp.empty+ dispFlags fs = Disp.text "--flags="+ <> Disp.doubleQuotes (flagAssToDoc fs)+ flagAssToDoc = foldr (\(FlagName fname,val) flagAssDoc ->+ (if not val then Disp.char '-'+ else Disp.empty)+ Disp.<> Disp.text fname+ Disp.<+> flagAssDoc)+ Disp.empty+ parse = do+ dep <- parse+ Parse.skipSpaces+ flagAss <- Parse.option [] parseFlagAssignment+ return $ WorldPkgInfo dep flagAss+ where+ parseFlagAssignment :: Parse.ReadP r FlagAssignment+ parseFlagAssignment = do+ _ <- Parse.string "--flags"+ Parse.skipSpaces+ _ <- Parse.char '='+ Parse.skipSpaces+ inDoubleQuotes $ Parse.many1 flag+ where+ inDoubleQuotes :: Parse.ReadP r a -> Parse.ReadP r a+ inDoubleQuotes = Parse.between (Parse.char '"') (Parse.char '"')++ flag = do+ Parse.skipSpaces+ val <- negative Parse.+++ positive+ name <- ident+ Parse.skipSpaces+ return (FlagName name,val)+ negative = do+ _ <- Parse.char '-'+ return False+ positive = return True++ ident :: Parse.ReadP r String+ ident = do+ -- First character must be a letter/digit to avoid flags+ -- like "+-debug":+ c <- Parse.satisfy Char.isAlphaNum+ cs <- Parse.munch (\ch -> Char.isAlphaNum ch || ch == '_'+ || ch == '-')+ return (c:cs)
+ Distribution/Compat/Exception.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -cpp #-}+{-# OPTIONS_NHC98 -cpp #-}+{-# OPTIONS_JHC -fcpp #-}+-- #hide+module Distribution.Compat.Exception (+ SomeException,+ onException,+ catchIO,+ handleIO,+ catchExit,+ throwIOIO+ ) where++import System.Exit+import qualified Control.Exception as Exception+#if MIN_VERSION_base(4,0,0)+import Control.Exception (SomeException)+#else+import Control.Exception (Exception)+type SomeException = Exception+#endif++onException :: IO a -> IO b -> IO a+#if MIN_VERSION_base(4,0,0)+onException = Exception.onException+#else+onException io what = io `Exception.catch` \e -> do what+ Exception.throw e+#endif++throwIOIO :: Exception.IOException -> IO a+#if MIN_VERSION_base(4,0,0)+throwIOIO = Exception.throwIO+#else+throwIOIO = Exception.throwIO . Exception.IOException+#endif++catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#if MIN_VERSION_base(4,0,0)+catchIO = Exception.catch+#else+catchIO = Exception.catchJust Exception.ioErrors+#endif++handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a+handleIO = flip catchIO++catchExit :: IO a -> (ExitCode -> IO a) -> IO a+#if MIN_VERSION_base(4,0,0)+catchExit = Exception.catch+#else+catchExit = Exception.catchJust exitExceptions+ where exitExceptions (Exception.ExitException ee) = Just ee+ exitExceptions _ = Nothing+#endif
+ Distribution/Compat/FilePerms.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE CPP #-}+-- #hide+module Distribution.Compat.FilePerms (+ setFileOrdinary,+ setFileExecutable,+ ) where++#ifndef mingw32_HOST_OS+import System.Posix.Types+ ( FileMode )+import System.Posix.Internals+ ( c_chmod )+import Foreign.C+ ( withCString )+#if MIN_VERSION_base(4,0,0)+import Foreign.C+ ( throwErrnoPathIfMinus1_ )+#else+import Foreign.C+ ( throwErrnoIfMinus1_ )+#endif+#endif /* mingw32_HOST_OS */++setFileOrdinary, setFileExecutable :: FilePath -> IO ()+#ifndef mingw32_HOST_OS+setFileOrdinary path = setFileMode path 0o644 -- file perms -rw-r--r--+setFileExecutable path = setFileMode path 0o755 -- file perms -rwxr-xr-x++setFileMode :: FilePath -> FileMode -> IO ()+setFileMode name m =+ withCString name $ \s -> do+#if __GLASGOW_HASKELL__ >= 608+ throwErrnoPathIfMinus1_ "setFileMode" name (c_chmod s m)+#else+ throwErrnoIfMinus1_ name (c_chmod s m)+#endif+#else+setFileOrdinary _ = return ()+setFileExecutable _ = return ()+#endif
+ Main.hs view
@@ -0,0 +1,403 @@+-----------------------------------------------------------------------------+-- |+-- Module : Main+-- Copyright : (c) David Himmelstrup 2005+-- License : BSD-like+--+-- Maintainer : lemmih@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Entry point to the default cabal-install front-end.+-----------------------------------------------------------------------------++module Main (main) where++import Distribution.Client.Setup+ ( GlobalFlags(..), globalCommand, globalRepos+ , ConfigFlags(..)+ , ConfigExFlags(..), configureExCommand+ , InstallFlags(..), defaultInstallFlags+ , installCommand, upgradeCommand+ , FetchFlags(..), fetchCommand+ , checkCommand+ , updateCommand+ , ListFlags(..), listCommand+ , InfoFlags(..), infoCommand+ , UploadFlags(..), uploadCommand+ , ReportFlags(..), reportCommand+ , InitFlags, initCommand+ , reportCommand+ , unpackCommand, UnpackFlags(..) )+import Distribution.Simple.Setup+ ( BuildFlags(..), buildCommand+ , HaddockFlags(..), haddockCommand+ , HscolourFlags(..), hscolourCommand+ , CopyFlags(..), copyCommand+ , RegisterFlags(..), registerCommand+ , CleanFlags(..), cleanCommand+ , SDistFlags(..), sdistCommand+ , TestFlags(..), testCommand+ , Flag(..), fromFlag, fromFlagOrDefault, flagToMaybe )++import Distribution.Client.SetupWrapper+ ( setupWrapper, SetupScriptOptions(..), defaultSetupScriptOptions )+import Distribution.Client.Config+ ( SavedConfig(..), loadConfig, defaultConfigFile )+import Distribution.Client.Targets+ ( readUserTargets )++import Distribution.Client.List (list, info)+import Distribution.Client.Install (install, upgrade)+import Distribution.Client.Configure (configure)+import Distribution.Client.Update (update)+import Distribution.Client.Fetch (fetch)+import Distribution.Client.Check as Check (check)+--import Distribution.Client.Clean (clean)+import Distribution.Client.Upload as Upload (upload, check, report)+import Distribution.Client.SrcDist (sdist)+import Distribution.Client.Unpack (unpack)+import Distribution.Client.Init (initCabal)+import qualified Distribution.Client.Win32SelfUpgrade as Win32SelfUpgrade++import Distribution.Simple.Compiler+ ( Compiler, PackageDB(..), PackageDBStack )+import Distribution.Simple.Program+ ( ProgramConfiguration, defaultProgramConfiguration )+import Distribution.Simple.Command+import Distribution.Simple.Configure (configCompilerAux)+import Distribution.Simple.Utils+ ( cabalVersion, die, topHandler, intercalate )+import Distribution.Text+ ( display )+import Distribution.Verbosity as Verbosity+ ( Verbosity, normal, intToVerbosity, lessVerbose )+import qualified Paths_cabal_install_bundle (version)++import System.Environment (getArgs, getProgName)+import System.Exit (exitFailure)+import System.FilePath (splitExtension, takeExtension)+import System.Directory (doesFileExist)+import Data.List (intersperse)+import Data.Maybe (fromMaybe)+import Data.Monoid (Monoid(..))+import Control.Monad (unless)++-- | Entry point+--+main :: IO ()+main = getArgs >>= mainWorker++mainWorker :: [String] -> IO ()+mainWorker ("win32selfupgrade":args) = win32SelfUpgradeAction args+mainWorker args = topHandler $+ case commandsRun globalCommand commands args of+ CommandHelp help -> printGlobalHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo (globalflags, commandParse) ->+ case commandParse of+ _ | fromFlag (globalVersion globalflags) -> printVersion+ | fromFlag (globalNumericVersion globalflags) -> printNumericVersion+ CommandHelp help -> printCommandHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo action -> action globalflags++ where+ printCommandHelp help = do+ pname <- getProgName+ putStr (help pname)+ printGlobalHelp help = do+ pname <- getProgName+ configFile <- defaultConfigFile+ putStr (help pname)+ putStr $ "\nYou can edit the cabal configuration file to set defaults:\n"+ ++ " " ++ configFile ++ "\n"+ printOptionsList = putStr . unlines+ printErrors errs = die $ concat (intersperse "\n" errs)+ printNumericVersion = putStrLn $ display Paths_cabal_install_bundle.version+ printVersion = putStrLn $ "cabal-install version "+ ++ display Paths_cabal_install_bundle.version+ ++ "\nusing version "+ ++ display cabalVersion+ ++ " of the Cabal library "++ commands =+ [installCommand `commandAddAction` installAction+ ,updateCommand `commandAddAction` updateAction+ ,listCommand `commandAddAction` listAction+ ,infoCommand `commandAddAction` infoAction+ ,fetchCommand `commandAddAction` fetchAction+ ,unpackCommand `commandAddAction` unpackAction+ ,checkCommand `commandAddAction` checkAction+ ,sdistCommand `commandAddAction` sdistAction+ ,uploadCommand `commandAddAction` uploadAction+ ,reportCommand `commandAddAction` reportAction+ ,initCommand `commandAddAction` initAction+ ,configureExCommand `commandAddAction` configureAction+ ,wrapperAction (buildCommand defaultProgramConfiguration)+ buildVerbosity buildDistPref+ ,wrapperAction copyCommand+ copyVerbosity copyDistPref+ ,wrapperAction haddockCommand+ haddockVerbosity haddockDistPref+ ,wrapperAction cleanCommand+ cleanVerbosity cleanDistPref+ ,wrapperAction hscolourCommand+ hscolourVerbosity hscolourDistPref+ ,wrapperAction registerCommand+ regVerbosity regDistPref+ ,wrapperAction testCommand+ testVerbosity testDistPref+ ,upgradeCommand `commandAddAction` upgradeAction+ ]++wrapperAction :: Monoid flags+ => CommandUI flags+ -> (flags -> Flag Verbosity)+ -> (flags -> Flag String)+ -> Command (GlobalFlags -> IO ())+wrapperAction command verbosityFlag distPrefFlag =+ commandAddAction command+ { commandDefaultFlags = mempty } $ \flags extraArgs _globalFlags -> do+ let verbosity = fromFlagOrDefault normal (verbosityFlag flags)+ setupScriptOptions = defaultSetupScriptOptions {+ useDistPref = fromFlagOrDefault+ (useDistPref defaultSetupScriptOptions)+ (distPrefFlag flags)+ }+ setupWrapper verbosity setupScriptOptions Nothing+ command (const flags) extraArgs++configureAction :: (ConfigFlags, ConfigExFlags)+ -> [String] -> GlobalFlags -> IO ()+configureAction (configFlags, configExFlags) extraArgs globalFlags = do+ let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+ config <- loadConfig verbosity (globalConfigFile globalFlags)+ (configUserInstall configFlags)+ let configFlags' = savedConfigureFlags config `mappend` configFlags+ configExFlags' = savedConfigureExFlags config `mappend` configExFlags+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux configFlags'+ configure verbosity+ (configPackageDB' configFlags') (globalRepos globalFlags')+ comp conf configFlags' configExFlags' extraArgs++installAction :: (ConfigFlags, ConfigExFlags, InstallFlags)+ -> [String] -> GlobalFlags -> IO ()+installAction (configFlags, _, installFlags) _ _globalFlags+ | fromFlagOrDefault False (installOnly installFlags)+ = let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+ in setupWrapper verbosity defaultSetupScriptOptions Nothing+ installCommand (const mempty) []++installAction (configFlags, configExFlags, installFlags)+ extraArgs globalFlags = do+ let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+ targets <- readUserTargets verbosity extraArgs+ config <- loadConfig verbosity (globalConfigFile globalFlags)+ (configUserInstall configFlags)+ let configFlags' = savedConfigureFlags config `mappend` configFlags+ configExFlags' = savedConfigureExFlags config `mappend` configExFlags+ installFlags' = defaultInstallFlags `mappend`+ savedInstallFlags config `mappend` installFlags+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux' configFlags'+ install verbosity+ (configPackageDB' configFlags') (globalRepos globalFlags')+ comp conf globalFlags' configFlags' configExFlags' installFlags'+ targets++listAction :: ListFlags -> [String] -> GlobalFlags -> IO ()+listAction listFlags extraArgs globalFlags = do+ let verbosity = fromFlag (listVerbosity listFlags)+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let configFlags = savedConfigureFlags config+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux' configFlags+ list verbosity+ (configPackageDB' configFlags)+ (globalRepos globalFlags')+ comp+ conf+ listFlags+ extraArgs++infoAction :: InfoFlags -> [String] -> GlobalFlags -> IO ()+infoAction infoFlags extraArgs globalFlags = do+ let verbosity = fromFlag (infoVerbosity infoFlags)+ targets <- readUserTargets verbosity extraArgs+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let configFlags = savedConfigureFlags config+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux configFlags+ info verbosity+ (configPackageDB' configFlags)+ (globalRepos globalFlags')+ comp+ conf+ globalFlags'+ infoFlags+ targets++updateAction :: Flag Verbosity -> [String] -> GlobalFlags -> IO ()+updateAction verbosityFlag extraArgs globalFlags = do+ unless (null extraArgs) $ do+ die $ "'update' doesn't take any extra arguments: " ++ unwords extraArgs+ let verbosity = fromFlag verbosityFlag+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let globalFlags' = savedGlobalFlags config `mappend` globalFlags+ update verbosity (globalRepos globalFlags')++upgradeAction :: (ConfigFlags, ConfigExFlags, InstallFlags)+ -> [String] -> GlobalFlags -> IO ()+upgradeAction (configFlags, configExFlags, installFlags)+ extraArgs globalFlags = do+ let verbosity = fromFlagOrDefault normal (configVerbosity configFlags)+ targets <- readUserTargets verbosity extraArgs+ config <- loadConfig verbosity (globalConfigFile globalFlags)+ (configUserInstall configFlags)+ let configFlags' = savedConfigureFlags config `mappend` configFlags+ configExFlags' = savedConfigureExFlags config `mappend` configExFlags+ installFlags' = defaultInstallFlags `mappend`+ savedInstallFlags config `mappend` installFlags+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux' configFlags'+ upgrade verbosity+ (configPackageDB' configFlags') (globalRepos globalFlags')+ comp conf globalFlags' configFlags' configExFlags' installFlags'+ targets++fetchAction :: FetchFlags -> [String] -> GlobalFlags -> IO ()+fetchAction fetchFlags extraArgs globalFlags = do+ let verbosity = fromFlag (fetchVerbosity fetchFlags)+ targets <- readUserTargets verbosity extraArgs+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let configFlags = savedConfigureFlags config+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ (comp, conf) <- configCompilerAux' configFlags+ fetch verbosity+ (configPackageDB' configFlags) (globalRepos globalFlags')+ comp conf globalFlags' fetchFlags+ targets++uploadAction :: UploadFlags -> [String] -> GlobalFlags -> IO ()+uploadAction uploadFlags extraArgs globalFlags = do+ let verbosity = fromFlag (uploadVerbosity uploadFlags)+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let uploadFlags' = savedUploadFlags config `mappend` uploadFlags+ globalFlags' = savedGlobalFlags config `mappend` globalFlags+ tarfiles = extraArgs+ checkTarFiles extraArgs+ if fromFlag (uploadCheck uploadFlags')+ then Upload.check verbosity tarfiles+ else upload verbosity+ (globalRepos globalFlags')+ (flagToMaybe $ uploadUsername uploadFlags')+ (flagToMaybe $ uploadPassword uploadFlags')+ tarfiles+ where+ checkTarFiles tarfiles+ | null tarfiles+ = die "the 'upload' command expects one or more .tar.gz packages."+ | not (null otherFiles)+ = die $ "the 'upload' command expects only .tar.gz packages: "+ ++ intercalate ", " otherFiles+ | otherwise = sequence_+ [ do exists <- doesFileExist tarfile+ unless exists $ die $ "file not found: " ++ tarfile+ | tarfile <- tarfiles ]++ where otherFiles = filter (not . isTarGzFile) tarfiles+ isTarGzFile file = case splitExtension file of+ (file', ".gz") -> takeExtension file' == ".tar"+ _ -> False++checkAction :: Flag Verbosity -> [String] -> GlobalFlags -> IO ()+checkAction verbosityFlag extraArgs _globalFlags = do+ unless (null extraArgs) $ do+ die $ "'check' doesn't take any extra arguments: " ++ unwords extraArgs+ allOk <- Check.check (fromFlag verbosityFlag)+ unless allOk exitFailure+++sdistAction :: SDistFlags -> [String] -> GlobalFlags -> IO ()+sdistAction sflags extraArgs _globalFlags = do+ unless (null extraArgs) $ do+ die $ "'sdist' doesn't take any extra arguments: " ++ unwords extraArgs+ sdist sflags++reportAction :: ReportFlags -> [String] -> GlobalFlags -> IO ()+reportAction reportFlags extraArgs globalFlags = do+ unless (null extraArgs) $ do+ die $ "'report' doesn't take any extra arguments: " ++ unwords extraArgs++ let verbosity = fromFlag (reportVerbosity reportFlags)+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let globalFlags' = savedGlobalFlags config `mappend` globalFlags+ reportFlags' = savedReportFlags config `mappend` reportFlags++ Upload.report verbosity (globalRepos globalFlags')+ (flagToMaybe $ reportUsername reportFlags')+ (flagToMaybe $ reportPassword reportFlags')++unpackAction :: UnpackFlags -> [String] -> GlobalFlags -> IO ()+unpackAction unpackFlags extraArgs globalFlags = do+ let verbosity = fromFlag (unpackVerbosity unpackFlags)+ targets <- readUserTargets verbosity extraArgs+ config <- loadConfig verbosity (globalConfigFile globalFlags) mempty+ let globalFlags' = savedGlobalFlags config `mappend` globalFlags+ unpack verbosity+ (globalRepos (savedGlobalFlags config))+ globalFlags'+ unpackFlags+ targets++initAction :: InitFlags -> [String] -> GlobalFlags -> IO ()+initAction flags _extraArgs _globalFlags = do+ initCabal flags++-- | See 'Distribution.Client.Install.withWin32SelfUpgrade' for details.+--+win32SelfUpgradeAction :: [String] -> IO ()+win32SelfUpgradeAction (pid:path:rest) =+ Win32SelfUpgrade.deleteOldExeFile verbosity (read pid) path+ where+ verbosity = case rest of+ (['-','-','v','e','r','b','o','s','e','=',n]:_) | n `elem` ['0'..'9']+ -> fromMaybe Verbosity.normal (Verbosity.intToVerbosity (read [n]))+ _ -> Verbosity.normal+win32SelfUpgradeAction _ = return ()++--+-- Utils (transitionary)+--++-- | Currently the user interface specifies the package dbs to use with just a+-- single valued option, a 'PackageDB'. However internally we represent the+-- stack of 'PackageDB's explictly as a list. This function converts encodes+-- the package db stack implicit in a single packagedb.+--+-- TODO: sort this out, make it consistent with the command line UI+implicitPackageDbStack :: Bool -> Maybe PackageDB -> PackageDBStack+implicitPackageDbStack userInstall packageDbFlag+ | userInstall = GlobalPackageDB : UserPackageDB : extra+ | otherwise = GlobalPackageDB : extra+ where+ extra = case packageDbFlag of+ Just (SpecificPackageDB db) -> [SpecificPackageDB db]+ _ -> []++configPackageDB' :: ConfigFlags -> PackageDBStack+configPackageDB' cfg =+ implicitPackageDbStack userInstall (flagToMaybe (configPackageDB cfg))+ where+ userInstall = fromFlagOrDefault True (configUserInstall cfg)++configCompilerAux' :: ConfigFlags+ -> IO (Compiler, ProgramConfiguration)+configCompilerAux' configFlags =+ configCompilerAux configFlags+ --FIXME: make configCompilerAux use a sensible verbosity+ { configVerbosity = fmap lessVerbose (configVerbosity configFlags) }
+ Network.hs view
@@ -0,0 +1,468 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file libraries/network/LICENSE)+-- +-- Maintainer : libraries@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- The "Network" interface is a \"higher-level\" interface to+-- networking facilities, and it is recommended unless you need the+-- lower-level interface in "Network.Socket".+--+-----------------------------------------------------------------------------++#include "HsNetworkConfig.h"++#ifdef HAVE_GETADDRINFO+-- Use IPv6-capable function definitions if the OS supports it.+#define IPV6_SOCKET_SUPPORT 1+#endif++module Network+ (+ -- * Basic data types+ Socket+ , PortID(..)+ , HostName+ , PortNumber++ -- * Initialisation+ , withSocketsDo+ + -- * Server-side connections+ , listenOn+ , accept+ , sClose++ -- * Client-side connections+ , connectTo++ -- * Simple sending and receiving+ {-$sendrecv-}+ , sendTo+ , recvFrom++ -- * Miscellaneous+ , socketPort++ -- * Networking Issues+ -- ** Buffering+ {-$buffering-}++ -- ** Improving I\/O Performance over sockets+ {-$performance-}++ -- ** @SIGPIPE@+ {-$sigpipe-}+ ) where++import Control.Monad (liftM)+import Data.Maybe (fromJust)+import Network.BSD+import Network.Socket hiding (accept, socketPort, recvFrom, sendTo, PortNumber)+import qualified Network.Socket as Socket (accept)+import System.IO+import Prelude+import qualified Control.Exception as Exception++-- ---------------------------------------------------------------------------+-- High Level ``Setup'' functions++-- If the @PortID@ specifies a unix family socket and the @Hostname@+-- differs from that returned by @getHostname@ then an error is+-- raised. Alternatively an empty string may be given to @connectTo@+-- signalling that the current hostname applies.++data PortID = + Service String -- Service Name eg "ftp"+ | PortNumber PortNumber -- User defined Port Number+#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+ | UnixSocket String -- Unix family socket in file system+#endif++-- | Calling 'connectTo' creates a client side socket which is+-- connected to the given host and port. The Protocol and socket type is+-- derived from the given port identifier. If a port number is given+-- then the result is always an internet family 'Stream' socket. ++connectTo :: HostName -- Hostname+ -> PortID -- Port Identifier+ -> IO Handle -- Connected Socket++#if defined(IPV6_SOCKET_SUPPORT)+-- IPv6 and IPv4.++connectTo hostname (Service serv) = connect' hostname serv++connectTo hostname (PortNumber port) = connect' hostname (show port)+#else+-- IPv4 only.++connectTo hostname (Service serv) = do+ proto <- getProtocolNumber "tcp"+ bracketOnError+ (socket AF_INET Stream proto)+ (sClose) -- only done if there's an error+ (\sock -> do+ port <- getServicePortNumber serv+ he <- getHostByName hostname+ connect sock (SockAddrInet port (hostAddress he))+ socketToHandle sock ReadWriteMode+ )++connectTo hostname (PortNumber port) = do+ proto <- getProtocolNumber "tcp"+ bracketOnError+ (socket AF_INET Stream proto)+ (sClose) -- only done if there's an error+ (\sock -> do+ he <- getHostByName hostname+ connect sock (SockAddrInet port (hostAddress he))+ socketToHandle sock ReadWriteMode+ )+#endif++#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+connectTo _ (UnixSocket path) = do+ bracketOnError+ (socket AF_UNIX Stream 0)+ (sClose)+ (\sock -> do+ connect sock (SockAddrUnix path)+ socketToHandle sock ReadWriteMode+ )+#endif++#if defined(IPV6_SOCKET_SUPPORT)+connect' :: HostName -> ServiceName -> IO Handle++connect' host serv = do+ proto <- getProtocolNumber "tcp"+ let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]+ , addrProtocol = proto+ , addrSocketType = Stream }+ addrs <- getAddrInfo (Just hints) (Just host) (Just serv)+ firstSuccessful $ map tryToConnect addrs+ where+ tryToConnect addr =+ bracketOnError+ (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))+ (sClose) -- only done if there's an error+ (\sock -> do+ connect sock (addrAddress addr)+ socketToHandle sock ReadWriteMode+ )+#endif++-- | Creates the server side socket which has been bound to the+-- specified port.+--+-- NOTE: To avoid the \"Address already in use\"+-- problems popped up several times on the GHC-Users mailing list we+-- set the 'ReuseAddr' socket option on the listening socket. If you+-- don't want this behaviour, please use the lower level+-- 'Network.Socket.listen' instead.+--+-- If available, the 'IPv6Only' socket option is set to 0+-- so that both IPv4 and IPv6 can be accepted with this socket.++listenOn :: PortID -- ^ Port Identifier+ -> IO Socket -- ^ Connected Socket++#if defined(IPV6_SOCKET_SUPPORT)+-- IPv6 and IPv4.++listenOn (Service serv) = listen' serv++listenOn (PortNumber port) = listen' (show port)+#else+-- IPv4 only.++listenOn (Service serv) = do+ proto <- getProtocolNumber "tcp"+ bracketOnError+ (socket AF_INET Stream proto)+ (sClose)+ (\sock -> do+ port <- getServicePortNumber serv+ setSocketOption sock ReuseAddr 1+ bindSocket sock (SockAddrInet port iNADDR_ANY)+ listen sock maxListenQueue+ return sock+ )++listenOn (PortNumber port) = do+ proto <- getProtocolNumber "tcp"+ bracketOnError+ (socket AF_INET Stream proto)+ (sClose)+ (\sock -> do+ setSocketOption sock ReuseAddr 1+ bindSocket sock (SockAddrInet port iNADDR_ANY)+ listen sock maxListenQueue+ return sock+ )+#endif++#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+listenOn (UnixSocket path) =+ bracketOnError+ (socket AF_UNIX Stream 0)+ (sClose)+ (\sock -> do+ setSocketOption sock ReuseAddr 1+ bindSocket sock (SockAddrUnix path)+ listen sock maxListenQueue+ return sock+ )+#endif++#if defined(IPV6_SOCKET_SUPPORT)+listen' :: ServiceName -> IO Socket++listen' serv = do+ proto <- getProtocolNumber "tcp"+ -- We should probably specify addrFamily = AF_INET6 and the filter+ -- code below should be removed. AI_ADDRCONFIG is probably not+ -- necessary. But this code is well-tested. So, let's keep it.+ let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_PASSIVE]+ , addrSocketType = Stream+ , addrProtocol = proto }+ addrs <- getAddrInfo (Just hints) Nothing (Just serv)+ -- Choose an IPv6 socket if exists. This ensures the socket can+ -- handle both IPv4 and IPv6 if v6only is false.+ let addrs' = filter (\x -> addrFamily x == AF_INET6) addrs+ addr = if null addrs' then head addrs else head addrs'+ bracketOnError+ (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))+ (sClose)+ (\sock -> do+ setSocketOption sock ReuseAddr 1+ bindSocket sock (addrAddress addr)+ listen sock maxListenQueue+ return sock+ )+#endif++-- -----------------------------------------------------------------------------+-- accept++-- | Accept a connection on a socket created by 'listenOn'. Normal+-- I\/O operations (see "System.IO") can be used on the 'Handle'+-- returned to communicate with the client.+-- Notice that although you can pass any Socket to Network.accept,+-- only sockets of either AF_UNIX, AF_INET, or AF_INET6 will work+-- (this shouldn't be a problem, though). When using AF_UNIX, HostName+-- will be set to the path of the socket and PortNumber to -1.+--+accept :: Socket -- ^ Listening Socket+ -> IO (Handle,+ HostName,+ PortNumber) -- ^ Triple of: read\/write 'Handle' for + -- communicating with the client,+ -- the 'HostName' of the peer socket, and+ -- the 'PortNumber' of the remote connection.+accept sock@(MkSocket _ AF_INET _ _ _) = do+ ~(sock', (SockAddrInet port haddr)) <- Socket.accept sock+ peer <- catchIO+ (do + (HostEntry peer _ _ _) <- getHostByAddr AF_INET haddr+ return peer+ )+ (\_e -> inet_ntoa haddr)+ -- if getHostByName fails, we fall back to the IP address+ handle <- socketToHandle sock' ReadWriteMode+ return (handle, peer, port)+#if defined(IPV6_SOCKET_SUPPORT)+accept sock@(MkSocket _ AF_INET6 _ _ _) = do+ (sock', addr) <- Socket.accept sock+ peer <- catchIO ((fromJust . fst) `liftM` getNameInfo [] True False addr) $+ \_ -> case addr of+ SockAddrInet _ a -> inet_ntoa a+ SockAddrInet6 _ _ a _ -> return (show a)+# if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+ SockAddrUnix a -> return a+# endif+ handle <- socketToHandle sock' ReadWriteMode+ let port = case addr of+ SockAddrInet p _ -> p+ SockAddrInet6 p _ _ _ -> p+ _ -> -1+ return (handle, peer, port)+#endif+#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+accept sock@(MkSocket _ AF_UNIX _ _ _) = do+ ~(sock', (SockAddrUnix path)) <- Socket.accept sock+ handle <- socketToHandle sock' ReadWriteMode+ return (handle, path, -1)+#endif+accept (MkSocket _ family _ _ _) =+ error $ "Sorry, address family " ++ (show family) ++ " is not supported!"++-- -----------------------------------------------------------------------------+-- sendTo/recvFrom++{-$sendrecv+Send and receive data from\/to the given host and port number. These+should normally only be used where the socket will not be required for+further calls. Also, note that due to the use of 'hGetContents' in 'recvFrom'+the socket will remain open (i.e. not available) even if the function already+returned. Their use is strongly discouraged except for small test-applications+or invocations from the command line.+-}++sendTo :: HostName -- Hostname+ -> PortID -- Port Number+ -> String -- Message to send+ -> IO ()+sendTo h p msg = do+ s <- connectTo h p+ hPutStr s msg+ hClose s++recvFrom :: HostName -- Hostname+ -> PortID -- Port Number+ -> IO String -- Received Data++#if defined(IPV6_SOCKET_SUPPORT)+recvFrom host port = do+ proto <- getProtocolNumber "tcp"+ let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]+ , addrProtocol = proto+ , addrSocketType = Stream }+ allowed <- map addrAddress `liftM` getAddrInfo (Just hints) (Just host)+ Nothing+ s <- listenOn port+ let waiting = do+ (s', addr) <- Socket.accept s+ if not (addr `oneOf` allowed)+ then sClose s' >> waiting+ else socketToHandle s' ReadMode >>= hGetContents+ waiting+ where+ a@(SockAddrInet _ ha) `oneOf` ((SockAddrInet _ hb):bs)+ | ha == hb = True+ | otherwise = a `oneOf` bs+ a@(SockAddrInet6 _ _ ha _) `oneOf` ((SockAddrInet6 _ _ hb _):bs)+ | ha == hb = True+ | otherwise = a `oneOf` bs+ _ `oneOf` _ = False+#else+recvFrom host port = do+ ip <- getHostByName host+ let ipHs = hostAddresses ip+ s <- listenOn port+ let + waiting = do+ ~(s', SockAddrInet _ haddr) <- Socket.accept s+ he <- getHostByAddr AF_INET haddr+ if not (any (`elem` ipHs) (hostAddresses he))+ then do+ sClose s'+ waiting+ else do+ h <- socketToHandle s' ReadMode+ msg <- hGetContents h+ return msg++ message <- waiting+ return message+#endif++-- ---------------------------------------------------------------------------+-- Access function returning the port type/id of socket.++-- | Returns the 'PortID' associated with a given socket.+socketPort :: Socket -> IO PortID+socketPort s = do+ sockaddr <- getSocketName s+ return (portID sockaddr)+ where+ portID sa =+ case sa of+ SockAddrInet port _ -> PortNumber port+#if defined(IPV6_SOCKET_SUPPORT)+ SockAddrInet6 port _ _ _ -> PortNumber port+#endif+#if !defined(mingw32_HOST_OS) && !defined(cygwin32_HOST_OS) && !defined(_WIN32)+ SockAddrUnix path -> UnixSocket path+#endif++-- ---------------------------------------------------------------------------+-- Utils++-- Like bracket, but only performs the final action if there was an +-- exception raised by the middle bit.+bracketOnError+ :: 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+#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 606+bracketOnError before after thing =+ Exception.block (do+ a <- before+ r <- Exception.catch+ (Exception.unblock (thing a))+ (\e -> do { after a; Exception.throw e })+ return r+ )+#else+bracketOnError = Exception.bracketOnError+#endif++-----------------------------------------------------------------------------+-- Extra documentation++{-$buffering++The 'Handle' returned by 'connectTo' and 'accept' is block-buffered by+default. For an interactive application you may want to set the+buffering mode on the 'Handle' to+'LineBuffering' or 'NoBuffering', like so:++> h <- connectTo host port+> hSetBuffering h LineBuffering+-}++{-$performance++For really fast I\/O, it might be worth looking at the 'hGetBuf' and+'hPutBuf' family of functions in "System.IO".+-}++{-$sigpipe++On Unix, when writing to a socket and the reading end is+closed by the remote client, the program is normally sent a+@SIGPIPE@ signal by the operating system. The+default behaviour when a @SIGPIPE@ is received is+to terminate the program silently, which can be somewhat confusing+if you haven't encountered this before. The solution is to+specify that @SIGPIPE@ is to be ignored, using+the POSIX library:++> import Posix+> main = do installHandler sigPIPE Ignore Nothing; ...+-}++catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a+#if MIN_VERSION_base(4,0,0)+catchIO = Exception.catch+#else+catchIO = Exception.catchJust Exception.ioErrors+#endif++-- Returns the first action from a list which does not throw an exception.+-- If all the actions throw exceptions (and the list of actions is not empty),+-- the last exception is thrown.+firstSuccessful :: [IO a] -> IO a+firstSuccessful [] = error "firstSuccessful: empty list"+firstSuccessful (p:ps) = catchIO p $ \e ->+ case ps of+ [] -> Exception.throw e+ _ -> firstSuccessful ps
+ Network/BSD.hsc view
@@ -0,0 +1,587 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.BSD+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file libraries/network/LICENSE)+-- +-- Maintainer : libraries@haskell.org+-- Stability : experimental+-- Portability : non-portable+--+-- The "Network.BSD" module defines Haskell bindings to network+-- programming functionality provided by BSD Unix derivatives.+--+-----------------------------------------------------------------------------++#include "HsNet.h"++-- NOTE: ##, we want this interpreted when compiling the .hs, not by hsc2hs.+##include "Typeable.h"++module Network.BSD+ (+ -- * Host names+ HostName+ , getHostName++ , HostEntry(..)+ , getHostByName+ , getHostByAddr+ , hostAddress++#if defined(HAVE_GETHOSTENT) && !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+ , getHostEntries++ -- ** Low level functionality+ , setHostEntry+ , getHostEntry+ , endHostEntry+#endif++ -- * Service names+ , ServiceEntry(..)+ , ServiceName+ , getServiceByName+ , getServiceByPort+ , getServicePortNumber++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+ , getServiceEntries++ -- ** Low level functionality+ , getServiceEntry+ , setServiceEntry+ , endServiceEntry+#endif++ -- * Protocol names+ , ProtocolName+ , ProtocolNumber+ , ProtocolEntry(..)+ , getProtocolByName+ , getProtocolByNumber+ , getProtocolNumber+ , defaultProtocol++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+ , getProtocolEntries+ -- ** Low level functionality+ , setProtocolEntry+ , getProtocolEntry+ , endProtocolEntry+#endif++ -- * Port numbers+ , PortNumber++ -- * Network names+ , NetworkName+ , NetworkAddr+ , NetworkEntry(..)++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+ , getNetworkByName+ , getNetworkByAddr+ , getNetworkEntries+ -- ** Low level functionality+ , setNetworkEntry+ , getNetworkEntry+ , endNetworkEntry+#endif+ ) where++#ifdef __HUGS__+import Hugs.Prelude (IOException(..), IOErrorType(..))+#endif+import Network.Socket++import Control.Concurrent (MVar, newMVar, withMVar)+import Control.Exception (catch)+import Foreign.C.String (CString, peekCString, withCString)+#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+import Foreign.C.Types ( CShort )+#endif+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types ( CInt(..), CULong(..), CSize(..) )+#else+import Foreign.C.Types ( CInt, CULong, CSize )+#endif+import Foreign.Ptr (Ptr, nullPtr)+import Foreign.Storable (Storable(..))+import Foreign.Marshal.Array (allocaArray0, peekArray0)+import Foreign.Marshal.Utils (with, fromBool)+import Data.Typeable+import Prelude hiding (catch)+import System.IO.Error (ioeSetErrorString, mkIOError)+import System.IO.Unsafe (unsafePerformIO)++#ifdef __GLASGOW_HASKELL__+# if __GLASGOW_HASKELL__ >= 611+import GHC.IO.Exception+# else+import GHC.IOBase+# endif+#endif++import Control.Monad (liftM)++-- ---------------------------------------------------------------------------+-- Basic Types++type ProtocolName = String++-- ---------------------------------------------------------------------------+-- Service Database Access++-- Calling getServiceByName for a given service and protocol returns+-- the systems service entry. This should be used to find the port+-- numbers for standard protocols such as SMTP and FTP. The remaining+-- three functions should be used for browsing the service database+-- sequentially.++-- Calling setServiceEntry with True indicates that the service+-- database should be left open between calls to getServiceEntry. To+-- close the database a call to endServiceEntry is required. This+-- database file is usually stored in the file /etc/services.++data ServiceEntry = + ServiceEntry {+ serviceName :: ServiceName, -- Official Name+ serviceAliases :: [ServiceName], -- aliases+ servicePort :: PortNumber, -- Port Number ( network byte order )+ serviceProtocol :: ProtocolName -- Protocol+ } deriving (Show, Typeable)++instance Storable ServiceEntry where+ sizeOf _ = #const sizeof(struct servent)+ alignment _ = alignment (undefined :: CInt) -- ???++ peek p = do+ s_name <- (#peek struct servent, s_name) p >>= peekCString+ s_aliases <- (#peek struct servent, s_aliases) p+ >>= peekArray0 nullPtr+ >>= mapM peekCString+ s_port <- (#peek struct servent, s_port) p+ s_proto <- (#peek struct servent, s_proto) p >>= peekCString+ return (ServiceEntry {+ serviceName = s_name,+ serviceAliases = s_aliases,+#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+ servicePort = PortNum (fromIntegral (s_port :: CShort)),+#else+ -- s_port is already in network byte order, but it+ -- might be the wrong size.+ servicePort = PortNum (fromIntegral (s_port :: CInt)),+#endif+ serviceProtocol = s_proto+ })++ poke _p = error "Storable.poke(BSD.ServiceEntry) not implemented"+++-- | Get service by name.+getServiceByName :: ServiceName -- Service Name+ -> ProtocolName -- Protocol Name+ -> IO ServiceEntry -- Service Entry+getServiceByName name proto = withLock $ do+ withCString name $ \ cstr_name -> do+ withCString proto $ \ cstr_proto -> do+ throwNoSuchThingIfNull "getServiceByName" "no such service entry"+ $ (trySysCall (c_getservbyname cstr_name cstr_proto))+ >>= peek++foreign import CALLCONV unsafe "getservbyname" + c_getservbyname :: CString -> CString -> IO (Ptr ServiceEntry)++-- | Get the service given a 'PortNumber' and 'ProtocolName'.+getServiceByPort :: PortNumber -> ProtocolName -> IO ServiceEntry+getServiceByPort (PortNum port) proto = withLock $ do+ withCString proto $ \ cstr_proto -> do+ throwNoSuchThingIfNull "getServiceByPort" "no such service entry"+ $ (trySysCall (c_getservbyport (fromIntegral port) cstr_proto))+ >>= peek++foreign import CALLCONV unsafe "getservbyport" + c_getservbyport :: CInt -> CString -> IO (Ptr ServiceEntry)++-- | Get the 'PortNumber' corresponding to the 'ServiceName'.+getServicePortNumber :: ServiceName -> IO PortNumber+getServicePortNumber name = do+ (ServiceEntry _ _ port _) <- getServiceByName name "tcp"+ return port++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+getServiceEntry :: IO ServiceEntry+getServiceEntry = withLock $ do+ throwNoSuchThingIfNull "getServiceEntry" "no such service entry"+ $ trySysCall c_getservent+ >>= peek++foreign import ccall unsafe "getservent" c_getservent :: IO (Ptr ServiceEntry)++setServiceEntry :: Bool -> IO ()+setServiceEntry flg = withLock $ trySysCall $ c_setservent (fromBool flg)++foreign import ccall unsafe "setservent" c_setservent :: CInt -> IO ()++endServiceEntry :: IO ()+endServiceEntry = withLock $ trySysCall $ c_endservent++foreign import ccall unsafe "endservent" c_endservent :: IO ()++getServiceEntries :: Bool -> IO [ServiceEntry]+getServiceEntries stayOpen = do+ setServiceEntry stayOpen+ getEntries (getServiceEntry) (endServiceEntry)+#endif++-- ---------------------------------------------------------------------------+-- Protocol Entries++-- The following relate directly to the corresponding UNIX C+-- calls for returning the protocol entries. The protocol entry is+-- represented by the Haskell type ProtocolEntry.++-- As for setServiceEntry above, calling setProtocolEntry.+-- determines whether or not the protocol database file, usually+-- @/etc/protocols@, is to be kept open between calls of+-- getProtocolEntry. Similarly, ++data ProtocolEntry = + ProtocolEntry {+ protoName :: ProtocolName, -- Official Name+ protoAliases :: [ProtocolName], -- aliases+ protoNumber :: ProtocolNumber -- Protocol Number+ } deriving (Read, Show, Typeable)++instance Storable ProtocolEntry where+ sizeOf _ = #const sizeof(struct protoent)+ alignment _ = alignment (undefined :: CInt) -- ???++ peek p = do+ p_name <- (#peek struct protoent, p_name) p >>= peekCString+ p_aliases <- (#peek struct protoent, p_aliases) p+ >>= peekArray0 nullPtr+ >>= mapM peekCString+#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+ -- With WinSock, the protocol number is only a short;+ -- hoist it in as such, but represent it on the Haskell side+ -- as a CInt.+ p_proto_short <- (#peek struct protoent, p_proto) p + let p_proto = fromIntegral (p_proto_short :: CShort)+#else+ p_proto <- (#peek struct protoent, p_proto) p +#endif+ return (ProtocolEntry { + protoName = p_name,+ protoAliases = p_aliases,+ protoNumber = p_proto+ })++ poke _p = error "Storable.poke(BSD.ProtocolEntry) not implemented"++getProtocolByName :: ProtocolName -> IO ProtocolEntry+getProtocolByName name = withLock $ do+ withCString name $ \ name_cstr -> do+ throwNoSuchThingIfNull "getProtocolByName" ("no such protocol name: " ++ name)+ $ (trySysCall.c_getprotobyname) name_cstr+ >>= peek++foreign import CALLCONV unsafe "getprotobyname" + c_getprotobyname :: CString -> IO (Ptr ProtocolEntry)+++getProtocolByNumber :: ProtocolNumber -> IO ProtocolEntry+getProtocolByNumber num = withLock $ do+ throwNoSuchThingIfNull "getProtocolByNumber" ("no such protocol number: " ++ show num)+ $ (trySysCall.c_getprotobynumber) (fromIntegral num)+ >>= peek++foreign import CALLCONV unsafe "getprotobynumber"+ c_getprotobynumber :: CInt -> IO (Ptr ProtocolEntry)+++getProtocolNumber :: ProtocolName -> IO ProtocolNumber+getProtocolNumber proto = do+ (ProtocolEntry _ _ num) <- getProtocolByName proto+ return num++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+getProtocolEntry :: IO ProtocolEntry -- Next Protocol Entry from DB+getProtocolEntry = withLock $ do+ ent <- throwNoSuchThingIfNull "getProtocolEntry" "no such protocol entry"+ $ trySysCall c_getprotoent+ peek ent++foreign import ccall unsafe "getprotoent" c_getprotoent :: IO (Ptr ProtocolEntry)++setProtocolEntry :: Bool -> IO () -- Keep DB Open ?+setProtocolEntry flg = withLock $ trySysCall $ c_setprotoent (fromBool flg)++foreign import ccall unsafe "setprotoent" c_setprotoent :: CInt -> IO ()++endProtocolEntry :: IO ()+endProtocolEntry = withLock $ trySysCall $ c_endprotoent++foreign import ccall unsafe "endprotoent" c_endprotoent :: IO ()++getProtocolEntries :: Bool -> IO [ProtocolEntry]+getProtocolEntries stayOpen = withLock $ do+ setProtocolEntry stayOpen+ getEntries (getProtocolEntry) (endProtocolEntry)+#endif++-- ---------------------------------------------------------------------------+-- Host lookups++data HostEntry = + HostEntry {+ hostName :: HostName, -- Official Name+ hostAliases :: [HostName], -- aliases+ hostFamily :: Family, -- Host Type (currently AF_INET)+ hostAddresses :: [HostAddress] -- Set of Network Addresses (in network byte order)+ } deriving (Read, Show, Typeable)++instance Storable HostEntry where+ sizeOf _ = #const sizeof(struct hostent)+ alignment _ = alignment (undefined :: CInt) -- ???++ peek p = do+ h_name <- (#peek struct hostent, h_name) p >>= peekCString+ h_aliases <- (#peek struct hostent, h_aliases) p+ >>= peekArray0 nullPtr+ >>= mapM peekCString+ h_addrtype <- (#peek struct hostent, h_addrtype) p+ -- h_length <- (#peek struct hostent, h_length) p+ h_addr_list <- (#peek struct hostent, h_addr_list) p+ >>= peekArray0 nullPtr+ >>= mapM peek+ return (HostEntry {+ hostName = h_name,+ hostAliases = h_aliases,+#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+ hostFamily = unpackFamily (fromIntegral (h_addrtype :: CShort)),+#else+ hostFamily = unpackFamily h_addrtype,+#endif+ hostAddresses = h_addr_list+ })++ poke _p = error "Storable.poke(BSD.ServiceEntry) not implemented"+++-- convenience function:+hostAddress :: HostEntry -> HostAddress+hostAddress (HostEntry nm _ _ ls) =+ case ls of+ [] -> error ("BSD.hostAddress: empty network address list for " ++ nm)+ (x:_) -> x++-- getHostByName must use the same lock as the *hostent functions+-- may cause problems if called concurrently.++-- | Resolve a 'HostName' to IPv4 address.+getHostByName :: HostName -> IO HostEntry+getHostByName name = withLock $ do+ withCString name $ \ name_cstr -> do+ ent <- throwNoSuchThingIfNull "getHostByName" "no such host entry"+ $ trySysCall $ c_gethostbyname name_cstr+ peek ent++foreign import CALLCONV safe "gethostbyname" + c_gethostbyname :: CString -> IO (Ptr HostEntry)+++-- The locking of gethostbyaddr is similar to gethostbyname.+-- | Get a 'HostEntry' corresponding to the given address and family.+-- Note that only IPv4 is currently supported.+getHostByAddr :: Family -> HostAddress -> IO HostEntry+getHostByAddr family addr = do+ with addr $ \ ptr_addr -> withLock $ do+ throwNoSuchThingIfNull "getHostByAddr" "no such host entry"+ $ trySysCall $ c_gethostbyaddr ptr_addr (fromIntegral (sizeOf addr)) (packFamily family)+ >>= peek++foreign import CALLCONV safe "gethostbyaddr"+ c_gethostbyaddr :: Ptr HostAddress -> CInt -> CInt -> IO (Ptr HostEntry)++#if defined(HAVE_GETHOSTENT) && !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+getHostEntry :: IO HostEntry+getHostEntry = withLock $ do+ throwNoSuchThingIfNull "getHostEntry" "unable to retrieve host entry"+ $ trySysCall $ c_gethostent+ >>= peek++foreign import ccall unsafe "gethostent" c_gethostent :: IO (Ptr HostEntry)++setHostEntry :: Bool -> IO ()+setHostEntry flg = withLock $ trySysCall $ c_sethostent (fromBool flg)++foreign import ccall unsafe "sethostent" c_sethostent :: CInt -> IO ()++endHostEntry :: IO ()+endHostEntry = withLock $ c_endhostent++foreign import ccall unsafe "endhostent" c_endhostent :: IO ()++getHostEntries :: Bool -> IO [HostEntry]+getHostEntries stayOpen = do+ setHostEntry stayOpen+ getEntries (getHostEntry) (endHostEntry)+#endif++-- ---------------------------------------------------------------------------+-- Accessing network information++-- Same set of access functions as for accessing host,protocol and+-- service system info, this time for the types of networks supported.++-- network addresses are represented in host byte order.+type NetworkAddr = CULong++type NetworkName = String++data NetworkEntry =+ NetworkEntry {+ networkName :: NetworkName, -- official name+ networkAliases :: [NetworkName], -- aliases+ networkFamily :: Family, -- type+ networkAddress :: NetworkAddr+ } deriving (Read, Show, Typeable)++instance Storable NetworkEntry where+ sizeOf _ = #const sizeof(struct hostent)+ alignment _ = alignment (undefined :: CInt) -- ???++ peek p = do+ n_name <- (#peek struct netent, n_name) p >>= peekCString+ n_aliases <- (#peek struct netent, n_aliases) p+ >>= peekArray0 nullPtr+ >>= mapM peekCString+ n_addrtype <- (#peek struct netent, n_addrtype) p+ n_net <- (#peek struct netent, n_net) p+ return (NetworkEntry {+ networkName = n_name,+ networkAliases = n_aliases,+ networkFamily = unpackFamily (fromIntegral + (n_addrtype :: CInt)),+ networkAddress = n_net+ })++ poke _p = error "Storable.poke(BSD.NetEntry) not implemented"+++#if !defined(cygwin32_HOST_OS) && !defined(mingw32_HOST_OS) && !defined(_WIN32)+getNetworkByName :: NetworkName -> IO NetworkEntry+getNetworkByName name = withLock $ do+ withCString name $ \ name_cstr -> do+ throwNoSuchThingIfNull "getNetworkByName" "no such network entry"+ $ trySysCall $ c_getnetbyname name_cstr+ >>= peek++foreign import ccall unsafe "getnetbyname" + c_getnetbyname :: CString -> IO (Ptr NetworkEntry)++getNetworkByAddr :: NetworkAddr -> Family -> IO NetworkEntry+getNetworkByAddr addr family = withLock $ do+ throwNoSuchThingIfNull "getNetworkByAddr" "no such network entry"+ $ trySysCall $ c_getnetbyaddr addr (packFamily family)+ >>= peek++foreign import ccall unsafe "getnetbyaddr" + c_getnetbyaddr :: NetworkAddr -> CInt -> IO (Ptr NetworkEntry)++getNetworkEntry :: IO NetworkEntry+getNetworkEntry = withLock $ do+ throwNoSuchThingIfNull "getNetworkEntry" "no more network entries"+ $ trySysCall $ c_getnetent+ >>= peek++foreign import ccall unsafe "getnetent" c_getnetent :: IO (Ptr NetworkEntry)++-- | Open the network name database. The parameter specifies+-- whether a connection is maintained open between various+-- networkEntry calls+setNetworkEntry :: Bool -> IO ()+setNetworkEntry flg = withLock $ trySysCall $ c_setnetent (fromBool flg)++foreign import ccall unsafe "setnetent" c_setnetent :: CInt -> IO ()++-- | Close the connection to the network name database.+endNetworkEntry :: IO ()+endNetworkEntry = withLock $ trySysCall $ c_endnetent++foreign import ccall unsafe "endnetent" c_endnetent :: IO ()++-- | Get the list of network entries.+getNetworkEntries :: Bool -> IO [NetworkEntry]+getNetworkEntries stayOpen = do+ setNetworkEntry stayOpen+ getEntries (getNetworkEntry) (endNetworkEntry)+#endif++-- Mutex for name service lockdown++{-# NOINLINE lock #-}+lock :: MVar ()+lock = unsafePerformIO $ newMVar ()++withLock :: IO a -> IO a+withLock act = withMVar lock (\_ -> act)++-- ---------------------------------------------------------------------------+-- Miscellaneous Functions++-- | Calling getHostName returns the standard host name for the current+-- processor, as set at boot time.++getHostName :: IO HostName+getHostName = do+ let size = 256+ allocaArray0 size $ \ cstr -> do+ throwSocketErrorIfMinus1_ "getHostName" $ c_gethostname cstr (fromIntegral size)+ peekCString cstr++foreign import CALLCONV unsafe "gethostname" + c_gethostname :: CString -> CSize -> IO CInt++-- Helper function used by the exported functions that provides a+-- Haskellised view of the enumerator functions:++getEntries :: IO a -- read+ -> IO () -- at end+ -> IO [a]+getEntries getOne atEnd = loop+ where+ loop = do+ vv <- catch (liftM Just getOne)+ (\ e -> let _types = e :: IOException in return Nothing)+ case vv of+ Nothing -> return []+ Just v -> loop >>= \ vs -> atEnd >> return (v:vs)+++-- ---------------------------------------------------------------------------+-- Winsock only:+-- The BSD API networking calls made locally return NULL upon failure.+-- That failure may very well be due to WinSock not being initialised,+-- so if NULL is seen try init'ing and repeat the call.+#if !defined(mingw32_HOST_OS) && !defined(_WIN32)+trySysCall :: IO a -> IO a+trySysCall act = act+#else+trySysCall :: IO (Ptr a) -> IO (Ptr a)+trySysCall act = do+ ptr <- act+ if (ptr == nullPtr)+ then withSocketsDo act+ else return ptr+#endif++throwNoSuchThingIfNull :: String -> String -> IO (Ptr a) -> IO (Ptr a)+throwNoSuchThingIfNull loc desc act = do+ ptr <- act+ if (ptr == nullPtr)+ then ioError (ioeSetErrorString (mkIOError NoSuchThing loc Nothing Nothing) desc)+ else return ptr
+ Network/Browser.hs view
@@ -0,0 +1,1062 @@+{-# LANGUAGE MultiParamTypeClasses, GeneralizedNewtypeDeriving, CPP #-}+{- |++Module : Network.Browser+Copyright : See LICENSE file+License : BSD+ +Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+Stability : experimental+Portability : non-portable (not tested)++Session-level interactions over HTTP.+ +The "Network.Browser" goes beyond the basic "Network.HTTP" functionality in +providing support for more involved, and real, request/response interactions over +HTTP. Additional features supported are:++* HTTP Authentication handling++* Transparent handling of redirects++* Cookie stores + transmission.++* Transaction logging++* Proxy-mediated connections.++Example use:++> do+> (_, rsp)+> <- Network.Browser.browse $ do+> setAllowRedirects True -- handle HTTP redirects+> request $ getRequest "http://www.haskell.org/"+> return (take 100 (rspBody rsp))+ +-}+module Network.Browser + ( BrowserState+ , BrowserAction -- browser monad, effectively a state monad.+ , Proxy(..)+ + , browse -- :: BrowserAction a -> IO a+ , request -- :: Request -> BrowserAction Response+ + , getBrowserState -- :: BrowserAction t (BrowserState t)+ , withBrowserState -- :: BrowserState t -> BrowserAction t a -> BrowserAction t a+ + , setAllowRedirects -- :: Bool -> BrowserAction t ()+ , getAllowRedirects -- :: BrowserAction t Bool++ , setMaxRedirects -- :: Int -> BrowserAction t ()+ , getMaxRedirects -- :: BrowserAction t (Maybe Int)+ + , Authority(..)+ , getAuthorities+ , setAuthorities+ , addAuthority+ , Challenge(..)+ , Qop(..)+ , Algorithm(..)+ + , getAuthorityGen+ , setAuthorityGen+ , setAllowBasicAuth+ , getAllowBasicAuth+ + , setMaxErrorRetries -- :: Maybe Int -> BrowserAction t ()+ , getMaxErrorRetries -- :: BrowserAction t (Maybe Int)++ , setMaxPoolSize -- :: Int -> BrowserAction t ()+ , getMaxPoolSize -- :: BrowserAction t (Maybe Int)++ , setMaxAuthAttempts -- :: Maybe Int -> BrowserAction t ()+ , getMaxAuthAttempts -- :: BrowserAction t (Maybe Int)++ , setCookieFilter -- :: (URI -> Cookie -> IO Bool) -> BrowserAction t ()+ , getCookieFilter -- :: BrowserAction t (URI -> Cookie -> IO Bool)+ , defaultCookieFilter -- :: URI -> Cookie -> IO Bool+ , userCookieFilter -- :: URI -> Cookie -> IO Bool+ + , Cookie(..)+ , getCookies -- :: BrowserAction t [Cookie]+ , setCookies -- :: [Cookie] -> BrowserAction t ()+ , addCookie -- :: Cookie -> BrowserAction t ()+ + , setErrHandler -- :: (String -> IO ()) -> BrowserAction t ()+ , setOutHandler -- :: (String -> IO ()) -> BrowserAction t ()+ + , setEventHandler -- :: (BrowserEvent -> BrowserAction t ()) -> BrowserAction t ()+ + , BrowserEvent(..)+ , BrowserEventType(..)+ , RequestID+ + , setProxy -- :: Proxy -> BrowserAction t ()+ , getProxy -- :: BrowserAction t Proxy++ , setCheckForProxy -- :: Bool -> BrowserAction t ()+ , getCheckForProxy -- :: BrowserAction t Bool++ , setDebugLog -- :: Maybe String -> BrowserAction t ()+ + , getUserAgent -- :: BrowserAction t String+ , setUserAgent -- :: String -> BrowserAction t ()+ + , out -- :: String -> BrowserAction t ()+ , err -- :: String -> BrowserAction t ()+ , ioAction -- :: IO a -> BrowserAction a++ , defaultGETRequest+ , defaultGETRequest_+ + , formToRequest+ , uriDefaultTo+ + -- old and half-baked; don't use:+ , Form(..)+ , FormVar+ ) where++import Network.URI+ ( URI(..)+ , URIAuth(..)+ , parseURI, parseURIReference, relativeTo+ )+import Network.StreamDebugger (debugByteStream)+import Network.HTTP hiding ( sendHTTP_notify )+import Network.HTTP.HandleStream ( sendHTTP_notify )+import Network.HTTP.Auth+import Network.HTTP.Cookie+import Network.HTTP.Proxy++import Network.Stream ( ConnError(..), Result )+import Network.BufferType++import Data.Char (toLower)+import Data.List (isPrefixOf)+import Data.Maybe (fromMaybe, listToMaybe, catMaybes )+import Control.Applicative (Applicative (..), (<$>))+#ifdef MTL1+import Control.Monad (filterM, when, ap)+#else+import Control.Monad (filterM, when)+#endif+import Control.Monad.State (StateT (..), MonadIO (..), modify, gets, withStateT, evalStateT, MonadState (..))++import qualified System.IO+ ( hSetBuffering, hPutStr, stdout, stdin, hGetChar+ , BufferMode(NoBuffering, LineBuffering)+ )+import System.Time ( ClockTime, getClockTime )+++------------------------------------------------------------------+----------------------- Cookie Stuff -----------------------------+------------------------------------------------------------------++-- | @defaultCookieFilter@ is the initial cookie acceptance filter.+-- It welcomes them all into the store @:-)@+defaultCookieFilter :: URI -> Cookie -> IO Bool+defaultCookieFilter _url _cky = return True++-- | @userCookieFilter@ is a handy acceptance filter, asking the+-- user if he/she is willing to accept an incoming cookie before+-- adding it to the store.+userCookieFilter :: URI -> Cookie -> IO Bool+userCookieFilter url cky = do+ do putStrLn ("Set-Cookie received when requesting: " ++ show url)+ case ckComment cky of+ Nothing -> return ()+ Just x -> putStrLn ("Cookie Comment:\n" ++ x)+ let pth = maybe "" ('/':) (ckPath cky)+ putStrLn ("Domain/Path: " ++ ckDomain cky ++ pth)+ putStrLn (ckName cky ++ '=' : ckValue cky)+ System.IO.hSetBuffering System.IO.stdout System.IO.NoBuffering+ System.IO.hSetBuffering System.IO.stdin System.IO.NoBuffering+ System.IO.hPutStr System.IO.stdout "Accept [y/n]? "+ x <- System.IO.hGetChar System.IO.stdin+ System.IO.hSetBuffering System.IO.stdin System.IO.LineBuffering+ System.IO.hSetBuffering System.IO.stdout System.IO.LineBuffering+ return (toLower x == 'y')++-- | @addCookie c@ adds a cookie to the browser state, removing duplicates.+addCookie :: Cookie -> BrowserAction t ()+addCookie c = modify (\b -> b{bsCookies = c : filter (/=c) (bsCookies b) })++-- | @setCookies cookies@ replaces the set of cookies known to+-- the browser to @cookies@. Useful when wanting to restore cookies+-- used across 'browse' invocations.+setCookies :: [Cookie] -> BrowserAction t ()+setCookies cs = modify (\b -> b { bsCookies=cs })++-- | @getCookies@ returns the current set of cookies known to+-- the browser.+getCookies :: BrowserAction t [Cookie]+getCookies = gets bsCookies++-- ...get domain specific cookies...+-- ... this needs changing for consistency with rfc2109...+-- ... currently too broad.+getCookiesFor :: String -> String -> BrowserAction t [Cookie]+getCookiesFor dom path =+ do cks <- getCookies+ return (filter cookiematch cks)+ where+ cookiematch :: Cookie -> Bool+ cookiematch = cookieMatch (dom,path)+ ++-- | @setCookieFilter fn@ sets the cookie acceptance filter to @fn@.+setCookieFilter :: (URI -> Cookie -> IO Bool) -> BrowserAction t ()+setCookieFilter f = modify (\b -> b { bsCookieFilter=f })++-- | @getCookieFilter@ returns the current cookie acceptance filter.+getCookieFilter :: BrowserAction t (URI -> Cookie -> IO Bool)+getCookieFilter = gets bsCookieFilter++------------------------------------------------------------------+----------------------- Authorisation Stuff ----------------------+------------------------------------------------------------------++{-++The browser handles 401 responses in the following manner:+ 1) extract all WWW-Authenticate headers from a 401 response+ 2) rewrite each as a Challenge object, using "headerToChallenge"+ 3) pick a challenge to respond to, usually the strongest+ challenge understood by the client, using "pickChallenge"+ 4) generate a username/password combination using the browsers+ "bsAuthorityGen" function (the default behaviour is to ask+ the user)+ 5) build an Authority object based upon the challenge and user+ data, store this new Authority in the browser state+ 6) convert the Authority to a request header and add this+ to a request using "withAuthority"+ 7) send the amended request++Note that by default requests are annotated with authority headers+before the first sending, based upon previously generated Authority+objects (which contain domain information). Once a specific authority+is added to a rejected request this predictive annotation is suppressed.++407 responses are handled in a similar manner, except+ a) Authorities are not collected, only a single proxy authority+ is kept by the browser+ b) If the proxy used by the browser (type Proxy) is NoProxy, then+ a 407 response will generate output on the "err" stream and+ the response will be returned.+++Notes:+ - digest authentication so far ignores qop, so fails to authenticate + properly with qop=auth-int challenges+ - calculates a1 more than necessary+ - doesn't reverse authenticate+ - doesn't properly receive AuthenticationInfo headers, so fails+ to use next-nonce etc++-}++-- | Return authorities for a given domain and path.+-- Assumes "dom" is lower case+getAuthFor :: String -> String -> BrowserAction t [Authority]+getAuthFor dom pth = getAuthorities >>= return . (filter match)+ where+ match :: Authority -> Bool+ match au@AuthBasic{} = matchURI (auSite au)+ match au@AuthDigest{} = or (map matchURI (auDomain au))++ matchURI :: URI -> Bool+ matchURI s = (uriToAuthorityString s == dom) && (uriPath s `isPrefixOf` pth)+ ++-- | @getAuthorities@ return the current set of @Authority@s known+-- to the browser.+getAuthorities :: BrowserAction t [Authority]+getAuthorities = gets bsAuthorities++-- @setAuthorities as@ replaces the Browser's known set+-- of 'Authority's to @as@.+setAuthorities :: [Authority] -> BrowserAction t ()+setAuthorities as = modify (\b -> b { bsAuthorities=as })++-- @addAuthority a@ adds 'Authority' @a@ to the Browser's+-- set of known authorities.+addAuthority :: Authority -> BrowserAction t ()+addAuthority a = modify (\b -> b { bsAuthorities=a:bsAuthorities b })++-- | @getAuthorityGen@ returns the current authority generator+getAuthorityGen :: BrowserAction t (URI -> String -> IO (Maybe (String,String)))+getAuthorityGen = gets bsAuthorityGen++-- | @setAuthorityGen genAct@ sets the auth generator to @genAct@.+setAuthorityGen :: (URI -> String -> IO (Maybe (String,String))) -> BrowserAction t ()+setAuthorityGen f = modify (\b -> b { bsAuthorityGen=f })++-- | @setAllowBasicAuth onOff@ enables\/disables HTTP Basic Authentication.+setAllowBasicAuth :: Bool -> BrowserAction t ()+setAllowBasicAuth ba = modify (\b -> b { bsAllowBasicAuth=ba })++getAllowBasicAuth :: BrowserAction t Bool+getAllowBasicAuth = gets bsAllowBasicAuth++-- | @setMaxAuthAttempts mbMax@ sets the maximum number of authentication attempts+-- to do. If @Nothing@, rever to default max.+setMaxAuthAttempts :: Maybe Int -> BrowserAction t ()+setMaxAuthAttempts mb + | fromMaybe 0 mb < 0 = return ()+ | otherwise = modify (\ b -> b{bsMaxAuthAttempts=mb})++-- | @getMaxAuthAttempts@ returns the current max auth attempts. If @Nothing@,+-- the browser's default is used.+getMaxAuthAttempts :: BrowserAction t (Maybe Int)+getMaxAuthAttempts = gets bsMaxAuthAttempts++-- | @setMaxErrorRetries mbMax@ sets the maximum number of attempts at+-- transmitting a request. If @Nothing@, rever to default max.+setMaxErrorRetries :: Maybe Int -> BrowserAction t ()+setMaxErrorRetries mb+ | fromMaybe 0 mb < 0 = return ()+ | otherwise = modify (\ b -> b{bsMaxErrorRetries=mb})++-- | @getMaxErrorRetries@ returns the current max number of error retries.+getMaxErrorRetries :: BrowserAction t (Maybe Int)+getMaxErrorRetries = gets bsMaxErrorRetries++-- TO BE CHANGED!!!+pickChallenge :: Bool -> [Challenge] -> Maybe Challenge+pickChallenge allowBasic []+ | allowBasic = Just (ChalBasic "/") -- manufacture a challenge if one missing; more robust.+pickChallenge _ ls = listToMaybe ls++-- | Retrieve a likely looking authority for a Request.+anticipateChallenge :: Request ty -> BrowserAction t (Maybe Authority)+anticipateChallenge rq =+ let uri = rqURI rq in+ do { authlist <- getAuthFor (uriAuthToString $ reqURIAuth rq) (uriPath uri)+ ; return (listToMaybe authlist)+ }++-- | Asking the user to respond to a challenge+challengeToAuthority :: URI -> Challenge -> BrowserAction t (Maybe Authority)+challengeToAuthority uri ch+ | not (answerable ch) = return Nothing+ | otherwise = do+ -- prompt user for authority+ prompt <- getAuthorityGen+ userdetails <- liftIO $ prompt uri (chRealm ch)+ case userdetails of+ Nothing -> return Nothing+ Just (u,p) -> return (Just $ buildAuth ch u p)+ where+ answerable :: Challenge -> Bool+ answerable ChalBasic{} = True+ answerable chall = (chAlgorithm chall) == Just AlgMD5++ buildAuth :: Challenge -> String -> String -> Authority+ buildAuth (ChalBasic r) u p = + AuthBasic { auSite=uri+ , auRealm=r+ , auUsername=u+ , auPassword=p+ }++ -- note to self: this is a pretty stupid operation+ -- to perform isn't it? ChalX and AuthX are so very+ -- similar.+ buildAuth (ChalDigest r d n o _stale a q) u p =+ AuthDigest { auRealm=r+ , auUsername=u+ , auPassword=p+ , auDomain=d+ , auNonce=n+ , auOpaque=o+ , auAlgorithm=a+ , auQop=q+ }+++------------------------------------------------------------------+------------------ Browser State Actions -------------------------+------------------------------------------------------------------+++-- | @BrowserState@ is the (large) record type tracking the current+-- settings of the browser.+data BrowserState connection+ = BS { bsErr, bsOut :: String -> IO ()+ , bsCookies :: [Cookie]+ , bsCookieFilter :: URI -> Cookie -> IO Bool+ , bsAuthorityGen :: URI -> String -> IO (Maybe (String,String))+ , bsAuthorities :: [Authority]+ , bsAllowRedirects :: Bool+ , bsAllowBasicAuth :: Bool+ , bsMaxRedirects :: Maybe Int+ , bsMaxErrorRetries :: Maybe Int+ , bsMaxAuthAttempts :: Maybe Int+ , bsMaxPoolSize :: Maybe Int+ , bsConnectionPool :: [connection]+ , bsCheckProxy :: Bool+ , bsProxy :: Proxy+ , bsDebug :: Maybe String+ , bsEvent :: Maybe (BrowserEvent -> BrowserAction connection ())+ , bsRequestID :: RequestID+ , bsUserAgent :: Maybe String+ }++instance Show (BrowserState t) where+ show bs = "BrowserState { " + ++ shows (bsCookies bs) ("\n"+ {- ++ show (bsAuthorities bs) ++ "\n"-}+ ++ "AllowRedirects: " ++ shows (bsAllowRedirects bs) "} ")++-- | @BrowserAction@ is the IO monad, but carrying along a 'BrowserState'.+newtype BrowserAction conn a+ = BA { unBA :: StateT (BrowserState conn) IO a }+#ifdef MTL1+ deriving (Functor, Monad, MonadIO, MonadState (BrowserState conn))++instance Applicative (BrowserAction conn) where+ pure = return+ (<*>) = ap+#else+ deriving (Functor, Applicative, Monad, MonadIO, MonadState (BrowserState conn))+#endif++runBA :: BrowserState conn -> BrowserAction conn a -> IO a+runBA bs = flip evalStateT bs . unBA++-- | @browse act@ is the toplevel action to perform a 'BrowserAction'.+-- Example use: @browse (request (getRequest yourURL))@.+browse :: BrowserAction conn a -> IO a+browse = runBA defaultBrowserState++-- | The default browser state has the settings +defaultBrowserState :: BrowserState t+defaultBrowserState = res+ where+ res = BS+ { bsErr = putStrLn+ , bsOut = putStrLn+ , bsCookies = []+ , bsCookieFilter = defaultCookieFilter+ , bsAuthorityGen = \ _uri _realm -> do+ bsErr res "No action for prompting/generating user+password credentials provided (use: setAuthorityGen); returning Nothing"+ return Nothing+ , bsAuthorities = []+ , bsAllowRedirects = True+ , bsAllowBasicAuth = False+ , bsMaxRedirects = Nothing+ , bsMaxErrorRetries = Nothing+ , bsMaxAuthAttempts = Nothing+ , bsMaxPoolSize = Nothing+ , bsConnectionPool = []+ , bsCheckProxy = defaultAutoProxyDetect+ , bsProxy = noProxy+ , bsDebug = Nothing + , bsEvent = Nothing+ , bsRequestID = 0+ , bsUserAgent = Nothing+ }++{-# DEPRECATED getBrowserState "Use Control.Monad.State.get instead." #-}+-- | @getBrowserState@ returns the current browser config. Useful+-- for restoring state across 'BrowserAction's.+getBrowserState :: BrowserAction t (BrowserState t)+getBrowserState = get++-- | @withBrowserAction st act@ performs @act@ with 'BrowserState' @st@.+withBrowserState :: BrowserState t -> BrowserAction t a -> BrowserAction t a+withBrowserState bs = BA . withStateT (const bs) . unBA++-- | @nextRequest act@ performs the browser action @act@ as+-- the next request, i.e., setting up a new request context+-- before doing so.+nextRequest :: BrowserAction t a -> BrowserAction t a+nextRequest act = do+ let updReqID st = + let + rid = succ (bsRequestID st)+ in+ rid `seq` st{bsRequestID=rid}+ modify updReqID+ act++-- | Lifts an IO action into the 'BrowserAction' monad.+{-# DEPRECATED ioAction "Use Control.Monad.Trans.liftIO instead." #-}+ioAction :: IO a -> BrowserAction t a+ioAction = liftIO++-- | @setErrHandler@ sets the IO action to call when+-- the browser reports running errors. To disable any+-- such, set it to @const (return ())@.+setErrHandler :: (String -> IO ()) -> BrowserAction t ()+setErrHandler h = modify (\b -> b { bsErr=h })++-- | @setErrHandler@ sets the IO action to call when+-- the browser chatters info on its running. To disable any+-- such, set it to @const (return ())@.+setOutHandler :: (String -> IO ()) -> BrowserAction t ()+setOutHandler h = modify (\b -> b { bsOut=h })++out, err :: String -> BrowserAction t ()+out s = do { f <- gets bsOut ; liftIO $ f s }+err s = do { f <- gets bsErr ; liftIO $ f s }++-- | @setAllowRedirects onOff@ toggles the willingness to+-- follow redirects (HTTP responses with 3xx status codes).+setAllowRedirects :: Bool -> BrowserAction t ()+setAllowRedirects bl = modify (\b -> b {bsAllowRedirects=bl})++-- | @getAllowRedirects@ returns current setting of the do-chase-redirects flag.+getAllowRedirects :: BrowserAction t Bool+getAllowRedirects = gets bsAllowRedirects++-- | @setMaxRedirects maxCount@ sets the maxiumum number of forwarding hops+-- we are willing to jump through. A no-op if the count is negative; if zero,+-- the max is set to whatever default applies. Notice that setting the max+-- redirects count does /not/ enable following of redirects itself; use+-- 'setAllowRedirects' to do so.+setMaxRedirects :: Maybe Int -> BrowserAction t ()+setMaxRedirects c + | fromMaybe 0 c < 0 = return ()+ | otherwise = modify (\b -> b{bsMaxRedirects=c})++-- | @getMaxRedirects@ returns the current setting for the max-redirect count.+-- If @Nothing@, the "Network.Browser"'s default is used.+getMaxRedirects :: BrowserAction t (Maybe Int)+getMaxRedirects = gets bsMaxRedirects++-- | @setMaxPoolSize maxCount@ sets the maximum size of the connection pool+-- that is used to cache connections between requests+setMaxPoolSize :: Maybe Int -> BrowserAction t ()+setMaxPoolSize c = modify (\b -> b{bsMaxPoolSize=c})++-- | @getMaxPoolSize@ gets the maximum size of the connection pool+-- that is used to cache connections between requests.+-- If @Nothing@, the "Network.Browser"'s default is used.+getMaxPoolSize :: BrowserAction t (Maybe Int)+getMaxPoolSize = gets bsMaxPoolSize++-- | @setProxy p@ will disable proxy usage if @p@ is @NoProxy@.+-- If @p@ is @Proxy proxyURL mbAuth@, then @proxyURL@ is interpreted+-- as the URL of the proxy to use, possibly authenticating via +-- 'Authority' information in @mbAuth@.+setProxy :: Proxy -> BrowserAction t ()+setProxy p =+ -- Note: if user _explicitly_ sets the proxy, we turn+ -- off any auto-detection of proxies.+ modify (\b -> b {bsProxy = p, bsCheckProxy=False})++-- | @getProxy@ returns the current proxy settings. If+-- the auto-proxy flag is set to @True@, @getProxy@ will+-- perform the necessary +getProxy :: BrowserAction t Proxy+getProxy = do+ p <- gets bsProxy+ case p of+ -- Note: if there is a proxy, no need to perform any auto-detect.+ -- Presumably this is the user's explicit and preferred proxy server.+ Proxy{} -> return p+ NoProxy{} -> do+ flg <- gets bsCheckProxy+ if not flg+ then return p + else do+ np <- liftIO $ fetchProxy True{-issue warning on stderr if ill-formed...-}+ -- note: this resets the check-proxy flag; a one-off affair.+ setProxy np+ return np++-- | @setCheckForProxy flg@ sets the one-time check for proxy+-- flag to @flg@. If @True@, the session will try to determine+-- the proxy server is locally configured. See 'Network.HTTP.Proxy.fetchProxy'+-- for details of how this done.+setCheckForProxy :: Bool -> BrowserAction t ()+setCheckForProxy flg = modify (\ b -> b{bsCheckProxy=flg})++-- | @getCheckForProxy@ returns the current check-proxy setting.+-- Notice that this may not be equal to @True@ if the session has+-- set it to that via 'setCheckForProxy' and subsequently performed+-- some HTTP protocol interactions. i.e., the flag return represents+-- whether a proxy will be checked for again before any future protocol+-- interactions.+getCheckForProxy :: BrowserAction t Bool+getCheckForProxy = gets bsCheckProxy++-- | @setDebugLog mbFile@ turns off debug logging iff @mbFile@+-- is @Nothing@. If set to @Just fStem@, logs of browser activity+-- is appended to files of the form @fStem-url-authority@, i.e.,+-- @fStem@ is just the prefix for a set of log files, one per host/authority.+setDebugLog :: Maybe String -> BrowserAction t ()+setDebugLog v = modify (\b -> b {bsDebug=v})++-- | @setUserAgent ua@ sets the current @User-Agent:@ string to @ua@. It+-- will be used if no explicit user agent header is found in subsequent requests.+--+-- A common form of user agent string is @\"name\/version (details)\"@. For+-- example @\"cabal-install/0.10.2 (HTTP 4000.1.2)\"@. Including the version+-- of this HTTP package can be helpful if you ever need to track down HTTP+-- compatability quirks. This version is available via 'httpPackageVersion'.+-- For more info see <http://en.wikipedia.org/wiki/User_agent>.+--+setUserAgent :: String -> BrowserAction t ()+setUserAgent ua = modify (\b -> b{bsUserAgent=Just ua})++-- | @getUserAgent@ returns the current @User-Agent:@ default string.+getUserAgent :: BrowserAction t String+getUserAgent = do+ n <- gets bsUserAgent+ return (maybe defaultUserAgent id n)++-- | @RequestState@ is an internal tallying type keeping track of various +-- per-connection counters, like the number of authorization attempts and +-- forwards we've gone through.+data RequestState + = RequestState+ { reqDenies :: Int -- ^ number of 401 responses so far+ , reqRedirects :: Int -- ^ number of redirects so far+ , reqRetries :: Int -- ^ number of retries so far+ , reqStopOnDeny :: Bool -- ^ whether to pre-empt 401 response+ }++type RequestID = Int -- yeah, it will wrap around.++nullRequestState :: RequestState+nullRequestState = RequestState+ { reqDenies = 0+ , reqRedirects = 0+ , reqRetries = 0+ , reqStopOnDeny = True+ }++-- | @BrowserEvent@ is the event record type that a user-defined handler, set+-- via 'setEventHandler', will be passed. It indicates various state changes+-- encountered in the processing of a given 'RequestID', along with timestamps+-- at which they occurred.+data BrowserEvent+ = BrowserEvent+ { browserTimestamp :: ClockTime+ , browserRequestID :: RequestID+ , browserRequestURI :: {-URI-}String+ , browserEventType :: BrowserEventType+ }++-- | 'BrowserEventType' is the enumerated list of events that the browser+-- internals will report to a user-defined event handler.+data BrowserEventType+ = OpenConnection+ | ReuseConnection+ | RequestSent+ | ResponseEnd ResponseData+ | ResponseFinish+{- not yet, you will have to determine these via the ResponseEnd event.+ | Redirect+ | AuthChallenge+ | AuthResponse+-}+ +-- | @setEventHandler onBrowserEvent@ configures event handling.+-- If @onBrowserEvent@ is @Nothing@, event handling is turned off;+-- setting it to @Just onEv@ causes the @onEv@ IO action to be+-- notified of browser events during the processing of a request+-- by the Browser pipeline.+setEventHandler :: Maybe (BrowserEvent -> BrowserAction ty ()) -> BrowserAction ty ()+setEventHandler mbH = modify (\b -> b { bsEvent=mbH})++buildBrowserEvent :: BrowserEventType -> {-URI-}String -> RequestID -> IO BrowserEvent+buildBrowserEvent bt uri reqID = do+ ct <- getClockTime+ return BrowserEvent + { browserTimestamp = ct+ , browserRequestID = reqID+ , browserRequestURI = uri+ , browserEventType = bt+ }++reportEvent :: BrowserEventType -> {-URI-}String -> BrowserAction t ()+reportEvent bt uri = do+ st <- get+ case bsEvent st of+ Nothing -> return ()+ Just evH -> do+ evt <- liftIO $ buildBrowserEvent bt uri (bsRequestID st)+ evH evt -- if it fails, we fail.++-- | The default number of hops we are willing not to go beyond for +-- request forwardings.+defaultMaxRetries :: Int+defaultMaxRetries = 4++-- | The default number of error retries we are willing to perform.+defaultMaxErrorRetries :: Int+defaultMaxErrorRetries = 4++-- | The default maximum HTTP Authentication attempts we will make for+-- a single request.+defaultMaxAuthAttempts :: Int+defaultMaxAuthAttempts = 2++-- | The default setting for auto-proxy detection.+-- You may change this within a session via 'setAutoProxyDetect'.+-- To avoid initial backwards compatibility issues, leave this as @False@.+defaultAutoProxyDetect :: Bool+defaultAutoProxyDetect = False++-- | @request httpRequest@ tries to submit the 'Request' @httpRequest@+-- to some HTTP server (possibly going via a /proxy/, see 'setProxy'.)+-- Upon successful delivery, the URL where the response was fetched from+-- is returned along with the 'Response' itself.+request :: HStream ty+ => Request ty+ -> BrowserAction (HandleStream ty) (URI,Response ty)+request req = nextRequest $ do+ res <- request' nullVal initialState req+ reportEvent ResponseFinish (show (rqURI req))+ case res of+ Right r -> return r+ Left e -> do+ let errStr = ("Network.Browser.request: Error raised " ++ show e)+ err errStr+ fail errStr+ where+ initialState = nullRequestState+ nullVal = buf_empty bufferOps++-- | Internal helper function, explicitly carrying along per-request +-- counts.+request' :: HStream ty+ => ty+ -> RequestState+ -> Request ty+ -> BrowserAction (HandleStream ty) (Result (URI,Response ty))+request' nullVal rqState rq = do+ let uri = rqURI rq+ failHTTPS uri+ let uria = reqURIAuth rq + -- add cookies to request+ cookies <- getCookiesFor (uriAuthToString uria) (uriPath uri)+{- Not for now:+ (case uriUserInfo uria of+ "" -> id+ xs ->+ case chopAtDelim ':' xs of+ (_,[]) -> id+ (usr,pwd) -> withAuth+ AuthBasic{ auUserName = usr+ , auPassword = pwd+ , auRealm = "/"+ , auSite = uri+ }) $ do+-}+ when (not $ null cookies) + (out $ "Adding cookies to request. Cookie names: " ++ unwords (map ckName cookies))+ -- add credentials to request+ rq' <- + if not (reqStopOnDeny rqState) + then return rq + else do + auth <- anticipateChallenge rq+ case auth of+ Nothing -> return rq+ Just x -> return (insertHeader HdrAuthorization (withAuthority x rq) rq)+ let rq'' = if not $ null cookies then insertHeaders [cookiesToHeader cookies] rq' else rq'+ p <- getProxy+ def_ua <- gets bsUserAgent+ let defaultOpts = + case p of + NoProxy -> defaultNormalizeRequestOptions{normUserAgent=def_ua}+ Proxy _ ath ->+ defaultNormalizeRequestOptions+ { normForProxy = True+ , normUserAgent = def_ua+ , normCustoms = + maybe []+ (\ authS -> [\ _ r -> insertHeader HdrProxyAuthorization (withAuthority authS r) r])+ ath+ }+ let final_req = normalizeRequest defaultOpts rq''+ out ("Sending:\n" ++ show final_req)+ e_rsp <- + case p of+ NoProxy -> dorequest (reqURIAuth rq'') final_req+ Proxy str _ath -> do+ let notURI + | null pt || null hst = + URIAuth{ uriUserInfo = ""+ , uriRegName = str+ , uriPort = ""+ }+ | otherwise = + URIAuth{ uriUserInfo = ""+ , uriRegName = hst+ , uriPort = pt+ }+ -- If the ':' is dropped from port below, dorequest will assume port 80. Leave it!+ where (hst, pt) = span (':'/=) str+ -- Proxy can take multiple forms - look for http://host:port first,+ -- then host:port. Fall back to just the string given (probably a host name).+ let proxyURIAuth =+ maybe notURI+ (\parsed -> maybe notURI id (uriAuthority parsed))+ (parseURI str)++ out $ "proxy uri host: " ++ uriRegName proxyURIAuth ++ ", port: " ++ uriPort proxyURIAuth+ dorequest proxyURIAuth final_req+ mbMx <- getMaxErrorRetries+ case e_rsp of+ Left v + | (reqRetries rqState < fromMaybe defaultMaxErrorRetries mbMx) && + (v == ErrorReset || v == ErrorClosed) ->+ request' nullVal rqState{reqRetries=succ (reqRetries rqState)} rq+ | otherwise -> + return (Left v)+ Right rsp -> do + out ("Received:\n" ++ show rsp)+ -- add new cookies to browser state+ handleCookies uri (uriAuthToString $ reqURIAuth rq) + (retrieveHeaders HdrSetCookie rsp)+ mbMxAuths <- getMaxAuthAttempts+ case rspCode rsp of+ (4,0,1) -- Credentials not sent or refused.+ | reqDenies rqState > fromMaybe defaultMaxAuthAttempts mbMxAuths -> do+ out "401 - credentials again refused; exceeded retry count (2)"+ return (Right (uri,rsp))+ | otherwise -> do+ out "401 - credentials not supplied or refused; retrying.."+ let hdrs = retrieveHeaders HdrWWWAuthenticate rsp+ flg <- getAllowBasicAuth+ case pickChallenge flg (catMaybes $ map (headerToChallenge uri) hdrs) of+ Nothing -> do+ out "no challenge"+ return (Right (uri,rsp)) {- do nothing -}+ Just x -> do+ au <- challengeToAuthority uri x+ case au of+ Nothing -> do+ out "no auth"+ return (Right (uri,rsp)) {- do nothing -}+ Just au' -> do+ out "Retrying request with new credentials"+ request' nullVal+ rqState{ reqDenies = succ(reqDenies rqState)+ , reqStopOnDeny = False+ }+ (insertHeader HdrAuthorization (withAuthority au' rq) rq)++ (4,0,7) -- Proxy Authentication required+ | reqDenies rqState > fromMaybe defaultMaxAuthAttempts mbMxAuths -> do+ out "407 - proxy authentication required; max deny count exceeeded (2)"+ return (Right (uri,rsp))+ | otherwise -> do+ out "407 - proxy authentication required"+ let hdrs = retrieveHeaders HdrProxyAuthenticate rsp+ flg <- getAllowBasicAuth+ case pickChallenge flg (catMaybes $ map (headerToChallenge uri) hdrs) of+ Nothing -> return (Right (uri,rsp)) {- do nothing -}+ Just x -> do+ au <- challengeToAuthority uri x+ case au of+ Nothing -> return (Right (uri,rsp)) {- do nothing -}+ Just au' -> do+ pxy <- gets bsProxy+ case pxy of+ NoProxy -> do+ err "Proxy authentication required without proxy!"+ return (Right (uri,rsp))+ Proxy px _ -> do+ out "Retrying with proxy authentication"+ setProxy (Proxy px (Just au'))+ request' nullVal+ rqState{ reqDenies = succ(reqDenies rqState)+ , reqStopOnDeny = False+ }+ rq++ (3,0,x) | x `elem` [2,3,1,7] -> do+ out ("30" ++ show x ++ " - redirect")+ allow_redirs <- allowRedirect rqState+ case allow_redirs of+ False -> return (Right (uri,rsp))+ _ -> do+ case retrieveHeaders HdrLocation rsp of+ [] -> do + err "No Location: header in redirect response"+ return (Right (uri,rsp))+ (Header _ u:_) -> + case parseURIReference u of+ Nothing -> do+ err ("Parse of Location: header in a redirect response failed: " ++ u)+ return (Right (uri,rsp))+ Just newURI+ | {-uriScheme newURI_abs /= uriScheme uri && -}(not (supportedScheme newURI_abs)) -> do+ err ("Unable to handle redirect, unsupported scheme: " ++ show newURI_abs)+ return (Right (uri, rsp))+ | otherwise -> do + out ("Redirecting to " ++ show newURI_abs ++ " ...") + + -- Redirect using GET request method, depending on+ -- response code.+ let toGet = x `elem` [2,3]+ method = if toGet then GET else rqMethod rq+ rq1 = rq { rqMethod=method, rqURI=newURI_abs }+ rq2 = if toGet then (replaceHeader HdrContentLength "0") (rq1 {rqBody = nullVal}) else rq1+ + request' nullVal+ rqState{ reqDenies = 0+ , reqRedirects = succ(reqRedirects rqState)+ , reqStopOnDeny = True+ }+ rq2+ where+ newURI_abs = maybe newURI id (newURI `relativeTo` uri)++ (3,0,5) ->+ case retrieveHeaders HdrLocation rsp of+ [] -> do + err "No Location header in proxy redirect response."+ return (Right (uri,rsp))+ (Header _ u:_) -> + case parseURIReference u of+ Nothing -> do+ err ("Parse of Location header in a proxy redirect response failed: " ++ u)+ return (Right (uri,rsp))+ Just newuri -> do+ out ("Retrying with proxy " ++ show newuri ++ "...")+ setProxy (Proxy (uriToAuthorityString newuri) Nothing)+ request' nullVal rqState{ reqDenies = 0+ , reqRedirects = 0+ , reqRetries = succ (reqRetries rqState)+ , reqStopOnDeny = True+ }+ rq+ _ -> return (Right (uri,rsp))++-- | The internal request handling state machine.+dorequest :: (HStream ty)+ => URIAuth+ -> Request ty+ -> BrowserAction (HandleStream ty)+ (Result (Response ty))+dorequest hst rqst = do+ pool <- gets bsConnectionPool+ let uPort = uriAuthPort Nothing{-ToDo: feed in complete URL-} hst+ conn <- liftIO $ filterM (\c -> c `isTCPConnectedTo` EndPoint (uriRegName hst) uPort) pool+ rsp <- + case conn of+ [] -> do + out ("Creating new connection to " ++ uriAuthToString hst)+ reportEvent OpenConnection (show (rqURI rqst))+ c <- liftIO $ openStream (uriRegName hst) uPort+ updateConnectionPool c+ dorequest2 c rqst+ (c:_) -> do+ out ("Recovering connection to " ++ uriAuthToString hst)+ reportEvent ReuseConnection (show (rqURI rqst))+ dorequest2 c rqst+ case rsp of + Right (Response a b c _) -> + reportEvent (ResponseEnd (a,b,c)) (show (rqURI rqst)) ; _ -> return ()+ return rsp+ where+ dorequest2 c r = do+ dbg <- gets bsDebug+ st <- get+ let + onSendComplete =+ maybe (return ())+ (\evh -> do+ x <- buildBrowserEvent RequestSent (show (rqURI r)) (bsRequestID st)+ runBA st (evh x)+ return ())+ (bsEvent st)+ liftIO $ + maybe (sendHTTP_notify c r onSendComplete)+ (\ f -> do+ c' <- debugByteStream (f++'-': uriAuthToString hst) c+ sendHTTP_notify c' r onSendComplete)+ dbg++updateConnectionPool :: HStream hTy+ => HandleStream hTy+ -> BrowserAction (HandleStream hTy) ()+updateConnectionPool c = do+ pool <- gets bsConnectionPool+ let len_pool = length pool+ maxPoolSize <- fromMaybe defaultMaxPoolSize <$> gets bsMaxPoolSize+ when (len_pool > maxPoolSize)+ (liftIO $ close (last pool))+ let pool' + | len_pool > maxPoolSize = init pool+ | otherwise = pool+ when (maxPoolSize > 0) $ modify (\b -> b { bsConnectionPool=c:pool' })+ return ()+ +-- | Default maximum number of open connections we are willing to have active.+defaultMaxPoolSize :: Int+defaultMaxPoolSize = 5++handleCookies :: URI -> String -> [Header] -> BrowserAction t ()+handleCookies _ _ [] = return () -- cut short the silliness.+handleCookies uri dom cookieHeaders = do+ when (not $ null errs)+ (err $ unlines ("Errors parsing these cookie values: ":errs))+ when (not $ null newCookies)+ (out $ foldl (\x y -> x ++ "\n " ++ show y) "Cookies received:" newCookies)+ filterfn <- getCookieFilter+ newCookies' <- liftIO (filterM (filterfn uri) newCookies)+ when (not $ null newCookies')+ (out $ "Accepting cookies with names: " ++ unwords (map ckName newCookies'))+ mapM_ addCookie newCookies'+ where+ (errs, newCookies) = processCookieHeaders dom cookieHeaders++------------------------------------------------------------------+----------------------- Miscellaneous ----------------------------+------------------------------------------------------------------++allowRedirect :: RequestState -> BrowserAction t Bool+allowRedirect rqState = do+ rd <- getAllowRedirects+ mbMxRetries <- getMaxRedirects+ return (rd && (reqRedirects rqState <= fromMaybe defaultMaxRetries mbMxRetries))++-- | Return @True@ iff the package is able to handle requests and responses+-- over it.+supportedScheme :: URI -> Bool+supportedScheme u = uriScheme u == "http:"++-- | @uriDefaultTo a b@ returns a URI that is consistent with the first+-- argument URI @a@ when read in the context of the second URI @b@.+-- If the second argument is not sufficient context for determining+-- a full URI then anarchy reins.+uriDefaultTo :: URI -> URI -> URI+uriDefaultTo a b = maybe a id (a `relativeTo` b)+++-- This form junk is completely untested...++type FormVar = (String,String)++data Form = Form RequestMethod URI [FormVar]++formToRequest :: Form -> Request_String+formToRequest (Form m u vs) =+ let enc = urlEncodeVars vs+ in case m of+ GET -> Request { rqMethod=GET+ , rqHeaders=[ Header HdrContentLength "0" ]+ , rqBody=""+ , rqURI=u { uriQuery= '?' : enc } -- What about old query?+ }+ POST -> Request { rqMethod=POST+ , rqHeaders=[ Header HdrContentType "application/x-www-form-urlencoded",+ Header HdrContentLength (show $ length enc) ]+ , rqBody=enc+ , rqURI=u+ }+ _ -> error ("unexpected request: " ++ show m)++
+ Network/BufferType.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE TypeSynonymInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.BufferType+-- Description : Abstract representation of request and response buffer types.+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- In order to give the user freedom in how request and response content+-- is represented, a sufficiently abstract representation is needed of+-- these internally. The "Network.BufferType" module provides this, defining+-- the 'BufferType' class and its ad-hoc representation of buffer operations+-- via the 'BufferOp' record.+--+-- This module provides definitions for the standard buffer types that the+-- package supports, i.e., for @String@ and @ByteString@ (strict and lazy.)+-- +-----------------------------------------------------------------------------+module Network.BufferType+ ( + BufferType(..)++ , BufferOp(..)+ , strictBufferOp+ , lazyBufferOp+ , stringBufferOp+ ) where+++import qualified Data.ByteString as Strict hiding ( unpack, pack, span )+import qualified Data.ByteString.Char8 as Strict ( unpack, pack, span )+import qualified Data.ByteString.Lazy as Lazy hiding ( pack, unpack,span )+import qualified Data.ByteString.Lazy.Char8 as Lazy ( pack, unpack, span )+import System.IO ( Handle )+import Data.Word ( Word8 )++import Network.HTTP.Utils ( crlf, lf )++-- | The @BufferType@ class encodes, in a mixed-mode way, the interface+-- that the library requires to operate over data embedded in HTTP+-- requests and responses. That is, we use explicit dictionaries+-- for the operations, but overload the name of the dicts themselves.+-- +class BufferType bufType where+ bufferOps :: BufferOp bufType++instance BufferType Lazy.ByteString where+ bufferOps = lazyBufferOp++instance BufferType Strict.ByteString where+ bufferOps = strictBufferOp++instance BufferType String where+ bufferOps = stringBufferOp++-- | @BufferOp@ encodes the I/O operations of the underlying buffer over +-- a Handle in an (explicit) dictionary type. May not be needed, but gives+-- us flexibility in explicit overriding and wrapping up of these methods.+--+-- Along with IO operations is an ad-hoc collection of functions for working+-- with these abstract buffers, as needed by the internals of the code+-- that processes requests and responses.+--+-- We supply three default @BufferOp@ values, for @String@ along with the+-- strict and lazy versions of @ByteString@. To add others, provide @BufferOp@+-- definitions for +data BufferOp a+ = BufferOp+ { buf_hGet :: Handle -> Int -> IO a+ , buf_hGetContents :: Handle -> IO a+ , buf_hPut :: Handle -> a -> IO ()+ , buf_hGetLine :: Handle -> IO a+ , buf_empty :: a+ , buf_append :: a -> a -> a+ , buf_concat :: [a] -> a+ , buf_fromStr :: String -> a+ , buf_toStr :: a -> String+ , buf_snoc :: a -> Word8 -> a+ , buf_splitAt :: Int -> a -> (a,a)+ , buf_span :: (Char -> Bool) -> a -> (a,a)+ , buf_isLineTerm :: a -> Bool+ , buf_isEmpty :: a -> Bool+ }++instance Eq (BufferOp a) where+ _ == _ = False++-- | @strictBufferOp@ is the 'BufferOp' definition over @ByteString@s,+-- the non-lazy kind.+strictBufferOp :: BufferOp Strict.ByteString+strictBufferOp = + BufferOp + { buf_hGet = Strict.hGet+ , buf_hGetContents = Strict.hGetContents+ , buf_hPut = Strict.hPut+ , buf_hGetLine = Strict.hGetLine+ , buf_append = Strict.append+ , buf_concat = Strict.concat+ , buf_fromStr = Strict.pack+ , buf_toStr = Strict.unpack+ , buf_snoc = Strict.snoc+ , buf_splitAt = Strict.splitAt+ , buf_span = Strict.span+ , buf_empty = Strict.empty+ , buf_isLineTerm = \ b -> Strict.length b == 2 && p_crlf == b ||+ Strict.length b == 1 && p_lf == b+ , buf_isEmpty = Strict.null + }+ where+ p_crlf = Strict.pack crlf+ p_lf = Strict.pack lf++-- | @lazyBufferOp@ is the 'BufferOp' definition over @ByteString@s,+-- the non-strict kind.+lazyBufferOp :: BufferOp Lazy.ByteString+lazyBufferOp = + BufferOp + { buf_hGet = Lazy.hGet+ , buf_hGetContents = Lazy.hGetContents+ , buf_hPut = Lazy.hPut+ , buf_hGetLine = \ h -> Strict.hGetLine h >>= \ l -> return (Lazy.fromChunks [l])+ , buf_append = Lazy.append+ , buf_concat = Lazy.concat+ , buf_fromStr = Lazy.pack+ , buf_toStr = Lazy.unpack+ , buf_snoc = Lazy.snoc+ , buf_splitAt = \ i x -> Lazy.splitAt (fromIntegral i) x+ , buf_span = Lazy.span+ , buf_empty = Lazy.empty+ , buf_isLineTerm = \ b -> Lazy.length b == 2 && p_crlf == b ||+ Lazy.length b == 1 && p_lf == b+ , buf_isEmpty = Lazy.null + }+ where+ p_crlf = Lazy.pack crlf+ p_lf = Lazy.pack lf++-- | @stringBufferOp@ is the 'BufferOp' definition over @String@s.+-- It is defined in terms of @strictBufferOp@ operations,+-- unpacking/converting to @String@ when needed.+stringBufferOp :: BufferOp String+stringBufferOp =BufferOp + { buf_hGet = \ h n -> buf_hGet strictBufferOp h n >>= return . Strict.unpack+ , buf_hGetContents = \ h -> buf_hGetContents strictBufferOp h >>= return . Strict.unpack+ , buf_hPut = \ h s -> buf_hPut strictBufferOp h (Strict.pack s)+ , buf_hGetLine = \ h -> buf_hGetLine strictBufferOp h >>= return . Strict.unpack+ , buf_append = (++)+ , buf_concat = concat+ , buf_fromStr = id+ , buf_toStr = id+ , buf_snoc = \ a x -> a ++ [toEnum (fromIntegral x)]+ , buf_splitAt = splitAt+ , buf_span = \ p a -> + case Strict.span p (Strict.pack a) of+ (x,y) -> (Strict.unpack x, Strict.unpack y)+ , buf_empty = []+ , buf_isLineTerm = \ b -> b == crlf || b == lf+ , buf_isEmpty = null + }+
+ Network/HTTP.hs view
@@ -0,0 +1,229 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- The 'Network.HTTP' module provides a simple interface for sending and+-- receiving content over HTTP in Haskell. Here's how to fetch a document from+-- a URL and return it as a String:+--+-- >+-- > simpleHTTP (getRequest "http://www.haskell.org/") >>= fmap (take 100) . getResponseBody+-- > -- fetch document and return it (as a 'String'.)+--+-- Other functions let you control the submission and transfer of HTTP+-- 'Request's and 'Response's more carefully, letting you integrate the use+-- of 'Network.HTTP' functionality into your application.+--+-- The module also exports the main types of the package, 'Request' and 'Response',+-- along with 'Header' and functions for working with these.+--+-- The actual functionality is implemented by modules in the @Network.HTTP.*@+-- namespace, letting you either use the default implementation here+-- by importing @Network.HTTP@ or, for more specific uses, selectively+-- import the modules in @Network.HTTP.*@. To wit, more than one kind of+-- representation of the bulk data that flows across a HTTP connection is +-- supported. (see "Network.HTTP.HandleStream".)+-- +-- /NOTE:/ The 'Request' send actions will normalize the @Request@ prior to transmission.+-- Normalization such as having the request path be in the expected form and, possibly,+-- introduce a default @Host:@ header if one isn't already present. If you do not +-- want the requests tampered with, but sent as-is, please import and use the+-- the "Network.HTTP.HandleStream" or "Network.HTTP.Stream" modules instead. They+-- export the same functions, but leaves construction and any normalization of +-- @Request@s to the user.+--+-- /NOTE:/ This package only supports HTTP; it does not support HTTPS.+-- Attempts to use HTTPS result in an error.+-----------------------------------------------------------------------------+module Network.HTTP + ( module Network.HTTP.Base+ , module Network.HTTP.Headers++ {- the functionality that the implementation modules, + Network.HTTP.HandleStream and Network.HTTP.Stream,+ exposes:+ -}+ , simpleHTTP -- :: Request -> IO (Result Response)+ , simpleHTTP_ -- :: Stream s => s -> Request -> IO (Result Response)+ , sendHTTP -- :: Stream s => s -> Request -> IO (Result Response)+ , sendHTTP_notify -- :: Stream s => s -> Request -> IO () -> IO (Result Response)+ , receiveHTTP -- :: Stream s => s -> IO (Result Request)+ , respondHTTP -- :: Stream s => s -> Response -> IO ()++ , module Network.TCP+ + , getRequest -- :: String -> Request_String+ , postRequest -- :: String -> Request_String+ , postRequestWithBody -- :: String -> String -> String -> Request_String+ + , getResponseBody -- :: Requesty ty -> ty+ ) where++-----------------------------------------------------------------+------------------ Imports --------------------------------------+-----------------------------------------------------------------++import Network.HTTP.Headers+import Network.HTTP.Base+import qualified Network.HTTP.HandleStream as S+-- old implementation: import Network.HTTP.Stream+import Network.TCP+import Network.Stream ( Result )+import Network.URI ( parseURI )++import Data.Maybe ( fromMaybe )++{-+ Note: if you switch over/back to using Network.HTTP.Stream here, you'll+ have to wrap the results from 'openStream' as Connections via 'hstreamToConnection'+ prior to delegating to the Network.HTTP.Stream functions.+-}++-- | @simpleHTTP req@ transmits the 'Request' @req@ by opening a /direct/, non-persistent+-- connection to the HTTP server that @req@ is destined for, followed by transmitting+-- it and gathering up the response as a 'Result'. Prior to sending the request,+-- it is normalized (via 'normalizeRequest'). If you have to mediate the request+-- via an HTTP proxy, you will have to normalize the request yourself. Or switch to+-- using 'Network.Browser' instead.+--+-- Examples:+--+-- > simpleHTTP (getRequest "http://hackage.haskell.org/")+-- > simpleHTTP (getRequest "http://hackage.haskell.org:8012/")++simpleHTTP :: (HStream ty) => Request ty -> IO (Result (Response ty))+simpleHTTP r = do+ auth <- getAuth r+ failHTTPS (rqURI r)+ c <- openStream (host auth) (fromMaybe 80 (port auth))+ let norm_r = normalizeRequest defaultNormalizeRequestOptions{normDoClose=True} r+ simpleHTTP_ c norm_r+ +-- | Identical to 'simpleHTTP', but acting on an already opened stream.+simpleHTTP_ :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+simpleHTTP_ s r = do + let norm_r = normalizeRequest defaultNormalizeRequestOptions{normDoClose=True} r+ S.sendHTTP s norm_r++-- | @sendHTTP hStream httpRequest@ transmits @httpRequest@ (after normalization) over+-- @hStream@, but does not alter the status of the connection, nor request it to be+-- closed upon receiving the response.+sendHTTP :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+sendHTTP conn rq = do+ let norm_r = normalizeRequest defaultNormalizeRequestOptions rq + S.sendHTTP conn norm_r++-- | @sendHTTP_notify hStream httpRequest action@ behaves like 'sendHTTP', but+-- lets you supply an IO @action@ to execute once the request has been successfully+-- transmitted over the connection. Useful when you want to set up tracing of+-- request transmission and its performance.+sendHTTP_notify :: HStream ty+ => HandleStream ty+ -> Request ty+ -> IO ()+ -> IO (Result (Response ty))+sendHTTP_notify conn rq onSendComplete = do+ let norm_r = normalizeRequest defaultNormalizeRequestOptions rq + S.sendHTTP_notify conn norm_r onSendComplete++-- | @receiveHTTP hStream@ reads a 'Request' from the 'HandleStream' @hStream@+receiveHTTP :: HStream ty => HandleStream ty -> IO (Result (Request ty))+receiveHTTP conn = S.receiveHTTP conn++-- | @respondHTTP hStream httpResponse@ transmits an HTTP 'Response' over+-- the 'HandleStream' @hStream@. It could be used to implement simple web+-- server interactions, performing the dual role to 'sendHTTP'.+respondHTTP :: HStream ty => HandleStream ty -> Response ty -> IO ()+respondHTTP conn rsp = S.respondHTTP conn rsp++-- | @getRequest urlString@ is convenience constructor for basic GET 'Request's. If+-- @urlString@ isn't a syntactically valid URL, the function raises an error.+getRequest :: String -> Request_String+getRequest urlString = + case parseURI urlString of+ Nothing -> error ("getRequest: Not a valid URL - " ++ urlString)+ Just u -> mkRequest GET u++-- | @postRequest urlString@ is convenience constructor for POST 'Request's. If+-- @urlString@ isn\'t a syntactically valid URL, the function raises an error.+postRequest :: String -> Request_String+postRequest urlString = + case parseURI urlString of+ Nothing -> error ("postRequest: Not a valid URL - " ++ urlString)+ Just u -> mkRequest POST u++-- | @postRequestWithBody urlString typ body@ is convenience constructor for+-- POST 'Request's. It constructs a request and sets the body as well as+-- the Content-Type and Content-Length headers. The contents of the body+-- are forced to calculate the value for the Content-Length header.+-- If @urlString@ isn\'t a syntactically valid URL, the function raises+-- an error.+postRequestWithBody :: String -> String -> String -> Request_String+postRequestWithBody urlString typ body = + case parseURI urlString of+ Nothing -> error ("postRequestWithBody: Not a valid URL - " ++ urlString)+ Just u -> setRequestBody (mkRequest POST u) (typ, body)++-- | @getResponseBody response@ takes the response of a HTTP requesting action and+-- tries to extricate the body of the 'Response' @response@. If the request action+-- returned an error, an IO exception is raised.+getResponseBody :: Result (Response ty) -> IO ty+getResponseBody (Left err) = fail (show err)+getResponseBody (Right r) = return (rspBody r)++--+-- * TODO+-- - request pipelining+-- - https upgrade (includes full TLS, i.e. SSL, implementation)+-- - use of Stream classes will pay off+-- - consider C implementation of encryption\/decryption+-- - comm timeouts+-- - MIME & entity stuff (happening in separate module)+-- - support \"*\" uri-request-string for OPTIONS request method+-- +-- +-- * Header notes:+--+-- [@Host@]+-- Required by HTTP\/1.1, if not supplied as part+-- of a request a default Host value is extracted+-- from the request-uri.+-- +-- [@Connection@] +-- If this header is present in any request or+-- response, and it's value is "close", then+-- the current request\/response is the last +-- to be allowed on that connection.+-- +-- [@Expect@]+-- Should a request contain a body, an Expect+-- header will be added to the request. The added+-- header has the value \"100-continue\". After+-- a 417 \"Expectation Failed\" response the request+-- is attempted again without this added Expect+-- header.+-- +-- [@TransferEncoding,ContentLength,...@]+-- if request is inconsistent with any of these+-- header values then you may not receive any response+-- or will generate an error response (probably 4xx).+--+--+-- * Response code notes+-- Some response codes induce special behaviour:+--+-- [@1xx@] \"100 Continue\" will cause any unsent request body to be sent.+-- \"101 Upgrade\" will be returned.+-- Other 1xx responses are ignored.+-- +-- [@417@] The reason for this code is \"Expectation failed\", indicating+-- that the server did not like the Expect \"100-continue\" header+-- added to a request. Receipt of 417 will induce another+-- request attempt (without Expect header), unless no Expect header+-- had been added (in which case 417 response is returned).
+ Network/HTTP/Auth.hs view
@@ -0,0 +1,216 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Auth+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Representing HTTP Auth values in Haskell.+-- Right now, it contains mostly functionality needed by 'Network.Browser'.+-- +-----------------------------------------------------------------------------+module Network.HTTP.Auth+ ( Authority(..)+ , Algorithm(..)+ , Challenge(..)+ , Qop(..)++ , headerToChallenge -- :: URI -> Header -> Maybe Challenge+ , withAuthority -- :: Authority -> Request ty -> String+ ) where++import Network.URI+import Network.HTTP.Base+import Network.HTTP.Utils+import Network.HTTP.Headers ( Header(..) )+import qualified Network.HTTP.MD5Aux as MD5 (md5s, Str(Str))+import qualified Network.HTTP.Base64 as Base64 (encode)+import Text.ParserCombinators.Parsec+ ( Parser, char, many, many1, satisfy, parse, spaces, sepBy1 )++import Data.Char+import Data.Maybe+import Data.Word ( Word8 )++-- | @Authority@ specifies the HTTP Authentication method to use for+-- a given domain/realm; @Basic@ or @Digest@.+data Authority + = AuthBasic { auRealm :: String+ , auUsername :: String+ , auPassword :: String+ , auSite :: URI+ }+ | AuthDigest{ auRealm :: String+ , auUsername :: String+ , auPassword :: String+ , auNonce :: String+ , auAlgorithm :: Maybe Algorithm+ , auDomain :: [URI]+ , auOpaque :: Maybe String+ , auQop :: [Qop]+ }+++data Challenge + = ChalBasic { chRealm :: String }+ | ChalDigest { chRealm :: String+ , chDomain :: [URI]+ , chNonce :: String+ , chOpaque :: Maybe String+ , chStale :: Bool+ , chAlgorithm ::Maybe Algorithm+ , chQop :: [Qop]+ }++-- | @Algorithm@ controls the digest algorithm to, @MD5@ or @MD5Session@.+data Algorithm = AlgMD5 | AlgMD5sess+ deriving(Eq)++instance Show Algorithm where+ show AlgMD5 = "md5"+ show AlgMD5sess = "md5-sess"++-- | +data Qop = QopAuth | QopAuthInt+ deriving(Eq,Show)++-- | @withAuthority auth req@ generates a credentials value from the @auth@ 'Authority',+-- in the context of the given request.+-- +-- If a client nonce was to be used then this function might need to be of type ... -> BrowserAction String+withAuthority :: Authority -> Request ty -> String+withAuthority a rq = case a of+ AuthBasic{} -> "Basic " ++ base64encode (auUsername a ++ ':' : auPassword a)+ AuthDigest{} ->+ "Digest " +++ concat [ "username=" ++ quo (auUsername a)+ , ",realm=" ++ quo (auRealm a)+ , ",nonce=" ++ quo (auNonce a)+ , ",uri=" ++ quo digesturi+ , ",response=" ++ quo rspdigest+ -- plus optional stuff:+ , fromMaybe "" (fmap (\ alg -> ",algorithm=" ++ quo (show alg)) (auAlgorithm a))+ , fromMaybe "" (fmap (\ o -> ",opaque=" ++ quo o) (auOpaque a))+ , if null (auQop a) then "" else ",qop=auth"+ ]+ where+ quo s = '"':s ++ "\""++ rspdigest = map toLower (kd (md5 a1) (noncevalue ++ ":" ++ md5 a2))++ a1, a2 :: String+ a1 = auUsername a ++ ":" ++ auRealm a ++ ":" ++ auPassword a+ + {-+ If the "qop" directive's value is "auth" or is unspecified, then A2+ is:+ A2 = Method ":" digest-uri-value+ If the "qop" value is "auth-int", then A2 is:+ A2 = Method ":" digest-uri-value ":" H(entity-body)+ -}+ a2 = show (rqMethod rq) ++ ":" ++ digesturi++ digesturi = show (rqURI rq)+ noncevalue = auNonce a++type Octet = Word8++-- FIXME: these probably only work right for latin-1 strings+stringToOctets :: String -> [Octet]+stringToOctets = map (fromIntegral . fromEnum)++base64encode :: String -> String+base64encode = Base64.encode . stringToOctets++md5 :: String -> String+md5 = MD5.md5s . MD5.Str++kd :: String -> String -> String+kd a b = md5 (a ++ ":" ++ b)+++++-- | @headerToChallenge base www_auth@ tries to convert the @WWW-Authenticate@ header +-- @www_auth@ into a 'Challenge' value.+headerToChallenge :: URI -> Header -> Maybe Challenge+headerToChallenge baseURI (Header _ str) =+ case parse challenge "" str of+ Left{} -> Nothing+ Right (name,props) -> case name of+ "basic" -> mkBasic props+ "digest" -> mkDigest props+ _ -> Nothing+ where+ challenge :: Parser (String,[(String,String)])+ challenge =+ do { nme <- word+ ; spaces+ ; pps <- cprops+ ; return (map toLower nme,pps)+ }++ cprops = sepBy1 cprop comma++ comma = do { spaces ; _ <- char ',' ; spaces }++ cprop =+ do { nm <- word+ ; _ <- char '='+ ; val <- quotedstring+ ; return (map toLower nm,val)+ }++ mkBasic, mkDigest :: [(String,String)] -> Maybe Challenge++ mkBasic params = fmap ChalBasic (lookup "realm" params)++ mkDigest params =+ -- with Maybe monad+ do { r <- lookup "realm" params+ ; n <- lookup "nonce" params+ ; return $ + ChalDigest { chRealm = r+ , chDomain = (annotateURIs + $ map parseURI+ $ words + $ fromMaybe [] + $ lookup "domain" params)+ , chNonce = n+ , chOpaque = lookup "opaque" params+ , chStale = "true" == (map toLower+ $ fromMaybe "" (lookup "stale" params))+ , chAlgorithm= readAlgorithm (fromMaybe "MD5" $ lookup "algorithm" params)+ , chQop = readQop (fromMaybe "" $ lookup "qop" params)+ }+ }++ annotateURIs :: [Maybe URI] -> [URI]+ annotateURIs = (map (\u -> fromMaybe u (u `relativeTo` baseURI))) . catMaybes++ -- Change These:+ readQop :: String -> [Qop]+ readQop = catMaybes . (map strToQop) . (splitBy ',')++ strToQop qs = case map toLower (trim qs) of+ "auth" -> Just QopAuth+ "auth-int" -> Just QopAuthInt+ _ -> Nothing++ readAlgorithm astr = case map toLower (trim astr) of+ "md5" -> Just AlgMD5+ "md5-sess" -> Just AlgMD5sess+ _ -> Nothing++word, quotedstring :: Parser String+quotedstring =+ do { _ <- char '"' -- "+ ; str <- many (satisfy $ not . (=='"'))+ ; _ <- char '"'+ ; return str+ }++word = many1 (satisfy (\x -> isAlphaNum x || x=='_' || x=='.' || x=='-' || x==':'))
+ Network/HTTP/Base.hs view
@@ -0,0 +1,895 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Base+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Definitions of @Request@ and @Response@ types along with functions+-- for normalizing them. It is assumed to be an internal module; user+-- code should, if possible, import @Network.HTTP@ to access the functionality+-- that this module provides.+--+-- Additionally, the module exports internal functions for working with URLs,+-- and for handling the processing of requests and responses coming back.+--+-----------------------------------------------------------------------------+module Network.HTTP.Base+ (+ -- ** Constants+ httpVersion -- :: String++ -- ** HTTP+ , Request(..)+ , Response(..)+ , RequestMethod(..)+ + , Request_String+ , Response_String+ , HTTPRequest+ , HTTPResponse+ + -- ** URL Encoding+ , urlEncode+ , urlDecode+ , urlEncodeVars++ -- ** URI authority parsing+ , URIAuthority(..)+ , parseURIAuthority+ + -- internal+ , uriToAuthorityString -- :: URI -> String+ , uriAuthToString -- :: URIAuth -> String+ , uriAuthPort -- :: Maybe URI -> URIAuth -> Int+ , reqURIAuth -- :: Request ty -> URIAuth++ , parseResponseHead -- :: [String] -> Result ResponseData+ , parseRequestHead -- :: [String] -> Result RequestData++ , ResponseNextStep(..)+ , matchResponse+ , ResponseData+ , ResponseCode+ , RequestData+ + , NormalizeRequestOptions(..) + , defaultNormalizeRequestOptions -- :: NormalizeRequestOptions ty+ , RequestNormalizer++ , normalizeRequest -- :: NormalizeRequestOptions ty -> Request ty -> Request ty++ , splitRequestURI++ , getAuth+ , normalizeRequestURI+ , normalizeHostHeader+ , findConnClose++ -- internal export (for the use by Network.HTTP.{Stream,ByteStream} )+ , linearTransfer+ , hopefulTransfer+ , chunkedTransfer+ , uglyDeathTransfer+ , readTillEmpty1+ , readTillEmpty2+ + , defaultGETRequest+ , defaultGETRequest_+ , mkRequest+ , setRequestBody++ , defaultUserAgent+ , httpPackageVersion+ , libUA {- backwards compatibility, will disappear..soon -}+ + , catchIO+ , catchIO_+ , responseParseError+ + , getRequestVersion+ , getResponseVersion+ , setRequestVersion+ , setResponseVersion++ , failHTTPS+ + ) where++import Network.URI+ ( URI(uriAuthority, uriPath, uriScheme)+ , URIAuth(URIAuth, uriUserInfo, uriRegName, uriPort)+ , parseURIReference+ )++import Control.Monad ( guard )+import Control.Monad.Error ()+import Data.Char ( digitToInt, intToDigit, toLower, isDigit,+ isAscii, isAlphaNum )+import Data.List ( partition, find )+import Data.Maybe ( listToMaybe, fromMaybe )+import Numeric ( readHex )++import Network.Stream+import Network.BufferType ( BufferOp(..), BufferType(..) )+import Network.HTTP.Headers+import Network.HTTP.Utils ( trim, crlf, sp, readsOne )++import Text.Read.Lex (readDecP)+import Text.ParserCombinators.ReadP+ ( ReadP, readP_to_S, char, (<++), look, munch )++import Control.Exception as Exception (IOException)++import qualified Paths_cabal_install_bundle as Self (version)+import Data.Version (showVersion)++-----------------------------------------------------------------+------------------ URI Authority parsing ------------------------+-----------------------------------------------------------------++data URIAuthority = URIAuthority { user :: Maybe String, + password :: Maybe String,+ host :: String,+ port :: Maybe Int+ } deriving (Eq,Show)++-- | Parse the authority part of a URL.+--+-- > RFC 1732, section 3.1:+-- >+-- > //<user>:<password>@<host>:<port>/<url-path>+-- > Some or all of the parts "<user>:<password>@", ":<password>",+-- > ":<port>", and "/<url-path>" may be excluded.+parseURIAuthority :: String -> Maybe URIAuthority+parseURIAuthority s = listToMaybe (map fst (readP_to_S pURIAuthority s))+++pURIAuthority :: ReadP URIAuthority+pURIAuthority = do+ (u,pw) <- (pUserInfo `before` char '@') + <++ return (Nothing, Nothing)+ h <- munch (/=':')+ p <- orNothing (char ':' >> readDecP)+ look >>= guard . null + return URIAuthority{ user=u, password=pw, host=h, port=p }++pUserInfo :: ReadP (Maybe String, Maybe String)+pUserInfo = do+ u <- orNothing (munch (`notElem` ":@"))+ p <- orNothing (char ':' >> munch (/='@'))+ return (u,p)++before :: Monad m => m a -> m b -> m a+before a b = a >>= \x -> b >> return x++orNothing :: ReadP a -> ReadP (Maybe a)+orNothing p = fmap Just p <++ return Nothing++-- This function duplicates old Network.URI.authority behaviour.+uriToAuthorityString :: URI -> String+uriToAuthorityString u = maybe "" uriAuthToString (uriAuthority u)++uriAuthToString :: URIAuth -> String+uriAuthToString ua = + concat [ uriUserInfo ua + , uriRegName ua+ , uriPort ua+ ]++uriAuthPort :: Maybe URI -> URIAuth -> Int+uriAuthPort mbURI u = + case uriPort u of+ (':':s) -> readsOne id (default_port mbURI) s+ _ -> default_port mbURI+ where+ default_port Nothing = default_http+ default_port (Just url) = + case map toLower $ uriScheme url of+ "http:" -> default_http+ "https:" -> default_https+ -- todo: refine+ _ -> default_http++ default_http = 80+ default_https = 443++failHTTPS :: Monad m => URI -> m ()+failHTTPS uri+ | map toLower (uriScheme uri) == "https:" = fail "https not supported"+ | otherwise = return ()++-- Fish out the authority from a possibly normalized Request, i.e.,+-- the information may either be in the request's URI or inside+-- the Host: header.+reqURIAuth :: Request ty -> URIAuth+reqURIAuth req = + case uriAuthority (rqURI req) of+ Just ua -> ua+ _ -> case lookupHeader HdrHost (rqHeaders req) of+ Nothing -> error ("reqURIAuth: no URI authority for: " ++ show req)+ Just h -> + case toHostPort h of+ (ht,p) -> URIAuth { uriUserInfo = ""+ , uriRegName = ht+ , uriPort = p+ }+ where+ -- Note: just in case you're wondering..the convention is to include the ':'+ -- in the port part..+ toHostPort h = break (==':') h++-----------------------------------------------------------------+------------------ HTTP Messages --------------------------------+-----------------------------------------------------------------+++-- Protocol version+httpVersion :: String+httpVersion = "HTTP/1.1"+++-- | The HTTP request method, to be used in the 'Request' object.+-- We are missing a few of the stranger methods, but these are+-- not really necessary until we add full TLS.+data RequestMethod = HEAD | PUT | GET | POST | DELETE | OPTIONS | TRACE | CONNECT | Custom String+ deriving(Eq)++instance Show RequestMethod where+ show x = + case x of+ HEAD -> "HEAD"+ PUT -> "PUT"+ GET -> "GET"+ POST -> "POST"+ DELETE -> "DELETE"+ OPTIONS -> "OPTIONS"+ TRACE -> "TRACE"+ CONNECT -> "CONNECT"+ Custom c -> c++rqMethodMap :: [(String, RequestMethod)]+rqMethodMap = [("HEAD", HEAD),+ ("PUT", PUT),+ ("GET", GET),+ ("POST", POST),+ ("DELETE", DELETE),+ ("OPTIONS", OPTIONS),+ ("TRACE", TRACE),+ ("CONNECT", CONNECT)]++-- +-- for backwards-ish compatibility; suggest+-- migrating to new Req/Resp by adding type param.+-- +type Request_String = Request String+type Response_String = Response String++-- Hmm..I really want to use these for the record+-- type, but it will upset codebases wanting to+-- migrate (and live with using pre-HTTPbis versions.)+type HTTPRequest a = Request a+type HTTPResponse a = Response a++-- | An HTTP Request.+-- The 'Show' instance of this type is used for message serialisation,+-- which means no body data is output.+data Request a =+ Request { rqURI :: URI -- ^ might need changing in future+ -- 1) to support '*' uri in OPTIONS request+ -- 2) transparent support for both relative+ -- & absolute uris, although this should+ -- already work (leave scheme & host parts empty).+ , rqMethod :: RequestMethod+ , rqHeaders :: [Header]+ , rqBody :: a+ }++-- Notice that request body is not included,+-- this show function is used to serialise+-- a request for the transport link, we send+-- the body separately where possible.+instance Show (Request a) where+ show req@(Request u m h _) =+ show m ++ sp ++ alt_uri ++ sp ++ ver ++ crlf+ ++ foldr (++) [] (map show (dropHttpVersion h)) ++ crlf+ where+ ver = fromMaybe httpVersion (getRequestVersion req)+ alt_uri = show $ if null (uriPath u) || head (uriPath u) /= '/' + then u { uriPath = '/' : uriPath u } + else u++instance HasHeaders (Request a) where+ getHeaders = rqHeaders+ setHeaders rq hdrs = rq { rqHeaders=hdrs }++-- | For easy pattern matching, HTTP response codes @xyz@ are+-- represented as @(x,y,z)@.+type ResponseCode = (Int,Int,Int)++-- | @ResponseData@ contains the head of a response payload;+-- HTTP response code, accompanying text description + header+-- fields.+type ResponseData = (ResponseCode,String,[Header])++-- | @RequestData@ contains the head of a HTTP request; method,+-- its URL along with the auxillary/supporting header data.+type RequestData = (RequestMethod,URI,[Header])++-- | An HTTP Response.+-- The 'Show' instance of this type is used for message serialisation,+-- which means no body data is output, additionally the output will+-- show an HTTP version of 1.1 instead of the actual version returned+-- by a server.+data Response a =+ Response { rspCode :: ResponseCode+ , rspReason :: String+ , rspHeaders :: [Header]+ , rspBody :: a+ }+ +-- This is an invalid representation of a received response, +-- since we have made the assumption that all responses are HTTP/1.1+instance Show (Response a) where+ show rsp@(Response (a,b,c) reason headers _) =+ ver ++ ' ' : map intToDigit [a,b,c] ++ ' ' : reason ++ crlf+ ++ foldr (++) [] (map show (dropHttpVersion headers)) ++ crlf+ where+ ver = fromMaybe httpVersion (getResponseVersion rsp)++instance HasHeaders (Response a) where+ getHeaders = rspHeaders+ setHeaders rsp hdrs = rsp { rspHeaders=hdrs }+++------------------------------------------------------------------+------------------ Request Building ------------------------------+------------------------------------------------------------------++-- | Deprecated. Use 'defaultUserAgent'+libUA :: String+libUA = "hs-HTTP-4000.0.9"+{-# DEPRECATED libUA "Use defaultUserAgent instead (but note the user agent name change)" #-}++-- | A default user agent string. The string is @\"haskell-HTTP/$version\"@+-- where @$version@ is the version of this HTTP package.+--+defaultUserAgent :: String+defaultUserAgent = "haskell-HTTP/" ++ httpPackageVersion++-- | The version of this HTTP package as a string, e.g. @\"4000.1.2\"@. This+-- may be useful to include in a user agent string so that you can determine+-- from server logs what version of this package HTTP clients are using.+-- This can be useful for tracking down HTTP compatibility quirks.+--+httpPackageVersion :: String+httpPackageVersion = showVersion Self.version++defaultGETRequest :: URI -> Request_String+defaultGETRequest uri = defaultGETRequest_ uri++defaultGETRequest_ :: BufferType a => URI -> Request a+defaultGETRequest_ uri = mkRequest GET uri ++-- | 'mkRequest method uri' constructs a well formed+-- request for the given HTTP method and URI. It does not+-- normalize the URI for the request _nor_ add the required +-- Host: header. That is done either explicitly by the user+-- or when requests are normalized prior to transmission.+mkRequest :: BufferType ty => RequestMethod -> URI -> Request ty+mkRequest meth uri = req+ where+ req = + Request { rqURI = uri+ , rqBody = empty+ , rqHeaders = [ Header HdrContentLength "0"+ , Header HdrUserAgent defaultUserAgent+ ]+ , rqMethod = meth+ }++ empty = buf_empty (toBufOps req)++-- set rqBody, Content-Type and Content-Length headers.+setRequestBody :: Request_String -> (String, String) -> Request_String+setRequestBody req (typ, body) = req' { rqBody=body }+ where+ req' = replaceHeader HdrContentType typ .+ replaceHeader HdrContentLength (show $ length body) $+ req++{-+ -- stub out the user info.+ updAuth = fmap (\ x -> x{uriUserInfo=""}) (uriAuthority uri)++ withHost = + case uriToAuthorityString uri{uriAuthority=updAuth} of+ "" -> id+ h -> ((Header HdrHost h):)++ uri_req + | forProxy = uri+ | otherwise = snd (splitRequestURI uri)+-}+++toBufOps :: BufferType a => Request a -> BufferOp a+toBufOps _ = bufferOps++-----------------------------------------------------------------+------------------ Parsing --------------------------------------+-----------------------------------------------------------------++-- Parsing a request+parseRequestHead :: [String] -> Result RequestData+parseRequestHead [] = Left ErrorClosed+parseRequestHead (com:hdrs) = do+ (version,rqm,uri) <- requestCommand com (words com)+ hdrs' <- parseHeaders hdrs+ return (rqm,uri,withVer version hdrs')+ where+ withVer [] hs = hs+ withVer (h:_) hs = withVersion h hs++ requestCommand l _yes@(rqm:uri:version) =+ case (parseURIReference uri, lookup rqm rqMethodMap) of+ (Just u, Just r) -> return (version,r,u)+ (Just u, Nothing) -> return (version,Custom rqm,u)+ _ -> parse_err l+ requestCommand l _+ | null l = failWith ErrorClosed+ | otherwise = parse_err l++ parse_err l = responseParseError "parseRequestHead"+ ("Request command line parse failure: " ++ l)++-- Parsing a response+parseResponseHead :: [String] -> Result ResponseData+parseResponseHead [] = failWith ErrorClosed+parseResponseHead (sts:hdrs) = do+ (version,code,reason) <- responseStatus sts (words sts)+ hdrs' <- parseHeaders hdrs+ return (code,reason, withVersion version hdrs')+ where+ responseStatus _l _yes@(version:code:reason) =+ return (version,match code,concatMap (++" ") reason)+ responseStatus l _no + | null l = failWith ErrorClosed -- an assumption+ | otherwise = parse_err l++ parse_err l = + responseParseError + "parseResponseHead"+ ("Response status line parse failure: " ++ l)++ match [a,b,c] = (digitToInt a,+ digitToInt b,+ digitToInt c)+ match _ = (-1,-1,-1) -- will create appropriate behaviour++-- To avoid changing the @RequestData@ and @ResponseData@ types+-- just for this (and the upstream backwards compat. woes that+-- will result in), encode version info as a custom header.+-- Used by 'parseResponseData' and 'parseRequestData'.+--+-- Note: the Request and Response types do not currently represent+-- the version info explicitly in their record types. You have to use+-- {get,set}{Request,Response}Version for that.+withVersion :: String -> [Header] -> [Header]+withVersion v hs + | v == httpVersion = hs -- don't bother adding it if the default.+ | otherwise = (Header (HdrCustom "X-HTTP-Version") v) : hs++-- | @getRequestVersion req@ returns the HTTP protocol version of+-- the request @req@. If @Nothing@, the default 'httpVersion' can be assumed.+getRequestVersion :: Request a -> Maybe String+getRequestVersion r = getHttpVersion r++-- | @setRequestVersion v req@ returns a new request, identical to+-- @req@, but with its HTTP version set to @v@.+setRequestVersion :: String -> Request a -> Request a+setRequestVersion s r = setHttpVersion r s+++-- | @getResponseVersion rsp@ returns the HTTP protocol version of+-- the response @rsp@. If @Nothing@, the default 'httpVersion' can be +-- assumed.+getResponseVersion :: Response a -> Maybe String+getResponseVersion r = getHttpVersion r++-- | @setResponseVersion v rsp@ returns a new response, identical to+-- @rsp@, but with its HTTP version set to @v@.+setResponseVersion :: String -> Response a -> Response a+setResponseVersion s r = setHttpVersion r s++-- internal functions for accessing HTTP-version info in+-- requests and responses. Not exported as it exposes ho+-- version info is represented internally.++getHttpVersion :: HasHeaders a => a -> Maybe String+getHttpVersion r = + fmap toVersion $+ find isHttpVersion $+ getHeaders r+ where+ toVersion (Header _ x) = x++setHttpVersion :: HasHeaders a => a -> String -> a+setHttpVersion r v = + setHeaders r $+ withVersion v $+ dropHttpVersion $+ getHeaders r++dropHttpVersion :: [Header] -> [Header]+dropHttpVersion hs = filter (not.isHttpVersion) hs++isHttpVersion :: Header -> Bool+isHttpVersion (Header (HdrCustom "X-HTTP-Version") _) = True+isHttpVersion _ = False ++++-----------------------------------------------------------------+------------------ HTTP Send / Recv ----------------------------------+-----------------------------------------------------------------++data ResponseNextStep+ = Continue+ | Retry+ | Done+ | ExpectEntity+ | DieHorribly String++matchResponse :: RequestMethod -> ResponseCode -> ResponseNextStep+matchResponse rqst rsp =+ case rsp of+ (1,0,0) -> Continue+ (1,0,1) -> Done -- upgrade to TLS+ (1,_,_) -> Continue -- default+ (2,0,4) -> Done+ (2,0,5) -> Done+ (2,_,_) -> ans+ (3,0,4) -> Done+ (3,0,5) -> Done+ (3,_,_) -> ans+ (4,1,7) -> Retry -- Expectation failed+ (4,_,_) -> ans+ (5,_,_) -> ans+ (a,b,c) -> DieHorribly ("Response code " ++ map intToDigit [a,b,c] ++ " not recognised")+ where+ ans | rqst == HEAD = Done+ | otherwise = ExpectEntity+ ++ +-----------------------------------------------------------------+------------------ A little friendly funtionality ---------------+-----------------------------------------------------------------+++{-+ I had a quick look around but couldn't find any RFC about+ the encoding of data on the query string. I did find an+ IETF memo, however, so this is how I justify the urlEncode+ and urlDecode methods.++ Doc name: draft-tiwari-appl-wxxx-forms-01.txt (look on www.ietf.org)++ Reserved chars: ";", "/", "?", ":", "@", "&", "=", "+", ",", and "$" are reserved.+ Unwise: "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"+ URI delims: "<" | ">" | "#" | "%" | <">+ Unallowed ASCII: <US-ASCII coded characters 00-1F and 7F hexadecimal>+ <US-ASCII coded character 20 hexadecimal>+ Also unallowed: any non-us-ascii character++ Escape method: char -> '%' a b where a, b :: Hex digits+-}++urlDecode :: String -> String+urlDecode ('%':a:b:rest) = toEnum (16 * digitToInt a + digitToInt b)+ : urlDecode rest+urlDecode (h:t) = h : urlDecode t+urlDecode [] = []+++urlEncode :: String -> String+urlEncode [] = []+urlEncode (ch:t) + | (isAscii ch && isAlphaNum ch) || ch `elem` "-_.~" = ch : urlEncode t+ | not (isAscii ch) = foldr escape (urlEncode t) (eightBs [] (fromEnum ch))+ | otherwise = escape (fromEnum ch) (urlEncode t)+ where+ escape b rs = '%':showH (b `div` 16) (showH (b `mod` 16) rs)+ + showH x xs+ | x <= 9 = toEnum (o_0 + x) : xs+ | otherwise = toEnum (o_A + (x-10)) : xs+ where+ o_0 = fromEnum '0'+ o_A = fromEnum 'A'++ eightBs :: [Int] -> Int -> [Int]+ eightBs acc x+ | x <= 0xff = (x:acc)+ | otherwise = eightBs ((x `mod` 256) : acc) (x `div` 256)++-- Encode form variables, useable in either the+-- query part of a URI, or the body of a POST request.+-- I have no source for this information except experience,+-- this sort of encoding worked fine in CGI programming.+urlEncodeVars :: [(String,String)] -> String+urlEncodeVars ((n,v):t) =+ let (same,diff) = partition ((==n) . fst) t+ in urlEncode n ++ '=' : foldl (\x y -> x ++ ',' : urlEncode y) (urlEncode $ v) (map snd same)+ ++ urlEncodeRest diff+ where urlEncodeRest [] = []+ urlEncodeRest diff = '&' : urlEncodeVars diff+urlEncodeVars [] = []++-- | @getAuth req@ fishes out the authority portion of the URL in a request's @Host@+-- header.+getAuth :: Monad m => Request ty -> m URIAuthority+getAuth r = + -- ToDo: verify that Network.URI functionality doesn't take care of this (now.)+ case parseURIAuthority auth of+ Just x -> return x + Nothing -> fail $ "Network.HTTP.Base.getAuth: Error parsing URI authority '" ++ auth ++ "'"+ where + auth = maybe (uriToAuthorityString uri) id (findHeader HdrHost r)+ uri = rqURI r++{-# DEPRECATED normalizeRequestURI "Please use Network.HTTP.Base.normalizeRequest instead" #-}+normalizeRequestURI :: Bool{-do close-} -> {-URI-}String -> Request ty -> Request ty+normalizeRequestURI doClose h r = + (if doClose then replaceHeader HdrConnection "close" else id) $+ insertHeaderIfMissing HdrHost h $+ r { rqURI = (rqURI r){ uriScheme = ""+ , uriAuthority = Nothing+ }}++-- | @NormalizeRequestOptions@ brings together the various defaulting\/normalization options+-- over 'Request's. Use 'defaultNormalizeRequestOptions' for the standard selection of option+data NormalizeRequestOptions ty+ = NormalizeRequestOptions+ { normDoClose :: Bool+ , normForProxy :: Bool+ , normUserAgent :: Maybe String+ , normCustoms :: [RequestNormalizer ty]+ }++-- | @RequestNormalizer@ is the shape of a (pure) function that rewrites+-- a request into some normalized form.+type RequestNormalizer ty = NormalizeRequestOptions ty -> Request ty -> Request ty++defaultNormalizeRequestOptions :: NormalizeRequestOptions ty+defaultNormalizeRequestOptions = NormalizeRequestOptions+ { normDoClose = False+ , normForProxy = False+ , normUserAgent = Just defaultUserAgent+ , normCustoms = []+ }++-- | @normalizeRequest opts req@ is the entry point to use to normalize your+-- request prior to transmission (or other use.) Normalization is controlled+-- via the @NormalizeRequestOptions@ record.+normalizeRequest :: NormalizeRequestOptions ty+ -> Request ty+ -> Request ty+normalizeRequest opts req = foldr (\ f -> f opts) req normalizers+ where+ --normalizers :: [RequestNormalizer ty]+ normalizers = + ( normalizeHostURI+ : normalizeConnectionClose+ : normalizeUserAgent + : normCustoms opts+ )++-- | @normalizeUserAgent ua x req@ augments the request @req@ with +-- a @User-Agent: ua@ header if @req@ doesn't already have a +-- a @User-Agent:@ set.+normalizeUserAgent :: RequestNormalizer ty+normalizeUserAgent opts req = + case normUserAgent opts of+ Nothing -> req+ Just ua -> + case findHeader HdrUserAgent req of+ Just u | u /= defaultUserAgent -> req+ _ -> replaceHeader HdrUserAgent ua req++-- | @normalizeConnectionClose opts req@ sets the header @Connection: close@ +-- to indicate one-shot behavior iff @normDoClose@ is @True@. i.e., it then+-- _replaces_ any an existing @Connection:@ header in @req@.+normalizeConnectionClose :: RequestNormalizer ty+normalizeConnectionClose opts req + | normDoClose opts = replaceHeader HdrConnection "close" req+ | otherwise = req++-- | @normalizeHostURI forProxy req@ rewrites your request to have it+-- follow the expected formats by the receiving party (proxy or server.)+-- +normalizeHostURI :: RequestNormalizer ty+normalizeHostURI opts req = + case splitRequestURI uri of+ ("",_uri_abs)+ | forProxy -> + case findHeader HdrHost req of+ Nothing -> req -- no host/authority in sight..not much we can do.+ Just h -> req{rqURI=uri{ uriAuthority=Just URIAuth{uriUserInfo="", uriRegName=hst, uriPort=pNum}+ , uriScheme=if (null (uriScheme uri)) then "http" else uriScheme uri+ }}+ where + hst = case span (/='@') user_hst of+ (as,'@':bs) -> + case span (/=':') as of+ (_,_:_) -> bs+ _ -> user_hst+ _ -> user_hst++ (user_hst, pNum) = + case span isDigit (reverse h) of+ (ds,':':bs) -> (reverse bs, ':':reverse ds)+ _ -> (h,"")+ | otherwise -> + case findHeader HdrHost req of+ Nothing -> req -- no host/authority in sight..not much we can do...complain?+ Just{} -> req+ (h,uri_abs) + | forProxy -> insertHeaderIfMissing HdrHost h req + | otherwise -> replaceHeader HdrHost h req{rqURI=uri_abs} -- Note: _not_ stubbing out user:pass+ where+ uri0 = rqURI req + -- stub out the user:pass + uri = uri0{uriAuthority=fmap (\ x -> x{uriUserInfo=""}) (uriAuthority uri0)}++ forProxy = normForProxy opts++{- Comments re: above rewriting:+ RFC 2616, section 5.1.2:+ "The most common form of Request-URI is that used to identify a+ resource on an origin server or gateway. In this case the absolute+ path of the URI MUST be transmitted (see section 3.2.1, abs_path) as+ the Request-URI, and the network location of the URI (authority) MUST+ be transmitted in a Host header field." + We assume that this is the case, so we take the host name from+ the Host header if there is one, otherwise from the request-URI.+ Then we make the request-URI an abs_path and make sure that there+ is a Host header.+-}++splitRequestURI :: URI -> ({-authority-}String, URI)+splitRequestURI uri = (uriToAuthorityString uri, uri{uriScheme="", uriAuthority=Nothing})++-- Adds a Host header if one is NOT ALREADY PRESENT..+{-# DEPRECATED normalizeHostHeader "Please use Network.HTTP.Base.normalizeRequest instead" #-}+normalizeHostHeader :: Request ty -> Request ty+normalizeHostHeader rq = + insertHeaderIfMissing HdrHost+ (uriToAuthorityString $ rqURI rq)+ rq+ +-- Looks for a "Connection" header with the value "close".+-- Returns True when this is found.+findConnClose :: [Header] -> Bool+findConnClose hdrs =+ maybe False+ (\ x -> map toLower (trim x) == "close")+ (lookupHeader HdrConnection hdrs)++-- | Used when we know exactly how many bytes to expect.+linearTransfer :: (Int -> IO (Result a)) -> Int -> IO (Result ([Header],a))+linearTransfer readBlk n = fmapE (\str -> Right ([],str)) (readBlk n)++-- | Used when nothing about data is known,+-- Unfortunately waiting for a socket closure+-- causes bad behaviour. Here we just+-- take data once and give up the rest.+hopefulTransfer :: BufferOp a+ -> IO (Result a)+ -> [a]+ -> IO (Result ([Header],a))+hopefulTransfer bufOps readL strs + = readL >>= + either (\v -> return $ Left v)+ (\more -> if (buf_isEmpty bufOps more)+ then return (Right ([],foldr (flip (buf_append bufOps)) (buf_empty bufOps) strs))+ else hopefulTransfer bufOps readL (more:strs))++-- | A necessary feature of HTTP\/1.1+-- Also the only transfer variety likely to+-- return any footers.+chunkedTransfer :: BufferOp a+ -> IO (Result a)+ -> (Int -> IO (Result a))+ -> IO (Result ([Header], a))+chunkedTransfer bufOps readL readBlk = chunkedTransferC bufOps readL readBlk [] 0++chunkedTransferC :: BufferOp a+ -> IO (Result a)+ -> (Int -> IO (Result a))+ -> [a]+ -> Int+ -> IO (Result ([Header], a))+chunkedTransferC bufOps readL readBlk acc n = do+ v <- readL+ case v of+ Left e -> return (Left e)+ Right line + | size == 0 -> + -- last chunk read; look for trailing headers..+ fmapE (\ strs -> do+ ftrs <- parseHeaders (map (buf_toStr bufOps) strs)+ -- insert (computed) Content-Length header.+ let ftrs' = Header HdrContentLength (show n) : ftrs+ return (ftrs',buf_concat bufOps (reverse acc)))++ (readTillEmpty2 bufOps readL [])++ | otherwise -> do+ some <- readBlk size+ case some of+ Left e -> return (Left e)+ Right cdata -> do+ _ <- readL -- CRLF is mandated after the chunk block; ToDo: check that the line is empty.?+ chunkedTransferC bufOps readL readBlk (cdata:acc) (n+size)+ where+ size + | buf_isEmpty bufOps line = 0+ | otherwise = + case readHex (buf_toStr bufOps line) of+ (hx,_):_ -> hx+ _ -> 0++-- | Maybe in the future we will have a sensible thing+-- to do here, at that time we might want to change+-- the name.+uglyDeathTransfer :: String -> IO (Result ([Header],a))+uglyDeathTransfer loc = return (responseParseError loc "Unknown Transfer-Encoding")++-- | Remove leading crlfs then call readTillEmpty2 (not required by RFC)+readTillEmpty1 :: BufferOp a+ -> IO (Result a)+ -> IO (Result [a])+readTillEmpty1 bufOps readL =+ readL >>=+ either (return . Left)+ (\ s -> + if buf_isLineTerm bufOps s+ then readTillEmpty1 bufOps readL+ else readTillEmpty2 bufOps readL [s])++-- | Read lines until an empty line (CRLF),+-- also accepts a connection close as end of+-- input, which is not an HTTP\/1.1 compliant+-- thing to do - so probably indicates an+-- error condition.+readTillEmpty2 :: BufferOp a+ -> IO (Result a)+ -> [a]+ -> IO (Result [a])+readTillEmpty2 bufOps readL list =+ readL >>=+ either (return . Left)+ (\ s ->+ if buf_isLineTerm bufOps s || buf_isEmpty bufOps s+ then return (Right $ reverse (s:list))+ else readTillEmpty2 bufOps readL (s:list))++--+-- Misc+--++-- | @catchIO a h@ handles IO action exceptions throughout codebase; version-specific+-- tweaks better go here.+catchIO :: IO a -> (IOException -> IO a) -> IO a+catchIO a h = Prelude.catch a h++catchIO_ :: IO a -> IO a -> IO a+catchIO_ a h = Prelude.catch a (const h)++responseParseError :: String -> String -> Result a+responseParseError loc v = failWith (ErrorParse (loc ++ ' ':v))
+ Network/HTTP/Base64.hs view
@@ -0,0 +1,282 @@+-----------------------------------------------------------------------------+-- |+-- Module : Codec.Binary.Base64+-- Copyright : (c) Dominic Steinitz 2005, Warrick Gray 2002+-- License : BSD-style (see the file ReadMe.tex)+--+-- Maintainer : dominic.steinitz@blueyonder.co.uk+-- Stability : experimental+-- Portability : portable+--+-- Base64 encoding and decoding functions provided by Warwick Gray. +-- See <http://homepages.paradise.net.nz/warrickg/haskell/http/#base64> +-- and <http://www.faqs.org/rfcs/rfc2045.html>.+--+-----------------------------------------------------------------------------++module Network.HTTP.Base64+ ( encode+ , decode+ , chop72+ , Octet+ ) where++{------------------------------------------------------------------------+This is what RFC2045 had to say:++6.8. Base64 Content-Transfer-Encoding++ The Base64 Content-Transfer-Encoding is designed to represent+ arbitrary sequences of octets in a form that need not be humanly+ readable. The encoding and decoding algorithms are simple, but the+ encoded data are consistently only about 33 percent larger than the+ unencoded data. This encoding is virtually identical to the one used+ in Privacy Enhanced Mail (PEM) applications, as defined in RFC 1421.++ A 65-character subset of US-ASCII is used, enabling 6 bits to be+ represented per printable character. (The extra 65th character, "=",+ is used to signify a special processing function.)++ NOTE: This subset has the important property that it is represented+ identically in all versions of ISO 646, including US-ASCII, and all+ characters in the subset are also represented identically in all+ versions of EBCDIC. Other popular encodings, such as the encoding+ used by the uuencode utility, Macintosh binhex 4.0 [RFC-1741], and+ the base85 encoding specified as part of Level 2 PostScript, do not+ share these properties, and thus do not fulfill the portability+ requirements a binary transport encoding for mail must meet.++ The encoding process represents 24-bit groups of input bits as output+ strings of 4 encoded characters. Proceeding from left to right, a+ 24-bit input group is formed by concatenating 3 8bit input groups.+ These 24 bits are then treated as 4 concatenated 6-bit groups, each+ of which is translated into a single digit in the base64 alphabet.+ When encoding a bit stream via the base64 encoding, the bit stream+ must be presumed to be ordered with the most-significant-bit first.+ That is, the first bit in the stream will be the high-order bit in+ the first 8bit byte, and the eighth bit will be the low-order bit in+ the first 8bit byte, and so on.++ Each 6-bit group is used as an index into an array of 64 printable+ characters. The character referenced by the index is placed in the+ output string. These characters, identified in Table 1, below, are+ selected so as to be universally representable, and the set excludes+ characters with particular significance to SMTP (e.g., ".", CR, LF)+ and to the multipart boundary delimiters defined in RFC 2046 (e.g.,+ "-").++++ Table 1: The Base64 Alphabet++ Value Encoding Value Encoding Value Encoding Value Encoding+ 0 A 17 R 34 i 51 z+ 1 B 18 S 35 j 52 0+ 2 C 19 T 36 k 53 1+ 3 D 20 U 37 l 54 2+ 4 E 21 V 38 m 55 3+ 5 F 22 W 39 n 56 4+ 6 G 23 X 40 o 57 5+ 7 H 24 Y 41 p 58 6+ 8 I 25 Z 42 q 59 7+ 9 J 26 a 43 r 60 8+ 10 K 27 b 44 s 61 9+ 11 L 28 c 45 t 62 ++ 12 M 29 d 46 u 63 /+ 13 N 30 e 47 v+ 14 O 31 f 48 w (pad) =+ 15 P 32 g 49 x+ 16 Q 33 h 50 y++ The encoded output stream must be represented in lines of no more+ than 76 characters each. All line breaks or other characters not+ found in Table 1 must be ignored by decoding software. In base64+ data, characters other than those in Table 1, line breaks, and other+ white space probably indicate a transmission error, about which a+ warning message or even a message rejection might be appropriate+ under some circumstances.++ Special processing is performed if fewer than 24 bits are available+ at the end of the data being encoded. A full encoding quantum is+ always completed at the end of a body. When fewer than 24 input bits+ are available in an input group, zero bits are added (on the right)+ to form an integral number of 6-bit groups. Padding at the end of+ the data is performed using the "=" character. Since all base64+ input is an integral number of octets, only the following cases can+ arise: (1) the final quantum of encoding input is an integral+ multiple of 24 bits; here, the final unit of encoded output will be+ an integral multiple of 4 characters with no "=" padding, (2) the+ final quantum of encoding input is exactly 8 bits; here, the final+ unit of encoded output will be two characters followed by two "="+ padding characters, or (3) the final quantum of encoding input is+ exactly 16 bits; here, the final unit of encoded output will be three+ characters followed by one "=" padding character.++ Because it is used only for padding at the end of the data, the+ occurrence of any "=" characters may be taken as evidence that the+ end of the data has been reached (without truncation in transit). No+ such assurance is possible, however, when the number of octets+ transmitted was a multiple of three and no "=" characters are+ present.++ Any characters outside of the base64 alphabet are to be ignored in+ base64-encoded data.++ Care must be taken to use the proper octets for line breaks if base64+ encoding is applied directly to text material that has not been+ converted to canonical form. In particular, text line breaks must be+ converted into CRLF sequences prior to base64 encoding. The+ important thing to note is that this may be done directly by the+ encoder rather than in a prior canonicalization step in some+ implementations.++ NOTE: There is no need to worry about quoting potential boundary+ delimiters within base64-encoded bodies within multipart entities+ because no hyphen characters are used in the base64 encoding.++----------------------------------------------------------------------------}++{-++The following properties should hold:++ decode . encode = id+ decode . chop72 . encode = id++I.E. Both "encode" and "chop72 . encode" are valid methods of encoding input,+the second variation corresponds better with the RFC above, but outside of+MIME applications might be undesireable.+++But: The Haskell98 Char type is at least 16bits (and often 32), these implementations assume only + 8 significant bits, which is more than enough for US-ASCII. +-}+++import Data.Array (Array, array, (!))+import Data.Bits (shiftL, shiftR, (.&.), (.|.))+import Data.Char (chr, ord)+import Data.Word (Word8)++type Octet = Word8++encodeArray :: Array Int Char+encodeArray = array (0,64) + [ (0,'A'), (1,'B'), (2,'C'), (3,'D'), (4,'E'), (5,'F') + , (6,'G'), (7,'H'), (8,'I'), (9,'J'), (10,'K'), (11,'L') + , (12,'M'), (13,'N'), (14,'O'), (15,'P'), (16,'Q'), (17,'R')+ , (18,'S'), (19,'T'), (20,'U'), (21,'V'), (22,'W'), (23,'X')+ , (24,'Y'), (25,'Z'), (26,'a'), (27,'b'), (28,'c'), (29,'d')+ , (30,'e'), (31,'f'), (32,'g'), (33,'h'), (34,'i'), (35,'j')+ , (36,'k'), (37,'l'), (38,'m'), (39,'n'), (40,'o'), (41,'p')+ , (42,'q'), (43,'r'), (44,'s'), (45,'t'), (46,'u'), (47,'v')+ , (48,'w'), (49,'x'), (50,'y'), (51,'z'), (52,'0'), (53,'1')+ , (54,'2'), (55,'3'), (56,'4'), (57,'5'), (58,'6'), (59,'7')+ , (60,'8'), (61,'9'), (62,'+'), (63,'/') ]+++-- Convert between 4 base64 (6bits ea) integers and 1 ordinary integer (32 bits)+-- clearly the upmost/leftmost 8 bits of the answer are 0.+-- Hack Alert: In the last entry of the answer, the upper 8 bits encode +-- the integer number of 6bit groups encoded in that integer, ie 1, 2, 3.+-- 0 represents a 4 :(+int4_char3 :: [Int] -> [Char]+int4_char3 (a:b:c:d:t) = + let n = (a `shiftL` 18 .|. b `shiftL` 12 .|. c `shiftL` 6 .|. d)+ in (chr (n `shiftR` 16 .&. 0xff))+ : (chr (n `shiftR` 8 .&. 0xff))+ : (chr (n .&. 0xff)) : int4_char3 t++int4_char3 [a,b,c] =+ let n = (a `shiftL` 18 .|. b `shiftL` 12 .|. c `shiftL` 6)+ in [ (chr (n `shiftR` 16 .&. 0xff))+ , (chr (n `shiftR` 8 .&. 0xff)) ]++int4_char3 [a,b] = + let n = (a `shiftL` 18 .|. b `shiftL` 12)+ in [ (chr (n `shiftR` 16 .&. 0xff)) ]++int4_char3 [_] = error "Network.HTTP.Base64.int4_char3: impossible number of Ints."++int4_char3 [] = []+++++-- Convert triplets of characters to+-- 4 base64 integers. The last entries+-- in the list may not produce 4 integers,+-- a trailing 2 character group gives 3 integers,+-- while a trailing single character gives 2 integers.+char3_int4 :: [Char] -> [Int]+char3_int4 (a:b:c:t) + = let n = (ord a `shiftL` 16 .|. ord b `shiftL` 8 .|. ord c)+ in (n `shiftR` 18 .&. 0x3f) : (n `shiftR` 12 .&. 0x3f) : (n `shiftR` 6 .&. 0x3f) : (n .&. 0x3f) : char3_int4 t++char3_int4 [a,b]+ = let n = (ord a `shiftL` 16 .|. ord b `shiftL` 8)+ in [ (n `shiftR` 18 .&. 0x3f)+ , (n `shiftR` 12 .&. 0x3f)+ , (n `shiftR` 6 .&. 0x3f) ]+ +char3_int4 [a]+ = let n = (ord a `shiftL` 16)+ in [(n `shiftR` 18 .&. 0x3f),(n `shiftR` 12 .&. 0x3f)]++char3_int4 [] = []+++-- Retrieve base64 char, given an array index integer in the range [0..63]+enc1 :: Int -> Char+enc1 ch = encodeArray!ch+++-- | Cut up a string into 72 char lines, each line terminated by CRLF.++chop72 :: String -> String+chop72 str = let (bgn,end) = splitAt 70 str+ in if null end then bgn else "\r\n" ++ chop72 end+++-- Pads a base64 code to a multiple of 4 characters, using the special+-- '=' character.+quadruplets :: [Char] -> [Char]+quadruplets (a:b:c:d:t) = a:b:c:d:quadruplets t+quadruplets [a,b,c] = [a,b,c,'='] -- 16bit tail unit+quadruplets [a,b] = [a,b,'=','='] -- 8bit tail unit+quadruplets [_] = error "Network.HTTP.Base64.quadruplets: impossible number of characters."+quadruplets [] = [] -- 24bit tail unit+++enc :: [Int] -> [Char]+enc = quadruplets . map enc1+++dcd :: String -> [Int]+dcd [] = []+dcd (h:t)+ | h <= 'Z' && h >= 'A' = ord h - ord 'A' : dcd t+ | h >= '0' && h <= '9' = ord h - ord '0' + 52 : dcd t+ | h >= 'a' && h <= 'z' = ord h - ord 'a' + 26 : dcd t+ | h == '+' = 62 : dcd t+ | h == '/' = 63 : dcd t+ | h == '=' = [] -- terminate data stream+ | otherwise = dcd t+++-- Principal encoding and decoding functions.++encode :: [Octet] -> String+encode = enc . char3_int4 . (map (chr .fromIntegral))++{-+prop_base64 os =+ os == (f . g . h) os+ where types = (os :: [Word8])+ f = map (fromIntegral. ord)+ g = decode . encode+ h = map (chr . fromIntegral)+-}++decode :: String -> [Octet]+decode = (map (fromIntegral . ord)) . int4_char3 . dcd
+ Network/HTTP/Cookie.hs view
@@ -0,0 +1,141 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Cookie+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- This module provides the data types and functions for working with HTTP cookies.+-- Right now, it contains mostly functionality needed by 'Network.Browser'.+-- +-----------------------------------------------------------------------------+module Network.HTTP.Cookie+ ( Cookie(..)+ , cookieMatch -- :: (String,String) -> Cookie -> Bool++ -- functions for translating cookies and headers.+ , cookiesToHeader -- :: [Cookie] -> Header+ , processCookieHeaders -- :: String -> [Header] -> ([String], [Cookie])+ ) where++import Network.HTTP.Headers++import Data.Char+import Data.List+import Data.Maybe++import Text.ParserCombinators.Parsec+ ( Parser, char, many, many1, satisfy, parse, option, try+ , (<|>), sepBy1+ )++------------------------------------------------------------------+----------------------- Cookie Stuff -----------------------------+------------------------------------------------------------------++-- | @Cookie@ is the Haskell representation of HTTP cookie values.+-- See its relevant specs for authoritative details.+data Cookie + = MkCookie + { ckDomain :: String+ , ckName :: String+ , ckValue :: String+ , ckPath :: Maybe String+ , ckComment :: Maybe String+ , ckVersion :: Maybe String+ }+ deriving(Show,Read)++instance Eq Cookie where+ a == b = ckDomain a == ckDomain b + && ckName a == ckName b + && ckPath a == ckPath b++-- | @cookieToHeaders ck@ serialises @Cookie@s to an HTTP request header.+cookiesToHeader :: [Cookie] -> Header+cookiesToHeader cs = Header HdrCookie (mkCookieHeaderValue cs)++-- | Turn a list of cookies into a key=value pair list, separated by+-- semicolons.+mkCookieHeaderValue :: [Cookie] -> String+mkCookieHeaderValue = intercalate "; " . map mkCookieHeaderValue1+ where+ mkCookieHeaderValue1 c = ckName c ++ "=" ++ ckValue c++-- | @cookieMatch (domain,path) ck@ performs the standard cookie+-- match wrt the given domain and path. +cookieMatch :: (String, String) -> Cookie -> Bool+cookieMatch (dom,path) ck =+ ckDomain ck `isSuffixOf` dom &&+ case ckPath ck of+ Nothing -> True+ Just p -> p `isPrefixOf` path+++-- | @processCookieHeaders dom hdrs@ +processCookieHeaders :: String -> [Header] -> ([String], [Cookie])+processCookieHeaders dom hdrs = foldr (headerToCookies dom) ([],[]) hdrs++-- | @headerToCookies dom hdr acc@ +headerToCookies :: String -> Header -> ([String], [Cookie]) -> ([String], [Cookie])+headerToCookies dom (Header HdrSetCookie val) (accErr, accCookie) = + case parse cookies "" val of+ Left{} -> (val:accErr, accCookie)+ Right x -> (accErr, x ++ accCookie)+ where+ cookies :: Parser [Cookie]+ cookies = sepBy1 cookie (char ',')++ cookie :: Parser Cookie+ cookie =+ do name <- word+ _ <- spaces_l+ _ <- char '='+ _ <- spaces_l+ val1 <- cvalue+ args <- cdetail+ return $ mkCookie name val1 args++ cvalue :: Parser String+ + spaces_l = many (satisfy isSpace)++ cvalue = quotedstring <|> many1 (satisfy $ not . (==';')) <|> return ""+ + -- all keys in the result list MUST be in lower case+ cdetail :: Parser [(String,String)]+ cdetail = many $+ try (do _ <- spaces_l+ _ <- char ';'+ _ <- spaces_l+ s1 <- word+ _ <- spaces_l+ s2 <- option "" (char '=' >> spaces_l >> cvalue)+ return (map toLower s1,s2)+ )++ mkCookie :: String -> String -> [(String,String)] -> Cookie+ mkCookie nm cval more = + MkCookie { ckName = nm+ , ckValue = cval+ , ckDomain = map toLower (fromMaybe dom (lookup "domain" more))+ , ckPath = lookup "path" more+ , ckVersion = lookup "version" more+ , ckComment = lookup "comment" more+ }+headerToCookies _ _ acc = acc++ +++word, quotedstring :: Parser String+quotedstring =+ do _ <- char '"' -- "+ str <- many (satisfy $ not . (=='"'))+ _ <- char '"'+ return str++word = many1 (satisfy (\x -> isAlphaNum x || x=='_' || x=='.' || x=='-' || x==':'))
+ Network/HTTP/HandleStream.hs view
@@ -0,0 +1,246 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.HandleStream+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- A 'HandleStream'-based version of "Network.HTTP" interface.+--+-- For more detailed information about what the individual exports do, please consult+-- the documentation for "Network.HTTP". /Notice/ however that the functions here do+-- not perform any kind of normalization prior to transmission (or receipt); you are+-- responsible for doing any such yourself, or, if you prefer, just switch to using+-- "Network.HTTP" function instead.+-- +-----------------------------------------------------------------------------+module Network.HTTP.HandleStream + ( simpleHTTP -- :: Request ty -> IO (Result (Response ty))+ , simpleHTTP_ -- :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+ , sendHTTP -- :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+ , sendHTTP_notify -- :: HStream ty => HandleStream ty -> Request ty -> IO () -> IO (Result (Response ty))+ , receiveHTTP -- :: HStream ty => HandleStream ty -> IO (Result (Request ty))+ , respondHTTP -- :: HStream ty => HandleStream ty -> Response ty -> IO ()+ + , simpleHTTP_debug -- :: FilePath -> Request DebugString -> IO (Response DebugString)+ ) where++-----------------------------------------------------------------+------------------ Imports --------------------------------------+-----------------------------------------------------------------++import Network.BufferType+import Network.Stream ( fmapE, Result )+import Network.StreamDebugger ( debugByteStream )+import Network.TCP (HStream(..), HandleStream )++import Network.HTTP.Base+import Network.HTTP.Headers+import Network.HTTP.Utils ( trim, readsOne )++import Data.Char (toLower)+import Data.Maybe (fromMaybe)+import Control.Monad (when)++-----------------------------------------------------------------+------------------ Misc -----------------------------------------+-----------------------------------------------------------------++-- | @simpleHTTP@ transmits a resource across a non-persistent connection.+simpleHTTP :: HStream ty => Request ty -> IO (Result (Response ty))+simpleHTTP r = do + auth <- getAuth r+ failHTTPS (rqURI r)+ c <- openStream (host auth) (fromMaybe 80 (port auth))+ simpleHTTP_ c r++-- | @simpleHTTP_debug debugFile req@ behaves like 'simpleHTTP', but logs+-- the HTTP operation via the debug file @debugFile@.+simpleHTTP_debug :: HStream ty => FilePath -> Request ty -> IO (Result (Response ty))+simpleHTTP_debug httpLogFile r = do + auth <- getAuth r+ failHTTPS (rqURI r)+ c0 <- openStream (host auth) (fromMaybe 80 (port auth))+ c <- debugByteStream httpLogFile c0+ simpleHTTP_ c r++-- | Like 'simpleHTTP', but acting on an already opened stream.+simpleHTTP_ :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+simpleHTTP_ s r = sendHTTP s r++-- | @sendHTTP hStream httpRequest@ transmits @httpRequest@ over+-- @hStream@, but does not alter the status of the connection, nor request it to be+-- closed upon receiving the response.+sendHTTP :: HStream ty => HandleStream ty -> Request ty -> IO (Result (Response ty))+sendHTTP conn rq = sendHTTP_notify conn rq (return ())++-- | @sendHTTP_notify hStream httpRequest action@ behaves like 'sendHTTP', but+-- lets you supply an IO @action@ to execute once the request has been successfully+-- transmitted over the connection. Useful when you want to set up tracing of+-- request transmission and its performance.+sendHTTP_notify :: HStream ty+ => HandleStream ty+ -> Request ty+ -> IO ()+ -> IO (Result (Response ty))+sendHTTP_notify conn rq onSendComplete = do+ when providedClose $ (closeOnEnd conn True)+ catchIO (sendMain conn rq onSendComplete)+ (\e -> do { close conn; ioError e })+ where+ providedClose = findConnClose (rqHeaders rq)++-- From RFC 2616, section 8.2.3:+-- 'Because of the presence of older implementations, the protocol allows+-- ambiguous situations in which a client may send "Expect: 100-+-- continue" without receiving either a 417 (Expectation Failed) status+-- or a 100 (Continue) status. Therefore, when a client sends this+-- header field to an origin server (possibly via a proxy) from which it+-- has never seen a 100 (Continue) status, the client SHOULD NOT wait+-- for an indefinite period before sending the request body.'+--+-- Since we would wait forever, I have disabled use of 100-continue for now.+sendMain :: HStream ty+ => HandleStream ty+ -> Request ty+ -> (IO ())+ -> IO (Result (Response ty))+sendMain conn rqst onSendComplete = do+ --let str = if null (rqBody rqst)+ -- then show rqst+ -- else show (insertHeader HdrExpect "100-continue" rqst)+ writeBlock conn (buf_fromStr bufferOps $ show rqst)+ -- write body immediately, don't wait for 100 CONTINUE+ writeBlock conn (rqBody rqst)+ onSendComplete+ rsp <- getResponseHead conn+ switchResponse conn True False rsp rqst++ -- Hmmm, this could go bad if we keep getting "100 Continue"+ -- responses... Except this should never happen according+ -- to the RFC.++switchResponse :: HStream ty+ => HandleStream ty+ -> Bool {- allow retry? -}+ -> Bool {- is body sent? -}+ -> Result ResponseData+ -> Request ty+ -> IO (Result (Response ty))+switchResponse _ _ _ (Left e) _ = return (Left e)+ -- retry on connreset?+ -- if we attempt to use the same socket then there is an excellent+ -- chance that the socket is not in a completely closed state.++switchResponse conn allow_retry bdy_sent (Right (cd,rn,hdrs)) rqst = + case matchResponse (rqMethod rqst) cd of+ Continue+ | not bdy_sent -> do {- Time to send the body -}+ writeBlock conn (rqBody rqst) >>= either (return . Left)+ (\ _ -> do+ rsp <- getResponseHead conn+ switchResponse conn allow_retry True rsp rqst)+ | otherwise -> do {- keep waiting -}+ rsp <- getResponseHead conn+ switchResponse conn allow_retry bdy_sent rsp rqst++ Retry -> do {- Request with "Expect" header failed.+ Trouble is the request contains Expects+ other than "100-Continue" -}+ writeBlock conn ((buf_append bufferOps)+ (buf_fromStr bufferOps (show rqst))+ (rqBody rqst))+ rsp <- getResponseHead conn+ switchResponse conn False bdy_sent rsp rqst+ + Done -> do+ when (findConnClose hdrs)+ (closeOnEnd conn True)+ return (Right $ Response cd rn hdrs (buf_empty bufferOps))++ DieHorribly str -> do+ close conn+ return (responseParseError "Invalid response:" str)+ ExpectEntity -> do+ r <- fmapE (\ (ftrs,bdy) -> Right (Response cd rn (hdrs++ftrs) bdy)) $+ maybe (maybe (hopefulTransfer bo (readLine conn) [])+ (\ x -> + readsOne (linearTransfer (readBlock conn))+ (return$responseParseError "unrecognized content-length value" x)+ x)+ cl)+ (ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))+ (uglyDeathTransfer "sendHTTP"))+ tc+ case r of+ Left{} -> do+ close conn+ return r+ Right (Response _ _ hs _) -> do+ when (findConnClose hs)+ (closeOnEnd conn True)+ return r++ where+ tc = lookupHeader HdrTransferEncoding hdrs+ cl = lookupHeader HdrContentLength hdrs+ bo = bufferOps+ +-- reads and parses headers+getResponseHead :: HStream ty => HandleStream ty -> IO (Result ResponseData)+getResponseHead conn = + fmapE (\es -> parseResponseHead (map (buf_toStr bufferOps) es))+ (readTillEmpty1 bufferOps (readLine conn))++-- | @receiveHTTP hStream@ reads a 'Request' from the 'HandleStream' @hStream@+receiveHTTP :: HStream bufTy => HandleStream bufTy -> IO (Result (Request bufTy))+receiveHTTP conn = getRequestHead >>= either (return . Left) processRequest+ where+ -- reads and parses headers+ getRequestHead :: IO (Result RequestData)+ getRequestHead = do+ fmapE (\es -> parseRequestHead (map (buf_toStr bufferOps) es))+ (readTillEmpty1 bufferOps (readLine conn))+ + processRequest (rm,uri,hdrs) =+ fmapE (\ (ftrs,bdy) -> Right (Request uri rm (hdrs++ftrs) bdy)) $+ maybe + (maybe (return (Right ([], buf_empty bo))) -- hopefulTransfer ""+ (\ x -> readsOne (linearTransfer (readBlock conn))+ (return$responseParseError "unrecognized Content-Length value" x)+ x)+ + cl)+ (ifChunked (chunkedTransfer bo (readLine conn) (readBlock conn))+ (uglyDeathTransfer "receiveHTTP"))+ tc+ where+ -- FIXME : Also handle 100-continue.+ tc = lookupHeader HdrTransferEncoding hdrs+ cl = lookupHeader HdrContentLength hdrs+ bo = bufferOps++-- | @respondHTTP hStream httpResponse@ transmits an HTTP 'Response' over+-- the 'HandleStream' @hStream@. It could be used to implement simple web+-- server interactions, performing the dual role to 'sendHTTP'.+respondHTTP :: HStream ty => HandleStream ty -> Response ty -> IO ()+respondHTTP conn rsp = do + writeBlock conn (buf_fromStr bufferOps $ show rsp)+ -- write body immediately, don't wait for 100 CONTINUE+ writeBlock conn (rspBody rsp)+ return ()++------------------------------------------------------------------------------++headerName :: String -> String+headerName x = map toLower (trim x)++ifChunked :: a -> a -> String -> a+ifChunked a b s = + case headerName s of+ "chunked" -> a+ _ -> b+
+ Network/HTTP/Headers.hs view
@@ -0,0 +1,306 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Headers+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- This module provides the data types for representing HTTP headers, and+-- operations for looking up header values and working with sequences of+-- header values in 'Request's and 'Response's. To avoid having to provide+-- separate set of operations for doing so, we introduce a type class 'HasHeaders'+-- to facilitate writing such processing using overloading instead.+-- +-----------------------------------------------------------------------------+module Network.HTTP.Headers+ ( HasHeaders(..) -- type class++ , Header(..)+ , mkHeader -- :: HeaderName -> String -> Header+ , hdrName -- :: Header -> HeaderName+ , hdrValue -- :: Header -> String++ , HeaderName(..)++ , insertHeader -- :: HasHeaders a => HeaderName -> String -> a -> a+ , insertHeaderIfMissing -- :: HasHeaders a => HeaderName -> String -> a -> a+ , insertHeaders -- :: HasHeaders a => [Header] -> a -> a+ , retrieveHeaders -- :: HasHeaders a => HeaderName -> a -> [Header]+ , replaceHeader -- :: HasHeaders a => HeaderName -> String -> a -> a+ , findHeader -- :: HasHeaders a => HeaderName -> a -> Maybe String+ , lookupHeader -- :: HeaderName -> [Header] -> Maybe String++ , parseHeader -- :: parseHeader :: String -> Result Header+ , parseHeaders -- :: [String] -> Result [Header]+ + , headerMap -- :: [(String, HeaderName)]+ + , HeaderSetter+ ) where++import Data.Char (toLower)+import Network.Stream (Result, failParse)+import Network.HTTP.Utils ( trim, split, crlf )++-- | The @Header@ data type pairs header names & values.+data Header = Header HeaderName String++hdrName :: Header -> HeaderName+hdrName (Header h _) = h++hdrValue :: Header -> String+hdrValue (Header _ v) = v++-- | Header constructor as a function, hiding above rep.+mkHeader :: HeaderName -> String -> Header+mkHeader = Header++instance Show Header where+ show (Header key value) = shows key (':':' ':value ++ crlf)++-- | HTTP @HeaderName@ type, a Haskell data constructor for each+-- specification-defined header, prefixed with @Hdr@ and CamelCased,+-- (i.e., eliding the @-@ in the process.) Should you require using+-- a custom header, there's the @HdrCustom@ constructor which takes+-- a @String@ argument.+--+-- Encoding HTTP header names differently, as Strings perhaps, is an+-- equally fine choice..no decidedly clear winner, but let's stick+-- with data constructors here.+-- +data HeaderName + -- Generic Headers --+ = HdrCacheControl+ | HdrConnection+ | HdrDate+ | HdrPragma+ | HdrTransferEncoding + | HdrUpgrade + | HdrVia+ -- Request Headers --+ | HdrAccept+ | HdrAcceptCharset+ | HdrAcceptEncoding+ | HdrAcceptLanguage+ | HdrAuthorization+ | HdrCookie+ | HdrExpect+ | HdrFrom+ | HdrHost+ | HdrIfModifiedSince+ | HdrIfMatch+ | HdrIfNoneMatch+ | HdrIfRange+ | HdrIfUnmodifiedSince+ | HdrMaxForwards+ | HdrProxyAuthorization+ | HdrRange+ | HdrReferer+ | HdrUserAgent+ -- Response Headers+ | HdrAge+ | HdrLocation+ | HdrProxyAuthenticate+ | HdrPublic+ | HdrRetryAfter+ | HdrServer+ | HdrSetCookie+ | HdrTE+ | HdrTrailer+ | HdrVary+ | HdrWarning+ | HdrWWWAuthenticate+ -- Entity Headers+ | HdrAllow+ | HdrContentBase+ | HdrContentEncoding+ | HdrContentLanguage+ | HdrContentLength+ | HdrContentLocation+ | HdrContentMD5+ | HdrContentRange+ | HdrContentType+ | HdrETag+ | HdrExpires+ | HdrLastModified+ -- | MIME entity headers (for sub-parts)+ | HdrContentTransferEncoding+ -- | Allows for unrecognised or experimental headers.+ | HdrCustom String -- not in header map below.+ deriving(Eq)++-- | @headerMap@ is a straight assoc list for translating between header names +-- and values.+headerMap :: [ (String,HeaderName) ]+headerMap =+ [ p "Cache-Control" HdrCacheControl+ , p "Connection" HdrConnection+ , p "Date" HdrDate+ , p "Pragma" HdrPragma+ , p "Transfer-Encoding" HdrTransferEncoding+ , p "Upgrade" HdrUpgrade+ , p "Via" HdrVia+ , p "Accept" HdrAccept+ , p "Accept-Charset" HdrAcceptCharset+ , p "Accept-Encoding" HdrAcceptEncoding+ , p "Accept-Language" HdrAcceptLanguage+ , p "Authorization" HdrAuthorization+ , p "Cookie" HdrCookie+ , p "Expect" HdrExpect+ , p "From" HdrFrom+ , p "Host" HdrHost+ , p "If-Modified-Since" HdrIfModifiedSince+ , p "If-Match" HdrIfMatch+ , p "If-None-Match" HdrIfNoneMatch+ , p "If-Range" HdrIfRange+ , p "If-Unmodified-Since" HdrIfUnmodifiedSince+ , p "Max-Forwards" HdrMaxForwards+ , p "Proxy-Authorization" HdrProxyAuthorization+ , p "Range" HdrRange+ , p "Referer" HdrReferer+ , p "User-Agent" HdrUserAgent+ , p "Age" HdrAge+ , p "Location" HdrLocation+ , p "Proxy-Authenticate" HdrProxyAuthenticate+ , p "Public" HdrPublic+ , p "Retry-After" HdrRetryAfter+ , p "Server" HdrServer+ , p "Set-Cookie" HdrSetCookie+ , p "TE" HdrTE+ , p "Trailer" HdrTrailer+ , p "Vary" HdrVary+ , p "Warning" HdrWarning+ , p "WWW-Authenticate" HdrWWWAuthenticate+ , p "Allow" HdrAllow+ , p "Content-Base" HdrContentBase+ , p "Content-Encoding" HdrContentEncoding+ , p "Content-Language" HdrContentLanguage+ , p "Content-Length" HdrContentLength+ , p "Content-Location" HdrContentLocation+ , p "Content-MD5" HdrContentMD5+ , p "Content-Range" HdrContentRange+ , p "Content-Type" HdrContentType+ , p "ETag" HdrETag+ , p "Expires" HdrExpires+ , p "Last-Modified" HdrLastModified+ , p "Content-Transfer-Encoding" HdrContentTransferEncoding+ ]+ where+ p a b = (a,b)++instance Show HeaderName where+ show (HdrCustom s) = s+ show x = case filter ((==x).snd) headerMap of+ [] -> error "headerMap incomplete"+ (h:_) -> fst h++-- | @HasHeaders@ is a type class for types containing HTTP headers, allowing+-- you to write overloaded header manipulation functions+-- for both 'Request' and 'Response' data types, for instance.+class HasHeaders x where+ getHeaders :: x -> [Header]+ setHeaders :: x -> [Header] -> x++-- Header manipulation functions++type HeaderSetter a = HeaderName -> String -> a -> a++-- | @insertHeader hdr val x@ inserts a header with the given header name+-- and value. Does not check for existing headers with same name, allowing+-- duplicates to be introduce (use 'replaceHeader' if you want to avoid this.)+insertHeader :: HasHeaders a => HeaderSetter a+insertHeader name value x = setHeaders x newHeaders+ where+ newHeaders = (Header name value) : getHeaders x++-- | @insertHeaderIfMissing hdr val x@ adds the new header only if no previous+-- header with name @hdr@ exists in @x@.+insertHeaderIfMissing :: HasHeaders a => HeaderSetter a+insertHeaderIfMissing name value x = setHeaders x (newHeaders $ getHeaders x)+ where+ newHeaders list@(h@(Header n _): rest)+ | n == name = list+ | otherwise = h : newHeaders rest+ newHeaders [] = [Header name value]++-- | @replaceHeader hdr val o@ replaces the header @hdr@ with the+-- value @val@, dropping any existing +replaceHeader :: HasHeaders a => HeaderSetter a+replaceHeader name value h = setHeaders h newHeaders+ where+ newHeaders = Header name value : [ x | x@(Header n _) <- getHeaders h, name /= n ]+ +-- | @insertHeaders hdrs x@ appends multiple headers to @x@'s existing+-- set.+insertHeaders :: HasHeaders a => [Header] -> a -> a+insertHeaders hdrs x = setHeaders x (getHeaders x ++ hdrs)++-- | @retrieveHeaders hdrNm x@ gets a list of headers with 'HeaderName' @hdrNm@.+retrieveHeaders :: HasHeaders a => HeaderName -> a -> [Header]+retrieveHeaders name x = filter matchname (getHeaders x)+ where+ matchname (Header n _) = n == name ++-- | @findHeader hdrNm x@ looks up @hdrNm@ in @x@, returning the first+-- header that matches, if any.+findHeader :: HasHeaders a => HeaderName -> a -> Maybe String+findHeader n x = lookupHeader n (getHeaders x)++-- | @lookupHeader hdr hdrs@ locates the first header matching @hdr@ in the+-- list @hdrs@.+lookupHeader :: HeaderName -> [Header] -> Maybe String+lookupHeader _ [] = Nothing+lookupHeader v (Header n s:t) + | v == n = Just s+ | otherwise = lookupHeader v t++-- | @parseHeader headerNameAndValueString@ tries to unscramble a+-- @header: value@ pairing and returning it as a 'Header'.+parseHeader :: String -> Result Header+parseHeader str =+ case split ':' str of+ Nothing -> failParse ("Unable to parse header: " ++ str)+ Just (k,v) -> return $ Header (fn k) (trim $ drop 1 v)+ where+ fn k = case map snd $ filter (match k . fst) headerMap of+ [] -> (HdrCustom k)+ (h:_) -> h++ match :: String -> String -> Bool+ match s1 s2 = map toLower s1 == map toLower s2+ +-- | @parseHeaders hdrs@ takes a sequence of strings holding header+-- information and parses them into a set of headers (preserving their+-- order in the input argument.) Handles header values split up over+-- multiple lines.+parseHeaders :: [String] -> Result [Header]+parseHeaders = catRslts [] . + map (parseHeader . clean) . + joinExtended ""+ where+ -- Joins consecutive lines where the second line+ -- begins with ' ' or '\t'.+ joinExtended old [] = [old]+ joinExtended old (h : t)+ | isLineExtension h = joinExtended (old ++ ' ' : tail h) t+ | otherwise = old : joinExtended h t+ + isLineExtension (x:_) = x == ' ' || x == '\t'+ isLineExtension _ = False++ clean [] = []+ clean (h:t) | h `elem` "\t\r\n" = ' ' : clean t+ | otherwise = h : clean t++ -- tolerant of errors? should parse+ -- errors here be reported or ignored?+ -- currently ignored.+ catRslts :: [a] -> [Result a] -> Result [a]+ catRslts list (h:t) = + case h of+ Left _ -> catRslts list t+ Right v -> catRslts (v:list) t+ catRslts list [] = Right $ reverse list
+ Network/HTTP/MD5Aux.hs view
@@ -0,0 +1,342 @@+module Network.HTTP.MD5Aux + (md5, md5s, md5i,+ MD5(..), ABCD(..), + Zord64, Str(..), BoolList(..), WordList(..)) where++import Data.Char (ord, chr)+import Data.Bits (rotateL, shiftL, shiftR, (.&.), (.|.), xor, complement)+import Data.Word (Word32, Word64)++rotL :: Word32 -> Int -> Word32+rotL x = rotateL x++type Zord64 = Word64++-- ===================== TYPES AND CLASS DEFINTIONS ========================+++type XYZ = (Word32, Word32, Word32)+type Rotation = Int+newtype ABCD = ABCD (Word32, Word32, Word32, Word32) deriving (Eq, Show)+newtype Str = Str String+newtype BoolList = BoolList [Bool]+newtype WordList = WordList ([Word32], Word64)++-- Anything we want to work out the MD5 of must be an instance of class MD5++class MD5 a where+ get_next :: a -> ([Word32], Int, a) -- get the next blocks worth+ -- \ \ \------ the rest of the input+ -- \ \--------- the number of bits returned+ -- \--------------- the bits returned in 32bit words+ len_pad :: Word64 -> a -> a -- append the padding and length+ finished :: a -> Bool -- Have we run out of input yet?+++-- Mainly exists because it's fairly easy to do MD5s on input where the+-- length is not a multiple of 8++instance MD5 BoolList where+ get_next (BoolList s) = (bools_to_word32s ys, length ys, BoolList zs)+ where (ys, zs) = splitAt 512 s+ len_pad l (BoolList bs)+ = BoolList (bs ++ [True]+ ++ replicate (fromIntegral $ (447 - l) .&. 511) False+ ++ [l .&. (shiftL 1 x) > 0 | x <- (mangle [0..63])]+ )+ where mangle [] = []+ mangle xs = reverse ys ++ mangle zs+ where (ys, zs) = splitAt 8 xs+ finished (BoolList s) = s == []+++-- The string instance is fairly straightforward++instance MD5 Str where+ get_next (Str s) = (string_to_word32s ys, 8 * length ys, Str zs)+ where (ys, zs) = splitAt 64 s+ len_pad c64 (Str s) = Str (s ++ padding ++ l)+ where padding = '\128':replicate (fromIntegral zeros) '\000'+ zeros = shiftR ((440 - c64) .&. 511) 3+ l = length_to_chars 8 c64+ finished (Str s) = s == ""+++-- YA instance that is believed will be useful++instance MD5 WordList where+ get_next (WordList (ws, l)) = (xs, fromIntegral taken, WordList (ys, l - taken))+ where (xs, ys) = splitAt 16 ws+ taken = if l > 511 then 512 else l .&. 511+ len_pad c64 (WordList (ws, l)) = WordList (beginning ++ nextish ++ blanks ++ size, newlen)+ where beginning = if length ws > 0 then start ++ lastone' else []+ start = init ws+ lastone = last ws+ offset = c64 .&. 31+ lastone' = [if offset > 0 then lastone + theone else lastone]+ theone = shiftL (shiftR 128 (fromIntegral $ offset .&. 7))+ (fromIntegral $ offset .&. (31 - 7))+ nextish = if offset == 0 then [128] else []+ c64' = c64 + (32 - offset)+ num_blanks = (fromIntegral $ shiftR ((448 - c64') .&. 511) 5)+ blanks = replicate num_blanks 0+ lowsize = fromIntegral $ c64 .&. (shiftL 1 32 - 1)+ topsize = fromIntegral $ shiftR c64 32+ size = [lowsize, topsize]+ newlen = l .&. (complement 511)+ + if c64 .&. 511 >= 448 then 1024 else 512+ finished (WordList (_, z)) = z == 0+++instance Num ABCD where+ ABCD (a1, b1, c1, d1) + ABCD (a2, b2, c2, d2) = ABCD (a1 + a2, b1 + b2, c1 + c2, d1 + d2)++ (*) = error "(*){ABCD}: no instance method defined"+ signum = error "signum{ABCD}: no instance method defined"+ fromInteger = error "fromInteger{ABCD}: no instance method defined"+ abs = error "abs{ABCD}: no instance method defined"+-- ===================== EXPORTED FUNCTIONS ========================+++-- The simplest function, gives you the MD5 of a string as 4-tuple of+-- 32bit words.++md5 :: (MD5 a) => a -> ABCD+md5 m = md5_main False 0 magic_numbers m+++-- Returns a hex number ala the md5sum program++md5s :: (MD5 a) => a -> String+md5s = abcd_to_string . md5+++-- Returns an integer equivalent to the above hex number++md5i :: (MD5 a) => a -> Integer+md5i = abcd_to_integer . md5+++-- ===================== THE CORE ALGORITHM ========================+++-- Decides what to do. The first argument indicates if padding has been+-- added. The second is the length mod 2^64 so far. Then we have the+-- starting state, the rest of the string and the final state.++md5_main :: (MD5 a) =>+ Bool -- Have we added padding yet?+ -> Word64 -- The length so far mod 2^64+ -> ABCD -- The initial state+ -> a -- The non-processed portion of the message+ -> ABCD -- The resulting state+md5_main padded ilen abcd m+ = if finished m && padded+ then abcd+ else md5_main padded' (ilen + 512) (abcd + abcd') m''+ where (m16, l, m') = get_next m+ len' = ilen + fromIntegral l+ ((m16', _, m''), padded') = if not padded && l < 512+ then (get_next $ len_pad len' m, True)+ else ((m16, l, m'), padded)+ abcd' = md5_do_block abcd m16'+++-- md5_do_block processes a 512 bit block by calling md5_round 4 times to+-- apply each round with the correct constants and permutations of the+-- block++md5_do_block :: ABCD -- Initial state+ -> [Word32] -- The block to be processed - 16 32bit words+ -> ABCD -- Resulting state+md5_do_block abcd0 w = abcd4+ where (r1, r2, r3, r4) = rounds+ {-+ map (\x -> w !! x) [1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12]+ -- [(5 * x + 1) `mod` 16 | x <- [0..15]]+ map (\x -> w !! x) [5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2]+ -- [(3 * x + 5) `mod` 16 | x <- [0..15]]+ map (\x -> w !! x) [0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9]+ -- [(7 * x) `mod` 16 | x <- [0..15]]+ -}+ perm5 [c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15]+ = [c1,c6,c11,c0,c5,c10,c15,c4,c9,c14,c3,c8,c13,c2,c7,c12]+ perm5 _ = error "broke at perm5"+ perm3 [c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15]+ = [c5,c8,c11,c14,c1,c4,c7,c10,c13,c0,c3,c6,c9,c12,c15,c2]+ perm3 _ = error "broke at perm3"+ perm7 [c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13,c14,c15]+ = [c0,c7,c14,c5,c12,c3,c10,c1,c8,c15,c6,c13,c4,c11,c2,c9]+ perm7 _ = error "broke at perm7"+ abcd1 = md5_round md5_f abcd0 w r1+ abcd2 = md5_round md5_g abcd1 (perm5 w) r2+ abcd3 = md5_round md5_h abcd2 (perm3 w) r3+ abcd4 = md5_round md5_i abcd3 (perm7 w) r4+++-- md5_round does one of the rounds. It takes an auxiliary function and foldls+-- (md5_inner_function f) to repeatedly apply it to the initial state with the+-- correct constants++md5_round :: (XYZ -> Word32) -- Auxiliary function (F, G, H or I+ -- for those of you with a copy of+ -- the prayer book^W^WRFC)+ -> ABCD -- Initial state+ -> [Word32] -- The 16 32bit words of input+ -> [(Rotation, Word32)] -- The list of 16 rotations and+ -- additive constants+ -> ABCD -- Resulting state+md5_round f abcd s ns = foldl (md5_inner_function f) abcd ns'+ where ns' = zipWith (\x (y, z) -> (y, x + z)) s ns+++-- Apply one of the functions md5_[fghi] and put the new ABCD together++md5_inner_function :: (XYZ -> Word32) -- Auxiliary function+ -> ABCD -- Initial state+ -> (Rotation, Word32) -- The rotation and additive+ -- constant (X[i] + T[j])+ -> ABCD -- Resulting state+md5_inner_function f (ABCD (a, b, c, d)) (s, ki) = ABCD (d, a', b, c)+ where mid_a = a + f(b,c,d) + ki+ rot_a = rotL mid_a s+ a' = b + rot_a+++-- The 4 auxiliary functions++md5_f :: XYZ -> Word32+md5_f (x, y, z) = z `xor` (x .&. (y `xor` z))+{- optimised version of: (x .&. y) .|. ((complement x) .&. z) -}++md5_g :: XYZ -> Word32+md5_g (x, y, z) = md5_f (z, x, y)+{- was: (x .&. z) .|. (y .&. (complement z)) -}++md5_h :: XYZ -> Word32+md5_h (x, y, z) = x `xor` y `xor` z++md5_i :: XYZ -> Word32+md5_i (x, y, z) = y `xor` (x .|. (complement z))+++-- The magic numbers from the RFC.++magic_numbers :: ABCD+magic_numbers = ABCD (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476)+++-- The 4 lists of (rotation, additive constant) tuples, one for each round++rounds :: ([(Rotation, Word32)],+ [(Rotation, Word32)],+ [(Rotation, Word32)],+ [(Rotation, Word32)])+rounds = (r1, r2, r3, r4)+ where r1 = [(s11, 0xd76aa478), (s12, 0xe8c7b756), (s13, 0x242070db),+ (s14, 0xc1bdceee), (s11, 0xf57c0faf), (s12, 0x4787c62a),+ (s13, 0xa8304613), (s14, 0xfd469501), (s11, 0x698098d8),+ (s12, 0x8b44f7af), (s13, 0xffff5bb1), (s14, 0x895cd7be),+ (s11, 0x6b901122), (s12, 0xfd987193), (s13, 0xa679438e),+ (s14, 0x49b40821)]+ r2 = [(s21, 0xf61e2562), (s22, 0xc040b340), (s23, 0x265e5a51),+ (s24, 0xe9b6c7aa), (s21, 0xd62f105d), (s22, 0x2441453),+ (s23, 0xd8a1e681), (s24, 0xe7d3fbc8), (s21, 0x21e1cde6),+ (s22, 0xc33707d6), (s23, 0xf4d50d87), (s24, 0x455a14ed),+ (s21, 0xa9e3e905), (s22, 0xfcefa3f8), (s23, 0x676f02d9),+ (s24, 0x8d2a4c8a)]+ r3 = [(s31, 0xfffa3942), (s32, 0x8771f681), (s33, 0x6d9d6122),+ (s34, 0xfde5380c), (s31, 0xa4beea44), (s32, 0x4bdecfa9),+ (s33, 0xf6bb4b60), (s34, 0xbebfbc70), (s31, 0x289b7ec6),+ (s32, 0xeaa127fa), (s33, 0xd4ef3085), (s34, 0x4881d05),+ (s31, 0xd9d4d039), (s32, 0xe6db99e5), (s33, 0x1fa27cf8),+ (s34, 0xc4ac5665)]+ r4 = [(s41, 0xf4292244), (s42, 0x432aff97), (s43, 0xab9423a7),+ (s44, 0xfc93a039), (s41, 0x655b59c3), (s42, 0x8f0ccc92),+ (s43, 0xffeff47d), (s44, 0x85845dd1), (s41, 0x6fa87e4f),+ (s42, 0xfe2ce6e0), (s43, 0xa3014314), (s44, 0x4e0811a1),+ (s41, 0xf7537e82), (s42, 0xbd3af235), (s43, 0x2ad7d2bb),+ (s44, 0xeb86d391)]+ s11 = 7+ s12 = 12+ s13 = 17+ s14 = 22+ s21 = 5+ s22 = 9+ s23 = 14+ s24 = 20+ s31 = 4+ s32 = 11+ s33 = 16+ s34 = 23+ s41 = 6+ s42 = 10+ s43 = 15+ s44 = 21+++-- ===================== CONVERSION FUNCTIONS ========================+++-- Turn the 4 32 bit words into a string representing the hex number they+-- represent.++abcd_to_string :: ABCD -> String+abcd_to_string (ABCD (a,b,c,d)) = concat $ map display_32bits_as_hex [a,b,c,d]+++-- Split the 32 bit word up, swap the chunks over and convert the numbers+-- to their hex equivalents.++display_32bits_as_hex :: Word32 -> String+display_32bits_as_hex w = swap_pairs cs+ where cs = map (\x -> getc $ (shiftR w (4*x)) .&. 15) [0..7]+ getc n = (['0'..'9'] ++ ['a'..'f']) !! (fromIntegral n)+ swap_pairs (x1:x2:xs) = x2:x1:swap_pairs xs+ swap_pairs _ = []++-- Convert to an integer, performing endianness magic as we go++abcd_to_integer :: ABCD -> Integer+abcd_to_integer (ABCD (a,b,c,d)) = rev_num a * 2^(96 :: Int)+ + rev_num b * 2^(64 :: Int)+ + rev_num c * 2^(32 :: Int)+ + rev_num d++rev_num :: Word32 -> Integer+rev_num i = toInteger j `mod` (2^(32 :: Int))+ -- NHC's fault ~~~~~~~~~~~~~~~~~~~~~+ where j = foldl (\so_far next -> shiftL so_far 8 + (shiftR i next .&. 255))+ 0 [0,8,16,24]++-- Used to convert a 64 byte string to 16 32bit words++string_to_word32s :: String -> [Word32]+string_to_word32s "" = []+string_to_word32s ss = this:string_to_word32s ss'+ where (s, ss') = splitAt 4 ss+ this = foldr (\c w -> shiftL w 8 + (fromIntegral.ord) c) 0 s+++-- Used to convert a list of 512 bools to 16 32bit words++bools_to_word32s :: [Bool] -> [Word32]+bools_to_word32s [] = []+bools_to_word32s bs = this:bools_to_word32s rest+ where (bs1, bs1') = splitAt 8 bs+ (bs2, bs2') = splitAt 8 bs1'+ (bs3, bs3') = splitAt 8 bs2'+ (bs4, rest) = splitAt 8 bs3'+ this = boolss_to_word32 [bs1, bs2, bs3, bs4]+ bools_to_word8 = foldl (\w b -> shiftL w 1 + if b then 1 else 0) 0+ boolss_to_word32 = foldr (\w8 w -> shiftL w 8 + bools_to_word8 w8) 0+++-- Convert the size into a list of characters used by the len_pad function+-- for strings++length_to_chars :: Int -> Word64 -> String+length_to_chars 0 _ = []+length_to_chars p n = this:length_to_chars (p-1) (shiftR n 8)+ where this = chr $ fromIntegral $ n .&. 255+
+ Network/HTTP/Proxy.hs view
@@ -0,0 +1,169 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Proxy+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Handling proxy server settings and their resolution.+-- +-----------------------------------------------------------------------------+module Network.HTTP.Proxy+ ( Proxy(..)+ , noProxy -- :: Proxy+ , fetchProxy -- :: Bool -> IO Proxy+ , parseProxy -- :: String -> Maybe Proxy+ ) where++import Control.Monad ( when, mplus, join, liftM2)++import Network.HTTP.Utils ( dropWhileTail, chopAtDelim )+import Network.HTTP.Auth+import Network.URI+ ( URI(..), URIAuth(..), parseAbsoluteURI )+import System.IO ( hPutStrLn, stderr )+import System.Environment++{-+#if !defined(WIN32) && defined(mingw32_HOST_OS)+#define WIN32 1+#endif+-}++#if defined(WIN32)+import System.Win32.Types ( DWORD, HKEY )+import System.Win32.Registry( hKEY_CURRENT_USER, regOpenKey, regCloseKey, regQueryValue, regQueryValueEx )+import Control.Exception ( bracket )+import Foreign ( toBool, Storable(peek, sizeOf), castPtr, alloca )+#endif++-- | HTTP proxies (or not) are represented via 'Proxy', specifying if a+-- proxy should be used for the request (see 'Network.Browser.setProxy')+data Proxy + = NoProxy -- ^ Don't use a proxy.+ | Proxy String+ (Maybe Authority) -- ^ Use the proxy given. Should be of the+ -- form "http:\/\/host:port", "host", "host:port", or "http:\/\/host".+ -- Additionally, an optional 'Authority' for authentication with the proxy.+++noProxy :: Proxy+noProxy = NoProxy++-- | @envProxyString@ locates proxy server settings by looking+-- up env variable @HTTP_PROXY@ (or its lower-case equivalent.)+-- If no mapping found, returns @Nothing@.+envProxyString :: IO (Maybe String)+envProxyString = do+ env <- getEnvironment+ return (lookup "http_proxy" env `mplus` lookup "HTTP_PROXY" env)++-- | @proxyString@ tries to locate the user's proxy server setting.+-- Consults environment variable, and in case of Windows, by querying+-- the Registry (cf. @registryProxyString@.)+proxyString :: IO (Maybe String)+proxyString = liftM2 mplus envProxyString registryProxyString++registryProxyString :: IO (Maybe String)+#if !defined(WIN32)+registryProxyString = return Nothing+#else+registryProxyLoc :: (HKEY,String)+registryProxyLoc = (hive, path)+ where+ -- some sources say proxy settings should be at + -- HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows+ -- \CurrentVersion\Internet Settings\ProxyServer+ -- but if the user sets them with IE connection panel they seem to+ -- end up in the following place:+ hive = hKEY_CURRENT_USER+ path = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"++-- read proxy settings from the windows registry; this is just a best+-- effort and may not work on all setups. +registryProxyString = Prelude.catch+ (bracket (uncurry regOpenKey registryProxyLoc) regCloseKey $ \hkey -> do+ enable <- fmap toBool $ regQueryValueDWORD hkey "ProxyEnable"+ if enable+ then fmap Just $ regQueryValue hkey (Just "ProxyServer")+ else return Nothing)+ (\_ -> return Nothing)+#endif++-- | @fetchProxy flg@ gets the local proxy settings and parse the string+-- into a @Proxy@ value. If you want to be informed of ill-formed proxy+-- configuration strings, supply @True@ for @flg@.+-- Proxy settings are sourced from the @HTTP_PROXY@ environment variable,+-- and in the case of Windows platforms, by consulting IE/WinInet's proxy+-- setting in the Registry.+fetchProxy :: Bool -> IO Proxy+fetchProxy warnIfIllformed = do+ mstr <- proxyString+ case mstr of+ Nothing -> return NoProxy+ Just str -> case parseProxy str of+ Just p -> return p + Nothing -> do+ when warnIfIllformed $ System.IO.hPutStrLn System.IO.stderr $ unlines+ [ "invalid http proxy uri: " ++ show str+ , "proxy uri must be http with a hostname"+ , "ignoring http proxy, trying a direct connection"+ ]+ return NoProxy++-- | @parseProxy str@ translates a proxy server string into a @Proxy@ value;+-- returns @Nothing@ if not well-formed.+parseProxy :: String -> Maybe Proxy+parseProxy str = join+ . fmap uri2proxy+ $ parseHttpURI str+ `mplus` parseHttpURI ("http://" ++ str)+ where+ parseHttpURI str' =+ case parseAbsoluteURI str' of+ Just uri@URI{uriAuthority = Just{}} -> Just (fixUserInfo uri)+ _ -> Nothing++ -- Note: we need to be able to parse non-URIs like @\"wwwcache.example.com:80\"@+ -- which lack the @\"http://\"@ URI scheme. The problem is that+ -- @\"wwwcache.example.com:80\"@ is in fact a valid URI but with scheme+ -- @\"wwwcache.example.com:\"@, no authority part and a path of @\"80\"@.+ --+ -- So our strategy is to try parsing as normal uri first and if it lacks the+ -- 'uriAuthority' then we try parsing again with a @\"http://\"@ prefix.+ --++-- | tidy up user portion, don't want the trailing "\@".+fixUserInfo :: URI -> URI+fixUserInfo uri = uri{ uriAuthority = f `fmap` uriAuthority uri }+ where+ f a@URIAuth{uriUserInfo=s} = a{uriUserInfo=dropWhileTail (=='@') s}++-- +uri2proxy :: URI -> Maybe Proxy+uri2proxy uri@URI{ uriScheme = "http:"+ , uriAuthority = Just (URIAuth auth' hst prt)+ } =+ Just (Proxy (hst ++ prt) auth)+ where+ auth =+ case auth' of+ [] -> Nothing+ as -> Just (AuthBasic "" usr pwd uri)+ where+ (usr,pwd) = chopAtDelim ':' as++uri2proxy _ = Nothing++-- utilities+#if defined(WIN32)+regQueryValueDWORD :: HKEY -> String -> IO DWORD+regQueryValueDWORD hkey name = alloca $ \ptr -> do+ regQueryValueEx hkey name (castPtr ptr) (sizeOf (undefined :: DWORD))+ peek ptr++#endif
+ Network/HTTP/Stream.hs view
@@ -0,0 +1,230 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Stream+-- Copyright : See LICENSE file+-- License : BSD+-- +-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Transmitting HTTP requests and responses holding @String@ in their payload bodies.+-- This is one of the implementation modules for the "Network.HTTP" interface, representing+-- request and response content as @String@s and transmitting them in non-packed form+-- (cf. "Network.HTTP.HandleStream" and its use of @ByteString@s.) over 'Stream' handles.+-- It is mostly here for backwards compatibility, representing how requests and responses+-- were transmitted up until the 4.x releases of the HTTP package.+--+-- For more detailed information about what the individual exports do, please consult+-- the documentation for "Network.HTTP". /Notice/ however that the functions here do+-- not perform any kind of normalization prior to transmission (or receipt); you are+-- responsible for doing any such yourself, or, if you prefer, just switch to using+-- "Network.HTTP" function instead.+-- +-----------------------------------------------------------------------------+module Network.HTTP.Stream + ( module Network.Stream++ , simpleHTTP -- :: Request_String -> IO (Result Response_String)+ , simpleHTTP_ -- :: Stream s => s -> Request_String -> IO (Result Response_String)+ , sendHTTP -- :: Stream s => s -> Request_String -> IO (Result Response_String)+ , sendHTTP_notify -- :: Stream s => s -> Request_String -> IO () -> IO (Result Response_String)+ , receiveHTTP -- :: Stream s => s -> IO (Result Request_String)+ , respondHTTP -- :: Stream s => s -> Response_String -> IO ()+ + ) where++-----------------------------------------------------------------+------------------ Imports --------------------------------------+-----------------------------------------------------------------++import Network.Stream+import Network.StreamDebugger (debugStream)+import Network.TCP (openTCPPort)+import Network.BufferType ( stringBufferOp )++import Network.HTTP.Base+import Network.HTTP.Headers+import Network.HTTP.Utils ( trim )++import Data.Char (toLower)+import Data.Maybe (fromMaybe)+import Control.Monad (when)+++-- Turn on to enable HTTP traffic logging+debug :: Bool+debug = False++-- File that HTTP traffic logs go to+httpLogFile :: String+httpLogFile = "http-debug.log"++-----------------------------------------------------------------+------------------ Misc -----------------------------------------+-----------------------------------------------------------------+++-- | Simple way to transmit a resource across a non-persistent connection.+simpleHTTP :: Request_String -> IO (Result Response_String)+simpleHTTP r = do + auth <- getAuth r+ c <- openTCPPort (host auth) (fromMaybe 80 (port auth))+ simpleHTTP_ c r++-- | Like 'simpleHTTP', but acting on an already opened stream.+simpleHTTP_ :: Stream s => s -> Request_String -> IO (Result Response_String)+simpleHTTP_ s r+ | not debug = sendHTTP s r+ | otherwise = do+ s' <- debugStream httpLogFile s+ sendHTTP s' r++sendHTTP :: Stream s => s -> Request_String -> IO (Result Response_String)+sendHTTP conn rq = sendHTTP_notify conn rq (return ())++sendHTTP_notify :: Stream s => s -> Request_String -> IO () -> IO (Result Response_String)+sendHTTP_notify conn rq onSendComplete = do+ when providedClose $ (closeOnEnd conn True)+ catchIO (sendMain conn rq onSendComplete)+ (\e -> do { close conn; ioError e })+ where+ providedClose = findConnClose (rqHeaders rq)++-- From RFC 2616, section 8.2.3:+-- 'Because of the presence of older implementations, the protocol allows+-- ambiguous situations in which a client may send "Expect: 100-+-- continue" without receiving either a 417 (Expectation Failed) status+-- or a 100 (Continue) status. Therefore, when a client sends this+-- header field to an origin server (possibly via a proxy) from which it+-- has never seen a 100 (Continue) status, the client SHOULD NOT wait+-- for an indefinite period before sending the request body.'+--+-- Since we would wait forever, I have disabled use of 100-continue for now.+sendMain :: Stream s => s -> Request_String -> IO () -> IO (Result Response_String)+sendMain conn rqst onSendComplete = do + --let str = if null (rqBody rqst)+ -- then show rqst+ -- else show (insertHeader HdrExpect "100-continue" rqst)+ writeBlock conn (show rqst)+ -- write body immediately, don't wait for 100 CONTINUE+ writeBlock conn (rqBody rqst)+ onSendComplete+ rsp <- getResponseHead conn+ switchResponse conn True False rsp rqst+ +-- reads and parses headers+getResponseHead :: Stream s => s -> IO (Result ResponseData)+getResponseHead conn = do+ lor <- readTillEmpty1 stringBufferOp (readLine conn)+ return $ lor >>= parseResponseHead++-- Hmmm, this could go bad if we keep getting "100 Continue"+-- responses... Except this should never happen according+-- to the RFC.+switchResponse :: Stream s+ => s+ -> Bool {- allow retry? -}+ -> Bool {- is body sent? -}+ -> Result ResponseData+ -> Request_String+ -> IO (Result Response_String)+switchResponse _ _ _ (Left e) _ = return (Left e)+ -- retry on connreset?+ -- if we attempt to use the same socket then there is an excellent+ -- chance that the socket is not in a completely closed state.+switchResponse conn allow_retry bdy_sent (Right (cd,rn,hdrs)) rqst =+ case matchResponse (rqMethod rqst) cd of+ Continue+ | not bdy_sent -> {- Time to send the body -}+ do { val <- writeBlock conn (rqBody rqst)+ ; case val of+ Left e -> return (Left e)+ Right _ ->+ do { rsp <- getResponseHead conn+ ; switchResponse conn allow_retry True rsp rqst+ }+ }+ | otherwise -> {- keep waiting -}+ do { rsp <- getResponseHead conn+ ; switchResponse conn allow_retry bdy_sent rsp rqst + }++ Retry -> {- Request with "Expect" header failed.+ Trouble is the request contains Expects+ other than "100-Continue" -}+ do { writeBlock conn (show rqst ++ rqBody rqst)+ ; rsp <- getResponseHead conn+ ; switchResponse conn False bdy_sent rsp rqst+ } + + Done -> do+ when (findConnClose hdrs)+ (closeOnEnd conn True)+ return (Right $ Response cd rn hdrs "")++ DieHorribly str -> do+ close conn+ return $ responseParseError "sendHTTP" ("Invalid response: " ++ str)++ ExpectEntity ->+ let tc = lookupHeader HdrTransferEncoding hdrs+ cl = lookupHeader HdrContentLength hdrs+ in+ do { rslt <- case tc of+ Nothing -> + case cl of+ Just x -> linearTransfer (readBlock conn) (read x :: Int)+ Nothing -> hopefulTransfer stringBufferOp {-null (++) []-} (readLine conn) []+ Just x -> + case map toLower (trim x) of+ "chunked" -> chunkedTransfer stringBufferOp+ (readLine conn) (readBlock conn)+ _ -> uglyDeathTransfer "sendHTTP"+ ; case rslt of+ Left e -> close conn >> return (Left e)+ Right (ftrs,bdy) -> do+ when (findConnClose (hdrs++ftrs))+ (closeOnEnd conn True)+ return (Right (Response cd rn (hdrs++ftrs) bdy))+ }++-- | Receive and parse a HTTP request from the given Stream. Should be used +-- for server side interactions.+receiveHTTP :: Stream s => s -> IO (Result Request_String)+receiveHTTP conn = getRequestHead >>= processRequest+ where+ -- reads and parses headers+ getRequestHead :: IO (Result RequestData)+ getRequestHead =+ do { lor <- readTillEmpty1 stringBufferOp (readLine conn)+ ; return $ lor >>= parseRequestHead+ }+ + processRequest (Left e) = return $ Left e+ processRequest (Right (rm,uri,hdrs)) = + do -- FIXME : Also handle 100-continue.+ let tc = lookupHeader HdrTransferEncoding hdrs+ cl = lookupHeader HdrContentLength hdrs+ rslt <- case tc of+ Nothing ->+ case cl of+ Just x -> linearTransfer (readBlock conn) (read x :: Int)+ Nothing -> return (Right ([], "")) -- hopefulTransfer ""+ Just x ->+ case map toLower (trim x) of+ "chunked" -> chunkedTransfer stringBufferOp+ (readLine conn) (readBlock conn)+ _ -> uglyDeathTransfer "receiveHTTP"+ + return $ do+ (ftrs,bdy) <- rslt+ return (Request uri rm (hdrs++ftrs) bdy)++-- | Very simple function, send a HTTP response over the given stream. This +-- could be improved on to use different transfer types.+respondHTTP :: Stream s => s -> Response_String -> IO ()+respondHTTP conn rsp = do writeBlock conn (show rsp)+ -- write body immediately, don't wait for 100 CONTINUE+ writeBlock conn (rspBody rsp)+ return ()
+ Network/HTTP/Utils.hs view
@@ -0,0 +1,111 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.HTTP.Utils+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Set of utility functions and definitions used by package modules.+--+module Network.HTTP.Utils+ ( trim -- :: String -> String+ , trimL -- :: String -> String+ , trimR -- :: String -> String+ + , crlf -- :: String+ , lf -- :: String+ , sp -- :: String++ , split -- :: Eq a => a -> [a] -> Maybe ([a],[a])+ , splitBy -- :: Eq a => a -> [a] -> [[a]]+ + , readsOne -- :: Read a => (a -> b) -> b -> String -> b++ , dropWhileTail -- :: (a -> Bool) -> [a] -> [a]+ , chopAtDelim -- :: Eq a => a -> [a] -> ([a],[a])+ + ) where+ +import Data.Char+import Data.List ( elemIndex )+import Data.Maybe ( fromMaybe )++-- | @crlf@ is our beloved two-char line terminator.+crlf :: String+crlf = "\r\n"++-- | @lf@ is a tolerated line terminator, per RFC 2616 section 19.3.+lf :: String+lf = "\n"++-- | @sp@ lets you save typing one character.+sp :: String+sp = " "++-- | @split delim ls@ splits a list into two parts, the @delim@ occurring+-- at the head of the second list. If @delim@ isn't in @ls@, @Nothing@ is+-- returned.+split :: Eq a => a -> [a] -> Maybe ([a],[a])+split delim list = case delim `elemIndex` list of+ Nothing -> Nothing+ Just x -> Just $ splitAt x list++-- | @trim str@ removes leading and trailing whitespace from @str@.+trim :: String -> String+trim xs = trimR (trimL xs)+ +-- | @trimL str@ removes leading whitespace (as defined by 'Data.Char.isSpace')+-- from @str@.+trimL :: String -> String+trimL xs = dropWhile isSpace xs++-- | @trimL str@ removes trailing whitespace (as defined by 'Data.Char.isSpace')+-- from @str@.+trimR :: String -> String+trimR str = fromMaybe "" $ foldr trimIt Nothing str+ where+ trimIt x (Just xs) = Just (x:xs)+ trimIt x Nothing + | isSpace x = Nothing+ | otherwise = Just [x]++-- | @splitMany delim ls@ removes the delimiter @delim@ from @ls@.+splitBy :: Eq a => a -> [a] -> [[a]]+splitBy _ [] = []+splitBy c xs = + case break (==c) xs of+ (_,[]) -> [xs]+ (as,_:bs) -> as : splitBy c bs++-- | @readsOne f def str@ tries to 'read' @str@, taking+-- the first result and passing it to @f@. If the 'read'+-- doesn't succeed, return @def@.+readsOne :: Read a => (a -> b) -> b -> String -> b+readsOne f n str = + case reads str of+ ((v,_):_) -> f v+ _ -> n+++-- | @dropWhileTail p ls@ chops off trailing elements from @ls@+-- until @p@ returns @False@.+dropWhileTail :: (a -> Bool) -> [a] -> [a]+dropWhileTail f ls =+ case foldr chop Nothing ls of { Just xs -> xs; Nothing -> [] }+ where+ chop x (Just xs) = Just (x:xs)+ chop x _+ | f x = Nothing+ | otherwise = Just [x]++-- | @chopAtDelim elt ls@ breaks up @ls@ into two at first occurrence+-- of @elt@; @elt@ is elided too. If @elt@ does not occur, the second+-- list is empty and the first is equal to @ls@.+chopAtDelim :: Eq a => a -> [a] -> ([a],[a])+chopAtDelim elt xs =+ case break (==elt) xs of+ (_,[]) -> (xs,[])+ (as,_:bs) -> (as,bs)
+ Network/Socket.hsc view
@@ -0,0 +1,2236 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-do-bind #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.Socket+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file libraries/network/LICENSE)+-- +-- Maintainer : libraries@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- The "Network.Socket" module is for when you want full control over+-- sockets. Essentially the entire C socket API is exposed through+-- this module; in general the operations follow the behaviour of the C+-- functions of the same name (consult your favourite Unix networking book).+--+-- A higher level interface to networking operations is provided+-- through the module "Network".+--+-----------------------------------------------------------------------------++#include "HsNet.h"++-- NOTE: ##, we want this interpreted when compiling the .hs, not by hsc2hs.+##include "Typeable.h"++-- In order to process this file, you need to have CALLCONV defined.++module Network.Socket+ (+ -- * Types+ Socket(..)+ , Family(..) + , SocketType(..)+ , SockAddr(..)+ , SocketStatus(..)+ , HostAddress+#if defined(IPV6_SOCKET_SUPPORT)+ , HostAddress6+ , FlowInfo+ , ScopeID+#endif+ , ShutdownCmd(..)+ , ProtocolNumber+ , defaultProtocol+ , PortNumber(..)+ -- PortNumber is used non-abstractly in Network.BSD. ToDo: remove+ -- this use and make the type abstract.++ -- * Address operations++ , HostName+ , ServiceName++#if defined(IPV6_SOCKET_SUPPORT)+ , AddrInfo(..)++ , AddrInfoFlag(..)+ , addrInfoFlagImplemented++ , defaultHints++ , getAddrInfo++ , NameInfoFlag(..)++ , getNameInfo+#endif++ -- * Socket operations+ , socket+#if defined(DOMAIN_SOCKET_SUPPORT)+ , socketPair+#endif+ , connect+ , bindSocket+ , listen+ , accept+ , getPeerName+ , getSocketName++#ifdef HAVE_STRUCT_UCRED+ -- get the credentials of our domain socket peer.+ , getPeerCred+#endif++ , socketPort++ , socketToHandle++ -- ** Sending and receiving data+ -- $sendrecv+ , sendTo+ , sendBufTo++ , recvFrom+ , recvBufFrom+ + , send+ , recv+ , recvLen++ , inet_addr+ , inet_ntoa++ , shutdown+ , sClose++ -- ** Predicates on sockets+ , sIsConnected+ , sIsBound+ , sIsListening+ , sIsReadable+ , sIsWritable++ -- * Socket options+ , SocketOption(..)+ , getSocketOption+ , setSocketOption++ -- * File descriptor transmission+#ifdef DOMAIN_SOCKET_SUPPORT+ , sendFd+ , recvFd++ -- Note: these two will disappear shortly+ , sendAncillary+ , recvAncillary++#endif++ -- * Special constants+ , aNY_PORT+ , iNADDR_ANY+#if defined(IPV6_SOCKET_SUPPORT)+ , iN6ADDR_ANY+#endif+ , sOMAXCONN+ , sOL_SOCKET+#ifdef SCM_RIGHTS+ , sCM_RIGHTS+#endif+ , maxListenQueue++ -- * Initialisation+ , withSocketsDo+ + -- * Very low level operations+ -- in case you ever want to get at the underlying file descriptor..+ , fdSocket+ , mkSocket++ -- * Internal++ -- | The following are exported ONLY for use in the BSD module and+ -- should not be used anywhere else.++ , packFamily+ , unpackFamily+ , packSocketType+ , throwSocketErrorIfMinus1_+ ) where++#ifdef __HUGS__+import Hugs.Prelude ( IOException(..), IOErrorType(..) )+import Hugs.IO ( openFd )++{-# CFILES cbits/HsNet.c #-}+# if HAVE_STRUCT_MSGHDR_MSG_CONTROL || HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS+{-# CFILES cbits/ancilData.c #-}+# endif+# if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+{-# CFILES cbits/initWinSock.c cbits/winSockErr.c #-}+# endif+#endif++import Data.Bits+import Data.List (foldl')+import Data.Word (Word16, Word32)+import Foreign.Ptr (Ptr, castPtr, nullPtr)+import Foreign.Storable (Storable(..))+import Foreign.C.Error+import Foreign.C.String (CString, withCString, peekCString, peekCStringLen)+import Foreign.C.Types (CUInt, CChar)+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CInt(..), CSize(..))+#else+import Foreign.C.Types (CInt, CSize)+#endif+import Foreign.Marshal.Alloc ( alloca, allocaBytes )+import Foreign.Marshal.Array ( peekArray )+import Foreign.Marshal.Utils ( maybeWith, with )++import System.IO+import Control.Monad (liftM, when)+import Data.Ratio ((%))++import qualified Control.Exception+import Control.Concurrent.MVar+import Data.Typeable+import System.IO.Error++#ifdef __GLASGOW_HASKELL__+import GHC.Conc (threadWaitRead, threadWaitWrite)+##if MIN_VERSION_base(4,3,1)+import GHC.Conc (closeFdWith)+##endif+# if defined(mingw32_HOST_OS)+import GHC.Conc (asyncDoProc)+import Foreign (FunPtr)+# endif+# if __GLASGOW_HASKELL__ >= 611+import qualified GHC.IO.Device+import GHC.IO.Handle.FD+import GHC.IO.Exception+import GHC.IO+# else+import GHC.IOBase+import GHC.Handle+# endif+import qualified System.Posix.Internals+#else+import System.IO.Unsafe (unsafePerformIO)+#endif++# if __GLASGOW_HASKELL__ >= 611+import GHC.IO.FD+#endif++import Network.Socket.Internal++-- | Either a host name e.g., @\"haskell.org\"@ or a numeric host+-- address string consisting of a dotted decimal IPv4 address or an+-- IPv6 address e.g., @\"192.168.0.1\"@.+type HostName = String+type ServiceName = String++-- ----------------------------------------------------------------------------+-- On Windows, our sockets are not put in non-blocking mode (non-blocking+-- is not supported for regular file descriptors on Windows, and it would+-- be a pain to support it only for sockets). So there are two cases:+--+-- - the threaded RTS uses safe calls for socket operations to get+-- non-blocking I/O, just like the rest of the I/O library+--+-- - with the non-threaded RTS, only some operations on sockets will be+-- non-blocking. Reads and writes go through the normal async I/O+-- system. accept() uses asyncDoProc so is non-blocking. A handful+-- of others (recvFrom, sendFd, recvFd) will block all threads - if this+-- is a problem, -threaded is the workaround.+--+##if defined(mingw32_HOST_OS)+##define SAFE_ON_WIN safe+##else+##define SAFE_ON_WIN unsafe+##endif++-----------------------------------------------------------------------------+-- Socket types++-- There are a few possible ways to do this. The first is convert the+-- structs used in the C library into an equivalent Haskell type. An+-- other possible implementation is to keep all the internals in the C+-- code and use an Int## and a status flag. The second method is used+-- here since a lot of the C structures are not required to be+-- manipulated.++-- Originally the status was non-mutable so we had to return a new+-- socket each time we changed the status. This version now uses+-- mutable variables to avoid the need to do this. The result is a+-- cleaner interface and better security since the application+-- programmer now can't circumvent the status information to perform+-- invalid operations on sockets.++data SocketStatus+ -- Returned Status Function called+ = NotConnected -- socket+ | Bound -- bindSocket+ | Listening -- listen+ | Connected -- connect/accept+ | ConvertedToHandle -- is now a Handle, don't touch+ | Closed -- sClose + deriving (Eq, Show, Typeable)++data Socket+ = MkSocket+ CInt -- File Descriptor+ Family + SocketType + ProtocolNumber -- Protocol Number+ (MVar SocketStatus) -- Status Flag+ deriving Typeable++#if __GLASGOW_HASKELL__ >= 611 && defined(mingw32_HOST_OS)+socket2FD (MkSocket fd _ _ _ _) = + -- HACK, 1 means True + FD{fdFD = fd,fdIsSocket_ = 1} +#endif++mkSocket :: CInt+ -> Family+ -> SocketType+ -> ProtocolNumber+ -> SocketStatus+ -> IO Socket+mkSocket fd fam sType pNum stat = do+ mStat <- newMVar stat+ return (MkSocket fd fam sType pNum mStat)++instance Eq Socket where+ (MkSocket _ _ _ _ m1) == (MkSocket _ _ _ _ m2) = m1 == m2++instance Show Socket where+ showsPrec _n (MkSocket fd _ _ _ _) =+ showString "<socket: " . shows fd . showString ">"+++fdSocket :: Socket -> CInt+fdSocket (MkSocket fd _ _ _ _) = fd++type ProtocolNumber = CInt++-- | This is the default protocol for a given service.+defaultProtocol :: ProtocolNumber+defaultProtocol = 0++----------------------------------------------------------------------------+-- Port Numbers++instance Show PortNumber where+ showsPrec p pn = showsPrec p (portNumberToInt pn)++intToPortNumber :: Int -> PortNumber+intToPortNumber v = PortNum (htons (fromIntegral v))++portNumberToInt :: PortNumber -> Int+portNumberToInt (PortNum po) = fromIntegral (ntohs po)++foreign import CALLCONV unsafe "ntohs" ntohs :: Word16 -> Word16+foreign import CALLCONV unsafe "htons" htons :: Word16 -> Word16+--foreign import CALLCONV unsafe "ntohl" ntohl :: Word32 -> Word32+foreign import CALLCONV unsafe "htonl" htonl :: Word32 -> Word32++instance Enum PortNumber where+ toEnum = intToPortNumber+ fromEnum = portNumberToInt++instance Num PortNumber where+ fromInteger i = intToPortNumber (fromInteger i)+ -- for completeness.+ (+) x y = intToPortNumber (portNumberToInt x + portNumberToInt y)+ (-) x y = intToPortNumber (portNumberToInt x - portNumberToInt y)+ negate x = intToPortNumber (-portNumberToInt x)+ (*) x y = intToPortNumber (portNumberToInt x * portNumberToInt y)+ abs n = intToPortNumber (abs (portNumberToInt n))+ signum n = intToPortNumber (signum (portNumberToInt n))++instance Real PortNumber where+ toRational x = toInteger x % 1++instance Integral PortNumber where+ quotRem a b = let (c,d) = quotRem (portNumberToInt a) (portNumberToInt b) in+ (intToPortNumber c, intToPortNumber d)+ toInteger a = toInteger (portNumberToInt a)++instance Storable PortNumber where+ sizeOf _ = sizeOf (undefined :: Word16)+ alignment _ = alignment (undefined :: Word16)+ poke p (PortNum po) = poke (castPtr p) po+ peek p = PortNum `liftM` peek (castPtr p)++-----------------------------------------------------------------------------+-- SockAddr++instance Show SockAddr where+#if defined(DOMAIN_SOCKET_SUPPORT)+ showsPrec _ (SockAddrUnix str) = showString str+#endif+ showsPrec _ (SockAddrInet port ha)+ = showString (unsafePerformIO (inet_ntoa ha)) + . showString ":"+ . shows port+#if defined(IPV6_SOCKET_SUPPORT)+ showsPrec _ addr@(SockAddrInet6 port _ _ _)+ = showChar '['+ . showString (unsafePerformIO $+ fst `liftM` getNameInfo [NI_NUMERICHOST] True False addr >>=+ maybe (fail "showsPrec: impossible internal error") return)+ . showString "]:"+ . shows port+#endif++-----------------------------------------------------------------------------+-- Connection Functions++-- In the following connection and binding primitives. The names of+-- the equivalent C functions have been preserved where possible. It+-- should be noted that some of these names used in the C library,+-- \tr{bind} in particular, have a different meaning to many Haskell+-- programmers and have thus been renamed by appending the prefix+-- Socket.++-- | Create a new socket using the given address family, socket type+-- and protocol number. The address family is usually 'AF_INET',+-- 'AF_INET6', or 'AF_UNIX'. The socket type is usually 'Stream' or+-- 'Datagram'. The protocol number is usually 'defaultProtocol'.+-- If 'AF_INET6' is used, the 'IPv6Only' socket option is set to 0+-- so that both IPv4 and IPv6 can be handled with one socket.+socket :: Family -- Family Name (usually AF_INET)+ -> SocketType -- Socket Type (usually Stream)+ -> ProtocolNumber -- Protocol Number (getProtocolByName to find value)+ -> IO Socket -- Unconnected Socket+socket family stype protocol = do+ fd <- throwSocketErrorIfMinus1Retry "socket" $+ c_socket (packFamily family) (packSocketType stype) protocol+#if !defined(__HUGS__)+# if __GLASGOW_HASKELL__ < 611+ System.Posix.Internals.setNonBlockingFD fd+# else+ System.Posix.Internals.setNonBlockingFD fd True+# endif+#endif+ socket_status <- newMVar NotConnected+ let sock = MkSocket fd family stype protocol socket_status+#if HAVE_DECL_IPV6_V6ONLY+# if defined(mingw32_HOST_OS)+ -- the IPv6Only option is only supported on Windows Vista and later,+ -- so trying to change it might throw an error+ when (family == AF_INET6) $+ catch (setSocketOption sock IPv6Only 0) $ const $ return ()+# else+ when (family == AF_INET6) $ setSocketOption sock IPv6Only 0+# endif+#endif+ return sock++-- | Build a pair of connected socket objects using the given address+-- family, socket type, and protocol number. Address family, socket+-- type, and protocol number are as for the 'socket' function above.+-- Availability: Unix.+#if defined(DOMAIN_SOCKET_SUPPORT)+socketPair :: Family -- Family Name (usually AF_INET or AF_INET6)+ -> SocketType -- Socket Type (usually Stream)+ -> ProtocolNumber -- Protocol Number+ -> IO (Socket, Socket) -- unnamed and connected.+socketPair family stype protocol = do+ allocaBytes (2 * sizeOf (1 :: CInt)) $ \ fdArr -> do+ _rc <- throwSocketErrorIfMinus1Retry "socketpair" $+ c_socketpair (packFamily family)+ (packSocketType stype)+ protocol fdArr+ [fd1,fd2] <- peekArray 2 fdArr + s1 <- mkNonBlockingSocket fd1+ s2 <- mkNonBlockingSocket fd2+ return (s1,s2)+ where+ mkNonBlockingSocket fd = do+#if !defined(__HUGS__)+# if __GLASGOW_HASKELL__ < 611+ System.Posix.Internals.setNonBlockingFD fd+# else+ System.Posix.Internals.setNonBlockingFD fd True+# endif+#endif+ stat <- newMVar Connected+ return (MkSocket fd family stype protocol stat)++foreign import ccall unsafe "socketpair"+ c_socketpair :: CInt -> CInt -> CInt -> Ptr CInt -> IO CInt+#endif++-----------------------------------------------------------------------------+-- Binding a socket++-- | Bind the socket to an address. The socket must not already be+-- bound. The 'Family' passed to @bindSocket@ must be the+-- same as that passed to 'socket'. If the special port number+-- 'aNY_PORT' is passed then the system assigns the next available+-- use port.+bindSocket :: Socket -- Unconnected Socket+ -> SockAddr -- Address to Bind to+ -> IO ()+bindSocket (MkSocket s _family _stype _protocol socketStatus) addr = do+ modifyMVar_ socketStatus $ \ status -> do+ if status /= NotConnected + then+ ioError (userError ("bindSocket: can't peform bind on socket in status " +++ show status))+ else do+ withSockAddr addr $ \p_addr sz -> do+ _status <- throwSocketErrorIfMinus1Retry "bind" $ c_bind s p_addr (fromIntegral sz)+ return Bound++-----------------------------------------------------------------------------+-- Connecting a socket++-- | Connect to a remote socket at address.+connect :: Socket -- Unconnected Socket+ -> SockAddr -- Socket address stuff+ -> IO ()+connect sock@(MkSocket s _family _stype _protocol socketStatus) addr = do+ modifyMVar_ socketStatus $ \currentStatus -> do+ if currentStatus /= NotConnected && currentStatus /= Bound+ then+ ioError (userError ("connect: can't peform connect on socket in status " +++ show currentStatus))+ else do+ withSockAddr addr $ \p_addr sz -> do++ let connectLoop = do+ r <- c_connect s p_addr (fromIntegral sz)+ if r == -1+ then do +#if !(defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS))+ err <- getErrno+ case () of+ _ | err == eINTR -> connectLoop+ _ | err == eINPROGRESS -> connectBlocked+-- _ | err == eAGAIN -> connectBlocked+ _otherwise -> throwSocketError "connect"+#else+ rc <- c_getLastError+ case rc of+ 10093 -> do -- WSANOTINITIALISED+ withSocketsDo (return ())+ r <- c_connect s p_addr (fromIntegral sz)+ if r == -1+ then throwSocketError "connect"+ else return r+ _ -> throwSocketError "connect"+#endif+ else return r++ connectBlocked = do +#if !defined(__HUGS__)+ threadWaitWrite (fromIntegral s)+#endif+ err <- getSocketOption sock SoError+ if (err == 0)+ then return 0+ else do ioError (errnoToIOError "connect" + (Errno (fromIntegral err))+ Nothing Nothing)++ connectLoop+ return Connected++-----------------------------------------------------------------------------+-- Listen++-- | Listen for connections made to the socket. The second argument+-- specifies the maximum number of queued connections and should be at+-- least 1; the maximum value is system-dependent (usually 5).+listen :: Socket -- Connected & Bound Socket+ -> Int -- Queue Length+ -> IO ()+listen (MkSocket s _family _stype _protocol socketStatus) backlog = do+ modifyMVar_ socketStatus $ \ status -> do+ if status /= Bound + then+ ioError (userError ("listen: can't peform listen on socket in status " +++ show status))+ else do+ throwSocketErrorIfMinus1Retry "listen" (c_listen s (fromIntegral backlog))+ return Listening++-----------------------------------------------------------------------------+-- Accept+--+-- A call to `accept' only returns when data is available on the given+-- socket, unless the socket has been set to non-blocking. It will+-- return a new socket which should be used to read the incoming data and+-- should then be closed. Using the socket returned by `accept' allows+-- incoming requests to be queued on the original socket.++-- | Accept a connection. The socket must be bound to an address and+-- listening for connections. The return value is a pair @(conn,+-- address)@ where @conn@ is a new socket object usable to send and+-- receive data on the connection, and @address@ is the address bound+-- to the socket on the other end of the connection.+accept :: Socket -- Queue Socket+ -> IO (Socket, -- Readable Socket+ SockAddr) -- Peer details++accept sock@(MkSocket s family stype protocol status) = do+ currentStatus <- readMVar status+ okay <- sIsAcceptable sock+ if not okay+ then+ ioError (userError ("accept: can't perform accept on socket (" ++ (show (family,stype,protocol)) ++") in status " +++ show currentStatus))+ else do+ let sz = sizeOfSockAddrByFamily family+ allocaBytes sz $ \ sockaddr -> do+#if defined(mingw32_HOST_OS) && defined(__GLASGOW_HASKELL__)+ new_sock <-+ if threaded + then with (fromIntegral sz) $ \ ptr_len ->+ throwErrnoIfMinus1Retry "Network.Socket.accept" $+ c_accept_safe s sockaddr ptr_len+ else do+ paramData <- c_newAcceptParams s (fromIntegral sz) sockaddr+ rc <- asyncDoProc c_acceptDoProc paramData+ new_sock <- c_acceptNewSock paramData+ c_free paramData+ when (rc /= 0)+ (ioError (errnoToIOError "Network.Socket.accept" (Errno (fromIntegral rc)) Nothing Nothing))+ return new_sock+#else + with (fromIntegral sz) $ \ ptr_len -> do+ new_sock <- +# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "accept"+ (threadWaitRead (fromIntegral s))+# endif+ (c_accept s sockaddr ptr_len)+# if !defined(__HUGS__)+# if __GLASGOW_HASKELL__ < 611+ System.Posix.Internals.setNonBlockingFD new_sock+# else+ System.Posix.Internals.setNonBlockingFD new_sock True+# endif+# endif+#endif+ addr <- peekSockAddr sockaddr+ new_status <- newMVar Connected+ return ((MkSocket new_sock family stype protocol new_status), addr)++#if defined(mingw32_HOST_OS) && !defined(__HUGS__)+foreign import ccall unsafe "HsNet.h acceptNewSock"+ c_acceptNewSock :: Ptr () -> IO CInt+foreign import ccall unsafe "HsNet.h newAcceptParams"+ c_newAcceptParams :: CInt -> CInt -> Ptr a -> IO (Ptr ())+foreign import ccall unsafe "HsNet.h &acceptDoProc"+ c_acceptDoProc :: FunPtr (Ptr () -> IO Int)+foreign import ccall unsafe "free"+ c_free:: Ptr a -> IO ()+#endif++-----------------------------------------------------------------------------+-- ** Sending and reciving data++-- $sendrecv+--+-- Do not use the @send@ and @recv@ functions defined in this module+-- in new code, as they incorrectly represent binary data as a Unicode+-- string. As a result, these functions are inefficient and may lead+-- to bugs in the program. Instead use the @send@ and @recv@+-- functions defined in the 'Network.Socket.ByteString' module.++-----------------------------------------------------------------------------+-- sendTo & recvFrom++-- | Send data to the socket. The recipient can be specified+-- explicitly, so the socket need not be in a connected state.+-- Returns the number of bytes sent. Applications are responsible for+-- ensuring that all data has been sent.+--+-- NOTE: blocking on Windows unless you compile with -threaded (see+-- GHC ticket #1129)+sendTo :: Socket -- (possibly) bound/connected Socket+ -> String -- Data to send+ -> SockAddr+ -> IO Int -- Number of Bytes sent+sendTo sock xs addr = do+ withCString xs $ \str -> do+ sendBufTo sock str (length xs) addr++-- | Send data to the socket. The recipient can be specified+-- explicitly, so the socket need not be in a connected state.+-- Returns the number of bytes sent. Applications are responsible for+-- ensuring that all data has been sent.+sendBufTo :: Socket -- (possibly) bound/connected Socket+ -> Ptr a -> Int -- Data to send+ -> SockAddr+ -> IO Int -- Number of Bytes sent+sendBufTo (MkSocket s _family _stype _protocol _status) ptr nbytes addr = do+ withSockAddr addr $ \p_addr sz -> do+ liftM fromIntegral $+#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "sendTo"+ (threadWaitWrite (fromIntegral s)) $+#endif+ c_sendto s ptr (fromIntegral $ nbytes) 0{-flags-} + p_addr (fromIntegral sz)++-- | Receive data from the socket. The socket need not be in a+-- connected state. Returns @(bytes, nbytes, address)@ where @bytes@+-- is a @String@ of length @nbytes@ representing the data received and+-- @address@ is a 'SockAddr' representing the address of the sending+-- socket.+--+-- NOTE: blocking on Windows unless you compile with -threaded (see+-- GHC ticket #1129)+recvFrom :: Socket -> Int -> IO (String, Int, SockAddr)+recvFrom sock nbytes =+ allocaBytes nbytes $ \ptr -> do+ (len, sockaddr) <- recvBufFrom sock ptr nbytes+ str <- peekCStringLen (ptr, len)+ return (str, len, sockaddr)++-- | Receive data from the socket, writing it into buffer instead of+-- creating a new string. The socket need not be in a connected+-- state. Returns @(nbytes, address)@ where @nbytes@ is the number of+-- bytes received and @address@ is a 'SockAddr' representing the+-- address of the sending socket.+--+-- NOTE: blocking on Windows unless you compile with -threaded (see+-- GHC ticket #1129)+recvBufFrom :: Socket -> Ptr a -> Int -> IO (Int, SockAddr)+recvBufFrom sock@(MkSocket s family _stype _protocol _status) ptr nbytes+ | nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recvFrom")+ | otherwise = + withNewSockAddr family $ \ptr_addr sz -> do+ alloca $ \ptr_len -> do+ poke ptr_len (fromIntegral sz)+ len <- +#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "recvFrom"+ (threadWaitRead (fromIntegral s)) $+#endif+ c_recvfrom s ptr (fromIntegral nbytes) 0{-flags-} + ptr_addr ptr_len+ let len' = fromIntegral len+ if len' == 0+ then ioError (mkEOFError "Network.Socket.recvFrom")+ else do+ flg <- sIsConnected sock+ -- For at least one implementation (WinSock 2), recvfrom() ignores+ -- filling in the sockaddr for connected TCP sockets. Cope with + -- this by using getPeerName instead.+ sockaddr <- + if flg then+ getPeerName sock+ else+ peekSockAddr ptr_addr + return (len', sockaddr)++-----------------------------------------------------------------------------+-- send & recv++-- | Send data to the socket. The socket must be connected to a remote+-- socket. Returns the number of bytes sent. Applications are+-- responsible for ensuring that all data has been sent.+send :: Socket -- Bound/Connected Socket+ -> String -- Data to send+ -> IO Int -- Number of Bytes sent+send sock@(MkSocket s _family _stype _protocol _status) xs = do+ let len = length xs+ withCString xs $ \str -> do+ liftM fromIntegral $+#if defined(__GLASGOW_HASKELL__) && defined(mingw32_HOST_OS)+# if __GLASGOW_HASKELL__ >= 611 + writeRawBufferPtr + "Network.Socket.send" + (socket2FD sock)+ (castPtr str)+ 0+ (fromIntegral len)+#else + writeRawBufferPtr + "Network.Socket.send" + (fromIntegral s) + True + str + 0 + (fromIntegral len)+#endif + +#else+# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "send"+ (threadWaitWrite (fromIntegral s)) $+# endif+ c_send s str (fromIntegral len) 0{-flags-} +#endif++-- | Receive data from the socket. The socket must be in a connected+-- state. This function may return fewer bytes than specified. If the+-- message is longer than the specified length, it may be discarded+-- depending on the type of socket. This function may block until a+-- message arrives.+--+-- Considering hardware and network realities, the maximum number of+-- bytes to receive should be a small power of 2, e.g., 4096.+--+-- For TCP sockets, a zero length return value means the peer has+-- closed its half side of the connection.+recv :: Socket -> Int -> IO String+recv sock l = recvLen sock l >>= \ (s,_) -> return s++recvLen :: Socket -> Int -> IO (String, Int)+recvLen sock@(MkSocket s _family _stype _protocol _status) nbytes+ | nbytes <= 0 = ioError (mkInvalidRecvArgError "Network.Socket.recv")+ | otherwise = do+ allocaBytes nbytes $ \ptr -> do+ len <- +#if defined(__GLASGOW_HASKELL__) && defined(mingw32_HOST_OS)+# if __GLASGOW_HASKELL__ >= 611 + readRawBufferPtr "Network.Socket.recvLen" (socket2FD sock) ptr 0+ (fromIntegral nbytes)+#else + readRawBufferPtr "Network.Socket.recvLen" (fromIntegral s) True ptr 0+ (fromIntegral nbytes)+#endif+#else+# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "recv"+ (threadWaitRead (fromIntegral s)) $+# endif+ c_recv s ptr (fromIntegral nbytes) 0{-flags-} +#endif+ let len' = fromIntegral len+ if len' == 0+ then ioError (mkEOFError "Network.Socket.recv")+ else do+ s' <- peekCStringLen (castPtr ptr,len')+ return (s', len')++-- ---------------------------------------------------------------------------+-- socketPort+--+-- The port number the given socket is currently connected to can be+-- determined by calling $port$, is generally only useful when bind+-- was given $aNY\_PORT$.++socketPort :: Socket -- Connected & Bound Socket+ -> IO PortNumber -- Port Number of Socket+socketPort sock@(MkSocket _ AF_INET _ _ _) = do+ (SockAddrInet port _) <- getSocketName sock+ return port+#if defined(IPV6_SOCKET_SUPPORT)+socketPort sock@(MkSocket _ AF_INET6 _ _ _) = do+ (SockAddrInet6 port _ _ _) <- getSocketName sock+ return port+#endif+socketPort (MkSocket _ family _ _ _) =+ ioError (userError ("socketPort: not supported for Family " ++ show family))+++-- ---------------------------------------------------------------------------+-- getPeerName++-- Calling $getPeerName$ returns the address details of the machine,+-- other than the local one, which is connected to the socket. This is+-- used in programs such as FTP to determine where to send the+-- returning data. The corresponding call to get the details of the+-- local machine is $getSocketName$.++getPeerName :: Socket -> IO SockAddr+getPeerName (MkSocket s family _ _ _) = do+ withNewSockAddr family $ \ptr sz -> do+ with (fromIntegral sz) $ \int_star -> do+ throwSocketErrorIfMinus1Retry "getPeerName" $ c_getpeername s ptr int_star+ _sz <- peek int_star+ peekSockAddr ptr+ +getSocketName :: Socket -> IO SockAddr+getSocketName (MkSocket s family _ _ _) = do+ withNewSockAddr family $ \ptr sz -> do+ with (fromIntegral sz) $ \int_star -> do+ throwSocketErrorIfMinus1Retry "getSocketName" $ c_getsockname s ptr int_star+ peekSockAddr ptr++-----------------------------------------------------------------------------+-- Socket Properties++data SocketOption+ = DummySocketOption__+#ifdef SO_DEBUG+ | Debug {- SO_DEBUG -}+#endif+#ifdef SO_REUSEADDR+ | ReuseAddr {- SO_REUSEADDR -}+#endif+#ifdef SO_TYPE+ | Type {- SO_TYPE -}+#endif+#ifdef SO_ERROR+ | SoError {- SO_ERROR -}+#endif+#ifdef SO_DONTROUTE+ | DontRoute {- SO_DONTROUTE -}+#endif+#ifdef SO_BROADCAST+ | Broadcast {- SO_BROADCAST -}+#endif+#ifdef SO_SNDBUF+ | SendBuffer {- SO_SNDBUF -}+#endif+#ifdef SO_RCVBUF+ | RecvBuffer {- SO_RCVBUF -}+#endif+#ifdef SO_KEEPALIVE+ | KeepAlive {- SO_KEEPALIVE -}+#endif+#ifdef SO_OOBINLINE+ | OOBInline {- SO_OOBINLINE -}+#endif+#ifdef IP_TTL+ | TimeToLive {- IP_TTL -}+#endif+#ifdef TCP_MAXSEG+ | MaxSegment {- TCP_MAXSEG -}+#endif+#ifdef TCP_NODELAY+ | NoDelay {- TCP_NODELAY -}+#endif+#ifdef SO_LINGER+ | Linger {- SO_LINGER -}+#endif+#ifdef SO_REUSEPORT+ | ReusePort {- SO_REUSEPORT -}+#endif+#ifdef SO_RCVLOWAT+ | RecvLowWater {- SO_RCVLOWAT -}+#endif+#ifdef SO_SNDLOWAT+ | SendLowWater {- SO_SNDLOWAT -}+#endif+#ifdef SO_RCVTIMEO+ | RecvTimeOut {- SO_RCVTIMEO -}+#endif+#ifdef SO_SNDTIMEO+ | SendTimeOut {- SO_SNDTIMEO -}+#endif+#ifdef SO_USELOOPBACK+ | UseLoopBack {- SO_USELOOPBACK -}+#endif+#if HAVE_DECL_IPV6_V6ONLY+ | IPv6Only {- IPV6_V6ONLY -}+#endif+ deriving (Show, Typeable)++socketOptLevel :: SocketOption -> CInt+socketOptLevel so = + case so of+#ifdef IP_TTL+ TimeToLive -> #const IPPROTO_IP+#endif+#ifdef TCP_MAXSEG+ MaxSegment -> #const IPPROTO_TCP+#endif+#ifdef TCP_NODELAY+ NoDelay -> #const IPPROTO_TCP+#endif+#if HAVE_DECL_IPV6_V6ONLY+ IPv6Only -> #const IPPROTO_IPV6+#endif+ _ -> #const SOL_SOCKET++packSocketOption :: SocketOption -> CInt+packSocketOption so =+ case so of+#ifdef SO_DEBUG+ Debug -> #const SO_DEBUG+#endif+#ifdef SO_REUSEADDR+ ReuseAddr -> #const SO_REUSEADDR+#endif+#ifdef SO_TYPE+ Type -> #const SO_TYPE+#endif+#ifdef SO_ERROR+ SoError -> #const SO_ERROR+#endif+#ifdef SO_DONTROUTE+ DontRoute -> #const SO_DONTROUTE+#endif+#ifdef SO_BROADCAST+ Broadcast -> #const SO_BROADCAST+#endif+#ifdef SO_SNDBUF+ SendBuffer -> #const SO_SNDBUF+#endif+#ifdef SO_RCVBUF+ RecvBuffer -> #const SO_RCVBUF+#endif+#ifdef SO_KEEPALIVE+ KeepAlive -> #const SO_KEEPALIVE+#endif+#ifdef SO_OOBINLINE+ OOBInline -> #const SO_OOBINLINE+#endif+#ifdef IP_TTL+ TimeToLive -> #const IP_TTL+#endif+#ifdef TCP_MAXSEG+ MaxSegment -> #const TCP_MAXSEG+#endif+#ifdef TCP_NODELAY+ NoDelay -> #const TCP_NODELAY+#endif+#ifdef SO_LINGER+ Linger -> #const SO_LINGER+#endif+#ifdef SO_REUSEPORT+ ReusePort -> #const SO_REUSEPORT+#endif+#ifdef SO_RCVLOWAT+ RecvLowWater -> #const SO_RCVLOWAT+#endif+#ifdef SO_SNDLOWAT+ SendLowWater -> #const SO_SNDLOWAT+#endif+#ifdef SO_RCVTIMEO+ RecvTimeOut -> #const SO_RCVTIMEO+#endif+#ifdef SO_SNDTIMEO+ SendTimeOut -> #const SO_SNDTIMEO+#endif+#ifdef SO_USELOOPBACK+ UseLoopBack -> #const SO_USELOOPBACK+#endif+#if HAVE_DECL_IPV6_V6ONLY+ IPv6Only -> #const IPV6_V6ONLY+#endif+ unknown -> error ("Network.Socket.packSocketOption: unknown option " +++ show unknown)++setSocketOption :: Socket + -> SocketOption -- Option Name+ -> Int -- Option Value+ -> IO ()+setSocketOption (MkSocket s _ _ _ _) so v = do+ with (fromIntegral v) $ \ptr_v -> do+ throwErrnoIfMinus1_ "setSocketOption" $+ c_setsockopt s (socketOptLevel so) (packSocketOption so) ptr_v + (fromIntegral (sizeOf (undefined :: CInt)))+ return ()+++getSocketOption :: Socket+ -> SocketOption -- Option Name+ -> IO Int -- Option Value+getSocketOption (MkSocket s _ _ _ _) so = do+ alloca $ \ptr_v ->+ with (fromIntegral (sizeOf (undefined :: CInt))) $ \ptr_sz -> do+ throwErrnoIfMinus1 "getSocketOption" $+ c_getsockopt s (socketOptLevel so) (packSocketOption so) ptr_v ptr_sz+ fromIntegral `liftM` peek ptr_v+++#ifdef HAVE_STRUCT_UCRED+-- | Returns the processID, userID and groupID of the socket's peer.+--+-- Only available on platforms that support SO_PEERCRED on domain sockets.+getPeerCred :: Socket -> IO (CUInt, CUInt, CUInt)+getPeerCred sock = do+ let fd = fdSocket sock+ let sz = (fromIntegral (#const sizeof(struct ucred)))+ with sz $ \ ptr_cr -> + alloca $ \ ptr_sz -> do+ poke ptr_sz sz+ throwErrnoIfMinus1 "getPeerCred" $+ c_getsockopt fd (#const SOL_SOCKET) (#const SO_PEERCRED) ptr_cr ptr_sz+ pid <- (#peek struct ucred, pid) ptr_cr+ uid <- (#peek struct ucred, uid) ptr_cr+ gid <- (#peek struct ucred, gid) ptr_cr+ return (pid, uid, gid)+#endif++##if !(MIN_VERSION_base(4,3,1))+closeFdWith closer fd = closer fd+##endif++#if defined(DOMAIN_SOCKET_SUPPORT)+-- sending/receiving ancillary socket data; low-level mechanism+-- for transmitting file descriptors, mainly.+sendFd :: Socket -> CInt -> IO ()+sendFd sock outfd = do+ let fd = fdSocket sock+#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "sendFd"+ (threadWaitWrite (fromIntegral fd)) $+ c_sendFd fd outfd+#else+ c_sendFd fd outfd+#endif+ -- Note: If Winsock supported FD-passing, thi would have been + -- incorrect (since socket FDs need to be closed via closesocket().)+ close outfd+ +recvFd :: Socket -> IO CInt+recvFd sock = do+ let fd = fdSocket sock+ theFd <- +#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "recvFd" + (threadWaitRead (fromIntegral fd)) $+#endif+ c_recvFd fd+ return theFd+++sendAncillary :: Socket+ -> Int+ -> Int+ -> Int+ -> Ptr a+ -> Int+ -> IO ()+sendAncillary sock level ty flags datum len = do+ let fd = fdSocket sock+ _ <-+#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "sendAncillary"+ (threadWaitWrite (fromIntegral fd)) $+#endif+ c_sendAncillary fd (fromIntegral level) (fromIntegral ty)+ (fromIntegral flags) datum (fromIntegral len)+ return ()++recvAncillary :: Socket+ -> Int+ -> Int+ -> IO (Int,Int,Ptr a,Int)+recvAncillary sock flags len = do+ let fd = fdSocket sock+ alloca $ \ ptr_len ->+ alloca $ \ ptr_lev ->+ alloca $ \ ptr_ty ->+ alloca $ \ ptr_pData -> do+ poke ptr_len (fromIntegral len)+ _ <- +#if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "recvAncillary" + (threadWaitRead (fromIntegral fd)) $+#endif+ c_recvAncillary fd ptr_lev ptr_ty (fromIntegral flags) ptr_pData ptr_len+ rcvlen <- fromIntegral `liftM` peek ptr_len+ lev <- fromIntegral `liftM` peek ptr_lev+ ty <- fromIntegral `liftM` peek ptr_ty+ pD <- peek ptr_pData+ return (lev,ty,pD, rcvlen)+foreign import ccall SAFE_ON_WIN "sendAncillary"+ c_sendAncillary :: CInt -> CInt -> CInt -> CInt -> Ptr a -> CInt -> IO CInt++foreign import ccall SAFE_ON_WIN "recvAncillary"+ c_recvAncillary :: CInt -> Ptr CInt -> Ptr CInt -> CInt -> Ptr (Ptr a) -> Ptr CInt -> IO CInt++foreign import ccall SAFE_ON_WIN "sendFd" c_sendFd :: CInt -> CInt -> IO CInt+foreign import ccall SAFE_ON_WIN "recvFd" c_recvFd :: CInt -> IO CInt++#endif+++{-+A calling sequence table for the main functions is shown in the table below.++\begin{figure}[h]+\begin{center}+\begin{tabular}{|l|c|c|c|c|c|c|c|}d+\hline+{\bf A Call to} & socket & connect & bindSocket & listen & accept & read & write \\+\hline+{\bf Precedes} & & & & & & & \\+\hline +socket & & & & & & & \\+\hline+connect & + & & & & & & \\+\hline+bindSocket & + & & & & & & \\+\hline+listen & & & + & & & & \\+\hline+accept & & & & + & & & \\+\hline+read & & + & & + & + & + & + \\+\hline+write & & + & & + & + & + & + \\+\hline+\end{tabular}+\caption{Sequence Table for Major functions of Socket}+\label{tab:api-seq}+\end{center}+\end{figure}+-}++-- ---------------------------------------------------------------------------+-- OS Dependent Definitions+ +unpackFamily :: CInt -> Family+packFamily :: Family -> CInt++packSocketType :: SocketType -> CInt+unpackSocketType:: CInt -> SocketType++------ ------+ +packFamily f = case f of+ AF_UNSPEC -> #const AF_UNSPEC+#ifdef AF_UNIX+ AF_UNIX -> #const AF_UNIX+#endif+#ifdef AF_INET+ AF_INET -> #const AF_INET+#endif+#ifdef AF_INET6+ AF_INET6 -> #const AF_INET6+#endif+#ifdef AF_IMPLINK+ AF_IMPLINK -> #const AF_IMPLINK+#endif+#ifdef AF_PUP+ AF_PUP -> #const AF_PUP+#endif+#ifdef AF_CHAOS+ AF_CHAOS -> #const AF_CHAOS+#endif+#ifdef AF_NS+ AF_NS -> #const AF_NS+#endif+#ifdef AF_NBS+ AF_NBS -> #const AF_NBS+#endif+#ifdef AF_ECMA+ AF_ECMA -> #const AF_ECMA+#endif+#ifdef AF_DATAKIT+ AF_DATAKIT -> #const AF_DATAKIT+#endif+#ifdef AF_CCITT+ AF_CCITT -> #const AF_CCITT+#endif+#ifdef AF_SNA+ AF_SNA -> #const AF_SNA+#endif+#ifdef AF_DECnet+ AF_DECnet -> #const AF_DECnet+#endif+#ifdef AF_DLI+ AF_DLI -> #const AF_DLI+#endif+#ifdef AF_LAT+ AF_LAT -> #const AF_LAT+#endif+#ifdef AF_HYLINK+ AF_HYLINK -> #const AF_HYLINK+#endif+#ifdef AF_APPLETALK+ AF_APPLETALK -> #const AF_APPLETALK+#endif+#ifdef AF_ROUTE+ AF_ROUTE -> #const AF_ROUTE+#endif+#ifdef AF_NETBIOS+ AF_NETBIOS -> #const AF_NETBIOS+#endif+#ifdef AF_NIT+ AF_NIT -> #const AF_NIT+#endif+#ifdef AF_802+ AF_802 -> #const AF_802+#endif+#ifdef AF_ISO+ AF_ISO -> #const AF_ISO+#endif+#ifdef AF_OSI+ AF_OSI -> #const AF_OSI+#endif+#ifdef AF_NETMAN+ AF_NETMAN -> #const AF_NETMAN+#endif+#ifdef AF_X25+ AF_X25 -> #const AF_X25+#endif+#ifdef AF_AX25+ AF_AX25 -> #const AF_AX25+#endif+#ifdef AF_OSINET+ AF_OSINET -> #const AF_OSINET+#endif+#ifdef AF_GOSSIP+ AF_GOSSIP -> #const AF_GOSSIP+#endif+#ifdef AF_IPX+ AF_IPX -> #const AF_IPX+#endif+#ifdef Pseudo_AF_XTP+ Pseudo_AF_XTP -> #const Pseudo_AF_XTP+#endif+#ifdef AF_CTF+ AF_CTF -> #const AF_CTF+#endif+#ifdef AF_WAN+ AF_WAN -> #const AF_WAN+#endif+#ifdef AF_SDL+ AF_SDL -> #const AF_SDL+#endif+#ifdef AF_NETWARE+ AF_NETWARE -> #const AF_NETWARE +#endif+#ifdef AF_NDD+ AF_NDD -> #const AF_NDD +#endif+#ifdef AF_INTF+ AF_INTF -> #const AF_INTF+#endif+#ifdef AF_COIP+ AF_COIP -> #const AF_COIP+#endif+#ifdef AF_CNT+ AF_CNT -> #const AF_CNT+#endif+#ifdef Pseudo_AF_RTIP+ Pseudo_AF_RTIP -> #const Pseudo_AF_RTIP+#endif+#ifdef Pseudo_AF_PIP+ Pseudo_AF_PIP -> #const Pseudo_AF_PIP+#endif+#ifdef AF_SIP+ AF_SIP -> #const AF_SIP+#endif+#ifdef AF_ISDN+ AF_ISDN -> #const AF_ISDN+#endif+#ifdef Pseudo_AF_KEY+ Pseudo_AF_KEY -> #const Pseudo_AF_KEY+#endif+#ifdef AF_NATM+ AF_NATM -> #const AF_NATM+#endif+#ifdef AF_ARP+ AF_ARP -> #const AF_ARP+#endif+#ifdef Pseudo_AF_HDRCMPLT+ Pseudo_AF_HDRCMPLT -> #const Pseudo_AF_HDRCMPLT+#endif+#ifdef AF_ENCAP+ AF_ENCAP -> #const AF_ENCAP +#endif+#ifdef AF_LINK+ AF_LINK -> #const AF_LINK+#endif+#ifdef AF_RAW+ AF_RAW -> #const AF_RAW+#endif+#ifdef AF_RIF+ AF_RIF -> #const AF_RIF+#endif+#ifdef AF_NETROM+ AF_NETROM -> #const AF_NETROM+#endif+#ifdef AF_BRIDGE+ AF_BRIDGE -> #const AF_BRIDGE+#endif+#ifdef AF_ATMPVC+ AF_ATMPVC -> #const AF_ATMPVC+#endif+#ifdef AF_ROSE+ AF_ROSE -> #const AF_ROSE+#endif+#ifdef AF_NETBEUI+ AF_NETBEUI -> #const AF_NETBEUI+#endif+#ifdef AF_SECURITY+ AF_SECURITY -> #const AF_SECURITY+#endif+#ifdef AF_PACKET+ AF_PACKET -> #const AF_PACKET+#endif+#ifdef AF_ASH+ AF_ASH -> #const AF_ASH+#endif+#ifdef AF_ECONET+ AF_ECONET -> #const AF_ECONET+#endif+#ifdef AF_ATMSVC+ AF_ATMSVC -> #const AF_ATMSVC+#endif+#ifdef AF_IRDA+ AF_IRDA -> #const AF_IRDA+#endif+#ifdef AF_PPPOX+ AF_PPPOX -> #const AF_PPPOX+#endif+#ifdef AF_WANPIPE+ AF_WANPIPE -> #const AF_WANPIPE+#endif+#ifdef AF_BLUETOOTH+ AF_BLUETOOTH -> #const AF_BLUETOOTH+#endif++--------- ----------++unpackFamily f = case f of+ (#const AF_UNSPEC) -> AF_UNSPEC+#ifdef AF_UNIX+ (#const AF_UNIX) -> AF_UNIX+#endif+#ifdef AF_INET+ (#const AF_INET) -> AF_INET+#endif+#ifdef AF_INET6+ (#const AF_INET6) -> AF_INET6+#endif+#ifdef AF_IMPLINK+ (#const AF_IMPLINK) -> AF_IMPLINK+#endif+#ifdef AF_PUP+ (#const AF_PUP) -> AF_PUP+#endif+#ifdef AF_CHAOS+ (#const AF_CHAOS) -> AF_CHAOS+#endif+#ifdef AF_NS+ (#const AF_NS) -> AF_NS+#endif+#ifdef AF_NBS+ (#const AF_NBS) -> AF_NBS+#endif+#ifdef AF_ECMA+ (#const AF_ECMA) -> AF_ECMA+#endif+#ifdef AF_DATAKIT+ (#const AF_DATAKIT) -> AF_DATAKIT+#endif+#ifdef AF_CCITT+ (#const AF_CCITT) -> AF_CCITT+#endif+#ifdef AF_SNA+ (#const AF_SNA) -> AF_SNA+#endif+#ifdef AF_DECnet+ (#const AF_DECnet) -> AF_DECnet+#endif+#ifdef AF_DLI+ (#const AF_DLI) -> AF_DLI+#endif+#ifdef AF_LAT+ (#const AF_LAT) -> AF_LAT+#endif+#ifdef AF_HYLINK+ (#const AF_HYLINK) -> AF_HYLINK+#endif+#ifdef AF_APPLETALK+ (#const AF_APPLETALK) -> AF_APPLETALK+#endif+#ifdef AF_ROUTE+ (#const AF_ROUTE) -> AF_ROUTE+#endif+#ifdef AF_NETBIOS+ (#const AF_NETBIOS) -> AF_NETBIOS+#endif+#ifdef AF_NIT+ (#const AF_NIT) -> AF_NIT+#endif+#ifdef AF_802+ (#const AF_802) -> AF_802+#endif+#ifdef AF_ISO+ (#const AF_ISO) -> AF_ISO+#endif+#ifdef AF_OSI+# if (!defined(AF_ISO)) || (defined(AF_ISO) && (AF_ISO != AF_OSI))+ (#const AF_OSI) -> AF_OSI+# endif+#endif+#ifdef AF_NETMAN+ (#const AF_NETMAN) -> AF_NETMAN+#endif+#ifdef AF_X25+ (#const AF_X25) -> AF_X25+#endif+#ifdef AF_AX25+ (#const AF_AX25) -> AF_AX25+#endif+#ifdef AF_OSINET+ (#const AF_OSINET) -> AF_OSINET+#endif+#ifdef AF_GOSSIP+ (#const AF_GOSSIP) -> AF_GOSSIP+#endif+#if defined(AF_IPX) && (!defined(AF_NS) || AF_NS != AF_IPX)+ (#const AF_IPX) -> AF_IPX+#endif+#ifdef Pseudo_AF_XTP+ (#const Pseudo_AF_XTP) -> Pseudo_AF_XTP+#endif+#ifdef AF_CTF+ (#const AF_CTF) -> AF_CTF+#endif+#ifdef AF_WAN+ (#const AF_WAN) -> AF_WAN+#endif+#ifdef AF_SDL+ (#const AF_SDL) -> AF_SDL+#endif+#ifdef AF_NETWARE+ (#const AF_NETWARE) -> AF_NETWARE +#endif+#ifdef AF_NDD+ (#const AF_NDD) -> AF_NDD +#endif+#ifdef AF_INTF+ (#const AF_INTF) -> AF_INTF+#endif+#ifdef AF_COIP+ (#const AF_COIP) -> AF_COIP+#endif+#ifdef AF_CNT+ (#const AF_CNT) -> AF_CNT+#endif+#ifdef Pseudo_AF_RTIP+ (#const Pseudo_AF_RTIP) -> Pseudo_AF_RTIP+#endif+#ifdef Pseudo_AF_PIP+ (#const Pseudo_AF_PIP) -> Pseudo_AF_PIP+#endif+#ifdef AF_SIP+ (#const AF_SIP) -> AF_SIP+#endif+#ifdef AF_ISDN+ (#const AF_ISDN) -> AF_ISDN+#endif+#ifdef Pseudo_AF_KEY+ (#const Pseudo_AF_KEY) -> Pseudo_AF_KEY+#endif+#ifdef AF_NATM+ (#const AF_NATM) -> AF_NATM+#endif+#ifdef AF_ARP+ (#const AF_ARP) -> AF_ARP+#endif+#ifdef Pseudo_AF_HDRCMPLT+ (#const Pseudo_AF_HDRCMPLT) -> Pseudo_AF_HDRCMPLT+#endif+#ifdef AF_ENCAP+ (#const AF_ENCAP) -> AF_ENCAP +#endif+#ifdef AF_LINK+ (#const AF_LINK) -> AF_LINK+#endif+#ifdef AF_RAW+ (#const AF_RAW) -> AF_RAW+#endif+#ifdef AF_RIF+ (#const AF_RIF) -> AF_RIF+#endif+#ifdef AF_NETROM+ (#const AF_NETROM) -> AF_NETROM+#endif+#ifdef AF_BRIDGE+ (#const AF_BRIDGE) -> AF_BRIDGE+#endif+#ifdef AF_ATMPVC+ (#const AF_ATMPVC) -> AF_ATMPVC+#endif+#ifdef AF_ROSE+ (#const AF_ROSE) -> AF_ROSE+#endif+#ifdef AF_NETBEUI+ (#const AF_NETBEUI) -> AF_NETBEUI+#endif+#ifdef AF_SECURITY+ (#const AF_SECURITY) -> AF_SECURITY+#endif+#ifdef AF_PACKET+ (#const AF_PACKET) -> AF_PACKET+#endif+#ifdef AF_ASH+ (#const AF_ASH) -> AF_ASH+#endif+#ifdef AF_ECONET+ (#const AF_ECONET) -> AF_ECONET+#endif+#ifdef AF_ATMSVC+ (#const AF_ATMSVC) -> AF_ATMSVC+#endif+#ifdef AF_IRDA+ (#const AF_IRDA) -> AF_IRDA+#endif+#ifdef AF_PPPOX+ (#const AF_PPPOX) -> AF_PPPOX+#endif+#ifdef AF_WANPIPE+ (#const AF_WANPIPE) -> AF_WANPIPE+#endif+#ifdef AF_BLUETOOTH+ (#const AF_BLUETOOTH) -> AF_BLUETOOTH+#endif+ unknown -> error ("Network.Socket.unpackFamily: unknown address " +++ "family " ++ show unknown)++-- Socket Types.++-- | Socket Types.+--+-- This data type might have different constructors depending on what is+-- supported by the operating system.+data SocketType+ = NoSocketType+#ifdef SOCK_STREAM+ | Stream +#endif+#ifdef SOCK_DGRAM+ | Datagram+#endif+#ifdef SOCK_RAW+ | Raw +#endif+#ifdef SOCK_RDM+ | RDM +#endif+#ifdef SOCK_SEQPACKET+ | SeqPacket+#endif+ deriving (Eq, Ord, Read, Show, Typeable)++packSocketType stype = case stype of+ NoSocketType -> 0+#ifdef SOCK_STREAM+ Stream -> #const SOCK_STREAM+#endif+#ifdef SOCK_DGRAM+ Datagram -> #const SOCK_DGRAM+#endif+#ifdef SOCK_RAW+ Raw -> #const SOCK_RAW+#endif+#ifdef SOCK_RDM+ RDM -> #const SOCK_RDM+#endif+#ifdef SOCK_SEQPACKET+ SeqPacket -> #const SOCK_SEQPACKET+#endif++unpackSocketType t = case t of+ 0 -> NoSocketType+#ifdef SOCK_STREAM+ (#const SOCK_STREAM) -> Stream+#endif+#ifdef SOCK_DGRAM+ (#const SOCK_DGRAM) -> Datagram+#endif+#ifdef SOCK_RAW+ (#const SOCK_RAW) -> Raw+#endif+#ifdef SOCK_RDM+ (#const SOCK_RDM) -> RDM+#endif+#ifdef SOCK_SEQPACKET+ (#const SOCK_SEQPACKET) -> SeqPacket+#endif+ _ -> NoSocketType++-- ---------------------------------------------------------------------------+-- Utility Functions++aNY_PORT :: PortNumber +aNY_PORT = 0++-- | The IPv4 wild card address.++iNADDR_ANY :: HostAddress+iNADDR_ANY = htonl (#const INADDR_ANY)++#if defined(IPV6_SOCKET_SUPPORT)+-- | The IPv6 wild card address.++iN6ADDR_ANY :: HostAddress6+iN6ADDR_ANY = (0, 0, 0, 0)+#endif++sOMAXCONN :: Int+sOMAXCONN = #const SOMAXCONN++sOL_SOCKET :: Int+sOL_SOCKET = #const SOL_SOCKET++#ifdef SCM_RIGHTS+sCM_RIGHTS :: Int+sCM_RIGHTS = #const SCM_RIGHTS+#endif++maxListenQueue :: Int+maxListenQueue = sOMAXCONN++-- -----------------------------------------------------------------------------++data ShutdownCmd + = ShutdownReceive+ | ShutdownSend+ | ShutdownBoth+ deriving Typeable++sdownCmdToInt :: ShutdownCmd -> CInt+sdownCmdToInt ShutdownReceive = 0+sdownCmdToInt ShutdownSend = 1+sdownCmdToInt ShutdownBoth = 2++-- | Shut down one or both halves of the connection, depending on the+-- second argument to the function. If the second argument is+-- 'ShutdownReceive', further receives are disallowed. If it is+-- 'ShutdownSend', further sends are disallowed. If it is+-- 'ShutdownBoth', further sends and receives are disallowed.+shutdown :: Socket -> ShutdownCmd -> IO ()+shutdown (MkSocket s _ _ _ _) stype = do+ throwSocketErrorIfMinus1Retry "shutdown" (c_shutdown s (sdownCmdToInt stype))+ return ()++-- -----------------------------------------------------------------------------++-- | Close the socket. All future operations on the socket object+-- will fail. The remote end will receive no more data (after queued+-- data is flushed).+sClose :: Socket -> IO ()+sClose (MkSocket s _ _ _ socketStatus) = do + modifyMVar_ socketStatus $ \ status ->+ case status of+ ConvertedToHandle ->+ ioError (userError ("sClose: converted to a Handle, use hClose instead"))+ Closed ->+ return status+ _ -> closeFdWith (close . fromIntegral) (fromIntegral s) >> return Closed++-- -----------------------------------------------------------------------------++sIsConnected :: Socket -> IO Bool+sIsConnected (MkSocket _ _ _ _ status) = do+ value <- readMVar status+ return (value == Connected) ++-- -----------------------------------------------------------------------------+-- Socket Predicates++sIsBound :: Socket -> IO Bool+sIsBound (MkSocket _ _ _ _ status) = do+ value <- readMVar status+ return (value == Bound) ++sIsListening :: Socket -> IO Bool+sIsListening (MkSocket _ _ _ _ status) = do+ value <- readMVar status+ return (value == Listening) ++sIsReadable :: Socket -> IO Bool+sIsReadable (MkSocket _ _ _ _ status) = do+ value <- readMVar status+ return (value == Listening || value == Connected)++sIsWritable :: Socket -> IO Bool+sIsWritable = sIsReadable -- sort of.++sIsAcceptable :: Socket -> IO Bool+#if defined(DOMAIN_SOCKET_SUPPORT)+sIsAcceptable (MkSocket _ AF_UNIX x _ status)+ | x == Stream || x == SeqPacket = do+ value <- readMVar status+ return (value == Connected || value == Bound || value == Listening)+sIsAcceptable (MkSocket _ AF_UNIX _ _ _) = return False+#endif+sIsAcceptable (MkSocket _ _ _ _ status) = do+ value <- readMVar status+ return (value == Connected || value == Listening)+ +-- -----------------------------------------------------------------------------+-- Internet address manipulation routines:++inet_addr :: String -> IO HostAddress+inet_addr ipstr = do+ withCString ipstr $ \str -> do+ had <- c_inet_addr str+ if had == -1+ then ioError (userError ("inet_addr: Malformed address: " ++ ipstr))+ else return had -- network byte order++inet_ntoa :: HostAddress -> IO String+inet_ntoa haddr = do+ pstr <- c_inet_ntoa haddr+ peekCString pstr++-- | Turns a Socket into an 'Handle'. By default, the new handle is+-- unbuffered. Use 'System.IO.hSetBuffering' to change the buffering.+--+-- Note that since a 'Handle' is automatically closed by a finalizer+-- when it is no longer referenced, you should avoid doing any more+-- operations on the 'Socket' after calling 'socketToHandle'. To+-- close the 'Socket' after 'socketToHandle', call 'System.IO.hClose'+-- on the 'Handle'.++#ifndef __PARALLEL_HASKELL__+socketToHandle :: Socket -> IOMode -> IO Handle+socketToHandle s@(MkSocket fd _ _ _ socketStatus) mode = do+ modifyMVar socketStatus $ \ status ->+ if status == ConvertedToHandle+ then ioError (userError ("socketToHandle: already a Handle"))+ else do+# if __GLASGOW_HASKELL__ >= 611+ h <- fdToHandle' (fromIntegral fd) (Just GHC.IO.Device.Stream) True (show s) mode True{-bin-}+# elif __GLASGOW_HASKELL__ >= 608+ h <- fdToHandle' (fromIntegral fd) (Just System.Posix.Internals.Stream) True (show s) mode True{-bin-}+# elif __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 608+ h <- openFd (fromIntegral fd) (Just System.Posix.Internals.Stream) True (show s) mode True{-bin-}+# elif defined(__HUGS__)+ h <- openFd (fromIntegral fd) True{-is a socket-} mode True{-bin-}+# endif+ return (ConvertedToHandle, h)+#else+socketToHandle (MkSocket s family stype protocol status) m =+ error "socketToHandle not implemented in a parallel setup"+#endif++-- | Pack a list of values into a bitmask. The possible mappings from+-- value to bit-to-set are given as the first argument. We assume+-- that each value can cause exactly one bit to be set; unpackBits will+-- break if this property is not true.++packBits :: (Eq a, Num b, Bits b) => [(a, b)] -> [a] -> b++packBits mapping xs = foldl' pack 0 mapping+ where pack acc (k, v) | k `elem` xs = acc .|. v+ | otherwise = acc++-- | Unpack a bitmask into a list of values.++unpackBits :: (Num b, Bits b) => [(a, b)] -> b -> [a]++-- Be permissive and ignore unknown bit values. At least on OS X,+-- getaddrinfo returns an ai_flags field with bits set that have no+-- entry in <netdb.h>.+unpackBits [] _ = []+unpackBits ((k,v):xs) r+ | r .&. v /= 0 = k : unpackBits xs (r .&. complement v)+ | otherwise = unpackBits xs r++-----------------------------------------------------------------------------+-- Address and service lookups++#if defined(IPV6_SOCKET_SUPPORT)++-- | Flags that control the querying behaviour of 'getAddrInfo'.+data AddrInfoFlag+ = AI_ADDRCONFIG+ | AI_ALL+ | AI_CANONNAME+ | AI_NUMERICHOST+ | AI_NUMERICSERV+ | AI_PASSIVE+ | AI_V4MAPPED+ deriving (Eq, Read, Show, Typeable)++aiFlagMapping :: [(AddrInfoFlag, CInt)]++aiFlagMapping =+ [+#if HAVE_DECL_AI_ADDRCONFIG+ (AI_ADDRCONFIG, #const AI_ADDRCONFIG),+#else+ (AI_ADDRCONFIG, 0),+#endif+#if HAVE_DECL_AI_ALL+ (AI_ALL, #const AI_ALL),+#else+ (AI_ALL, 0),+#endif+ (AI_CANONNAME, #const AI_CANONNAME),+ (AI_NUMERICHOST, #const AI_NUMERICHOST),+#if HAVE_DECL_AI_NUMERICSERV+ (AI_NUMERICSERV, #const AI_NUMERICSERV),+#else+ (AI_NUMERICSERV, 0),+#endif+ (AI_PASSIVE, #const AI_PASSIVE),+#if HAVE_DECL_AI_V4MAPPED+ (AI_V4MAPPED, #const AI_V4MAPPED)+#else+ (AI_V4MAPPED, 0)+#endif+ ]++-- | Indicate whether the given 'AddrInfoFlag' will have any effect on+-- this system.+addrInfoFlagImplemented :: AddrInfoFlag -> Bool+addrInfoFlagImplemented f = packBits aiFlagMapping [f] /= 0++data AddrInfo =+ AddrInfo {+ addrFlags :: [AddrInfoFlag],+ addrFamily :: Family,+ addrSocketType :: SocketType,+ addrProtocol :: ProtocolNumber,+ addrAddress :: SockAddr,+ addrCanonName :: Maybe String+ }+ deriving (Eq, Show, Typeable)++instance Storable AddrInfo where+ sizeOf _ = #const sizeof(struct addrinfo)+ alignment _ = alignment (undefined :: CInt)++ peek p = do+ ai_flags <- (#peek struct addrinfo, ai_flags) p+ ai_family <- (#peek struct addrinfo, ai_family) p+ ai_socktype <- (#peek struct addrinfo, ai_socktype) p+ ai_protocol <- (#peek struct addrinfo, ai_protocol) p+ ai_addr <- (#peek struct addrinfo, ai_addr) p >>= peekSockAddr+ ai_canonname_ptr <- (#peek struct addrinfo, ai_canonname) p++ ai_canonname <- if ai_canonname_ptr == nullPtr+ then return Nothing+ else liftM Just $ peekCString ai_canonname_ptr+ + return (AddrInfo+ {+ addrFlags = unpackBits aiFlagMapping ai_flags,+ addrFamily = unpackFamily ai_family,+ addrSocketType = unpackSocketType ai_socktype,+ addrProtocol = ai_protocol,+ addrAddress = ai_addr,+ addrCanonName = ai_canonname+ })++ poke p (AddrInfo flags family socketType protocol _ _) = do+ (#poke struct addrinfo, ai_flags) p (packBits aiFlagMapping flags)+ (#poke struct addrinfo, ai_family) p (packFamily family)+ (#poke struct addrinfo, ai_socktype) p (packSocketType socketType)+ (#poke struct addrinfo, ai_protocol) p protocol++ -- stuff below is probably not needed, but let's zero it for safety++ (#poke struct addrinfo, ai_addrlen) p (0::CSize)+ (#poke struct addrinfo, ai_addr) p nullPtr+ (#poke struct addrinfo, ai_canonname) p nullPtr+ (#poke struct addrinfo, ai_next) p nullPtr++data NameInfoFlag+ = NI_DGRAM+ | NI_NAMEREQD+ | NI_NOFQDN+ | NI_NUMERICHOST+ | NI_NUMERICSERV+ deriving (Eq, Read, Show, Typeable)++niFlagMapping :: [(NameInfoFlag, CInt)]++niFlagMapping = [(NI_DGRAM, #const NI_DGRAM),+ (NI_NAMEREQD, #const NI_NAMEREQD),+ (NI_NOFQDN, #const NI_NOFQDN),+ (NI_NUMERICHOST, #const NI_NUMERICHOST),+ (NI_NUMERICSERV, #const NI_NUMERICSERV)]++-- | Default hints for address lookup with 'getAddrInfo'. The values+-- of the 'addrAddress' and 'addrCanonName' fields are 'undefined',+-- and are never inspected by 'getAddrInfo'.++defaultHints :: AddrInfo++defaultHints = AddrInfo {+ addrFlags = [],+ addrFamily = AF_UNSPEC,+ addrSocketType = NoSocketType,+ addrProtocol = defaultProtocol,+ addrAddress = undefined,+ addrCanonName = undefined+ }++-- | Resolve a host or service name to one or more addresses.+-- The 'AddrInfo' values that this function returns contain 'SockAddr'+-- values that you can pass directly to 'connect' or+-- 'bindSocket'.+--+-- This function is protocol independent. It can return both IPv4 and+-- IPv6 address information.+--+-- The 'AddrInfo' argument specifies the preferred query behaviour,+-- socket options, or protocol. You can override these conveniently+-- using Haskell's record update syntax on 'defaultHints', for example+-- as follows:+--+-- @+-- myHints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] }+-- @+--+-- Values for 'addrFlags' control query behaviour. The supported+-- flags are as follows:+--+-- [@AI_PASSIVE@] If no 'HostName' value is provided, the network+-- address in each 'SockAddr'+-- will be left as a "wild card", i.e. as either 'iNADDR_ANY'+-- or 'iN6ADDR_ANY'. This is useful for server applications that+-- will accept connections from any client.+--+-- [@AI_CANONNAME@] The 'addrCanonName' field of the first returned+-- 'AddrInfo' will contain the "canonical name" of the host.+--+-- [@AI_NUMERICHOST@] The 'HostName' argument /must/ be a numeric+-- address in string form, and network name lookups will not be+-- attempted.+-- +-- /Note/: Although the following flags are required by RFC 3493, they+-- may not have an effect on all platforms, because the underlying+-- network stack may not support them. To see whether a flag from the+-- list below will have any effect, call 'addrInfoFlagImplemented'.+--+-- [@AI_NUMERICSERV@] The 'ServiceName' argument /must/ be a port+-- number in string form, and service name lookups will not be+-- attempted.+--+-- [@AI_ADDRCONFIG@] The list of returned 'AddrInfo' values will+-- only contain IPv4 addresses if the local system has at least+-- one IPv4 interface configured, and likewise for IPv6.+--+-- [@AI_V4MAPPED@] If an IPv6 lookup is performed, and no IPv6+-- addresses are found, IPv6-mapped IPv4 addresses will be+-- returned.+--+-- [@AI_ALL@] If 'AI_ALL' is specified, return all matching IPv6 and+-- IPv4 addresses. Otherwise, this flag has no effect.+-- +-- You must provide a 'Just' value for at least one of the 'HostName'+-- or 'ServiceName' arguments. 'HostName' can be either a numeric+-- network address (dotted quad for IPv4, colon-separated hex for+-- IPv6) or a hostname. In the latter case, its addresses will be+-- looked up unless 'AI_NUMERICHOST' is specified as a hint. If you+-- do not provide a 'HostName' value /and/ do not set 'AI_PASSIVE' as+-- a hint, network addresses in the result will contain the address of+-- the loopback interface.+--+-- If the query fails, this function throws an IO exception instead of+-- returning an empty list. Otherwise, it returns a non-empty list+-- of 'AddrInfo' values.+--+-- There are several reasons why a query might result in several+-- values. For example, the queried-for host could be multihomed, or+-- the service might be available via several protocols.+--+-- Note: the order of arguments is slightly different to that defined+-- for @getaddrinfo@ in RFC 2553. The 'AddrInfo' parameter comes first+-- to make partial application easier.+--+-- Example:+-- @+-- let hints = defaultHints { addrFlags = [AI_ADDRCONFIG, AI_CANONNAME] }+-- addrs <- getAddrInfo (Just hints) (Just "www.haskell.org") (Just "http")+-- let addr = head addrs+-- sock <- socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr)+-- connect sock (addrAddress addr)+-- @++getAddrInfo :: Maybe AddrInfo -- ^ preferred socket type or protocol+ -> Maybe HostName -- ^ host name to look up+ -> Maybe ServiceName -- ^ service name to look up+ -> IO [AddrInfo] -- ^ resolved addresses, with "best" first++getAddrInfo hints node service =+ maybeWith withCString node $ \c_node ->+ maybeWith withCString service $ \c_service ->+ maybeWith with hints $ \c_hints ->+ alloca $ \ptr_ptr_addrs -> do+ ret <- c_getaddrinfo c_node c_service c_hints ptr_ptr_addrs+ case ret of+ 0 -> do ptr_addrs <- peek ptr_ptr_addrs+ ais <- followAddrInfo ptr_addrs+ c_freeaddrinfo ptr_addrs+ return ais+ _ -> do err <- gai_strerror ret+ ioError (ioeSetErrorString+ (mkIOError NoSuchThing "getAddrInfo" Nothing+ Nothing) err)++followAddrInfo :: Ptr AddrInfo -> IO [AddrInfo]++followAddrInfo ptr_ai | ptr_ai == nullPtr = return []+ | otherwise = do+ a <- peek ptr_ai+ as <- (#peek struct addrinfo, ai_next) ptr_ai >>= followAddrInfo+ return (a:as)++foreign import ccall safe "hsnet_getaddrinfo"+ c_getaddrinfo :: CString -> CString -> Ptr AddrInfo -> Ptr (Ptr AddrInfo)+ -> IO CInt++foreign import ccall safe "hsnet_freeaddrinfo"+ c_freeaddrinfo :: Ptr AddrInfo -> IO ()++gai_strerror :: CInt -> IO String++#ifdef HAVE_GAI_STRERROR+gai_strerror n = c_gai_strerror n >>= peekCString++foreign import ccall safe "gai_strerror"+ c_gai_strerror :: CInt -> IO CString+#else+gai_strerror n = return ("error " ++ show n)+#endif++withCStringIf :: Bool -> Int -> (CSize -> CString -> IO a) -> IO a+withCStringIf False _ f = f 0 nullPtr+withCStringIf True n f = allocaBytes n (f (fromIntegral n))+ +-- | Resolve an address to a host or service name.+-- This function is protocol independent.+--+-- The list of 'NameInfoFlag' values controls query behaviour. The+-- supported flags are as follows:+--+-- [@NI_NOFQDN@] If a host is local, return only the+-- hostname part of the FQDN.+--+-- [@NI_NUMERICHOST@] The name of the host is not+-- looked up. Instead, a numeric representation of the host's+-- address is returned. For an IPv4 address, this will be a+-- dotted-quad string. For IPv6, it will be colon-separated+-- hexadecimal.+--+-- [@NI_NUMERICSERV@] The name of the service is not+-- looked up. Instead, a numeric representation of the+-- service is returned.+--+-- [@NI_NAMEREQD@] If the hostname cannot be looked up, an IO error+-- is thrown.+--+-- [@NI_DGRAM@] Resolve a datagram-based service name. This is+-- required only for the few protocols that have different port+-- numbers for their datagram-based versions than for their+-- stream-based versions.+--+-- Hostname and service name lookups can be expensive. You can+-- specify which lookups to perform via the two 'Bool' arguments. If+-- one of these is 'False', the corresponding value in the returned+-- tuple will be 'Nothing', and no lookup will be performed.+--+-- If a host or service's name cannot be looked up, then the numeric+-- form of the address or service will be returned.+--+-- If the query fails, this function throws an IO exception.+--+-- Example:+-- @+-- (hostName, _) <- getNameInfo [] True False myAddress+-- @++getNameInfo :: [NameInfoFlag] -- ^ flags to control lookup behaviour+ -> Bool -- ^ whether to look up a hostname+ -> Bool -- ^ whether to look up a service name+ -> SockAddr -- ^ the address to look up+ -> IO (Maybe HostName, Maybe ServiceName)++getNameInfo flags doHost doService addr =+ withCStringIf doHost (#const NI_MAXHOST) $ \c_hostlen c_host ->+ withCStringIf doService (#const NI_MAXSERV) $ \c_servlen c_serv -> do+ withSockAddr addr $ \ptr_addr sz -> do+ ret <- c_getnameinfo ptr_addr (fromIntegral sz) c_host c_hostlen+ c_serv c_servlen (packBits niFlagMapping flags)+ case ret of+ 0 -> do+ let peekIf doIf c_val = if doIf+ then liftM Just $ peekCString c_val+ else return Nothing+ host <- peekIf doHost c_host+ serv <- peekIf doService c_serv+ return (host, serv)+ _ -> do err <- gai_strerror ret+ ioError (ioeSetErrorString+ (mkIOError NoSuchThing "getNameInfo" Nothing + Nothing) err)++foreign import ccall safe "hsnet_getnameinfo"+ c_getnameinfo :: Ptr SockAddr -> CInt{-CSockLen???-} -> CString -> CSize -> CString+ -> CSize -> CInt -> IO CInt+#endif++mkInvalidRecvArgError :: String -> IOError+mkInvalidRecvArgError loc = ioeSetErrorString (mkIOError+#ifdef __GLASGOW_HASKELL__+ InvalidArgument+#else+ IllegalOperation+#endif+ loc Nothing Nothing) "non-positive length"++mkEOFError :: String -> IOError+mkEOFError loc = ioeSetErrorString (mkIOError EOF loc Nothing Nothing) "end of file"++-- ---------------------------------------------------------------------------+-- foreign imports from the C library++foreign import ccall unsafe "my_inet_ntoa"+ c_inet_ntoa :: HostAddress -> IO (Ptr CChar)++foreign import CALLCONV unsafe "inet_addr"+ c_inet_addr :: Ptr CChar -> IO HostAddress++foreign import CALLCONV unsafe "shutdown"+ c_shutdown :: CInt -> CInt -> IO CInt ++close :: CInt -> IO ()+close fd = throwErrnoIfMinus1Retry_ "Network.Socket.close" $ c_close fd++#if !defined(WITH_WINSOCK)+foreign import ccall unsafe "close"+ c_close :: CInt -> IO CInt+#else+foreign import stdcall unsafe "closesocket"+ c_close :: CInt -> IO CInt+#endif++foreign import CALLCONV unsafe "socket"+ c_socket :: CInt -> CInt -> CInt -> IO CInt+foreign import CALLCONV unsafe "bind"+ c_bind :: CInt -> Ptr SockAddr -> CInt{-CSockLen???-} -> IO CInt+foreign import CALLCONV unsafe "connect"+ c_connect :: CInt -> Ptr SockAddr -> CInt{-CSockLen???-} -> IO CInt+foreign import CALLCONV unsafe "accept"+ c_accept :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> IO CInt+foreign import CALLCONV unsafe "listen"+ c_listen :: CInt -> CInt -> IO CInt++#if defined(mingw32_HOST_OS) && defined(__GLASGOW_HASKELL__)+foreign import CALLCONV safe "accept"+ c_accept_safe :: CInt -> Ptr SockAddr -> Ptr CInt{-CSockLen???-} -> IO CInt++foreign import ccall "rtsSupportsBoundThreads" threaded :: Bool+#endif++foreign import CALLCONV unsafe "send"+ c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt+foreign import CALLCONV SAFE_ON_WIN "sendto"+ c_sendto :: CInt -> Ptr a -> CSize -> CInt -> Ptr SockAddr -> CInt -> IO CInt+foreign import CALLCONV unsafe "recv"+ c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt+foreign import CALLCONV SAFE_ON_WIN "recvfrom"+ c_recvfrom :: CInt -> Ptr a -> CSize -> CInt -> Ptr SockAddr -> Ptr CInt -> IO CInt+foreign import CALLCONV unsafe "getpeername"+ c_getpeername :: CInt -> Ptr SockAddr -> Ptr CInt -> IO CInt+foreign import CALLCONV unsafe "getsockname"+ c_getsockname :: CInt -> Ptr SockAddr -> Ptr CInt -> IO CInt++foreign import CALLCONV unsafe "getsockopt"+ c_getsockopt :: CInt -> CInt -> CInt -> Ptr CInt -> Ptr CInt -> IO CInt+foreign import CALLCONV unsafe "setsockopt"+ c_setsockopt :: CInt -> CInt -> CInt -> Ptr CInt -> CInt -> IO CInt
+ Network/Socket/ByteString.hsc view
@@ -0,0 +1,383 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}++#include "HsNet.h"++-- |+-- Module : Network.Socket.ByteString+-- Copyright : (c) Johan Tibell 2007-2010+-- License : BSD-style+--+-- Maintainer : johan.tibell@gmail.com+-- Stability : stable+-- Portability : portable+--+-- This module provides access to the BSD /socket/ interface. This+-- module is generally more efficient than the 'String' based network+-- functions in 'Network.Socket'. For detailed documentation, consult+-- your favorite POSIX socket reference. All functions communicate+-- failures by converting the error number to 'System.IO.IOError'.+--+-- This module is made to be imported with 'Network.Socket' like so:+--+-- > import Network.Socket hiding (send, sendTo, recv, recvFrom)+-- > import Network.Socket.ByteString+--+module Network.Socket.ByteString+ ( + -- * Send data to a socket+ send+ , sendAll+ , sendTo+ , sendAllTo++ -- ** Vectored I/O+ -- $vectored+ , sendMany+ , sendManyTo++ -- * Receive data from a socket+ , recv+ , recvFrom++ -- * Example+ -- $example+ ) where++import Control.Monad (liftM, when)+import Data.ByteString (ByteString)+import Data.ByteString.Internal (createAndTrim)+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)+import Data.Word (Word8)+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CInt(..))+#else+import Foreign.C.Types (CInt)+#endif+import Foreign.Marshal.Alloc (allocaBytes)+import Foreign.Ptr (Ptr, castPtr)+import Network.Socket (SockAddr, Socket(..), sendBufTo, recvBufFrom)++import qualified Data.ByteString as B++import Network.Socket.ByteString.Internal++#if !defined(mingw32_HOST_OS)+import Control.Monad (zipWithM_)+import Foreign.C.Types (CChar)+# if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CSize(..))+# else+import Foreign.C.Types (CSize)+# endif+import Foreign.Marshal.Array (allocaArray)+import Foreign.Marshal.Utils (with)+import Foreign.Ptr (plusPtr)+import Foreign.Storable (Storable(..))+import Network.Socket.Internal (throwSocketErrorIfMinus1RetryMayBlock,+ withSockAddr)++import Network.Socket.ByteString.IOVec (IOVec(..))+import Network.Socket.ByteString.MsgHdr (MsgHdr(..))++# if defined(__GLASGOW_HASKELL__)+import GHC.Conc (threadWaitRead, threadWaitWrite)+# endif+#else+# if defined(__GLASGOW_HASKELL__)+# if __GLASGOW_HASKELL__ >= 611+import GHC.IO.FD+# else+import GHC.Handle (readRawBufferPtr, writeRawBufferPtr)+# endif+# endif+#endif++#if !defined(mingw32_HOST_OS)+foreign import CALLCONV unsafe "send"+ c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt+foreign import CALLCONV unsafe "recv"+ c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt+#endif++-- ----------------------------------------------------------------------------+-- Sending++-- | Send data to the socket. The socket must be connected to a+-- remote socket. Returns the number of bytes sent. Applications are+-- responsible for ensuring that all data has been sent.+send :: Socket -- ^ Connected socket+ -> ByteString -- ^ Data to send+ -> IO Int -- ^ Number of bytes sent+send (MkSocket s _ _ _ _) xs =+ unsafeUseAsCStringLen xs $ \(str, len) ->+ liftM fromIntegral $+#if defined(__GLASGOW_HASKELL__) && defined(mingw32_HOST_OS)+# if __GLASGOW_HASKELL__ >= 611+ writeRawBufferPtr "Network.Socket.ByteString.send"+ (FD s 1) (castPtr str) 0 (fromIntegral len)+# else+ writeRawBufferPtr "Network.Socket.ByteString.send"+ (fromIntegral s) True str 0 (fromIntegral len)+# endif+#else+# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "send"+ (threadWaitWrite (fromIntegral s)) $+# endif+ c_send s str (fromIntegral len) 0+#endif++-- | Send data to the socket. The socket must be connected to a+-- remote socket. Unlike 'send', this function continues to send data+-- until either all data has been sent or an error occurs. On error,+-- an exception is raised, and there is no way to determine how much+-- data, if any, was successfully sent.+sendAll :: Socket -- ^ Connected socket+ -> ByteString -- ^ Data to send+ -> IO ()+sendAll sock bs = do+ sent <- send sock bs+ when (sent < B.length bs) $ sendAll sock (B.drop sent bs)++-- | Send data to the socket. The recipient can be specified+-- explicitly, so the socket need not be in a connected state.+-- Returns the number of bytes sent. Applications are responsible for+-- ensuring that all data has been sent.+sendTo :: Socket -- ^ Socket+ -> ByteString -- ^ Data to send+ -> SockAddr -- ^ Recipient address+ -> IO Int -- ^ Number of bytes sent+sendTo sock xs addr =+ unsafeUseAsCStringLen xs $ \(str, len) -> sendBufTo sock str len addr++-- | Send data to the socket. The recipient can be specified+-- explicitly, so the socket need not be in a connected state. Unlike+-- 'sendTo', this function continues to send data until either all+-- data has been sent or an error occurs. On error, an exception is+-- raised, and there is no way to determine how much data, if any, was+-- successfully sent.+sendAllTo :: Socket -- ^ Socket+ -> ByteString -- ^ Data to send+ -> SockAddr -- ^ Recipient address+ -> IO ()+sendAllTo sock xs addr = do+ sent <- sendTo sock xs addr+ when (sent < B.length xs) $ sendAllTo sock (B.drop sent xs) addr++-- ----------------------------------------------------------------------------+-- ** Vectored I/O++-- $vectored+--+-- Vectored I\/O, also known as scatter\/gather I\/O, allows multiple+-- data segments to be sent using a single system call, without first+-- concatenating the segments. For example, given a list of+-- @ByteString@s, @xs@,+--+-- > sendMany sock xs+--+-- is equivalent to+--+-- > sendAll sock (concat xs)+--+-- but potentially more efficient.+--+-- Vectored I\/O are often useful when implementing network protocols+-- that, for example, group data into segments consisting of one or+-- more fixed-length headers followed by a variable-length body.++-- | Send data to the socket. The socket must be in a connected+-- state. The data is sent as if the parts have been concatenated.+-- This function continues to send data until either all data has been+-- sent or an error occurs. On error, an exception is raised, and+-- there is no way to determine how much data, if any, was+-- successfully sent.+sendMany :: Socket -- ^ Connected socket+ -> [ByteString] -- ^ Data to send+ -> IO ()+#if !defined(mingw32_HOST_OS)+sendMany sock@(MkSocket fd _ _ _ _) cs = do+ sent <- sendManyInner+ when (sent < totalLength cs) $ sendMany sock (remainingChunks sent cs)+ where+ sendManyInner =+ liftM fromIntegral . withIOVec cs $ \(iovsPtr, iovsLen) ->+ throwSocketErrorIfMinus1RetryMayBlock "writev"+ (threadWaitWrite (fromIntegral fd)) $+ c_writev (fromIntegral fd) iovsPtr (fromIntegral iovsLen)+#else+sendMany sock = sendAll sock . B.concat+#endif++-- | Send data to the socket. The recipient can be specified+-- explicitly, so the socket need not be in a connected state. The+-- data is sent as if the parts have been concatenated. This function+-- continues to send data until either all data has been sent or an+-- error occurs. On error, an exception is raised, and there is no+-- way to determine how much data, if any, was successfully sent.+sendManyTo :: Socket -- ^ Socket+ -> [ByteString] -- ^ Data to send+ -> SockAddr -- ^ Recipient address+ -> IO ()+#if !defined(mingw32_HOST_OS)+sendManyTo sock@(MkSocket fd _ _ _ _) cs addr = do+ sent <- liftM fromIntegral sendManyToInner+ when (sent < totalLength cs) $ sendManyTo sock (remainingChunks sent cs) addr+ where+ sendManyToInner =+ withSockAddr addr $ \addrPtr addrSize ->+ withIOVec cs $ \(iovsPtr, iovsLen) -> do+ let msgHdr = MsgHdr+ addrPtr (fromIntegral addrSize)+ iovsPtr (fromIntegral iovsLen)+ with msgHdr $ \msgHdrPtr ->+ throwSocketErrorIfMinus1RetryMayBlock "sendmsg"+ (threadWaitWrite (fromIntegral fd)) $+ c_sendmsg (fromIntegral fd) msgHdrPtr 0+#else+sendManyTo sock cs = sendAllTo sock (B.concat cs)+#endif++-- ----------------------------------------------------------------------------+-- Receiving++-- | Receive data from the socket. The socket must be in a connected+-- state. This function may return fewer bytes than specified. If+-- the message is longer than the specified length, it may be+-- discarded depending on the type of socket. This function may block+-- until a message arrives.+--+-- Considering hardware and network realities, the maximum number of bytes to+-- receive should be a small power of 2, e.g., 4096.+--+-- For TCP sockets, a zero length return value means the peer has+-- closed its half side of the connection.+recv :: Socket -- ^ Connected socket+ -> Int -- ^ Maximum number of bytes to receive+ -> IO ByteString -- ^ Data received+recv (MkSocket s _ _ _ _) nbytes+ | nbytes < 0 = ioError (mkInvalidRecvArgError "Network.Socket.ByteString.recv")+ | otherwise = createAndTrim nbytes $ recvInner s nbytes++recvInner :: CInt -> Int -> Ptr Word8 -> IO Int+recvInner s nbytes ptr =+ fmap fromIntegral $+#if defined(__GLASGOW_HASKELL__) && defined(mingw32_HOST_OS)+# if __GLASGOW_HASKELL__ >= 611+ readRawBufferPtr "Network.Socket.ByteString.recv" (FD s 1) ptr 0 (fromIntegral nbytes)+# else+ readRawBufferPtr "Network.Socket.ByteString.recv" (fromIntegral s)+ True (castPtr ptr) 0 (fromIntegral nbytes)+# endif+#else+# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "recv"+ (threadWaitRead (fromIntegral s)) $+# endif+ c_recv s (castPtr ptr) (fromIntegral nbytes) 0+#endif++-- | Receive data from the socket. The socket need not be in a+-- connected state. Returns @(bytes, address)@ where @bytes@ is a+-- 'ByteString' representing the data received and @address@ is a+-- 'SockAddr' representing the address of the sending socket.+recvFrom :: Socket -- ^ Socket+ -> Int -- ^ Maximum number of bytes to receive+ -> IO (ByteString, SockAddr) -- ^ Data received and sender address+recvFrom sock nbytes =+ allocaBytes nbytes $ \ptr -> do+ (len, sockaddr) <- recvBufFrom sock ptr nbytes+ str <- B.packCStringLen (ptr, len)+ return (str, sockaddr)++-- ----------------------------------------------------------------------------+-- Not exported++#if !defined(mingw32_HOST_OS)+-- | Suppose we try to transmit a list of chunks @cs@ via a gathering write+-- operation and find that @n@ bytes were sent. Then @remainingChunks n cs@ is+-- list of chunks remaining to be sent.+remainingChunks :: Int -> [ByteString] -> [ByteString]+remainingChunks _ [] = []+remainingChunks i (x:xs)+ | i < len = B.drop i x : xs+ | otherwise = let i' = i - len in i' `seq` remainingChunks i' xs+ where+ len = B.length x++-- | @totalLength cs@ is the sum of the lengths of the chunks in the list @cs@.+totalLength :: [ByteString] -> Int+totalLength = sum . map B.length++-- | @withIOVec cs f@ executes the computation @f@, passing as argument a pair+-- consisting of a pointer to a temporarily allocated array of pointers to+-- 'IOVec' made from @cs@ and the number of pointers (@length cs@).+-- /Unix only/.+withIOVec :: [ByteString] -> ((Ptr IOVec, Int) -> IO a) -> IO a+withIOVec cs f =+ allocaArray csLen $ \aPtr -> do+ zipWithM_ pokeIov (ptrs aPtr) cs+ f (aPtr, csLen)+ where+ csLen = length cs+ ptrs = iterate (`plusPtr` sizeOf (undefined :: IOVec))+ pokeIov ptr s =+ unsafeUseAsCStringLen s $ \(sPtr, sLen) ->+ poke ptr $ IOVec sPtr (fromIntegral sLen)+#endif++-- ---------------------------------------------------------------------+-- Example++-- $example+--+-- Here are two minimal example programs using the TCP/IP protocol: a+-- server that echoes all data that it receives back (servicing only+-- one client) and a client using it.+--+-- > -- Echo server program+-- > module Main where+-- >+-- > import Control.Monad (unless)+-- > import Network.Socket hiding (recv)+-- > import qualified Data.ByteString as S+-- > import Network.Socket.ByteString (recv, sendAll)+-- >+-- > main :: IO ()+-- > main = withSocketsDo $+-- > do addrinfos <- getAddrInfo+-- > (Just (defaultHints {addrFlags = [AI_PASSIVE]}))+-- > Nothing (Just "3000")+-- > let serveraddr = head addrinfos+-- > sock <- socket (addrFamily serveraddr) Stream defaultProtocol+-- > bindSocket sock (addrAddress serveraddr)+-- > listen sock 1+-- > (conn, _) <- accept sock+-- > talk conn+-- > sClose conn+-- > sClose sock+-- >+-- > where+-- > talk :: Socket -> IO ()+-- > talk conn =+-- > do msg <- recv conn 1024+-- > unless (S.null msg) $ sendAll conn msg >> talk conn+--+-- > -- Echo client program+-- > module Main where+-- >+-- > import Network.Socket hiding (recv)+-- > import Network.Socket.ByteString (recv, sendAll)+-- > import qualified Data.ByteString.Char8 as C+-- >+-- > main :: IO ()+-- > main = withSocketsDo $+-- > do addrinfos <- getAddrInfo Nothing (Just "") (Just "3000")+-- > let serveraddr = head addrinfos+-- > sock <- socket (addrFamily serveraddr) Stream defaultProtocol+-- > connect sock (addrAddress serveraddr)+-- > sendAll sock $ C.pack "Hello, world!"+-- > msg <- recv sock 1024+-- > sClose sock+-- > putStr "Received "+-- > C.putStrLn msg
+ Network/Socket/ByteString/IOVec.hsc view
@@ -0,0 +1,28 @@+-- | Support module for the POSIX writev system call.+module Network.Socket.ByteString.IOVec+ ( IOVec(..)+ ) where++import Foreign.C.Types (CChar, CInt, CSize)+import Foreign.Ptr (Ptr)+import Foreign.Storable (Storable(..))++#include <sys/uio.h>++data IOVec = IOVec+ { iovBase :: Ptr CChar+ , iovLen :: CSize+ }++instance Storable IOVec where+ sizeOf _ = (#const sizeof(struct iovec))+ alignment _ = alignment (undefined :: CInt)++ peek p = do+ base <- (#peek struct iovec, iov_base) p+ len <- (#peek struct iovec, iov_len) p+ return $ IOVec base len++ poke p iov = do+ (#poke struct iovec, iov_base) p (iovBase iov)+ (#poke struct iovec, iov_len) p (iovLen iov)
+ Network/Socket/ByteString/Internal.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE CPP, ForeignFunctionInterface #-}++-- |+-- Module : Network.Socket.ByteString.Internal+-- Copyright : (c) Johan Tibell 2007-2010+-- License : BSD-style+--+-- Maintainer : johan.tibell@gmail.com+-- Stability : stable+-- Portability : portable+--+module Network.Socket.ByteString.Internal+ (+ mkInvalidRecvArgError+#if !defined(mingw32_HOST_OS)+ , c_writev+ , c_sendmsg+#endif+ ) where++import System.IO.Error (ioeSetErrorString, mkIOError)++#if !defined(mingw32_HOST_OS)+# if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types (CInt(..))+import System.Posix.Types (CSsize(..))+# else+import Foreign.C.Types (CInt)+import System.Posix.Types (CSsize)+# endif+import Foreign.Ptr (Ptr)++import Network.Socket.ByteString.IOVec (IOVec)+import Network.Socket.ByteString.MsgHdr (MsgHdr)+#endif++#ifdef __GLASGOW_HASKELL__+# if __GLASGOW_HASKELL__ < 611+import GHC.IOBase (IOErrorType(..))+# else+import GHC.IO.Exception (IOErrorType(..))+# endif+#elif __HUGS__+import Hugs.Prelude (IOErrorType(..))+#endif++mkInvalidRecvArgError :: String -> IOError+mkInvalidRecvArgError loc = ioeSetErrorString (mkIOError+#ifdef __GLASGOW_HASKELL__+ InvalidArgument+#else+ IllegalOperation+#endif+ loc Nothing Nothing) "non-positive length"++#if !defined(mingw32_HOST_OS)+foreign import ccall unsafe "writev"+ c_writev :: CInt -> Ptr IOVec -> CInt -> IO CSsize++foreign import ccall unsafe "sendmsg"+ c_sendmsg :: CInt -> Ptr MsgHdr -> CInt -> IO CSsize+#endif
+ Network/Socket/ByteString/Lazy.hsc view
@@ -0,0 +1,154 @@+{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface #-}++-- |+-- Module : Network.Socket.ByteString.Lazy+-- Copyright : (c) Bryan O'Sullivan 2009+-- License : BSD-style+--+-- Maintainer : bos@serpentine.com+-- Stability : experimental+-- Portability : POSIX, GHC+--+-- This module provides access to the BSD /socket/ interface. This+-- module is generally more efficient than the 'String' based network+-- functions in 'Network.Socket'. For detailed documentation, consult+-- your favorite POSIX socket reference. All functions communicate+-- failures by converting the error number to 'System.IO.IOError'.+--+-- This module is made to be imported with 'Network.Socket' like so:+--+-- > import Network.Socket hiding (send, sendTo, recv, recvFrom)+-- > import Network.Socket.ByteString.Lazy+-- > import Prelude hiding (getContents)+--+module Network.Socket.ByteString.Lazy+ (+#if !defined(mingw32_HOST_OS)+ -- * Send data to a socket+ send+ , sendAll+ ,+#endif++ -- * Receive data from a socket+ getContents+ , recv+ ) where++import Control.Monad (liftM)+import Data.ByteString.Lazy.Internal (ByteString(..), defaultChunkSize)+import Data.Int (Int64)+import Network.Socket (Socket(..), ShutdownCmd(..), shutdown)+import Prelude hiding (getContents)+import System.IO.Unsafe (unsafeInterleaveIO)++import qualified Data.ByteString as S+import qualified Network.Socket.ByteString as N++#if !defined(mingw32_HOST_OS)+import Control.Monad (unless)+import Data.ByteString.Unsafe (unsafeUseAsCStringLen)+import Foreign.Marshal.Array (allocaArray)+import Foreign.Ptr (plusPtr)+import Foreign.Storable (Storable(..))+import Network.Socket.ByteString.IOVec (IOVec(IOVec))+import Network.Socket.ByteString.Internal (c_writev)+import Network.Socket.Internal (throwSocketErrorIfMinus1RetryMayBlock)++import qualified Data.ByteString.Lazy as L++# if defined(__GLASGOW_HASKELL__)+import GHC.Conc (threadWaitWrite)+# endif+#endif++#if !defined(mingw32_HOST_OS)+-- -----------------------------------------------------------------------------+-- Sending++-- | Send data to the socket. The socket must be in a connected state.+-- Returns the number of bytes sent. Applications are responsible for+-- ensuring that all data has been sent.+--+-- Because a lazily generated 'ByteString' may be arbitrarily long,+-- this function caps the amount it will attempt to send at 4MB. This+-- number is large (so it should not penalize performance on fast+-- networks), but not outrageously so (to avoid demanding lazily+-- computed data unnecessarily early). Before being sent, the lazy+-- 'ByteString' will be converted to a list of strict 'ByteString's+-- with 'L.toChunks'; at most 1024 chunks will be sent. /Unix only/.+send :: Socket -- ^ Connected socket+ -> ByteString -- ^ Data to send+ -> IO Int64 -- ^ Number of bytes sent+send (MkSocket fd _ _ _ _) s = do+ let cs = take maxNumChunks (L.toChunks s)+ len = length cs+ liftM fromIntegral . allocaArray len $ \ptr ->+ withPokes cs ptr $ \niovs ->+# if !defined(__HUGS__)+ throwSocketErrorIfMinus1RetryMayBlock "writev"+ (threadWaitWrite (fromIntegral fd)) $+# endif+ c_writev (fromIntegral fd) ptr niovs+ where+ withPokes ss p f = loop ss p 0 0+ where loop (c:cs) q k !niovs+ | k < maxNumBytes =+ unsafeUseAsCStringLen c $ \(ptr,len) -> do+ poke q $ IOVec ptr (fromIntegral len)+ loop cs (q `plusPtr` sizeOf (undefined :: IOVec))+ (k + fromIntegral len) (niovs + 1)+ | otherwise = f niovs+ loop _ _ _ niovs = f niovs+ maxNumBytes = 4194304 :: Int -- maximum number of bytes to transmit in one system call+ maxNumChunks = 1024 :: Int -- maximum number of chunks to transmit in one system call++-- | Send data to the socket. The socket must be in a connected+-- state. This function continues to send data until either all data+-- has been sent or an error occurs. If there is an error, an+-- exception is raised, and there is no way to determine how much data+-- was sent. /Unix only/.+sendAll :: Socket -- ^ Connected socket+ -> ByteString -- ^ Data to send+ -> IO ()+sendAll sock bs = do+ sent <- send sock bs+ let bs' = L.drop sent bs+ unless (L.null bs') $ sendAll sock bs'+#endif++-- -----------------------------------------------------------------------------+-- Receiving++-- | Receive data from the socket. The socket must be in a connected+-- state. Data is received on demand, in chunks; each chunk will be+-- sized to reflect the amount of data received by individual 'recv'+-- calls.+--+-- All remaining data from the socket is consumed. When there is no+-- more data to be received, the receiving side of the socket is shut+-- down. If there is an error and an exception is thrown, the socket+-- is not shut down.+getContents :: Socket -- ^ Connected socket+ -> IO ByteString -- ^ Data received+getContents sock = loop where+ loop = unsafeInterleaveIO $ do+ s <- N.recv sock defaultChunkSize+ if S.null s+ then shutdown sock ShutdownReceive >> return Empty+ else Chunk s `liftM` loop++-- | Receive data from the socket. The socket must be in a connected+-- state. This function may return fewer bytes than specified. If+-- the received data is longer than the specified length, it may be+-- discarded depending on the type of socket. This function may block+-- until a message arrives.+--+-- If there is no more data to be received, returns an empty 'ByteString'.+recv :: Socket -- ^ Connected socket+ -> Int64 -- ^ Maximum number of bytes to receive+ -> IO ByteString -- ^ Data received+recv sock nbytes = chunk `liftM` N.recv sock (fromIntegral nbytes) where+ chunk k+ | S.null k = Empty+ | otherwise = Chunk k Empty
+ Network/Socket/ByteString/MsgHdr.hsc view
@@ -0,0 +1,47 @@+{-# LANGUAGE CPP #-}++-- | Support module for the POSIX 'sendmsg' system call.+module Network.Socket.ByteString.MsgHdr+ ( MsgHdr(..)+ ) where++#include <sys/types.h>+#include <sys/socket.h>++import Foreign.C.Types (CInt, CSize)+import Foreign.Ptr (Ptr)+import Foreign.Storable (Storable(..))+import Network.Socket (SockAddr)+import Network.Socket.Internal (zeroMemory)++import Network.Socket.ByteString.IOVec (IOVec)++-- We don't use msg_control, msg_controllen, and msg_flags as these+-- don't exist on OpenSolaris.+data MsgHdr = MsgHdr+ { msgName :: Ptr SockAddr+ , msgNameLen :: CSize+ , msgIov :: Ptr IOVec+ , msgIovLen :: CSize+ }++instance Storable MsgHdr where+ sizeOf _ = (#const sizeof(struct msghdr))+ alignment _ = alignment (undefined :: CInt)++ peek p = do+ name <- (#peek struct msghdr, msg_name) p+ nameLen <- (#peek struct msghdr, msg_namelen) p+ iov <- (#peek struct msghdr, msg_iov) p+ iovLen <- (#peek struct msghdr, msg_iovlen) p+ return $ MsgHdr name nameLen iov iovLen++ poke p mh = do+ -- We need to zero the msg_control, msg_controllen, and msg_flags+ -- fields, but they only exist on some platforms (e.g. not on+ -- Solaris). Instead of using CPP, we zero the entire struct.+ zeroMemory p (#const sizeof(struct msghdr))+ (#poke struct msghdr, msg_name) p (msgName mh)+ (#poke struct msghdr, msg_namelen) p (msgNameLen mh)+ (#poke struct msghdr, msg_iov) p (msgIov mh)+ (#poke struct msghdr, msg_iovlen) p (msgIovLen mh)
+ Network/Socket/Internal.hsc view
@@ -0,0 +1,651 @@+{-# LANGUAGE CPP, FlexibleInstances, ForeignFunctionInterface #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.Socket.Internal+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file libraries/network/LICENSE)+--+-- Maintainer : libraries@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- A module containing semi-public 'Network.Socket' internals.+-- Modules which extend the 'Network.Socket' module will need to use+-- this module while ideally most users will be able to make do with+-- the public interface.+--+-----------------------------------------------------------------------------++#include "HsNet.h"++module Network.Socket.Internal+ (+ -- * Socket addresses+ HostAddress+#if defined(IPV6_SOCKET_SUPPORT)+ , HostAddress6+ , FlowInfo+ , ScopeID+#endif+ , PortNumber(..)+ , SockAddr(..)++ , peekSockAddr+ , pokeSockAddr+ , sizeOfSockAddr+ , sizeOfSockAddrByFamily+ , withSockAddr+ , withNewSockAddr++ -- * Protocol families+ , Family(..)++ -- * Socket error functions+#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+ , c_getLastError+#endif+ , throwSocketError++ -- * Guards for socket operations that may fail+ , throwSocketErrorIfMinus1_+ , throwSocketErrorIfMinus1Retry+ , throwSocketErrorIfMinus1RetryMayBlock++ -- * Initialization+ , withSocketsDo++ -- * Low-level helpers+ , zeroMemory+ ) where++import Data.Bits ( (.|.), shiftL, shiftR )+import Data.Word ( Word8, Word16, Word32 )+import Data.Typeable (Typeable)+import Foreign.C.Error (throwErrno, throwErrnoIfMinus1Retry,+ throwErrnoIfMinus1RetryMayBlock, throwErrnoIfMinus1_)+import Foreign.C.String ( castCharToCChar, peekCString )+#if __GLASGOW_HASKELL__ >= 703+import Foreign.C.Types ( CInt(..), CSize(..) )+#else+import Foreign.C.Types ( CInt, CSize )+#endif+import Foreign.Marshal.Alloc ( allocaBytes )+import Foreign.Marshal.Array ( pokeArray, pokeArray0 )+import Foreign.Ptr ( Ptr, castPtr, plusPtr )+import Foreign.Storable ( Storable(..) )++#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+import Control.Exception ( finally )+# if __GLASGOW_HASKELL__+import GHC.IOBase ( IOErrorType(..) )+# endif+import Foreign.C.Types ( CChar )+import System.IO.Error ( ioeSetErrorString, mkIOError )+#endif++------------------------------------------------------------------------++-- | Network byte order.+type HostAddress = Word32++#if defined(IPV6_SOCKET_SUPPORT)+-- | Host byte order.+type HostAddress6 = (Word32, Word32, Word32, Word32)++-- The peek32 and poke32 functions work around the fact that the RFCs+-- don't require 32-bit-wide address fields to be present. We can+-- only portably rely on an 8-bit field, s6_addr.++s6_addr_offset :: Int+s6_addr_offset = (#offset struct in6_addr, s6_addr)++peek32 :: Ptr a -> Int -> IO Word32+peek32 p i0 = do+ let i' = i0 * 4+ peekByte n = peekByteOff p (s6_addr_offset + i' + n) :: IO Word8+ a `sl` i = fromIntegral a `shiftL` i+ a0 <- peekByte 0+ a1 <- peekByte 1+ a2 <- peekByte 2+ a3 <- peekByte 3+ return ((a0 `sl` 24) .|. (a1 `sl` 16) .|. (a2 `sl` 8) .|. (a3 `sl` 0))++poke32 :: Ptr a -> Int -> Word32 -> IO ()+poke32 p i0 a = do+ let i' = i0 * 4+ pokeByte n = pokeByteOff p (s6_addr_offset + i' + n)+ x `sr` i = fromIntegral (x `shiftR` i) :: Word8+ pokeByte 0 (a `sr` 24)+ pokeByte 1 (a `sr` 16)+ pokeByte 2 (a `sr` 8)+ pokeByte 3 (a `sr` 0)++instance Storable HostAddress6 where+ sizeOf _ = (#const sizeof(struct in6_addr))+ alignment _ = alignment (undefined :: CInt)++ peek p = do+ a <- peek32 p 0+ b <- peek32 p 1+ c <- peek32 p 2+ d <- peek32 p 3+ return (a, b, c, d)++ poke p (a, b, c, d) = do+ poke32 p 0 a+ poke32 p 1 b+ poke32 p 2 c+ poke32 p 3 d+#endif++------------------------------------------------------------------------+-- Port Numbers+--+-- newtyped to prevent accidental use of sane-looking+-- port numbers that haven't actually been converted to+-- network-byte-order first.+--++newtype PortNumber = PortNum Word16 deriving (Eq, Ord, Typeable)++------------------------------------------------------------------------+-- Socket addresses++-- The scheme used for addressing sockets is somewhat quirky. The+-- calls in the BSD socket API that need to know the socket address+-- all operate in terms of struct sockaddr, a `virtual' type of+-- socket address.++-- The Internet family of sockets are addressed as struct sockaddr_in,+-- so when calling functions that operate on struct sockaddr, we have+-- to type cast the Internet socket address into a struct sockaddr.+-- Instances of the structure for different families might *not* be+-- the same size. Same casting is required of other families of+-- sockets such as Xerox NS. Similarly for Unix domain sockets.++-- To represent these socket addresses in Haskell-land, we do what BSD+-- didn't do, and use a union/algebraic type for the different+-- families. Currently only Unix domain sockets and the Internet+-- families are supported.++#if defined(IPV6_SOCKET_SUPPORT)+type FlowInfo = Word32+type ScopeID = Word32+#endif++data SockAddr -- C Names+ = SockAddrInet+ PortNumber -- sin_port (network byte order)+ HostAddress -- sin_addr (ditto)+#if defined(IPV6_SOCKET_SUPPORT)+ | SockAddrInet6+ PortNumber -- sin6_port (network byte order)+ FlowInfo -- sin6_flowinfo (ditto)+ HostAddress6 -- sin6_addr (ditto)+ ScopeID -- sin6_scope_id (ditto)+#endif+#if defined(DOMAIN_SOCKET_SUPPORT)+ | SockAddrUnix+ String -- sun_path+#endif+ deriving (Eq, Typeable)++#if defined(WITH_WINSOCK) || defined(cygwin32_HOST_OS)+type CSaFamily = (#type unsigned short)+#elif defined(darwin_HOST_OS)+type CSaFamily = (#type u_char)+#else+type CSaFamily = (#type sa_family_t)+#endif++-- | Computes the storage requirements (in bytes) of the given+-- 'SockAddr'. This function differs from 'Foreign.Storable.sizeOf'+-- in that the value of the argument /is/ used.+sizeOfSockAddr :: SockAddr -> Int+#if defined(DOMAIN_SOCKET_SUPPORT)+sizeOfSockAddr (SockAddrUnix path) =+ case path of+ '\0':_ -> (#const sizeof(sa_family_t)) + length path+ _ -> #const sizeof(struct sockaddr_un)+#endif+sizeOfSockAddr (SockAddrInet _ _) = #const sizeof(struct sockaddr_in)+#if defined(IPV6_SOCKET_SUPPORT)+sizeOfSockAddr (SockAddrInet6 _ _ _ _) = #const sizeof(struct sockaddr_in6)+#endif++-- | Computes the storage requirements (in bytes) required for a+-- 'SockAddr' with the given 'Family'.+sizeOfSockAddrByFamily :: Family -> Int+#if defined(DOMAIN_SOCKET_SUPPORT)+sizeOfSockAddrByFamily AF_UNIX = #const sizeof(struct sockaddr_un)+#endif+#if defined(IPV6_SOCKET_SUPPORT)+sizeOfSockAddrByFamily AF_INET6 = #const sizeof(struct sockaddr_in6)+#endif+sizeOfSockAddrByFamily AF_INET = #const sizeof(struct sockaddr_in)++-- | Use a 'SockAddr' with a function requiring a pointer to a+-- 'SockAddr' and the length of that 'SockAddr'.+withSockAddr :: SockAddr -> (Ptr SockAddr -> Int -> IO a) -> IO a+withSockAddr addr f = do+ let sz = sizeOfSockAddr addr+ allocaBytes sz $ \p -> pokeSockAddr p addr >> f (castPtr p) sz++-- | Create a new 'SockAddr' for use with a function requiring a+-- pointer to a 'SockAddr' and the length of that 'SockAddr'.+withNewSockAddr :: Family -> (Ptr SockAddr -> Int -> IO a) -> IO a+withNewSockAddr family f = do+ let sz = sizeOfSockAddrByFamily family+ allocaBytes sz $ \ptr -> f ptr sz++-- We can't write an instance of 'Storable' for 'SockAddr' because+-- @sockaddr@ is a sum type of variable size but+-- 'Foreign.Storable.sizeOf' is required to be constant.++-- Note that on Darwin, the sockaddr structure must be zeroed before+-- use.++-- | Write the given 'SockAddr' to the given memory location.+pokeSockAddr :: Ptr a -> SockAddr -> IO ()+#if defined(DOMAIN_SOCKET_SUPPORT)+pokeSockAddr p (SockAddrUnix path) = do+#if defined(darwin_TARGET_OS)+ zeroMemory p (#const sizeof(struct sockaddr_un))+#endif+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)+ (#poke struct sockaddr_un, sun_len) p ((#const sizeof(struct sockaddr_un)) :: Word8)+#endif+ (#poke struct sockaddr_un, sun_family) p ((#const AF_UNIX) :: CSaFamily)+ let pathC = map castCharToCChar path+ poker = case path of ('\0':_) -> pokeArray; _ -> pokeArray0 0+ poker ((#ptr struct sockaddr_un, sun_path) p) pathC+#endif+pokeSockAddr p (SockAddrInet (PortNum port) addr) = do+#if defined(darwin_TARGET_OS)+ zeroMemory p (#const sizeof(struct sockaddr_in))+#endif+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)+ (#poke struct sockaddr_in, sin_len) p ((#const sizeof(struct sockaddr_in)) :: Word8)+#endif+ (#poke struct sockaddr_in, sin_family) p ((#const AF_INET) :: CSaFamily)+ (#poke struct sockaddr_in, sin_port) p port+ (#poke struct sockaddr_in, sin_addr) p addr+#if defined(IPV6_SOCKET_SUPPORT)+pokeSockAddr p (SockAddrInet6 (PortNum port) flow addr scope) = do+#if defined(darwin_TARGET_OS)+ zeroMemory p (#const sizeof(struct sockaddr_in6))+#endif+#if defined(HAVE_STRUCT_SOCKADDR_SA_LEN)+ (#poke struct sockaddr_in6, sin6_len) p ((#const sizeof(struct sockaddr_in6)) :: Word8)+#endif+ (#poke struct sockaddr_in6, sin6_family) p ((#const AF_INET6) :: CSaFamily)+ (#poke struct sockaddr_in6, sin6_port) p port+ (#poke struct sockaddr_in6, sin6_flowinfo) p flow+ (#poke struct sockaddr_in6, sin6_addr) p addr+ (#poke struct sockaddr_in6, sin6_scope_id) p scope+#endif++-- | Read a 'SockAddr' from the given memory location.+peekSockAddr :: Ptr SockAddr -> IO SockAddr+peekSockAddr p = do+ family <- (#peek struct sockaddr, sa_family) p+ case family :: CSaFamily of+#if defined(DOMAIN_SOCKET_SUPPORT)+ (#const AF_UNIX) -> do+ str <- peekCString ((#ptr struct sockaddr_un, sun_path) p)+ return (SockAddrUnix str)+#endif+ (#const AF_INET) -> do+ addr <- (#peek struct sockaddr_in, sin_addr) p+ port <- (#peek struct sockaddr_in, sin_port) p+ return (SockAddrInet (PortNum port) addr)+#if defined(IPV6_SOCKET_SUPPORT)+ (#const AF_INET6) -> do+ port <- (#peek struct sockaddr_in6, sin6_port) p+ flow <- (#peek struct sockaddr_in6, sin6_flowinfo) p+ addr <- (#peek struct sockaddr_in6, sin6_addr) p+ scope <- (#peek struct sockaddr_in6, sin6_scope_id) p+ return (SockAddrInet6 (PortNum port) flow addr scope)+#endif++------------------------------------------------------------------------+-- Protocol Families.++-- | This data type might have different constructors depending on+-- what is supported by the operating system.+data Family+ = AF_UNSPEC -- unspecified+#ifdef AF_UNIX+ | AF_UNIX -- local to host (pipes, portals+#endif+#ifdef AF_INET+ | AF_INET -- internetwork: UDP, TCP, etc+#endif+#ifdef AF_INET6+ | AF_INET6 -- Internet Protocol version 6+#endif+#ifdef AF_IMPLINK+ | AF_IMPLINK -- arpanet imp addresses+#endif+#ifdef AF_PUP+ | AF_PUP -- pup protocols: e.g. BSP+#endif+#ifdef AF_CHAOS+ | AF_CHAOS -- mit CHAOS protocols+#endif+#ifdef AF_NS+ | AF_NS -- XEROX NS protocols+#endif+#ifdef AF_NBS+ | AF_NBS -- nbs protocols+#endif+#ifdef AF_ECMA+ | AF_ECMA -- european computer manufacturers+#endif+#ifdef AF_DATAKIT+ | AF_DATAKIT -- datakit protocols+#endif+#ifdef AF_CCITT+ | AF_CCITT -- CCITT protocols, X.25 etc+#endif+#ifdef AF_SNA+ | AF_SNA -- IBM SNA+#endif+#ifdef AF_DECnet+ | AF_DECnet -- DECnet+#endif+#ifdef AF_DLI+ | AF_DLI -- Direct data link interface+#endif+#ifdef AF_LAT+ | AF_LAT -- LAT+#endif+#ifdef AF_HYLINK+ | AF_HYLINK -- NSC Hyperchannel+#endif+#ifdef AF_APPLETALK+ | AF_APPLETALK -- Apple Talk+#endif+#ifdef AF_ROUTE+ | AF_ROUTE -- Internal Routing Protocol+#endif+#ifdef AF_NETBIOS+ | AF_NETBIOS -- NetBios-style addresses+#endif+#ifdef AF_NIT+ | AF_NIT -- Network Interface Tap+#endif+#ifdef AF_802+ | AF_802 -- IEEE 802.2, also ISO 8802+#endif+#ifdef AF_ISO+ | AF_ISO -- ISO protocols+#endif+#ifdef AF_OSI+ | AF_OSI -- umbrella of all families used by OSI+#endif+#ifdef AF_NETMAN+ | AF_NETMAN -- DNA Network Management+#endif+#ifdef AF_X25+ | AF_X25 -- CCITT X.25+#endif+#ifdef AF_AX25+ | AF_AX25+#endif+#ifdef AF_OSINET+ | AF_OSINET -- AFI+#endif+#ifdef AF_GOSSIP+ | AF_GOSSIP -- US Government OSI+#endif+#ifdef AF_IPX+ | AF_IPX -- Novell Internet Protocol+#endif+#ifdef Pseudo_AF_XTP+ | Pseudo_AF_XTP -- eXpress Transfer Protocol (no AF)+#endif+#ifdef AF_CTF+ | AF_CTF -- Common Trace Facility+#endif+#ifdef AF_WAN+ | AF_WAN -- Wide Area Network protocols+#endif+#ifdef AF_SDL+ | AF_SDL -- SGI Data Link for DLPI+#endif+#ifdef AF_NETWARE+ | AF_NETWARE+#endif+#ifdef AF_NDD+ | AF_NDD+#endif+#ifdef AF_INTF+ | AF_INTF -- Debugging use only+#endif+#ifdef AF_COIP+ | AF_COIP -- connection-oriented IP, aka ST II+#endif+#ifdef AF_CNT+ | AF_CNT -- Computer Network Technology+#endif+#ifdef Pseudo_AF_RTIP+ | Pseudo_AF_RTIP -- Help Identify RTIP packets+#endif+#ifdef Pseudo_AF_PIP+ | Pseudo_AF_PIP -- Help Identify PIP packets+#endif+#ifdef AF_SIP+ | AF_SIP -- Simple Internet Protocol+#endif+#ifdef AF_ISDN+ | AF_ISDN -- Integrated Services Digital Network+#endif+#ifdef Pseudo_AF_KEY+ | Pseudo_AF_KEY -- Internal key-management function+#endif+#ifdef AF_NATM+ | AF_NATM -- native ATM access+#endif+#ifdef AF_ARP+ | AF_ARP -- (rev.) addr. res. prot. (RFC 826)+#endif+#ifdef Pseudo_AF_HDRCMPLT+ | Pseudo_AF_HDRCMPLT -- Used by BPF to not rewrite hdrs in iface output+#endif+#ifdef AF_ENCAP+ | AF_ENCAP+#endif+#ifdef AF_LINK+ | AF_LINK -- Link layer interface+#endif+#ifdef AF_RAW+ | AF_RAW -- Link layer interface+#endif+#ifdef AF_RIF+ | AF_RIF -- raw interface+#endif+#ifdef AF_NETROM+ | AF_NETROM -- Amateur radio NetROM+#endif+#ifdef AF_BRIDGE+ | AF_BRIDGE -- multiprotocol bridge+#endif+#ifdef AF_ATMPVC+ | AF_ATMPVC -- ATM PVCs+#endif+#ifdef AF_ROSE+ | AF_ROSE -- Amateur Radio X.25 PLP+#endif+#ifdef AF_NETBEUI+ | AF_NETBEUI -- 802.2LLC+#endif+#ifdef AF_SECURITY+ | AF_SECURITY -- Security callback pseudo AF+#endif+#ifdef AF_PACKET+ | AF_PACKET -- Packet family+#endif+#ifdef AF_ASH+ | AF_ASH -- Ash+#endif+#ifdef AF_ECONET+ | AF_ECONET -- Acorn Econet+#endif+#ifdef AF_ATMSVC+ | AF_ATMSVC -- ATM SVCs+#endif+#ifdef AF_IRDA+ | AF_IRDA -- IRDA sockets+#endif+#ifdef AF_PPPOX+ | AF_PPPOX -- PPPoX sockets+#endif+#ifdef AF_WANPIPE+ | AF_WANPIPE -- Wanpipe API sockets+#endif+#ifdef AF_BLUETOOTH+ | AF_BLUETOOTH -- bluetooth sockets+#endif+ deriving (Eq, Ord, Read, Show)++-- ---------------------------------------------------------------------+-- Guards for socket operations that may fail++-- | Throw an 'IOError' corresponding to the current socket error.+throwSocketError :: String -- ^ textual description of the error location+ -> IO a++-- | Throw an 'IOError' corresponding to the current socket error if+-- the IO action returns a result of @-1@. Discards the result of the+-- IO action after error handling.+throwSocketErrorIfMinus1_+ :: (Eq a, Num a)+ => String -- ^ textual description of the location+ -> IO a -- ^ the 'IO' operation to be executed+ -> IO ()++{-# SPECIALIZE throwSocketErrorIfMinus1_ :: String -> IO CInt -> IO () #-}++-- | Throw an 'IOError' corresponding to the current socket error if+-- the IO action returns a result of @-1@, but retries in case of an+-- interrupted operation.+throwSocketErrorIfMinus1Retry+ :: (Eq a, Num a)+ => String -- ^ textual description of the location+ -> IO a -- ^ the 'IO' operation to be executed+ -> IO a++{-# SPECIALIZE throwSocketErrorIfMinus1Retry :: String -> IO CInt -> IO CInt #-}++-- | Throw an 'IOError' corresponding to the current socket error if+-- the IO action returns a result of @-1@, but retries in case of an+-- interrupted operation. Checks for operations that would block and+-- executes an alternative action before retrying in that case.+throwSocketErrorIfMinus1RetryMayBlock+ :: (Eq a, Num a)+ => String -- ^ textual description of the location+ -> IO b -- ^ action to execute before retrying if an+ -- immediate retry would block+ -> IO a -- ^ the 'IO' operation to be executed+ -> IO a++{-# SPECIALIZE throwSocketErrorIfMinus1RetryMayBlock+ :: String -> IO b -> IO CInt -> IO CInt #-}++#if defined(__GLASGOW_HASKELL__) && (!defined(HAVE_WINSOCK2_H) || defined(cygwin32_HOST_OS))++throwSocketErrorIfMinus1RetryMayBlock name on_block act =+ throwErrnoIfMinus1RetryMayBlock name act on_block++throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry++throwSocketErrorIfMinus1_ = throwErrnoIfMinus1_++throwSocketError = throwErrno++#else++throwSocketErrorIfMinus1RetryMayBlock name _ act+ = throwSocketErrorIfMinus1Retry name act++throwSocketErrorIfMinus1_ name act = do+ throwSocketErrorIfMinus1Retry name act+ return ()++# if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+throwSocketErrorIfMinus1Retry name act = do+ r <- act+ if (r == -1)+ then do+ rc <- c_getLastError+ case rc of+ 10093 -> do -- WSANOTINITIALISED+ withSocketsDo (return ())+ r <- act+ if (r == -1)+ then throwSocketError name+ else return r+ _ -> throwSocketError name+ else return r++throwSocketError name = do+ rc <- c_getLastError+ pstr <- c_getWSError rc+ str <- peekCString pstr+# if __GLASGOW_HASKELL__+ ioError (ioeSetErrorString (mkIOError OtherError name Nothing Nothing) str)+# else+ ioError (userError (name ++ ": socket error - " ++ str))+# endif++foreign import CALLCONV unsafe "WSAGetLastError"+ c_getLastError :: IO CInt++foreign import ccall unsafe "getWSErrorDescr"+ c_getWSError :: CInt -> IO (Ptr CChar)+++# else+throwSocketErrorIfMinus1Retry = throwErrnoIfMinus1Retry+throwSocketError = throwErrno+# endif+#endif /* __GLASGOW_HASKELL */++-- ---------------------------------------------------------------------------+-- WinSock support++{-| On Windows operating systems, the networking subsystem has to be+initialised using 'withSocketsDo' before any networking operations can+be used. eg.++> main = withSocketsDo $ do {...}++Although this is only strictly necessary on Windows platforms, it is+harmless on other platforms, so for portability it is good practice to+use it all the time.+-}+withSocketsDo :: IO a -> IO a+#if !defined(WITH_WINSOCK)+withSocketsDo x = x+#else+withSocketsDo act = do+ x <- initWinSock+ if x /= 0+ then ioError (userError "Failed to initialise WinSock")+ else act `finally` shutdownWinSock++foreign import ccall unsafe "initWinSock" initWinSock :: IO Int+foreign import ccall unsafe "shutdownWinSock" shutdownWinSock :: IO ()++#endif++------------------------------------------------------------------------+-- Helper functions++foreign import ccall unsafe "string.h" memset :: Ptr a -> CInt -> CSize -> IO ()++-- | Zero a structure.+zeroMemory :: Ptr a -> CSize -> IO ()+zeroMemory dest nbytes = memset dest 0 (fromIntegral nbytes)
+ Network/Stream.hs view
@@ -0,0 +1,91 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.Stream+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- An library for creating abstract streams. Originally part of Gray's\/Bringert's+-- HTTP module.+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+-- - Removed unnecessary import statements.+-- - Moved Debug code to StreamDebugger.hs+-- - Moved Socket-related code to StreamSocket.hs.+--+-- * Changes by Simon Foster:+-- - Split Network.HTTPmodule up into to separate+-- Network.[Stream,TCP,HTTP] modules+-----------------------------------------------------------------------------+module Network.Stream+ ( Stream(..)+ , ConnError(..)+ , Result+ , bindE+ , fmapE++ , failParse -- :: String -> Result a+ , failWith -- :: ConnError -> Result a+ , failMisc -- :: String -> Result a+ ) where++import Control.Monad.Error++data ConnError + = ErrorReset + | ErrorClosed+ | ErrorParse String+ | ErrorMisc String+ deriving(Show,Eq)++instance Error ConnError where+ noMsg = strMsg "unknown error"+ strMsg x = ErrorMisc x++-- in GHC 7.0 the Monad instance for Error no longer+-- uses fail x = Left (strMsg x). failMisc is therefore+-- used instead.+failMisc :: String -> Result a+failMisc x = failWith (strMsg x)++failParse :: String -> Result a+failParse x = failWith (ErrorParse x)++failWith :: ConnError -> Result a+failWith x = Left x++bindE :: Result a -> (a -> Result b) -> Result b+bindE (Left e) _ = Left e+bindE (Right v) f = f v++fmapE :: (a -> Result b) -> IO (Result a) -> IO (Result b)+fmapE f a = do+ x <- a+ case x of+ Left e -> return (Left e)+ Right r -> return (f r) + +-- | This is the type returned by many exported network functions.+type Result a = Either ConnError {- error -}+ a {- result -}++-- | Streams should make layering of TLS protocol easier in future,+-- they allow reading/writing to files etc for debugging,+-- they allow use of protocols other than TCP/IP+-- and they allow customisation.+--+-- Instances of this class should not trim+-- the input in any way, e.g. leave LF on line+-- endings etc. Unless that is exactly the behaviour+-- you want from your twisted instances ;)+class Stream x where + readLine :: x -> IO (Result String)+ readBlock :: x -> Int -> IO (Result String)+ writeBlock :: x -> String -> IO (Result ())+ close :: x -> IO ()+ closeOnEnd :: x -> Bool -> IO ()+ -- ^ True => shutdown the connection when response has been read / end-of-stream+ -- has been reached.
+ Network/StreamDebugger.hs view
@@ -0,0 +1,103 @@+-----------------------------------------------------------------------------+-- |+-- Module : Network.StreamDebugger+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Implements debugging of @Stream@s. Originally part of Gray's\/Bringert's+-- HTTP module.+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+-- - Created. Made minor formatting changes.+-- +-----------------------------------------------------------------------------+module Network.StreamDebugger+ ( StreamDebugger+ , debugStream+ , debugByteStream+ ) where++import Network.Stream (Stream(..))+import System.IO+ ( Handle, hFlush, hPutStrLn, IOMode(AppendMode), hClose, openFile,+ hSetBuffering, BufferMode(NoBuffering)+ )+import Network.TCP ( HandleStream, HStream, + StreamHooks(..), setStreamHooks, getStreamHooks )++-- | Allows stream logging. Refer to 'debugStream' below.+data StreamDebugger x+ = Dbg Handle x++instance (Stream x) => Stream (StreamDebugger x) where+ readBlock (Dbg h x) n =+ do val <- readBlock x n+ hPutStrLn h ("--readBlock " ++ show n)+ hPutStrLn h (show val)+ return val+ readLine (Dbg h x) =+ do val <- readLine x+ hPutStrLn h ("--readLine")+ hPutStrLn h (show val)+ return val+ writeBlock (Dbg h x) str =+ do val <- writeBlock x str+ hPutStrLn h ("--writeBlock" ++ show str)+ hPutStrLn h (show val)+ return val+ close (Dbg h x) =+ do hPutStrLn h "--closing..."+ hFlush h+ close x+ hPutStrLn h "--closed."+ hClose h+ closeOnEnd (Dbg h x) f =+ do hPutStrLn h ("--close-on-end.." ++ show f)+ hFlush h + closeOnEnd x f++-- | Wraps a stream with logging I\/O.+-- The first argument is a filename which is opened in @AppendMode@.+debugStream :: (Stream a) => FilePath -> a -> IO (StreamDebugger a)+debugStream file stream = + do h <- openFile file AppendMode+ hPutStrLn h ("File \"" ++ file ++ "\" opened for appending.")+ return (Dbg h stream)++debugByteStream :: HStream ty => FilePath -> HandleStream ty -> IO (HandleStream ty)+debugByteStream file stream = do+ sh <- getStreamHooks stream + case sh of+ Just h + | hook_name h == file -> return stream -- reuse the stream hooks.+ _ -> do+ h <- openFile file AppendMode+ hSetBuffering h NoBuffering+ hPutStrLn h ("File \"" ++ file ++ "\" opened for appending.")+ setStreamHooks stream (debugStreamHooks h file)+ return stream++debugStreamHooks :: HStream ty => Handle -> String -> StreamHooks ty+debugStreamHooks h nm = + StreamHooks+ { hook_readBlock = \ toStr n val -> do+ let eval = case val of { Left e -> Left e ; Right v -> Right $ toStr v}+ hPutStrLn h ("--readBlock " ++ show n)+ hPutStrLn h (either show show eval)+ , hook_readLine = \ toStr val -> do+ let eval = case val of { Left e -> Left e ; Right v -> Right $ toStr v}+ hPutStrLn h ("--readLine")+ hPutStrLn h (either show show eval)+ , hook_writeBlock = \ toStr str val -> do+ hPutStrLn h ("--writeBlock " ++ show val)+ hPutStrLn h (toStr str)+ , hook_close = do+ hPutStrLn h "--closing..."+ hFlush h+ hClose h+ , hook_name = nm+ }
+ Network/StreamSocket.hs view
@@ -0,0 +1,93 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.StreamSocket+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Socket Stream instance. Originally part of Gray's\/Bringert's HTTP module.+--+-- * Changes by Robin Bate Boerop <robin@bateboerop.name>:+-- - Made dependencies explicit in import statements.+-- - Removed false dependencies in import statements.+-- - Created separate module for instance Stream Socket.+--+-- * Changes by Simon Foster:+-- - Split module up into to sepearate Network.[Stream,TCP,HTTP] modules+-- +-----------------------------------------------------------------------------+module Network.StreamSocket+ ( handleSocketError+ , myrecv+ ) where++import Network.Stream+ ( Stream(..), ConnError(ErrorReset, ErrorMisc), Result+ )+import Network.Socket+ ( Socket, getSocketOption, shutdown, send, recv, sClose+ , ShutdownCmd(ShutdownBoth), SocketOption(SoError)+ )++import Network.HTTP.Base ( catchIO )+import Control.Monad (liftM)+import Control.Exception as Exception (IOException)+import System.IO.Error (catch, isEOFError)++-- | Exception handler for socket operations.+handleSocketError :: Socket -> IOException -> IO (Result a)+handleSocketError sk e =+ do se <- getSocketOption sk SoError+ case se of+ 0 -> ioError e+ 10054 -> return $ Left ErrorReset -- reset+ _ -> return $ Left $ ErrorMisc $ show se++myrecv :: Socket -> Int -> IO String+myrecv sock len =+ let handler e = if isEOFError e then return [] else ioError e+ in System.IO.Error.catch (recv sock len) handler++instance Stream Socket where+ readBlock sk n = readBlockSocket sk n+ readLine sk = readLineSocket sk+ writeBlock sk str = writeBlockSocket sk str+ close sk = do+ -- This slams closed the connection (which is considered rude for TCP\/IP)+ shutdown sk ShutdownBoth+ sClose sk+ closeOnEnd _sk _ = return () -- can't really deal with this, so do run the risk of leaking sockets here.++readBlockSocket :: Socket -> Int -> IO (Result String)+readBlockSocket sk n = (liftM Right $ fn n) `catchIO` (handleSocketError sk)+ where+ fn x = do { str <- myrecv sk x+ ; let len = length str+ ; if len < x+ then ( fn (x-len) >>= \more -> return (str++more) )+ else return str+ }++-- Use of the following function is discouraged.+-- The function reads in one character at a time, +-- which causes many calls to the kernel recv()+-- hence causes many context switches.+readLineSocket :: Socket -> IO (Result String)+readLineSocket sk = (liftM Right $ fn "") `catchIO` (handleSocketError sk)+ where+ fn str = do+ c <- myrecv sk 1 -- like eating through a straw.+ if null c || c == "\n"+ then return (reverse str++c)+ else fn (head c:str)+ +writeBlockSocket :: Socket -> String -> IO (Result ())+writeBlockSocket sk str = (liftM Right $ fn str) `catchIO` (handleSocketError sk)+ where+ fn [] = return ()+ fn x = send sk x >>= \i -> fn (drop i x)+
+ Network/TCP.hs view
@@ -0,0 +1,411 @@+{-# LANGUAGE TypeSynonymInstances #-}+-----------------------------------------------------------------------------+-- |+-- Module : Network.TCP+-- Copyright : See LICENSE file+-- License : BSD+--+-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>+-- Stability : experimental+-- Portability : non-portable (not tested)+--+-- Some utility functions for working with the Haskell @network@ package. Mostly+-- for internal use by the @Network.HTTP@ code, but +-- +-----------------------------------------------------------------------------+module Network.TCP+ ( Connection+ , EndPoint(..)+ , openTCPPort+ , isConnectedTo++ , openTCPConnection+ , socketConnection+ , isTCPConnectedTo+ + , HandleStream+ , HStream(..)+ + , StreamHooks(..)+ , nullHooks+ , setStreamHooks+ , getStreamHooks+ , hstreamToConnection++ ) where++import Network.BSD (getHostByName, hostAddresses)+import Network.Socket+ ( Socket, SockAddr(SockAddrInet), SocketOption(KeepAlive)+ , SocketType(Stream), inet_addr, connect+ , shutdown, ShutdownCmd(..)+ , sClose, setSocketOption, getPeerName+ , socket, Family(AF_INET)+ )+import qualified Network.Stream as Stream+ ( Stream(readBlock, readLine, writeBlock, close, closeOnEnd) )+import Network.Stream+ ( ConnError(..)+ , Result+ , failWith+ , failMisc+ )+import Network.BufferType++import Network.HTTP.Base ( catchIO )+import Network.Socket ( socketToHandle )++import Data.Char ( toLower )+import Data.Word ( Word8 )+import Control.Concurrent+import Control.Monad ( liftM, when )+import System.IO ( Handle, hFlush, IOMode(..), hClose )+import System.IO.Error ( isEOFError )++import qualified Data.ByteString as Strict+import qualified Data.ByteString.Lazy as Lazy++-----------------------------------------------------------------+------------------ TCP Connections ------------------------------+-----------------------------------------------------------------++-- | The 'Connection' newtype is a wrapper that allows us to make+-- connections an instance of the Stream class, without GHC extensions.+-- While this looks sort of like a generic reference to the transport+-- layer it is actually TCP specific, which can be seen in the+-- implementation of the 'Stream Connection' instance.+newtype Connection = Connection (HandleStream String)++newtype HandleStream a = HandleStream {getRef :: MVar (Conn a)}++data EndPoint = EndPoint { epHost :: String, epPort :: Int }++instance Eq EndPoint where+ EndPoint host1 port1 == EndPoint host2 port2 =+ map toLower host1 == map toLower host2 && port1 == port2++data Conn a + = MkConn { connSock :: ! Socket+ , connHandle :: Handle+ , connBuffer :: BufferOp a+ , connInput :: Maybe a+ , connEndPoint :: EndPoint+ , connHooks :: Maybe (StreamHooks a)+ , connCloseEOF :: Bool -- True => close socket upon reaching end-of-stream.+ }+ | ConnClosed+ deriving(Eq)++hstreamToConnection :: HandleStream String -> Connection+hstreamToConnection h = Connection h++connHooks' :: Conn a -> Maybe (StreamHooks a)+connHooks' ConnClosed{} = Nothing+connHooks' x = connHooks x++-- all of these are post-op hooks+data StreamHooks ty+ = StreamHooks+ { hook_readLine :: (ty -> String) -> Result ty -> IO ()+ , hook_readBlock :: (ty -> String) -> Int -> Result ty -> IO ()+ , hook_writeBlock :: (ty -> String) -> ty -> Result () -> IO ()+ , hook_close :: IO ()+ , hook_name :: String -- hack alert: name of the hook itself.+ }++instance Eq ty => Eq (StreamHooks ty) where+ (==) _ _ = True++nullHooks :: StreamHooks ty+nullHooks = StreamHooks + { hook_readLine = \ _ _ -> return ()+ , hook_readBlock = \ _ _ _ -> return ()+ , hook_writeBlock = \ _ _ _ -> return ()+ , hook_close = return ()+ , hook_name = ""+ }++setStreamHooks :: HandleStream ty -> StreamHooks ty -> IO ()+setStreamHooks h sh = modifyMVar_ (getRef h) (\ c -> return c{connHooks=Just sh})++getStreamHooks :: HandleStream ty -> IO (Maybe (StreamHooks ty))+getStreamHooks h = readMVar (getRef h) >>= return.connHooks++-- | @HStream@ overloads the use of 'HandleStream's, letting you+-- overload the handle operations over the type that is communicated+-- across the handle. It comes in handy for @Network.HTTP@ 'Request'+-- and 'Response's as the payload representation isn't fixed, but overloaded.+--+-- The library comes with instances for @ByteString@s and @String@, but+-- should you want to plug in your own payload representation, defining+-- your own @HStream@ instance _should_ be all that it takes.+-- +class BufferType bufType => HStream bufType where+ openStream :: String -> Int -> IO (HandleStream bufType)+ openSocketStream :: String -> Int -> Socket -> IO (HandleStream bufType)+ readLine :: HandleStream bufType -> IO (Result bufType)+ readBlock :: HandleStream bufType -> Int -> IO (Result bufType)+ writeBlock :: HandleStream bufType -> bufType -> IO (Result ())+ close :: HandleStream bufType -> IO ()+ closeQuick :: HandleStream bufType -> IO ()+ closeOnEnd :: HandleStream bufType -> Bool -> IO ()+ +instance HStream Strict.ByteString where+ openStream = openTCPConnection+ openSocketStream = socketConnection+ readBlock c n = readBlockBS c n+ readLine c = readLineBS c+ writeBlock c str = writeBlockBS c str+ close c = closeIt c Strict.null True+ closeQuick c = closeIt c Strict.null False+ closeOnEnd c f = closeEOF c f++instance HStream Lazy.ByteString where+ openStream = \ a b -> openTCPConnection_ a b True+ openSocketStream = \ a b c -> socketConnection_ a b c True+ readBlock c n = readBlockBS c n+ readLine c = readLineBS c+ writeBlock c str = writeBlockBS c str+ close c = closeIt c Lazy.null True+ closeQuick c = closeIt c Lazy.null False+ closeOnEnd c f = closeEOF c f++instance Stream.Stream Connection where+ readBlock (Connection c) = Network.TCP.readBlock c+ readLine (Connection c) = Network.TCP.readLine c+ writeBlock (Connection c) = Network.TCP.writeBlock c + close (Connection c) = Network.TCP.close c+ closeOnEnd (Connection c) f = Network.TCP.closeEOF c f+ +instance HStream String where+ openStream = openTCPConnection+ openSocketStream = socketConnection+ readBlock ref n = readBlockBS ref n++ -- This function uses a buffer, at this time the buffer is just 1000 characters.+ -- (however many bytes this is is left to the user to decypher)+ readLine ref = readLineBS ref+ -- The 'Connection' object allows no outward buffering, + -- since in general messages are serialised in their entirety.+ writeBlock ref str = writeBlockBS ref str -- (stringToBuf str)++ -- Closes a Connection. Connection will no longer+ -- allow any of the other Stream functions. Notice that a Connection may close+ -- at any time before a call to this function. This function is idempotent.+ -- (I think the behaviour here is TCP specific)+ close c = closeIt c null True+ + -- Closes a Connection without munching the rest of the stream.+ closeQuick c = closeIt c null False++ closeOnEnd c f = closeEOF c f+ +-- | @openTCPPort uri port@ establishes a connection to a remote+-- host, using 'getHostByName' which possibly queries the DNS system, hence +-- may trigger a network connection.+openTCPPort :: String -> Int -> IO Connection+openTCPPort uri port = openTCPConnection uri port >>= return.Connection++-- Add a "persistent" option? Current persistent is default.+-- Use "Result" type for synchronous exception reporting?+openTCPConnection :: BufferType ty => String -> Int -> IO (HandleStream ty)+openTCPConnection uri port = openTCPConnection_ uri port False++openTCPConnection_ :: BufferType ty => String -> Int -> Bool -> IO (HandleStream ty)+openTCPConnection_ uri port stashInput = withSocket $ \s -> do+ setSocketOption s KeepAlive 1+ hostA <- getHostAddr uri+ let a = SockAddrInet (toEnum port) hostA+ connect s a+ socketConnection_ uri port s stashInput+ where+ withSocket action = do+ s <- socket AF_INET Stream 6+ catchIO (action s) (\e -> sClose s >> ioError e)+ getHostAddr h = do+ catchIO (inet_addr uri) -- handles ascii IP numbers+ (\ _ -> do+ host <- getHostByName_safe uri+ case hostAddresses host of+ [] -> fail ("openTCPConnection: no addresses in host entry for " ++ show h)+ (ha:_) -> return ha)++ getHostByName_safe h = + catchIO (getHostByName h)+ (\ _ -> fail ("openTCPConnection: host lookup failure for " ++ show h))++-- | @socketConnection@, like @openConnection@ but using a pre-existing 'Socket'.+socketConnection :: BufferType ty+ => String+ -> Int+ -> Socket+ -> IO (HandleStream ty)+socketConnection hst port sock = socketConnection_ hst port sock False++-- Internal function used to control the on-demand streaming of input+-- for /lazy/ streams.+socketConnection_ :: BufferType ty+ => String+ -> Int+ -> Socket+ -> Bool+ -> IO (HandleStream ty)+socketConnection_ hst port sock stashInput = do+ h <- socketToHandle sock ReadWriteMode+ mb <- case stashInput of { True -> liftM Just $ buf_hGetContents bufferOps h; _ -> return Nothing }+ let conn = MkConn + { connSock = sock+ , connHandle = h+ , connBuffer = bufferOps+ , connInput = mb+ , connEndPoint = EndPoint hst port+ , connHooks = Nothing+ , connCloseEOF = False+ }+ v <- newMVar conn+ return (HandleStream v)++closeConnection :: HStream a => HandleStream a -> IO Bool -> IO ()+closeConnection ref readL = do+ -- won't hold onto the lock for the duration+ -- we are draining it...ToDo: have Connection+ -- into a shutting-down state so that other+ -- threads will simply back off if/when attempting+ -- to also close it.+ c <- readMVar (getRef ref)+ closeConn c `catchIO` (\_ -> return ())+ modifyMVar_ (getRef ref) (\ _ -> return ConnClosed)+ where+ -- Be kind to peer & close gracefully.+ closeConn ConnClosed = return ()+ closeConn conn = do+ let sk = connSock conn+ hFlush (connHandle conn)+ shutdown sk ShutdownSend+ suck readL+ hClose (connHandle conn)+ shutdown sk ShutdownReceive+ sClose sk++ suck :: IO Bool -> IO ()+ suck rd = do+ f <- rd+ if f then return () else suck rd++-- | Checks both that the underlying Socket is connected+-- and that the connection peer matches the given+-- host name (which is recorded locally).+isConnectedTo :: Connection -> EndPoint -> IO Bool+isConnectedTo (Connection conn) endPoint = do+ v <- readMVar (getRef conn)+ case v of+ ConnClosed -> print "aa" >> return False+ _ + | connEndPoint v == endPoint ->+ catch (getPeerName (connSock v) >> return True) (const $ return False)+ | otherwise -> return False++isTCPConnectedTo :: HandleStream ty -> EndPoint -> IO Bool+isTCPConnectedTo conn endPoint = do+ v <- readMVar (getRef conn)+ case v of+ ConnClosed -> return False+ _ + | connEndPoint v == endPoint ->+ catch (getPeerName (connSock v) >> return True) (const $ return False)+ | otherwise -> return False++readBlockBS :: HStream a => HandleStream a -> Int -> IO (Result a)+readBlockBS ref n = onNonClosedDo ref $ \ conn -> do+ x <- bufferGetBlock ref n+ maybe (return ())+ (\ h -> hook_readBlock h (buf_toStr $ connBuffer conn) n x)+ (connHooks' conn)+ return x++-- This function uses a buffer, at this time the buffer is just 1000 characters.+-- (however many bytes this is is left for the user to decipher)+readLineBS :: HStream a => HandleStream a -> IO (Result a)+readLineBS ref = onNonClosedDo ref $ \ conn -> do+ x <- bufferReadLine ref+ maybe (return ())+ (\ h -> hook_readLine h (buf_toStr $ connBuffer conn) x)+ (connHooks' conn)+ return x++-- The 'Connection' object allows no outward buffering, +-- since in general messages are serialised in their entirety.+writeBlockBS :: HandleStream a -> a -> IO (Result ())+writeBlockBS ref b = onNonClosedDo ref $ \ conn -> do+ x <- bufferPutBlock (connBuffer conn) (connHandle conn) b+ maybe (return ())+ (\ h -> hook_writeBlock h (buf_toStr $ connBuffer conn) b x)+ (connHooks' conn)+ return x++closeIt :: HStream ty => HandleStream ty -> (ty -> Bool) -> Bool -> IO ()+closeIt c p b = do+ closeConnection c (if b+ then readLineBS c >>= \ x -> case x of { Right xs -> return (p xs); _ -> return True}+ else return True)+ conn <- readMVar (getRef c)+ maybe (return ())+ (hook_close)+ (connHooks' conn)++closeEOF :: HandleStream ty -> Bool -> IO ()+closeEOF c flg = modifyMVar_ (getRef c) (\ co -> return co{connCloseEOF=flg})++bufferGetBlock :: HStream a => HandleStream a -> Int -> IO (Result a)+bufferGetBlock ref n = onNonClosedDo ref $ \ conn -> do+ case connInput conn of+ Just c -> do+ let (a,b) = buf_splitAt (connBuffer conn) n c+ modifyMVar_ (getRef ref) (\ co -> return co{connInput=Just b})+ return (return a)+ _ -> do+ Prelude.catch (buf_hGet (connBuffer conn) (connHandle conn) n >>= return.return)+ (\ e -> + if isEOFError e + then do+ when (connCloseEOF conn) $ catch (closeQuick ref) (\ _ -> return ())+ return (return (buf_empty (connBuffer conn)))+ else return (failMisc (show e)))++bufferPutBlock :: BufferOp a -> Handle -> a -> IO (Result ())+bufferPutBlock ops h b = + Prelude.catch (buf_hPut ops h b >> hFlush h >> return (return ()))+ (\ e -> return (failMisc (show e)))++bufferReadLine :: HStream a => HandleStream a -> IO (Result a)+bufferReadLine ref = onNonClosedDo ref $ \ conn -> do+ case connInput conn of+ Just c -> do+ let (a,b0) = buf_span (connBuffer conn) (/='\n') c+ let (newl,b1) = buf_splitAt (connBuffer conn) 1 b0+ modifyMVar_ (getRef ref) (\ co -> return co{connInput=Just b1})+ return (return (buf_append (connBuffer conn) a newl))+ _ -> Prelude.catch + (buf_hGetLine (connBuffer conn) (connHandle conn) >>= + return . return . appendNL (connBuffer conn))+ (\ e ->+ if isEOFError e+ then do+ when (connCloseEOF conn) $ catch (closeQuick ref) (\ _ -> return ())+ return (return (buf_empty (connBuffer conn)))+ else return (failMisc (show e)))+ where+ -- yes, this s**ks.. _may_ have to be addressed if perf+ -- suggests worthiness.+ appendNL ops b = buf_snoc ops b nl+ + nl :: Word8+ nl = fromIntegral (fromEnum '\n')++onNonClosedDo :: HandleStream a -> (Conn a -> IO (Result b)) -> IO (Result b)+onNonClosedDo h act = do+ x <- readMVar (getRef h)+ case x of+ ConnClosed{} -> return (failWith ErrorClosed)+ _ -> act x+
+ Network/URI.hs view
@@ -0,0 +1,1289 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}+--------------------------------------------------------------------------------+-- |+-- Module : Network.URI+-- Copyright : (c) 2004, Graham Klyne+-- License : BSD-style (see end of this file)+--+-- Maintainer : Graham Klyne <gk@ninebynine.org>+-- Stability : provisional+-- Portability : portable+--+-- This module defines functions for handling URIs. It presents substantially the+-- same interface as the older GHC Network.URI module, but is implemented using+-- Parsec rather than a Regex library that is not available with Hugs. The internal+-- representation of URI has been changed so that URI strings are more+-- completely preserved when round-tripping to a URI value and back.+--+-- In addition, four methods are provided for parsing different+-- kinds of URI string (as noted in RFC3986):+-- 'parseURI',+-- 'parseURIReference',+-- 'parseRelativeReference' and+-- 'parseAbsoluteURI'.+--+-- Further, four methods are provided for classifying different+-- kinds of URI string (as noted in RFC3986):+-- 'isURI',+-- 'isURIReference',+-- 'isRelativeReference' and+-- 'isAbsoluteURI'.+--+-- The long-standing official reference for URI handling was RFC2396 [1],+-- as updated by RFC 2732 [2], but this was replaced by a new specification,+-- RFC3986 [3] in January 2005. This latter specification has been used+-- as the primary reference for constructing the URI parser implemented+-- here, and it is intended that there is a direct relationship between+-- the syntax definition in that document and this parser implementation.+--+-- RFC 1808 [4] contains a number of test cases for relative URI handling.+-- Dan Connolly's Python module @uripath.py@ [5] also contains useful details+-- and test cases.+--+-- Some of the code has been copied from the previous GHC implementation,+-- but the parser is replaced with one that performs more complete+-- syntax checking of the URI itself, according to RFC3986 [3].+--+-- References+--+-- (1) <http://www.ietf.org/rfc/rfc2396.txt>+--+-- (2) <http://www.ietf.org/rfc/rfc2732.txt>+--+-- (3) <http://www.ietf.org/rfc/rfc3986.txt>+--+-- (4) <http://www.ietf.org/rfc/rfc1808.txt>+--+-- (5) <http://www.w3.org/2000/10/swap/uripath.py>+--+--------------------------------------------------------------------------------++module Network.URI+ (+ -- * The URI type+ URI(..)+ , URIAuth(..)+ , nullURI+ + -- * Parsing+ , parseURI+ , parseURIReference+ , parseRelativeReference+ , parseAbsoluteURI+ + -- * Test for strings containing various kinds of URI+ , isURI+ , isURIReference+ , isRelativeReference+ , isAbsoluteURI+ , isIPv6address+ , isIPv4address+ + -- * Relative URIs+ , relativeTo+ , nonStrictRelativeTo+ , relativeFrom+ + -- * Operations on URI strings+ -- | Support for putting strings into URI-friendly+ -- escaped format and getting them back again.+ -- This can't be done transparently in all cases, because certain+ -- characters have different meanings in different kinds of URI.+ -- The URI spec [3], section 2.4, indicates that all URI components+ -- should be escaped before they are assembled as a URI:+ -- \"Once produced, a URI is always in its percent-encoded form\"+ , uriToString+ , isReserved, isUnreserved+ , isAllowedInURI, isUnescapedInURI+ , escapeURIChar+ , escapeURIString+ , unEscapeString+ + -- * URI Normalization functions+ , normalizeCase+ , normalizeEscape+ , normalizePathSegments+ + -- * Deprecated functions+ , parseabsoluteURI+ , escapeString+ , reserved, unreserved+ , scheme, authority, path, query, fragment+ ) where++import Text.ParserCombinators.Parsec+ ( GenParser, ParseError+ , parse, (<|>), (<?>), try+ , option, many, many1, count, notFollowedBy+ , char, satisfy, oneOf, string, eof+ , unexpected+ )++import Control.Monad (MonadPlus(..))+import Data.Char (ord, chr, isHexDigit, toLower, toUpper, digitToInt)+import Debug.Trace (trace)+import Numeric (showIntAtBase)++#ifdef __GLASGOW_HASKELL__+import Data.Typeable (Typeable)+# if MIN_VERSION_base(4,0,0)+import Data.Data (Data)+# else+import Data.Generics (Data)+# endif+#else+import Data.Typeable (Typeable(..), TyCon, mkTyCon, mkTyConApp)+#endif++------------------------------------------------------------+-- The URI datatype+------------------------------------------------------------++-- |Represents a general universal resource identifier using+-- its component parts.+--+-- For example, for the URI+--+-- > foo://anonymous@www.haskell.org:42/ghc?query#frag+--+-- the components are:+--+data URI = URI+ { uriScheme :: String -- ^ @foo:@+ , uriAuthority :: Maybe URIAuth -- ^ @\/\/anonymous\@www.haskell.org:42@+ , uriPath :: String -- ^ @\/ghc@+ , uriQuery :: String -- ^ @?query@+ , uriFragment :: String -- ^ @#frag@+ } deriving (Eq+#ifdef __GLASGOW_HASKELL__+ , Typeable, Data+#endif+ )++#ifndef __GLASGOW_HASKELL__+uriTc :: TyCon+uriTc = mkTyCon "URI"++instance Typeable URI where+ typeOf _ = mkTyConApp uriTc []+#endif++-- |Type for authority value within a URI+data URIAuth = URIAuth+ { uriUserInfo :: String -- ^ @anonymous\@@+ , uriRegName :: String -- ^ @www.haskell.org@+ , uriPort :: String -- ^ @:42@+ } deriving (Eq+#ifdef __GLASGOW_HASKELL__+ , Typeable, Data+#endif+ )++#ifndef __GLASGOW_HASKELL__+uriAuthTc :: TyCon+uriAuthTc = mkTyCon "URIAuth"++instance Typeable URIAuth where+ typeOf _ = mkTyConApp uriAuthTc []+#endif++-- |Blank URI+nullURI :: URI+nullURI = URI+ { uriScheme = ""+ , uriAuthority = Nothing+ , uriPath = ""+ , uriQuery = ""+ , uriFragment = ""+ }++-- URI as instance of Show. Note that for security reasons, the default+-- behaviour is to suppress any userinfo field (see RFC3986, section 7.5).+-- This can be overridden by using uriToString directly with first+-- argument @id@ (noting that this returns a ShowS value rather than a string).+--+-- [[[Another design would be to embed the userinfo mapping function in+-- the URIAuth value, with the default value suppressing userinfo formatting,+-- but providing a function to return a new URI value with userinfo+-- data exposed by show.]]]+--+instance Show URI where+ showsPrec _ = uriToString defaultUserInfoMap++defaultUserInfoMap :: String -> String+defaultUserInfoMap uinf = user++newpass+ where+ (user,pass) = break (==':') uinf+ newpass = if null pass || (pass == "@")+ || (pass == ":@")+ then pass+ else ":...@"++testDefaultUserInfoMap :: [Bool]+testDefaultUserInfoMap =+ [ defaultUserInfoMap "" == ""+ , defaultUserInfoMap "@" == "@"+ , defaultUserInfoMap "user@" == "user@"+ , defaultUserInfoMap "user:@" == "user:@"+ , defaultUserInfoMap "user:anonymous@" == "user:...@"+ , defaultUserInfoMap "user:pass@" == "user:...@"+ , defaultUserInfoMap "user:pass" == "user:...@"+ , defaultUserInfoMap "user:anonymous" == "user:...@"+ ]++------------------------------------------------------------+-- Parse a URI+------------------------------------------------------------++-- |Turn a string containing a URI into a 'URI'.+-- Returns 'Nothing' if the string is not a valid URI;+-- (an absolute URI with optional fragment identifier).+--+-- NOTE: this is different from the previous network.URI,+-- whose @parseURI@ function works like 'parseURIReference'+-- in this module.+--+parseURI :: String -> Maybe URI+parseURI = parseURIAny uri++-- |Parse a URI reference to a 'URI' value.+-- Returns 'Nothing' if the string is not a valid URI reference.+-- (an absolute or relative URI with optional fragment identifier).+--+parseURIReference :: String -> Maybe URI+parseURIReference = parseURIAny uriReference++-- |Parse a relative URI to a 'URI' value.+-- Returns 'Nothing' if the string is not a valid relative URI.+-- (a relative URI with optional fragment identifier).+--+parseRelativeReference :: String -> Maybe URI+parseRelativeReference = parseURIAny relativeRef++-- |Parse an absolute URI to a 'URI' value.+-- Returns 'Nothing' if the string is not a valid absolute URI.+-- (an absolute URI without a fragment identifier).+--+parseAbsoluteURI :: String -> Maybe URI+parseAbsoluteURI = parseURIAny absoluteURI++-- |Test if string contains a valid URI+-- (an absolute URI with optional fragment identifier).+--+isURI :: String -> Bool+isURI = isValidParse uri++-- |Test if string contains a valid URI reference+-- (an absolute or relative URI with optional fragment identifier).+--+isURIReference :: String -> Bool+isURIReference = isValidParse uriReference++-- |Test if string contains a valid relative URI+-- (a relative URI with optional fragment identifier).+--+isRelativeReference :: String -> Bool+isRelativeReference = isValidParse relativeRef++-- |Test if string contains a valid absolute URI+-- (an absolute URI without a fragment identifier).+--+isAbsoluteURI :: String -> Bool+isAbsoluteURI = isValidParse absoluteURI++-- |Test if string contains a valid IPv6 address+--+isIPv6address :: String -> Bool+isIPv6address = isValidParse ipv6address++-- |Test if string contains a valid IPv4 address+--+isIPv4address :: String -> Bool+isIPv4address = isValidParse ipv4address++-- |Test function: parse and reconstruct a URI reference+--+testURIReference :: String -> String+testURIReference uristr = show (parseAll uriReference "" uristr)++-- Helper function for turning a string into a URI+--+parseURIAny :: URIParser URI -> String -> Maybe URI+parseURIAny parser uristr = case parseAll parser "" uristr of+ Left _ -> Nothing+ Right u -> Just u++-- Helper function to test a string match to a parser+--+isValidParse :: URIParser a -> String -> Bool+isValidParse parser uristr = case parseAll parser "" uristr of+ -- Left e -> error (show e)+ Left _ -> False+ Right _ -> True++parseAll :: URIParser a -> String -> String -> Either ParseError a+parseAll parser filename uristr = parse newparser filename uristr+ where+ newparser =+ do { res <- parser+ ; eof+ ; return res+ }++------------------------------------------------------------+-- URI parser body based on Parsec elements and combinators+------------------------------------------------------------++-- Parser parser type.+-- Currently+type URIParser a = GenParser Char () a++-- RFC3986, section 2.1+--+-- Parse and return a 'pct-encoded' sequence+--+escaped :: URIParser String+escaped =+ do { char '%'+ ; h1 <- hexDigitChar+ ; h2 <- hexDigitChar+ ; return $ ['%',h1,h2]+ }++-- RFC3986, section 2.2+--+-- |Returns 'True' if the character is a \"reserved\" character in a+-- URI. To include a literal instance of one of these characters in a+-- component of a URI, it must be escaped.+--+isReserved :: Char -> Bool+isReserved c = isGenDelims c || isSubDelims c++isGenDelims :: Char -> Bool+isGenDelims c = c `elem` ":/?#[]@"++isSubDelims :: Char -> Bool+isSubDelims c = c `elem` "!$&'()*+,;="++genDelims :: URIParser String+genDelims = do { c <- satisfy isGenDelims ; return [c] }++subDelims :: URIParser String+subDelims = do { c <- satisfy isSubDelims ; return [c] }++-- RFC3986, section 2.3+--+-- |Returns 'True' if the character is an \"unreserved\" character in+-- a URI. These characters do not need to be escaped in a URI. The+-- only characters allowed in a URI are either \"reserved\",+-- \"unreserved\", or an escape sequence (@%@ followed by two hex digits).+--+isUnreserved :: Char -> Bool+isUnreserved c = isAlphaNumChar c || (c `elem` "-_.~")++unreservedChar :: URIParser String+unreservedChar = do { c <- satisfy isUnreserved ; return [c] }++-- RFC3986, section 3+--+-- URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]+--+-- hier-part = "//" authority path-abempty+-- / path-abs+-- / path-rootless+-- / path-empty++uri :: URIParser URI+uri =+ do { us <- try uscheme+ -- ; ua <- option Nothing ( do { try (string "//") ; uauthority } )+ -- ; up <- upath+ ; (ua,up) <- hierPart+ ; uq <- option "" ( do { char '?' ; uquery } )+ ; uf <- option "" ( do { char '#' ; ufragment } )+ ; return $ URI+ { uriScheme = us+ , uriAuthority = ua+ , uriPath = up+ , uriQuery = uq+ , uriFragment = uf+ }+ }++hierPart :: URIParser ((Maybe URIAuth),String)+hierPart =+ do { try (string "//")+ ; ua <- uauthority+ ; up <- pathAbEmpty+ ; return (ua,up)+ }+ <|> do { up <- pathAbs+ ; return (Nothing,up)+ }+ <|> do { up <- pathRootLess+ ; return (Nothing,up)+ }+ <|> do { return (Nothing,"")+ }++-- RFC3986, section 3.1++uscheme :: URIParser String+uscheme =+ do { s <- oneThenMany alphaChar (satisfy isSchemeChar)+ ; char ':'+ ; return $ s++":"+ }++-- RFC3986, section 3.2++uauthority :: URIParser (Maybe URIAuth)+uauthority =+ do { uu <- option "" (try userinfo)+ ; uh <- host+ ; up <- option "" port+ ; return $ Just $ URIAuth+ { uriUserInfo = uu+ , uriRegName = uh+ , uriPort = up+ }+ }++-- RFC3986, section 3.2.1++userinfo :: URIParser String+userinfo =+ do { uu <- many (uchar ";:&=+$,")+ ; char '@'+ ; return (concat uu ++"@")+ }++-- RFC3986, section 3.2.2++host :: URIParser String+host = ipLiteral <|> try ipv4address <|> regName++ipLiteral :: URIParser String+ipLiteral =+ do { char '['+ ; ua <- ( ipv6address <|> ipvFuture )+ ; char ']'+ ; return $ "[" ++ ua ++ "]"+ }+ <?> "IP address literal"++ipvFuture :: URIParser String+ipvFuture =+ do { char 'v'+ ; h <- hexDigitChar+ ; char '.'+ ; a <- many1 (satisfy isIpvFutureChar)+ ; return $ 'c':h:'.':a+ }++isIpvFutureChar :: Char -> Bool+isIpvFutureChar c = isUnreserved c || isSubDelims c || (c==';')++ipv6address :: URIParser String+ipv6address =+ try ( do+ { a2 <- count 6 h4c+ ; a3 <- ls32+ ; return $ concat a2 ++ a3+ } )+ <|> try ( do+ { string "::"+ ; a2 <- count 5 h4c+ ; a3 <- ls32+ ; return $ "::" ++ concat a2 ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 0+ ; string "::"+ ; a2 <- count 4 h4c+ ; a3 <- ls32+ ; return $ a1 ++ "::" ++ concat a2 ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 1+ ; string "::"+ ; a2 <- count 3 h4c+ ; a3 <- ls32+ ; return $ a1 ++ "::" ++ concat a2 ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 2+ ; string "::"+ ; a2 <- count 2 h4c+ ; a3 <- ls32+ ; return $ a1 ++ "::" ++ concat a2 ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 3+ ; string "::"+ ; a2 <- h4c+ ; a3 <- ls32+ ; return $ a1 ++ "::" ++ a2 ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 4+ ; string "::"+ ; a3 <- ls32+ ; return $ a1 ++ "::" ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 5+ ; string "::"+ ; a3 <- h4+ ; return $ a1 ++ "::" ++ a3+ } )+ <|> try ( do+ { a1 <- opt_n_h4c_h4 6+ ; string "::"+ ; return $ a1 ++ "::"+ } )+ <?> "IPv6 address"++opt_n_h4c_h4 :: Int -> URIParser String+opt_n_h4c_h4 n = option "" $+ do { a1 <- countMinMax 0 n h4c+ ; a2 <- h4+ ; return $ concat a1 ++ a2+ }++ls32 :: URIParser String+ls32 = try ( do+ { a1 <- h4c+ ; a2 <- h4+ ; return (a1++a2)+ } )+ <|> ipv4address++h4c :: URIParser String+h4c = try $+ do { a1 <- h4+ ; char ':'+ ; notFollowedBy (char ':')+ ; return $ a1 ++ ":"+ }++h4 :: URIParser String+h4 = countMinMax 1 4 hexDigitChar++ipv4address :: URIParser String+ipv4address =+ do { a1 <- decOctet ; char '.'+ ; a2 <- decOctet ; char '.'+ ; a3 <- decOctet ; char '.'+ ; a4 <- decOctet+ ; return $ a1++"."++a2++"."++a3++"."++a4+ }++decOctet :: URIParser String+decOctet =+ do { a1 <- countMinMax 1 3 digitChar+ ; if (read a1 :: Integer) > 255 then+ fail "Decimal octet value too large"+ else+ return a1+ }++regName :: URIParser String+regName =+ do { ss <- countMinMax 0 255 ( unreservedChar <|> escaped <|> subDelims )+ ; return $ concat ss+ }+ <?> "Registered name"++-- RFC3986, section 3.2.3++port :: URIParser String+port =+ do { char ':'+ ; p <- many digitChar+ ; return (':':p)+ }++--+-- RFC3986, section 3.3+--+-- path = path-abempty ; begins with "/" or is empty+-- / path-abs ; begins with "/" but not "//"+-- / path-noscheme ; begins with a non-colon segment+-- / path-rootless ; begins with a segment+-- / path-empty ; zero characters+--+-- path-abempty = *( "/" segment )+-- path-abs = "/" [ segment-nz *( "/" segment ) ]+-- path-noscheme = segment-nzc *( "/" segment )+-- path-rootless = segment-nz *( "/" segment )+-- path-empty = 0<pchar>+--+-- segment = *pchar+-- segment-nz = 1*pchar+-- segment-nzc = 1*( unreserved / pct-encoded / sub-delims / "@" )+--+-- pchar = unreserved / pct-encoded / sub-delims / ":" / "@"++{-+upath :: URIParser String+upath = pathAbEmpty+ <|> pathAbs+ <|> pathNoScheme+ <|> pathRootLess+ <|> pathEmpty+-}++pathAbEmpty :: URIParser String+pathAbEmpty =+ do { ss <- many slashSegment+ ; return $ concat ss+ }++pathAbs :: URIParser String+pathAbs =+ do { char '/'+ ; ss <- option "" pathRootLess+ ; return $ '/':ss+ }++pathNoScheme :: URIParser String+pathNoScheme =+ do { s1 <- segmentNzc+ ; ss <- many slashSegment+ ; return $ concat (s1:ss)+ }++pathRootLess :: URIParser String+pathRootLess =+ do { s1 <- segmentNz+ ; ss <- many slashSegment+ ; return $ concat (s1:ss)+ }++slashSegment :: URIParser String+slashSegment =+ do { char '/'+ ; s <- segment+ ; return ('/':s)+ }++segment :: URIParser String+segment =+ do { ps <- many pchar+ ; return $ concat ps+ }++segmentNz :: URIParser String+segmentNz =+ do { ps <- many1 pchar+ ; return $ concat ps+ }++segmentNzc :: URIParser String+segmentNzc =+ do { ps <- many1 (uchar "@")+ ; return $ concat ps+ }++pchar :: URIParser String+pchar = uchar ":@"++-- helper function for pchar and friends+uchar :: String -> URIParser String+uchar extras =+ unreservedChar+ <|> escaped+ <|> subDelims+ <|> do { c <- oneOf extras ; return [c] }++-- RFC3986, section 3.4++uquery :: URIParser String+uquery =+ do { ss <- many $ uchar (":@"++"/?")+ ; return $ '?':concat ss+ }++-- RFC3986, section 3.5++ufragment :: URIParser String+ufragment =+ do { ss <- many $ uchar (":@"++"/?")+ ; return $ '#':concat ss+ }++-- Reference, Relative and Absolute URI forms+--+-- RFC3986, section 4.1++uriReference :: URIParser URI+uriReference = uri <|> relativeRef++-- RFC3986, section 4.2+--+-- relative-URI = relative-part [ "?" query ] [ "#" fragment ]+--+-- relative-part = "//" authority path-abempty+-- / path-abs+-- / path-noscheme+-- / path-empty++relativeRef :: URIParser URI+relativeRef =+ do { notMatching uscheme+ -- ; ua <- option Nothing ( do { try (string "//") ; uauthority } )+ -- ; up <- upath+ ; (ua,up) <- relativePart+ ; uq <- option "" ( do { char '?' ; uquery } )+ ; uf <- option "" ( do { char '#' ; ufragment } )+ ; return $ URI+ { uriScheme = ""+ , uriAuthority = ua+ , uriPath = up+ , uriQuery = uq+ , uriFragment = uf+ }+ }++relativePart :: URIParser ((Maybe URIAuth),String)+relativePart =+ do { try (string "//")+ ; ua <- uauthority+ ; up <- pathAbEmpty+ ; return (ua,up)+ }+ <|> do { up <- pathAbs+ ; return (Nothing,up)+ }+ <|> do { up <- pathNoScheme+ ; return (Nothing,up)+ }+ <|> do { return (Nothing,"")+ }++-- RFC3986, section 4.3++absoluteURI :: URIParser URI+absoluteURI =+ do { us <- uscheme+ -- ; ua <- option Nothing ( do { try (string "//") ; uauthority } )+ -- ; up <- upath+ ; (ua,up) <- hierPart+ ; uq <- option "" ( do { char '?' ; uquery } )+ ; return $ URI+ { uriScheme = us+ , uriAuthority = ua+ , uriPath = up+ , uriQuery = uq+ , uriFragment = ""+ }+ }++-- Imports from RFC 2234++ -- NOTE: can't use isAlphaNum etc. because these deal with ISO 8859+ -- (and possibly Unicode!) chars.+ -- [[[Above was a comment originally in GHC Network/URI.hs:+ -- when IRIs are introduced then most codepoints above 128(?) should+ -- be treated as unreserved, and higher codepoints for letters should+ -- certainly be allowed.+ -- ]]]++isAlphaChar :: Char -> Bool+isAlphaChar c = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')++isDigitChar :: Char -> Bool+isDigitChar c = (c >= '0' && c <= '9')++isAlphaNumChar :: Char -> Bool+isAlphaNumChar c = isAlphaChar c || isDigitChar c++isHexDigitChar :: Char -> Bool+isHexDigitChar c = isHexDigit c++isSchemeChar :: Char -> Bool+isSchemeChar c = (isAlphaNumChar c) || (c `elem` "+-.")++alphaChar :: URIParser Char+alphaChar = satisfy isAlphaChar -- or: Parsec.letter ?++digitChar :: URIParser Char+digitChar = satisfy isDigitChar -- or: Parsec.digit ?++alphaNumChar :: URIParser Char+alphaNumChar = satisfy isAlphaNumChar++hexDigitChar :: URIParser Char+hexDigitChar = satisfy isHexDigitChar -- or: Parsec.hexDigit ?++-- Additional parser combinators for common patterns++oneThenMany :: GenParser t s a -> GenParser t s a -> GenParser t s [a]+oneThenMany p1 pr =+ do { a1 <- p1+ ; ar <- many pr+ ; return (a1:ar)+ }++countMinMax :: Int -> Int -> GenParser t s a -> GenParser t s [a]+countMinMax m n p | m > 0 =+ do { a1 <- p+ ; ar <- countMinMax (m-1) (n-1) p+ ; return (a1:ar)+ }+countMinMax _ n _ | n <= 0 = return []+countMinMax _ n p = option [] $+ do { a1 <- p+ ; ar <- countMinMax 0 (n-1) p+ ; return (a1:ar)+ }++notMatching :: Show a => GenParser tok st a -> GenParser tok st ()+notMatching p = do { a <- try p ; unexpected (show a) } <|> return ()++------------------------------------------------------------+-- Reconstruct a URI string+------------------------------------------------------------+--+-- |Turn a 'URI' into a string.+--+-- Uses a supplied function to map the userinfo part of the URI.+--+-- The Show instance for URI uses a mapping that hides any password+-- that may be present in the URI. Use this function with argument @id@+-- to preserve the password in the formatted output.+--+uriToString :: (String->String) -> URI -> ShowS+uriToString userinfomap URI { uriScheme=myscheme+ , uriAuthority=myauthority+ , uriPath=mypath+ , uriQuery=myquery+ , uriFragment=myfragment+ } =+ (myscheme++) . (uriAuthToString userinfomap myauthority)+ . (mypath++) . (myquery++) . (myfragment++)++uriAuthToString :: (String->String) -> (Maybe URIAuth) -> ShowS+uriAuthToString _ Nothing = id -- shows ""+uriAuthToString userinfomap+ (Just URIAuth { uriUserInfo = myuinfo+ , uriRegName = myregname+ , uriPort = myport+ } ) =+ ("//"++) . (if null myuinfo then id else ((userinfomap myuinfo)++))+ . (myregname++)+ . (myport++)++------------------------------------------------------------+-- Character classes+------------------------------------------------------------++-- | Returns 'True' if the character is allowed in a URI.+--+isAllowedInURI :: Char -> Bool+isAllowedInURI c = isReserved c || isUnreserved c || c == '%' -- escape char++-- | Returns 'True' if the character is allowed unescaped in a URI.+--+isUnescapedInURI :: Char -> Bool+isUnescapedInURI c = isReserved c || isUnreserved c++------------------------------------------------------------+-- Escape sequence handling+------------------------------------------------------------++-- |Escape character if supplied predicate is not satisfied,+-- otherwise return character as singleton string.+--+escapeURIChar :: (Char->Bool) -> Char -> String+escapeURIChar p c+ | p c = [c]+ | otherwise = '%' : myShowHex (ord c) ""+ where+ myShowHex :: Int -> ShowS+ myShowHex n r = case showIntAtBase 16 (toChrHex) n r of+ [] -> "00"+ [x] -> ['0',x]+ cs -> cs+ toChrHex d+ | d < 10 = chr (ord '0' + fromIntegral d)+ | otherwise = chr (ord 'A' + fromIntegral (d - 10))++-- |Can be used to make a string valid for use in a URI.+--+escapeURIString+ :: (Char->Bool) -- ^ a predicate which returns 'False'+ -- if the character should be escaped+ -> String -- ^ the string to process+ -> String -- ^ the resulting URI string+escapeURIString p s = concatMap (escapeURIChar p) s++-- |Turns all instances of escaped characters in the string back+-- into literal characters.+--+unEscapeString :: String -> String+unEscapeString [] = ""+unEscapeString ('%':x1:x2:s) | isHexDigit x1 && isHexDigit x2 =+ chr (digitToInt x1 * 16 + digitToInt x2) : unEscapeString s+unEscapeString (c:s) = c : unEscapeString s++------------------------------------------------------------+-- Resolving a relative URI relative to a base URI+------------------------------------------------------------++-- |Returns a new 'URI' which represents the value of the+-- first 'URI' interpreted as relative to the second 'URI'.+-- For example:+--+-- > "foo" `relativeTo` "http://bar.org/" = "http://bar.org/foo"+-- > "http:foo" `nonStrictRelativeTo` "http://bar.org/" = "http://bar.org/foo"+--+-- Algorithm from RFC3986 [3], section 5.2.2+--++nonStrictRelativeTo :: URI -> URI -> Maybe URI+nonStrictRelativeTo ref base = relativeTo ref' base+ where+ ref' = if uriScheme ref == uriScheme base+ then ref { uriScheme="" }+ else ref++isDefined :: ( MonadPlus m, Eq (m a) ) => m a -> Bool+isDefined a = a /= mzero++-- |Compute an absolute 'URI' for a supplied URI+-- relative to a given base.+relativeTo :: URI -> URI -> Maybe URI+relativeTo ref base+ | isDefined ( uriScheme ref ) =+ just_segments ref+ | isDefined ( uriAuthority ref ) =+ just_segments ref { uriScheme = uriScheme base }+ | isDefined ( uriPath ref ) =+ if (head (uriPath ref) == '/') then+ just_segments ref+ { uriScheme = uriScheme base+ , uriAuthority = uriAuthority base+ }+ else+ just_segments ref+ { uriScheme = uriScheme base+ , uriAuthority = uriAuthority base+ , uriPath = mergePaths base ref+ }+ | isDefined ( uriQuery ref ) =+ just_segments ref+ { uriScheme = uriScheme base+ , uriAuthority = uriAuthority base+ , uriPath = uriPath base+ }+ | otherwise =+ just_segments ref+ { uriScheme = uriScheme base+ , uriAuthority = uriAuthority base+ , uriPath = uriPath base+ , uriQuery = uriQuery base+ }+ where+ just_segments u =+ Just $ u { uriPath = removeDotSegments (uriPath u) }+ mergePaths b r+ | isDefined (uriAuthority b) && null pb = '/':pr+ | otherwise = dropLast pb ++ pr+ where+ pb = uriPath b+ pr = uriPath r+ dropLast = fst . splitLast -- reverse . dropWhile (/='/') . reverse++-- Remove dot segments, but protect leading '/' character+removeDotSegments :: String -> String+removeDotSegments ('/':ps) = '/':elimDots ps []+removeDotSegments ps = elimDots ps []++-- Second arg accumulates segments processed so far in reverse order+elimDots :: String -> [String] -> String+-- elimDots ps rs | traceVal "\nps " ps $ traceVal "rs " rs $ False = error ""+elimDots [] [] = ""+elimDots [] rs = concat (reverse rs)+elimDots ( '.':'/':ps) rs = elimDots ps rs+elimDots ( '.':[] ) rs = elimDots [] rs+elimDots ( '.':'.':'/':ps) rs = elimDots ps (drop 1 rs)+elimDots ( '.':'.':[] ) rs = elimDots [] (drop 1 rs)+elimDots ps rs = elimDots ps1 (r:rs)+ where+ (r,ps1) = nextSegment ps++-- Returns the next segment and the rest of the path from a path string.+-- Each segment ends with the next '/' or the end of string.+--+nextSegment :: String -> (String,String)+nextSegment ps =+ case break (=='/') ps of+ (r,'/':ps1) -> (r++"/",ps1)+ (r,_) -> (r,[])++-- Split last (name) segment from path, returning (path,name)+splitLast :: String -> (String,String)+splitLast p = (reverse revpath,reverse revname)+ where+ (revname,revpath) = break (=='/') $ reverse p++------------------------------------------------------------+-- Finding a URI relative to a base URI+------------------------------------------------------------++-- |Returns a new 'URI' which represents the relative location of+-- the first 'URI' with respect to the second 'URI'. Thus, the+-- values supplied are expected to be absolute URIs, and the result+-- returned may be a relative URI.+--+-- Example:+--+-- > "http://example.com/Root/sub1/name2#frag"+-- > `relativeFrom` "http://example.com/Root/sub2/name2#frag"+-- > == "../sub1/name2#frag"+--+-- There is no single correct implementation of this function,+-- but any acceptable implementation must satisfy the following:+--+-- > (uabs `relativeFrom` ubase) `relativeTo` ubase == uabs+--+-- For any valid absolute URI.+-- (cf. <http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html>+-- <http://lists.w3.org/Archives/Public/uri/2003Jan/0005.html>)+--+relativeFrom :: URI -> URI -> URI+relativeFrom uabs base+ | diff uriScheme uabs base = uabs+ | diff uriAuthority uabs base = uabs { uriScheme = "" }+ | diff uriPath uabs base = uabs+ { uriScheme = ""+ , uriAuthority = Nothing+ , uriPath = relPathFrom (removeBodyDotSegments $ uriPath uabs)+ (removeBodyDotSegments $ uriPath base)+ }+ | diff uriQuery uabs base = uabs+ { uriScheme = ""+ , uriAuthority = Nothing+ , uriPath = ""+ }+ | otherwise = uabs -- Always carry fragment from uabs+ { uriScheme = ""+ , uriAuthority = Nothing+ , uriPath = ""+ , uriQuery = ""+ }+ where+ diff :: Eq b => (a -> b) -> a -> a -> Bool+ diff sel u1 u2 = sel u1 /= sel u2+ -- Remove dot segments except the final segment+ removeBodyDotSegments p = removeDotSegments p1 ++ p2+ where+ (p1,p2) = splitLast p++relPathFrom :: String -> String -> String+relPathFrom [] _ = "/"+relPathFrom pabs [] = pabs+relPathFrom pabs base = -- Construct a relative path segments+ if sa1 == sb1 -- if the paths share a leading segment+ then if (sa1 == "/") -- other than a leading '/'+ then if (sa2 == sb2)+ then relPathFrom1 ra2 rb2+ else pabs+ else relPathFrom1 ra1 rb1+ else pabs+ where+ (sa1,ra1) = nextSegment pabs+ (sb1,rb1) = nextSegment base+ (sa2,ra2) = nextSegment ra1+ (sb2,rb2) = nextSegment rb1++-- relPathFrom1 strips off trailing names from the supplied paths,+-- and calls difPathFrom to find the relative path from base to+-- target+relPathFrom1 :: String -> String -> String+relPathFrom1 pabs base = relName+ where+ (sa,na) = splitLast pabs+ (sb,nb) = splitLast base+ rp = relSegsFrom sa sb+ relName = if null rp then+ if (na == nb) then ""+ else if protect na then "./"++na+ else na+ else+ rp++na+ -- Precede name with some path if it is null or contains a ':'+ protect s = null s || ':' `elem` s++-- relSegsFrom discards any common leading segments from both paths,+-- then invokes difSegsFrom to calculate a relative path from the end+-- of the base path to the end of the target path.+-- The final name is handled separately, so this deals only with+-- "directory" segtments.+--+relSegsFrom :: String -> String -> String+{-+relSegsFrom sabs base+ | traceVal "\nrelSegsFrom\nsabs " sabs $ traceVal "base " base $+ False = error ""+-}+relSegsFrom [] [] = "" -- paths are identical+relSegsFrom sabs base =+ if sa1 == sb1+ then relSegsFrom ra1 rb1+ else difSegsFrom sabs base+ where+ (sa1,ra1) = nextSegment sabs+ (sb1,rb1) = nextSegment base++-- difSegsFrom calculates a path difference from base to target,+-- not including the final name at the end of the path+-- (i.e. results always ends with '/')+--+-- This function operates under the invariant that the supplied+-- value of sabs is the desired path relative to the beginning of+-- base. Thus, when base is empty, the desired path has been found.+--+difSegsFrom :: String -> String -> String+{-+difSegsFrom sabs base+ | traceVal "\ndifSegsFrom\nsabs " sabs $ traceVal "base " base $+ False = error ""+-}+difSegsFrom sabs "" = sabs+difSegsFrom sabs base = difSegsFrom ("../"++sabs) (snd $ nextSegment base)++------------------------------------------------------------+-- Other normalization functions+------------------------------------------------------------++-- |Case normalization; cf. RFC3986 section 6.2.2.1+-- NOTE: authority case normalization is not performed+--+normalizeCase :: String -> String+normalizeCase uristr = ncScheme uristr+ where+ ncScheme (':':cs) = ':':ncEscape cs+ ncScheme (c:cs) | isSchemeChar c = toLower c:ncScheme cs+ ncScheme _ = ncEscape uristr -- no scheme present+ ncEscape ('%':h1:h2:cs) = '%':toUpper h1:toUpper h2:ncEscape cs+ ncEscape (c:cs) = c:ncEscape cs+ ncEscape [] = []++-- |Encoding normalization; cf. RFC3986 section 6.2.2.2+--+normalizeEscape :: String -> String+normalizeEscape ('%':h1:h2:cs)+ | isHexDigit h1 && isHexDigit h2 && isUnreserved escval =+ escval:normalizeEscape cs+ where+ escval = chr (digitToInt h1*16+digitToInt h2)+normalizeEscape (c:cs) = c:normalizeEscape cs+normalizeEscape [] = []++-- |Path segment normalization; cf. RFC3986 section 6.2.2.4+--+normalizePathSegments :: String -> String+normalizePathSegments uristr = normstr juri+ where+ juri = parseURI uristr+ normstr Nothing = uristr+ normstr (Just u) = show (normuri u)+ normuri u = u { uriPath = removeDotSegments (uriPath u) }++------------------------------------------------------------+-- Local trace helper functions+------------------------------------------------------------++traceShow :: Show a => String -> a -> a+traceShow msg x = trace (msg ++ show x) x++traceVal :: Show a => String -> a -> b -> b+traceVal msg x y = trace (msg ++ show x) y++------------------------------------------------------------+-- Deprecated functions+------------------------------------------------------------++{-# DEPRECATED parseabsoluteURI "use parseAbsoluteURI" #-}+parseabsoluteURI :: String -> Maybe URI+parseabsoluteURI = parseAbsoluteURI++{-# DEPRECATED escapeString "use escapeURIString, and note the flipped arguments" #-}+escapeString :: String -> (Char->Bool) -> String+escapeString = flip escapeURIString++{-# DEPRECATED reserved "use isReserved" #-}+reserved :: Char -> Bool+reserved = isReserved++{-# DEPRECATED unreserved "use isUnreserved" #-}+unreserved :: Char -> Bool+unreserved = isUnreserved++-- Additional component access functions for backward compatibility++{-# DEPRECATED scheme "use uriScheme" #-}+scheme :: URI -> String+scheme = orNull init . uriScheme++{-# DEPRECATED authority "use uriAuthority, and note changed functionality" #-}+authority :: URI -> String+authority = dropss . ($"") . uriAuthToString id . uriAuthority+ where+ -- Old-style authority component does not include leading '//'+ dropss ('/':'/':s) = s+ dropss s = s++{-# DEPRECATED path "use uriPath" #-}+path :: URI -> String+path = uriPath++{-# DEPRECATED query "use uriQuery, and note changed functionality" #-}+query :: URI -> String+query = orNull tail . uriQuery++{-# DEPRECATED fragment "use uriFragment, and note changed functionality" #-}+fragment :: URI -> String+fragment = orNull tail . uriFragment++orNull :: ([a]->[a]) -> [a] -> [a]+orNull _ [] = []+orNull f as = f as++--------------------------------------------------------------------------------+--+-- Copyright (c) 2004, G. KLYNE. All rights reserved.+-- Distributed as free software under the following license.+--+-- Redistribution and use in source and binary forms, with or without+-- modification, are permitted provided that the following conditions+-- are met:+--+-- - Redistributions of source code must retain the above copyright notice,+-- this list of conditions and the following disclaimer.+--+-- - Redistributions in binary form must reproduce the above copyright+-- notice, this list of conditions and the following disclaimer in the+-- documentation and/or other materials provided with the distribution.+--+-- - Neither name of the copyright holders nor the names of its+-- contributors may be used to endorse or promote products derived from+-- this software without specific prior written permission.+--+-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND THE CONTRIBUTORS+-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+-- HOLDERS OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+-- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS+-- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR+-- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE+-- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+--+--------------------------------------------------------------------------------
+ Setup.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Distribution.Simple++main :: IO ()+main = defaultMainWithHooks autoconfUserHooks
+ System/Random.hs view
@@ -0,0 +1,567 @@+#if __GLASGOW_HASKELL__ >= 701+{-# LANGUAGE Trustworthy #-}+#endif++-----------------------------------------------------------------------------+-- |+-- Module : System.Random+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE in the 'random' repository)+-- +-- Maintainer : libraries@haskell.org+-- Stability : stable+-- Portability : portable+--+-- This library deals with the common task of pseudo-random number+-- generation. The library makes it possible to generate repeatable+-- results, by starting with a specified initial random number generator,+-- or to get different results on each run by using the system-initialised+-- generator or by supplying a seed from some other source.+--+-- The library is split into two layers: +--+-- * A core /random number generator/ provides a supply of bits.+-- The class 'RandomGen' provides a common interface to such generators.+-- The library provides one instance of 'RandomGen', the abstract+-- data type 'StdGen'. Programmers may, of course, supply their own+-- instances of 'RandomGen'.+--+-- * The class 'Random' provides a way to extract values of a particular+-- type from a random number generator. For example, the 'Float'+-- instance of 'Random' allows one to generate random values of type+-- 'Float'.+--+-- This implementation uses the Portable Combined Generator of L'Ecuyer+-- ["System.Random\#LEcuyer"] for 32-bit computers, transliterated by+-- Lennart Augustsson. It has a period of roughly 2.30584e18.+--+-----------------------------------------------------------------------------++#include "MachDeps.h"++module System.Random+ (++ -- $intro++ -- * Random number generators++#ifdef ENABLE_SPLITTABLEGEN+ RandomGen(next, genRange)+ , SplittableGen(split)+#else+ RandomGen(next, genRange, split)+#endif+ -- ** Standard random number generators+ , StdGen+ , mkStdGen++ -- ** The global random number generator++ -- $globalrng++ , getStdRandom+ , getStdGen+ , setStdGen+ , newStdGen++ -- * Random values of various types+ , Random ( random, randomR,+ randoms, randomRs,+ randomIO, randomRIO )++ -- * References+ -- $references++ ) where++import Prelude++import Data.Bits+import Data.Int+import Data.Word+import Foreign.C.Types++#ifdef __NHC__+import CPUTime ( getCPUTime )+import Foreign.Ptr ( Ptr, nullPtr )+import Foreign.C ( CTime, CUInt )+#else+import System.CPUTime ( getCPUTime )+import Data.Time ( getCurrentTime, UTCTime(..) )+import Data.Ratio ( numerator, denominator )+#endif+import Data.Char ( isSpace, chr, ord )+import System.IO.Unsafe ( unsafePerformIO )+import Data.IORef+import Numeric ( readDec )++-- The standard nhc98 implementation of Time.ClockTime does not match+-- the extended one expected in this module, so we lash-up a quick+-- replacement here.+#ifdef __NHC__+foreign import ccall "time.h time" readtime :: Ptr CTime -> IO CTime+getTime :: IO (Integer, Integer)+getTime = do CTime t <- readtime nullPtr; return (toInteger t, 0)+#else+getTime :: IO (Integer, Integer)+getTime = do+ utc <- getCurrentTime+ let daytime = toRational $ utctDayTime utc+ return $ quotRem (numerator daytime) (denominator daytime)+#endif++-- | The class 'RandomGen' provides a common interface to random number+-- generators.+--+#ifdef ENABLE_SPLITTABLEGEN+-- Minimal complete definition: 'next'.+#else+-- Minimal complete definition: 'next' and 'split'.+#endif++class RandomGen g where++ -- |The 'next' operation returns an 'Int' that is uniformly distributed+ -- in the range returned by 'genRange' (including both end points),+ -- and a new generator.+ next :: g -> (Int, g)++ -- |The 'genRange' operation yields the range of values returned by+ -- the generator.+ --+ -- It is required that:+ --+ -- * If @(a,b) = 'genRange' g@, then @a < b@.+ --+ -- * 'genRange' always returns a pair of defined 'Int's.+ --+ -- The second condition ensures that 'genRange' cannot examine its+ -- argument, and hence the value it returns can be determined only by the+ -- instance of 'RandomGen'. That in turn allows an implementation to make+ -- a single call to 'genRange' to establish a generator's range, without+ -- being concerned that the generator returned by (say) 'next' might have+ -- a different range to the generator passed to 'next'.+ --+ -- The default definition spans the full range of 'Int'.+ genRange :: g -> (Int,Int)++ -- default method+ genRange _ = (minBound, maxBound)++#ifdef ENABLE_SPLITTABLEGEN+-- | The class 'SplittableGen' proivides a way to specify a random number+-- generator that can be split into two new generators.+class SplittableGen g where+#endif+ -- |The 'split' operation allows one to obtain two distinct random number+ -- generators. This is very useful in functional programs (for example, when+ -- passing a random number generator down to recursive calls), but very+ -- little work has been done on statistically robust implementations of+ -- 'split' (["System.Random\#Burton", "System.Random\#Hellekalek"]+ -- are the only examples we know of).+ split :: g -> (g, g)++{- |+The 'StdGen' instance of 'RandomGen' has a 'genRange' of at least 30 bits.++The result of repeatedly using 'next' should be at least as statistically+robust as the /Minimal Standard Random Number Generator/ described by+["System.Random\#Park", "System.Random\#Carta"].+Until more is known about implementations of 'split', all we require is+that 'split' deliver generators that are (a) not identical and+(b) independently robust in the sense just given.++The 'Show' and 'Read' instances of 'StdGen' provide a primitive way to save the+state of a random number generator.+It is required that @'read' ('show' g) == g@.++In addition, 'reads' may be used to map an arbitrary string (not necessarily one+produced by 'show') onto a value of type 'StdGen'. In general, the 'Read'+instance of 'StdGen' has the following properties: ++* It guarantees to succeed on any string. ++* It guarantees to consume only a finite portion of the string. ++* Different argument strings are likely to result in different results.++-}++data StdGen + = StdGen Int32 Int32++instance RandomGen StdGen where+ next = stdNext+ genRange _ = stdRange++#ifdef ENABLE_SPLITTABLEGEN+instance SplittableGen StdGen where+#endif+ split = stdSplit++instance Show StdGen where+ showsPrec p (StdGen s1 s2) = + showsPrec p s1 . + showChar ' ' .+ showsPrec p s2++instance Read StdGen where+ readsPrec _p = \ r ->+ case try_read r of+ r'@[_] -> r'+ _ -> [stdFromString r] -- because it shouldn't ever fail.+ where + try_read r = do+ (s1, r1) <- readDec (dropWhile isSpace r)+ (s2, r2) <- readDec (dropWhile isSpace r1)+ return (StdGen s1 s2, r2)++{-+ If we cannot unravel the StdGen from a string, create+ one based on the string given.+-}+stdFromString :: String -> (StdGen, String)+stdFromString s = (mkStdGen num, rest)+ where (cs, rest) = splitAt 6 s+ num = foldl (\a x -> x + 3 * a) 1 (map ord cs)+++{- |+The function 'mkStdGen' provides an alternative way of producing an initial+generator, by mapping an 'Int' into a generator. Again, distinct arguments+should be likely to produce distinct generators.+-}+mkStdGen :: Int -> StdGen -- why not Integer ?+mkStdGen s = mkStdGen32 $ fromIntegral s++mkStdGen32 :: Int32 -> StdGen+mkStdGen32 sMaybeNegative = StdGen (s1+1) (s2+1)+ where+ -- We want a non-negative number, but we can't just take the abs+ -- of sMaybeNegative as -minBound == minBound.+ s = sMaybeNegative .&. maxBound+ (q, s1) = s `divMod` 2147483562+ s2 = q `mod` 2147483398++createStdGen :: Integer -> StdGen+createStdGen s = mkStdGen32 $ fromIntegral s++-- FIXME: 1/2/3 below should be ** (vs@30082002) XXX++{- |+With a source of random number supply in hand, the 'Random' class allows the+programmer to extract random values of a variety of types.++Minimal complete definition: 'randomR' and 'random'.++-}++class Random a where+ -- | Takes a range /(lo,hi)/ and a random number generator+ -- /g/, and returns a random value uniformly distributed in the closed+ -- interval /[lo,hi]/, together with a new generator. It is unspecified+ -- what happens if /lo>hi/. For continuous types there is no requirement+ -- that the values /lo/ and /hi/ are ever produced, but they may be,+ -- depending on the implementation and the interval.+ randomR :: RandomGen g => (a,a) -> g -> (a,g)++ -- | The same as 'randomR', but using a default range determined by the type:+ --+ -- * For bounded types (instances of 'Bounded', such as 'Char'),+ -- the range is normally the whole type.+ --+ -- * For fractional types, the range is normally the semi-closed interval+ -- @[0,1)@.+ --+ -- * For 'Integer', the range is (arbitrarily) the range of 'Int'.+ random :: RandomGen g => g -> (a, g)++ -- | Plural variant of 'randomR', producing an infinite list of+ -- random values instead of returning a new generator.+ randomRs :: RandomGen g => (a,a) -> g -> [a]+ randomRs ival g = x : randomRs ival g' where (x,g') = randomR ival g++ -- | Plural variant of 'random', producing an infinite list of+ -- random values instead of returning a new generator.+ randoms :: RandomGen g => g -> [a]+ randoms g = (\(x,g') -> x : randoms g') (random g)++ -- | A variant of 'randomR' that uses the global random number generator+ -- (see "System.Random#globalrng").+ randomRIO :: (a,a) -> IO a+ randomRIO range = getStdRandom (randomR range)++ -- | A variant of 'random' that uses the global random number generator+ -- (see "System.Random#globalrng").+ randomIO :: IO a+ randomIO = getStdRandom random+++instance Random Integer where+ randomR ival g = randomIvalInteger ival g+ random g = randomR (toInteger (minBound::Int), toInteger (maxBound::Int)) g++instance Random Int where randomR = randomIvalIntegral; random = randomBounded+instance Random Int8 where randomR = randomIvalIntegral; random = randomBounded+instance Random Int16 where randomR = randomIvalIntegral; random = randomBounded+instance Random Int32 where randomR = randomIvalIntegral; random = randomBounded+instance Random Int64 where randomR = randomIvalIntegral; random = randomBounded++#ifndef __NHC__+-- Word is a type synonym in nhc98.+instance Random Word where randomR = randomIvalIntegral; random = randomBounded+#endif+instance Random Word8 where randomR = randomIvalIntegral; random = randomBounded+instance Random Word16 where randomR = randomIvalIntegral; random = randomBounded+instance Random Word32 where randomR = randomIvalIntegral; random = randomBounded+instance Random Word64 where randomR = randomIvalIntegral; random = randomBounded++instance Random CChar where randomR = randomIvalIntegral; random = randomBounded+instance Random CSChar where randomR = randomIvalIntegral; random = randomBounded+instance Random CUChar where randomR = randomIvalIntegral; random = randomBounded+instance Random CShort where randomR = randomIvalIntegral; random = randomBounded+instance Random CUShort where randomR = randomIvalIntegral; random = randomBounded+instance Random CInt where randomR = randomIvalIntegral; random = randomBounded+instance Random CUInt where randomR = randomIvalIntegral; random = randomBounded+instance Random CLong where randomR = randomIvalIntegral; random = randomBounded+instance Random CULong where randomR = randomIvalIntegral; random = randomBounded+instance Random CPtrdiff where randomR = randomIvalIntegral; random = randomBounded+instance Random CSize where randomR = randomIvalIntegral; random = randomBounded+instance Random CWchar where randomR = randomIvalIntegral; random = randomBounded+instance Random CSigAtomic where randomR = randomIvalIntegral; random = randomBounded+instance Random CLLong where randomR = randomIvalIntegral; random = randomBounded+instance Random CULLong where randomR = randomIvalIntegral; random = randomBounded+instance Random CIntPtr where randomR = randomIvalIntegral; random = randomBounded+instance Random CUIntPtr where randomR = randomIvalIntegral; random = randomBounded+instance Random CIntMax where randomR = randomIvalIntegral; random = randomBounded+instance Random CUIntMax where randomR = randomIvalIntegral; random = randomBounded++instance Random Char where+ randomR (a,b) g = + case (randomIvalInteger (toInteger (ord a), toInteger (ord b)) g) of+ (x,g') -> (chr x, g')+ random g = randomR (minBound,maxBound) g++instance Random Bool where+ randomR (a,b) g = + case (randomIvalInteger (bool2Int a, bool2Int b) g) of+ (x, g') -> (int2Bool x, g')+ where+ bool2Int :: Bool -> Integer+ bool2Int False = 0+ bool2Int True = 1++ int2Bool :: Int -> Bool+ int2Bool 0 = False+ int2Bool _ = True++ random g = randomR (minBound,maxBound) g++{-# INLINE randomRFloating #-}+randomRFloating :: (Fractional a, Num a, Ord a, Random a, RandomGen g) => (a, a) -> g -> (a, g)+randomRFloating (l,h) g + | l>h = randomRFloating (h,l) g+ | otherwise = let (coef,g') = random g in + (2.0 * (0.5*l + coef * (0.5*h - 0.5*l)), g') -- avoid overflow++instance Random Double where+ randomR = randomRFloating+ random rng = + case random rng of + (x,rng') -> + -- We use 53 bits of randomness corresponding to the 53 bit significand:+ ((fromIntegral (mask53 .&. (x::Int64)) :: Double) + / fromIntegral twoto53, rng')+ where + twoto53 = (2::Int64) ^ (53::Int64)+ mask53 = twoto53 - 1+ +instance Random Float where+ randomR = randomRFloating+ random rng = + -- TODO: Faster to just use 'next' IF it generates enough bits of randomness. + case random rng of + (x,rng') -> + -- We use 24 bits of randomness corresponding to the 24 bit significand:+ ((fromIntegral (mask24 .&. (x::Int32)) :: Float) + / fromIntegral twoto24, rng')+ -- Note, encodeFloat is another option, but I'm not seeing slightly+ -- worse performance with the following [2011.06.25]:+-- (encodeFloat rand (-24), rng')+ where+ mask24 = twoto24 - 1+ twoto24 = (2::Int32) ^ (24::Int32)++-- CFloat/CDouble are basically the same as a Float/Double:+instance Random CFloat where+ randomR = randomRFloating+ random rng = case random rng of + (x,rng') -> (realToFrac (x::Float), rng')++instance Random CDouble where+ randomR = randomRFloating+ -- A MYSTERY:+ -- Presently, this is showing better performance than the Double instance:+ -- (And yet, if the Double instance uses randomFrac then its performance is much worse!)+ random = randomFrac+ -- random rng = case random rng of + -- (x,rng') -> (realToFrac (x::Double), rng')++mkStdRNG :: Integer -> IO StdGen+mkStdRNG o = do+ ct <- getCPUTime+ (sec, psec) <- getTime+ return (createStdGen (sec * 12345 + psec + ct + o))++randomBounded :: (RandomGen g, Random a, Bounded a) => g -> (a, g)+randomBounded = randomR (minBound, maxBound)++-- The two integer functions below take an [inclusive,inclusive] range.+randomIvalIntegral :: (RandomGen g, Integral a) => (a, a) -> g -> (a, g)+randomIvalIntegral (l,h) = randomIvalInteger (toInteger l, toInteger h)++randomIvalInteger :: (RandomGen g, Num a) => (Integer, Integer) -> g -> (a, g)+randomIvalInteger (l,h) rng+ | l > h = randomIvalInteger (h,l) rng+ | otherwise = case (f n 1 rng) of (v, rng') -> (fromInteger (l + v `mod` k), rng')+ where+ k = h - l + 1+ -- ERROR: b here (2^31-87) represents a baked-in assumption about genRange:+ b = 2147483561+ n = iLogBase b k++ -- Here we loop until we've generated enough randomness to cover the range:+ f 0 acc g = (acc, g)+ f n' acc g =+ let+ (x,g') = next g+ in+ -- We shift over the random bits generated thusfar (* b) and add in the new ones.+ f (n' - 1) (fromIntegral x + acc * b) g'++-- The continuous functions on the other hand take an [inclusive,exclusive) range.+randomFrac :: (RandomGen g, Fractional a) => g -> (a, g)+randomFrac = randomIvalDouble (0::Double,1) realToFrac++randomIvalDouble :: (RandomGen g, Fractional a) => (Double, Double) -> (Double -> a) -> g -> (a, g)+randomIvalDouble (l,h) fromDouble rng + | l > h = randomIvalDouble (h,l) fromDouble rng+ | otherwise = + case (randomIvalInteger (toInteger (minBound::Int32), toInteger (maxBound::Int32)) rng) of+ (x, rng') -> + let+ scaled_x = + fromDouble (0.5*l + 0.5*h) + -- previously (l+h)/2, overflowed+ fromDouble ((0.5*h - 0.5*l) / (0.5 * realToFrac int32Count)) * -- avoid overflow+ fromIntegral (x::Int32)+ in+ (scaled_x, rng')++int32Count :: Integer+int32Count = toInteger (maxBound::Int32) - toInteger (minBound::Int32) + 1++-- Perform an expensive logarithm on arbitrary-size integers by repeated division.+-- +-- (NOTE: This actually returns ceiling(log(i) base b) except with an+-- incorrect result at iLogBase b b = 2.)+iLogBase :: Integer -> Integer -> Integer+iLogBase b i = if i < b then 1 else 1 + iLogBase b (i `div` b)++stdRange :: (Int,Int)+stdRange = (0, 2147483562)++stdNext :: StdGen -> (Int, StdGen)+-- Returns values in the range stdRange+stdNext (StdGen s1 s2) = (fromIntegral z', StdGen s1'' s2'')+ where z' = if z < 1 then z + 2147483562 else z+ z = s1'' - s2''++ k = s1 `quot` 53668+ s1' = 40014 * (s1 - k * 53668) - k * 12211+ s1'' = if s1' < 0 then s1' + 2147483563 else s1'+ + k' = s2 `quot` 52774+ s2' = 40692 * (s2 - k' * 52774) - k' * 3791+ s2'' = if s2' < 0 then s2' + 2147483399 else s2'++stdSplit :: StdGen -> (StdGen, StdGen)+stdSplit std@(StdGen s1 s2)+ = (left, right)+ where+ -- no statistical foundation for this!+ left = StdGen new_s1 t2+ right = StdGen t1 new_s2++ new_s1 | s1 == 2147483562 = 1+ | otherwise = s1 + 1++ new_s2 | s2 == 1 = 2147483398+ | otherwise = s2 - 1++ StdGen t1 t2 = snd (next std)++-- The global random number generator++{- $globalrng #globalrng#++There is a single, implicit, global random number generator of type+'StdGen', held in some global variable maintained by the 'IO' monad. It is+initialised automatically in some system-dependent fashion, for example, by+using the time of day, or Linux's kernel random number generator. To get+deterministic behaviour, use 'setStdGen'.+-}++-- |Sets the global random number generator.+setStdGen :: StdGen -> IO ()+setStdGen sgen = writeIORef theStdGen sgen++-- |Gets the global random number generator.+getStdGen :: IO StdGen+getStdGen = readIORef theStdGen++theStdGen :: IORef StdGen+theStdGen = unsafePerformIO $ do+ rng <- mkStdRNG 0+ newIORef rng++-- |Applies 'split' to the current global random generator,+-- updates it with one of the results, and returns the other.+newStdGen :: IO StdGen+newStdGen = atomicModifyIORef theStdGen split++{- |Uses the supplied function to get a value from the current global+random generator, and updates the global generator with the new generator+returned by the function. For example, @rollDice@ gets a random integer+between 1 and 6:++> rollDice :: IO Int+> rollDice = getStdRandom (randomR (1,6))++-}++getStdRandom :: (StdGen -> (a,StdGen)) -> IO a+getStdRandom f = atomicModifyIORef theStdGen (swap . f)+ where swap (v,g) = (g,v)++{- $references++1. FW #Burton# Burton and RL Page, /Distributed random number generation/,+Journal of Functional Programming, 2(2):203-212, April 1992.++2. SK #Park# Park, and KW Miller, /Random number generators -+good ones are hard to find/, Comm ACM 31(10), Oct 1988, pp1192-1201.++3. DG #Carta# Carta, /Two fast implementations of the minimal standard+random number generator/, Comm ACM, 33(1), Jan 1990, pp87-88.++4. P #Hellekalek# Hellekalek, /Don\'t trust parallel Monte Carlo/,+Department of Mathematics, University of Salzburg,+<http://random.mat.sbg.ac.at/~peter/pads98.ps>, 1998.++5. Pierre #LEcuyer# L'Ecuyer, /Efficient and portable combined random+number generators/, Comm ACM, 31(6), Jun 1988, pp742-749.++The Web site <http://random.mat.sbg.ac.at/> is a great source of information.++-}
+ Text/Parsec.hs view
@@ -0,0 +1,36 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-----------------------------------------------------------------------------++module Text.Parsec+ ( module Text.Parsec.Prim+ , module Text.Parsec.Char+ , module Text.Parsec.Combinator+ , module Text.Parsec.String+ , module Text.Parsec.ByteString+ , module Text.Parsec.ByteString.Lazy+ , ParseError+ , errorPos+ , SourcePos+ , SourceName, Line, Column+ , sourceName, sourceLine, sourceColumn+ , incSourceLine, incSourceColumn+ , setSourceLine, setSourceColumn, setSourceName+ ) where++import Text.Parsec.Pos+import Text.Parsec.Error+import Text.Parsec.Prim+import Text.Parsec.Char+import Text.Parsec.Combinator+import Text.Parsec.String hiding ( Parser, GenParser, parseFromFile )+import Text.Parsec.ByteString hiding ( Parser, GenParser, parseFromFile )+import Text.Parsec.ByteString.Lazy hiding ( Parser, GenParser, parseFromFile )
+ Text/Parsec/ByteString.hs view
@@ -0,0 +1,46 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.ByteString+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Make strict ByteStrings an instance of 'Stream' with 'Char' token type.+--+-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.Parsec.ByteString+ ( Parser, GenParser, parseFromFile+ ) where++import Text.Parsec.Error+import Text.Parsec.Prim++import qualified Data.ByteString.Char8 as C++instance (Monad m) => Stream C.ByteString m Char where+ uncons = return . C.uncons++type Parser = Parsec C.ByteString ()+type GenParser t st = Parsec C.ByteString st++-- | @parseFromFile p filePath@ runs a strict bytestring parser @p@ on the+-- input read from @filePath@ using 'ByteString.Char8.readFile'. Returns either a 'ParseError'+-- ('Left') or a value of type @a@ ('Right').+--+-- > main = do{ result <- parseFromFile numbers "digits.txt"+-- > ; case result of+-- > Left err -> print err+-- > Right xs -> print (sum xs)+-- > }++parseFromFile :: Parser a -> String -> IO (Either ParseError a)+parseFromFile p fname+ = do input <- C.readFile fname+ return (runP p () fname input)
+ Text/Parsec/ByteString/Lazy.hs view
@@ -0,0 +1,45 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.ByteString.Lazy+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+--+-- Make lazy ByteStrings an instance of 'Stream' with 'Char' token type.+--+-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.Parsec.ByteString.Lazy+ ( Parser, GenParser, parseFromFile+ ) where++import Text.Parsec.Error+import Text.Parsec.Prim++import qualified Data.ByteString.Lazy.Char8 as C++instance (Monad m) => Stream C.ByteString m Char where+ uncons = return . C.uncons++type Parser = Parsec C.ByteString ()+type GenParser t st = Parsec C.ByteString st++-- | @parseFromFile p filePath@ runs a lazy bytestring parser @p@ on the+-- input read from @filePath@ using 'ByteString.Lazy.Char8.readFile'. Returns either a 'ParseError'+-- ('Left') or a value of type @a@ ('Right').+--+-- > main = do{ result <- parseFromFile numbers "digits.txt"+-- > ; case result of+-- > Left err -> print err+-- > Right xs -> print (sum xs)+-- > }+parseFromFile :: Parser a -> String -> IO (Either ParseError a)+parseFromFile p fname+ = do input <- C.readFile fname+ return (runP p () fname input)
+ Text/Parsec/Char.hs view
@@ -0,0 +1,135 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Char+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Commonly used character parsers.+-- +-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleContexts #-}++module Text.Parsec.Char where++import Data.Char+import Text.Parsec.Pos+import Text.Parsec.Prim++-- | @oneOf cs@ succeeds if the current character is in the supplied+-- list of characters @cs@. Returns the parsed character. See also+-- 'satisfy'.+-- +-- > vowel = oneOf "aeiou"++oneOf :: (Stream s m Char) => [Char] -> ParsecT s u m Char+oneOf cs = satisfy (\c -> elem c cs)++-- | As the dual of 'oneOf', @noneOf cs@ succeeds if the current+-- character /not/ in the supplied list of characters @cs@. Returns the+-- parsed character.+--+-- > consonant = noneOf "aeiou"++noneOf :: (Stream s m Char) => [Char] -> ParsecT s u m Char+noneOf cs = satisfy (\c -> not (elem c cs))++-- | Skips /zero/ or more white space characters. See also 'skipMany'.++spaces :: (Stream s m Char) => ParsecT s u m ()+spaces = skipMany space <?> "white space"++-- | Parses a white space character (any character which satisfies 'isSpace')+-- Returns the parsed character. ++space :: (Stream s m Char) => ParsecT s u m Char+space = satisfy isSpace <?> "space"++-- | Parses a newline character (\'\\n\'). Returns a newline character. ++newline :: (Stream s m Char) => ParsecT s u m Char+newline = char '\n' <?> "new-line"++-- | Parses a tab character (\'\\t\'). Returns a tab character. ++tab :: (Stream s m Char) => ParsecT s u m Char+tab = char '\t' <?> "tab"++-- | Parses an upper case letter (a character between \'A\' and \'Z\').+-- Returns the parsed character. ++upper :: (Stream s m Char) => ParsecT s u m Char+upper = satisfy isUpper <?> "uppercase letter"++-- | Parses a lower case character (a character between \'a\' and \'z\').+-- Returns the parsed character. ++lower :: (Stream s m Char) => ParsecT s u m Char+lower = satisfy isLower <?> "lowercase letter"++-- | Parses a letter or digit (a character between \'0\' and \'9\').+-- Returns the parsed character. ++alphaNum :: (Stream s m Char => ParsecT s u m Char)+alphaNum = satisfy isAlphaNum <?> "letter or digit"++-- | Parses a letter (an upper case or lower case character). Returns the+-- parsed character. ++letter :: (Stream s m Char) => ParsecT s u m Char+letter = satisfy isAlpha <?> "letter"++-- | Parses a digit. Returns the parsed character. ++digit :: (Stream s m Char) => ParsecT s u m Char+digit = satisfy isDigit <?> "digit"++-- | Parses a hexadecimal digit (a digit or a letter between \'a\' and+-- \'f\' or \'A\' and \'F\'). Returns the parsed character. ++hexDigit :: (Stream s m Char) => ParsecT s u m Char+hexDigit = satisfy isHexDigit <?> "hexadecimal digit"++-- | Parses an octal digit (a character between \'0\' and \'7\'). Returns+-- the parsed character. ++octDigit :: (Stream s m Char) => ParsecT s u m Char+octDigit = satisfy isOctDigit <?> "octal digit"++-- | @char c@ parses a single character @c@. Returns the parsed+-- character (i.e. @c@).+--+-- > semiColon = char ';'++char :: (Stream s m Char) => Char -> ParsecT s u m Char+char c = satisfy (==c) <?> show [c]++-- | This parser succeeds for any character. Returns the parsed character. ++anyChar :: (Stream s m Char) => ParsecT s u m Char+anyChar = satisfy (const True)++-- | The parser @satisfy f@ succeeds for any character for which the+-- supplied function @f@ returns 'True'. Returns the character that is+-- actually parsed.++-- > digit = satisfy isDigit+-- > oneOf cs = satisfy (\c -> c `elem` cs)++satisfy :: (Stream s m Char) => (Char -> Bool) -> ParsecT s u m Char+satisfy f = tokenPrim (\c -> show [c])+ (\pos c _cs -> updatePosChar pos c)+ (\c -> if f c then Just c else Nothing)++-- | @string s@ parses a sequence of characters given by @s@. Returns+-- the parsed string (i.e. @s@).+--+-- > divOrMod = string "div" +-- > <|> string "mod"++string :: (Stream s m Char) => String -> ParsecT s u m String+string s = tokens show updatePosString s
+ Text/Parsec/Combinator.hs view
@@ -0,0 +1,277 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Combinator+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Commonly used generic combinators+-- +-----------------------------------------------------------------------------++module Text.Parsec.Combinator+ ( choice+ , count+ , between+ , option, optionMaybe, optional+ , skipMany1+ , many1+ , sepBy, sepBy1+ , endBy, endBy1+ , sepEndBy, sepEndBy1+ , chainl, chainl1+ , chainr, chainr1+ , eof, notFollowedBy+ -- tricky combinators+ , manyTill, lookAhead, anyToken+ ) where++import Control.Monad+import Text.Parsec.Prim++-- | @choice ps@ tries to apply the parsers in the list @ps@ in order,+-- until one of them succeeds. Returns the value of the succeeding+-- parser.++choice :: (Stream s m t) => [ParsecT s u m a] -> ParsecT s u m a+choice ps = foldr (<|>) mzero ps++-- | @option x p@ tries to apply parser @p@. If @p@ fails without+-- consuming input, it returns the value @x@, otherwise the value+-- returned by @p@.+--+-- > priority = option 0 (do{ d <- digit+-- > ; return (digitToInt d) +-- > })++option :: (Stream s m t) => a -> ParsecT s u m a -> ParsecT s u m a+option x p = p <|> return x++-- | @optionMaybe p@ tries to apply parser @p@. If @p@ fails without+-- consuming input, it return 'Nothing', otherwise it returns+-- 'Just' the value returned by @p@.++optionMaybe :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m (Maybe a)+optionMaybe p = option Nothing (liftM Just p)++-- | @optional p@ tries to apply parser @p@. It will parse @p@ or nothing.+-- It only fails if @p@ fails after consuming input. It discards the result+-- of @p@.++optional :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m ()+optional p = do{ p; return ()} <|> return ()++-- | @between open close p@ parses @open@, followed by @p@ and @close@.+-- Returns the value returned by @p@.+--+-- > braces = between (symbol "{") (symbol "}")++between :: (Stream s m t) => ParsecT s u m open -> ParsecT s u m close+ -> ParsecT s u m a -> ParsecT s u m a+between open close p+ = do{ open; x <- p; close; return x }++-- | @skipMany1 p@ applies the parser @p@ /one/ or more times, skipping+-- its result. ++skipMany1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m ()+skipMany1 p = do{ p; skipMany p }+{-+skipMany p = scan+ where+ scan = do{ p; scan } <|> return ()+-}++-- | @many1 p@ applies the parser @p@ /one/ or more times. Returns a+-- list of the returned values of @p@.+--+-- > word = many1 letter++many1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m [a]+many1 p = do{ x <- p; xs <- many p; return (x:xs) }+{-+many p = scan id+ where+ scan f = do{ x <- p+ ; scan (\tail -> f (x:tail))+ }+ <|> return (f [])+-}+++-- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated+-- by @sep@. Returns a list of values returned by @p@.+--+-- > commaSep p = p `sepBy` (symbol ",")++sepBy :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+sepBy p sep = sepBy1 p sep <|> return []++-- | @sepBy1 p sep@ parses /one/ or more occurrences of @p@, separated+-- by @sep@. Returns a list of values returned by @p@. ++sepBy1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+sepBy1 p sep = do{ x <- p+ ; xs <- many (sep >> p)+ ; return (x:xs)+ }+++-- | @sepEndBy1 p sep@ parses /one/ or more occurrences of @p@,+-- separated and optionally ended by @sep@. Returns a list of values+-- returned by @p@. ++sepEndBy1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+sepEndBy1 p sep = do{ x <- p+ ; do{ sep+ ; xs <- sepEndBy p sep+ ; return (x:xs)+ }+ <|> return [x]+ }++-- | @sepEndBy p sep@ parses /zero/ or more occurrences of @p@,+-- separated and optionally ended by @sep@, ie. haskell style+-- statements. Returns a list of values returned by @p@.+--+-- > haskellStatements = haskellStatement `sepEndBy` semi++sepEndBy :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+sepEndBy p sep = sepEndBy1 p sep <|> return []+++-- | @endBy1 p sep@ parses /one/ or more occurrences of @p@, seperated+-- and ended by @sep@. Returns a list of values returned by @p@. ++endBy1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+endBy1 p sep = many1 (do{ x <- p; sep; return x })++-- | @endBy p sep@ parses /zero/ or more occurrences of @p@, seperated+-- and ended by @sep@. Returns a list of values returned by @p@.+--+-- > cStatements = cStatement `endBy` semi++endBy :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]+endBy p sep = many (do{ x <- p; sep; return x })++-- | @count n p@ parses @n@ occurrences of @p@. If @n@ is smaller or+-- equal to zero, the parser equals to @return []@. Returns a list of+-- @n@ values returned by @p@. ++count :: (Stream s m t) => Int -> ParsecT s u m a -> ParsecT s u m [a]+count n p | n <= 0 = return []+ | otherwise = sequence (replicate n p)++-- | @chainr p op x@ parser /zero/ or more occurrences of @p@,+-- separated by @op@ Returns a value obtained by a /right/ associative+-- application of all functions returned by @op@ to the values returned+-- by @p@. If there are no occurrences of @p@, the value @x@ is+-- returned.++chainr :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m (a -> a -> a) -> a -> ParsecT s u m a+chainr p op x = chainr1 p op <|> return x++-- | @chainl p op x@ parser /zero/ or more occurrences of @p@,+-- separated by @op@. Returns a value obtained by a /left/ associative+-- application of all functions returned by @op@ to the values returned+-- by @p@. If there are zero occurrences of @p@, the value @x@ is+-- returned.++chainl :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m (a -> a -> a) -> a -> ParsecT s u m a+chainl p op x = chainl1 p op <|> return x++-- | @chainl1 p op x@ parser /one/ or more occurrences of @p@,+-- separated by @op@ Returns a value obtained by a /left/ associative+-- application of all functions returned by @op@ to the values returned+-- by @p@. . This parser can for example be used to eliminate left+-- recursion which typically occurs in expression grammars.+--+-- > expr = term `chainl1` addop+-- > term = factor `chainl1` mulop+-- > factor = parens expr <|> integer+-- >+-- > mulop = do{ symbol "*"; return (*) }+-- > <|> do{ symbol "/"; return (div) }+-- >+-- > addop = do{ symbol "+"; return (+) }+-- > <|> do{ symbol "-"; return (-) }++chainl1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m (a -> a -> a) -> ParsecT s u m a+chainl1 p op = do{ x <- p; rest x }+ where+ rest x = do{ f <- op+ ; y <- p+ ; rest (f x y)+ }+ <|> return x++-- | @chainr1 p op x@ parser /one/ or more occurrences of |p|,+-- separated by @op@ Returns a value obtained by a /right/ associative+-- application of all functions returned by @op@ to the values returned+-- by @p@.++chainr1 :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m (a -> a -> a) -> ParsecT s u m a+chainr1 p op = scan+ where+ scan = do{ x <- p; rest x }++ rest x = do{ f <- op+ ; y <- scan+ ; return (f x y)+ }+ <|> return x++-----------------------------------------------------------+-- Tricky combinators+-----------------------------------------------------------+-- | The parser @anyToken@ accepts any kind of token. It is for example+-- used to implement 'eof'. Returns the accepted token. ++anyToken :: (Stream s m t, Show t) => ParsecT s u m t+anyToken = tokenPrim show (\pos _tok _toks -> pos) Just++-- | This parser only succeeds at the end of the input. This is not a+-- primitive parser but it is defined using 'notFollowedBy'.+--+-- > eof = notFollowedBy anyToken <?> "end of input"++eof :: (Stream s m t, Show t) => ParsecT s u m ()+eof = notFollowedBy anyToken <?> "end of input"++-- | @notFollowedBy p@ only succeeds when parser @p@ fails. This parser+-- does not consume any input. This parser can be used to implement the+-- \'longest match\' rule. For example, when recognizing keywords (for+-- example @let@), we want to make sure that a keyword is not followed+-- by a legal identifier character, in which case the keyword is+-- actually an identifier (for example @lets@). We can program this+-- behaviour as follows:+--+-- > keywordLet = try (do{ string "let"+-- > ; notFollowedBy alphaNum+-- > })++notFollowedBy :: (Stream s m t, Show a) => ParsecT s u m a -> ParsecT s u m ()+notFollowedBy p = try (do{ c <- try p; unexpected (show c) }+ <|> return ()+ )++-- | @manyTill p end@ applies parser @p@ /zero/ or more times until+-- parser @end@ succeeds. Returns the list of values returned by @p@.+-- This parser can be used to scan comments:+--+-- > simpleComment = do{ string "<!--"+-- > ; manyTill anyChar (try (string "-->"))+-- > }+--+-- Note the overlapping parsers @anyChar@ and @string \"-->\"@, and+-- therefore the use of the 'try' combinator.++manyTill :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m end -> ParsecT s u m [a]+manyTill p end = scan+ where+ scan = do{ end; return [] }+ <|>+ do{ x <- p; xs <- scan; return (x:xs) }
+ Text/Parsec/Error.hs view
@@ -0,0 +1,201 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Error+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parse errors+-- +-----------------------------------------------------------------------------++module Text.Parsec.Error+ ( Message ( SysUnExpect, UnExpect, Expect, Message )+ , messageString+ , ParseError, errorPos, errorMessages, errorIsUnknown+ , showErrorMessages+ , newErrorMessage, newErrorUnknown+ , addErrorMessage, setErrorPos, setErrorMessage+ , mergeError+ ) where++import Data.List ( nub, sort )++import Text.Parsec.Pos++-- | This abstract data type represents parse error messages. There are+-- four kinds of messages:+--+-- > data Message = SysUnExpect String+-- > | UnExpect String+-- > | Expect String+-- > | Message String+-- +-- The fine distinction between different kinds of parse errors allows+-- the system to generate quite good error messages for the user. It+-- also allows error messages that are formatted in different+-- languages. Each kind of message is generated by different combinators:+--+-- * A 'SysUnExpect' message is automatically generated by the+-- 'Text.Parsec.Combinator.satisfy' combinator. The argument is the+-- unexpected input.+--+-- * A 'UnExpect' message is generated by the 'Text.Parsec.Prim.unexpected'+-- combinator. The argument describes the+-- unexpected item.+--+-- * A 'Expect' message is generated by the 'Text.Parsec.Prim.<?>'+-- combinator. The argument describes the expected item.+--+-- * A 'Message' message is generated by the 'fail'+-- combinator. The argument is some general parser message. ++data Message = SysUnExpect !String -- @ library generated unexpect+ | UnExpect !String -- @ unexpected something+ | Expect !String -- @ expecting something+ | Message !String -- @ raw message++instance Enum Message where+ fromEnum (SysUnExpect _) = 0+ fromEnum (UnExpect _) = 1+ fromEnum (Expect _) = 2+ fromEnum (Message _) = 3+ toEnum _ = error "toEnum is undefined for Message"++-- < Return 'True' only when 'compare' would return 'EQ'.++instance Eq Message where++ m1 == m2 = fromEnum m1 == fromEnum m2++-- < Compares two error messages without looking at their content. Only+-- the constructors are compared where:+-- +-- > 'SysUnExpect' < 'UnExpect' < 'Expect' < 'Message'++instance Ord Message where+ compare msg1 msg2 = compare (fromEnum msg1) (fromEnum msg2)++-- | Extract the message string from an error message ++messageString :: Message -> String+messageString (SysUnExpect s) = s+messageString (UnExpect s) = s+messageString (Expect s) = s+messageString (Message s) = s++-- | The abstract data type @ParseError@ represents parse errors. It+-- provides the source position ('SourcePos') of the error+-- and a list of error messages ('Message'). A @ParseError@+-- can be returned by the function 'Text.Parsec.Prim.parse'. @ParseError@ is an+-- instance of the 'Show' class. ++data ParseError = ParseError !SourcePos [Message]++-- | Extracts the source position from the parse error++errorPos :: ParseError -> SourcePos+errorPos (ParseError pos _msgs)+ = pos++-- | Extracts the list of error messages from the parse error++errorMessages :: ParseError -> [Message]+errorMessages (ParseError _pos msgs)+ = sort msgs++errorIsUnknown :: ParseError -> Bool+errorIsUnknown (ParseError _pos msgs)+ = null msgs++-- < Create parse errors++newErrorUnknown :: SourcePos -> ParseError+newErrorUnknown pos+ = ParseError pos []++newErrorMessage :: Message -> SourcePos -> ParseError+newErrorMessage msg pos+ = ParseError pos [msg]++addErrorMessage :: Message -> ParseError -> ParseError+addErrorMessage msg (ParseError pos msgs)+ = ParseError pos (msg:msgs)++setErrorPos :: SourcePos -> ParseError -> ParseError+setErrorPos pos (ParseError _ msgs)+ = ParseError pos msgs++setErrorMessage :: Message -> ParseError -> ParseError+setErrorMessage msg (ParseError pos msgs)+ = ParseError pos (msg : filter (msg /=) msgs)++mergeError :: ParseError -> ParseError -> ParseError+mergeError (ParseError pos1 msgs1) (ParseError pos2 msgs2)+ = case pos1 `compare` pos2 of+ -- select the longest match+ EQ -> ParseError pos1 (msgs1 ++ msgs2)+ GT -> ParseError pos1 msgs1+ LT -> ParseError pos2 msgs2++instance Show ParseError where+ show err+ = show (errorPos err) ++ ":" +++ showErrorMessages "or" "unknown parse error"+ "expecting" "unexpected" "end of input"+ (errorMessages err)++-- Language independent show function++-- TODO+-- < The standard function for showing error messages. Formats a list of+-- error messages in English. This function is used in the |Show|+-- instance of |ParseError <#ParseError>|. The resulting string will be+-- formatted like:+--+-- |unexpected /{The first UnExpect or a SysUnExpect message}/;+-- expecting /{comma separated list of Expect messages}/;+-- /{comma separated list of Message messages}/++showErrorMessages ::+ String -> String -> String -> String -> String -> [Message] -> String+showErrorMessages msgOr msgUnknown msgExpecting msgUnExpected msgEndOfInput msgs+ | null msgs = msgUnknown+ | otherwise = concat $ map ("\n"++) $ clean $+ [showSysUnExpect,showUnExpect,showExpect,showMessages]+ where+ (sysUnExpect,msgs1) = span ((SysUnExpect "") ==) msgs+ (unExpect,msgs2) = span ((UnExpect "") ==) msgs1+ (expect,messages) = span ((Expect "") ==) msgs2++ showExpect = showMany msgExpecting expect+ showUnExpect = showMany msgUnExpected unExpect+ showSysUnExpect | not (null unExpect) ||+ null sysUnExpect = ""+ | null firstMsg = msgUnExpected ++ " " ++ msgEndOfInput+ | otherwise = msgUnExpected ++ " " ++ firstMsg+ where+ firstMsg = messageString (head sysUnExpect)++ showMessages = showMany "" messages++ -- helpers+ showMany pre msgs = case clean (map messageString msgs) of+ [] -> ""+ ms | null pre -> commasOr ms+ | otherwise -> pre ++ " " ++ commasOr ms++ commasOr [] = ""+ commasOr [m] = m+ commasOr ms = commaSep (init ms) ++ " " ++ msgOr ++ " " ++ last ms++ commaSep = seperate ", " . clean++ seperate _ [] = ""+ seperate _ [m] = m+ seperate sep (m:ms) = m ++ sep ++ seperate sep ms++ clean = nub . filter (not . null)
+ Text/Parsec/Expr.hs view
@@ -0,0 +1,166 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Expr+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : non-portable+-- +-- A helper module to parse \"expressions\".+-- Builds a parser given a table of operators and associativities.+-- +-----------------------------------------------------------------------------++module Text.Parsec.Expr+ ( Assoc(..), Operator(..), OperatorTable+ , buildExpressionParser+ ) where++import Text.Parsec.Prim+import Text.Parsec.Combinator++-----------------------------------------------------------+-- Assoc and OperatorTable+-----------------------------------------------------------++-- | This data type specifies the associativity of operators: left, right+-- or none.++data Assoc = AssocNone+ | AssocLeft+ | AssocRight++-- | This data type specifies operators that work on values of type @a@.+-- An operator is either binary infix or unary prefix or postfix. A+-- binary operator has also an associated associativity.++data Operator s u m a = Infix (ParsecT s u m (a -> a -> a)) Assoc+ | Prefix (ParsecT s u m (a -> a))+ | Postfix (ParsecT s u m (a -> a))++-- | An @OperatorTable s u m a@ is a list of @Operator s u m a@+-- lists. The list is ordered in descending+-- precedence. All operators in one list have the same precedence (but+-- may have a different associativity).++type OperatorTable s u m a = [[Operator s u m a]]++-----------------------------------------------------------+-- Convert an OperatorTable and basic term parser into+-- a full fledged expression parser+-----------------------------------------------------------++-- | @buildExpressionParser table term@ builds an expression parser for+-- terms @term@ with operators from @table@, taking the associativity+-- and precedence specified in @table@ into account. Prefix and postfix+-- operators of the same precedence can only occur once (i.e. @--2@ is+-- not allowed if @-@ is prefix negate). Prefix and postfix operators+-- of the same precedence associate to the left (i.e. if @++@ is+-- postfix increment, than @-2++@ equals @-1@, not @-3@).+--+-- The @buildExpressionParser@ takes care of all the complexity+-- involved in building expression parser. Here is an example of an+-- expression parser that handles prefix signs, postfix increment and+-- basic arithmetic.+--+-- > expr = buildExpressionParser table term+-- > <?> "expression"+-- >+-- > term = parens expr +-- > <|> natural+-- > <?> "simple expression"+-- >+-- > table = [ [prefix "-" negate, prefix "+" id ]+-- > , [postfix "++" (+1)]+-- > , [binary "*" (*) AssocLeft, binary "/" (div) AssocLeft ]+-- > , [binary "+" (+) AssocLeft, binary "-" (-) AssocLeft ]+-- > ]+-- > +-- > binary name fun assoc = Infix (do{ reservedOp name; return fun }) assoc+-- > prefix name fun = Prefix (do{ reservedOp name; return fun })+-- > postfix name fun = Postfix (do{ reservedOp name; return fun })++buildExpressionParser :: (Stream s m t)+ => OperatorTable s u m a+ -> ParsecT s u m a+ -> ParsecT s u m a+buildExpressionParser operators simpleExpr+ = foldl (makeParser) simpleExpr operators+ where+ makeParser term ops+ = let (rassoc,lassoc,nassoc+ ,prefix,postfix) = foldr splitOp ([],[],[],[],[]) ops++ rassocOp = choice rassoc+ lassocOp = choice lassoc+ nassocOp = choice nassoc+ prefixOp = choice prefix <?> ""+ postfixOp = choice postfix <?> ""++ ambigious assoc op= try $+ do{ op; fail ("ambiguous use of a " ++ assoc+ ++ " associative operator")+ }++ ambigiousRight = ambigious "right" rassocOp+ ambigiousLeft = ambigious "left" lassocOp+ ambigiousNon = ambigious "non" nassocOp++ termP = do{ pre <- prefixP+ ; x <- term+ ; post <- postfixP+ ; return (post (pre x))+ }++ postfixP = postfixOp <|> return id++ prefixP = prefixOp <|> return id++ rassocP x = do{ f <- rassocOp+ ; y <- do{ z <- termP; rassocP1 z }+ ; return (f x y)+ }+ <|> ambigiousLeft+ <|> ambigiousNon+ -- <|> return x++ rassocP1 x = rassocP x <|> return x++ lassocP x = do{ f <- lassocOp+ ; y <- termP+ ; lassocP1 (f x y)+ }+ <|> ambigiousRight+ <|> ambigiousNon+ -- <|> return x++ lassocP1 x = lassocP x <|> return x++ nassocP x = do{ f <- nassocOp+ ; y <- termP+ ; ambigiousRight+ <|> ambigiousLeft+ <|> ambigiousNon+ <|> return (f x y)+ }+ -- <|> return x++ in do{ x <- termP+ ; rassocP x <|> lassocP x <|> nassocP x <|> return x+ <?> "operator"+ }+++ splitOp (Infix op assoc) (rassoc,lassoc,nassoc,prefix,postfix)+ = case assoc of+ AssocNone -> (rassoc,lassoc,op:nassoc,prefix,postfix)+ AssocLeft -> (rassoc,op:lassoc,nassoc,prefix,postfix)+ AssocRight -> (op:rassoc,lassoc,nassoc,prefix,postfix)++ splitOp (Prefix op) (rassoc,lassoc,nassoc,prefix,postfix)+ = (rassoc,lassoc,nassoc,op:prefix,postfix)++ splitOp (Postfix op) (rassoc,lassoc,nassoc,prefix,postfix)+ = (rassoc,lassoc,nassoc,prefix,op:postfix)
+ Text/Parsec/Language.hs view
@@ -0,0 +1,150 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Language+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : non-portable (uses non-portable module Text.Parsec.Token)+--+-- A helper module that defines some language definitions that can be used+-- to instantiate a token parser (see "Text.Parsec.Token").+-- +-----------------------------------------------------------------------------++module Text.Parsec.Language+ ( haskellDef, haskell+ , mondrianDef, mondrian+ , emptyDef+ , haskellStyle+ , javaStyle+ , LanguageDef+ , GenLanguageDef+ ) where++import Text.Parsec+import Text.Parsec.Token++-----------------------------------------------------------+-- Styles: haskellStyle, javaStyle+-----------------------------------------------------------++-- | This is a minimal token definition for Haskell style languages. It+-- defines the style of comments, valid identifiers and case+-- sensitivity. It does not define any reserved words or operators.++haskellStyle :: LanguageDef st+haskellStyle = emptyDef+ { commentStart = "{-"+ , commentEnd = "-}"+ , commentLine = "--"+ , nestedComments = True+ , identStart = letter+ , identLetter = alphaNum <|> oneOf "_'"+ , opStart = opLetter haskellStyle+ , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~"+ , reservedOpNames= []+ , reservedNames = []+ , caseSensitive = True+ }++-- | This is a minimal token definition for Java style languages. It+-- defines the style of comments, valid identifiers and case+-- sensitivity. It does not define any reserved words or operators.++javaStyle :: LanguageDef st+javaStyle = emptyDef+ { commentStart = "/*"+ , commentEnd = "*/"+ , commentLine = "//"+ , nestedComments = True+ , identStart = letter+ , identLetter = alphaNum <|> oneOf "_'"+ , reservedNames = []+ , reservedOpNames= []+ , caseSensitive = False+ }++-----------------------------------------------------------+-- minimal language definition+--------------------------------------------------------++-- TODO: This seems wrong+-- < This is the most minimal token definition. It is recommended to use+-- this definition as the basis for other definitions. @emptyDef@ has+-- no reserved names or operators, is case sensitive and doesn't accept+-- comments, identifiers or operators.++emptyDef :: LanguageDef st+emptyDef = LanguageDef+ { commentStart = ""+ , commentEnd = ""+ , commentLine = ""+ , nestedComments = True+ , identStart = letter <|> char '_'+ , identLetter = alphaNum <|> oneOf "_'"+ , opStart = opLetter emptyDef+ , opLetter = oneOf ":!#$%&*+./<=>?@\\^|-~"+ , reservedOpNames= []+ , reservedNames = []+ , caseSensitive = True+ }++++-----------------------------------------------------------+-- Haskell+-----------------------------------------------------------++-- | A lexer for the haskell language.++haskell :: TokenParser st+haskell = makeTokenParser haskellDef++-- | The language definition for the Haskell language.++haskellDef :: LanguageDef st+haskellDef = haskell98Def+ { identLetter = identLetter haskell98Def <|> char '#'+ , reservedNames = reservedNames haskell98Def +++ ["foreign","import","export","primitive"+ ,"_ccall_","_casm_"+ ,"forall"+ ]+ }++-- | The language definition for the language Haskell98.++haskell98Def :: LanguageDef st+haskell98Def = haskellStyle+ { reservedOpNames= ["::","..","=","\\","|","<-","->","@","~","=>"]+ , reservedNames = ["let","in","case","of","if","then","else",+ "data","type",+ "class","default","deriving","do","import",+ "infix","infixl","infixr","instance","module",+ "newtype","where",+ "primitive"+ -- "as","qualified","hiding"+ ]+ }+++-----------------------------------------------------------+-- Mondrian+-----------------------------------------------------------++-- | A lexer for the mondrian language.++mondrian :: TokenParser st+mondrian = makeTokenParser mondrianDef++-- | The language definition for the language Mondrian.++mondrianDef :: LanguageDef st+mondrianDef = javaStyle+ { reservedNames = [ "case", "class", "default", "extends"+ , "import", "in", "let", "new", "of", "package"+ ]+ , caseSensitive = True+ }
+ Text/Parsec/Perm.hs view
@@ -0,0 +1,181 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Perm+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the file libraries/parsec/LICENSE)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : non-portable (uses existentially quantified data constructors)+-- +-- This module implements permutation parsers. The algorithm used+-- is fairly complex since we push the type system to its limits :-)+-- The algorithm is described in:+-- +-- /Parsing Permutation Phrases,/+-- by Arthur Baars, Andres Loh and Doaitse Swierstra.+-- Published as a functional pearl at the Haskell Workshop 2001.+-- +-----------------------------------------------------------------------------++{-# LANGUAGE ExistentialQuantification #-}++module Text.Parsec.Perm+ ( PermParser+ , StreamPermParser -- abstract++ , permute+ , (<||>), (<$$>)+ , (<|?>), (<$?>)+ ) where++import Text.Parsec++import Control.Monad.Identity++infixl 1 <||>, <|?>+infixl 2 <$$>, <$?>+++{---------------------------------------------------------------+ test -- parse a permutation of+ * an optional string of 'a's+ * a required 'b'+ * an optional 'c'+---------------------------------------------------------------}+{-+test input+ = parse (do{ x <- ptest; eof; return x }) "" input++ptest :: Parser (String,Char,Char)+ptest+ = permute $+ (,,) <$?> ("",many1 (char 'a'))+ <||> char 'b'+ <|?> ('_',char 'c')+-}++{---------------------------------------------------------------+ Building a permutation parser+---------------------------------------------------------------}++-- | The expression @perm \<||> p@ adds parser @p@ to the permutation+-- parser @perm@. The parser @p@ is not allowed to accept empty input -+-- use the optional combinator ('<|?>') instead. Returns a+-- new permutation parser that includes @p@. ++(<||>) :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> Parsec s st a -> StreamPermParser s st b+(<||>) perm p = add perm p++-- | The expression @f \<$$> p@ creates a fresh permutation parser+-- consisting of parser @p@. The the final result of the permutation+-- parser is the function @f@ applied to the return value of @p@. The+-- parser @p@ is not allowed to accept empty input - use the optional+-- combinator ('<$?>') instead.+--+-- If the function @f@ takes more than one parameter, the type variable+-- @b@ is instantiated to a functional type which combines nicely with+-- the adds parser @p@ to the ('<||>') combinator. This+-- results in stylized code where a permutation parser starts with a+-- combining function @f@ followed by the parsers. The function @f@+-- gets its parameters in the order in which the parsers are specified,+-- but actual input can be in any order.++(<$$>) :: (Stream s Identity tok) => (a -> b) -> Parsec s st a -> StreamPermParser s st b+(<$$>) f p = newperm f <||> p++-- | The expression @perm \<||> (x,p)@ adds parser @p@ to the+-- permutation parser @perm@. The parser @p@ is optional - if it can+-- not be applied, the default value @x@ will be used instead. Returns+-- a new permutation parser that includes the optional parser @p@. ++(<|?>) :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> (a, Parsec s st a) -> StreamPermParser s st b+(<|?>) perm (x,p) = addopt perm x p++-- | The expression @f \<$?> (x,p)@ creates a fresh permutation parser+-- consisting of parser @p@. The the final result of the permutation+-- parser is the function @f@ applied to the return value of @p@. The+-- parser @p@ is optional - if it can not be applied, the default value+-- @x@ will be used instead. ++(<$?>) :: (Stream s Identity tok) => (a -> b) -> (a, Parsec s st a) -> StreamPermParser s st b+(<$?>) f (x,p) = newperm f <|?> (x,p)++{---------------------------------------------------------------+ The permutation tree+---------------------------------------------------------------}++-- | Provided for backwards compatibility. The tok type is ignored.++type PermParser tok st a = StreamPermParser String st a++-- | The type @StreamPermParser s st a@ denotes a permutation parser that,+-- when converted by the 'permute' function, parses +-- @s@ streams with user state @st@ and returns a value of+-- type @a@ on success.+--+-- Normally, a permutation parser is first build with special operators+-- like ('<||>') and than transformed into a normal parser+-- using 'permute'.++data StreamPermParser s st a = Perm (Maybe a) [StreamBranch s st a]++-- type Branch st a = StreamBranch String st a++data StreamBranch s st a = forall b. Branch (StreamPermParser s st (b -> a)) (Parsec s st b)++-- | The parser @permute perm@ parses a permutation of parser described+-- by @perm@. For example, suppose we want to parse a permutation of:+-- an optional string of @a@'s, the character @b@ and an optional @c@.+-- This can be described by:+--+-- > test = permute (tuple <$?> ("",many1 (char 'a'))+-- > <||> char 'b' +-- > <|?> ('_',char 'c'))+-- > where+-- > tuple a b c = (a,b,c)++-- transform a permutation tree into a normal parser+permute :: (Stream s Identity tok) => StreamPermParser s st a -> Parsec s st a+permute (Perm def xs)+ = choice (map branch xs ++ empty)+ where+ empty+ = case def of+ Nothing -> []+ Just x -> [return x]++ branch (Branch perm p)+ = do{ x <- p+ ; f <- permute perm+ ; return (f x)+ }++-- build permutation trees+newperm :: (Stream s Identity tok) => (a -> b) -> StreamPermParser s st (a -> b)+newperm f+ = Perm (Just f) []++add :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> Parsec s st a -> StreamPermParser s st b+add perm@(Perm _mf fs) p+ = Perm Nothing (first:map insert fs)+ where+ first = Branch perm p+ insert (Branch perm' p')+ = Branch (add (mapPerms flip perm') p) p'++addopt :: (Stream s Identity tok) => StreamPermParser s st (a -> b) -> a -> Parsec s st a -> StreamPermParser s st b+addopt perm@(Perm mf fs) x p+ = Perm (fmap ($ x) mf) (first:map insert fs)+ where+ first = Branch perm p+ insert (Branch perm' p')+ = Branch (addopt (mapPerms flip perm') x p) p'+++mapPerms :: (Stream s Identity tok) => (a -> b) -> StreamPermParser s st a -> StreamPermParser s st b+mapPerms f (Perm x xs)+ = Perm (fmap f x) (map mapBranch xs)+ where+ mapBranch (Branch perm p)+ = Branch (mapPerms (f.) perm) p
+ Text/Parsec/Pos.hs view
@@ -0,0 +1,130 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Pos+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Textual source positions.+-- +-----------------------------------------------------------------------------++module Text.Parsec.Pos+ ( SourceName, Line, Column+ , SourcePos+ , sourceLine, sourceColumn, sourceName+ , incSourceLine, incSourceColumn+ , setSourceLine, setSourceColumn, setSourceName+ , newPos, initialPos+ , updatePosChar, updatePosString+ ) where++#ifdef BASE3+import Data.Generics+#else+import Data.Data (Data)+import Data.Typeable (Typeable)+#endif++-- < Source positions: a file name, a line and a column+-- upper left is (1,1)++type SourceName = String+type Line = Int+type Column = Int++-- | The abstract data type @SourcePos@ represents source positions. It+-- contains the name of the source (i.e. file name), a line number and+-- a column number. @SourcePos@ is an instance of the 'Show', 'Eq' and+-- 'Ord' class. ++data SourcePos = SourcePos SourceName !Line !Column+ deriving ( Eq, Ord, Data, Typeable)++-- | Create a new 'SourcePos' with the given source name,+-- line number and column number.++newPos :: SourceName -> Line -> Column -> SourcePos+newPos name line column+ = SourcePos name line column++-- | Create a new 'SourcePos' with the given source name,+-- and line number and column number set to 1, the upper left.++initialPos :: SourceName -> SourcePos+initialPos name+ = newPos name 1 1++-- | Extracts the name of the source from a source position. ++sourceName :: SourcePos -> SourceName+sourceName (SourcePos name _line _column) = name++-- | Extracts the line number from a source position. ++sourceLine :: SourcePos -> Line+sourceLine (SourcePos _name line _column) = line++-- | Extracts the column number from a source position. ++sourceColumn :: SourcePos -> Column+sourceColumn (SourcePos _name _line column) = column++-- | Increments the line number of a source position. ++incSourceLine :: SourcePos -> Line -> SourcePos+incSourceLine (SourcePos name line column) n = SourcePos name (line+n) column++-- | Increments the column number of a source position. ++incSourceColumn :: SourcePos -> Column -> SourcePos+incSourceColumn (SourcePos name line column) n = SourcePos name line (column+n)++-- | Set the name of the source.++setSourceName :: SourcePos -> SourceName -> SourcePos+setSourceName (SourcePos _name line column) n = SourcePos n line column++-- | Set the line number of a source position. ++setSourceLine :: SourcePos -> Line -> SourcePos+setSourceLine (SourcePos name _line column) n = SourcePos name n column++-- | Set the column number of a source position. ++setSourceColumn :: SourcePos -> Column -> SourcePos+setSourceColumn (SourcePos name line _column) n = SourcePos name line n++-- | The expression @updatePosString pos s@ updates the source position+-- @pos@ by calling 'updatePosChar' on every character in @s@, ie.+-- @foldl updatePosChar pos string@. ++updatePosString :: SourcePos -> String -> SourcePos+updatePosString pos string+ = foldl updatePosChar pos string++-- | Update a source position given a character. If the character is a+-- newline (\'\\n\') or carriage return (\'\\r\') the line number is+-- incremented by 1. If the character is a tab (\'\t\') the column+-- number is incremented to the nearest 8'th column, ie. @column + 8 -+-- ((column-1) \`mod\` 8)@. In all other cases, the column is+-- incremented by 1. ++updatePosChar :: SourcePos -> Char -> SourcePos+updatePosChar (SourcePos name line column) c+ = case c of+ '\n' -> SourcePos name (line+1) 1+ '\t' -> SourcePos name line (column + 8 - ((column-1) `mod` 8))+ _ -> SourcePos name line (column + 1)++instance Show SourcePos where+ show (SourcePos name line column)+ | null name = showLineColumn+ | otherwise = "\"" ++ name ++ "\" " ++ showLineColumn+ where+ showLineColumn = "(line " ++ show line +++ ", column " ++ show column +++ ")"
+ Text/Parsec/Prim.hs view
@@ -0,0 +1,740 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Prim+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- The primitive parser combinators.+-- +----------------------------------------------------------------------------- ++{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleContexts,+ UndecidableInstances #-}++module Text.Parsec.Prim+ ( unknownError+ , sysUnExpectError+ , unexpected+ , ParsecT+ , runParsecT+ , mkPT+ , Parsec+ , Consumed(..)+ , Reply(..)+ , State(..)+ , parsecMap+ , parserReturn+ , parserBind+ , mergeErrorReply+ , parserFail+ , parserZero+ , parserPlus+ , (<?>)+ , (<|>)+ , label+ , labels+ , lookAhead+ , Stream(..)+ , tokens+ , try+ , token+ , tokenPrim+ , tokenPrimEx+ , many+ , skipMany+ , manyAccum+ , runPT+ , runP+ , runParserT+ , runParser+ , parse+ , parseTest+ , getPosition+ , getInput+ , setPosition+ , setInput+ , getParserState+ , setParserState+ , updateParserState+ , getState+ , putState+ , modifyState+ , setState+ , updateState+ ) where++import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )+import Control.Monad()+import Control.Monad.Trans+import Control.Monad.Identity++import Control.Monad.Reader.Class+import Control.Monad.State.Class+import Control.Monad.Cont.Class+import Control.Monad.Error.Class++import Text.Parsec.Pos+import Text.Parsec.Error++unknownError :: State s u -> ParseError+unknownError state = newErrorUnknown (statePos state)++sysUnExpectError :: String -> SourcePos -> Reply s u a+sysUnExpectError msg pos = Error (newErrorMessage (SysUnExpect msg) pos)++-- | The parser @unexpected msg@ always fails with an unexpected error+-- message @msg@ without consuming any input.+--+-- The parsers 'fail', ('<?>') and @unexpected@ are the three parsers+-- used to generate error messages. Of these, only ('<?>') is commonly+-- used. For an example of the use of @unexpected@, see the definition+-- of 'Text.Parsec.Combinator.notFollowedBy'.++unexpected :: (Stream s m t) => String -> ParsecT s u m a+unexpected msg+ = ParsecT $ \s _ _ _ eerr ->+ eerr $ newErrorMessage (UnExpect msg) (statePos s)++-- | ParserT monad transformer and Parser type++-- | @ParsecT s u m a@ is a parser with stream type @s@, user state type @u@,+-- underlying monad @m@ and return type @a@. Parsec is strict in the user state.+-- If this is undesirable, simply used a data type like @data Box a = Box a@ and+-- the state type @Box YourStateType@ to add a level of indirection.++newtype ParsecT s u m a+ = ParsecT {unParser :: forall b .+ State s u+ -> (a -> State s u -> ParseError -> m b) -- consumed ok+ -> (ParseError -> m b) -- consumed err+ -> (a -> State s u -> ParseError -> m b) -- empty ok+ -> (ParseError -> m b) -- empty err+ -> m b+ }++-- | Low-level unpacking of the ParsecT type. To run your parser, please look to+-- runPT, runP, runParserT, runParser and other such functions.+runParsecT :: Monad m => ParsecT s u m a -> State s u -> m (Consumed (m (Reply s u a)))+runParsecT p s = unParser p s cok cerr eok eerr+ where cok a s' err = return . Consumed . return $ Ok a s' err+ cerr err = return . Consumed . return $ Error err+ eok a s' err = return . Empty . return $ Ok a s' err+ eerr err = return . Empty . return $ Error err++-- | Low-level creation of the ParsecT type. You really shouldn't have to do this.+mkPT :: Monad m => (State s u -> m (Consumed (m (Reply s u a)))) -> ParsecT s u m a+mkPT k = ParsecT $ \s cok cerr eok eerr -> do+ cons <- k s+ case cons of+ Consumed mrep -> do+ rep <- mrep+ case rep of+ Ok x s' err -> cok x s' err+ Error err -> cerr err+ Empty mrep -> do+ rep <- mrep+ case rep of+ Ok x s' err -> eok x s' err+ Error err -> eerr err++type Parsec s u = ParsecT s u Identity++data Consumed a = Consumed a+ | Empty !a++data Reply s u a = Ok a !(State s u) ParseError+ | Error ParseError++data State s u = State {+ stateInput :: s,+ statePos :: !SourcePos,+ stateUser :: !u+ }++instance Functor Consumed where+ fmap f (Consumed x) = Consumed (f x)+ fmap f (Empty x) = Empty (f x)++instance Functor (Reply s u) where+ fmap f (Ok x s e) = Ok (f x) s e+ fmap _ (Error e) = Error e -- XXX++instance Functor (ParsecT s u m) where+ fmap f p = parsecMap f p++parsecMap :: (a -> b) -> ParsecT s u m a -> ParsecT s u m b+parsecMap f p+ = ParsecT $ \s cok cerr eok eerr ->+ unParser p s (cok . f) cerr (eok . f) eerr++instance Applicative.Applicative (ParsecT s u m) where+ pure = return+ (<*>) = ap -- TODO: Can this be optimized?++instance Applicative.Alternative (ParsecT s u m) where+ empty = mzero+ (<|>) = mplus++instance Monad (ParsecT s u m) where+ return x = parserReturn x+ p >>= f = parserBind p f+ fail msg = parserFail msg++instance (MonadIO m) => MonadIO (ParsecT s u m) where+ liftIO = lift . liftIO++instance (MonadReader r m) => MonadReader r (ParsecT s u m) where+ ask = lift ask+ local f p = mkPT $ \s -> local f (runParsecT p s)++-- I'm presuming the user might want a separate, non-backtracking+-- state aside from the Parsec user state.+instance (MonadState s m) => MonadState s (ParsecT s' u m) where+ get = lift get+ put = lift . put++instance (MonadCont m) => MonadCont (ParsecT s u m) where+ callCC f = mkPT $ \s ->+ callCC $ \c ->+ runParsecT (f (\a -> mkPT $ \s' -> c (pack s' a))) s++ where pack s a= Empty $ return (Ok a s (unknownError s))++instance (MonadError e m) => MonadError e (ParsecT s u m) where+ throwError = lift . throwError+ p `catchError` h = mkPT $ \s ->+ runParsecT p s `catchError` \e ->+ runParsecT (h e) s++parserReturn :: a -> ParsecT s u m a+parserReturn x+ = ParsecT $ \s _ _ eok _ ->+ eok x s (unknownError s)++parserBind :: ParsecT s u m a -> (a -> ParsecT s u m b) -> ParsecT s u m b+{-# INLINE parserBind #-}+parserBind m k+ = ParsecT $ \s cok cerr eok eerr ->+ let+ -- consumed-okay case for m+ mcok x s err =+ let+ -- if (k x) consumes, those go straigt up+ pcok = cok+ pcerr = cerr+ + -- if (k x) doesn't consume input, but is okay,+ -- we still return in the consumed continuation+ peok x s err' = cok x s (mergeError err err')++ -- if (k x) doesn't consume input, but errors,+ -- we return the error in the 'consumed-error'+ -- continuation+ peerr err' = cerr (mergeError err err')+ in unParser (k x) s pcok pcerr peok peerr ++ -- empty-ok case for m+ meok x s err =+ let+ -- in these cases, (k x) can return as empty+ pcok = cok+ peok x s err' = eok x s (mergeError err err')+ pcerr = cerr+ peerr err' = eerr (mergeError err err') + in unParser (k x) s pcok pcerr peok peerr+ -- consumed-error case for m+ mcerr = cerr++ -- empty-error case for m+ meerr = eerr++ in unParser m s mcok mcerr meok meerr+++mergeErrorReply :: ParseError -> Reply s u a -> Reply s u a+mergeErrorReply err1 reply -- XXX where to put it?+ = case reply of+ Ok x state err2 -> Ok x state (mergeError err1 err2)+ Error err2 -> Error (mergeError err1 err2)++parserFail :: String -> ParsecT s u m a+parserFail msg+ = ParsecT $ \s _ _ _ eerr ->+ eerr $ newErrorMessage (Message msg) (statePos s)++instance MonadPlus (ParsecT s u m) where+ mzero = parserZero+ mplus p1 p2 = parserPlus p1 p2++-- | @parserZero@ always fails without consuming any input. @parserZero@ is defined+-- equal to the 'mzero' member of the 'MonadPlus' class and to the 'Control.Applicative.empty' member +-- of the 'Control.Applicative.Applicative' class.++parserZero :: ParsecT s u m a+parserZero+ = ParsecT $ \s _ _ _ eerr ->+ eerr $ unknownError s++parserPlus :: ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a+{-# INLINE parserPlus #-}+parserPlus m n+ = ParsecT $ \s cok cerr eok eerr ->+ let+ meerr err =+ let+ neok y s' err' = eok y s' (mergeError err err')+ neerr err' = eerr $ mergeError err err'+ in unParser n s cok cerr neok neerr+ in unParser m s cok cerr eok meerr++instance MonadTrans (ParsecT s u) where+ lift amb = ParsecT $ \s _ _ eok _ -> do+ a <- amb+ eok a s $ unknownError s++infix 0 <?>+infixr 1 <|>++-- | The parser @p <?> msg@ behaves as parser @p@, but whenever the+-- parser @p@ fails /without consuming any input/, it replaces expect+-- error messages with the expect error message @msg@.+--+-- This is normally used at the end of a set alternatives where we want+-- to return an error message in terms of a higher level construct+-- rather than returning all possible characters. For example, if the+-- @expr@ parser from the 'try' example would fail, the error+-- message is: '...: expecting expression'. Without the @(\<?>)@+-- combinator, the message would be like '...: expecting \"let\" or+-- letter', which is less friendly.++(<?>) :: (ParsecT s u m a) -> String -> (ParsecT s u m a)+p <?> msg = label p msg++-- | This combinator implements choice. The parser @p \<|> q@ first+-- applies @p@. If it succeeds, the value of @p@ is returned. If @p@+-- fails /without consuming any input/, parser @q@ is tried. This+-- combinator is defined equal to the 'mplus' member of the 'MonadPlus'+-- class and the ('Control.Applicative.<|>') member of 'Control.Applicative.Alternative'.+--+-- The parser is called /predictive/ since @q@ is only tried when+-- parser @p@ didn't consume any input (i.e.. the look ahead is 1).+-- This non-backtracking behaviour allows for both an efficient+-- implementation of the parser combinators and the generation of good+-- error messages.++(<|>) :: (ParsecT s u m a) -> (ParsecT s u m a) -> (ParsecT s u m a)+p1 <|> p2 = mplus p1 p2++label :: ParsecT s u m a -> String -> ParsecT s u m a+label p msg+ = labels p [msg]++labels :: ParsecT s u m a -> [String] -> ParsecT s u m a+labels p msgs =+ ParsecT $ \s cok cerr eok eerr ->+ let eok' x s' error = eok x s' $ if errorIsUnknown error+ then error+ else setExpectErrors error msgs+ eerr' err = eerr $ setExpectErrors err msgs++ in unParser p s cok cerr eok' eerr'++ where+ setExpectErrors err [] = setErrorMessage (Expect "") err+ setExpectErrors err [msg] = setErrorMessage (Expect msg) err+ setExpectErrors err (msg:msgs)+ = foldr (\msg' err' -> addErrorMessage (Expect msg') err')+ (setErrorMessage (Expect msg) err) msgs++-- TODO: There should be a stronger statement that can be made about this++-- | An instance of @Stream@ has stream type @s@, underlying monad @m@ and token type @t@ determined by the stream+-- +-- Some rough guidelines for a \"correct\" instance of Stream:+--+-- * unfoldM uncons gives the [t] corresponding to the stream+--+-- * A @Stream@ instance is responsible for maintaining the \"position within the stream\" in the stream state @s@. This is trivial unless you are using the monad in a non-trivial way.++class (Monad m) => Stream s m t | s -> t where+ uncons :: s -> m (Maybe (t,s))++tokens :: (Stream s m t, Eq t)+ => ([t] -> String) -- Pretty print a list of tokens+ -> (SourcePos -> [t] -> SourcePos)+ -> [t] -- List of tokens to parse+ -> ParsecT s u m [t]+{-# INLINE tokens #-}+tokens _ _ []+ = ParsecT $ \s _ _ eok _ ->+ eok [] s $ unknownError s+tokens showTokens nextposs tts@(tok:toks)+ = ParsecT $ \(State input pos u) cok cerr eok eerr -> + let+ errEof = (setErrorMessage (Expect (showTokens tts))+ (newErrorMessage (SysUnExpect "") pos))++ errExpect x = (setErrorMessage (Expect (showTokens tts))+ (newErrorMessage (SysUnExpect (showTokens [x])) pos))++ walk [] rs = ok rs+ walk (t:ts) rs = do+ sr <- uncons rs+ case sr of+ Nothing -> cerr $ errEof+ Just (x,xs) | t == x -> walk ts xs+ | otherwise -> cerr $ errExpect x++ ok rs = let pos' = nextposs pos tts+ s' = State rs pos' u+ in cok tts s' (newErrorUnknown pos')+ in do+ sr <- uncons input+ case sr of+ Nothing -> eerr $ errEof+ Just (x,xs)+ | tok == x -> walk toks xs+ | otherwise -> eerr $ errExpect x+ +-- | The parser @try p@ behaves like parser @p@, except that it+-- pretends that it hasn't consumed any input when an error occurs.+--+-- This combinator is used whenever arbitrary look ahead is needed.+-- Since it pretends that it hasn't consumed any input when @p@ fails,+-- the ('<|>') combinator will try its second alternative even when the+-- first parser failed while consuming input.+--+-- The @try@ combinator can for example be used to distinguish+-- identifiers and reserved words. Both reserved words and identifiers+-- are a sequence of letters. Whenever we expect a certain reserved+-- word where we can also expect an identifier we have to use the @try@+-- combinator. Suppose we write:+--+-- > expr = letExpr <|> identifier <?> "expression"+-- >+-- > letExpr = do{ string "let"; ... }+-- > identifier = many1 letter+--+-- If the user writes \"lexical\", the parser fails with: @unexpected+-- \'x\', expecting \'t\' in \"let\"@. Indeed, since the ('<|>') combinator+-- only tries alternatives when the first alternative hasn't consumed+-- input, the @identifier@ parser is never tried (because the prefix+-- \"le\" of the @string \"let\"@ parser is already consumed). The+-- right behaviour can be obtained by adding the @try@ combinator:+--+-- > expr = letExpr <|> identifier <?> "expression"+-- >+-- > letExpr = do{ try (string "let"); ... }+-- > identifier = many1 letter++try :: ParsecT s u m a -> ParsecT s u m a+try p =+ ParsecT $ \s cok _ eok eerr ->+ unParser p s cok eerr eok eerr++-- | @lookAhead p@ parses @p@ without consuming any input.+--+-- If @p@ fails and consumes some input, so does @lookAhead@. Combine with 'try'+-- if this is undesirable.++lookAhead :: (Stream s m t) => ParsecT s u m a -> ParsecT s u m a+lookAhead p = do{ state <- getParserState+ ; x <- p'+ ; setParserState state+ ; return x+ }+ where+ p' = ParsecT $ \s cok cerr eok eerr ->+ unParser p s eok cerr eok eerr++-- | The parser @tokenPrim showTok posFromTok testTok@ accepts a token @t@+-- with result @x@ when the function @testTok t@ returns @'Just' x@. The+-- source position of the @t@ should be returned by @posFromTok t@ and+-- the token can be shown using @showTok t@.+--+-- This combinator is expressed in terms of 'tokenPrim'.+-- It is used to accept user defined token streams. For example,+-- suppose that we have a stream of basic tokens tupled with source+-- positions. We can than define a parser that accepts single tokens as:+--+-- > mytoken x+-- > = token showTok posFromTok testTok+-- > where+-- > showTok (pos,t) = show t+-- > posFromTok (pos,t) = pos+-- > testTok (pos,t) = if x == t then Just t else Nothing++token :: (Stream s Identity t)+ => (t -> String) -- ^ Token pretty-printing function.+ -> (t -> SourcePos) -- ^ Computes the position of a token.+ -> (t -> Maybe a) -- ^ Matching function for the token to parse.+ -> Parsec s u a+token showToken tokpos test = tokenPrim showToken nextpos test+ where+ nextpos _ tok ts = case runIdentity (uncons ts) of+ Nothing -> tokpos tok+ Just (tok',_) -> tokpos tok'++-- | The parser @token showTok nextPos testTok@ accepts a token @t@+-- with result @x@ when the function @testTok t@ returns @'Just' x@. The+-- token can be shown using @showTok t@. The position of the /next/+-- token should be returned when @nextPos@ is called with the current+-- source position @pos@, the current token @t@ and the rest of the+-- tokens @toks@, @nextPos pos t toks@.+--+-- This is the most primitive combinator for accepting tokens. For+-- example, the 'Text.Parsec.Char.char' parser could be implemented as:+--+-- > char c+-- > = tokenPrim showChar nextPos testChar+-- > where+-- > showChar x = "'" ++ x ++ "'"+-- > testChar x = if x == c then Just x else Nothing+-- > nextPos pos x xs = updatePosChar pos x++tokenPrim :: (Stream s m t)+ => (t -> String) -- ^ Token pretty-printing function.+ -> (SourcePos -> t -> s -> SourcePos) -- ^ Next position calculating function.+ -> (t -> Maybe a) -- ^ Matching function for the token to parse.+ -> ParsecT s u m a+{-# INLINE tokenPrim #-}+tokenPrim showToken nextpos test = tokenPrimEx showToken nextpos Nothing test++tokenPrimEx :: (Stream s m t)+ => (t -> String) + -> (SourcePos -> t -> s -> SourcePos)+ -> Maybe (SourcePos -> t -> s -> u -> u)+ -> (t -> Maybe a) + -> ParsecT s u m a+{-# INLINE tokenPrimEx #-}+tokenPrimEx showToken nextpos Nothing test+ = ParsecT $ \(State input pos user) cok cerr eok eerr -> do+ r <- uncons input+ case r of+ Nothing -> eerr $ unexpectError "" pos+ Just (c,cs)+ -> case test c of+ Just x -> let newpos = nextpos pos c cs+ newstate = State cs newpos user+ in seq newpos $ seq newstate $+ cok x newstate (newErrorUnknown newpos)+ Nothing -> eerr $ unexpectError (showToken c) pos+tokenPrimEx showToken nextpos (Just nextState) test+ = ParsecT $ \(State input pos user) cok cerr eok eerr -> do+ r <- uncons input+ case r of+ Nothing -> eerr $ unexpectError "" pos+ Just (c,cs)+ -> case test c of+ Just x -> let newpos = nextpos pos c cs+ newUser = nextState pos c cs user+ newstate = State cs newpos newUser+ in seq newpos $ seq newstate $+ cok x newstate $ newErrorUnknown newpos+ Nothing -> eerr $ unexpectError (showToken c) pos++unexpectError msg pos = newErrorMessage (SysUnExpect msg) pos+++-- | @many p@ applies the parser @p@ /zero/ or more times. Returns a+-- list of the returned values of @p@.+--+-- > identifier = do{ c <- letter+-- > ; cs <- many (alphaNum <|> char '_')+-- > ; return (c:cs)+-- > }++many :: ParsecT s u m a -> ParsecT s u m [a]+many p+ = do xs <- manyAccum (:) p+ return (reverse xs)++-- | @skipMany p@ applies the parser @p@ /zero/ or more times, skipping+-- its result.+--+-- > spaces = skipMany space++skipMany :: ParsecT s u m a -> ParsecT s u m ()+skipMany p+ = do manyAccum (\_ _ -> []) p+ return ()++manyAccum :: (a -> [a] -> [a])+ -> ParsecT s u m a+ -> ParsecT s u m [a]+manyAccum acc p =+ ParsecT $ \s cok cerr eok eerr ->+ let walk xs x s' err =+ unParser p s'+ (seq xs $ walk $ acc x xs) -- consumed-ok+ cerr -- consumed-err+ manyErr -- empty-ok+ (\e -> cok (acc x xs) s' e) -- empty-err+ in unParser p s (walk []) cerr manyErr (\e -> eok [] s e)++manyErr = error "Text.ParserCombinators.Parsec.Prim.many: combinator 'many' is applied to a parser that accepts an empty string."+++-- < Running a parser: monadic (runPT) and pure (runP)++runPT :: (Stream s m t)+ => ParsecT s u m a -> u -> SourceName -> s -> m (Either ParseError a)+runPT p u name s+ = do res <- runParsecT p (State s (initialPos name) u)+ r <- parserReply res+ case r of+ Ok x _ _ -> return (Right x)+ Error err -> return (Left err)+ where+ parserReply res+ = case res of+ Consumed r -> r+ Empty r -> r++runP :: (Stream s Identity t)+ => Parsec s u a -> u -> SourceName -> s -> Either ParseError a+runP p u name s = runIdentity $ runPT p u name s++-- | The most general way to run a parser. @runParserT p state filePath+-- input@ runs parser @p@ on the input list of tokens @input@,+-- obtained from source @filePath@ with the initial user state @st@.+-- The @filePath@ is only used in error messages and may be the empty+-- string. Returns a computation in the underlying monad @m@ that return either a 'ParseError' ('Left') or a+-- value of type @a@ ('Right').++runParserT :: (Stream s m t)+ => ParsecT s u m a -> u -> SourceName -> s -> m (Either ParseError a)+runParserT = runPT++-- | The most general way to run a parser over the Identity monad. @runParser p state filePath+-- input@ runs parser @p@ on the input list of tokens @input@,+-- obtained from source @filePath@ with the initial user state @st@.+-- The @filePath@ is only used in error messages and may be the empty+-- string. Returns either a 'ParseError' ('Left') or a+-- value of type @a@ ('Right').+--+-- > parseFromFile p fname+-- > = do{ input <- readFile fname+-- > ; return (runParser p () fname input)+-- > }++runParser :: (Stream s Identity t)+ => Parsec s u a -> u -> SourceName -> s -> Either ParseError a+runParser = runP++-- | @parse p filePath input@ runs a parser @p@ over Identity without user+-- state. The @filePath@ is only used in error messages and may be the+-- empty string. Returns either a 'ParseError' ('Left')+-- or a value of type @a@ ('Right').+--+-- > main = case (parse numbers "" "11, 2, 43") of+-- > Left err -> print err+-- > Right xs -> print (sum xs)+-- >+-- > numbers = commaSep integer++parse :: (Stream s Identity t)+ => Parsec s () a -> SourceName -> s -> Either ParseError a+parse p = runP p ()++-- | The expression @parseTest p input@ applies a parser @p@ against+-- input @input@ and prints the result to stdout. Used for testing+-- parsers.++parseTest :: (Stream s Identity t, Show a)+ => Parsec s () a -> s -> IO ()+parseTest p input+ = case parse p "" input of+ Left err -> do putStr "parse error at "+ print err+ Right x -> print x++-- < Parser state combinators++-- | Returns the current source position. See also 'SourcePos'.++getPosition :: (Monad m) => ParsecT s u m SourcePos+getPosition = do state <- getParserState+ return (statePos state)++-- | Returns the current input ++getInput :: (Monad m) => ParsecT s u m s+getInput = do state <- getParserState+ return (stateInput state)++-- | @setPosition pos@ sets the current source position to @pos@. ++setPosition :: (Monad m) => SourcePos -> ParsecT s u m ()+setPosition pos+ = do updateParserState (\(State input _ user) -> State input pos user)+ return ()++-- | @setInput input@ continues parsing with @input@. The 'getInput' and+-- @setInput@ functions can for example be used to deal with #include+-- files. ++setInput :: (Monad m) => s -> ParsecT s u m ()+setInput input+ = do updateParserState (\(State _ pos user) -> State input pos user)+ return ()++-- | Returns the full parser state as a 'State' record.++getParserState :: (Monad m) => ParsecT s u m (State s u)+getParserState = updateParserState id++-- | @setParserState st@ set the full parser state to @st@. ++setParserState :: (Monad m) => State s u -> ParsecT s u m (State s u)+setParserState st = updateParserState (const st)++-- | @updateParserState f@ applies function @f@ to the parser state.++updateParserState :: (State s u -> State s u) -> ParsecT s u m (State s u)+updateParserState f =+ ParsecT $ \s _ _ eok _ ->+ let s' = f s + in eok s' s' $ unknownError s' ++-- < User state combinators++-- | Returns the current user state. ++getState :: (Monad m) => ParsecT s u m u+getState = stateUser `liftM` getParserState++-- | @putState st@ set the user state to @st@. ++putState :: (Monad m) => u -> ParsecT s u m ()+putState u = do updateParserState $ \s -> s { stateUser = u }+ return ()++-- | @updateState f@ applies function @f@ to the user state. Suppose+-- that we want to count identifiers in a source, we could use the user+-- state as:+--+-- > expr = do{ x <- identifier+-- > ; updateState (+1)+-- > ; return (Id x)+-- > }++modifyState :: (Monad m) => (u -> u) -> ParsecT s u m ()+modifyState f = do updateParserState $ \s -> s { stateUser = f (stateUser s) }+ return ()++-- XXX Compat++-- | An alias for putState for backwards compatibility.++setState :: (Monad m) => u -> ParsecT s u m ()+setState = putState++-- | An alias for modifyState for backwards compatibility.++updateState :: (Monad m) => (u -> u) -> ParsecT s u m ()+updateState = modifyState
+ Text/Parsec/String.hs view
@@ -0,0 +1,45 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.String+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the file libraries/parsec/LICENSE)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Make Strings an instance of 'Stream' with 'Char' token type.+--+-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.Parsec.String+ ( Parser, GenParser, parseFromFile+ ) where++import Text.Parsec.Error+import Text.Parsec.Prim++instance (Monad m) => Stream [tok] m tok where+ uncons [] = return $ Nothing+ uncons (t:ts) = return $ Just (t,ts)+ {-# INLINE uncons #-}++type Parser = Parsec String ()+type GenParser tok st = Parsec [tok] st++-- | @parseFromFile p filePath@ runs a string parser @p@ on the+-- input read from @filePath@ using 'Prelude.readFile'. Returns either a 'ParseError'+-- ('Left') or a value of type @a@ ('Right').+--+-- > main = do{ result <- parseFromFile numbers "digits.txt"+-- > ; case result of+-- > Left err -> print err+-- > Right xs -> print (sum xs)+-- > }+parseFromFile :: Parser a -> String -> IO (Either ParseError a)+parseFromFile p fname+ = do input <- readFile fname+ return (runP p () fname input)
+ Text/Parsec/Text.hs view
@@ -0,0 +1,31 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.String+-- Copyright : (c) Antoine Latter 2011+-- License : BSD-style (see the file libraries/parsec/LICENSE)+-- +-- Maintainer : aslatter@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Make Text an instance of 'Stream' with 'Char' token type.+--+-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.Parsec.Text+ ( Parser, GenParser+ ) where++import qualified Data.Text as Text+import Text.Parsec.Error+import Text.Parsec.Prim++instance (Monad m) => Stream Text.Text m Char where+ uncons = return . Text.uncons+ {-# INLINE uncons #-}++type Parser = Parsec Text.Text ()+type GenParser st = Parsec Text.Text st
+ Text/Parsec/Text/Lazy.hs view
@@ -0,0 +1,31 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.String+-- Copyright : (c) Antoine Latter 2011+-- License : BSD-style (see the file libraries/parsec/LICENSE)+-- +-- Maintainer : aslatter@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Make Text an instance of 'Stream' with 'Char' token type.+--+-----------------------------------------------------------------------------++{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Text.Parsec.Text.Lazy+ ( Parser, GenParser+ ) where++import qualified Data.Text.Lazy as Text+import Text.Parsec.Error+import Text.Parsec.Prim++instance (Monad m) => Stream Text.Text m Char where+ uncons = return . Text.uncons+ {-# INLINE uncons #-}++type Parser = Parsec Text.Text ()+type GenParser st = Parsec Text.Text st
+ Text/Parsec/Token.hs view
@@ -0,0 +1,722 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.Parsec.Token+-- Copyright : (c) Daan Leijen 1999-2001, (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : non-portable (uses local universal quantification: PolymorphicComponents)+-- +-- A helper module to parse lexical elements (tokens). See 'makeTokenParser'+-- for a description of how to use it.+-- +-----------------------------------------------------------------------------++{-# LANGUAGE PolymorphicComponents #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}++module Text.Parsec.Token+ ( LanguageDef+ , GenLanguageDef (..)+ , TokenParser+ , GenTokenParser (..)+ , makeTokenParser+ ) where++import Data.Char ( isAlpha, toLower, toUpper, isSpace, digitToInt )+import Data.List ( nub, sort )+import Control.Monad.Identity+import Text.Parsec.Prim+import Text.Parsec.Char+import Text.Parsec.Combinator++-----------------------------------------------------------+-- Language Definition+-----------------------------------------------------------++type LanguageDef st = GenLanguageDef String st Identity++-- | The @GenLanguageDef@ type is a record that contains all parameterizable+-- features of the 'Text.Parsec.Token' module. The module 'Text.Parsec.Language'+-- contains some default definitions.++data GenLanguageDef s u m+ = LanguageDef { + + -- | Describes the start of a block comment. Use the empty string if the+ -- language doesn't support block comments. For example \"\/*\". ++ commentStart :: String,++ -- | Describes the end of a block comment. Use the empty string if the+ -- language doesn't support block comments. For example \"*\/\". ++ commentEnd :: String,++ -- | Describes the start of a line comment. Use the empty string if the+ -- language doesn't support line comments. For example \"\/\/\". ++ commentLine :: String,++ -- | Set to 'True' if the language supports nested block comments. ++ nestedComments :: Bool,++ -- | This parser should accept any start characters of identifiers. For+ -- example @letter \<|> char \"_\"@. ++ identStart :: ParsecT s u m Char,++ -- | This parser should accept any legal tail characters of identifiers.+ -- For example @alphaNum \<|> char \"_\"@. ++ identLetter :: ParsecT s u m Char,++ -- | This parser should accept any start characters of operators. For+ -- example @oneOf \":!#$%&*+.\/\<=>?\@\\\\^|-~\"@ ++ opStart :: ParsecT s u m Char,++ -- | This parser should accept any legal tail characters of operators.+ -- Note that this parser should even be defined if the language doesn't+ -- support user-defined operators, or otherwise the 'reservedOp'+ -- parser won't work correctly. ++ opLetter :: ParsecT s u m Char,++ -- | The list of reserved identifiers. ++ reservedNames :: [String],++ -- | The list of reserved operators. ++ reservedOpNames:: [String],++ -- | Set to 'True' if the language is case sensitive. ++ caseSensitive :: Bool++ }++-----------------------------------------------------------+-- A first class module: TokenParser+-----------------------------------------------------------++type TokenParser st = GenTokenParser String st Identity++-- | The type of the record that holds lexical parsers that work on+-- @s@ streams with state @u@ over a monad @m@.++data GenTokenParser s u m+ = TokenParser {++ -- | This lexeme parser parses a legal identifier. Returns the identifier+ -- string. This parser will fail on identifiers that are reserved+ -- words. Legal identifier (start) characters and reserved words are+ -- defined in the 'LanguageDef' that is passed to+ -- 'makeTokenParser'. An @identifier@ is treated as+ -- a single token using 'try'.++ identifier :: ParsecT s u m String,+ + -- | The lexeme parser @reserved name@ parses @symbol + -- name@, but it also checks that the @name@ is not a prefix of a+ -- valid identifier. A @reserved@ word is treated as a single token+ -- using 'try'. ++ reserved :: String -> ParsecT s u m (),++ -- | This lexeme parser parses a legal operator. Returns the name of the+ -- operator. This parser will fail on any operators that are reserved+ -- operators. Legal operator (start) characters and reserved operators+ -- are defined in the 'LanguageDef' that is passed to+ -- 'makeTokenParser'. An @operator@ is treated as a+ -- single token using 'try'. ++ operator :: ParsecT s u m String,++ -- |The lexeme parser @reservedOp name@ parses @symbol+ -- name@, but it also checks that the @name@ is not a prefix of a+ -- valid operator. A @reservedOp@ is treated as a single token using+ -- 'try'. ++ reservedOp :: String -> ParsecT s u m (),+++ -- | This lexeme parser parses a single literal character. Returns the+ -- literal character value. This parsers deals correctly with escape+ -- sequences. The literal character is parsed according to the grammar+ -- rules defined in the Haskell report (which matches most programming+ -- languages quite closely). ++ charLiteral :: ParsecT s u m Char,++ -- | This lexeme parser parses a literal string. Returns the literal+ -- string value. This parsers deals correctly with escape sequences and+ -- gaps. The literal string is parsed according to the grammar rules+ -- defined in the Haskell report (which matches most programming+ -- languages quite closely). ++ stringLiteral :: ParsecT s u m String,++ -- | This lexeme parser parses a natural number (a positive whole+ -- number). Returns the value of the number. The number can be+ -- specified in 'decimal', 'hexadecimal' or+ -- 'octal'. The number is parsed according to the grammar+ -- rules in the Haskell report. ++ natural :: ParsecT s u m Integer,++ -- | This lexeme parser parses an integer (a whole number). This parser+ -- is like 'natural' except that it can be prefixed with+ -- sign (i.e. \'-\' or \'+\'). Returns the value of the number. The+ -- number can be specified in 'decimal', 'hexadecimal'+ -- or 'octal'. The number is parsed according+ -- to the grammar rules in the Haskell report. + + integer :: ParsecT s u m Integer,++ -- | This lexeme parser parses a floating point value. Returns the value+ -- of the number. The number is parsed according to the grammar rules+ -- defined in the Haskell report. ++ float :: ParsecT s u m Double,++ -- | This lexeme parser parses either 'natural' or a 'float'.+ -- Returns the value of the number. This parsers deals with+ -- any overlap in the grammar rules for naturals and floats. The number+ -- is parsed according to the grammar rules defined in the Haskell report. ++ naturalOrFloat :: ParsecT s u m (Either Integer Double),++ -- | Parses a positive whole number in the decimal system. Returns the+ -- value of the number. ++ decimal :: ParsecT s u m Integer,++ -- | Parses a positive whole number in the hexadecimal system. The number+ -- should be prefixed with \"0x\" or \"0X\". Returns the value of the+ -- number. ++ hexadecimal :: ParsecT s u m Integer,++ -- | Parses a positive whole number in the octal system. The number+ -- should be prefixed with \"0o\" or \"0O\". Returns the value of the+ -- number. ++ octal :: ParsecT s u m Integer,++ -- | Lexeme parser @symbol s@ parses 'string' @s@ and skips+ -- trailing white space. ++ symbol :: String -> ParsecT s u m String,++ -- | @lexeme p@ first applies parser @p@ and than the 'whiteSpace'+ -- parser, returning the value of @p@. Every lexical+ -- token (lexeme) is defined using @lexeme@, this way every parse+ -- starts at a point without white space. Parsers that use @lexeme@ are+ -- called /lexeme/ parsers in this document.+ -- + -- The only point where the 'whiteSpace' parser should be+ -- called explicitly is the start of the main parser in order to skip+ -- any leading white space.+ --+ -- > mainParser = do{ whiteSpace+ -- > ; ds <- many (lexeme digit)+ -- > ; eof+ -- > ; return (sum ds)+ -- > }++ lexeme :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | Parses any white space. White space consists of /zero/ or more+ -- occurrences of a 'space', a line comment or a block (multi+ -- line) comment. Block comments may be nested. How comments are+ -- started and ended is defined in the 'LanguageDef'+ -- that is passed to 'makeTokenParser'. ++ whiteSpace :: ParsecT s u m (),++ -- | Lexeme parser @parens p@ parses @p@ enclosed in parenthesis,+ -- returning the value of @p@.++ parens :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | Lexeme parser @braces p@ parses @p@ enclosed in braces (\'{\' and+ -- \'}\'), returning the value of @p@. ++ braces :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | Lexeme parser @angles p@ parses @p@ enclosed in angle brackets (\'\<\'+ -- and \'>\'), returning the value of @p@. ++ angles :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | Lexeme parser @brackets p@ parses @p@ enclosed in brackets (\'[\'+ -- and \']\'), returning the value of @p@. ++ brackets :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | DEPRECATED: Use 'brackets'.++ squares :: forall a. ParsecT s u m a -> ParsecT s u m a,++ -- | Lexeme parser |semi| parses the character \';\' and skips any+ -- trailing white space. Returns the string \";\". ++ semi :: ParsecT s u m String,++ -- | Lexeme parser @comma@ parses the character \',\' and skips any+ -- trailing white space. Returns the string \",\". ++ comma :: ParsecT s u m String,++ -- | Lexeme parser @colon@ parses the character \':\' and skips any+ -- trailing white space. Returns the string \":\". ++ colon :: ParsecT s u m String,++ -- | Lexeme parser @dot@ parses the character \'.\' and skips any+ -- trailing white space. Returns the string \".\". ++ dot :: ParsecT s u m String,++ -- | Lexeme parser @semiSep p@ parses /zero/ or more occurrences of @p@+ -- separated by 'semi'. Returns a list of values returned by+ -- @p@.++ semiSep :: forall a . ParsecT s u m a -> ParsecT s u m [a],++ -- | Lexeme parser @semiSep1 p@ parses /one/ or more occurrences of @p@+ -- separated by 'semi'. Returns a list of values returned by @p@. ++ semiSep1 :: forall a . ParsecT s u m a -> ParsecT s u m [a],++ -- | Lexeme parser @commaSep p@ parses /zero/ or more occurrences of+ -- @p@ separated by 'comma'. Returns a list of values returned+ -- by @p@. ++ commaSep :: forall a . ParsecT s u m a -> ParsecT s u m [a],++ -- | Lexeme parser @commaSep1 p@ parses /one/ or more occurrences of+ -- @p@ separated by 'comma'. Returns a list of values returned+ -- by @p@. ++ commaSep1 :: forall a . ParsecT s u m a -> ParsecT s u m [a]+ }++-----------------------------------------------------------+-- Given a LanguageDef, create a token parser.+-----------------------------------------------------------++-- | The expression @makeTokenParser language@ creates a 'GenTokenParser'+-- record that contains lexical parsers that are+-- defined using the definitions in the @language@ record.+--+-- The use of this function is quite stylized - one imports the+-- appropiate language definition and selects the lexical parsers that+-- are needed from the resulting 'GenTokenParser'.+--+-- > module Main where+-- >+-- > import Text.Parsec+-- > import qualified Text.Parsec.Token as P+-- > import Text.Parsec.Language (haskellDef)+-- >+-- > -- The parser+-- > ...+-- >+-- > expr = parens expr+-- > <|> identifier+-- > <|> ...+-- > +-- >+-- > -- The lexer+-- > lexer = P.makeTokenParser haskellDef +-- > +-- > parens = P.parens lexer+-- > braces = P.braces lexer+-- > identifier = P.identifier lexer+-- > reserved = P.reserved lexer+-- > ...++makeTokenParser :: (Stream s m Char)+ => GenLanguageDef s u m -> GenTokenParser s u m+makeTokenParser languageDef+ = TokenParser{ identifier = identifier+ , reserved = reserved+ , operator = operator+ , reservedOp = reservedOp++ , charLiteral = charLiteral+ , stringLiteral = stringLiteral+ , natural = natural+ , integer = integer+ , float = float+ , naturalOrFloat = naturalOrFloat+ , decimal = decimal+ , hexadecimal = hexadecimal+ , octal = octal++ , symbol = symbol+ , lexeme = lexeme+ , whiteSpace = whiteSpace++ , parens = parens+ , braces = braces+ , angles = angles+ , brackets = brackets+ , squares = brackets+ , semi = semi+ , comma = comma+ , colon = colon+ , dot = dot+ , semiSep = semiSep+ , semiSep1 = semiSep1+ , commaSep = commaSep+ , commaSep1 = commaSep1+ }+ where++ -----------------------------------------------------------+ -- Bracketing+ -----------------------------------------------------------+ parens p = between (symbol "(") (symbol ")") p+ braces p = between (symbol "{") (symbol "}") p+ angles p = between (symbol "<") (symbol ">") p+ brackets p = between (symbol "[") (symbol "]") p++ semi = symbol ";"+ comma = symbol ","+ dot = symbol "."+ colon = symbol ":"++ commaSep p = sepBy p comma+ semiSep p = sepBy p semi++ commaSep1 p = sepBy1 p comma+ semiSep1 p = sepBy1 p semi+++ -----------------------------------------------------------+ -- Chars & Strings+ -----------------------------------------------------------+ charLiteral = lexeme (between (char '\'')+ (char '\'' <?> "end of character")+ characterChar )+ <?> "character"++ characterChar = charLetter <|> charEscape+ <?> "literal character"++ charEscape = do{ char '\\'; escapeCode }+ charLetter = satisfy (\c -> (c /= '\'') && (c /= '\\') && (c > '\026'))++++ stringLiteral = lexeme (+ do{ str <- between (char '"')+ (char '"' <?> "end of string")+ (many stringChar)+ ; return (foldr (maybe id (:)) "" str)+ }+ <?> "literal string")++ stringChar = do{ c <- stringLetter; return (Just c) }+ <|> stringEscape+ <?> "string character"++ stringLetter = satisfy (\c -> (c /= '"') && (c /= '\\') && (c > '\026'))++ stringEscape = do{ char '\\'+ ; do{ escapeGap ; return Nothing }+ <|> do{ escapeEmpty; return Nothing }+ <|> do{ esc <- escapeCode; return (Just esc) }+ }++ escapeEmpty = char '&'+ escapeGap = do{ many1 space+ ; char '\\' <?> "end of string gap"+ }++++ -- escape codes+ escapeCode = charEsc <|> charNum <|> charAscii <|> charControl+ <?> "escape code"++ charControl = do{ char '^'+ ; code <- upper+ ; return (toEnum (fromEnum code - fromEnum 'A'))+ }++ charNum = do{ code <- decimal+ <|> do{ char 'o'; number 8 octDigit }+ <|> do{ char 'x'; number 16 hexDigit }+ ; return (toEnum (fromInteger code))+ }++ charEsc = choice (map parseEsc escMap)+ where+ parseEsc (c,code) = do{ char c; return code }++ charAscii = choice (map parseAscii asciiMap)+ where+ parseAscii (asc,code) = try (do{ string asc; return code })+++ -- escape code tables+ escMap = zip ("abfnrtv\\\"\'") ("\a\b\f\n\r\t\v\\\"\'")+ asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2)++ ascii2codes = ["BS","HT","LF","VT","FF","CR","SO","SI","EM",+ "FS","GS","RS","US","SP"]+ ascii3codes = ["NUL","SOH","STX","ETX","EOT","ENQ","ACK","BEL",+ "DLE","DC1","DC2","DC3","DC4","NAK","SYN","ETB",+ "CAN","SUB","ESC","DEL"]++ ascii2 = ['\BS','\HT','\LF','\VT','\FF','\CR','\SO','\SI',+ '\EM','\FS','\GS','\RS','\US','\SP']+ ascii3 = ['\NUL','\SOH','\STX','\ETX','\EOT','\ENQ','\ACK',+ '\BEL','\DLE','\DC1','\DC2','\DC3','\DC4','\NAK',+ '\SYN','\ETB','\CAN','\SUB','\ESC','\DEL']+++ -----------------------------------------------------------+ -- Numbers+ -----------------------------------------------------------+ naturalOrFloat = lexeme (natFloat) <?> "number"++ float = lexeme floating <?> "float"+ integer = lexeme int <?> "integer"+ natural = lexeme nat <?> "natural"+++ -- floats+ floating = do{ n <- decimal+ ; fractExponent n+ }+++ natFloat = do{ char '0'+ ; zeroNumFloat+ }+ <|> decimalFloat++ zeroNumFloat = do{ n <- hexadecimal <|> octal+ ; return (Left n)+ }+ <|> decimalFloat+ <|> fractFloat 0+ <|> return (Left 0)++ decimalFloat = do{ n <- decimal+ ; option (Left n)+ (fractFloat n)+ }++ fractFloat n = do{ f <- fractExponent n+ ; return (Right f)+ }++ fractExponent n = do{ fract <- fraction+ ; expo <- option 1.0 exponent'+ ; return ((fromInteger n + fract)*expo)+ }+ <|>+ do{ expo <- exponent'+ ; return ((fromInteger n)*expo)+ }++ fraction = do{ char '.'+ ; digits <- many1 digit <?> "fraction"+ ; return (foldr op 0.0 digits)+ }+ <?> "fraction"+ where+ op d f = (f + fromIntegral (digitToInt d))/10.0++ exponent' = do{ oneOf "eE"+ ; f <- sign+ ; e <- decimal <?> "exponent"+ ; return (power (f e))+ }+ <?> "exponent"+ where+ power e | e < 0 = 1.0/power(-e)+ | otherwise = fromInteger (10^e)+++ -- integers and naturals+ int = do{ f <- lexeme sign+ ; n <- nat+ ; return (f n)+ }++ sign = (char '-' >> return negate)+ <|> (char '+' >> return id)+ <|> return id++ nat = zeroNumber <|> decimal++ zeroNumber = do{ char '0'+ ; hexadecimal <|> octal <|> decimal <|> return 0+ }+ <?> ""++ decimal = number 10 digit+ hexadecimal = do{ oneOf "xX"; number 16 hexDigit }+ octal = do{ oneOf "oO"; number 8 octDigit }++ number base baseDigit+ = do{ digits <- many1 baseDigit+ ; let n = foldl (\x d -> base*x + toInteger (digitToInt d)) 0 digits+ ; seq n (return n)+ }++ -----------------------------------------------------------+ -- Operators & reserved ops+ -----------------------------------------------------------+ reservedOp name =+ lexeme $ try $+ do{ string name+ ; notFollowedBy (opLetter languageDef) <?> ("end of " ++ show name)+ }++ operator =+ lexeme $ try $+ do{ name <- oper+ ; if (isReservedOp name)+ then unexpected ("reserved operator " ++ show name)+ else return name+ }++ oper =+ do{ c <- (opStart languageDef)+ ; cs <- many (opLetter languageDef)+ ; return (c:cs)+ }+ <?> "operator"++ isReservedOp name =+ isReserved (sort (reservedOpNames languageDef)) name+++ -----------------------------------------------------------+ -- Identifiers & Reserved words+ -----------------------------------------------------------+ reserved name =+ lexeme $ try $+ do{ caseString name+ ; notFollowedBy (identLetter languageDef) <?> ("end of " ++ show name)+ }++ caseString name+ | caseSensitive languageDef = string name+ | otherwise = do{ walk name; return name }+ where+ walk [] = return ()+ walk (c:cs) = do{ caseChar c <?> msg; walk cs }++ caseChar c | isAlpha c = char (toLower c) <|> char (toUpper c)+ | otherwise = char c++ msg = show name+++ identifier =+ lexeme $ try $+ do{ name <- ident+ ; if (isReservedName name)+ then unexpected ("reserved word " ++ show name)+ else return name+ }+++ ident+ = do{ c <- identStart languageDef+ ; cs <- many (identLetter languageDef)+ ; return (c:cs)+ }+ <?> "identifier"++ isReservedName name+ = isReserved theReservedNames caseName+ where+ caseName | caseSensitive languageDef = name+ | otherwise = map toLower name+++ isReserved names name+ = scan names+ where+ scan [] = False+ scan (r:rs) = case (compare r name) of+ LT -> scan rs+ EQ -> True+ GT -> False++ theReservedNames+ | caseSensitive languageDef = sort reserved+ | otherwise = sort . map (map toLower) $ reserved+ where+ reserved = reservedNames languageDef++++ -----------------------------------------------------------+ -- White space & symbols+ -----------------------------------------------------------+ symbol name+ = lexeme (string name)++ lexeme p+ = do{ x <- p; whiteSpace; return x }+++ --whiteSpace+ whiteSpace+ | noLine && noMulti = skipMany (simpleSpace <?> "")+ | noLine = skipMany (simpleSpace <|> multiLineComment <?> "")+ | noMulti = skipMany (simpleSpace <|> oneLineComment <?> "")+ | otherwise = skipMany (simpleSpace <|> oneLineComment <|> multiLineComment <?> "")+ where+ noLine = null (commentLine languageDef)+ noMulti = null (commentStart languageDef)+++ simpleSpace =+ skipMany1 (satisfy isSpace)++ oneLineComment =+ do{ try (string (commentLine languageDef))+ ; skipMany (satisfy (/= '\n'))+ ; return ()+ }++ multiLineComment =+ do { try (string (commentStart languageDef))+ ; inComment+ }++ inComment+ | nestedComments languageDef = inCommentMulti+ | otherwise = inCommentSingle++ inCommentMulti+ = do{ try (string (commentEnd languageDef)) ; return () }+ <|> do{ multiLineComment ; inCommentMulti }+ <|> do{ skipMany1 (noneOf startEnd) ; inCommentMulti }+ <|> do{ oneOf startEnd ; inCommentMulti }+ <?> "end of comment"+ where+ startEnd = nub (commentEnd languageDef ++ commentStart languageDef)++ inCommentSingle+ = do{ try (string (commentEnd languageDef)); return () }+ <|> do{ skipMany1 (noneOf startEnd) ; inCommentSingle }+ <|> do{ oneOf startEnd ; inCommentSingle }+ <?> "end of comment"+ where+ startEnd = nub (commentEnd languageDef ++ commentStart languageDef)
+ Text/ParserCombinators/Parsec.hs view
@@ -0,0 +1,41 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec+ ( -- complete modules+ module Text.ParserCombinators.Parsec.Prim+ , module Text.ParserCombinators.Parsec.Combinator+ , module Text.ParserCombinators.Parsec.Char++ -- module Text.ParserCombinators.Parsec.Error+ , ParseError+ , errorPos++ -- module Text.ParserCombinators.Parsec.Pos+ , SourcePos+ , SourceName, Line, Column+ , sourceName, sourceLine, sourceColumn+ , incSourceLine, incSourceColumn+ , setSourceLine, setSourceColumn, setSourceName++ ) where++import Text.Parsec.String()++import Text.ParserCombinators.Parsec.Prim+import Text.ParserCombinators.Parsec.Combinator+import Text.ParserCombinators.Parsec.Char++import Text.ParserCombinators.Parsec.Error+import Text.ParserCombinators.Parsec.Pos
+ Text/ParserCombinators/Parsec/Char.hs view
@@ -0,0 +1,40 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Char+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Char+ ( CharParser,+ spaces,+ space,+ newline,+ tab,+ upper,+ lower,+ alphaNum,+ letter,+ digit,+ hexDigit,+ octDigit,+ char,+ string,+ anyChar,+ oneOf,+ noneOf,+ satisfy+ ) where+++import Text.Parsec.Char+import Text.Parsec.String++type CharParser st = GenParser Char st
+ Text/ParserCombinators/Parsec/Combinator.hs view
@@ -0,0 +1,42 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Combinator+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Combinator+ ( choice,+ count,+ between,+ option,+ optionMaybe,+ optional,+ skipMany1,+ many1,+ sepBy,+ sepBy1,+ endBy,+ endBy1,+ sepEndBy,+ sepEndBy1,+ chainl,+ chainl1,+ chainr,+ chainr1,+ eof,+ notFollowedBy,+ manyTill,+ lookAhead,+ anyToken+ ) where+++import Text.Parsec.Combinator
+ Text/ParserCombinators/Parsec/Error.hs view
@@ -0,0 +1,40 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Error+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Error+ ( Message (SysUnExpect,UnExpect,Expect,Message),+ messageString,+ messageCompare,+ messageEq,+ ParseError,+ errorPos,+ errorMessages,+ errorIsUnknown,+ showErrorMessages,+ newErrorMessage,+ newErrorUnknown,+ addErrorMessage,+ setErrorPos,+ setErrorMessage,+ mergeError+ ) where++import Text.Parsec.Error+++messageCompare :: Message -> Message -> Ordering+messageCompare = compare++messageEq :: Message -> Message -> Bool+messageEq = (==)
+ Text/ParserCombinators/Parsec/Expr.hs view
@@ -0,0 +1,42 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Expr+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Expr+ ( Assoc (AssocNone,AssocLeft,AssocRight),+ Operator(..),+ OperatorTable,+ buildExpressionParser+ ) where++import Text.Parsec.Expr(Assoc(..))+import qualified Text.Parsec.Expr as N+import Text.ParserCombinators.Parsec(GenParser)++import Control.Monad.Identity++data Operator tok st a = Infix (GenParser tok st (a -> a -> a)) Assoc+ | Prefix (GenParser tok st (a -> a))+ | Postfix (GenParser tok st (a -> a))++type OperatorTable tok st a = [[Operator tok st a]]++convert :: Operator tok st a -> N.Operator [tok] st Identity a+convert (Infix p a) = N.Infix p a+convert (Prefix p) = N.Prefix p+convert (Postfix p) = N.Postfix p++buildExpressionParser :: OperatorTable tok st a+ -> GenParser tok st a+ -> GenParser tok st a+buildExpressionParser = N.buildExpressionParser . map (map convert)
+ Text/ParserCombinators/Parsec/Language.hs view
@@ -0,0 +1,28 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Language+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Language+ ( haskellDef,+ haskell,+ mondrianDef,+ mondrian,+ emptyDef,+ haskellStyle,+ javaStyle,+ LanguageDef,+ GenLanguageDef(..),+ ) where++import Text.Parsec.Token+import Text.Parsec.Language
+ Text/ParserCombinators/Parsec/Perm.hs view
@@ -0,0 +1,24 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Perm+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Perm+ ( PermParser,+ permute,+ (<||>),+ (<$$>),+ (<|?>),+ (<$?>)+ ) where++import Text.Parsec.Perm
+ Text/ParserCombinators/Parsec/Pos.hs view
@@ -0,0 +1,35 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Pos+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Pos+ ( SourceName,+ Line,+ Column,+ SourcePos,+ sourceLine,+ sourceColumn,+ sourceName,+ incSourceLine,+ incSourceColumn,+ setSourceLine,+ setSourceColumn,+ setSourceName,+ newPos,+ initialPos,+ updatePosChar,+ updatePosString+ ) where+ ++import Text.Parsec.Pos
+ Text/ParserCombinators/Parsec/Prim.hs view
@@ -0,0 +1,65 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Prim+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Prim+ ( (<?>),+ (<|>),+ Parser,+ GenParser,+ runParser,+ parse,+ parseFromFile,+ parseTest,+ token,+ tokens,+ tokenPrim,+ tokenPrimEx,+ try,+ label,+ labels,+ unexpected,+ pzero,+ many,+ skipMany,+ getState,+ setState,+ updateState,+ getPosition,+ setPosition,+ getInput,+ setInput,+ State(..),+ getParserState,+ setParserState+ ) where++import Text.Parsec.Prim hiding (runParser, try)+import qualified Text.Parsec.Prim as N -- 'N' for 'New'+import Text.Parsec.String++import Text.Parsec.Error+import Text.Parsec.Pos++pzero :: GenParser tok st a+pzero = parserZero++runParser :: GenParser tok st a+ -> st+ -> SourceName+ -> [tok]+ -> Either ParseError a+runParser = N.runParser++try :: GenParser tok st a -> GenParser tok st a+try = N.try
+ Text/ParserCombinators/Parsec/Token.hs view
@@ -0,0 +1,23 @@+-----------------------------------------------------------------------------+-- |+-- Module : Text.ParserCombinators.Parsec.Token+-- Copyright : (c) Paolo Martini 2007+-- License : BSD-style (see the LICENSE file)+-- +-- Maintainer : derek.a.elkins@gmail.com+-- Stability : provisional+-- Portability : portable+-- +-- Parsec compatibility module+-- +-----------------------------------------------------------------------------++module Text.ParserCombinators.Parsec.Token+ ( LanguageDef,+ GenLanguageDef(..),+ TokenParser,+ GenTokenParser(..),+ makeTokenParser+ ) where++import Text.Parsec.Token
+ cabal-install-bundle.cabal view
@@ -0,0 +1,205 @@+Name: cabal-install-bundle+Version: 0.10.2+Synopsis: The (bundled) command-line interface for Cabal and Hackage.+Description: This is cabal-install with bundled dependencies. Easier to bootstrap.+License: BSD3+Maintainer: paczesiowa@gmail.com+Category: Distribution+build-type: Configure+Cabal-Version: >= 1.8+extra-tmp-files:+ config.log config.status autom4te.cache network.buildinfo+ include/HsNetworkConfig.h+extra-source-files:+ config.guess config.sub install-sh+ configure.ac configure network.buildinfo.in+ include/HsNetworkConfig.h.in include/HsNet.h+ -- C sources only used on some systems+ cbits/ancilData.c cbits/asyncAccept.c cbits/initWinSock.c+ cbits/winSockErr.c+ cbits/crc32.h cbits/inffast.h cbits/inflate.h+ cbits/trees.h cbits/deflate.h cbits/inffixed.h+ cbits/inftrees.h cbits/zutil.h++Executable cabal+ Main-Is: Main.hs+ ghc-options: -Wall+ include-dirs: include+ includes: HsNet.h zlib.h+ install-includes: HsNet.h HsNetworkConfig.h+ extra-libraries: z+ c-sources: cbits/HsNet.c+ Other-Modules:+ System.Random+ Codec.Compression.GZip,+ Codec.Compression.Zlib,+ Codec.Compression.Zlib.Raw,+ Codec.Compression.Zlib.Internal+ Codec.Compression.Zlib.Stream+ Control.Monad.Cont+ Control.Monad.Cont.Class+ Control.Monad.Error+ Control.Monad.Error.Class+ Control.Monad.Identity+ Control.Monad.List+ Control.Monad.RWS+ Control.Monad.RWS.Class+ Control.Monad.RWS.Lazy+ Control.Monad.RWS.Strict+ Control.Monad.Reader+ Control.Monad.Reader.Class+ Control.Monad.State+ Control.Monad.State.Class+ Control.Monad.State.Lazy+ Control.Monad.State.Strict+ Control.Monad.Trans+ Control.Monad.Writer+ Control.Monad.Writer.Class+ Control.Monad.Writer.Lazy+ Control.Monad.Writer.Strict+ Control.Applicative.Backwards+ Control.Applicative.Lift+ Control.Monad.IO.Class+ Control.Monad.Trans.Class+ Control.Monad.Trans.Cont+ Control.Monad.Trans.Error+ Control.Monad.Trans.Identity+ Control.Monad.Trans.List+ Control.Monad.Trans.Maybe+ Control.Monad.Trans.Reader+ Control.Monad.Trans.RWS+ Control.Monad.Trans.RWS.Lazy+ Control.Monad.Trans.RWS.Strict+ Control.Monad.Trans.State+ Control.Monad.Trans.State.Lazy+ Control.Monad.Trans.State.Strict+ Control.Monad.Trans.Writer+ Control.Monad.Trans.Writer.Lazy+ Control.Monad.Trans.Writer.Strict+ Data.Functor.Compose+ Data.Functor.Constant+ Data.Functor.Identity+ Data.Functor.Product+ Data.Functor.Reverse+ Text.Parsec,+ Text.Parsec.String,+ Text.Parsec.ByteString,+ Text.Parsec.ByteString.Lazy,+ Text.Parsec.Text,+ Text.Parsec.Text.Lazy,+ Text.Parsec.Pos,+ Text.Parsec.Error,+ Text.Parsec.Prim,+ Text.Parsec.Char,+ Text.Parsec.Combinator,+ Text.Parsec.Token,+ Text.Parsec.Expr,+ Text.Parsec.Language,+ Text.Parsec.Perm,+ Text.ParserCombinators.Parsec,+ Text.ParserCombinators.Parsec.Char,+ Text.ParserCombinators.Parsec.Combinator,+ Text.ParserCombinators.Parsec.Error,+ Text.ParserCombinators.Parsec.Expr,+ Text.ParserCombinators.Parsec.Language,+ Text.ParserCombinators.Parsec.Perm,+ Text.ParserCombinators.Parsec.Pos,+ Text.ParserCombinators.Parsec.Prim,+ Text.ParserCombinators.Parsec.Token+ Network+ Network.BSD+ Network.Socket+ Network.Socket.ByteString+ Network.Socket.ByteString.Lazy+ Network.Socket.Internal+ Network.URI+ Network.Socket.ByteString.Internal+ Network.Socket.ByteString.IOVec+ Network.Socket.ByteString.MsgHdr+ Network.BufferType,+ Network.Stream,+ Network.StreamDebugger,+ Network.StreamSocket,+ Network.TCP,+ Network.HTTP,+ Network.HTTP.Headers,+ Network.HTTP.Base,+ Network.HTTP.Stream,+ Network.HTTP.Auth,+ Network.HTTP.Cookie,+ Network.HTTP.Proxy,+ Network.HTTP.HandleStream,+ Network.Browser+ Network.HTTP.Base64,+ Network.HTTP.MD5Aux,+ Network.HTTP.Utils+ Distribution.Client.BuildReports.Anonymous+ Distribution.Client.BuildReports.Storage+ Distribution.Client.BuildReports.Types+ Distribution.Client.BuildReports.Upload+ Distribution.Client.Check+ Distribution.Client.Config+ Distribution.Client.Configure+ Distribution.Client.Dependency+ Distribution.Client.Dependency.TopDown+ Distribution.Client.Dependency.TopDown.Constraints+ Distribution.Client.Dependency.TopDown.Types+ Distribution.Client.Dependency.Types+ Distribution.Client.Dependency.Modular+ Distribution.Client.Dependency.Modular.Assignment+ Distribution.Client.Dependency.Modular.Builder+ Distribution.Client.Dependency.Modular.Configured+ Distribution.Client.Dependency.Modular.ConfiguredConversion+ Distribution.Client.Dependency.Modular.Dependency+ Distribution.Client.Dependency.Modular.Explore+ Distribution.Client.Dependency.Modular.Flag+ Distribution.Client.Dependency.Modular.Index+ Distribution.Client.Dependency.Modular.IndexConversion+ Distribution.Client.Dependency.Modular.Log+ Distribution.Client.Dependency.Modular.Message+ Distribution.Client.Dependency.Modular.Package+ Distribution.Client.Dependency.Modular.Preference+ Distribution.Client.Dependency.Modular.PSQ+ Distribution.Client.Dependency.Modular.Solver+ Distribution.Client.Dependency.Modular.Tree+ Distribution.Client.Dependency.Modular.Validate+ Distribution.Client.Dependency.Modular.Version+ Distribution.Client.Fetch+ Distribution.Client.FetchUtils+ Distribution.Client.GZipUtils+ Distribution.Client.Haddock+ Distribution.Client.HttpUtils+ Distribution.Client.IndexUtils+ Distribution.Client.Init+ Distribution.Client.Init.Heuristics+ Distribution.Client.Init.Licenses+ Distribution.Client.Init.Types+ Distribution.Client.Install+ Distribution.Client.InstallPlan+ Distribution.Client.InstallSymlink+ Distribution.Client.List+ Distribution.Client.PackageIndex+ Distribution.Client.PackageUtils+ Distribution.Client.Setup+ Distribution.Client.SetupWrapper+ Distribution.Client.SrcDist+ Distribution.Client.Tar+ Distribution.Client.Targets+ Distribution.Client.Types+ Distribution.Client.Unpack+ Distribution.Client.Update+ Distribution.Client.Upload+ Distribution.Client.Utils+ Distribution.Client.World+ Distribution.Client.Win32SelfUpgrade+ Distribution.Compat.Exception+ Distribution.Compat.FilePerms+ Paths_cabal_install_bundle++ build-depends: base >= 2 && < 99, Cabal >= 1.10, filepath, time, process, directory, pretty, containers, array, old-time, bytestring, unix++ extensions: CPP, DeriveDataTypeable, ForeignFunctionInterface, TypeSynonymInstances, ExistentialQuantification, PolymorphicComponents, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, DeriveDataTypeable, FunctionalDependencies++source-repository head+ type: git+ location: git://github.com/Paczesiowa/cabal-install-bundle.git
+ cbits/HsNet.c view
@@ -0,0 +1,8 @@+/* -----------------------------------------------------------------------------+ * (c) The University of Glasgow 2002+ *+ * static versions of the inline functions from HsNet.h+ * -------------------------------------------------------------------------- */++#define INLINE+#include "HsNet.h"
+ cbits/ancilData.c view
@@ -0,0 +1,237 @@+/*+ * Copyright(c), 2002 The GHC Team.+ */++#ifdef aix_HOST_OS+#define _LINUX_SOURCE_COMPAT +// Required to get CMSG_SPACE/CMSG_LEN macros. See #265.+// Alternative is to #define COMPAT_43 and use the +// HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS code instead, but that means+// fiddling with the configure script too.+#endif++#include "HsNet.h"+#include <string.h>++#if HAVE_STRUCT_MSGHDR_MSG_CONTROL || HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS /* until end */++/* + * Support for transmitting file descriptors.+ *+ * + */+ ++/*+ * sendmsg() and recvmsg() wrappers for transmitting+ * ancillary socket data.+ *+ * Doesn't provide the full generality of either, specifically:+ *+ * - no support for scattered read/writes.+ * - only possible to send one ancillary chunk of data at a time.+ *+ *+ * NOTE: recv/sendAncillary() is being phased out in preference+ * of the more specific send/recvFd(), as the latter is+ * really the only application of recv/sendAncillary() and+ * stand the chance of being supported on platforms that+ * don't have send/recvmsg() (but do have ioctl() support+ * for this kind of thing, for instance.)+ *+ */++int+sendFd(int sock,+ int outfd)+{+ struct msghdr msg = {0};+ struct iovec iov[1];+ char buf[2];+#if HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS+ msg.msg_accrights = (void*)&outfd;+ msg.msg_accrightslen = sizeof(int);+#else+ struct cmsghdr *cmsg;+ char ancBuffer[CMSG_SPACE(sizeof(int))];+ char* dPtr;+ + msg.msg_control = ancBuffer;+ msg.msg_controllen = sizeof(ancBuffer);++ cmsg = CMSG_FIRSTHDR(&msg);+ cmsg->cmsg_level = SOL_SOCKET;+ cmsg->cmsg_type = SCM_RIGHTS;+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));+ dPtr = (char*)CMSG_DATA(cmsg);+ + *(int*)dPtr = outfd;+ msg.msg_controllen = cmsg->cmsg_len;+#endif++ buf[0] = 0; buf[1] = '\0';+ iov[0].iov_base = buf;+ iov[0].iov_len = 2;++ msg.msg_iov = iov;+ msg.msg_iovlen = 1;+ + return sendmsg(sock,&msg,0);+}++int+sendAncillary(int sock,+ int level,+ int type,+ int flags,+ void* data,+ int len)+{+ struct msghdr msg = {0};+ struct iovec iov[1];+ char buf[2];+#if HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS+ /* Contains the older BSD msghdr fields only, so no room+ for 'type' or 'level' data.+ */+ msg.msg_accrights = data;+ msg.msg_accrightslen=len;+#else+ struct cmsghdr *cmsg;+ char ancBuffer[CMSG_SPACE(len)];+ char* dPtr;+ + msg.msg_control = ancBuffer;+ msg.msg_controllen = sizeof(ancBuffer);++ cmsg = CMSG_FIRSTHDR(&msg);+ cmsg->cmsg_level = level;+ cmsg->cmsg_type = type;+ cmsg->cmsg_len = CMSG_LEN(len);+ dPtr = (char*)CMSG_DATA(cmsg);+ + memcpy(dPtr, data, len);+ msg.msg_controllen = cmsg->cmsg_len;+#endif+ buf[0] = 0; buf[1] = '\0';+ iov[0].iov_base = buf;+ iov[0].iov_len = 2;++ msg.msg_iov = iov;+ msg.msg_iovlen = 1;+ + return sendmsg(sock,&msg,flags);+}++int+recvFd(int sock)+{+ struct msghdr msg = {0};+ char duffBuf[10];+ int rc;+ int len = sizeof(int);+ struct iovec iov[1];+#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ struct cmsghdr *cmsg = NULL;+ struct cmsghdr *cptr;+#else+ int* fdBuffer;+#endif++ iov[0].iov_base = duffBuf;+ iov[0].iov_len = sizeof(duffBuf);+ msg.msg_iov = iov;+ msg.msg_iovlen = 1;++#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ cmsg = (struct cmsghdr*)malloc(CMSG_SPACE(len));+ if (cmsg==NULL) {+ return -1;+ }+ + msg.msg_control = (void *)cmsg;+ msg.msg_controllen = CMSG_LEN(len);+#else+ fdBuffer = (int*)malloc(len);+ if (fdBuffer) {+ msg.msg_accrights = (void *)fdBuffer;+ } else {+ return -1;+ }+ msg.msg_accrightslen = len;+#endif++ if ((rc = recvmsg(sock,&msg,0)) < 0) {+ return rc;+ }+ +#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ cptr = (struct cmsghdr*)CMSG_FIRSTHDR(&msg);+ return *(int*)CMSG_DATA(cptr);+#else+ return *(int*)fdBuffer;+#endif+}+++int+recvAncillary(int sock,+ int* pLevel,+ int* pType,+ int flags,+ void** pData,+ int* pLen)+{+ struct msghdr msg = {0};+ char duffBuf[10];+ int rc;+ struct iovec iov[1];+#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ struct cmsghdr *cmsg = NULL;+ struct cmsghdr *cptr;+#endif+ + iov[0].iov_base = duffBuf;+ iov[0].iov_len = sizeof(duffBuf);+ msg.msg_iov = iov;+ msg.msg_iovlen = 1;++#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ cmsg = (struct cmsghdr*)malloc(CMSG_SPACE(*pLen));+ if (cmsg==NULL) {+ return -1;+ }+ + msg.msg_control = (void *)cmsg;+ msg.msg_controllen = CMSG_LEN(*pLen);+#else+ *pData = (void*)malloc(*pLen);+ if (*pData) {+ msg.msg_accrights = *pData;+ } else {+ return -1;+ }+ msg.msg_accrightslen = *pLen;+#endif++ if ((rc = recvmsg(sock,&msg,flags)) < 0) {+ return rc;+ }+ +#if HAVE_STRUCT_MSGHDR_MSG_CONTROL+ cptr = (struct cmsghdr*)CMSG_FIRSTHDR(&msg);++ *pLevel = cptr->cmsg_level;+ *pType = cptr->cmsg_type;+ /* The length of the data portion only */+ *pLen = cptr->cmsg_len - sizeof(struct cmsghdr);+ *pData = CMSG_DATA(cptr);+#else+ /* Sensible defaults, I hope.. */+ *pLevel = 0;+ *pType = 0;+#endif++ return rc;+}+#endif
+ cbits/asyncAccept.c view
@@ -0,0 +1,72 @@+/*+ * (c) sof, 2003.+ */++#include "HsNet.h"+#include "HsFFI.h"++#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__) && !defined(__HUGS__)++/* all the way to the end */++/*+ * To support non-blocking accept()s with WinSock, we use the asyncDoProc#+ * primop, which lets a Haskell thread call an external routine without+ * blocking the progress of other threads. + *+ * As can readily be seen, this is a low-level mechanism.+ *+ */++typedef struct AcceptData {+ int fdSock;+ int newSock;+ void* sockAddr;+ int size;+} AcceptData;++/*+ * Fill in parameter block that's passed along when the RTS invokes the + * accept()-calling proc below (acceptDoProc())+ */+void*+newAcceptParams(int sock,+ int sz,+ void* sockaddr)+{+ AcceptData* data = (AcceptData*)malloc(sizeof(AcceptData));+ if (!data) return NULL;+ data->fdSock = sock;+ data->newSock = 0;+ data->sockAddr = sockaddr;+ data->size = sz;+ + return data;+}++/* Accessors for return code and accept()'s socket result. */++int+acceptNewSock(void* d)+{+ return (((AcceptData*)d)->newSock);+}++/* Routine invoked by an RTS worker thread */+int+acceptDoProc(void* param)+{+ SOCKET s;++ AcceptData* data = (AcceptData*)param;+ s = accept( data->fdSock,+ data->sockAddr,+ &data->size);+ data->newSock = s;+ if ( s == INVALID_SOCKET ) {+ return GetLastError();+ } else {+ return 0;+ }+}+#endif
+ cbits/crc32.h view
@@ -0,0 +1,441 @@+/* crc32.h -- tables for rapid CRC calculation+ * Generated automatically by crc32.c+ */++local const unsigned long FAR crc_table[TBLS][256] =+{+ {+ 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,+ 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,+ 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,+ 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,+ 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,+ 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,+ 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,+ 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,+ 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,+ 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,+ 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,+ 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,+ 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,+ 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,+ 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,+ 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,+ 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,+ 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,+ 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,+ 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,+ 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,+ 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,+ 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,+ 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,+ 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,+ 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,+ 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,+ 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,+ 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,+ 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,+ 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,+ 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,+ 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,+ 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,+ 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,+ 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,+ 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,+ 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,+ 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,+ 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,+ 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,+ 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,+ 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,+ 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,+ 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,+ 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,+ 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,+ 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,+ 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,+ 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,+ 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,+ 0x2d02ef8dUL+#ifdef BYFOUR+ },+ {+ 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,+ 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,+ 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,+ 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,+ 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,+ 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,+ 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,+ 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,+ 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,+ 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,+ 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,+ 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,+ 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,+ 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,+ 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,+ 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,+ 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,+ 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,+ 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,+ 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,+ 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,+ 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,+ 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,+ 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,+ 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,+ 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,+ 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,+ 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,+ 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,+ 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,+ 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,+ 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,+ 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,+ 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,+ 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,+ 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,+ 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,+ 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,+ 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,+ 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,+ 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,+ 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,+ 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,+ 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,+ 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,+ 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,+ 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,+ 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,+ 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,+ 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,+ 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,+ 0x9324fd72UL+ },+ {+ 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,+ 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,+ 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,+ 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,+ 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,+ 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,+ 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,+ 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,+ 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,+ 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,+ 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,+ 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,+ 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,+ 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,+ 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,+ 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,+ 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,+ 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,+ 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,+ 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,+ 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,+ 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,+ 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,+ 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,+ 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,+ 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,+ 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,+ 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,+ 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,+ 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,+ 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,+ 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,+ 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,+ 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,+ 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,+ 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,+ 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,+ 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,+ 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,+ 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,+ 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,+ 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,+ 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,+ 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,+ 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,+ 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,+ 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,+ 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,+ 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,+ 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,+ 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,+ 0xbe9834edUL+ },+ {+ 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,+ 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,+ 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,+ 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,+ 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,+ 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,+ 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,+ 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,+ 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,+ 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,+ 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,+ 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,+ 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,+ 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,+ 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,+ 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,+ 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,+ 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,+ 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,+ 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,+ 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,+ 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,+ 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,+ 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,+ 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,+ 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,+ 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,+ 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,+ 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,+ 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,+ 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,+ 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,+ 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,+ 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,+ 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,+ 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,+ 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,+ 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,+ 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,+ 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,+ 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,+ 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,+ 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,+ 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,+ 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,+ 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,+ 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,+ 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,+ 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,+ 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,+ 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,+ 0xde0506f1UL+ },+ {+ 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,+ 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,+ 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,+ 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,+ 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,+ 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,+ 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,+ 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,+ 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,+ 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,+ 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,+ 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,+ 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,+ 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,+ 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,+ 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,+ 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,+ 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,+ 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,+ 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,+ 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,+ 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,+ 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,+ 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,+ 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,+ 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,+ 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,+ 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,+ 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,+ 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,+ 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,+ 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,+ 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,+ 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,+ 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,+ 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,+ 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,+ 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,+ 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,+ 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,+ 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,+ 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,+ 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,+ 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,+ 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,+ 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,+ 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,+ 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,+ 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,+ 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,+ 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,+ 0x8def022dUL+ },+ {+ 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,+ 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,+ 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,+ 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,+ 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,+ 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,+ 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,+ 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,+ 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,+ 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,+ 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,+ 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,+ 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,+ 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,+ 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,+ 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,+ 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,+ 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,+ 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,+ 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,+ 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,+ 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,+ 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,+ 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,+ 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,+ 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,+ 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,+ 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,+ 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,+ 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,+ 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,+ 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,+ 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,+ 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,+ 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,+ 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,+ 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,+ 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,+ 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,+ 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,+ 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,+ 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,+ 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,+ 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,+ 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,+ 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,+ 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,+ 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,+ 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,+ 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,+ 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,+ 0x72fd2493UL+ },+ {+ 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,+ 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,+ 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,+ 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,+ 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,+ 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,+ 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,+ 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,+ 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,+ 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,+ 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,+ 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,+ 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,+ 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,+ 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,+ 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,+ 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,+ 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,+ 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,+ 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,+ 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,+ 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,+ 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,+ 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,+ 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,+ 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,+ 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,+ 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,+ 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,+ 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,+ 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,+ 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,+ 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,+ 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,+ 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,+ 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,+ 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,+ 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,+ 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,+ 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,+ 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,+ 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,+ 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,+ 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,+ 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,+ 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,+ 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,+ 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,+ 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,+ 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,+ 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,+ 0xed3498beUL+ },+ {+ 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,+ 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,+ 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,+ 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,+ 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,+ 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,+ 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,+ 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,+ 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,+ 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,+ 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,+ 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,+ 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,+ 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,+ 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,+ 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,+ 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,+ 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,+ 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,+ 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,+ 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,+ 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,+ 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,+ 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,+ 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,+ 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,+ 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,+ 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,+ 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,+ 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,+ 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,+ 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,+ 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,+ 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,+ 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,+ 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,+ 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,+ 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,+ 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,+ 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,+ 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,+ 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,+ 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,+ 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,+ 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,+ 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,+ 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,+ 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,+ 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,+ 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,+ 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,+ 0xf10605deUL+#endif+ }+};
+ cbits/deflate.h view
@@ -0,0 +1,342 @@+/* deflate.h -- internal compression state+ * Copyright (C) 1995-2010 Jean-loup Gailly+ * For conditions of distribution and use, see copyright notice in zlib.h+ */++/* WARNING: this file should *not* be used by applications. It is+ part of the implementation of the compression library and is+ subject to change. Applications should only use zlib.h.+ */++/* @(#) $Id$ */++#ifndef DEFLATE_H+#define DEFLATE_H++#include "zutil.h"++/* define NO_GZIP when compiling if you want to disable gzip header and+ trailer creation by deflate(). NO_GZIP would be used to avoid linking in+ the crc code when it is not needed. For shared libraries, gzip encoding+ should be left enabled. */+#ifndef NO_GZIP+# define GZIP+#endif++/* ===========================================================================+ * Internal compression state.+ */++#define LENGTH_CODES 29+/* number of length codes, not counting the special END_BLOCK code */++#define LITERALS 256+/* number of literal bytes 0..255 */++#define L_CODES (LITERALS+1+LENGTH_CODES)+/* number of Literal or Length codes, including the END_BLOCK code */++#define D_CODES 30+/* number of distance codes */++#define BL_CODES 19+/* number of codes used to transfer the bit lengths */++#define HEAP_SIZE (2*L_CODES+1)+/* maximum heap size */++#define MAX_BITS 15+/* All codes must not exceed MAX_BITS bits */++#define INIT_STATE 42+#define EXTRA_STATE 69+#define NAME_STATE 73+#define COMMENT_STATE 91+#define HCRC_STATE 103+#define BUSY_STATE 113+#define FINISH_STATE 666+/* Stream status */+++/* Data structure describing a single value and its code string. */+typedef struct ct_data_s {+ union {+ ush freq; /* frequency count */+ ush code; /* bit string */+ } fc;+ union {+ ush dad; /* father node in Huffman tree */+ ush len; /* length of bit string */+ } dl;+} FAR ct_data;++#define Freq fc.freq+#define Code fc.code+#define Dad dl.dad+#define Len dl.len++typedef struct static_tree_desc_s static_tree_desc;++typedef struct tree_desc_s {+ ct_data *dyn_tree; /* the dynamic tree */+ int max_code; /* largest code with non zero frequency */+ static_tree_desc *stat_desc; /* the corresponding static tree */+} FAR tree_desc;++typedef ush Pos;+typedef Pos FAR Posf;+typedef unsigned IPos;++/* A Pos is an index in the character window. We use short instead of int to+ * save space in the various tables. IPos is used only for parameter passing.+ */++typedef struct internal_state {+ z_streamp strm; /* pointer back to this zlib stream */+ int status; /* as the name implies */+ Bytef *pending_buf; /* output still pending */+ ulg pending_buf_size; /* size of pending_buf */+ Bytef *pending_out; /* next pending byte to output to the stream */+ uInt pending; /* nb of bytes in the pending buffer */+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */+ gz_headerp gzhead; /* gzip header information to write */+ uInt gzindex; /* where in extra, name, or comment */+ Byte method; /* STORED (for zip only) or DEFLATED */+ int last_flush; /* value of flush param for previous deflate call */++ /* used by deflate.c: */++ uInt w_size; /* LZ77 window size (32K by default) */+ uInt w_bits; /* log2(w_size) (8..16) */+ uInt w_mask; /* w_size - 1 */++ Bytef *window;+ /* Sliding window. Input bytes are read into the second half of the window,+ * and move to the first half later to keep a dictionary of at least wSize+ * bytes. With this organization, matches are limited to a distance of+ * wSize-MAX_MATCH bytes, but this ensures that IO is always+ * performed with a length multiple of the block size. Also, it limits+ * the window size to 64K, which is quite useful on MSDOS.+ * To do: use the user input buffer as sliding window.+ */++ ulg window_size;+ /* Actual size of window: 2*wSize, except when the user input buffer+ * is directly used as sliding window.+ */++ Posf *prev;+ /* Link to older string with same hash index. To limit the size of this+ * array to 64K, this link is maintained only for the last 32K strings.+ * An index in this array is thus a window index modulo 32K.+ */++ Posf *head; /* Heads of the hash chains or NIL. */++ uInt ins_h; /* hash index of string to be inserted */+ uInt hash_size; /* number of elements in hash table */+ uInt hash_bits; /* log2(hash_size) */+ uInt hash_mask; /* hash_size-1 */++ uInt hash_shift;+ /* Number of bits by which ins_h must be shifted at each input+ * step. It must be such that after MIN_MATCH steps, the oldest+ * byte no longer takes part in the hash key, that is:+ * hash_shift * MIN_MATCH >= hash_bits+ */++ long block_start;+ /* Window position at the beginning of the current output block. Gets+ * negative when the window is moved backwards.+ */++ uInt match_length; /* length of best match */+ IPos prev_match; /* previous match */+ int match_available; /* set if previous match exists */+ uInt strstart; /* start of string to insert */+ uInt match_start; /* start of matching string */+ uInt lookahead; /* number of valid bytes ahead in window */++ uInt prev_length;+ /* Length of the best match at previous step. Matches not greater than this+ * are discarded. This is used in the lazy match evaluation.+ */++ uInt max_chain_length;+ /* To speed up deflation, hash chains are never searched beyond this+ * length. A higher limit improves compression ratio but degrades the+ * speed.+ */++ uInt max_lazy_match;+ /* Attempt to find a better match only when the current match is strictly+ * smaller than this value. This mechanism is used only for compression+ * levels >= 4.+ */+# define max_insert_length max_lazy_match+ /* Insert new strings in the hash table only if the match length is not+ * greater than this length. This saves time but degrades compression.+ * max_insert_length is used only for compression levels <= 3.+ */++ int level; /* compression level (1..9) */+ int strategy; /* favor or force Huffman coding*/++ uInt good_match;+ /* Use a faster search when the previous match is longer than this */++ int nice_match; /* Stop searching when current match exceeds this */++ /* used by trees.c: */+ /* Didn't use ct_data typedef below to supress compiler warning */+ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */+ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */+ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */++ struct tree_desc_s l_desc; /* desc. for literal tree */+ struct tree_desc_s d_desc; /* desc. for distance tree */+ struct tree_desc_s bl_desc; /* desc. for bit length tree */++ ush bl_count[MAX_BITS+1];+ /* number of codes at each bit length for an optimal tree */++ int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */+ int heap_len; /* number of elements in the heap */+ int heap_max; /* element of largest frequency */+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.+ * The same heap array is used to build all trees.+ */++ uch depth[2*L_CODES+1];+ /* Depth of each subtree used as tie breaker for trees of equal frequency+ */++ uchf *l_buf; /* buffer for literals or lengths */++ uInt lit_bufsize;+ /* Size of match buffer for literals/lengths. There are 4 reasons for+ * limiting lit_bufsize to 64K:+ * - frequencies can be kept in 16 bit counters+ * - if compression is not successful for the first block, all input+ * data is still in the window so we can still emit a stored block even+ * when input comes from standard input. (This can also be done for+ * all blocks if lit_bufsize is not greater than 32K.)+ * - if compression is not successful for a file smaller than 64K, we can+ * even emit a stored file instead of a stored block (saving 5 bytes).+ * This is applicable only for zip (not gzip or zlib).+ * - creating new Huffman trees less frequently may not provide fast+ * adaptation to changes in the input data statistics. (Take for+ * example a binary file with poorly compressible code followed by+ * a highly compressible string table.) Smaller buffer sizes give+ * fast adaptation but have of course the overhead of transmitting+ * trees more frequently.+ * - I can't count above 4+ */++ uInt last_lit; /* running index in l_buf */++ ushf *d_buf;+ /* Buffer for distances. To simplify the code, d_buf and l_buf have+ * the same number of elements. To use different lengths, an extra flag+ * array would be necessary.+ */++ ulg opt_len; /* bit length of current block with optimal trees */+ ulg static_len; /* bit length of current block with static trees */+ uInt matches; /* number of string matches in current block */+ int last_eob_len; /* bit length of EOB code for last block */++#ifdef DEBUG+ ulg compressed_len; /* total bit length of compressed file mod 2^32 */+ ulg bits_sent; /* bit length of compressed data sent mod 2^32 */+#endif++ ush bi_buf;+ /* Output buffer. bits are inserted starting at the bottom (least+ * significant bits).+ */+ int bi_valid;+ /* Number of valid bits in bi_buf. All bits above the last valid bit+ * are always zero.+ */++ ulg high_water;+ /* High water mark offset in window for initialized bytes -- bytes above+ * this are set to zero in order to avoid memory check warnings when+ * longest match routines access bytes past the input. This is then+ * updated to the new high water mark.+ */++} FAR deflate_state;++/* Output a byte on the stream.+ * IN assertion: there is enough room in pending_buf.+ */+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}+++#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)+/* Minimum amount of lookahead, except at the end of the input file.+ * See deflate.c for comments about the MIN_MATCH+1.+ */++#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD)+/* In order to simplify the code, particularly on 16 bit machines, match+ * distances are limited to MAX_DIST instead of WSIZE.+ */++#define WIN_INIT MAX_MATCH+/* Number of bytes after end of data in window to initialize in order to avoid+ memory checker errors from longest match routines */++ /* in trees.c */+void ZLIB_INTERNAL _tr_init OF((deflate_state *s));+int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));+void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,+ ulg stored_len, int last));+void ZLIB_INTERNAL _tr_align OF((deflate_state *s));+void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,+ ulg stored_len, int last));++#define d_code(dist) \+ ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])+/* Mapping from a distance to a distance code. dist is the distance - 1 and+ * must not have side effects. _dist_code[256] and _dist_code[257] are never+ * used.+ */++#ifndef DEBUG+/* Inline versions of _tr_tally for speed: */++#if defined(GEN_TREES_H) || !defined(STDC)+ extern uch ZLIB_INTERNAL _length_code[];+ extern uch ZLIB_INTERNAL _dist_code[];+#else+ extern const uch ZLIB_INTERNAL _length_code[];+ extern const uch ZLIB_INTERNAL _dist_code[];+#endif++# define _tr_tally_lit(s, c, flush) \+ { uch cc = (c); \+ s->d_buf[s->last_lit] = 0; \+ s->l_buf[s->last_lit++] = cc; \+ s->dyn_ltree[cc].Freq++; \+ flush = (s->last_lit == s->lit_bufsize-1); \+ }+# define _tr_tally_dist(s, distance, length, flush) \+ { uch len = (length); \+ ush dist = (distance); \+ s->d_buf[s->last_lit] = dist; \+ s->l_buf[s->last_lit++] = len; \+ dist--; \+ s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \+ s->dyn_dtree[d_code(dist)].Freq++; \+ flush = (s->last_lit == s->lit_bufsize-1); \+ }+#else+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)+# define _tr_tally_dist(s, distance, length, flush) \+ flush = _tr_tally(s, distance, length)+#endif++#endif /* DEFLATE_H */
+ cbits/inffast.h view
@@ -0,0 +1,11 @@+/* inffast.h -- header to use inffast.c+ * Copyright (C) 1995-2003, 2010 Mark Adler+ * For conditions of distribution and use, see copyright notice in zlib.h+ */++/* WARNING: this file should *not* be used by applications. It is+ part of the implementation of the compression library and is+ subject to change. Applications should only use zlib.h.+ */++void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
+ cbits/inffixed.h view
@@ -0,0 +1,94 @@+ /* inffixed.h -- table for decoding fixed codes+ * Generated automatically by makefixed().+ */++ /* WARNING: this file should *not* be used by applications. It+ is part of the implementation of the compression library and+ is subject to change. Applications should only use zlib.h.+ */++ static const code lenfix[512] = {+ {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},+ {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},+ {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},+ {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},+ {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},+ {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},+ {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},+ {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},+ {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},+ {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},+ {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},+ {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},+ {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},+ {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},+ {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},+ {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},+ {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},+ {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},+ {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},+ {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},+ {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},+ {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},+ {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},+ {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},+ {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},+ {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},+ {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},+ {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},+ {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},+ {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},+ {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},+ {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},+ {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},+ {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},+ {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},+ {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},+ {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},+ {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},+ {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},+ {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},+ {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},+ {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},+ {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},+ {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},+ {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},+ {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},+ {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},+ {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},+ {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},+ {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},+ {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},+ {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},+ {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},+ {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},+ {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},+ {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},+ {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},+ {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},+ {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},+ {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},+ {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},+ {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},+ {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},+ {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},+ {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},+ {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},+ {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},+ {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},+ {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},+ {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},+ {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},+ {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},+ {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},+ {0,9,255}+ };++ static const code distfix[32] = {+ {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},+ {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},+ {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},+ {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},+ {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},+ {22,5,193},{64,5,0}+ };
+ cbits/inflate.h view
@@ -0,0 +1,122 @@+/* inflate.h -- internal inflate state definition+ * Copyright (C) 1995-2009 Mark Adler+ * For conditions of distribution and use, see copyright notice in zlib.h+ */++/* WARNING: this file should *not* be used by applications. It is+ part of the implementation of the compression library and is+ subject to change. Applications should only use zlib.h.+ */++/* define NO_GZIP when compiling if you want to disable gzip header and+ trailer decoding by inflate(). NO_GZIP would be used to avoid linking in+ the crc code when it is not needed. For shared libraries, gzip decoding+ should be left enabled. */+#ifndef NO_GZIP+# define GUNZIP+#endif++/* Possible inflate modes between inflate() calls */+typedef enum {+ HEAD, /* i: waiting for magic header */+ FLAGS, /* i: waiting for method and flags (gzip) */+ TIME, /* i: waiting for modification time (gzip) */+ OS, /* i: waiting for extra flags and operating system (gzip) */+ EXLEN, /* i: waiting for extra length (gzip) */+ EXTRA, /* i: waiting for extra bytes (gzip) */+ NAME, /* i: waiting for end of file name (gzip) */+ COMMENT, /* i: waiting for end of comment (gzip) */+ HCRC, /* i: waiting for header crc (gzip) */+ DICTID, /* i: waiting for dictionary check value */+ DICT, /* waiting for inflateSetDictionary() call */+ TYPE, /* i: waiting for type bits, including last-flag bit */+ TYPEDO, /* i: same, but skip check to exit inflate on new block */+ STORED, /* i: waiting for stored size (length and complement) */+ COPY_, /* i/o: same as COPY below, but only first time in */+ COPY, /* i/o: waiting for input or output to copy stored block */+ TABLE, /* i: waiting for dynamic block table lengths */+ LENLENS, /* i: waiting for code length code lengths */+ CODELENS, /* i: waiting for length/lit and distance code lengths */+ LEN_, /* i: same as LEN below, but only first time in */+ LEN, /* i: waiting for length/lit/eob code */+ LENEXT, /* i: waiting for length extra bits */+ DIST, /* i: waiting for distance code */+ DISTEXT, /* i: waiting for distance extra bits */+ MATCH, /* o: waiting for output space to copy string */+ LIT, /* o: waiting for output space to write literal */+ CHECK, /* i: waiting for 32-bit check value */+ LENGTH, /* i: waiting for 32-bit length (gzip) */+ DONE, /* finished check, done -- remain here until reset */+ BAD, /* got a data error -- remain here until reset */+ MEM, /* got an inflate() memory error -- remain here until reset */+ SYNC /* looking for synchronization bytes to restart inflate() */+} inflate_mode;++/*+ State transitions between above modes -++ (most modes can go to BAD or MEM on error -- not shown for clarity)++ Process header:+ HEAD -> (gzip) or (zlib) or (raw)+ (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->+ HCRC -> TYPE+ (zlib) -> DICTID or TYPE+ DICTID -> DICT -> TYPE+ (raw) -> TYPEDO+ Read deflate blocks:+ TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK+ STORED -> COPY_ -> COPY -> TYPE+ TABLE -> LENLENS -> CODELENS -> LEN_+ LEN_ -> LEN+ Read deflate codes in fixed or dynamic block:+ LEN -> LENEXT or LIT or TYPE+ LENEXT -> DIST -> DISTEXT -> MATCH -> LEN+ LIT -> LEN+ Process trailer:+ CHECK -> LENGTH -> DONE+ */++/* state maintained between inflate() calls. Approximately 10K bytes. */+struct inflate_state {+ inflate_mode mode; /* current inflate mode */+ int last; /* true if processing last block */+ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */+ int havedict; /* true if dictionary provided */+ int flags; /* gzip header method and flags (0 if zlib) */+ unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */+ unsigned long check; /* protected copy of check value */+ unsigned long total; /* protected copy of output count */+ gz_headerp head; /* where to save gzip header information */+ /* sliding window */+ unsigned wbits; /* log base 2 of requested window size */+ unsigned wsize; /* window size or zero if not using window */+ unsigned whave; /* valid bytes in the window */+ unsigned wnext; /* window write index */+ unsigned char FAR *window; /* allocated sliding window, if needed */+ /* bit accumulator */+ unsigned long hold; /* input bit accumulator */+ unsigned bits; /* number of bits in "in" */+ /* for string and stored block copying */+ unsigned length; /* literal or length of data to copy */+ unsigned offset; /* distance back to copy string from */+ /* for table and code decoding */+ unsigned extra; /* extra bits needed */+ /* fixed and dynamic code tables */+ code const FAR *lencode; /* starting table for length/literal codes */+ code const FAR *distcode; /* starting table for distance codes */+ unsigned lenbits; /* index bits for lencode */+ unsigned distbits; /* index bits for distcode */+ /* dynamic table building */+ unsigned ncode; /* number of code length code lengths */+ unsigned nlen; /* number of length code lengths */+ unsigned ndist; /* number of distance code lengths */+ unsigned have; /* number of code lengths in lens[] */+ code FAR *next; /* next available space in codes[] */+ unsigned short lens[320]; /* temporary storage for code lengths */+ unsigned short work[288]; /* work area for code table building */+ code codes[ENOUGH]; /* space for code tables */+ int sane; /* if false, allow invalid distance too far */+ int back; /* bits back of last unprocessed length/lit */+ unsigned was; /* initial length of match */+};
+ cbits/inftrees.h view
@@ -0,0 +1,62 @@+/* inftrees.h -- header to use inftrees.c+ * Copyright (C) 1995-2005, 2010 Mark Adler+ * For conditions of distribution and use, see copyright notice in zlib.h+ */++/* WARNING: this file should *not* be used by applications. It is+ part of the implementation of the compression library and is+ subject to change. Applications should only use zlib.h.+ */++/* Structure for decoding tables. Each entry provides either the+ information needed to do the operation requested by the code that+ indexed that table entry, or it provides a pointer to another+ table that indexes more bits of the code. op indicates whether+ the entry is a pointer to another table, a literal, a length or+ distance, an end-of-block, or an invalid code. For a table+ pointer, the low four bits of op is the number of index bits of+ that table. For a length or distance, the low four bits of op+ is the number of extra bits to get after the code. bits is+ the number of bits in this code or part of the code to drop off+ of the bit buffer. val is the actual byte to output in the case+ of a literal, the base length or distance, or the offset from+ the current table to the next table. Each entry is four bytes. */+typedef struct {+ unsigned char op; /* operation, extra bits, table bits */+ unsigned char bits; /* bits in this part of the code */+ unsigned short val; /* offset in table or code value */+} code;++/* op values as set by inflate_table():+ 00000000 - literal+ 0000tttt - table link, tttt != 0 is the number of table index bits+ 0001eeee - length or distance, eeee is the number of extra bits+ 01100000 - end of block+ 01000000 - invalid code+ */++/* Maximum size of the dynamic table. The maximum number of code structures is+ 1444, which is the sum of 852 for literal/length codes and 592 for distance+ codes. These values were found by exhaustive searches using the program+ examples/enough.c found in the zlib distribtution. The arguments to that+ program are the number of symbols, the initial root table size, and the+ maximum bit length of a code. "enough 286 9 15" for literal/length codes+ returns returns 852, and "enough 30 6 15" for distance codes returns 592.+ The initial root table size (9 or 6) is found in the fifth argument of the+ inflate_table() calls in inflate.c and infback.c. If the root table size is+ changed, then these maximum sizes would be need to be recalculated and+ updated. */+#define ENOUGH_LENS 852+#define ENOUGH_DISTS 592+#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)++/* Type of code to build for inflate_table() */+typedef enum {+ CODES,+ LENS,+ DISTS+} codetype;++int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,+ unsigned codes, code FAR * FAR *table,+ unsigned FAR *bits, unsigned short FAR *work));
+ cbits/initWinSock.c view
@@ -0,0 +1,63 @@+#include "HsNet.h"+#include "HsFFI.h"++#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)++static int winsock_inited = 0;+static int winsock_uninited = 0;++/* Initialising WinSock... */+int+initWinSock ()+{+ WORD wVersionRequested;+ WSADATA wsaData; + int err;+#ifdef __HUGS__+ int optval = SO_SYNCHRONOUS_NONALERT;+#endif++ if (!winsock_inited) {+ wVersionRequested = MAKEWORD( 1, 1 );++ err = WSAStartup ( wVersionRequested, &wsaData );+ + if ( err != 0 ) {+ return err;+ }++ if ( LOBYTE( wsaData.wVersion ) != 1 ||+ HIBYTE( wsaData.wVersion ) != 1 ) {+ WSACleanup();+ return (-1);+ }+#ifdef __HUGS__+ /* By default, socket() creates sockets in overlapped mode+ * (so that async I/O is possible). The CRT can only handle+ * non-overlapped sockets, so turn off overlap mode here.+ */+ setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,+ &optval, sizeof(optval));+#endif++ winsock_inited = 1;+ }+ return 0;+}++static void+shutdownHandler(void)+{+ WSACleanup();+}++void+shutdownWinSock()+{+ if (!winsock_uninited) {+ atexit(shutdownHandler);+ winsock_uninited = 1;+ }+}++#endif
+ cbits/trees.h view
@@ -0,0 +1,128 @@+/* header created automatically with -DGEN_TREES_H */++local const ct_data static_ltree[L_CODES+2] = {+{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}},+{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}},+{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}},+{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}},+{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}},+{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}},+{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}},+{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}},+{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}},+{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}},+{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}},+{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}},+{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}},+{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}},+{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}},+{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}},+{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}},+{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}},+{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}},+{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}},+{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}},+{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}},+{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}},+{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}},+{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}},+{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}},+{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}},+{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}},+{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}},+{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}},+{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}},+{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}},+{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}},+{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}},+{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}},+{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}},+{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}},+{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}},+{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}},+{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}},+{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}},+{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}},+{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}},+{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}},+{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}},+{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}},+{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}},+{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}},+{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}},+{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}},+{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}},+{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}},+{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}},+{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}},+{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}},+{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}},+{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}},+{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}}+};++local const ct_data static_dtree[D_CODES] = {+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}+};++const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {+ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,+ 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10,+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17,+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29+};++const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12,+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28+};++local const int base_length[LENGTH_CODES] = {+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,+64, 80, 96, 112, 128, 160, 192, 224, 0+};++local const int base_dist[D_CODES] = {+ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24,+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,+ 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576+};+
+ cbits/winSockErr.c view
@@ -0,0 +1,76 @@+#include "HsNet.h"+#include "HsFFI.h"++#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#include <stdio.h>++/* to the end */++const char*+getWSErrorDescr(int err)+{+ static char otherErrMsg[256];++ switch (err) {+ case WSAEINTR: return "Interrupted function call (WSAEINTR)";+ case WSAEBADF: return "bad socket descriptor (WSAEBADF)";+ case WSAEACCES: return "Permission denied (WSAEACCESS)";+ case WSAEFAULT: return "Bad address (WSAEFAULT)";+ case WSAEINVAL: return "Invalid argument (WSAEINVAL)";+ case WSAEMFILE: return "Too many open files (WSAEMFILE)";+ case WSAEWOULDBLOCK: return "Resource temporarily unavailable (WSAEWOULDBLOCK)";+ case WSAEINPROGRESS: return "Operation now in progress (WSAEINPROGRESS)";+ case WSAEALREADY: return "Operation already in progress (WSAEALREADY)";+ case WSAENOTSOCK: return "Socket operation on non-socket (WSAENOTSOCK)";+ case WSAEDESTADDRREQ: return "Destination address required (WSAEDESTADDRREQ)";+ case WSAEMSGSIZE: return "Message too long (WSAEMSGSIZE)";+ case WSAEPROTOTYPE: return "Protocol wrong type for socket (WSAEPROTOTYPE)";+ case WSAENOPROTOOPT: return "Bad protocol option (WSAENOPROTOOPT)";+ case WSAEPROTONOSUPPORT: return "Protocol not supported (WSAEPROTONOSUPPORT)";+ case WSAESOCKTNOSUPPORT: return "Socket type not supported (WSAESOCKTNOSUPPORT)";+ case WSAEOPNOTSUPP: return "Operation not supported (WSAEOPNOTSUPP)";+ case WSAEPFNOSUPPORT: return "Protocol family not supported (WSAEPFNOSUPPORT)";+ case WSAEAFNOSUPPORT: return "Address family not supported by protocol family (WSAEAFNOSUPPORT)";+ case WSAEADDRINUSE: return "Address already in use (WSAEADDRINUSE)";+ case WSAEADDRNOTAVAIL: return "Cannot assign requested address (WSAEADDRNOTAVAIL)";+ case WSAENETDOWN: return "Network is down (WSAENETDOWN)";+ case WSAENETUNREACH: return "Network is unreachable (WSAENETUNREACH)";+ case WSAENETRESET: return "Network dropped connection on reset (WSAENETRESET)";+ case WSAECONNABORTED: return "Software caused connection abort (WSAECONNABORTED)";+ case WSAECONNRESET: return "Connection reset by peer (WSAECONNRESET)";+ case WSAENOBUFS: return "No buffer space available (WSAENOBUFS)";+ case WSAEISCONN: return "Socket is already connected (WSAEISCONN)";+ case WSAENOTCONN: return "Socket is not connected (WSAENOTCONN)";+ case WSAESHUTDOWN: return "Cannot send after socket shutdown (WSAESHUTDOWN)";+ case WSAETOOMANYREFS: return "Too many references (WSAETOOMANYREFS)";+ case WSAETIMEDOUT: return "Connection timed out (WSAETIMEDOUT)";+ case WSAECONNREFUSED: return "Connection refused (WSAECONNREFUSED)";+ case WSAELOOP: return "Too many levels of symbolic links (WSAELOOP)";+ case WSAENAMETOOLONG: return "Filename too long (WSAENAMETOOLONG)";+ case WSAEHOSTDOWN: return "Host is down (WSAEHOSTDOWN)";+ case WSAEHOSTUNREACH: return "Host is unreachable (WSAEHOSTUNREACH)";+ case WSAENOTEMPTY: return "Resource not empty (WSAENOTEMPTY)";+ case WSAEPROCLIM: return "Too many processes (WSAEPROCLIM)";+ case WSAEUSERS: return "Too many users (WSAEUSERS)";+ case WSAEDQUOT: return "Disk quota exceeded (WSAEDQUOT)";+ case WSAESTALE: return "Stale NFS file handle (WSAESTALE)";+ case WSAEREMOTE: return "Too many levels of remote in path (WSAEREMOTE)";+ case WSAEDISCON: return "Graceful shutdown in progress (WSAEDISCON)";+ case WSASYSNOTREADY: return "Network subsystem is unavailable (WSASYSNOTREADY)";+ case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range (WSAVERNOTSUPPORTED)";+ case WSANOTINITIALISED: return "Successful WSAStartup not yet performed (WSANOTINITIALISED)";+#ifdef WSATYPE_NOT_FOUND+ case WSATYPE_NOT_FOUND: return "Class type not found (WSATYPE_NOT_FOUND)";+#endif+ case WSAHOST_NOT_FOUND: return "Host not found (WSAHOST_NOT_FOUND)";+ case WSATRY_AGAIN: return "Nonauthoritative host not found (WSATRY_AGAIN)";+ case WSANO_RECOVERY: return "This is a nonrecoverable error (WSANO_RECOVERY)";+ case WSANO_DATA: return "Valid name, no data record of requested type (WSANO_DATA)";+ default:+ sprintf(otherErrMsg, "Unknown WinSock error: %u", err);+ return otherErrMsg;+ }+}++#endif+
+ cbits/zutil.h view
@@ -0,0 +1,274 @@+/* zutil.h -- internal interface and configuration of the compression library+ * Copyright (C) 1995-2010 Jean-loup Gailly.+ * For conditions of distribution and use, see copyright notice in zlib.h+ */++/* WARNING: this file should *not* be used by applications. It is+ part of the implementation of the compression library and is+ subject to change. Applications should only use zlib.h.+ */++/* @(#) $Id$ */++#ifndef ZUTIL_H+#define ZUTIL_H++#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ)+# define ZLIB_INTERNAL __attribute__((visibility ("hidden")))+#else+# define ZLIB_INTERNAL+#endif++#include "zlib.h"++#ifdef STDC+# if !(defined(_WIN32_WCE) && defined(_MSC_VER))+# include <stddef.h>+# endif+# include <string.h>+# include <stdlib.h>+#endif++#ifndef local+# define local static+#endif+/* compile with -Dlocal if your debugger can't find static symbols */++typedef unsigned char uch;+typedef uch FAR uchf;+typedef unsigned short ush;+typedef ush FAR ushf;+typedef unsigned long ulg;++extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */+/* (size given to avoid silly warnings with Visual C++) */++#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]++#define ERR_RETURN(strm,err) \+ return (strm->msg = (char*)ERR_MSG(err), (err))+/* To be used only when the state is known to be valid */++ /* common constants */++#ifndef DEF_WBITS+# define DEF_WBITS MAX_WBITS+#endif+/* default windowBits for decompression. MAX_WBITS is for compression only */++#if MAX_MEM_LEVEL >= 8+# define DEF_MEM_LEVEL 8+#else+# define DEF_MEM_LEVEL MAX_MEM_LEVEL+#endif+/* default memLevel */++#define STORED_BLOCK 0+#define STATIC_TREES 1+#define DYN_TREES 2+/* The three kinds of block type */++#define MIN_MATCH 3+#define MAX_MATCH 258+/* The minimum and maximum match lengths */++#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */++ /* target dependencies */++#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))+# define OS_CODE 0x00+# if defined(__TURBOC__) || defined(__BORLANDC__)+# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))+ /* Allow compilation with ANSI keywords only enabled */+ void _Cdecl farfree( void *block );+ void *_Cdecl farmalloc( unsigned long nbytes );+# else+# include <alloc.h>+# endif+# else /* MSC or DJGPP */+# include <malloc.h>+# endif+#endif++#ifdef AMIGA+# define OS_CODE 0x01+#endif++#if defined(VAXC) || defined(VMS)+# define OS_CODE 0x02+# define F_OPEN(name, mode) \+ fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")+#endif++#if defined(ATARI) || defined(atarist)+# define OS_CODE 0x05+#endif++#ifdef OS2+# define OS_CODE 0x06+# ifdef M_I86+# include <malloc.h>+# endif+#endif++#if defined(MACOS) || defined(TARGET_OS_MAC)+# define OS_CODE 0x07+# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os+# include <unix.h> /* for fdopen */+# else+# ifndef fdopen+# define fdopen(fd,mode) NULL /* No fdopen() */+# endif+# endif+#endif++#ifdef TOPS20+# define OS_CODE 0x0a+#endif++#ifdef WIN32+# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */+# define OS_CODE 0x0b+# endif+#endif++#ifdef __50SERIES /* Prime/PRIMOS */+# define OS_CODE 0x0f+#endif++#if defined(_BEOS_) || defined(RISCOS)+# define fdopen(fd,mode) NULL /* No fdopen() */+#endif++#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX+# if defined(_WIN32_WCE)+# define fdopen(fd,mode) NULL /* No fdopen() */+# ifndef _PTRDIFF_T_DEFINED+ typedef int ptrdiff_t;+# define _PTRDIFF_T_DEFINED+# endif+# else+# define fdopen(fd,type) _fdopen(fd,type)+# endif+#endif++#if defined(__BORLANDC__)+ #pragma warn -8004+ #pragma warn -8008+ #pragma warn -8066+#endif++/* provide prototypes for these when building zlib without LFS */+#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0+ ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));+ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));+#endif++ /* common defaults */++#ifndef OS_CODE+# define OS_CODE 0x03 /* assume Unix */+#endif++#ifndef F_OPEN+# define F_OPEN(name, mode) fopen((name), (mode))+#endif++ /* functions */++#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)+# ifndef HAVE_VSNPRINTF+# define HAVE_VSNPRINTF+# endif+#endif+#if defined(__CYGWIN__)+# ifndef HAVE_VSNPRINTF+# define HAVE_VSNPRINTF+# endif+#endif+#ifndef HAVE_VSNPRINTF+# ifdef MSDOS+ /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),+ but for now we just assume it doesn't. */+# define NO_vsnprintf+# endif+# ifdef __TURBOC__+# define NO_vsnprintf+# endif+# ifdef WIN32+ /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */+# if !defined(vsnprintf) && !defined(NO_vsnprintf)+# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 )+# define vsnprintf _vsnprintf+# endif+# endif+# endif+# ifdef __SASC+# define NO_vsnprintf+# endif+#endif+#ifdef VMS+# define NO_vsnprintf+#endif++#if defined(pyr)+# define NO_MEMCPY+#endif+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)+ /* Use our own functions for small and medium model with MSC <= 5.0.+ * You may have to use the same strategy for Borland C (untested).+ * The __SC__ check is for Symantec.+ */+# define NO_MEMCPY+#endif+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)+# define HAVE_MEMCPY+#endif+#ifdef HAVE_MEMCPY+# ifdef SMALL_MEDIUM /* MSDOS small or medium model */+# define zmemcpy _fmemcpy+# define zmemcmp _fmemcmp+# define zmemzero(dest, len) _fmemset(dest, 0, len)+# else+# define zmemcpy memcpy+# define zmemcmp memcmp+# define zmemzero(dest, len) memset(dest, 0, len)+# endif+#else+ void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));+ int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));+ void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));+#endif++/* Diagnostic functions */+#ifdef DEBUG+# include <stdio.h>+ extern int ZLIB_INTERNAL z_verbose;+ extern void ZLIB_INTERNAL z_error OF((char *m));+# define Assert(cond,msg) {if(!(cond)) z_error(msg);}+# define Trace(x) {if (z_verbose>=0) fprintf x ;}+# define Tracev(x) {if (z_verbose>0) fprintf x ;}+# define Tracevv(x) {if (z_verbose>1) fprintf x ;}+# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}+# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}+#else+# define Assert(cond,msg)+# define Trace(x)+# define Tracev(x)+# define Tracevv(x)+# define Tracec(c,x)+# define Tracecv(c,x)+#endif+++voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,+ unsigned size));+void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr));++#define ZALLOC(strm, items, size) \+ (*((strm)->zalloc))((strm)->opaque, (items), (size))+#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}++#endif /* ZUTIL_H */
+ config.guess view
@@ -0,0 +1,1497 @@+#! /bin/sh+# Attempt to guess a canonical system name.+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.++timestamp='2006-02-23'++# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA+# 02110-1301, USA.+#+# As a special exception to the GNU General Public License, if you+# distribute this file as part of a program that contains a+# configuration script generated by Autoconf, you may include it under+# the same distribution terms that you use for the rest of that program.+++# Originally written by Per Bothner <per@bothner.com>.+# Please send patches to <config-patches@gnu.org>. Submit a context+# diff and a properly formatted ChangeLog entry.+#+# This script attempts to guess a canonical system name similar to+# config.sub. If it succeeds, it prints the system name on stdout, and+# exits with 0. Otherwise, it exits with 1.+#+# The plan is that this can be called by configure scripts if you+# don't specify an explicit build system type.++me=`echo "$0" | sed -e 's,.*/,,'`++usage="\+Usage: $0 [OPTION]++Output the configuration name of the system \`$me' is run on.++Operation modes:+ -h, --help print this help, then exit+ -t, --time-stamp print date of last modification, then exit+ -v, --version print version number, then exit++Report bugs and patches to <config-patches@gnu.org>."++version="\+GNU config.guess ($timestamp)++Originally written by Per Bothner.+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005+Free Software Foundation, Inc.++This is free software; see the source for copying conditions. There is NO+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."++help="+Try \`$me --help' for more information."++# Parse command line+while test $# -gt 0 ; do+ case $1 in+ --time-stamp | --time* | -t )+ echo "$timestamp" ; exit ;;+ --version | -v )+ echo "$version" ; exit ;;+ --help | --h* | -h )+ echo "$usage"; exit ;;+ -- ) # Stop option processing+ shift; break ;;+ - ) # Use stdin as input.+ break ;;+ -* )+ echo "$me: invalid option $1$help" >&2+ exit 1 ;;+ * )+ break ;;+ esac+done++if test $# != 0; then+ echo "$me: too many arguments$help" >&2+ exit 1+fi++trap 'exit 1' 1 2 15++# CC_FOR_BUILD -- compiler used by this script. Note that the use of a+# compiler to aid in system detection is discouraged as it requires+# temporary files to be created and, as you can see below, it is a+# headache to deal with in a portable fashion.++# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still+# use `HOST_CC' if defined, but it is deprecated.++# Portable tmp directory creation inspired by the Autoconf team.++set_cc_for_build='+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;+: ${TMPDIR=/tmp} ;+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;+dummy=$tmp/dummy ;+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;+case $CC_FOR_BUILD,$HOST_CC,$CC in+ ,,) echo "int x;" > $dummy.c ;+ for c in cc gcc c89 c99 ; do+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then+ CC_FOR_BUILD="$c"; break ;+ fi ;+ done ;+ if test x"$CC_FOR_BUILD" = x ; then+ CC_FOR_BUILD=no_compiler_found ;+ fi+ ;;+ ,,*) CC_FOR_BUILD=$CC ;;+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;+esac ; set_cc_for_build= ;'++# This is needed to find uname on a Pyramid OSx when run in the BSD universe.+# (ghazi@noc.rutgers.edu 1994-08-24)+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then+ PATH=$PATH:/.attbin ; export PATH+fi++UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown++# Note: order is significant - the case branches are not exclusive.++case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in+ *:NetBSD:*:*)+ # NetBSD (nbsd) targets should (where applicable) match one or+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently+ # switched to ELF, *-*-netbsd* would select the old+ # object file format. This provides both forward+ # compatibility and a consistent mechanism for selecting the+ # object file format.+ #+ # Note: NetBSD doesn't particularly care about the vendor+ # portion of the name. We always set it to "unknown".+ sysctl="sysctl -n hw.machine_arch"+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`+ case "${UNAME_MACHINE_ARCH}" in+ armeb) machine=armeb-unknown ;;+ arm*) machine=arm-unknown ;;+ sh3el) machine=shl-unknown ;;+ sh3eb) machine=sh-unknown ;;+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;+ esac+ # The Operating System including object format, if it has switched+ # to ELF recently, or will in the future.+ case "${UNAME_MACHINE_ARCH}" in+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)+ eval $set_cc_for_build+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \+ | grep __ELF__ >/dev/null+ then+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).+ # Return netbsd for either. FIX?+ os=netbsd+ else+ os=netbsdelf+ fi+ ;;+ *)+ os=netbsd+ ;;+ esac+ # The OS release+ # Debian GNU/NetBSD machines have a different userland, and+ # thus, need a distinct triplet. However, they do not need+ # kernel version information, so it can be replaced with a+ # suitable tag, in the style of linux-gnu.+ case "${UNAME_VERSION}" in+ Debian*)+ release='-gnu'+ ;;+ *)+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`+ ;;+ esac+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:+ # contains redundant information, the shorter form:+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.+ echo "${machine}-${os}${release}"+ exit ;;+ *:OpenBSD:*:*)+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`+ echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}+ exit ;;+ *:ekkoBSD:*:*)+ echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}+ exit ;;+ *:SolidBSD:*:*)+ echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}+ exit ;;+ macppc:MirBSD:*:*)+ echo powerppc-unknown-mirbsd${UNAME_RELEASE}+ exit ;;+ *:MirBSD:*:*)+ echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}+ exit ;;+ alpha:OSF1:*:*)+ case $UNAME_RELEASE in+ *4.0)+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`+ ;;+ *5.*)+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`+ ;;+ esac+ # According to Compaq, /usr/sbin/psrinfo has been available on+ # OSF/1 and Tru64 systems produced since 1995. I hope that+ # covers most systems running today. This code pipes the CPU+ # types through head -n 1, so we only detect the type of CPU 0.+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`+ case "$ALPHA_CPU_TYPE" in+ "EV4 (21064)")+ UNAME_MACHINE="alpha" ;;+ "EV4.5 (21064)")+ UNAME_MACHINE="alpha" ;;+ "LCA4 (21066/21068)")+ UNAME_MACHINE="alpha" ;;+ "EV5 (21164)")+ UNAME_MACHINE="alphaev5" ;;+ "EV5.6 (21164A)")+ UNAME_MACHINE="alphaev56" ;;+ "EV5.6 (21164PC)")+ UNAME_MACHINE="alphapca56" ;;+ "EV5.7 (21164PC)")+ UNAME_MACHINE="alphapca57" ;;+ "EV6 (21264)")+ UNAME_MACHINE="alphaev6" ;;+ "EV6.7 (21264A)")+ UNAME_MACHINE="alphaev67" ;;+ "EV6.8CB (21264C)")+ UNAME_MACHINE="alphaev68" ;;+ "EV6.8AL (21264B)")+ UNAME_MACHINE="alphaev68" ;;+ "EV6.8CX (21264D)")+ UNAME_MACHINE="alphaev68" ;;+ "EV6.9A (21264/EV69A)")+ UNAME_MACHINE="alphaev69" ;;+ "EV7 (21364)")+ UNAME_MACHINE="alphaev7" ;;+ "EV7.9 (21364A)")+ UNAME_MACHINE="alphaev79" ;;+ esac+ # A Pn.n version is a patched version.+ # A Vn.n version is a released version.+ # A Tn.n version is a released field test version.+ # A Xn.n version is an unreleased experimental baselevel.+ # 1.2 uses "1.2" for uname -r.+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`+ exit ;;+ Alpha\ *:Windows_NT*:*)+ # How do we know it's Interix rather than the generic POSIX subsystem?+ # Should we change UNAME_MACHINE based on the output of uname instead+ # of the specific Alpha model?+ echo alpha-pc-interix+ exit ;;+ 21064:Windows_NT:50:3)+ echo alpha-dec-winnt3.5+ exit ;;+ Amiga*:UNIX_System_V:4.0:*)+ echo m68k-unknown-sysv4+ exit ;;+ *:[Aa]miga[Oo][Ss]:*:*)+ echo ${UNAME_MACHINE}-unknown-amigaos+ exit ;;+ *:[Mm]orph[Oo][Ss]:*:*)+ echo ${UNAME_MACHINE}-unknown-morphos+ exit ;;+ *:OS/390:*:*)+ echo i370-ibm-openedition+ exit ;;+ *:z/VM:*:*)+ echo s390-ibm-zvmoe+ exit ;;+ *:OS400:*:*)+ echo powerpc-ibm-os400+ exit ;;+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)+ echo arm-acorn-riscix${UNAME_RELEASE}+ exit ;;+ arm:riscos:*:*|arm:RISCOS:*:*)+ echo arm-unknown-riscos+ exit ;;+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)+ echo hppa1.1-hitachi-hiuxmpp+ exit ;;+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.+ if test "`(/bin/universe) 2>/dev/null`" = att ; then+ echo pyramid-pyramid-sysv3+ else+ echo pyramid-pyramid-bsd+ fi+ exit ;;+ NILE*:*:*:dcosx)+ echo pyramid-pyramid-svr4+ exit ;;+ DRS?6000:unix:4.0:6*)+ echo sparc-icl-nx6+ exit ;;+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)+ case `/usr/bin/uname -p` in+ sparc) echo sparc-icl-nx7; exit ;;+ esac ;;+ sun4H:SunOS:5.*:*)+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`+ exit ;;+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`+ exit ;;+ i86pc:SunOS:5.*:*)+ echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`+ exit ;;+ sun4*:SunOS:6*:*)+ # According to config.sub, this is the proper way to canonicalize+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but+ # it's likely to be more like Solaris than SunOS4.+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`+ exit ;;+ sun4*:SunOS:*:*)+ case "`/usr/bin/arch -k`" in+ Series*|S4*)+ UNAME_RELEASE=`uname -v`+ ;;+ esac+ # Japanese Language versions have a version number like `4.1.3-JL'.+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`+ exit ;;+ sun3*:SunOS:*:*)+ echo m68k-sun-sunos${UNAME_RELEASE}+ exit ;;+ sun*:*:4.2BSD:*)+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3+ case "`/bin/arch`" in+ sun3)+ echo m68k-sun-sunos${UNAME_RELEASE}+ ;;+ sun4)+ echo sparc-sun-sunos${UNAME_RELEASE}+ ;;+ esac+ exit ;;+ aushp:SunOS:*:*)+ echo sparc-auspex-sunos${UNAME_RELEASE}+ exit ;;+ # The situation for MiNT is a little confusing. The machine name+ # can be virtually everything (everything which is not+ # "atarist" or "atariste" at least should have a processor+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"+ # to the lowercase version "mint" (or "freemint"). Finally+ # the system name "TOS" denotes a system which is actually not+ # MiNT. But MiNT is downward compatible to TOS, so this should+ # be no problem.+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)+ echo m68k-atari-mint${UNAME_RELEASE}+ exit ;;+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)+ echo m68k-atari-mint${UNAME_RELEASE}+ exit ;;+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)+ echo m68k-atari-mint${UNAME_RELEASE}+ exit ;;+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)+ echo m68k-milan-mint${UNAME_RELEASE}+ exit ;;+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)+ echo m68k-hades-mint${UNAME_RELEASE}+ exit ;;+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)+ echo m68k-unknown-mint${UNAME_RELEASE}+ exit ;;+ m68k:machten:*:*)+ echo m68k-apple-machten${UNAME_RELEASE}+ exit ;;+ powerpc:machten:*:*)+ echo powerpc-apple-machten${UNAME_RELEASE}+ exit ;;+ RISC*:Mach:*:*)+ echo mips-dec-mach_bsd4.3+ exit ;;+ RISC*:ULTRIX:*:*)+ echo mips-dec-ultrix${UNAME_RELEASE}+ exit ;;+ VAX*:ULTRIX*:*:*)+ echo vax-dec-ultrix${UNAME_RELEASE}+ exit ;;+ 2020:CLIX:*:* | 2430:CLIX:*:*)+ echo clipper-intergraph-clix${UNAME_RELEASE}+ exit ;;+ mips:*:*:UMIPS | mips:*:*:RISCos)+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+#ifdef __cplusplus+#include <stdio.h> /* for printf() prototype */+ int main (int argc, char *argv[]) {+#else+ int main (argc, argv) int argc; char *argv[]; {+#endif+ #if defined (host_mips) && defined (MIPSEB)+ #if defined (SYSTYPE_SYSV)+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);+ #endif+ #if defined (SYSTYPE_SVR4)+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);+ #endif+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);+ #endif+ #endif+ exit (-1);+ }+EOF+ $CC_FOR_BUILD -o $dummy $dummy.c &&+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&+ SYSTEM_NAME=`$dummy $dummyarg` &&+ { echo "$SYSTEM_NAME"; exit; }+ echo mips-mips-riscos${UNAME_RELEASE}+ exit ;;+ Motorola:PowerMAX_OS:*:*)+ echo powerpc-motorola-powermax+ exit ;;+ Motorola:*:4.3:PL8-*)+ echo powerpc-harris-powermax+ exit ;;+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)+ echo powerpc-harris-powermax+ exit ;;+ Night_Hawk:Power_UNIX:*:*)+ echo powerpc-harris-powerunix+ exit ;;+ m88k:CX/UX:7*:*)+ echo m88k-harris-cxux7+ exit ;;+ m88k:*:4*:R4*)+ echo m88k-motorola-sysv4+ exit ;;+ m88k:*:3*:R3*)+ echo m88k-motorola-sysv3+ exit ;;+ AViiON:dgux:*:*)+ # DG/UX returns AViiON for all architectures+ UNAME_PROCESSOR=`/usr/bin/uname -p`+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]+ then+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \+ [ ${TARGET_BINARY_INTERFACE}x = x ]+ then+ echo m88k-dg-dgux${UNAME_RELEASE}+ else+ echo m88k-dg-dguxbcs${UNAME_RELEASE}+ fi+ else+ echo i586-dg-dgux${UNAME_RELEASE}+ fi+ exit ;;+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)+ echo m88k-dolphin-sysv3+ exit ;;+ M88*:*:R3*:*)+ # Delta 88k system running SVR3+ echo m88k-motorola-sysv3+ exit ;;+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)+ echo m88k-tektronix-sysv3+ exit ;;+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)+ echo m68k-tektronix-bsd+ exit ;;+ *:IRIX*:*:*)+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`+ exit ;;+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '+ i*86:AIX:*:*)+ echo i386-ibm-aix+ exit ;;+ ia64:AIX:*:*)+ if [ -x /usr/bin/oslevel ] ; then+ IBM_REV=`/usr/bin/oslevel`+ else+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}+ fi+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}+ exit ;;+ *:AIX:2:3)+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+ #include <sys/systemcfg.h>++ main()+ {+ if (!__power_pc())+ exit(1);+ puts("powerpc-ibm-aix3.2.5");+ exit(0);+ }+EOF+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`+ then+ echo "$SYSTEM_NAME"+ else+ echo rs6000-ibm-aix3.2.5+ fi+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then+ echo rs6000-ibm-aix3.2.4+ else+ echo rs6000-ibm-aix3.2+ fi+ exit ;;+ *:AIX:*:[45])+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then+ IBM_ARCH=rs6000+ else+ IBM_ARCH=powerpc+ fi+ if [ -x /usr/bin/oslevel ] ; then+ IBM_REV=`/usr/bin/oslevel`+ else+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}+ fi+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}+ exit ;;+ *:AIX:*:*)+ echo rs6000-ibm-aix+ exit ;;+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)+ echo romp-ibm-bsd4.4+ exit ;;+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to+ exit ;; # report: romp-ibm BSD 4.3+ *:BOSX:*:*)+ echo rs6000-bull-bosx+ exit ;;+ DPX/2?00:B.O.S.:*:*)+ echo m68k-bull-sysv3+ exit ;;+ 9000/[34]??:4.3bsd:1.*:*)+ echo m68k-hp-bsd+ exit ;;+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)+ echo m68k-hp-bsd4.4+ exit ;;+ 9000/[34678]??:HP-UX:*:*)+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`+ case "${UNAME_MACHINE}" in+ 9000/31? ) HP_ARCH=m68000 ;;+ 9000/[34]?? ) HP_ARCH=m68k ;;+ 9000/[678][0-9][0-9])+ if [ -x /usr/bin/getconf ]; then+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`+ case "${sc_cpu_version}" in+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1+ 532) # CPU_PA_RISC2_0+ case "${sc_kernel_bits}" in+ 32) HP_ARCH="hppa2.0n" ;;+ 64) HP_ARCH="hppa2.0w" ;;+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20+ esac ;;+ esac+ fi+ if [ "${HP_ARCH}" = "" ]; then+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c++ #define _HPUX_SOURCE+ #include <stdlib.h>+ #include <unistd.h>++ int main ()+ {+ #if defined(_SC_KERNEL_BITS)+ long bits = sysconf(_SC_KERNEL_BITS);+ #endif+ long cpu = sysconf (_SC_CPU_VERSION);++ switch (cpu)+ {+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;+ case CPU_PA_RISC2_0:+ #if defined(_SC_KERNEL_BITS)+ switch (bits)+ {+ case 64: puts ("hppa2.0w"); break;+ case 32: puts ("hppa2.0n"); break;+ default: puts ("hppa2.0"); break;+ } break;+ #else /* !defined(_SC_KERNEL_BITS) */+ puts ("hppa2.0"); break;+ #endif+ default: puts ("hppa1.0"); break;+ }+ exit (0);+ }+EOF+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`+ test -z "$HP_ARCH" && HP_ARCH=hppa+ fi ;;+ esac+ if [ ${HP_ARCH} = "hppa2.0w" ]+ then+ eval $set_cc_for_build++ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler+ # generating 64-bit code. GNU and HP use different nomenclature:+ #+ # $ CC_FOR_BUILD=cc ./config.guess+ # => hppa2.0w-hp-hpux11.23+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess+ # => hppa64-hp-hpux11.23++ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |+ grep __LP64__ >/dev/null+ then+ HP_ARCH="hppa2.0w"+ else+ HP_ARCH="hppa64"+ fi+ fi+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}+ exit ;;+ ia64:HP-UX:*:*)+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`+ echo ia64-hp-hpux${HPUX_REV}+ exit ;;+ 3050*:HI-UX:*:*)+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+ #include <unistd.h>+ int+ main ()+ {+ long cpu = sysconf (_SC_CPU_VERSION);+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct+ results, however. */+ if (CPU_IS_PA_RISC (cpu))+ {+ switch (cpu)+ {+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;+ default: puts ("hppa-hitachi-hiuxwe2"); break;+ }+ }+ else if (CPU_IS_HP_MC68K (cpu))+ puts ("m68k-hitachi-hiuxwe2");+ else puts ("unknown-hitachi-hiuxwe2");+ exit (0);+ }+EOF+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&+ { echo "$SYSTEM_NAME"; exit; }+ echo unknown-hitachi-hiuxwe2+ exit ;;+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )+ echo hppa1.1-hp-bsd+ exit ;;+ 9000/8??:4.3bsd:*:*)+ echo hppa1.0-hp-bsd+ exit ;;+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)+ echo hppa1.0-hp-mpeix+ exit ;;+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )+ echo hppa1.1-hp-osf+ exit ;;+ hp8??:OSF1:*:*)+ echo hppa1.0-hp-osf+ exit ;;+ i*86:OSF1:*:*)+ if [ -x /usr/sbin/sysversion ] ; then+ echo ${UNAME_MACHINE}-unknown-osf1mk+ else+ echo ${UNAME_MACHINE}-unknown-osf1+ fi+ exit ;;+ parisc*:Lites*:*:*)+ echo hppa1.1-hp-lites+ exit ;;+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)+ echo c1-convex-bsd+ exit ;;+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)+ if getsysinfo -f scalar_acc+ then echo c32-convex-bsd+ else echo c2-convex-bsd+ fi+ exit ;;+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)+ echo c34-convex-bsd+ exit ;;+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)+ echo c38-convex-bsd+ exit ;;+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)+ echo c4-convex-bsd+ exit ;;+ CRAY*Y-MP:*:*:*)+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'+ exit ;;+ CRAY*[A-Z]90:*:*:*)+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \+ -e 's/\.[^.]*$/.X/'+ exit ;;+ CRAY*TS:*:*:*)+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'+ exit ;;+ CRAY*T3E:*:*:*)+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'+ exit ;;+ CRAY*SV1:*:*:*)+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'+ exit ;;+ *:UNICOS/mp:*:*)+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'+ exit ;;+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"+ exit ;;+ 5000:UNIX_System_V:4.*:*)+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"+ exit ;;+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}+ exit ;;+ sparc*:BSD/OS:*:*)+ echo sparc-unknown-bsdi${UNAME_RELEASE}+ exit ;;+ *:BSD/OS:*:*)+ echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}+ exit ;;+ *:FreeBSD:*:*)+ case ${UNAME_MACHINE} in+ pc98)+ echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;+ *)+ echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;+ esac+ exit ;;+ i*:CYGWIN*:*)+ echo ${UNAME_MACHINE}-pc-cygwin+ exit ;;+ i*:MINGW*:*)+ echo ${UNAME_MACHINE}-pc-mingw32+ exit ;;+ i*:MSYS_NT-*:*:*)+ echo ${UNAME_MACHINE}-pc-mingw32+ exit ;;+ i*:windows32*:*)+ # uname -m includes "-pc" on this system.+ echo ${UNAME_MACHINE}-mingw32+ exit ;;+ i*:PW*:*)+ echo ${UNAME_MACHINE}-pc-pw32+ exit ;;+ x86:Interix*:[345]*)+ echo i586-pc-interix${UNAME_RELEASE}+ exit ;;+ EM64T:Interix*:[345]*)+ echo x86_64-unknown-interix${UNAME_RELEASE}+ exit ;;+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)+ echo i${UNAME_MACHINE}-pc-mks+ exit ;;+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)+ # How do we know it's Interix rather than the generic POSIX subsystem?+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we+ # UNAME_MACHINE based on the output of uname instead of i386?+ echo i586-pc-interix+ exit ;;+ i*:UWIN*:*)+ echo ${UNAME_MACHINE}-pc-uwin+ exit ;;+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)+ echo x86_64-unknown-cygwin+ exit ;;+ p*:CYGWIN*:*)+ echo powerpcle-unknown-cygwin+ exit ;;+ prep*:SunOS:5.*:*)+ echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`+ exit ;;+ *:GNU:*:*)+ # the GNU system+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`+ exit ;;+ *:GNU/*:*:*)+ # other systems with GNU libc and userland+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu+ exit ;;+ i*86:Minix:*:*)+ echo ${UNAME_MACHINE}-pc-minix+ exit ;;+ arm*:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ cris:Linux:*:*)+ echo cris-axis-linux-gnu+ exit ;;+ crisv32:Linux:*:*)+ echo crisv32-axis-linux-gnu+ exit ;;+ frv:Linux:*:*)+ echo frv-unknown-linux-gnu+ exit ;;+ ia64:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ m32r*:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ m68*:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ mips:Linux:*:*)+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+ #undef CPU+ #undef mips+ #undef mipsel+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)+ CPU=mipsel+ #else+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)+ CPU=mips+ #else+ CPU=+ #endif+ #endif+EOF+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '+ /^CPU/{+ s: ::g+ p+ }'`"+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }+ ;;+ mips64:Linux:*:*)+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+ #undef CPU+ #undef mips64+ #undef mips64el+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)+ CPU=mips64el+ #else+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)+ CPU=mips64+ #else+ CPU=+ #endif+ #endif+EOF+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '+ /^CPU/{+ s: ::g+ p+ }'`"+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }+ ;;+ or32:Linux:*:*)+ echo or32-unknown-linux-gnu+ exit ;;+ ppc:Linux:*:*)+ echo powerpc-unknown-linux-gnu+ exit ;;+ ppc64:Linux:*:*)+ echo powerpc64-unknown-linux-gnu+ exit ;;+ alpha:Linux:*:*)+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in+ EV5) UNAME_MACHINE=alphaev5 ;;+ EV56) UNAME_MACHINE=alphaev56 ;;+ PCA56) UNAME_MACHINE=alphapca56 ;;+ PCA57) UNAME_MACHINE=alphapca56 ;;+ EV6) UNAME_MACHINE=alphaev6 ;;+ EV67) UNAME_MACHINE=alphaev67 ;;+ EV68*) UNAME_MACHINE=alphaev68 ;;+ esac+ objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi+ echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}+ exit ;;+ parisc:Linux:*:* | hppa:Linux:*:*)+ # Look for CPU level+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in+ PA7*) echo hppa1.1-unknown-linux-gnu ;;+ PA8*) echo hppa2.0-unknown-linux-gnu ;;+ *) echo hppa-unknown-linux-gnu ;;+ esac+ exit ;;+ parisc64:Linux:*:* | hppa64:Linux:*:*)+ echo hppa64-unknown-linux-gnu+ exit ;;+ s390:Linux:*:* | s390x:Linux:*:*)+ echo ${UNAME_MACHINE}-ibm-linux+ exit ;;+ sh64*:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ sh*:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ sparc:Linux:*:* | sparc64:Linux:*:*)+ echo ${UNAME_MACHINE}-unknown-linux-gnu+ exit ;;+ vax:Linux:*:*)+ echo ${UNAME_MACHINE}-dec-linux-gnu+ exit ;;+ x86_64:Linux:*:*)+ echo x86_64-unknown-linux-gnu+ exit ;;+ i*86:Linux:*:*)+ # The BFD linker knows what the default object file format is, so+ # first see if it will tell us. cd to the root directory to prevent+ # problems with other programs or directories called `ld' in the path.+ # Set LC_ALL=C to ensure ld outputs messages in English.+ ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \+ | sed -ne '/supported targets:/!d+ s/[ ][ ]*/ /g+ s/.*supported targets: *//+ s/ .*//+ p'`+ case "$ld_supported_targets" in+ elf32-i386)+ TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"+ ;;+ a.out-i386-linux)+ echo "${UNAME_MACHINE}-pc-linux-gnuaout"+ exit ;;+ coff-i386)+ echo "${UNAME_MACHINE}-pc-linux-gnucoff"+ exit ;;+ "")+ # Either a pre-BFD a.out linker (linux-gnuoldld) or+ # one that does not give us useful --help.+ echo "${UNAME_MACHINE}-pc-linux-gnuoldld"+ exit ;;+ esac+ # Determine whether the default compiler is a.out or elf+ eval $set_cc_for_build+ sed 's/^ //' << EOF >$dummy.c+ #include <features.h>+ #ifdef __ELF__+ # ifdef __GLIBC__+ # if __GLIBC__ >= 2+ LIBC=gnu+ # else+ LIBC=gnulibc1+ # endif+ # else+ LIBC=gnulibc1+ # endif+ #else+ #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__sun)+ LIBC=gnu+ #else+ LIBC=gnuaout+ #endif+ #endif+ #ifdef __dietlibc__+ LIBC=dietlibc+ #endif+EOF+ eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '+ /^LIBC/{+ s: ::g+ p+ }'`"+ test x"${LIBC}" != x && {+ echo "${UNAME_MACHINE}-pc-linux-${LIBC}"+ exit+ }+ test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }+ ;;+ i*86:DYNIX/ptx:4*:*)+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.+ # earlier versions are messed up and put the nodename in both+ # sysname and nodename.+ echo i386-sequent-sysv4+ exit ;;+ i*86:UNIX_SV:4.2MP:2.*)+ # Unixware is an offshoot of SVR4, but it has its own version+ # number series starting with 2...+ # I am not positive that other SVR4 systems won't match this,+ # I just have to hope. -- rms.+ # Use sysv4.2uw... so that sysv4* matches it.+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}+ exit ;;+ i*86:OS/2:*:*)+ # If we were able to find `uname', then EMX Unix compatibility+ # is probably installed.+ echo ${UNAME_MACHINE}-pc-os2-emx+ exit ;;+ i*86:XTS-300:*:STOP)+ echo ${UNAME_MACHINE}-unknown-stop+ exit ;;+ i*86:atheos:*:*)+ echo ${UNAME_MACHINE}-unknown-atheos+ exit ;;+ i*86:syllable:*:*)+ echo ${UNAME_MACHINE}-pc-syllable+ exit ;;+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)+ echo i386-unknown-lynxos${UNAME_RELEASE}+ exit ;;+ i*86:*DOS:*:*)+ echo ${UNAME_MACHINE}-pc-msdosdjgpp+ exit ;;+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}+ else+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}+ fi+ exit ;;+ i*86:*:5:[678]*)+ # UnixWare 7.x, OpenUNIX and OpenServer 6.+ case `/bin/uname -X | grep "^Machine"` in+ *486*) UNAME_MACHINE=i486 ;;+ *Pentium) UNAME_MACHINE=i586 ;;+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;+ esac+ echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}+ exit ;;+ i*86:*:3.2:*)+ if test -f /usr/options/cb.name; then+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL+ elif /bin/uname -X 2>/dev/null >/dev/null ; then+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \+ && UNAME_MACHINE=i586+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \+ && UNAME_MACHINE=i686+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \+ && UNAME_MACHINE=i686+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL+ else+ echo ${UNAME_MACHINE}-pc-sysv32+ fi+ exit ;;+ pc:*:*:*)+ # Left here for compatibility:+ # uname -m prints for DJGPP always 'pc', but it prints nothing about+ # the processor, so we play safe by assuming i386.+ echo i386-pc-msdosdjgpp+ exit ;;+ Intel:Mach:3*:*)+ echo i386-pc-mach3+ exit ;;+ paragon:*:*:*)+ echo i860-intel-osf1+ exit ;;+ i860:*:4.*:*) # i860-SVR4+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4+ else # Add other i860-SVR4 vendors below as they are discovered.+ echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4+ fi+ exit ;;+ mini*:CTIX:SYS*5:*)+ # "miniframe"+ echo m68010-convergent-sysv+ exit ;;+ mc68k:UNIX:SYSTEM5:3.51m)+ echo m68k-convergent-sysv+ exit ;;+ M680?0:D-NIX:5.3:*)+ echo m68k-diab-dnix+ exit ;;+ M68*:*:R3V[5678]*:*)+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)+ OS_REL=''+ test -r /etc/.relid \+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \+ && { echo i486-ncr-sysv4; exit; } ;;+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)+ echo m68k-unknown-lynxos${UNAME_RELEASE}+ exit ;;+ mc68030:UNIX_System_V:4.*:*)+ echo m68k-atari-sysv4+ exit ;;+ TSUNAMI:LynxOS:2.*:*)+ echo sparc-unknown-lynxos${UNAME_RELEASE}+ exit ;;+ rs6000:LynxOS:2.*:*)+ echo rs6000-unknown-lynxos${UNAME_RELEASE}+ exit ;;+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)+ echo powerpc-unknown-lynxos${UNAME_RELEASE}+ exit ;;+ SM[BE]S:UNIX_SV:*:*)+ echo mips-dde-sysv${UNAME_RELEASE}+ exit ;;+ RM*:ReliantUNIX-*:*:*)+ echo mips-sni-sysv4+ exit ;;+ RM*:SINIX-*:*:*)+ echo mips-sni-sysv4+ exit ;;+ *:SINIX-*:*:*)+ if uname -p 2>/dev/null >/dev/null ; then+ UNAME_MACHINE=`(uname -p) 2>/dev/null`+ echo ${UNAME_MACHINE}-sni-sysv4+ else+ echo ns32k-sni-sysv+ fi+ exit ;;+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort+ # says <Richard.M.Bartel@ccMail.Census.GOV>+ echo i586-unisys-sysv4+ exit ;;+ *:UNIX_System_V:4*:FTX*)+ # From Gerald Hewes <hewes@openmarket.com>.+ # How about differentiating between stratus architectures? -djm+ echo hppa1.1-stratus-sysv4+ exit ;;+ *:*:*:FTX*)+ # From seanf@swdc.stratus.com.+ echo i860-stratus-sysv4+ exit ;;+ i*86:VOS:*:*)+ # From Paul.Green@stratus.com.+ echo ${UNAME_MACHINE}-stratus-vos+ exit ;;+ *:VOS:*:*)+ # From Paul.Green@stratus.com.+ echo hppa1.1-stratus-vos+ exit ;;+ mc68*:A/UX:*:*)+ echo m68k-apple-aux${UNAME_RELEASE}+ exit ;;+ news*:NEWS-OS:6*:*)+ echo mips-sony-newsos6+ exit ;;+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)+ if [ -d /usr/nec ]; then+ echo mips-nec-sysv${UNAME_RELEASE}+ else+ echo mips-unknown-sysv${UNAME_RELEASE}+ fi+ exit ;;+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.+ echo powerpc-be-beos+ exit ;;+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.+ echo powerpc-apple-beos+ exit ;;+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.+ echo i586-pc-beos+ exit ;;+ SX-4:SUPER-UX:*:*)+ echo sx4-nec-superux${UNAME_RELEASE}+ exit ;;+ SX-5:SUPER-UX:*:*)+ echo sx5-nec-superux${UNAME_RELEASE}+ exit ;;+ SX-6:SUPER-UX:*:*)+ echo sx6-nec-superux${UNAME_RELEASE}+ exit ;;+ Power*:Rhapsody:*:*)+ echo powerpc-apple-rhapsody${UNAME_RELEASE}+ exit ;;+ *:Rhapsody:*:*)+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}+ exit ;;+ *:Darwin:*:*)+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown+ case $UNAME_PROCESSOR in+ unknown) UNAME_PROCESSOR=powerpc ;;+ esac+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}+ exit ;;+ *:procnto*:*:* | *:QNX:[0123456789]*:*)+ UNAME_PROCESSOR=`uname -p`+ if test "$UNAME_PROCESSOR" = "x86"; then+ UNAME_PROCESSOR=i386+ UNAME_MACHINE=pc+ fi+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}+ exit ;;+ *:QNX:*:4*)+ echo i386-pc-qnx+ exit ;;+ NSE-?:NONSTOP_KERNEL:*:*)+ echo nse-tandem-nsk${UNAME_RELEASE}+ exit ;;+ NSR-?:NONSTOP_KERNEL:*:*)+ echo nsr-tandem-nsk${UNAME_RELEASE}+ exit ;;+ *:NonStop-UX:*:*)+ echo mips-compaq-nonstopux+ exit ;;+ BS2000:POSIX*:*:*)+ echo bs2000-siemens-sysv+ exit ;;+ DS/*:UNIX_System_V:*:*)+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}+ exit ;;+ *:Plan9:*:*)+ # "uname -m" is not consistent, so use $cputype instead. 386+ # is converted to i386 for consistency with other x86+ # operating systems.+ if test "$cputype" = "386"; then+ UNAME_MACHINE=i386+ else+ UNAME_MACHINE="$cputype"+ fi+ echo ${UNAME_MACHINE}-unknown-plan9+ exit ;;+ *:TOPS-10:*:*)+ echo pdp10-unknown-tops10+ exit ;;+ *:TENEX:*:*)+ echo pdp10-unknown-tenex+ exit ;;+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)+ echo pdp10-dec-tops20+ exit ;;+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)+ echo pdp10-xkl-tops20+ exit ;;+ *:TOPS-20:*:*)+ echo pdp10-unknown-tops20+ exit ;;+ *:ITS:*:*)+ echo pdp10-unknown-its+ exit ;;+ SEI:*:*:SEIUX)+ echo mips-sei-seiux${UNAME_RELEASE}+ exit ;;+ *:DragonFly:*:*)+ echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`+ exit ;;+ *:*VMS:*:*)+ UNAME_MACHINE=`(uname -p) 2>/dev/null`+ case "${UNAME_MACHINE}" in+ A*) echo alpha-dec-vms ; exit ;;+ I*) echo ia64-dec-vms ; exit ;;+ V*) echo vax-dec-vms ; exit ;;+ esac ;;+ *:XENIX:*:SysV)+ echo i386-pc-xenix+ exit ;;+ i*86:skyos:*:*)+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'+ exit ;;+ i*86:rdos:*:*)+ echo ${UNAME_MACHINE}-pc-rdos+ exit ;;+esac++#echo '(No uname command or uname output not recognized.)' 1>&2+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2++eval $set_cc_for_build+cat >$dummy.c <<EOF+#ifdef _SEQUENT_+# include <sys/types.h>+# include <sys/utsname.h>+#endif+main ()+{+#if defined (sony)+#if defined (MIPSEB)+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,+ I don't know.... */+ printf ("mips-sony-bsd\n"); exit (0);+#else+#include <sys/param.h>+ printf ("m68k-sony-newsos%s\n",+#ifdef NEWSOS4+ "4"+#else+ ""+#endif+ ); exit (0);+#endif+#endif++#if defined (__arm) && defined (__acorn) && defined (__unix)+ printf ("arm-acorn-riscix\n"); exit (0);+#endif++#if defined (hp300) && !defined (hpux)+ printf ("m68k-hp-bsd\n"); exit (0);+#endif++#if defined (NeXT)+#if !defined (__ARCHITECTURE__)+#define __ARCHITECTURE__ "m68k"+#endif+ int version;+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;+ if (version < 4)+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);+ else+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);+ exit (0);+#endif++#if defined (MULTIMAX) || defined (n16)+#if defined (UMAXV)+ printf ("ns32k-encore-sysv\n"); exit (0);+#else+#if defined (CMU)+ printf ("ns32k-encore-mach\n"); exit (0);+#else+ printf ("ns32k-encore-bsd\n"); exit (0);+#endif+#endif+#endif++#if defined (__386BSD__)+ printf ("i386-pc-bsd\n"); exit (0);+#endif++#if defined (sequent)+#if defined (i386)+ printf ("i386-sequent-dynix\n"); exit (0);+#endif+#if defined (ns32000)+ printf ("ns32k-sequent-dynix\n"); exit (0);+#endif+#endif++#if defined (_SEQUENT_)+ struct utsname un;++ uname(&un);++ if (strncmp(un.version, "V2", 2) == 0) {+ printf ("i386-sequent-ptx2\n"); exit (0);+ }+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */+ printf ("i386-sequent-ptx1\n"); exit (0);+ }+ printf ("i386-sequent-ptx\n"); exit (0);++#endif++#if defined (vax)+# if !defined (ultrix)+# include <sys/param.h>+# if defined (BSD)+# if BSD == 43+ printf ("vax-dec-bsd4.3\n"); exit (0);+# else+# if BSD == 199006+ printf ("vax-dec-bsd4.3reno\n"); exit (0);+# else+ printf ("vax-dec-bsd\n"); exit (0);+# endif+# endif+# else+ printf ("vax-dec-bsd\n"); exit (0);+# endif+# else+ printf ("vax-dec-ultrix\n"); exit (0);+# endif+#endif++#if defined (alliant) && defined (i860)+ printf ("i860-alliant-bsd\n"); exit (0);+#endif++ exit (1);+}+EOF++$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&+ { echo "$SYSTEM_NAME"; exit; }++# Apollos put the system type in the environment.++test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }++# Convex versions that predate uname can use getsysinfo(1)++if [ -x /usr/convex/getsysinfo ]+then+ case `getsysinfo -f cpu_type` in+ c1*)+ echo c1-convex-bsd+ exit ;;+ c2*)+ if getsysinfo -f scalar_acc+ then echo c32-convex-bsd+ else echo c2-convex-bsd+ fi+ exit ;;+ c34*)+ echo c34-convex-bsd+ exit ;;+ c38*)+ echo c38-convex-bsd+ exit ;;+ c4*)+ echo c4-convex-bsd+ exit ;;+ esac+fi++cat >&2 <<EOF+$0: unable to guess system type++This script, last modified $timestamp, has failed to recognize+the operating system you are using. It is advised that you+download the most up to date version of the config scripts from++ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess+and+ http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub++If the version you run ($0) is already up to date, please+send the following data and any information you think might be+pertinent to <config-patches@gnu.org> in order to provide the needed+information to handle your system.++config.guess timestamp = $timestamp++uname -m = `(uname -m) 2>/dev/null || echo unknown`+uname -r = `(uname -r) 2>/dev/null || echo unknown`+uname -s = `(uname -s) 2>/dev/null || echo unknown`+uname -v = `(uname -v) 2>/dev/null || echo unknown`++/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`++hostinfo = `(hostinfo) 2>/dev/null`+/bin/universe = `(/bin/universe) 2>/dev/null`+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`+/bin/arch = `(/bin/arch) 2>/dev/null`+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`++UNAME_MACHINE = ${UNAME_MACHINE}+UNAME_RELEASE = ${UNAME_RELEASE}+UNAME_SYSTEM = ${UNAME_SYSTEM}+UNAME_VERSION = ${UNAME_VERSION}+EOF++exit 1++# Local variables:+# eval: (add-hook 'write-file-hooks 'time-stamp)+# time-stamp-start: "timestamp='"+# time-stamp-format: "%:y-%02m-%02d"+# time-stamp-end: "'"+# End:
+ config.sub view
@@ -0,0 +1,1608 @@+#! /bin/sh+# Configuration validation subroutine script.+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,+# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.++timestamp='2006-02-23'++# This file is (in principle) common to ALL GNU software.+# The presence of a machine in this file suggests that SOME GNU software+# can handle that machine. It does not imply ALL GNU software can.+#+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA+# 02110-1301, USA.+#+# As a special exception to the GNU General Public License, if you+# distribute this file as part of a program that contains a+# configuration script generated by Autoconf, you may include it under+# the same distribution terms that you use for the rest of that program.+++# Please send patches to <config-patches@gnu.org>. Submit a context+# diff and a properly formatted ChangeLog entry.+#+# Configuration subroutine to validate and canonicalize a configuration type.+# Supply the specified configuration type as an argument.+# If it is invalid, we print an error message on stderr and exit with code 1.+# Otherwise, we print the canonical config type on stdout and succeed.++# This file is supposed to be the same for all GNU packages+# and recognize all the CPU types, system types and aliases+# that are meaningful with *any* GNU software.+# Each package is responsible for reporting which valid configurations+# it does not support. The user should be able to distinguish+# a failure to support a valid configuration from a meaningless+# configuration.++# The goal of this file is to map all the various variations of a given+# machine specification into a single specification in the form:+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM+# or in some cases, the newer four-part form:+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM+# It is wrong to echo any other type of specification.++me=`echo "$0" | sed -e 's,.*/,,'`++usage="\+Usage: $0 [OPTION] CPU-MFR-OPSYS+ $0 [OPTION] ALIAS++Canonicalize a configuration name.++Operation modes:+ -h, --help print this help, then exit+ -t, --time-stamp print date of last modification, then exit+ -v, --version print version number, then exit++Report bugs and patches to <config-patches@gnu.org>."++version="\+GNU config.sub ($timestamp)++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005+Free Software Foundation, Inc.++This is free software; see the source for copying conditions. There is NO+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."++help="+Try \`$me --help' for more information."++# Parse command line+while test $# -gt 0 ; do+ case $1 in+ --time-stamp | --time* | -t )+ echo "$timestamp" ; exit ;;+ --version | -v )+ echo "$version" ; exit ;;+ --help | --h* | -h )+ echo "$usage"; exit ;;+ -- ) # Stop option processing+ shift; break ;;+ - ) # Use stdin as input.+ break ;;+ -* )+ echo "$me: invalid option $1$help"+ exit 1 ;;++ *local*)+ # First pass through any local machine types.+ echo $1+ exit ;;++ * )+ break ;;+ esac+done++case $# in+ 0) echo "$me: missing argument$help" >&2+ exit 1;;+ 1) ;;+ *) echo "$me: too many arguments$help" >&2+ exit 1;;+esac++# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).+# Here we must recognize all the valid KERNEL-OS combinations.+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`+case $maybe_os in+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \+ storm-chaos* | os2-emx* | rtmk-nova*)+ os=-$maybe_os+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`+ ;;+ *)+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`+ if [ $basic_machine != $1 ]+ then os=`echo $1 | sed 's/.*-/-/'`+ else os=; fi+ ;;+esac++### Let's recognize common machines as not being operating systems so+### that things like config.sub decstation-3100 work. We also+### recognize some manufacturers as not being operating systems, so we+### can provide default operating systems below.+case $os in+ -sun*os*)+ # Prevent following clause from handling this invalid input.+ ;;+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \+ -apple | -axis | -knuth | -cray)+ os=+ basic_machine=$1+ ;;+ -sim | -cisco | -oki | -wec | -winbond)+ os=+ basic_machine=$1+ ;;+ -scout)+ ;;+ -wrs)+ os=-vxworks+ basic_machine=$1+ ;;+ -chorusos*)+ os=-chorusos+ basic_machine=$1+ ;;+ -chorusrdb)+ os=-chorusrdb+ basic_machine=$1+ ;;+ -hiux*)+ os=-hiuxwe2+ ;;+ -sco6)+ os=-sco5v6+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco5)+ os=-sco3.2v5+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco4)+ os=-sco3.2v4+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco3.2.[4-9]*)+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco3.2v[4-9]*)+ # Don't forget version if it is 3.2v4 or newer.+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco5v6*)+ # Don't forget version if it is 3.2v4 or newer.+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -sco*)+ os=-sco3.2v2+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -udk*)+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -isc)+ os=-isc2.2+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -clix*)+ basic_machine=clipper-intergraph+ ;;+ -isc*)+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`+ ;;+ -lynx*)+ os=-lynxos+ ;;+ -ptx*)+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`+ ;;+ -windowsnt*)+ os=`echo $os | sed -e 's/windowsnt/winnt/'`+ ;;+ -psos*)+ os=-psos+ ;;+ -mint | -mint[0-9]*)+ basic_machine=m68k-atari+ os=-mint+ ;;+esac++# Decode aliases for certain CPU-COMPANY combinations.+case $basic_machine in+ # Recognize the basic CPU types without company name.+ # Some are omitted here because they have special meanings below.+ 1750a | 580 \+ | a29k \+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \+ | am33_2.0 \+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \+ | bfin \+ | c4x | clipper \+ | d10v | d30v | dlx | dsp16xx \+ | fr30 | frv \+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \+ | i370 | i860 | i960 | ia64 \+ | ip2k | iq2000 \+ | m32r | m32rle | m68000 | m68k | m88k | maxq | mb | microblaze | mcore \+ | mips | mipsbe | mipseb | mipsel | mipsle \+ | mips16 \+ | mips64 | mips64el \+ | mips64vr | mips64vrel \+ | mips64orion | mips64orionel \+ | mips64vr4100 | mips64vr4100el \+ | mips64vr4300 | mips64vr4300el \+ | mips64vr5000 | mips64vr5000el \+ | mips64vr5900 | mips64vr5900el \+ | mipsisa32 | mipsisa32el \+ | mipsisa32r2 | mipsisa32r2el \+ | mipsisa64 | mipsisa64el \+ | mipsisa64r2 | mipsisa64r2el \+ | mipsisa64sb1 | mipsisa64sb1el \+ | mipsisa64sr71k | mipsisa64sr71kel \+ | mipstx39 | mipstx39el \+ | mn10200 | mn10300 \+ | mt \+ | msp430 \+ | nios | nios2 \+ | ns16k | ns32k \+ | or32 \+ | pdp10 | pdp11 | pj | pjl \+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \+ | pyramid \+ | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \+ | sh64 | sh64le \+ | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \+ | sparcv8 | sparcv9 | sparcv9b \+ | strongarm \+ | tahoe | thumb | tic4x | tic80 | tron \+ | v850 | v850e \+ | we32k \+ | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \+ | z8k)+ basic_machine=$basic_machine-unknown+ ;;+ m32c)+ basic_machine=$basic_machine-unknown+ ;;+ m6811 | m68hc11 | m6812 | m68hc12)+ # Motorola 68HC11/12.+ basic_machine=$basic_machine-unknown+ os=-none+ ;;+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)+ ;;+ ms1)+ basic_machine=mt-unknown+ ;;++ # We use `pc' rather than `unknown'+ # because (1) that's what they normally are, and+ # (2) the word "unknown" tends to confuse beginning users.+ i*86 | x86_64)+ basic_machine=$basic_machine-pc+ ;;+ # Object if more than one company name word.+ *-*-*)+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2+ exit 1+ ;;+ # Recognize the basic CPU types with company name.+ 580-* \+ | a29k-* \+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \+ | avr-* \+ | bfin-* | bs2000-* \+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \+ | clipper-* | craynv-* | cydra-* \+ | d10v-* | d30v-* | dlx-* \+ | elxsi-* \+ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \+ | h8300-* | h8500-* \+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \+ | i*86-* | i860-* | i960-* | ia64-* \+ | ip2k-* | iq2000-* \+ | m32r-* | m32rle-* \+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \+ | m88110-* | m88k-* | maxq-* | mcore-* \+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \+ | mips16-* \+ | mips64-* | mips64el-* \+ | mips64vr-* | mips64vrel-* \+ | mips64orion-* | mips64orionel-* \+ | mips64vr4100-* | mips64vr4100el-* \+ | mips64vr4300-* | mips64vr4300el-* \+ | mips64vr5000-* | mips64vr5000el-* \+ | mips64vr5900-* | mips64vr5900el-* \+ | mipsisa32-* | mipsisa32el-* \+ | mipsisa32r2-* | mipsisa32r2el-* \+ | mipsisa64-* | mipsisa64el-* \+ | mipsisa64r2-* | mipsisa64r2el-* \+ | mipsisa64sb1-* | mipsisa64sb1el-* \+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \+ | mipstx39-* | mipstx39el-* \+ | mmix-* \+ | mt-* \+ | msp430-* \+ | nios-* | nios2-* \+ | none-* | np1-* | ns16k-* | ns32k-* \+ | orion-* \+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \+ | pyramid-* \+ | romp-* | rs6000-* \+ | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \+ | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \+ | sparclite-* \+ | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \+ | tahoe-* | thumb-* \+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \+ | tron-* \+ | v850-* | v850e-* | vax-* \+ | we32k-* \+ | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \+ | xstormy16-* | xtensa-* \+ | ymp-* \+ | z8k-*)+ ;;+ m32c-*)+ ;;+ # Recognize the various machine names and aliases which stand+ # for a CPU type and a company and sometimes even an OS.+ 386bsd)+ basic_machine=i386-unknown+ os=-bsd+ ;;+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)+ basic_machine=m68000-att+ ;;+ 3b*)+ basic_machine=we32k-att+ ;;+ a29khif)+ basic_machine=a29k-amd+ os=-udi+ ;;+ abacus)+ basic_machine=abacus-unknown+ ;;+ adobe68k)+ basic_machine=m68010-adobe+ os=-scout+ ;;+ alliant | fx80)+ basic_machine=fx80-alliant+ ;;+ altos | altos3068)+ basic_machine=m68k-altos+ ;;+ am29k)+ basic_machine=a29k-none+ os=-bsd+ ;;+ amd64)+ basic_machine=x86_64-pc+ ;;+ amd64-*)+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ amdahl)+ basic_machine=580-amdahl+ os=-sysv+ ;;+ amiga | amiga-*)+ basic_machine=m68k-unknown+ ;;+ amigaos | amigados)+ basic_machine=m68k-unknown+ os=-amigaos+ ;;+ amigaunix | amix)+ basic_machine=m68k-unknown+ os=-sysv4+ ;;+ apollo68)+ basic_machine=m68k-apollo+ os=-sysv+ ;;+ apollo68bsd)+ basic_machine=m68k-apollo+ os=-bsd+ ;;+ aux)+ basic_machine=m68k-apple+ os=-aux+ ;;+ balance)+ basic_machine=ns32k-sequent+ os=-dynix+ ;;+ c90)+ basic_machine=c90-cray+ os=-unicos+ ;;+ convex-c1)+ basic_machine=c1-convex+ os=-bsd+ ;;+ convex-c2)+ basic_machine=c2-convex+ os=-bsd+ ;;+ convex-c32)+ basic_machine=c32-convex+ os=-bsd+ ;;+ convex-c34)+ basic_machine=c34-convex+ os=-bsd+ ;;+ convex-c38)+ basic_machine=c38-convex+ os=-bsd+ ;;+ cray | j90)+ basic_machine=j90-cray+ os=-unicos+ ;;+ craynv)+ basic_machine=craynv-cray+ os=-unicosmp+ ;;+ cr16c)+ basic_machine=cr16c-unknown+ os=-elf+ ;;+ crds | unos)+ basic_machine=m68k-crds+ ;;+ crisv32 | crisv32-* | etraxfs*)+ basic_machine=crisv32-axis+ ;;+ cris | cris-* | etrax*)+ basic_machine=cris-axis+ ;;+ crx)+ basic_machine=crx-unknown+ os=-elf+ ;;+ da30 | da30-*)+ basic_machine=m68k-da30+ ;;+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)+ basic_machine=mips-dec+ ;;+ decsystem10* | dec10*)+ basic_machine=pdp10-dec+ os=-tops10+ ;;+ decsystem20* | dec20*)+ basic_machine=pdp10-dec+ os=-tops20+ ;;+ delta | 3300 | motorola-3300 | motorola-delta \+ | 3300-motorola | delta-motorola)+ basic_machine=m68k-motorola+ ;;+ delta88)+ basic_machine=m88k-motorola+ os=-sysv3+ ;;+ djgpp)+ basic_machine=i586-pc+ os=-msdosdjgpp+ ;;+ dpx20 | dpx20-*)+ basic_machine=rs6000-bull+ os=-bosx+ ;;+ dpx2* | dpx2*-bull)+ basic_machine=m68k-bull+ os=-sysv3+ ;;+ ebmon29k)+ basic_machine=a29k-amd+ os=-ebmon+ ;;+ elxsi)+ basic_machine=elxsi-elxsi+ os=-bsd+ ;;+ encore | umax | mmax)+ basic_machine=ns32k-encore+ ;;+ es1800 | OSE68k | ose68k | ose | OSE)+ basic_machine=m68k-ericsson+ os=-ose+ ;;+ fx2800)+ basic_machine=i860-alliant+ ;;+ genix)+ basic_machine=ns32k-ns+ ;;+ gmicro)+ basic_machine=tron-gmicro+ os=-sysv+ ;;+ go32)+ basic_machine=i386-pc+ os=-go32+ ;;+ h3050r* | hiux*)+ basic_machine=hppa1.1-hitachi+ os=-hiuxwe2+ ;;+ h8300hms)+ basic_machine=h8300-hitachi+ os=-hms+ ;;+ h8300xray)+ basic_machine=h8300-hitachi+ os=-xray+ ;;+ h8500hms)+ basic_machine=h8500-hitachi+ os=-hms+ ;;+ harris)+ basic_machine=m88k-harris+ os=-sysv3+ ;;+ hp300-*)+ basic_machine=m68k-hp+ ;;+ hp300bsd)+ basic_machine=m68k-hp+ os=-bsd+ ;;+ hp300hpux)+ basic_machine=m68k-hp+ os=-hpux+ ;;+ hp3k9[0-9][0-9] | hp9[0-9][0-9])+ basic_machine=hppa1.0-hp+ ;;+ hp9k2[0-9][0-9] | hp9k31[0-9])+ basic_machine=m68000-hp+ ;;+ hp9k3[2-9][0-9])+ basic_machine=m68k-hp+ ;;+ hp9k6[0-9][0-9] | hp6[0-9][0-9])+ basic_machine=hppa1.0-hp+ ;;+ hp9k7[0-79][0-9] | hp7[0-79][0-9])+ basic_machine=hppa1.1-hp+ ;;+ hp9k78[0-9] | hp78[0-9])+ # FIXME: really hppa2.0-hp+ basic_machine=hppa1.1-hp+ ;;+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)+ # FIXME: really hppa2.0-hp+ basic_machine=hppa1.1-hp+ ;;+ hp9k8[0-9][13679] | hp8[0-9][13679])+ basic_machine=hppa1.1-hp+ ;;+ hp9k8[0-9][0-9] | hp8[0-9][0-9])+ basic_machine=hppa1.0-hp+ ;;+ hppa-next)+ os=-nextstep3+ ;;+ hppaosf)+ basic_machine=hppa1.1-hp+ os=-osf+ ;;+ hppro)+ basic_machine=hppa1.1-hp+ os=-proelf+ ;;+ i370-ibm* | ibm*)+ basic_machine=i370-ibm+ ;;+# I'm not sure what "Sysv32" means. Should this be sysv3.2?+ i*86v32)+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`+ os=-sysv32+ ;;+ i*86v4*)+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`+ os=-sysv4+ ;;+ i*86v)+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`+ os=-sysv+ ;;+ i*86sol2)+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`+ os=-solaris2+ ;;+ i386mach)+ basic_machine=i386-mach+ os=-mach+ ;;+ i386-vsta | vsta)+ basic_machine=i386-unknown+ os=-vsta+ ;;+ iris | iris4d)+ basic_machine=mips-sgi+ case $os in+ -irix*)+ ;;+ *)+ os=-irix4+ ;;+ esac+ ;;+ isi68 | isi)+ basic_machine=m68k-isi+ os=-sysv+ ;;+ m88k-omron*)+ basic_machine=m88k-omron+ ;;+ magnum | m3230)+ basic_machine=mips-mips+ os=-sysv+ ;;+ merlin)+ basic_machine=ns32k-utek+ os=-sysv+ ;;+ mingw32)+ basic_machine=i386-pc+ os=-mingw32+ ;;+ miniframe)+ basic_machine=m68000-convergent+ ;;+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)+ basic_machine=m68k-atari+ os=-mint+ ;;+ mips3*-*)+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`+ ;;+ mips3*)+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown+ ;;+ monitor)+ basic_machine=m68k-rom68k+ os=-coff+ ;;+ morphos)+ basic_machine=powerpc-unknown+ os=-morphos+ ;;+ msdos)+ basic_machine=i386-pc+ os=-msdos+ ;;+ ms1-*)+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`+ ;;+ mvs)+ basic_machine=i370-ibm+ os=-mvs+ ;;+ ncr3000)+ basic_machine=i486-ncr+ os=-sysv4+ ;;+ netbsd386)+ basic_machine=i386-unknown+ os=-netbsd+ ;;+ netwinder)+ basic_machine=armv4l-rebel+ os=-linux+ ;;+ news | news700 | news800 | news900)+ basic_machine=m68k-sony+ os=-newsos+ ;;+ news1000)+ basic_machine=m68030-sony+ os=-newsos+ ;;+ news-3600 | risc-news)+ basic_machine=mips-sony+ os=-newsos+ ;;+ necv70)+ basic_machine=v70-nec+ os=-sysv+ ;;+ next | m*-next )+ basic_machine=m68k-next+ case $os in+ -nextstep* )+ ;;+ -ns2*)+ os=-nextstep2+ ;;+ *)+ os=-nextstep3+ ;;+ esac+ ;;+ nh3000)+ basic_machine=m68k-harris+ os=-cxux+ ;;+ nh[45]000)+ basic_machine=m88k-harris+ os=-cxux+ ;;+ nindy960)+ basic_machine=i960-intel+ os=-nindy+ ;;+ mon960)+ basic_machine=i960-intel+ os=-mon960+ ;;+ nonstopux)+ basic_machine=mips-compaq+ os=-nonstopux+ ;;+ np1)+ basic_machine=np1-gould+ ;;+ nsr-tandem)+ basic_machine=nsr-tandem+ ;;+ op50n-* | op60c-*)+ basic_machine=hppa1.1-oki+ os=-proelf+ ;;+ openrisc | openrisc-*)+ basic_machine=or32-unknown+ ;;+ os400)+ basic_machine=powerpc-ibm+ os=-os400+ ;;+ OSE68000 | ose68000)+ basic_machine=m68000-ericsson+ os=-ose+ ;;+ os68k)+ basic_machine=m68k-none+ os=-os68k+ ;;+ pa-hitachi)+ basic_machine=hppa1.1-hitachi+ os=-hiuxwe2+ ;;+ paragon)+ basic_machine=i860-intel+ os=-osf+ ;;+ pbd)+ basic_machine=sparc-tti+ ;;+ pbb)+ basic_machine=m68k-tti+ ;;+ pc532 | pc532-*)+ basic_machine=ns32k-pc532+ ;;+ pc98)+ basic_machine=i386-pc+ ;;+ pc98-*)+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ pentium | p5 | k5 | k6 | nexgen | viac3)+ basic_machine=i586-pc+ ;;+ pentiumpro | p6 | 6x86 | athlon | athlon_*)+ basic_machine=i686-pc+ ;;+ pentiumii | pentium2 | pentiumiii | pentium3)+ basic_machine=i686-pc+ ;;+ pentium4)+ basic_machine=i786-pc+ ;;+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ pentiumpro-* | p6-* | 6x86-* | athlon-*)+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ pentium4-*)+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ pn)+ basic_machine=pn-gould+ ;;+ power) basic_machine=power-ibm+ ;;+ ppc) basic_machine=powerpc-unknown+ ;;+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ ppcle | powerpclittle | ppc-le | powerpc-little)+ basic_machine=powerpcle-unknown+ ;;+ ppcle-* | powerpclittle-*)+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ ppc64) basic_machine=powerpc64-unknown+ ;;+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)+ basic_machine=powerpc64le-unknown+ ;;+ ppc64le-* | powerpc64little-*)+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`+ ;;+ ps2)+ basic_machine=i386-ibm+ ;;+ pw32)+ basic_machine=i586-unknown+ os=-pw32+ ;;+ rdos)+ basic_machine=i386-pc+ os=-rdos+ ;;+ rom68k)+ basic_machine=m68k-rom68k+ os=-coff+ ;;+ rm[46]00)+ basic_machine=mips-siemens+ ;;+ rtpc | rtpc-*)+ basic_machine=romp-ibm+ ;;+ s390 | s390-*)+ basic_machine=s390-ibm+ ;;+ s390x | s390x-*)+ basic_machine=s390x-ibm+ ;;+ sa29200)+ basic_machine=a29k-amd+ os=-udi+ ;;+ sb1)+ basic_machine=mipsisa64sb1-unknown+ ;;+ sb1el)+ basic_machine=mipsisa64sb1el-unknown+ ;;+ sei)+ basic_machine=mips-sei+ os=-seiux+ ;;+ sequent)+ basic_machine=i386-sequent+ ;;+ sh)+ basic_machine=sh-hitachi+ os=-hms+ ;;+ sh64)+ basic_machine=sh64-unknown+ ;;+ sparclite-wrs | simso-wrs)+ basic_machine=sparclite-wrs+ os=-vxworks+ ;;+ sps7)+ basic_machine=m68k-bull+ os=-sysv2+ ;;+ spur)+ basic_machine=spur-unknown+ ;;+ st2000)+ basic_machine=m68k-tandem+ ;;+ stratus)+ basic_machine=i860-stratus+ os=-sysv4+ ;;+ sun2)+ basic_machine=m68000-sun+ ;;+ sun2os3)+ basic_machine=m68000-sun+ os=-sunos3+ ;;+ sun2os4)+ basic_machine=m68000-sun+ os=-sunos4+ ;;+ sun3os3)+ basic_machine=m68k-sun+ os=-sunos3+ ;;+ sun3os4)+ basic_machine=m68k-sun+ os=-sunos4+ ;;+ sun4os3)+ basic_machine=sparc-sun+ os=-sunos3+ ;;+ sun4os4)+ basic_machine=sparc-sun+ os=-sunos4+ ;;+ sun4sol2)+ basic_machine=sparc-sun+ os=-solaris2+ ;;+ sun3 | sun3-*)+ basic_machine=m68k-sun+ ;;+ sun4)+ basic_machine=sparc-sun+ ;;+ sun386 | sun386i | roadrunner)+ basic_machine=i386-sun+ ;;+ sv1)+ basic_machine=sv1-cray+ os=-unicos+ ;;+ symmetry)+ basic_machine=i386-sequent+ os=-dynix+ ;;+ t3e)+ basic_machine=alphaev5-cray+ os=-unicos+ ;;+ t90)+ basic_machine=t90-cray+ os=-unicos+ ;;+ tic54x | c54x*)+ basic_machine=tic54x-unknown+ os=-coff+ ;;+ tic55x | c55x*)+ basic_machine=tic55x-unknown+ os=-coff+ ;;+ tic6x | c6x*)+ basic_machine=tic6x-unknown+ os=-coff+ ;;+ tx39)+ basic_machine=mipstx39-unknown+ ;;+ tx39el)+ basic_machine=mipstx39el-unknown+ ;;+ toad1)+ basic_machine=pdp10-xkl+ os=-tops20+ ;;+ tower | tower-32)+ basic_machine=m68k-ncr+ ;;+ tpf)+ basic_machine=s390x-ibm+ os=-tpf+ ;;+ udi29k)+ basic_machine=a29k-amd+ os=-udi+ ;;+ ultra3)+ basic_machine=a29k-nyu+ os=-sym1+ ;;+ v810 | necv810)+ basic_machine=v810-nec+ os=-none+ ;;+ vaxv)+ basic_machine=vax-dec+ os=-sysv+ ;;+ vms)+ basic_machine=vax-dec+ os=-vms+ ;;+ vpp*|vx|vx-*)+ basic_machine=f301-fujitsu+ ;;+ vxworks960)+ basic_machine=i960-wrs+ os=-vxworks+ ;;+ vxworks68)+ basic_machine=m68k-wrs+ os=-vxworks+ ;;+ vxworks29k)+ basic_machine=a29k-wrs+ os=-vxworks+ ;;+ w65*)+ basic_machine=w65-wdc+ os=-none+ ;;+ w89k-*)+ basic_machine=hppa1.1-winbond+ os=-proelf+ ;;+ xbox)+ basic_machine=i686-pc+ os=-mingw32+ ;;+ xps | xps100)+ basic_machine=xps100-honeywell+ ;;+ ymp)+ basic_machine=ymp-cray+ os=-unicos+ ;;+ z8k-*-coff)+ basic_machine=z8k-unknown+ os=-sim+ ;;+ none)+ basic_machine=none-none+ os=-none+ ;;++# Here we handle the default manufacturer of certain CPU types. It is in+# some cases the only manufacturer, in others, it is the most popular.+ w89k)+ basic_machine=hppa1.1-winbond+ ;;+ op50n)+ basic_machine=hppa1.1-oki+ ;;+ op60c)+ basic_machine=hppa1.1-oki+ ;;+ romp)+ basic_machine=romp-ibm+ ;;+ mmix)+ basic_machine=mmix-knuth+ ;;+ rs6000)+ basic_machine=rs6000-ibm+ ;;+ vax)+ basic_machine=vax-dec+ ;;+ pdp10)+ # there are many clones, so DEC is not a safe bet+ basic_machine=pdp10-unknown+ ;;+ pdp11)+ basic_machine=pdp11-dec+ ;;+ we32k)+ basic_machine=we32k-att+ ;;+ sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)+ basic_machine=sh-unknown+ ;;+ sparc | sparcv8 | sparcv9 | sparcv9b)+ basic_machine=sparc-sun+ ;;+ cydra)+ basic_machine=cydra-cydrome+ ;;+ orion)+ basic_machine=orion-highlevel+ ;;+ orion105)+ basic_machine=clipper-highlevel+ ;;+ mac | mpw | mac-mpw)+ basic_machine=m68k-apple+ ;;+ pmac | pmac-mpw)+ basic_machine=powerpc-apple+ ;;+ *-unknown)+ # Make sure to match an already-canonicalized machine name.+ ;;+ *)+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2+ exit 1+ ;;+esac++# Here we canonicalize certain aliases for manufacturers.+case $basic_machine in+ *-digital*)+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`+ ;;+ *-commodore*)+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`+ ;;+ *)+ ;;+esac++# Decode manufacturer-specific aliases for certain operating systems.++if [ x"$os" != x"" ]+then+case $os in+ # First match some system type aliases+ # that might get confused with valid system types.+ # -solaris* is a basic system type, with this one exception.+ -solaris1 | -solaris1.*)+ os=`echo $os | sed -e 's|solaris1|sunos4|'`+ ;;+ -solaris)+ os=-solaris2+ ;;+ -svr4*)+ os=-sysv4+ ;;+ -unixware*)+ os=-sysv4.2uw+ ;;+ -gnu/linux*)+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`+ ;;+ # First accept the basic system types.+ # The portable systems comes first.+ # Each alternative MUST END IN A *, to match a version number.+ # -sysv* is not here because it comes later, after sysvr4.+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \+ | -aos* \+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \+ | -openbsd* | -solidbsd* \+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \+ | -chorusos* | -chorusrdb* \+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \+ | -uxpv* | -beos* | -mpeix* | -udk* \+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \+ | -skyos* | -haiku* | -rdos*)+ # Remember, each alternative MUST END IN *, to match a version number.+ ;;+ -qnx*)+ case $basic_machine in+ x86-* | i*86-*)+ ;;+ *)+ os=-nto$os+ ;;+ esac+ ;;+ -nto-qnx*)+ ;;+ -nto*)+ os=`echo $os | sed -e 's|nto|nto-qnx|'`+ ;;+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)+ ;;+ -mac*)+ os=`echo $os | sed -e 's|mac|macos|'`+ ;;+ -linux-dietlibc)+ os=-linux-dietlibc+ ;;+ -linux*)+ os=`echo $os | sed -e 's|linux|linux-gnu|'`+ ;;+ -sunos5*)+ os=`echo $os | sed -e 's|sunos5|solaris2|'`+ ;;+ -sunos6*)+ os=`echo $os | sed -e 's|sunos6|solaris3|'`+ ;;+ -opened*)+ os=-openedition+ ;;+ -os400*)+ os=-os400+ ;;+ -wince*)+ os=-wince+ ;;+ -osfrose*)+ os=-osfrose+ ;;+ -osf*)+ os=-osf+ ;;+ -utek*)+ os=-bsd+ ;;+ -dynix*)+ os=-bsd+ ;;+ -acis*)+ os=-aos+ ;;+ -atheos*)+ os=-atheos+ ;;+ -syllable*)+ os=-syllable+ ;;+ -386bsd)+ os=-bsd+ ;;+ -ctix* | -uts*)+ os=-sysv+ ;;+ -nova*)+ os=-rtmk-nova+ ;;+ -ns2 )+ os=-nextstep2+ ;;+ -nsk*)+ os=-nsk+ ;;+ # Preserve the version number of sinix5.+ -sinix5.*)+ os=`echo $os | sed -e 's|sinix|sysv|'`+ ;;+ -sinix*)+ os=-sysv4+ ;;+ -tpf*)+ os=-tpf+ ;;+ -triton*)+ os=-sysv3+ ;;+ -oss*)+ os=-sysv3+ ;;+ -svr4)+ os=-sysv4+ ;;+ -svr3)+ os=-sysv3+ ;;+ -sysvr4)+ os=-sysv4+ ;;+ # This must come after -sysvr4.+ -sysv*)+ ;;+ -ose*)+ os=-ose+ ;;+ -es1800*)+ os=-ose+ ;;+ -xenix)+ os=-xenix+ ;;+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)+ os=-mint+ ;;+ -aros*)+ os=-aros+ ;;+ -kaos*)+ os=-kaos+ ;;+ -zvmoe)+ os=-zvmoe+ ;;+ -none)+ ;;+ *)+ # Get rid of the `-' at the beginning of $os.+ os=`echo $os | sed 's/[^-]*-//'`+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2+ exit 1+ ;;+esac+else++# Here we handle the default operating systems that come with various machines.+# The value should be what the vendor currently ships out the door with their+# machine or put another way, the most popular os provided with the machine.++# Note that if you're going to try to match "-MANUFACTURER" here (say,+# "-sun"), then you have to tell the case statement up towards the top+# that MANUFACTURER isn't an operating system. Otherwise, code above+# will signal an error saying that MANUFACTURER isn't an operating+# system, and we'll never get to this point.++case $basic_machine in+ *-acorn)+ os=-riscix1.2+ ;;+ arm*-rebel)+ os=-linux+ ;;+ arm*-semi)+ os=-aout+ ;;+ c4x-* | tic4x-*)+ os=-coff+ ;;+ # This must come before the *-dec entry.+ pdp10-*)+ os=-tops20+ ;;+ pdp11-*)+ os=-none+ ;;+ *-dec | vax-*)+ os=-ultrix4.2+ ;;+ m68*-apollo)+ os=-domain+ ;;+ i386-sun)+ os=-sunos4.0.2+ ;;+ m68000-sun)+ os=-sunos3+ # This also exists in the configure program, but was not the+ # default.+ # os=-sunos4+ ;;+ m68*-cisco)+ os=-aout+ ;;+ mips*-cisco)+ os=-elf+ ;;+ mips*-*)+ os=-elf+ ;;+ or32-*)+ os=-coff+ ;;+ *-tti) # must be before sparc entry or we get the wrong os.+ os=-sysv3+ ;;+ sparc-* | *-sun)+ os=-sunos4.1.1+ ;;+ *-be)+ os=-beos+ ;;+ *-haiku)+ os=-haiku+ ;;+ *-ibm)+ os=-aix+ ;;+ *-knuth)+ os=-mmixware+ ;;+ *-wec)+ os=-proelf+ ;;+ *-winbond)+ os=-proelf+ ;;+ *-oki)+ os=-proelf+ ;;+ *-hp)+ os=-hpux+ ;;+ *-hitachi)+ os=-hiux+ ;;+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)+ os=-sysv+ ;;+ *-cbm)+ os=-amigaos+ ;;+ *-dg)+ os=-dgux+ ;;+ *-dolphin)+ os=-sysv3+ ;;+ m68k-ccur)+ os=-rtu+ ;;+ m88k-omron*)+ os=-luna+ ;;+ *-next )+ os=-nextstep+ ;;+ *-sequent)+ os=-ptx+ ;;+ *-crds)+ os=-unos+ ;;+ *-ns)+ os=-genix+ ;;+ i370-*)+ os=-mvs+ ;;+ *-next)+ os=-nextstep3+ ;;+ *-gould)+ os=-sysv+ ;;+ *-highlevel)+ os=-bsd+ ;;+ *-encore)+ os=-bsd+ ;;+ *-sgi)+ os=-irix+ ;;+ *-siemens)+ os=-sysv4+ ;;+ *-masscomp)+ os=-rtu+ ;;+ f30[01]-fujitsu | f700-fujitsu)+ os=-uxpv+ ;;+ *-rom68k)+ os=-coff+ ;;+ *-*bug)+ os=-coff+ ;;+ *-apple)+ os=-macos+ ;;+ *-atari*)+ os=-mint+ ;;+ *)+ os=-none+ ;;+esac+fi++# Here we handle the case where we know the os, and the CPU type, but not the+# manufacturer. We pick the logical manufacturer.+vendor=unknown+case $basic_machine in+ *-unknown)+ case $os in+ -riscix*)+ vendor=acorn+ ;;+ -sunos*)+ vendor=sun+ ;;+ -aix*)+ vendor=ibm+ ;;+ -beos*)+ vendor=be+ ;;+ -hpux*)+ vendor=hp+ ;;+ -mpeix*)+ vendor=hp+ ;;+ -hiux*)+ vendor=hitachi+ ;;+ -unos*)+ vendor=crds+ ;;+ -dgux*)+ vendor=dg+ ;;+ -luna*)+ vendor=omron+ ;;+ -genix*)+ vendor=ns+ ;;+ -mvs* | -opened*)+ vendor=ibm+ ;;+ -os400*)+ vendor=ibm+ ;;+ -ptx*)+ vendor=sequent+ ;;+ -tpf*)+ vendor=ibm+ ;;+ -vxsim* | -vxworks* | -windiss*)+ vendor=wrs+ ;;+ -aux*)+ vendor=apple+ ;;+ -hms*)+ vendor=hitachi+ ;;+ -mpw* | -macos*)+ vendor=apple+ ;;+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)+ vendor=atari+ ;;+ -vos*)+ vendor=stratus+ ;;+ esac+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`+ ;;+esac++echo $basic_machine$os+exit++# Local variables:+# eval: (add-hook 'write-file-hooks 'time-stamp)+# time-stamp-start: "timestamp='"+# time-stamp-format: "%:y-%02m-%02d"+# time-stamp-end: "'"+# End:
+ configure view
@@ -0,0 +1,6297 @@+#! /bin/sh+# Guess values for system-dependent variables and create Makefiles.+# Generated by GNU Autoconf 2.61 for Haskell network package 2.3.0.11.+#+# Report bugs to <libraries@haskell.org>.+#+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.+# This configure script is free software; the Free Software Foundation+# gives unlimited permission to copy, distribute and modify it.+## --------------------- ##+## M4sh Initialization. ##+## --------------------- ##++# Be more Bourne compatible+DUALCASE=1; export DUALCASE # for MKS sh+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++++# PATH needs CR+# Avoid depending upon Character Ranges.+as_cr_letters='abcdefghijklmnopqrstuvwxyz'+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'+as_cr_Letters=$as_cr_letters$as_cr_LETTERS+as_cr_digits='0123456789'+as_cr_alnum=$as_cr_Letters$as_cr_digits++# The user is always right.+if test "${PATH_SEPARATOR+set}" != set; then+ echo "#! /bin/sh" >conf$$.sh+ echo "exit 0" >>conf$$.sh+ chmod +x conf$$.sh+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then+ PATH_SEPARATOR=';'+ else+ PATH_SEPARATOR=:+ fi+ rm -f conf$$.sh+fi++# Support unset when possible.+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then+ as_unset=unset+else+ as_unset=false+fi+++# IFS+# We need space, tab and new line, in precisely that order. Quoting is+# there to prevent editors from complaining about space-tab.+# (If _AS_PATH_WALK were called with IFS unset, it would disable word+# splitting by setting IFS to empty value.)+as_nl='+'+IFS=" "" $as_nl"++# Find who we are. Look in the path if we contain no directory separator.+case $0 in+ *[\\/]* ) as_myself=$0 ;;+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break+done+IFS=$as_save_IFS++ ;;+esac+# We did not find ourselves, most probably we were run as `sh COMMAND'+# in which case we are not to be found in the path.+if test "x$as_myself" = x; then+ as_myself=$0+fi+if test ! -f "$as_myself"; then+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2+ { (exit 1); exit 1; }+fi++# Work around bugs in pre-3.0 UWIN ksh.+for as_var in ENV MAIL MAILPATH+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+done+PS1='$ '+PS2='> '+PS4='+ '++# NLS nuisances.+for as_var in \+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \+ LC_TELEPHONE LC_TIME+do+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then+ eval $as_var=C; export $as_var+ else+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ fi+done++# Required to use basename.+if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then+ as_basename=basename+else+ as_basename=false+fi+++# Name of the executable.+as_me=`$as_basename -- "$0" ||+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X/"$0" |+ sed '/^.*\/\([^/][^/]*\)\/*$/{+ s//\1/+ q+ }+ /^X\/\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\/\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`++# CDPATH.+$as_unset CDPATH+++if test "x$CONFIG_SHELL" = x; then+ if (eval ":") 2>/dev/null; then+ as_have_required=yes+else+ as_have_required=no+fi++ if test $as_have_required = yes && (eval ":+(as_func_return () {+ (exit \$1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test \$exitcode = 0) || { (exit 1); exit 1; }++(+ as_lineno_1=\$LINENO+ as_lineno_2=\$LINENO+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }+") 2> /dev/null; then+ :+else+ as_candidate_shells=+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ case $as_dir in+ /*)+ for as_base in sh bash ksh sh5; do+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"+ done;;+ esac+done+IFS=$as_save_IFS+++ for as_shell in $as_candidate_shells $SHELL; do+ # Try only shells that exist, to save several forks.+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&+ { ("$as_shell") 2> /dev/null <<\_ASEOF+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++:+_ASEOF+}; then+ CONFIG_SHELL=$as_shell+ as_have_required=yes+ if { "$as_shell" 2> /dev/null <<\_ASEOF+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++:+(as_func_return () {+ (exit $1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = "$1" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test $exitcode = 0) || { (exit 1); exit 1; }++(+ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }++_ASEOF+}; then+ break+fi++fi++ done++ if test "x$CONFIG_SHELL" != x; then+ for as_var in BASH_ENV ENV+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ done+ export CONFIG_SHELL+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}+fi+++ if test $as_have_required = no; then+ echo This script requires a shell more modern than all the+ echo shells that I found on your system. Please install a+ echo modern shell, or manually run the script under such a+ echo shell if you do have one.+ { (exit 1); exit 1; }+fi+++fi++fi++++(eval "as_func_return () {+ (exit \$1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test \$exitcode = 0") || {+ echo No shell found that supports shell functions.+ echo Please tell autoconf@gnu.org about your system,+ echo including any error possibly output before this+ echo message+}++++ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {++ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO+ # uniformly replaced by the line number. The first 'sed' inserts a+ # line-number line after each line using $LINENO; the second 'sed'+ # does the real work. The second script uses 'N' to pair each+ # line-number line with the line containing $LINENO, and appends+ # trailing '-' during substitution so that $LINENO is not a special+ # case at line end.+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the+ # scripts with optimization help from Paolo Bonzini. Blame Lee+ # E. McMahon (1931-1989) for sed's syntax. :-)+ sed -n '+ p+ /[$]LINENO/=+ ' <$as_myself |+ sed '+ s/[$]LINENO.*/&-/+ t lineno+ b+ :lineno+ N+ :loop+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/+ t loop+ s/-\n.*//+ ' >$as_me.lineno &&+ chmod +x "$as_me.lineno" ||+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2+ { (exit 1); exit 1; }; }++ # Don't try to exec as it changes $[0], causing all sort of problems+ # (the dirname of $[0] is not the place where we might find the+ # original and so on. Autoconf is especially sensitive to this).+ . "./$as_me.lineno"+ # Exit status is that of the last command.+ exit+}+++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then+ as_dirname=dirname+else+ as_dirname=false+fi++ECHO_C= ECHO_N= ECHO_T=+case `echo -n x` in+-n*)+ case `echo 'x\c'` in+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.+ *) ECHO_C='\c';;+ esac;;+*)+ ECHO_N='-n';;+esac++if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++rm -f conf$$ conf$$.exe conf$$.file+if test -d conf$$.dir; then+ rm -f conf$$.dir/conf$$.file+else+ rm -f conf$$.dir+ mkdir conf$$.dir+fi+echo >conf$$.file+if ln -s conf$$.file conf$$ 2>/dev/null; then+ as_ln_s='ln -s'+ # ... but there are two gotchas:+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.+ # In both cases, we have to default to `cp -p'.+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||+ as_ln_s='cp -p'+elif ln conf$$.file conf$$ 2>/dev/null; then+ as_ln_s=ln+else+ as_ln_s='cp -p'+fi+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file+rmdir conf$$.dir 2>/dev/null++if mkdir -p . 2>/dev/null; then+ as_mkdir_p=:+else+ test -d ./-p && rmdir ./-p+ as_mkdir_p=false+fi++if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x++# Sed expression to map a string onto a valid CPP name.+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"++# Sed expression to map a string onto a valid variable name.+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"++++exec 7<&0 </dev/null 6>&1++# Name of the host.+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,+# so uname gets run too.+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`++#+# Initializations.+#+ac_default_prefix=/usr/local+ac_clean_files=+ac_config_libobj_dir=.+LIBOBJS=+cross_compiling=no+subdirs=+MFLAGS=+MAKEFLAGS=+SHELL=${CONFIG_SHELL-/bin/sh}++# Identity of this package.+PACKAGE_NAME='Haskell network package'+PACKAGE_TARNAME='network'+PACKAGE_VERSION='2.3.0.11'+PACKAGE_STRING='Haskell network package 2.3.0.11'+PACKAGE_BUGREPORT='libraries@haskell.org'++ac_unique_file="include/HsNet.h"+# Factoring default headers for most tests.+ac_includes_default="\+#include <stdio.h>+#ifdef HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#ifdef HAVE_SYS_STAT_H+# include <sys/stat.h>+#endif+#ifdef STDC_HEADERS+# include <stdlib.h>+# include <stddef.h>+#else+# ifdef HAVE_STDLIB_H+# include <stdlib.h>+# endif+#endif+#ifdef HAVE_STRING_H+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H+# include <memory.h>+# endif+# include <string.h>+#endif+#ifdef HAVE_STRINGS_H+# include <strings.h>+#endif+#ifdef HAVE_INTTYPES_H+# include <inttypes.h>+#endif+#ifdef HAVE_STDINT_H+# include <stdint.h>+#endif+#ifdef HAVE_UNISTD_H+# include <unistd.h>+#endif"++ac_subst_vars='SHELL+PATH_SEPARATOR+PACKAGE_NAME+PACKAGE_TARNAME+PACKAGE_VERSION+PACKAGE_STRING+PACKAGE_BUGREPORT+exec_prefix+prefix+program_transform_name+bindir+sbindir+libexecdir+datarootdir+datadir+sysconfdir+sharedstatedir+localstatedir+includedir+oldincludedir+docdir+infodir+htmldir+dvidir+pdfdir+psdir+libdir+localedir+mandir+DEFS+ECHO_C+ECHO_N+ECHO_T+LIBS+build_alias+host_alias+target_alias+build+build_cpu+build_vendor+build_os+host+host_cpu+host_vendor+host_os+CC+CFLAGS+LDFLAGS+CPPFLAGS+ac_ct_CC+EXEEXT+OBJEXT+CPP+GREP+EGREP+CALLCONV+EXTRA_CPPFLAGS+EXTRA_LIBS+EXTRA_SRCS+LIBOBJS+LTLIBOBJS'+ac_subst_files=''+ ac_precious_vars='build_alias+host_alias+target_alias+CC+CFLAGS+LDFLAGS+LIBS+CPPFLAGS+CPP'+++# Initialize some variables set by options.+ac_init_help=+ac_init_version=false+# The variables have the same names as the options, with+# dashes changed to underlines.+cache_file=/dev/null+exec_prefix=NONE+no_create=+no_recursion=+prefix=NONE+program_prefix=NONE+program_suffix=NONE+program_transform_name=s,x,x,+silent=+site=+srcdir=+verbose=+x_includes=NONE+x_libraries=NONE++# Installation directory options.+# These are left unexpanded so users can "make install exec_prefix=/foo"+# and all the variables that are supposed to be based on exec_prefix+# by default will actually change.+# Use braces instead of parens because sh, perl, etc. also accept them.+# (The list follows the same order as the GNU Coding Standards.)+bindir='${exec_prefix}/bin'+sbindir='${exec_prefix}/sbin'+libexecdir='${exec_prefix}/libexec'+datarootdir='${prefix}/share'+datadir='${datarootdir}'+sysconfdir='${prefix}/etc'+sharedstatedir='${prefix}/com'+localstatedir='${prefix}/var'+includedir='${prefix}/include'+oldincludedir='/usr/include'+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'+infodir='${datarootdir}/info'+htmldir='${docdir}'+dvidir='${docdir}'+pdfdir='${docdir}'+psdir='${docdir}'+libdir='${exec_prefix}/lib'+localedir='${datarootdir}/locale'+mandir='${datarootdir}/man'++ac_prev=+ac_dashdash=+for ac_option+do+ # If the previous option needs an argument, assign it.+ if test -n "$ac_prev"; then+ eval $ac_prev=\$ac_option+ ac_prev=+ continue+ fi++ case $ac_option in+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;+ *) ac_optarg=yes ;;+ esac++ # Accept the important Cygnus configure options, so we can diagnose typos.++ case $ac_dashdash$ac_option in+ --)+ ac_dashdash=yes ;;++ -bindir | --bindir | --bindi | --bind | --bin | --bi)+ ac_prev=bindir ;;+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)+ bindir=$ac_optarg ;;++ -build | --build | --buil | --bui | --bu)+ ac_prev=build_alias ;;+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)+ build_alias=$ac_optarg ;;++ -cache-file | --cache-file | --cache-fil | --cache-fi \+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)+ ac_prev=cache_file ;;+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)+ cache_file=$ac_optarg ;;++ --config-cache | -C)+ cache_file=config.cache ;;++ -datadir | --datadir | --datadi | --datad)+ ac_prev=datadir ;;+ -datadir=* | --datadir=* | --datadi=* | --datad=*)+ datadir=$ac_optarg ;;++ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \+ | --dataroo | --dataro | --datar)+ ac_prev=datarootdir ;;+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)+ datarootdir=$ac_optarg ;;++ -disable-* | --disable-*)+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2+ { (exit 1); exit 1; }; }+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`+ eval enable_$ac_feature=no ;;++ -docdir | --docdir | --docdi | --doc | --do)+ ac_prev=docdir ;;+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)+ docdir=$ac_optarg ;;++ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)+ ac_prev=dvidir ;;+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)+ dvidir=$ac_optarg ;;++ -enable-* | --enable-*)+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2+ { (exit 1); exit 1; }; }+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`+ eval enable_$ac_feature=\$ac_optarg ;;++ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \+ | --exec | --exe | --ex)+ ac_prev=exec_prefix ;;+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \+ | --exec=* | --exe=* | --ex=*)+ exec_prefix=$ac_optarg ;;++ -gas | --gas | --ga | --g)+ # Obsolete; use --with-gas.+ with_gas=yes ;;++ -help | --help | --hel | --he | -h)+ ac_init_help=long ;;+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)+ ac_init_help=recursive ;;+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)+ ac_init_help=short ;;++ -host | --host | --hos | --ho)+ ac_prev=host_alias ;;+ -host=* | --host=* | --hos=* | --ho=*)+ host_alias=$ac_optarg ;;++ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)+ ac_prev=htmldir ;;+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \+ | --ht=*)+ htmldir=$ac_optarg ;;++ -includedir | --includedir | --includedi | --included | --include \+ | --includ | --inclu | --incl | --inc)+ ac_prev=includedir ;;+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \+ | --includ=* | --inclu=* | --incl=* | --inc=*)+ includedir=$ac_optarg ;;++ -infodir | --infodir | --infodi | --infod | --info | --inf)+ ac_prev=infodir ;;+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)+ infodir=$ac_optarg ;;++ -libdir | --libdir | --libdi | --libd)+ ac_prev=libdir ;;+ -libdir=* | --libdir=* | --libdi=* | --libd=*)+ libdir=$ac_optarg ;;++ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \+ | --libexe | --libex | --libe)+ ac_prev=libexecdir ;;+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \+ | --libexe=* | --libex=* | --libe=*)+ libexecdir=$ac_optarg ;;++ -localedir | --localedir | --localedi | --localed | --locale)+ ac_prev=localedir ;;+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)+ localedir=$ac_optarg ;;++ -localstatedir | --localstatedir | --localstatedi | --localstated \+ | --localstate | --localstat | --localsta | --localst | --locals)+ ac_prev=localstatedir ;;+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)+ localstatedir=$ac_optarg ;;++ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)+ ac_prev=mandir ;;+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)+ mandir=$ac_optarg ;;++ -nfp | --nfp | --nf)+ # Obsolete; use --without-fp.+ with_fp=no ;;++ -no-create | --no-create | --no-creat | --no-crea | --no-cre \+ | --no-cr | --no-c | -n)+ no_create=yes ;;++ -no-recursion | --no-recursion | --no-recursio | --no-recursi \+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)+ no_recursion=yes ;;++ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \+ | --oldin | --oldi | --old | --ol | --o)+ ac_prev=oldincludedir ;;+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)+ oldincludedir=$ac_optarg ;;++ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)+ ac_prev=prefix ;;+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)+ prefix=$ac_optarg ;;++ -program-prefix | --program-prefix | --program-prefi | --program-pref \+ | --program-pre | --program-pr | --program-p)+ ac_prev=program_prefix ;;+ -program-prefix=* | --program-prefix=* | --program-prefi=* \+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)+ program_prefix=$ac_optarg ;;++ -program-suffix | --program-suffix | --program-suffi | --program-suff \+ | --program-suf | --program-su | --program-s)+ ac_prev=program_suffix ;;+ -program-suffix=* | --program-suffix=* | --program-suffi=* \+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)+ program_suffix=$ac_optarg ;;++ -program-transform-name | --program-transform-name \+ | --program-transform-nam | --program-transform-na \+ | --program-transform-n | --program-transform- \+ | --program-transform | --program-transfor \+ | --program-transfo | --program-transf \+ | --program-trans | --program-tran \+ | --progr-tra | --program-tr | --program-t)+ ac_prev=program_transform_name ;;+ -program-transform-name=* | --program-transform-name=* \+ | --program-transform-nam=* | --program-transform-na=* \+ | --program-transform-n=* | --program-transform-=* \+ | --program-transform=* | --program-transfor=* \+ | --program-transfo=* | --program-transf=* \+ | --program-trans=* | --program-tran=* \+ | --progr-tra=* | --program-tr=* | --program-t=*)+ program_transform_name=$ac_optarg ;;++ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)+ ac_prev=pdfdir ;;+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)+ pdfdir=$ac_optarg ;;++ -psdir | --psdir | --psdi | --psd | --ps)+ ac_prev=psdir ;;+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)+ psdir=$ac_optarg ;;++ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil)+ silent=yes ;;++ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)+ ac_prev=sbindir ;;+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \+ | --sbi=* | --sb=*)+ sbindir=$ac_optarg ;;++ -sharedstatedir | --sharedstatedir | --sharedstatedi \+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \+ | --sharedst | --shareds | --shared | --share | --shar \+ | --sha | --sh)+ ac_prev=sharedstatedir ;;+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \+ | --sha=* | --sh=*)+ sharedstatedir=$ac_optarg ;;++ -site | --site | --sit)+ ac_prev=site ;;+ -site=* | --site=* | --sit=*)+ site=$ac_optarg ;;++ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)+ ac_prev=srcdir ;;+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)+ srcdir=$ac_optarg ;;++ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \+ | --syscon | --sysco | --sysc | --sys | --sy)+ ac_prev=sysconfdir ;;+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)+ sysconfdir=$ac_optarg ;;++ -target | --target | --targe | --targ | --tar | --ta | --t)+ ac_prev=target_alias ;;+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)+ target_alias=$ac_optarg ;;++ -v | -verbose | --verbose | --verbos | --verbo | --verb)+ verbose=yes ;;++ -version | --version | --versio | --versi | --vers | -V)+ ac_init_version=: ;;++ -with-* | --with-*)+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid package name: $ac_package" >&2+ { (exit 1); exit 1; }; }+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`+ eval with_$ac_package=\$ac_optarg ;;++ -without-* | --without-*)+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid package name: $ac_package" >&2+ { (exit 1); exit 1; }; }+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`+ eval with_$ac_package=no ;;++ --x)+ # Obsolete; use --with-x.+ with_x=yes ;;++ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \+ | --x-incl | --x-inc | --x-in | --x-i)+ ac_prev=x_includes ;;+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)+ x_includes=$ac_optarg ;;++ -x-libraries | --x-libraries | --x-librarie | --x-librari \+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)+ ac_prev=x_libraries ;;+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)+ x_libraries=$ac_optarg ;;++ -*) { echo "$as_me: error: unrecognized option: $ac_option+Try \`$0 --help' for more information." >&2+ { (exit 1); exit 1; }; }+ ;;++ *=*)+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`+ # Reject names that are not valid shell variable names.+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2+ { (exit 1); exit 1; }; }+ eval $ac_envvar=\$ac_optarg+ export $ac_envvar ;;++ *)+ # FIXME: should be removed in autoconf 3.0.+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}+ ;;++ esac+done++if test -n "$ac_prev"; then+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`+ { echo "$as_me: error: missing argument to $ac_option" >&2+ { (exit 1); exit 1; }; }+fi++# Be sure to have absolute directory names.+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \+ datadir sysconfdir sharedstatedir localstatedir includedir \+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \+ libdir localedir mandir+do+ eval ac_val=\$$ac_var+ case $ac_val in+ [\\/$]* | ?:[\\/]* ) continue;;+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;+ esac+ { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2+ { (exit 1); exit 1; }; }+done++# There might be people who depend on the old broken behavior: `$host'+# used to hold the argument of --host etc.+# FIXME: To remove some day.+build=$build_alias+host=$host_alias+target=$target_alias++# FIXME: To remove some day.+if test "x$host_alias" != x; then+ if test "x$build_alias" = x; then+ cross_compiling=maybe+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.+ If a cross compiler is detected then cross compile mode will be used." >&2+ elif test "x$build_alias" != "x$host_alias"; then+ cross_compiling=yes+ fi+fi++ac_tool_prefix=+test -n "$host_alias" && ac_tool_prefix=$host_alias-++test "$silent" = yes && exec 6>/dev/null+++ac_pwd=`pwd` && test -n "$ac_pwd" &&+ac_ls_di=`ls -di .` &&+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||+ { echo "$as_me: error: Working directory cannot be determined" >&2+ { (exit 1); exit 1; }; }+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||+ { echo "$as_me: error: pwd does not report name of working directory" >&2+ { (exit 1); exit 1; }; }+++# Find the source files, if location was not specified.+if test -z "$srcdir"; then+ ac_srcdir_defaulted=yes+ # Try the directory containing this script, then the parent directory.+ ac_confdir=`$as_dirname -- "$0" ||+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$0" : 'X\(//\)[^/]' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$0" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ srcdir=$ac_confdir+ if test ! -r "$srcdir/$ac_unique_file"; then+ srcdir=..+ fi+else+ ac_srcdir_defaulted=no+fi+if test ! -r "$srcdir/$ac_unique_file"; then+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2+ { (exit 1); exit 1; }; }+fi+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"+ac_abs_confdir=`(+ cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2+ { (exit 1); exit 1; }; }+ pwd)`+# When building in place, set srcdir=.+if test "$ac_abs_confdir" = "$ac_pwd"; then+ srcdir=.+fi+# Remove unnecessary trailing slashes from srcdir.+# Double slashes in file names in object file debugging info+# mess up M-x gdb in Emacs.+case $srcdir in+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;+esac+for ac_var in $ac_precious_vars; do+ eval ac_env_${ac_var}_set=\${${ac_var}+set}+ eval ac_env_${ac_var}_value=\$${ac_var}+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}+ eval ac_cv_env_${ac_var}_value=\$${ac_var}+done++#+# Report the --help message.+#+if test "$ac_init_help" = "long"; then+ # Omit some internal or obsolete options to make the list less imposing.+ # This message is too long to be a string in the A/UX 3.1 sh.+ cat <<_ACEOF+\`configure' configures Haskell network package 2.3.0.11 to adapt to many kinds of systems.++Usage: $0 [OPTION]... [VAR=VALUE]...++To assign environment variables (e.g., CC, CFLAGS...), specify them as+VAR=VALUE. See below for descriptions of some of the useful variables.++Defaults for the options are specified in brackets.++Configuration:+ -h, --help display this help and exit+ --help=short display options specific to this package+ --help=recursive display the short help of all the included packages+ -V, --version display version information and exit+ -q, --quiet, --silent do not print \`checking...' messages+ --cache-file=FILE cache test results in FILE [disabled]+ -C, --config-cache alias for \`--cache-file=config.cache'+ -n, --no-create do not create output files+ --srcdir=DIR find the sources in DIR [configure dir or \`..']++Installation directories:+ --prefix=PREFIX install architecture-independent files in PREFIX+ [$ac_default_prefix]+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX+ [PREFIX]++By default, \`make install' will install all the files in+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify+an installation prefix other than \`$ac_default_prefix' using \`--prefix',+for instance \`--prefix=\$HOME'.++For better control, use the options below.++Fine tuning of the installation directories:+ --bindir=DIR user executables [EPREFIX/bin]+ --sbindir=DIR system admin executables [EPREFIX/sbin]+ --libexecdir=DIR program executables [EPREFIX/libexec]+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]+ --libdir=DIR object code libraries [EPREFIX/lib]+ --includedir=DIR C header files [PREFIX/include]+ --oldincludedir=DIR C header files for non-gcc [/usr/include]+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]+ --infodir=DIR info documentation [DATAROOTDIR/info]+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]+ --mandir=DIR man documentation [DATAROOTDIR/man]+ --docdir=DIR documentation root [DATAROOTDIR/doc/network]+ --htmldir=DIR html documentation [DOCDIR]+ --dvidir=DIR dvi documentation [DOCDIR]+ --pdfdir=DIR pdf documentation [DOCDIR]+ --psdir=DIR ps documentation [DOCDIR]+_ACEOF++ cat <<\_ACEOF++System types:+ --build=BUILD configure for building on BUILD [guessed]+ --host=HOST cross-compile to build programs to run on HOST [BUILD]+_ACEOF+fi++if test -n "$ac_init_help"; then+ case $ac_init_help in+ short | recursive ) echo "Configuration of Haskell network package 2.3.0.11:";;+ esac+ cat <<\_ACEOF++Optional Packages:+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)+C compiler++Some influential environment variables:+ CC C compiler command+ CFLAGS C compiler flags+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a+ nonstandard directory <lib dir>+ LIBS libraries to pass to the linker, e.g. -l<library>+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if+ you have headers in a nonstandard directory <include dir>+ CPP C preprocessor++Use these variables to override the choices made by `configure' or to help+it to find libraries and programs with nonstandard names/locations.++Report bugs to <libraries@haskell.org>.+_ACEOF+ac_status=$?+fi++if test "$ac_init_help" = "recursive"; then+ # If there are subdirs, report their specific --help.+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue+ test -d "$ac_dir" || continue+ ac_builddir=.++case "$ac_dir" in+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;+*)+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`+ # A ".." for each directory in $ac_dir_suffix.+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`+ case $ac_top_builddir_sub in+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;+ esac ;;+esac+ac_abs_top_builddir=$ac_pwd+ac_abs_builddir=$ac_pwd$ac_dir_suffix+# for backward compatibility:+ac_top_builddir=$ac_top_build_prefix++case $srcdir in+ .) # We are building in place.+ ac_srcdir=.+ ac_top_srcdir=$ac_top_builddir_sub+ ac_abs_top_srcdir=$ac_pwd ;;+ [\\/]* | ?:[\\/]* ) # Absolute name.+ ac_srcdir=$srcdir$ac_dir_suffix;+ ac_top_srcdir=$srcdir+ ac_abs_top_srcdir=$srcdir ;;+ *) # Relative name.+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix+ ac_top_srcdir=$ac_top_build_prefix$srcdir+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;+esac+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix++ cd "$ac_dir" || { ac_status=$?; continue; }+ # Check for guested configure.+ if test -f "$ac_srcdir/configure.gnu"; then+ echo &&+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive+ elif test -f "$ac_srcdir/configure"; then+ echo &&+ $SHELL "$ac_srcdir/configure" --help=recursive+ else+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2+ fi || ac_status=$?+ cd "$ac_pwd" || { ac_status=$?; break; }+ done+fi++test -n "$ac_init_help" && exit $ac_status+if $ac_init_version; then+ cat <<\_ACEOF+Haskell network package configure 2.3.0.11+generated by GNU Autoconf 2.61++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.+This configure script is free software; the Free Software Foundation+gives unlimited permission to copy, distribute and modify it.+_ACEOF+ exit+fi+cat >config.log <<_ACEOF+This file contains any messages produced by compilers while+running configure, to aid debugging if configure makes a mistake.++It was created by Haskell network package $as_me 2.3.0.11, which was+generated by GNU Autoconf 2.61. Invocation command line was++ $ $0 $@++_ACEOF+exec 5>>config.log+{+cat <<_ASUNAME+## --------- ##+## Platform. ##+## --------- ##++hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`+uname -m = `(uname -m) 2>/dev/null || echo unknown`+uname -r = `(uname -r) 2>/dev/null || echo unknown`+uname -s = `(uname -s) 2>/dev/null || echo unknown`+uname -v = `(uname -v) 2>/dev/null || echo unknown`++/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`++/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`++_ASUNAME++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ echo "PATH: $as_dir"+done+IFS=$as_save_IFS++} >&5++cat >&5 <<_ACEOF+++## ----------- ##+## Core tests. ##+## ----------- ##++_ACEOF+++# Keep a trace of the command line.+# Strip out --no-create and --no-recursion so they do not pile up.+# Strip out --silent because we don't want to record it for future runs.+# Also quote any args containing shell meta-characters.+# Make two passes to allow for proper duplicate-argument suppression.+ac_configure_args=+ac_configure_args0=+ac_configure_args1=+ac_must_keep_next=false+for ac_pass in 1 2+do+ for ac_arg+ do+ case $ac_arg in+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil)+ continue ;;+ *\'*)+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;+ esac+ case $ac_pass in+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;+ 2)+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"+ if test $ac_must_keep_next = true; then+ ac_must_keep_next=false # Got value, back to normal.+ else+ case $ac_arg in+ *=* | --config-cache | -C | -disable-* | --disable-* \+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \+ | -with-* | --with-* | -without-* | --without-* | --x)+ case "$ac_configure_args0 " in+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;+ esac+ ;;+ -* ) ac_must_keep_next=true ;;+ esac+ fi+ ac_configure_args="$ac_configure_args '$ac_arg'"+ ;;+ esac+ done+done+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }++# When interrupted or exit'd, cleanup temporary files, and complete+# config.log. We remove comments because anyway the quotes in there+# would cause problems or look ugly.+# WARNING: Use '\'' to represent an apostrophe within the trap.+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.+trap 'exit_status=$?+ # Save into config.log some information that might help in debugging.+ {+ echo++ cat <<\_ASBOX+## ---------------- ##+## Cache variables. ##+## ---------------- ##+_ASBOX+ echo+ # The following way of writing the cache mishandles newlines in values,+(+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do+ eval ac_val=\$$ac_var+ case $ac_val in #(+ *${as_nl}*)+ case $ac_var in #(+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;+ esac+ case $ac_var in #(+ _ | IFS | as_nl) ;; #(+ *) $as_unset $ac_var ;;+ esac ;;+ esac+ done+ (set) 2>&1 |+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(+ *${as_nl}ac_space=\ *)+ sed -n \+ "s/'\''/'\''\\\\'\'''\''/g;+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"+ ;; #(+ *)+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"+ ;;+ esac |+ sort+)+ echo++ cat <<\_ASBOX+## ----------------- ##+## Output variables. ##+## ----------------- ##+_ASBOX+ echo+ for ac_var in $ac_subst_vars+ do+ eval ac_val=\$$ac_var+ case $ac_val in+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;+ esac+ echo "$ac_var='\''$ac_val'\''"+ done | sort+ echo++ if test -n "$ac_subst_files"; then+ cat <<\_ASBOX+## ------------------- ##+## File substitutions. ##+## ------------------- ##+_ASBOX+ echo+ for ac_var in $ac_subst_files+ do+ eval ac_val=\$$ac_var+ case $ac_val in+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;+ esac+ echo "$ac_var='\''$ac_val'\''"+ done | sort+ echo+ fi++ if test -s confdefs.h; then+ cat <<\_ASBOX+## ----------- ##+## confdefs.h. ##+## ----------- ##+_ASBOX+ echo+ cat confdefs.h+ echo+ fi+ test "$ac_signal" != 0 &&+ echo "$as_me: caught signal $ac_signal"+ echo "$as_me: exit $exit_status"+ } >&5+ rm -f core *.core core.conftest.* &&+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&+ exit $exit_status+' 0+for ac_signal in 1 2 13 15; do+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal+done+ac_signal=0++# confdefs.h avoids OS command line length limits that DEFS can exceed.+rm -f -r conftest* confdefs.h++# Predefined preprocessor variables.++cat >>confdefs.h <<_ACEOF+#define PACKAGE_NAME "$PACKAGE_NAME"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_VERSION "$PACKAGE_VERSION"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_STRING "$PACKAGE_STRING"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"+_ACEOF+++# Let the site file select an alternate cache file if it wants to.+# Prefer explicitly selected file to automatically selected ones.+if test -n "$CONFIG_SITE"; then+ set x "$CONFIG_SITE"+elif test "x$prefix" != xNONE; then+ set x "$prefix/share/config.site" "$prefix/etc/config.site"+else+ set x "$ac_default_prefix/share/config.site" \+ "$ac_default_prefix/etc/config.site"+fi+shift+for ac_site_file+do+ if test -r "$ac_site_file"; then+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5+echo "$as_me: loading site script $ac_site_file" >&6;}+ sed 's/^/| /' "$ac_site_file" >&5+ . "$ac_site_file"+ fi+done++if test -r "$cache_file"; then+ # Some versions of bash will fail to source /dev/null (special+ # files actually), so we avoid doing that.+ if test -f "$cache_file"; then+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5+echo "$as_me: loading cache $cache_file" >&6;}+ case $cache_file in+ [\\/]* | ?:[\\/]* ) . "$cache_file";;+ *) . "./$cache_file";;+ esac+ fi+else+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5+echo "$as_me: creating cache $cache_file" >&6;}+ >$cache_file+fi++# Check that the precious variables saved in the cache have kept the same+# value.+ac_cache_corrupted=false+for ac_var in $ac_precious_vars; do+ eval ac_old_set=\$ac_cv_env_${ac_var}_set+ eval ac_new_set=\$ac_env_${ac_var}_set+ eval ac_old_val=\$ac_cv_env_${ac_var}_value+ eval ac_new_val=\$ac_env_${ac_var}_value+ case $ac_old_set,$ac_new_set in+ set,)+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}+ ac_cache_corrupted=: ;;+ ,set)+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}+ ac_cache_corrupted=: ;;+ ,);;+ *)+ if test "x$ac_old_val" != "x$ac_new_val"; then+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5+echo "$as_me: former value: $ac_old_val" >&2;}+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5+echo "$as_me: current value: $ac_new_val" >&2;}+ ac_cache_corrupted=:+ fi;;+ esac+ # Pass precious variables to config.status.+ if test "$ac_new_set" = set; then+ case $ac_new_val in+ *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;+ *) ac_arg=$ac_var=$ac_new_val ;;+ esac+ case " $ac_configure_args " in+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;+ esac+ fi+done+if $ac_cache_corrupted; then+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5+echo "$as_me: error: changes in the environment can compromise the build" >&2;}+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}+ { (exit 1); exit 1; }; }+fi++++++++++++++++++++++++++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu++++ac_includes_default="$ac_includes_default+#ifdef HAVE_NETDB_H+# include <netdb.h>+#endif+#ifdef HAVE_WINSOCK2_H+# include <winsock2.h>+#endif+#ifdef HAVE_WS2TCPIP_H+# include <ws2tcpip.h>+// fix for MingW not defining IPV6_V6ONLY+# define IPV6_V6ONLY 27+#endif+#ifdef HAVE_WSPIAPI_H+# include <wspiapi.h>+#endif"++# Safety check: Ensure that we are in the correct source directory.+++ac_config_headers="$ac_config_headers include/HsNetworkConfig.h"+++ac_aux_dir=+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do+ if test -f "$ac_dir/install-sh"; then+ ac_aux_dir=$ac_dir+ ac_install_sh="$ac_aux_dir/install-sh -c"+ break+ elif test -f "$ac_dir/install.sh"; then+ ac_aux_dir=$ac_dir+ ac_install_sh="$ac_aux_dir/install.sh -c"+ break+ elif test -f "$ac_dir/shtool"; then+ ac_aux_dir=$ac_dir+ ac_install_sh="$ac_aux_dir/shtool install -c"+ break+ fi+done+if test -z "$ac_aux_dir"; then+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5+echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;}+ { (exit 1); exit 1; }; }+fi++# These three variables are undocumented and unsupported,+# and are intended to be withdrawn in a future Autoconf release.+# They can cause serious problems if a builder's source tree is in a directory+# whose full name contains unusual characters.+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.+++# Make sure we can run config.sub.+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||+ { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5+echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;}+ { (exit 1); exit 1; }; }++{ echo "$as_me:$LINENO: checking build system type" >&5+echo $ECHO_N "checking build system type... $ECHO_C" >&6; }+if test "${ac_cv_build+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_build_alias=$build_alias+test "x$ac_build_alias" = x &&+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`+test "x$ac_build_alias" = x &&+ { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}+ { (exit 1); exit 1; }; }+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;}+ { (exit 1); exit 1; }; }++fi+{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5+echo "${ECHO_T}$ac_cv_build" >&6; }+case $ac_cv_build in+*-*-*) ;;+*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5+echo "$as_me: error: invalid value of canonical build" >&2;}+ { (exit 1); exit 1; }; };;+esac+build=$ac_cv_build+ac_save_IFS=$IFS; IFS='-'+set x $ac_cv_build+shift+build_cpu=$1+build_vendor=$2+shift; shift+# Remember, the first character of IFS is used to create $*,+# except with old shells:+build_os=$*+IFS=$ac_save_IFS+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac+++{ echo "$as_me:$LINENO: checking host system type" >&5+echo $ECHO_N "checking host system type... $ECHO_C" >&6; }+if test "${ac_cv_host+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test "x$host_alias" = x; then+ ac_cv_host=$ac_cv_build+else+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||+ { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5+echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}+ { (exit 1); exit 1; }; }+fi++fi+{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5+echo "${ECHO_T}$ac_cv_host" >&6; }+case $ac_cv_host in+*-*-*) ;;+*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5+echo "$as_me: error: invalid value of canonical host" >&2;}+ { (exit 1); exit 1; }; };;+esac+host=$ac_cv_host+ac_save_IFS=$IFS; IFS='-'+set x $ac_cv_host+shift+host_cpu=$1+host_vendor=$2+shift; shift+# Remember, the first character of IFS is used to create $*,+# except with old shells:+host_os=$*+IFS=$ac_save_IFS+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac+++++# Check whether --with-cc was given.+if test "${with_cc+set}" = set; then+ withval=$with_cc; CC=$withval+fi++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu+if test -n "$ac_tool_prefix"; then+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.+set dummy ${ac_tool_prefix}gcc; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$CC"; then+ ac_cv_prog_CC="$CC" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_CC="${ac_tool_prefix}gcc"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+CC=$ac_cv_prog_CC+if test -n "$CC"; then+ { echo "$as_me:$LINENO: result: $CC" >&5+echo "${ECHO_T}$CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++fi+if test -z "$ac_cv_prog_CC"; then+ ac_ct_CC=$CC+ # Extract the first word of "gcc", so it can be a program name with args.+set dummy gcc; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$ac_ct_CC"; then+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_ac_ct_CC="gcc"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+ac_ct_CC=$ac_cv_prog_ac_ct_CC+if test -n "$ac_ct_CC"; then+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5+echo "${ECHO_T}$ac_ct_CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi++ if test "x$ac_ct_CC" = x; then+ CC=""+ else+ case $cross_compiling:$ac_tool_warned in+yes:)+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&5+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&2;}+ac_tool_warned=yes ;;+esac+ CC=$ac_ct_CC+ fi+else+ CC="$ac_cv_prog_CC"+fi++if test -z "$CC"; then+ if test -n "$ac_tool_prefix"; then+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.+set dummy ${ac_tool_prefix}cc; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$CC"; then+ ac_cv_prog_CC="$CC" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_CC="${ac_tool_prefix}cc"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+CC=$ac_cv_prog_CC+if test -n "$CC"; then+ { echo "$as_me:$LINENO: result: $CC" >&5+echo "${ECHO_T}$CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++ fi+fi+if test -z "$CC"; then+ # Extract the first word of "cc", so it can be a program name with args.+set dummy cc; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$CC"; then+ ac_cv_prog_CC="$CC" # Let the user override the test.+else+ ac_prog_rejected=no+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then+ ac_prog_rejected=yes+ continue+ fi+ ac_cv_prog_CC="cc"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++if test $ac_prog_rejected = yes; then+ # We found a bogon in the path, so make sure we never use it.+ set dummy $ac_cv_prog_CC+ shift+ if test $# != 0; then+ # We chose a different compiler from the bogus one.+ # However, it has the same basename, so the bogon will be chosen+ # first if we set CC to just the basename; use the full file name.+ shift+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"+ fi+fi+fi+fi+CC=$ac_cv_prog_CC+if test -n "$CC"; then+ { echo "$as_me:$LINENO: result: $CC" >&5+echo "${ECHO_T}$CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++fi+if test -z "$CC"; then+ if test -n "$ac_tool_prefix"; then+ for ac_prog in cl.exe+ do+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.+set dummy $ac_tool_prefix$ac_prog; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$CC"; then+ ac_cv_prog_CC="$CC" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+CC=$ac_cv_prog_CC+if test -n "$CC"; then+ { echo "$as_me:$LINENO: result: $CC" >&5+echo "${ECHO_T}$CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++ test -n "$CC" && break+ done+fi+if test -z "$CC"; then+ ac_ct_CC=$CC+ for ac_prog in cl.exe+do+ # Extract the first word of "$ac_prog", so it can be a program name with args.+set dummy $ac_prog; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$ac_ct_CC"; then+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_ac_ct_CC="$ac_prog"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+ac_ct_CC=$ac_cv_prog_ac_ct_CC+if test -n "$ac_ct_CC"; then+ { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5+echo "${ECHO_T}$ac_ct_CC" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++ test -n "$ac_ct_CC" && break+done++ if test "x$ac_ct_CC" = x; then+ CC=""+ else+ case $cross_compiling:$ac_tool_warned in+yes:)+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&5+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&2;}+ac_tool_warned=yes ;;+esac+ CC=$ac_ct_CC+ fi+fi++fi+++test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH+See \`config.log' for more details." >&5+echo "$as_me: error: no acceptable C compiler found in \$PATH+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }++# Provide some information about the compiler.+echo "$as_me:$LINENO: checking for C compiler version" >&5+ac_compiler=`set X $ac_compile; echo $2`+{ (ac_try="$ac_compiler --version >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler --version >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }+{ (ac_try="$ac_compiler -v >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler -v >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }+{ (ac_try="$ac_compiler -V >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler -V >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }++cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+ac_clean_files_save=$ac_clean_files+ac_clean_files="$ac_clean_files a.out a.exe b.out"+# Try to create an executable without -o first, disregard a.out.+# It will help us diagnose broken compilers, and finding out an intuition+# of exeext.+{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; }+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`+#+# List of possible output files, starting from the most likely.+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)+# only as a last resort. b.out is created by i960 compilers.+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'+#+# The IRIX 6 linker writes into existing files which may not be+# executable, retaining their permissions. Remove them first so a+# subsequent execution test works.+ac_rmfiles=+for ac_file in $ac_files+do+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;+ esac+done+rm -f $ac_rmfiles++if { (ac_try="$ac_link_default"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link_default") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'+# in a Makefile. We should not override ac_cv_exeext if it was cached,+# so that the user can short-circuit this test for compilers unknown to+# Autoconf.+for ac_file in $ac_files ''+do+ test -f "$ac_file" || continue+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )+ ;;+ [ab].out )+ # We found the default executable, but exeext='' is most+ # certainly right.+ break;;+ *.* )+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;+ then :; else+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`+ fi+ # We set ac_cv_exeext here because the later test for it is not+ # safe: cross compilers may not add the suffix if given an `-o'+ # argument, so we may need to know it at that point already.+ # Even if this section looks crufty: it has the advantage of+ # actually working.+ break;;+ * )+ break;;+ esac+done+test "$ac_cv_exeext" = no && ac_cv_exeext=++else+ ac_file=''+fi++{ echo "$as_me:$LINENO: result: $ac_file" >&5+echo "${ECHO_T}$ac_file" >&6; }+if test -z "$ac_file"; then+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++{ { echo "$as_me:$LINENO: error: C compiler cannot create executables+See \`config.log' for more details." >&5+echo "$as_me: error: C compiler cannot create executables+See \`config.log' for more details." >&2;}+ { (exit 77); exit 77; }; }+fi++ac_exeext=$ac_cv_exeext++# Check that the compiler produces executables we can run. If not, either+# the compiler is broken, or we cross compile.+{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; }+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0+# If not cross compiling, check that we can run a simple program.+if test "$cross_compiling" != yes; then+ if { ac_try='./$ac_file'+ { (case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_try") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; }; then+ cross_compiling=no+ else+ if test "$cross_compiling" = maybe; then+ cross_compiling=yes+ else+ { { echo "$as_me:$LINENO: error: cannot run C compiled programs.+If you meant to cross compile, use \`--host'.+See \`config.log' for more details." >&5+echo "$as_me: error: cannot run C compiled programs.+If you meant to cross compile, use \`--host'.+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+ fi+ fi+fi+{ echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }++rm -f a.out a.exe conftest$ac_cv_exeext b.out+ac_clean_files=$ac_clean_files_save+# Check that the compiler produces executables we can run. If not, either+# the compiler is broken, or we cross compile.+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5+echo "${ECHO_T}$cross_compiling" >&6; }++{ echo "$as_me:$LINENO: checking for suffix of executables" >&5+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ # If both `conftest.exe' and `conftest' are `present' (well, observable)+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will+# work properly (i.e., refer to `conftest.exe'), while it won't with+# `rm'.+for ac_file in conftest.exe conftest conftest.*; do+ test -f "$ac_file" || continue+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`+ break;;+ * ) break;;+ esac+done+else+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link+See \`config.log' for more details." >&5+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++rm -f conftest$ac_cv_exeext+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5+echo "${ECHO_T}$ac_cv_exeext" >&6; }++rm -f conftest.$ac_ext+EXEEXT=$ac_cv_exeext+ac_exeext=$EXEEXT+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }+if test "${ac_cv_objext+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.o conftest.obj+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ for ac_file in conftest.o conftest.obj conftest.*; do+ test -f "$ac_file" || continue;+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`+ break;;+ esac+done+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile+See \`config.log' for more details." >&5+echo "$as_me: error: cannot compute suffix of object files: cannot compile+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++rm -f conftest.$ac_cv_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5+echo "${ECHO_T}$ac_cv_objext" >&6; }+OBJEXT=$ac_cv_objext+ac_objext=$OBJEXT+{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; }+if test "${ac_cv_c_compiler_gnu+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{+#ifndef __GNUC__+ choke me+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_compiler_gnu=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_compiler_gnu=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ac_cv_c_compiler_gnu=$ac_compiler_gnu++fi+{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; }+GCC=`test $ac_compiler_gnu = yes && echo yes`+ac_test_CFLAGS=${CFLAGS+set}+ac_save_CFLAGS=$CFLAGS+{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; }+if test "${ac_cv_prog_cc_g+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_save_c_werror_flag=$ac_c_werror_flag+ ac_c_werror_flag=yes+ ac_cv_prog_cc_g=no+ CFLAGS="-g"+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_prog_cc_g=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ CFLAGS=""+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_c_werror_flag=$ac_save_c_werror_flag+ CFLAGS="-g"+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_prog_cc_g=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5+++fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ ac_c_werror_flag=$ac_save_c_werror_flag+fi+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; }+if test "$ac_test_CFLAGS" = set; then+ CFLAGS=$ac_save_CFLAGS+elif test $ac_cv_prog_cc_g = yes; then+ if test "$GCC" = yes; then+ CFLAGS="-g -O2"+ else+ CFLAGS="-g"+ fi+else+ if test "$GCC" = yes; then+ CFLAGS="-O2"+ else+ CFLAGS=+ fi+fi+{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5+echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }+if test "${ac_cv_prog_cc_c89+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_cv_prog_cc_c89=no+ac_save_CC=$CC+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <stdarg.h>+#include <stdio.h>+#include <sys/types.h>+#include <sys/stat.h>+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */+struct buf { int x; };+FILE * (*rcsopen) (struct buf *, struct stat *, int);+static char *e (p, i)+ char **p;+ int i;+{+ return p[i];+}+static char *f (char * (*g) (char **, int), char **p, ...)+{+ char *s;+ va_list v;+ va_start (v,p);+ s = g (p, va_arg (v,int));+ va_end (v);+ return s;+}++/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has+ function prototypes and stuff, but not '\xHH' hex character constants.+ These don't provoke an error unfortunately, instead are silently treated+ as 'x'. The following induces an error, until -std is added to get+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an+ array size at least. It's necessary to write '\x00'==0 to get something+ that's true only with -std. */+int osf4_cc_array ['\x00' == 0 ? 1 : -1];++/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters+ inside strings and character constants. */+#define FOO(x) 'x'+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];++int test (int i, double x);+struct s1 {int (*f) (int a);};+struct s2 {int (*f) (double a);};+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);+int argc;+char **argv;+int+main ()+{+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];+ ;+ return 0;+}+_ACEOF+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"+do+ CC="$ac_save_CC $ac_arg"+ rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_prog_cc_c89=$ac_arg+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5+++fi++rm -f core conftest.err conftest.$ac_objext+ test "x$ac_cv_prog_cc_c89" != "xno" && break+done+rm -f conftest.$ac_ext+CC=$ac_save_CC++fi+# AC_CACHE_VAL+case "x$ac_cv_prog_cc_c89" in+ x)+ { echo "$as_me:$LINENO: result: none needed" >&5+echo "${ECHO_T}none needed" >&6; } ;;+ xno)+ { echo "$as_me:$LINENO: result: unsupported" >&5+echo "${ECHO_T}unsupported" >&6; } ;;+ *)+ CC="$CC $ac_cv_prog_cc_c89"+ { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5+echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;+esac+++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu++++{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5+echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; }+if test "${ac_cv_c_const+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{+/* FIXME: Include the comments suggested by Paul. */+#ifndef __cplusplus+ /* Ultrix mips cc rejects this. */+ typedef int charset[2];+ const charset cs;+ /* SunOS 4.1.1 cc rejects this. */+ char const *const *pcpcc;+ char **ppc;+ /* NEC SVR4.0.2 mips cc rejects this. */+ struct point {int x, y;};+ static struct point const zero = {0,0};+ /* AIX XL C 1.02.0.0 rejects this.+ It does not let you subtract one const X* pointer from another in+ an arm of an if-expression whose if-part is not a constant+ expression */+ const char *g = "string";+ pcpcc = &g + (g ? g-g : 0);+ /* HPUX 7.0 cc rejects these. */+ ++pcpcc;+ ppc = (char**) pcpcc;+ pcpcc = (char const *const *) ppc;+ { /* SCO 3.2v4 cc rejects this. */+ char *t;+ char const *s = 0 ? (char *) 0 : (char const *) 0;++ *t++ = 0;+ if (s) return 0;+ }+ { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */+ int x[] = {25, 17};+ const int *foo = &x[0];+ ++foo;+ }+ { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */+ typedef const int *iptr;+ iptr p = 0;+ ++p;+ }+ { /* AIX XL C 1.02.0.0 rejects this saying+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */+ struct s { int j; const int *ap[3]; };+ struct s *b; b->j = 5;+ }+ { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */+ const int foo = 10;+ if (!foo) return 0;+ }+ return !cs[0] && !zero.x;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_c_const=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_c_const=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5+echo "${ECHO_T}$ac_cv_c_const" >&6; }+if test $ac_cv_c_const = no; then++cat >>confdefs.h <<\_ACEOF+#define const+_ACEOF++fi+++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu+{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; }+# On Suns, sometimes $CPP names a directory.+if test -n "$CPP" && test -d "$CPP"; then+ CPP=+fi+if test -z "$CPP"; then+ if test "${ac_cv_prog_CPP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ # Double quotes because CPP needs to be expanded+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"+ do+ ac_preproc_ok=false+for ac_c_preproc_warn_flag in '' yes+do+ # Use a header file that comes with gcc, so configuring glibc+ # with a fresh cross-compiler works.+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ # <limits.h> exists even on freestanding compilers.+ # On the NeXT, cc -E runs the code through the compiler's parser,+ # not just through cpp. "Syntax error" is here to catch this case.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif+ Syntax error+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Broken: fails on valid input.+continue+fi++rm -f conftest.err conftest.$ac_ext++ # OK, works on sane cases. Now check whether nonexistent headers+ # can be detected and how.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ac_nonexistent.h>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ # Broken: success on invalid input.+continue+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Passes both tests.+ac_preproc_ok=:+break+fi++rm -f conftest.err conftest.$ac_ext++done+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.+rm -f conftest.err conftest.$ac_ext+if $ac_preproc_ok; then+ break+fi++ done+ ac_cv_prog_CPP=$CPP++fi+ CPP=$ac_cv_prog_CPP+else+ ac_cv_prog_CPP=$CPP+fi+{ echo "$as_me:$LINENO: result: $CPP" >&5+echo "${ECHO_T}$CPP" >&6; }+ac_preproc_ok=false+for ac_c_preproc_warn_flag in '' yes+do+ # Use a header file that comes with gcc, so configuring glibc+ # with a fresh cross-compiler works.+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ # <limits.h> exists even on freestanding compilers.+ # On the NeXT, cc -E runs the code through the compiler's parser,+ # not just through cpp. "Syntax error" is here to catch this case.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif+ Syntax error+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Broken: fails on valid input.+continue+fi++rm -f conftest.err conftest.$ac_ext++ # OK, works on sane cases. Now check whether nonexistent headers+ # can be detected and how.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ac_nonexistent.h>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ # Broken: success on invalid input.+continue+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Passes both tests.+ac_preproc_ok=:+break+fi++rm -f conftest.err conftest.$ac_ext++done+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.+rm -f conftest.err conftest.$ac_ext+if $ac_preproc_ok; then+ :+else+ { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check+See \`config.log' for more details." >&5+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu+++{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }+if test "${ac_cv_path_GREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ # Extract the first word of "grep ggrep" to use in msg output+if test -z "$GREP"; then+set dummy grep ggrep; ac_prog_name=$2+if test "${ac_cv_path_GREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_path_GREP_found=false+# Loop through the user's path and test for each of PROGNAME-LIST+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_prog in grep ggrep; do+ for ac_exec_ext in '' $ac_executable_extensions; do+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue+ # Check for GNU ac_path_GREP and select it if it is found.+ # Check for GNU $ac_path_GREP+case `"$ac_path_GREP" --version 2>&1` in+*GNU*)+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;+*)+ ac_count=0+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"+ while :+ do+ cat "conftest.in" "conftest.in" >"conftest.tmp"+ mv "conftest.tmp" "conftest.in"+ cp "conftest.in" "conftest.nl"+ echo 'GREP' >> "conftest.nl"+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break+ ac_count=`expr $ac_count + 1`+ if test $ac_count -gt ${ac_path_GREP_max-0}; then+ # Best one so far, save it but keep looking for a better one+ ac_cv_path_GREP="$ac_path_GREP"+ ac_path_GREP_max=$ac_count+ fi+ # 10*(2^10) chars as input seems more than enough+ test $ac_count -gt 10 && break+ done+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;+esac+++ $ac_path_GREP_found && break 3+ done+done++done+IFS=$as_save_IFS+++fi++GREP="$ac_cv_path_GREP"+if test -z "$GREP"; then+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}+ { (exit 1); exit 1; }; }+fi++else+ ac_cv_path_GREP=$GREP+fi+++fi+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }+ GREP="$ac_cv_path_GREP"+++{ echo "$as_me:$LINENO: checking for egrep" >&5+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }+if test "${ac_cv_path_EGREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1+ then ac_cv_path_EGREP="$GREP -E"+ else+ # Extract the first word of "egrep" to use in msg output+if test -z "$EGREP"; then+set dummy egrep; ac_prog_name=$2+if test "${ac_cv_path_EGREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_path_EGREP_found=false+# Loop through the user's path and test for each of PROGNAME-LIST+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_prog in egrep; do+ for ac_exec_ext in '' $ac_executable_extensions; do+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue+ # Check for GNU ac_path_EGREP and select it if it is found.+ # Check for GNU $ac_path_EGREP+case `"$ac_path_EGREP" --version 2>&1` in+*GNU*)+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;+*)+ ac_count=0+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"+ while :+ do+ cat "conftest.in" "conftest.in" >"conftest.tmp"+ mv "conftest.tmp" "conftest.in"+ cp "conftest.in" "conftest.nl"+ echo 'EGREP' >> "conftest.nl"+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break+ ac_count=`expr $ac_count + 1`+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then+ # Best one so far, save it but keep looking for a better one+ ac_cv_path_EGREP="$ac_path_EGREP"+ ac_path_EGREP_max=$ac_count+ fi+ # 10*(2^10) chars as input seems more than enough+ test $ac_count -gt 10 && break+ done+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;+esac+++ $ac_path_EGREP_found && break 3+ done+done++done+IFS=$as_save_IFS+++fi++EGREP="$ac_cv_path_EGREP"+if test -z "$EGREP"; then+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}+ { (exit 1); exit 1; }; }+fi++else+ ac_cv_path_EGREP=$EGREP+fi+++ fi+fi+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }+ EGREP="$ac_cv_path_EGREP"+++{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }+if test "${ac_cv_header_stdc+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <stdlib.h>+#include <stdarg.h>+#include <string.h>+#include <float.h>++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_header_stdc=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_header_stdc=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext++if test $ac_cv_header_stdc = yes; then+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <string.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "memchr" >/dev/null 2>&1; then+ :+else+ ac_cv_header_stdc=no+fi+rm -f -r conftest*++fi++if test $ac_cv_header_stdc = yes; then+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <stdlib.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "free" >/dev/null 2>&1; then+ :+else+ ac_cv_header_stdc=no+fi+rm -f -r conftest*++fi++if test $ac_cv_header_stdc = yes; then+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.+ if test "$cross_compiling" = yes; then+ :+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ctype.h>+#include <stdlib.h>+#if ((' ' & 0x0FF) == 0x020)+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))+#else+# define ISLOWER(c) \+ (('a' <= (c) && (c) <= 'i') \+ || ('j' <= (c) && (c) <= 'r') \+ || ('s' <= (c) && (c) <= 'z'))+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))+#endif++#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))+int+main ()+{+ int i;+ for (i = 0; i < 256; i++)+ if (XOR (islower (i), ISLOWER (i))+ || toupper (i) != TOUPPER (i))+ return 2;+ return 0;+}+_ACEOF+rm -f conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'+ { (case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_try") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; }; then+ :+else+ echo "$as_me: program exited with status $ac_status" >&5+echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++( exit $ac_status )+ac_cv_header_stdc=no+fi+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext+fi+++fi+fi+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }+if test $ac_cv_header_stdc = yes; then++cat >>confdefs.h <<\_ACEOF+#define STDC_HEADERS 1+_ACEOF++fi++# On IRIX 5.3, sys/types and inttypes.h are conflicting.++++++++++for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \+ inttypes.h stdint.h unistd.h+do+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`+{ echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default++#include <$ac_header>+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ eval "$as_ac_Header=yes"+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ eval "$as_ac_Header=no"+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+if test `eval echo '${'$as_ac_Header'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1+_ACEOF++fi++done+++++++++++for ac_header in fcntl.h limits.h stdlib.h sys/types.h unistd.h winsock2.h ws2tcpip.h wspiapi.h+do+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ { echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+else+ # Is the header compilable?+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+#include <$ac_header>+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_header_compiler=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_compiler=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5+echo "${ECHO_T}$ac_header_compiler" >&6; }++# Is the header present?+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <$ac_header>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ ac_header_preproc=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_preproc=no+fi++rm -f conftest.err conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5+echo "${ECHO_T}$ac_header_preproc" >&6; }++# So? What about this header?+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in+ yes:no: )+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}+ ac_header_preproc=yes+ ;;+ no:yes:* )+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}+ ( cat <<\_ASBOX+## ------------------------------------ ##+## Report this to libraries@haskell.org ##+## ------------------------------------ ##+_ASBOX+ ) | sed "s/^/$as_me: WARNING: /" >&2+ ;;+esac+{ echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ eval "$as_ac_Header=\$ac_header_preproc"+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }++fi+if test `eval echo '${'$as_ac_Header'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1+_ACEOF++fi++done+++++++++for ac_header in arpa/inet.h netdb.h netinet/in.h netinet/tcp.h sys/socket.h sys/uio.h sys/un.h+do+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ { echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+else+ # Is the header compilable?+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+#include <$ac_header>+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_header_compiler=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_compiler=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5+echo "${ECHO_T}$ac_header_compiler" >&6; }++# Is the header present?+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <$ac_header>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||+ test ! -s conftest.err+ }; then+ ac_header_preproc=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_preproc=no+fi++rm -f conftest.err conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5+echo "${ECHO_T}$ac_header_preproc" >&6; }++# So? What about this header?+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in+ yes:no: )+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}+ ac_header_preproc=yes+ ;;+ no:yes:* )+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}+ ( cat <<\_ASBOX+## ------------------------------------ ##+## Report this to libraries@haskell.org ##+## ------------------------------------ ##+_ASBOX+ ) | sed "s/^/$as_me: WARNING: /" >&2+ ;;+esac+{ echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ eval "$as_ac_Header=\$ac_header_preproc"+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }++fi+if test `eval echo '${'$as_ac_Header'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1+_ACEOF++fi++done+++++for ac_func in readlink symlink+do+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`+{ echo "$as_me:$LINENO: checking for $ac_func" >&5+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.+ For example, HP-UX 11i <limits.h> declares gettimeofday. */+#define $ac_func innocuous_$ac_func++/* System header to define __stub macros and hopefully few prototypes,+ which can conflict with char $ac_func (); below.+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ <limits.h> exists even on freestanding compilers. */++#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif++#undef $ac_func++/* Override any GCC internal prototype to avoid an error.+ Use char because int might match the return type of a GCC+ builtin and then its argument prototype would still apply. */+#ifdef __cplusplus+extern "C"+#endif+char $ac_func ();+/* The GNU C library defines this for functions which it implements+ to always fail with ENOSYS. Some functions are actually named+ something starting with __ and the normal name is an alias. */+#if defined __stub_$ac_func || defined __stub___$ac_func+choke me+#endif++int+main ()+{+return $ac_func ();+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest$ac_exeext &&+ $as_test_x conftest$ac_exeext; then+ eval "$as_ac_var=yes"+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ eval "$as_ac_var=no"+fi++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \+ conftest$ac_exeext conftest.$ac_ext+fi+ac_res=`eval echo '${'$as_ac_var'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+if test `eval echo '${'$as_ac_var'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1+_ACEOF++fi+done+++{ echo "$as_me:$LINENO: checking for struct msghdr.msg_control" >&5+echo $ECHO_N "checking for struct msghdr.msg_control... $ECHO_C" >&6; }+if test "${ac_cv_member_struct_msghdr_msg_control+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#if HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif++int+main ()+{+static struct msghdr ac_aggr;+if (ac_aggr.msg_control)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_msghdr_msg_control=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#if HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif++int+main ()+{+static struct msghdr ac_aggr;+if (sizeof ac_aggr.msg_control)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_msghdr_msg_control=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_member_struct_msghdr_msg_control=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_msghdr_msg_control" >&5+echo "${ECHO_T}$ac_cv_member_struct_msghdr_msg_control" >&6; }+if test $ac_cv_member_struct_msghdr_msg_control = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1+_ACEOF+++fi+{ echo "$as_me:$LINENO: checking for struct msghdr.msg_accrights" >&5+echo $ECHO_N "checking for struct msghdr.msg_accrights... $ECHO_C" >&6; }+if test "${ac_cv_member_struct_msghdr_msg_accrights+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#if HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif++int+main ()+{+static struct msghdr ac_aggr;+if (ac_aggr.msg_accrights)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_msghdr_msg_accrights=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#if HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif++int+main ()+{+static struct msghdr ac_aggr;+if (sizeof ac_aggr.msg_accrights)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_msghdr_msg_accrights=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_member_struct_msghdr_msg_accrights=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_msghdr_msg_accrights" >&5+echo "${ECHO_T}$ac_cv_member_struct_msghdr_msg_accrights" >&6; }+if test $ac_cv_member_struct_msghdr_msg_accrights = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS 1+_ACEOF+++fi+++{ echo "$as_me:$LINENO: checking for struct sockaddr.sa_len" >&5+echo $ECHO_N "checking for struct sockaddr.sa_len... $ECHO_C" >&6; }+if test "${ac_cv_member_struct_sockaddr_sa_len+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif++int+main ()+{+static struct sockaddr ac_aggr;+if (ac_aggr.sa_len)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_sockaddr_sa_len=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif++int+main ()+{+static struct sockaddr ac_aggr;+if (sizeof ac_aggr.sa_len)+return 0;+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_member_struct_sockaddr_sa_len=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_member_struct_sockaddr_sa_len=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_member_struct_sockaddr_sa_len" >&5+echo "${ECHO_T}$ac_cv_member_struct_sockaddr_sa_len" >&6; }+if test $ac_cv_member_struct_sockaddr_sa_len = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_STRUCT_SOCKADDR_SA_LEN 1+_ACEOF+++fi+++{ echo "$as_me:$LINENO: checking for in_addr_t in netinet/in.h" >&5+echo $ECHO_N "checking for in_addr_t in netinet/in.h... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <netinet/in.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "in_addr_t" >/dev/null 2>&1; then++cat >>confdefs.h <<\_ACEOF+#define HAVE_IN_ADDR_T 1+_ACEOF+ { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+rm -f -r conftest*+++{ echo "$as_me:$LINENO: checking for SO_PEERCRED and struct ucred in sys/socket.h" >&5+echo $ECHO_N "checking for SO_PEERCRED and struct ucred in sys/socket.h... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <sys/types.h>+#include <sys/socket.h>+#ifndef SO_PEERCRED+# error no SO_PEERCRED+#endif+struct ucred u;+int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_ucred=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_ucred=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+if test "x$ac_cv_ucred" = xno; then+ old_CFLAGS="$CFLAGS"+ CFLAGS="-D_GNU_SOURCE $CFLAGS"+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <sys/types.h>+#include <sys/socket.h>+#ifndef SO_PEERCRED+# error no SO_PEERCRED+#endif+struct ucred u;+int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_ucred=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_ucred=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ if test "x$ac_cv_ucred" = xyes; then+ EXTRA_CPPFLAGS=-D_GNU_SOURCE+ fi+else+ old_CFLAGS="$CFLAGS"+fi+if test "x$ac_cv_ucred" = xno; then+ CFLAGS="$old_CFLAGS"+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+else++cat >>confdefs.h <<\_ACEOF+#define HAVE_STRUCT_UCRED 1+_ACEOF++ { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+fi+++{ echo "$as_me:$LINENO: checking for _head_libws2_32_a in -lws2_32" >&5+echo $ECHO_N "checking for _head_libws2_32_a in -lws2_32... $ECHO_C" >&6; }+if test "${ac_cv_lib_ws2_32__head_libws2_32_a+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_check_lib_save_LIBS=$LIBS+LIBS="-lws2_32 $LIBS"+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++/* Override any GCC internal prototype to avoid an error.+ Use char because int might match the return type of a GCC+ builtin and then its argument prototype would still apply. */+#ifdef __cplusplus+extern "C"+#endif+char _head_libws2_32_a ();+int+main ()+{+return _head_libws2_32_a ();+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest$ac_exeext &&+ $as_test_x conftest$ac_exeext; then+ ac_cv_lib_ws2_32__head_libws2_32_a=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_lib_ws2_32__head_libws2_32_a=no+fi++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \+ conftest$ac_exeext conftest.$ac_ext+LIBS=$ac_check_lib_save_LIBS+fi+{ echo "$as_me:$LINENO: result: $ac_cv_lib_ws2_32__head_libws2_32_a" >&5+echo "${ECHO_T}$ac_cv_lib_ws2_32__head_libws2_32_a" >&6; }+if test $ac_cv_lib_ws2_32__head_libws2_32_a = yes; then+ cat >>confdefs.h <<_ACEOF+#define HAVE_LIBWS2_32 1+_ACEOF++ LIBS="-lws2_32 $LIBS"++fi+++{ echo "$as_me:$LINENO: checking for getaddrinfo" >&5+echo $ECHO_N "checking for getaddrinfo... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int testme(){ getaddrinfo; }+int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then++cat >>confdefs.h <<\_ACEOF+#define HAVE_GETADDRINFO 1+_ACEOF+ ac_have_getaddrinfo=yes; { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext++if test "x$ac_have_getaddrinfo" = x; then+ old_CFLAGS="$CFLAGS"+ if test "z$ac_cv_lib_ws2_32__head_libws2_32_a" = zyes; then+ CFLAGS="-DWINVER=0x0501 $CFLAGS"+ { echo "$as_me:$LINENO: checking for getaddrinfo if WINVER is 0x0501" >&5+echo $ECHO_N "checking for getaddrinfo if WINVER is 0x0501... $ECHO_C" >&6; }+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+ int testme(){ getaddrinfo; }+int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then++cat >>confdefs.h <<\_ACEOF+#define HAVE_GETADDRINFO 1+_ACEOF++cat >>confdefs.h <<\_ACEOF+#define NEED_WINVER_XP 1+_ACEOF+ EXTRA_CPPFLAGS="-DWINVER=0x0501 $EXTRA_CPPFLAGS"; { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ CFLAGS="$old_CFLAGS"; { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ fi+fi+++for ac_func in gai_strerror+do+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`+{ echo "$as_me:$LINENO: checking for $ac_func" >&5+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.+ For example, HP-UX 11i <limits.h> declares gettimeofday. */+#define $ac_func innocuous_$ac_func++/* System header to define __stub macros and hopefully few prototypes,+ which can conflict with char $ac_func (); below.+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ <limits.h> exists even on freestanding compilers. */++#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif++#undef $ac_func++/* Override any GCC internal prototype to avoid an error.+ Use char because int might match the return type of a GCC+ builtin and then its argument prototype would still apply. */+#ifdef __cplusplus+extern "C"+#endif+char $ac_func ();+/* The GNU C library defines this for functions which it implements+ to always fail with ENOSYS. Some functions are actually named+ something starting with __ and the normal name is an alias. */+#if defined __stub_$ac_func || defined __stub___$ac_func+choke me+#endif++int+main ()+{+return $ac_func ();+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest$ac_exeext &&+ $as_test_x conftest$ac_exeext; then+ eval "$as_ac_var=yes"+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ eval "$as_ac_var=no"+fi++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \+ conftest$ac_exeext conftest.$ac_ext+fi+ac_res=`eval echo '${'$as_ac_var'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+if test `eval echo '${'$as_ac_var'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1+_ACEOF++fi+done+++{ echo "$as_me:$LINENO: checking whether AI_ADDRCONFIG is declared" >&5+echo $ECHO_N "checking whether AI_ADDRCONFIG is declared... $ECHO_C" >&6; }+if test "${ac_cv_have_decl_AI_ADDRCONFIG+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int+main ()+{+#ifndef AI_ADDRCONFIG+ (void) AI_ADDRCONFIG;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_have_decl_AI_ADDRCONFIG=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_have_decl_AI_ADDRCONFIG=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_have_decl_AI_ADDRCONFIG" >&5+echo "${ECHO_T}$ac_cv_have_decl_AI_ADDRCONFIG" >&6; }+if test $ac_cv_have_decl_AI_ADDRCONFIG = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_ADDRCONFIG 1+_ACEOF+++else+ cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_ADDRCONFIG 0+_ACEOF+++fi+{ echo "$as_me:$LINENO: checking whether AI_ALL is declared" >&5+echo $ECHO_N "checking whether AI_ALL is declared... $ECHO_C" >&6; }+if test "${ac_cv_have_decl_AI_ALL+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int+main ()+{+#ifndef AI_ALL+ (void) AI_ALL;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_have_decl_AI_ALL=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_have_decl_AI_ALL=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_have_decl_AI_ALL" >&5+echo "${ECHO_T}$ac_cv_have_decl_AI_ALL" >&6; }+if test $ac_cv_have_decl_AI_ALL = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_ALL 1+_ACEOF+++else+ cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_ALL 0+_ACEOF+++fi+{ echo "$as_me:$LINENO: checking whether AI_NUMERICSERV is declared" >&5+echo $ECHO_N "checking whether AI_NUMERICSERV is declared... $ECHO_C" >&6; }+if test "${ac_cv_have_decl_AI_NUMERICSERV+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int+main ()+{+#ifndef AI_NUMERICSERV+ (void) AI_NUMERICSERV;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_have_decl_AI_NUMERICSERV=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_have_decl_AI_NUMERICSERV=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_have_decl_AI_NUMERICSERV" >&5+echo "${ECHO_T}$ac_cv_have_decl_AI_NUMERICSERV" >&6; }+if test $ac_cv_have_decl_AI_NUMERICSERV = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_NUMERICSERV 1+_ACEOF+++else+ cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_NUMERICSERV 0+_ACEOF+++fi+{ echo "$as_me:$LINENO: checking whether AI_V4MAPPED is declared" >&5+echo $ECHO_N "checking whether AI_V4MAPPED is declared... $ECHO_C" >&6; }+if test "${ac_cv_have_decl_AI_V4MAPPED+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int+main ()+{+#ifndef AI_V4MAPPED+ (void) AI_V4MAPPED;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_have_decl_AI_V4MAPPED=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_have_decl_AI_V4MAPPED=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_have_decl_AI_V4MAPPED" >&5+echo "${ECHO_T}$ac_cv_have_decl_AI_V4MAPPED" >&6; }+if test $ac_cv_have_decl_AI_V4MAPPED = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_V4MAPPED 1+_ACEOF+++else+ cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_AI_V4MAPPED 0+_ACEOF+++fi++++{ echo "$as_me:$LINENO: checking whether IPV6_V6ONLY is declared" >&5+echo $ECHO_N "checking whether IPV6_V6ONLY is declared... $ECHO_C" >&6; }+if test "${ac_cv_have_decl_IPV6_V6ONLY+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+int+main ()+{+#ifndef IPV6_V6ONLY+ (void) IPV6_V6ONLY;+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_have_decl_IPV6_V6ONLY=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_have_decl_IPV6_V6ONLY=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_have_decl_IPV6_V6ONLY" >&5+echo "${ECHO_T}$ac_cv_have_decl_IPV6_V6ONLY" >&6; }+if test $ac_cv_have_decl_IPV6_V6ONLY = yes; then++cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_IPV6_V6ONLY 1+_ACEOF+++else+ cat >>confdefs.h <<_ACEOF+#define HAVE_DECL_IPV6_V6ONLY 0+_ACEOF+++fi++++{ echo "$as_me:$LINENO: checking for sendfile in sys/sendfile.h" >&5+echo $ECHO_N "checking for sendfile in sys/sendfile.h... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <sys/sendfile.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "sendfile" >/dev/null 2>&1; then++cat >>confdefs.h <<\_ACEOF+#define HAVE_LINUX_SENDFILE 1+_ACEOF+ { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+rm -f -r conftest*+++{ echo "$as_me:$LINENO: checking for sendfile in sys/socket.h" >&5+echo $ECHO_N "checking for sendfile in sys/socket.h... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <sys/socket.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "sendfile" >/dev/null 2>&1; then++cat >>confdefs.h <<\_ACEOF+#define HAVE_BSD_SENDFILE 1+_ACEOF+ { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+rm -f -r conftest*++++for ac_func in gethostent+do+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`+{ echo "$as_me:$LINENO: checking for $ac_func" >&5+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; }+if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.+ For example, HP-UX 11i <limits.h> declares gettimeofday. */+#define $ac_func innocuous_$ac_func++/* System header to define __stub macros and hopefully few prototypes,+ which can conflict with char $ac_func (); below.+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ <limits.h> exists even on freestanding compilers. */++#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif++#undef $ac_func++/* Override any GCC internal prototype to avoid an error.+ Use char because int might match the return type of a GCC+ builtin and then its argument prototype would still apply. */+#ifdef __cplusplus+extern "C"+#endif+char $ac_func ();+/* The GNU C library defines this for functions which it implements+ to always fail with ENOSYS. Some functions are actually named+ something starting with __ and the normal name is an alias. */+#if defined __stub_$ac_func || defined __stub___$ac_func+choke me+#endif++int+main ()+{+return $ac_func ();+ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_c_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest$ac_exeext &&+ $as_test_x conftest$ac_exeext; then+ eval "$as_ac_var=yes"+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ eval "$as_ac_var=no"+fi++rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \+ conftest$ac_exeext conftest.$ac_ext+fi+ac_res=`eval echo '${'$as_ac_var'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+if test `eval echo '${'$as_ac_var'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1+_ACEOF++fi+done+++case "$host" in+*-mingw32)+ EXTRA_SRCS="cbits/initWinSock.c, cbits/winSockErr.c, cbits/asyncAccept.c"+ EXTRA_LIBS=ws2_32+ CALLCONV=stdcall ;;+*-solaris2*)+ EXTRA_SRCS="cbits/ancilData.c"+ EXTRA_LIBS="nsl, socket"+ CALLCONV=ccall ;;+*)+ EXTRA_SRCS="cbits/ancilData.c"+ EXTRA_LIBS=+ CALLCONV=ccall ;;+esac++++++ac_config_files="$ac_config_files network.buildinfo"+++cat >confcache <<\_ACEOF+# This file is a shell script that caches the results of configure+# tests run on this system so they can be shared between configure+# scripts and configure runs, see configure's option --config-cache.+# It is not useful on other systems. If it contains results you don't+# want to keep, you may remove or edit it.+#+# config.status only pays attention to the cache file if you give it+# the --recheck option to rerun configure.+#+# `ac_cv_env_foo' variables (set or unset) will be overridden when+# loading this file, other *unset* `ac_cv_foo' will be assigned the+# following values.++_ACEOF++# The following way of writing the cache mishandles newlines in values,+# but we know of no workaround that is simple, portable, and efficient.+# So, we kill variables containing newlines.+# Ultrix sh set writes to stderr and can't be redirected directly,+# and sets the high bit in the cache file unless we assign to the vars.+(+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do+ eval ac_val=\$$ac_var+ case $ac_val in #(+ *${as_nl}*)+ case $ac_var in #(+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;+ esac+ case $ac_var in #(+ _ | IFS | as_nl) ;; #(+ *) $as_unset $ac_var ;;+ esac ;;+ esac+ done++ (set) 2>&1 |+ case $as_nl`(ac_space=' '; set) 2>&1` in #(+ *${as_nl}ac_space=\ *)+ # `set' does not quote correctly, so add quotes (double-quote+ # substitution turns \\\\ into \\, and sed turns \\ into \).+ sed -n \+ "s/'/'\\\\''/g;+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"+ ;; #(+ *)+ # `set' quotes correctly as required by POSIX, so do not add quotes.+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"+ ;;+ esac |+ sort+) |+ sed '+ /^ac_cv_env_/b end+ t clear+ :clear+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/+ t end+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/+ :end' >>confcache+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else+ if test -w "$cache_file"; then+ test "x$cache_file" != "x/dev/null" &&+ { echo "$as_me:$LINENO: updating cache $cache_file" >&5+echo "$as_me: updating cache $cache_file" >&6;}+ cat confcache >$cache_file+ else+ { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5+echo "$as_me: not updating unwritable cache $cache_file" >&6;}+ fi+fi+rm -f confcache++test "x$prefix" = xNONE && prefix=$ac_default_prefix+# Let make expand exec_prefix.+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'++DEFS=-DHAVE_CONFIG_H++ac_libobjs=+ac_ltlibobjs=+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue+ # 1. Remove the extension, and $U if already installed.+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'+ ac_i=`echo "$ac_i" | sed "$ac_script"`+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR+ # will be set to the directory where LIBOBJS objects are built.+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'+done+LIBOBJS=$ac_libobjs++LTLIBOBJS=$ac_ltlibobjs++++: ${CONFIG_STATUS=./config.status}+ac_clean_files_save=$ac_clean_files+ac_clean_files="$ac_clean_files $CONFIG_STATUS"+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5+echo "$as_me: creating $CONFIG_STATUS" >&6;}+cat >$CONFIG_STATUS <<_ACEOF+#! $SHELL+# Generated by $as_me.+# Run this file to recreate the current configuration.+# Compiler output produced by configure, useful for debugging+# configure, is in config.log if it exists.++debug=false+ac_cs_recheck=false+ac_cs_silent=false+SHELL=\${CONFIG_SHELL-$SHELL}+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+## --------------------- ##+## M4sh Initialization. ##+## --------------------- ##++# Be more Bourne compatible+DUALCASE=1; export DUALCASE # for MKS sh+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++++# PATH needs CR+# Avoid depending upon Character Ranges.+as_cr_letters='abcdefghijklmnopqrstuvwxyz'+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'+as_cr_Letters=$as_cr_letters$as_cr_LETTERS+as_cr_digits='0123456789'+as_cr_alnum=$as_cr_Letters$as_cr_digits++# The user is always right.+if test "${PATH_SEPARATOR+set}" != set; then+ echo "#! /bin/sh" >conf$$.sh+ echo "exit 0" >>conf$$.sh+ chmod +x conf$$.sh+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then+ PATH_SEPARATOR=';'+ else+ PATH_SEPARATOR=:+ fi+ rm -f conf$$.sh+fi++# Support unset when possible.+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then+ as_unset=unset+else+ as_unset=false+fi+++# IFS+# We need space, tab and new line, in precisely that order. Quoting is+# there to prevent editors from complaining about space-tab.+# (If _AS_PATH_WALK were called with IFS unset, it would disable word+# splitting by setting IFS to empty value.)+as_nl='+'+IFS=" "" $as_nl"++# Find who we are. Look in the path if we contain no directory separator.+case $0 in+ *[\\/]* ) as_myself=$0 ;;+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break+done+IFS=$as_save_IFS++ ;;+esac+# We did not find ourselves, most probably we were run as `sh COMMAND'+# in which case we are not to be found in the path.+if test "x$as_myself" = x; then+ as_myself=$0+fi+if test ! -f "$as_myself"; then+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2+ { (exit 1); exit 1; }+fi++# Work around bugs in pre-3.0 UWIN ksh.+for as_var in ENV MAIL MAILPATH+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+done+PS1='$ '+PS2='> '+PS4='+ '++# NLS nuisances.+for as_var in \+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \+ LC_TELEPHONE LC_TIME+do+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then+ eval $as_var=C; export $as_var+ else+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ fi+done++# Required to use basename.+if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then+ as_basename=basename+else+ as_basename=false+fi+++# Name of the executable.+as_me=`$as_basename -- "$0" ||+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X/"$0" |+ sed '/^.*\/\([^/][^/]*\)\/*$/{+ s//\1/+ q+ }+ /^X\/\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\/\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`++# CDPATH.+$as_unset CDPATH++++ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {++ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO+ # uniformly replaced by the line number. The first 'sed' inserts a+ # line-number line after each line using $LINENO; the second 'sed'+ # does the real work. The second script uses 'N' to pair each+ # line-number line with the line containing $LINENO, and appends+ # trailing '-' during substitution so that $LINENO is not a special+ # case at line end.+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the+ # scripts with optimization help from Paolo Bonzini. Blame Lee+ # E. McMahon (1931-1989) for sed's syntax. :-)+ sed -n '+ p+ /[$]LINENO/=+ ' <$as_myself |+ sed '+ s/[$]LINENO.*/&-/+ t lineno+ b+ :lineno+ N+ :loop+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/+ t loop+ s/-\n.*//+ ' >$as_me.lineno &&+ chmod +x "$as_me.lineno" ||+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2+ { (exit 1); exit 1; }; }++ # Don't try to exec as it changes $[0], causing all sort of problems+ # (the dirname of $[0] is not the place where we might find the+ # original and so on. Autoconf is especially sensitive to this).+ . "./$as_me.lineno"+ # Exit status is that of the last command.+ exit+}+++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then+ as_dirname=dirname+else+ as_dirname=false+fi++ECHO_C= ECHO_N= ECHO_T=+case `echo -n x` in+-n*)+ case `echo 'x\c'` in+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.+ *) ECHO_C='\c';;+ esac;;+*)+ ECHO_N='-n';;+esac++if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++rm -f conf$$ conf$$.exe conf$$.file+if test -d conf$$.dir; then+ rm -f conf$$.dir/conf$$.file+else+ rm -f conf$$.dir+ mkdir conf$$.dir+fi+echo >conf$$.file+if ln -s conf$$.file conf$$ 2>/dev/null; then+ as_ln_s='ln -s'+ # ... but there are two gotchas:+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.+ # In both cases, we have to default to `cp -p'.+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||+ as_ln_s='cp -p'+elif ln conf$$.file conf$$ 2>/dev/null; then+ as_ln_s=ln+else+ as_ln_s='cp -p'+fi+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file+rmdir conf$$.dir 2>/dev/null++if mkdir -p . 2>/dev/null; then+ as_mkdir_p=:+else+ test -d ./-p && rmdir ./-p+ as_mkdir_p=false+fi++if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x++# Sed expression to map a string onto a valid CPP name.+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"++# Sed expression to map a string onto a valid variable name.+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"+++exec 6>&1++# Save the log message, to keep $[0] and so on meaningful, and to+# report actual input values of CONFIG_FILES etc. instead of their+# values after options handling.+ac_log="+This file was extended by Haskell network package $as_me 2.3.0.11, which was+generated by GNU Autoconf 2.61. Invocation command line was++ CONFIG_FILES = $CONFIG_FILES+ CONFIG_HEADERS = $CONFIG_HEADERS+ CONFIG_LINKS = $CONFIG_LINKS+ CONFIG_COMMANDS = $CONFIG_COMMANDS+ $ $0 $@++on `(hostname || uname -n) 2>/dev/null | sed 1q`+"++_ACEOF++cat >>$CONFIG_STATUS <<_ACEOF+# Files that config.status was made for.+config_files="$ac_config_files"+config_headers="$ac_config_headers"++_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+ac_cs_usage="\+\`$as_me' instantiates files from templates according to the+current configuration.++Usage: $0 [OPTIONS] [FILE]...++ -h, --help print this help, then exit+ -V, --version print version number and configuration settings, then exit+ -q, --quiet do not print progress messages+ -d, --debug don't remove temporary files+ --recheck update $as_me by reconfiguring in the same conditions+ --file=FILE[:TEMPLATE]+ instantiate the configuration file FILE+ --header=FILE[:TEMPLATE]+ instantiate the configuration header FILE++Configuration files:+$config_files++Configuration headers:+$config_headers++Report bugs to <bug-autoconf@gnu.org>."++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+ac_cs_version="\\+Haskell network package config.status 2.3.0.11+configured by $0, generated by GNU Autoconf 2.61,+ with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"++Copyright (C) 2006 Free Software Foundation, Inc.+This config.status script is free software; the Free Software Foundation+gives unlimited permission to copy, distribute and modify it."++ac_pwd='$ac_pwd'+srcdir='$srcdir'+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+# If no file are specified by the user, then we need to provide default+# value. By we need to know if files were specified by the user.+ac_need_defaults=:+while test $# != 0+do+ case $1 in+ --*=*)+ ac_option=`expr "X$1" : 'X\([^=]*\)='`+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`+ ac_shift=:+ ;;+ *)+ ac_option=$1+ ac_optarg=$2+ ac_shift=shift+ ;;+ esac++ case $ac_option in+ # Handling of the options.+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)+ ac_cs_recheck=: ;;+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )+ echo "$ac_cs_version"; exit ;;+ --debug | --debu | --deb | --de | --d | -d )+ debug=: ;;+ --file | --fil | --fi | --f )+ $ac_shift+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"+ ac_need_defaults=false;;+ --header | --heade | --head | --hea )+ $ac_shift+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"+ ac_need_defaults=false;;+ --he | --h)+ # Conflict between --help and --header+ { echo "$as_me: error: ambiguous option: $1+Try \`$0 --help' for more information." >&2+ { (exit 1); exit 1; }; };;+ --help | --hel | -h )+ echo "$ac_cs_usage"; exit ;;+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil | --si | --s)+ ac_cs_silent=: ;;++ # This is an error.+ -*) { echo "$as_me: error: unrecognized option: $1+Try \`$0 --help' for more information." >&2+ { (exit 1); exit 1; }; } ;;++ *) ac_config_targets="$ac_config_targets $1"+ ac_need_defaults=false ;;++ esac+ shift+done++ac_configure_extra_args=++if $ac_cs_silent; then+ exec 6>/dev/null+ ac_configure_extra_args="$ac_configure_extra_args --silent"+fi++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+if \$ac_cs_recheck; then+ echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6+ CONFIG_SHELL=$SHELL+ export CONFIG_SHELL+ exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion+fi++_ACEOF+cat >>$CONFIG_STATUS <<\_ACEOF+exec 5>>config.log+{+ echo+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX+## Running $as_me. ##+_ASBOX+ echo "$ac_log"+} >&5++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF++# Handling of arguments.+for ac_config_target in $ac_config_targets+do+ case $ac_config_target in+ "include/HsNetworkConfig.h") CONFIG_HEADERS="$CONFIG_HEADERS include/HsNetworkConfig.h" ;;+ "network.buildinfo") CONFIG_FILES="$CONFIG_FILES network.buildinfo" ;;++ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}+ { (exit 1); exit 1; }; };;+ esac+done+++# If the user did not use the arguments to specify the items to instantiate,+# then the envvar interface is used. Set only those that are not.+# We use the long form for the default assignment because of an extremely+# bizarre bug on SunOS 4.1.3.+if $ac_need_defaults; then+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers+fi++# Have a temporary directory for convenience. Make it in the build tree+# simply because there is no reason against having it here, and in addition,+# creating and moving files from /tmp can sometimes cause problems.+# Hook for its removal unless debugging.+# Note that there is a small window in which the directory will not be cleaned:+# after its creation but before its name has been assigned to `$tmp'.+$debug ||+{+ tmp=+ trap 'exit_status=$?+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status+' 0+ trap '{ (exit 1); exit 1; }' 1 2 13 15+}+# Create a (secure) tmp directory for tmp files.++{+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&+ test -n "$tmp" && test -d "$tmp"+} ||+{+ tmp=./conf$$-$RANDOM+ (umask 077 && mkdir "$tmp")+} ||+{+ echo "$me: cannot create a temporary directory in ." >&2+ { (exit 1); exit 1; }+}++#+# Set up the sed scripts for CONFIG_FILES section.+#++# No need to generate the scripts if there are no CONFIG_FILES.+# This happens for instance when ./config.status config.h+if test -n "$CONFIG_FILES"; then++_ACEOF++++ac_delim='%!_!# '+for ac_last_try in false false false false false :; do+ cat >conf$$subs.sed <<_ACEOF+SHELL!$SHELL$ac_delim+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim+PACKAGE_NAME!$PACKAGE_NAME$ac_delim+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim+PACKAGE_STRING!$PACKAGE_STRING$ac_delim+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim+exec_prefix!$exec_prefix$ac_delim+prefix!$prefix$ac_delim+program_transform_name!$program_transform_name$ac_delim+bindir!$bindir$ac_delim+sbindir!$sbindir$ac_delim+libexecdir!$libexecdir$ac_delim+datarootdir!$datarootdir$ac_delim+datadir!$datadir$ac_delim+sysconfdir!$sysconfdir$ac_delim+sharedstatedir!$sharedstatedir$ac_delim+localstatedir!$localstatedir$ac_delim+includedir!$includedir$ac_delim+oldincludedir!$oldincludedir$ac_delim+docdir!$docdir$ac_delim+infodir!$infodir$ac_delim+htmldir!$htmldir$ac_delim+dvidir!$dvidir$ac_delim+pdfdir!$pdfdir$ac_delim+psdir!$psdir$ac_delim+libdir!$libdir$ac_delim+localedir!$localedir$ac_delim+mandir!$mandir$ac_delim+DEFS!$DEFS$ac_delim+ECHO_C!$ECHO_C$ac_delim+ECHO_N!$ECHO_N$ac_delim+ECHO_T!$ECHO_T$ac_delim+LIBS!$LIBS$ac_delim+build_alias!$build_alias$ac_delim+host_alias!$host_alias$ac_delim+target_alias!$target_alias$ac_delim+build!$build$ac_delim+build_cpu!$build_cpu$ac_delim+build_vendor!$build_vendor$ac_delim+build_os!$build_os$ac_delim+host!$host$ac_delim+host_cpu!$host_cpu$ac_delim+host_vendor!$host_vendor$ac_delim+host_os!$host_os$ac_delim+CC!$CC$ac_delim+CFLAGS!$CFLAGS$ac_delim+LDFLAGS!$LDFLAGS$ac_delim+CPPFLAGS!$CPPFLAGS$ac_delim+ac_ct_CC!$ac_ct_CC$ac_delim+EXEEXT!$EXEEXT$ac_delim+OBJEXT!$OBJEXT$ac_delim+CPP!$CPP$ac_delim+GREP!$GREP$ac_delim+EGREP!$EGREP$ac_delim+CALLCONV!$CALLCONV$ac_delim+EXTRA_CPPFLAGS!$EXTRA_CPPFLAGS$ac_delim+EXTRA_LIBS!$EXTRA_LIBS$ac_delim+EXTRA_SRCS!$EXTRA_SRCS$ac_delim+LIBOBJS!$LIBOBJS$ac_delim+LTLIBOBJS!$LTLIBOBJS$ac_delim+_ACEOF++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 61; then+ break+ elif $ac_last_try; then+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}+ { (exit 1); exit 1; }; }+ else+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "+ fi+done++ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`+if test -n "$ac_eof"; then+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`+ ac_eof=`expr $ac_eof + 1`+fi++cat >>$CONFIG_STATUS <<_ACEOF+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end+_ACEOF+sed '+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g+s/^/s,@/; s/!/@,|#_!!_#|/+:n+t n+s/'"$ac_delim"'$/,g/; t+s/$/\\/; p+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n+' >>$CONFIG_STATUS <conf$$subs.sed+rm -f conf$$subs.sed+cat >>$CONFIG_STATUS <<_ACEOF+:end+s/|#_!!_#|//g+CEOF$ac_eof+_ACEOF+++# VPATH may cause trouble with some makes, so we remove $(srcdir),+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and+# trailing colons and then remove the whole line if VPATH becomes empty+# (actually we leave an empty line to preserve line numbers).+if test "x$srcdir" = x.; then+ ac_vpsub='/^[ ]*VPATH[ ]*=/{+s/:*\$(srcdir):*/:/+s/:*\${srcdir}:*/:/+s/:*@srcdir@:*/:/+s/^\([^=]*=[ ]*\):*/\1/+s/:*$//+s/^[^=]*=[ ]*$//+}'+fi++cat >>$CONFIG_STATUS <<\_ACEOF+fi # test -n "$CONFIG_FILES"+++for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS+do+ case $ac_tag in+ :[FHLC]) ac_mode=$ac_tag; continue;;+ esac+ case $ac_mode$ac_tag in+ :[FHL]*:*);;+ :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5+echo "$as_me: error: Invalid tag $ac_tag." >&2;}+ { (exit 1); exit 1; }; };;+ :[FH]-) ac_tag=-:-;;+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;+ esac+ ac_save_IFS=$IFS+ IFS=:+ set x $ac_tag+ IFS=$ac_save_IFS+ shift+ ac_file=$1+ shift++ case $ac_mode in+ :L) ac_source=$1;;+ :[FH])+ ac_file_inputs=+ for ac_f+ do+ case $ac_f in+ -) ac_f="$tmp/stdin";;+ *) # Look for the file first in the build tree, then in the source tree+ # (if the path is not absolute). The absolute path cannot be DOS-style,+ # because $ac_f cannot contain `:'.+ test -f "$ac_f" ||+ case $ac_f in+ [\\/$]*) false;;+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;+ esac ||+ { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5+echo "$as_me: error: cannot find input file: $ac_f" >&2;}+ { (exit 1); exit 1; }; };;+ esac+ ac_file_inputs="$ac_file_inputs $ac_f"+ done++ # Let's still pretend it is `configure' which instantiates (i.e., don't+ # use $as_me), people would be surprised to read:+ # /* config.h. Generated by config.status. */+ configure_input="Generated from "`IFS=:+ echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."+ if test x"$ac_file" != x-; then+ configure_input="$ac_file. $configure_input"+ { echo "$as_me:$LINENO: creating $ac_file" >&5+echo "$as_me: creating $ac_file" >&6;}+ fi++ case $ac_tag in+ *:-:* | *:-) cat >"$tmp/stdin";;+ esac+ ;;+ esac++ ac_dir=`$as_dirname -- "$ac_file" ||+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$ac_file" : 'X\(//\)[^/]' \| \+ X"$ac_file" : 'X\(//\)$' \| \+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$ac_file" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ { as_dir="$ac_dir"+ case $as_dir in #(+ -*) as_dir=./$as_dir;;+ esac+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {+ as_dirs=+ while :; do+ case $as_dir in #(+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(+ *) as_qdir=$as_dir;;+ esac+ as_dirs="'$as_qdir' $as_dirs"+ as_dir=`$as_dirname -- "$as_dir" ||+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$as_dir" : 'X\(//\)[^/]' \| \+ X"$as_dir" : 'X\(//\)$' \| \+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$as_dir" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ test -d "$as_dir" && break+ done+ test -z "$as_dirs" || eval "mkdir $as_dirs"+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5+echo "$as_me: error: cannot create directory $as_dir" >&2;}+ { (exit 1); exit 1; }; }; }+ ac_builddir=.++case "$ac_dir" in+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;+*)+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`+ # A ".." for each directory in $ac_dir_suffix.+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`+ case $ac_top_builddir_sub in+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;+ esac ;;+esac+ac_abs_top_builddir=$ac_pwd+ac_abs_builddir=$ac_pwd$ac_dir_suffix+# for backward compatibility:+ac_top_builddir=$ac_top_build_prefix++case $srcdir in+ .) # We are building in place.+ ac_srcdir=.+ ac_top_srcdir=$ac_top_builddir_sub+ ac_abs_top_srcdir=$ac_pwd ;;+ [\\/]* | ?:[\\/]* ) # Absolute name.+ ac_srcdir=$srcdir$ac_dir_suffix;+ ac_top_srcdir=$srcdir+ ac_abs_top_srcdir=$srcdir ;;+ *) # Relative name.+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix+ ac_top_srcdir=$ac_top_build_prefix$srcdir+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;+esac+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix+++ case $ac_mode in+ :F)+ #+ # CONFIG_FILE+ #++_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+# If the template does not know about datarootdir, expand it.+# FIXME: This hack should be removed a few years after 2.60.+ac_datarootdir_hack=; ac_datarootdir_seen=++case `sed -n '/datarootdir/ {+ p+ q+}+/@datadir@/p+/@docdir@/p+/@infodir@/p+/@localedir@/p+/@mandir@/p+' $ac_file_inputs` in+*datarootdir*) ac_datarootdir_seen=yes;;+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)+ { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}+_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+ ac_datarootdir_hack='+ s&@datadir@&$datadir&g+ s&@docdir@&$docdir&g+ s&@infodir@&$infodir&g+ s&@localedir@&$localedir&g+ s&@mandir@&$mandir&g+ s&\\\${datarootdir}&$datarootdir&g' ;;+esac+_ACEOF++# Neutralize VPATH when `$srcdir' = `.'.+# Shell code in configure.ac might set extrasub.+# FIXME: do we really want to maintain this feature?+cat >>$CONFIG_STATUS <<_ACEOF+ sed "$ac_vpsub+$extrasub+_ACEOF+cat >>$CONFIG_STATUS <<\_ACEOF+:t+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b+s&@configure_input@&$configure_input&;t t+s&@top_builddir@&$ac_top_builddir_sub&;t t+s&@srcdir@&$ac_srcdir&;t t+s&@abs_srcdir@&$ac_abs_srcdir&;t t+s&@top_srcdir@&$ac_top_srcdir&;t t+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t+s&@builddir@&$ac_builddir&;t t+s&@abs_builddir@&$ac_abs_builddir&;t t+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t+$ac_datarootdir_hack+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out++test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&+ { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'+which seems to be undefined. Please make sure it is defined." >&5+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'+which seems to be undefined. Please make sure it is defined." >&2;}++ rm -f "$tmp/stdin"+ case $ac_file in+ -) cat "$tmp/out"; rm -f "$tmp/out";;+ *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;+ esac+ ;;+ :H)+ #+ # CONFIG_HEADER+ #+_ACEOF++# Transform confdefs.h into a sed script `conftest.defines', that+# substitutes the proper values into config.h.in to produce config.h.+rm -f conftest.defines conftest.tail+# First, append a space to every undef/define line, to ease matching.+echo 's/$/ /' >conftest.defines+# Then, protect against being on the right side of a sed subst, or in+# an unquoted here document, in config.status. If some macros were+# called several times there might be several #defines for the same+# symbol, which is useless. But do not sort them, since the last+# AC_DEFINE must be honored.+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*+# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where+# NAME is the cpp macro being defined, VALUE is the value it is being given.+# PARAMS is the parameter list in the macro definition--in most cases, it's+# just an empty string.+ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*'+ac_dB='\\)[ (].*,\\1define\\2'+ac_dC=' '+ac_dD=' ,'++uniq confdefs.h |+ sed -n '+ t rset+ :rset+ s/^[ ]*#[ ]*define[ ][ ]*//+ t ok+ d+ :ok+ s/[\\&,]/\\&/g+ s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p+ s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p+ ' >>conftest.defines++# Remove the space that was appended to ease matching.+# Then replace #undef with comments. This is necessary, for+# example, in the case of _POSIX_SOURCE, which is predefined and required+# on some systems where configure will not decide to define it.+# (The regexp can be short, since the line contains either #define or #undef.)+echo 's/ $//+s,^[ #]*u.*,/* & */,' >>conftest.defines++# Break up conftest.defines:+ac_max_sed_lines=50++# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1"+# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2"+# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1"+# et cetera.+ac_in='$ac_file_inputs'+ac_out='"$tmp/out1"'+ac_nxt='"$tmp/out2"'++while :+do+ # Write a here document:+ cat >>$CONFIG_STATUS <<_ACEOF+ # First, check the format of the line:+ cat >"\$tmp/defines.sed" <<\\CEOF+/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*/b def+/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def+b+:def+_ACEOF+ sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS+ echo 'CEOF+ sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS+ ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in+ sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail+ grep . conftest.tail >/dev/null || break+ rm -f conftest.defines+ mv conftest.tail conftest.defines+done+rm -f conftest.defines conftest.tail++echo "ac_result=$ac_in" >>$CONFIG_STATUS+cat >>$CONFIG_STATUS <<\_ACEOF+ if test x"$ac_file" != x-; then+ echo "/* $configure_input */" >"$tmp/config.h"+ cat "$ac_result" >>"$tmp/config.h"+ if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then+ { echo "$as_me:$LINENO: $ac_file is unchanged" >&5+echo "$as_me: $ac_file is unchanged" >&6;}+ else+ rm -f $ac_file+ mv "$tmp/config.h" $ac_file+ fi+ else+ echo "/* $configure_input */"+ cat "$ac_result"+ fi+ rm -f "$tmp/out12"+ ;;+++ esac++done # for ac_tag+++{ (exit 0); exit 0; }+_ACEOF+chmod +x $CONFIG_STATUS+ac_clean_files=$ac_clean_files_save+++# configure is writing to config.log, and then calls config.status.+# config.status does its own redirection, appending to config.log.+# Unfortunately, on DOS this fails, as config.log is still kept open+# by configure, so config.status won't be able to write to it; its+# output is simply discarded. So we exec the FD to /dev/null,+# effectively closing config.log, so it can be properly (re)opened and+# appended to by config.status. When coming back to configure, we+# need to make the FD available again.+if test "$no_create" != yes; then+ ac_cs_success=:+ ac_config_status_args=+ test "$silent" = yes &&+ ac_config_status_args="$ac_config_status_args --quiet"+ exec 5>/dev/null+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false+ exec 5>>config.log+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which+ # would make configure fail if this is the last instruction.+ $ac_cs_success || { (exit 1); exit 1; }+fi+
+ configure.ac view
@@ -0,0 +1,176 @@+AC_INIT([Haskell network package], [2.3.0.11], [libraries@haskell.org], [network])++ac_includes_default="$ac_includes_default+#ifdef HAVE_NETDB_H+# include <netdb.h>+#endif+#ifdef HAVE_WINSOCK2_H+# include <winsock2.h>+#endif+#ifdef HAVE_WS2TCPIP_H+# include <ws2tcpip.h>+// fix for MingW not defining IPV6_V6ONLY+# define IPV6_V6ONLY 27+#endif+#ifdef HAVE_WSPIAPI_H+# include <wspiapi.h>+#endif"++# Safety check: Ensure that we are in the correct source directory.+AC_CONFIG_SRCDIR([include/HsNet.h])++AC_CONFIG_HEADERS([include/HsNetworkConfig.h])++AC_CANONICAL_HOST++AC_ARG_WITH([cc],+ [C compiler],+ [CC=$withval])+AC_PROG_CC()++AC_C_CONST++dnl ** check for specific header (.h) files that we are interested in+AC_CHECK_HEADERS([fcntl.h limits.h stdlib.h sys/types.h unistd.h winsock2.h ws2tcpip.h wspiapi.h])+AC_CHECK_HEADERS([arpa/inet.h netdb.h netinet/in.h netinet/tcp.h sys/socket.h sys/uio.h sys/un.h])++AC_CHECK_FUNCS([readlink symlink])++dnl ** check what fields struct msghdr contains+AC_CHECK_MEMBERS([struct msghdr.msg_control, struct msghdr.msg_accrights], [], [], [#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#if HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif])++dnl ** check if struct sockaddr contains sa_len+AC_CHECK_MEMBERS([struct sockaddr.sa_len], [], [], [#if HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#if HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif])++dnl --------------------------------------------------+dnl * test for in_addr_t+dnl --------------------------------------------------+AC_MSG_CHECKING(for in_addr_t in netinet/in.h)+AC_EGREP_HEADER(in_addr_t, netinet/in.h,+ [ AC_DEFINE([HAVE_IN_ADDR_T], [1], [Define to 1 if in_addr_t is available.]) AC_MSG_RESULT(yes) ],+ AC_MSG_RESULT(no))++dnl --------------------------------------------------+dnl * test for SO_PEERCRED and struct ucred+dnl --------------------------------------------------+AC_MSG_CHECKING(for SO_PEERCRED and struct ucred in sys/socket.h)+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>+#include <sys/socket.h>+#ifndef SO_PEERCRED+# error no SO_PEERCRED+#endif+struct ucred u;]])],ac_cv_ucred=yes,ac_cv_ucred=no)+if test "x$ac_cv_ucred" = xno; then+ old_CFLAGS="$CFLAGS"+ CFLAGS="-D_GNU_SOURCE $CFLAGS"+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>+#include <sys/socket.h>+#ifndef SO_PEERCRED+# error no SO_PEERCRED+#endif+struct ucred u;]])],ac_cv_ucred=yes,ac_cv_ucred=no)+ if test "x$ac_cv_ucred" = xyes; then+ EXTRA_CPPFLAGS=-D_GNU_SOURCE+ fi+else+ old_CFLAGS="$CFLAGS"+fi+if test "x$ac_cv_ucred" = xno; then+ CFLAGS="$old_CFLAGS"+ AC_MSG_RESULT(no)+else+ AC_DEFINE([HAVE_STRUCT_UCRED], [1], [Define to 1 if you have both SO_PEERCRED and struct ucred.])+ AC_MSG_RESULT(yes)+fi++dnl --------------------------------------------------+dnl * check for Windows networking libraries+dnl --------------------------------------------------+AC_CHECK_LIB(ws2_32, _head_libws2_32_a)++dnl --------------------------------------------------+dnl * test for getaddrinfo as proxy for IPv6 support+dnl --------------------------------------------------+AC_MSG_CHECKING(for getaddrinfo)+dnl Can't use AC_CHECK_FUNC here, because it doesn't do the right+dnl thing on Windows.+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$ac_includes_default+int testme(){ getaddrinfo; }]])],[AC_DEFINE([HAVE_GETADDRINFO], [1], [Define to 1 if you have the `getaddrinfo' function.]) ac_have_getaddrinfo=yes; AC_MSG_RESULT(yes)],[AC_MSG_RESULT(no)])++dnl Under mingw, we may need to set WINVER to 0x0501 to expose getaddrinfo.+if test "x$ac_have_getaddrinfo" = x; then+ old_CFLAGS="$CFLAGS"+ if test "z$ac_cv_lib_ws2_32__head_libws2_32_a" = zyes; then+ CFLAGS="-DWINVER=0x0501 $CFLAGS"+ AC_MSG_CHECKING(for getaddrinfo if WINVER is 0x0501)+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$ac_includes_default+ int testme(){ getaddrinfo; }]])],[AC_DEFINE([HAVE_GETADDRINFO], [1], [Define to 1 if you have the `getaddrinfo' function.]) AC_DEFINE([NEED_WINVER_XP], [1], [Define to 1 if the `getaddrinfo' function needs WINVER set.]) EXTRA_CPPFLAGS="-DWINVER=0x0501 $EXTRA_CPPFLAGS"; AC_MSG_RESULT(yes)],[CFLAGS="$old_CFLAGS"; AC_MSG_RESULT(no)])+ fi+fi++dnl Missing under mingw, sigh.+AC_CHECK_FUNCS(gai_strerror)++dnl -------------------------------------------------------+dnl * test for AI_* flags that not all implementations have+dnl -------------------------------------------------------+AC_CHECK_DECLS([AI_ADDRCONFIG, AI_ALL, AI_NUMERICSERV, AI_V4MAPPED])++dnl -------------------------------------------------------+dnl * test for IPV6_V6ONLY flags that not all implementations have+dnl -------------------------------------------------------+AC_CHECK_DECLS([IPV6_V6ONLY])++dnl --------------------------------------------------+dnl * test for Linux sendfile(2)+dnl --------------------------------------------------+AC_MSG_CHECKING(for sendfile in sys/sendfile.h)+AC_EGREP_HEADER(sendfile, sys/sendfile.h,+ [ AC_DEFINE([HAVE_LINUX_SENDFILE], [1], [Define to 1 if you have a Linux sendfile(2) implementation.]) AC_MSG_RESULT(yes) ],+ AC_MSG_RESULT(no))++dnl --------------------------------------------------+dnl * test for BSD sendfile(2)+dnl --------------------------------------------------+AC_MSG_CHECKING(for sendfile in sys/socket.h)+AC_EGREP_HEADER(sendfile, sys/socket.h,+ [ AC_DEFINE([HAVE_BSD_SENDFILE], [1], [Define to 1 if you have a BSDish sendfile(2) implementation.]) AC_MSG_RESULT(yes) ],+ AC_MSG_RESULT(no))++AC_CHECK_FUNCS(gethostent)++case "$host" in+*-mingw32)+ EXTRA_SRCS="cbits/initWinSock.c, cbits/winSockErr.c, cbits/asyncAccept.c"+ EXTRA_LIBS=ws2_32+ CALLCONV=stdcall ;;+*-solaris2*)+ EXTRA_SRCS="cbits/ancilData.c"+ EXTRA_LIBS="nsl, socket"+ CALLCONV=ccall ;;+*)+ EXTRA_SRCS="cbits/ancilData.c"+ EXTRA_LIBS=+ CALLCONV=ccall ;;+esac+AC_SUBST([CALLCONV])+AC_SUBST([EXTRA_CPPFLAGS])+AC_SUBST([EXTRA_LIBS])+AC_SUBST([EXTRA_SRCS])++AC_CONFIG_FILES([network.buildinfo])++AC_OUTPUT
+ include/HsNet.h view
@@ -0,0 +1,195 @@+/* -----------------------------------------------------------------------------+ *+ * Definitions for package `net' which are visible in Haskell land.+ *+ * ---------------------------------------------------------------------------*/++#ifndef HSNET_H+#define HSNET_H++#include "HsNetworkConfig.h"++#ifdef NEED_WINVER+# define WINVER 0x0501+#endif++/* ultra-evil... */+#undef PACKAGE_BUGREPORT+#undef PACKAGE_NAME+#undef PACKAGE_STRING+#undef PACKAGE_TARNAME+#undef PACKAGE_VERSION++#ifndef INLINE+# if defined(_MSC_VER)+# define INLINE extern __inline+# elif defined(__GNUC__)+# define INLINE extern inline+# else+# define INLINE inline+# endif+#endif++#ifdef HAVE_GETADDRINFO+# define IPV6_SOCKET_SUPPORT 1+#else+# undef IPV6_SOCKET_SUPPORT+#endif++#if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+#include <winsock2.h>+# ifdef HAVE_WS2TCPIP_H+# include <ws2tcpip.h>+// fix for MingW not defining IPV6_V6ONLY+# define IPV6_V6ONLY 27+# endif+# ifdef HAVE_WSPIAPI_H+# include <wspiapi.h>+# endif++extern void shutdownWinSock();+extern int initWinSock ();+extern const char* getWSErrorDescr(int err);++# if !defined(__HUGS__)+extern void* newAcceptParams(int sock,+ int sz,+ void* sockaddr);+extern int acceptNewSock(void* d);+extern int acceptDoProc(void* param);+# endif++#else++#ifdef HAVE_LIMITS_H+# include <limits.h>+#endif+#ifdef HAVE_STDLIB_H+# include <stdlib.h>+#endif+#ifdef HAVE_UNISTD_H+#include <unistd.h>+#endif+#ifdef HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#ifdef HAVE_FCNTL_H+# include <fcntl.h>+#endif+#ifdef HAVE_SYS_UIO_H+# include <sys/uio.h>+#endif+#ifdef HAVE_SYS_SOCKET_H+# include <sys/socket.h>+#endif+#ifdef HAVE_NETINET_TCP_H+# include <netinet/tcp.h>+#endif+#ifdef HAVE_NETINET_IN_H+# include <netinet/in.h>+#endif+#ifdef HAVE_SYS_UN_H+# include <sys/un.h>+#endif+#ifdef HAVE_ARPA_INET_H+# include <arpa/inet.h>+#endif+#ifdef HAVE_NETDB_H+#include <netdb.h>+#endif++#ifdef HAVE_BSD_SENDFILE+#include <sys/uio.h>+#endif+#ifdef HAVE_LINUX_SENDFILE+#if !defined(__USE_FILE_OFFSET64)+#include <sys/sendfile.h>+#endif+#endif++extern int+sendFd(int sock, int outfd);++extern int+recvFd(int sock);++/* The next two are scheduled for deletion */+extern int+sendAncillary(int sock,+ int level,+ int type,+ int flags,+ void* data,+ int len);++extern int+recvAncillary(int sock,+ int* pLevel,+ int* pType,+ int flags,+ void** pData,+ int* pLen);++#endif /* HAVE_WINSOCK2_H && !__CYGWIN */++INLINE char *+my_inet_ntoa(+#if defined(HAVE_WINSOCK2_H)+ u_long addr+#elif defined(HAVE_IN_ADDR_T)+ in_addr_t addr+#elif defined(HAVE_INTTYPES_H)+ u_int32_t addr+#else+ unsigned long addr+#endif+ )+{ + struct in_addr a;+ a.s_addr = addr;+ return inet_ntoa(a);+}++#ifdef HAVE_GETADDRINFO+INLINE int+hsnet_getnameinfo(const struct sockaddr* a,socklen_t b, char* c,+# if defined(HAVE_WINSOCK2_H) && !defined(__CYGWIN__)+ DWORD d, char* e, DWORD f, int g)+# else+ socklen_t d, char* e, socklen_t f, int g)+# endif+{+ return getnameinfo(a,b,c,d,e,f,g);+}++INLINE int+hsnet_getaddrinfo(const char *hostname, const char *servname,+ const struct addrinfo *hints, struct addrinfo **res)+{+ return getaddrinfo(hostname, servname, hints, res);+}++INLINE void+hsnet_freeaddrinfo(struct addrinfo *ai)+{+ freeaddrinfo(ai);+}+#endif++#if defined(HAVE_WINSOCK2_H) && !defined(cygwin32_HOST_OS)+# define WITH_WINSOCK 1+#endif++#if !defined(mingw32_HOST_OS) && !defined(_WIN32)+# define DOMAIN_SOCKET_SUPPORT 1+#endif++#if !defined(CALLCONV)+# if defined(WITH_WINSOCK)+# define CALLCONV stdcall+# else+# define CALLCONV ccall+# endif+#endif++#endif /* HSNET_H */
+ include/HsNetworkConfig.h.in view
@@ -0,0 +1,147 @@+/* include/HsNetworkConfig.h.in. Generated from configure.ac by autoheader. */++/* Define to 1 if you have the <arpa/inet.h> header file. */+#undef HAVE_ARPA_INET_H++/* Define to 1 if you have a BSDish sendfile(2) implementation. */+#undef HAVE_BSD_SENDFILE++/* Define to 1 if you have the declaration of `AI_ADDRCONFIG', and to 0 if you+ don't. */+#undef HAVE_DECL_AI_ADDRCONFIG++/* Define to 1 if you have the declaration of `AI_ALL', and to 0 if you don't.+ */+#undef HAVE_DECL_AI_ALL++/* Define to 1 if you have the declaration of `AI_NUMERICSERV', and to 0 if+ you don't. */+#undef HAVE_DECL_AI_NUMERICSERV++/* Define to 1 if you have the declaration of `AI_V4MAPPED', and to 0 if you+ don't. */+#undef HAVE_DECL_AI_V4MAPPED++/* Define to 1 if you have the declaration of `IPV6_V6ONLY', and to 0 if you+ don't. */+#undef HAVE_DECL_IPV6_V6ONLY++/* Define to 1 if you have the <fcntl.h> header file. */+#undef HAVE_FCNTL_H++/* Define to 1 if you have the `gai_strerror' function. */+#undef HAVE_GAI_STRERROR++/* Define to 1 if you have the `getaddrinfo' function. */+#undef HAVE_GETADDRINFO++/* Define to 1 if you have the `gethostent' function. */+#undef HAVE_GETHOSTENT++/* Define to 1 if you have the <inttypes.h> header file. */+#undef HAVE_INTTYPES_H++/* Define to 1 if in_addr_t is available. */+#undef HAVE_IN_ADDR_T++/* Define to 1 if you have the `ws2_32' library (-lws2_32). */+#undef HAVE_LIBWS2_32++/* Define to 1 if you have the <limits.h> header file. */+#undef HAVE_LIMITS_H++/* Define to 1 if you have a Linux sendfile(2) implementation. */+#undef HAVE_LINUX_SENDFILE++/* Define to 1 if you have the <memory.h> header file. */+#undef HAVE_MEMORY_H++/* Define to 1 if you have the <netdb.h> header file. */+#undef HAVE_NETDB_H++/* Define to 1 if you have the <netinet/in.h> header file. */+#undef HAVE_NETINET_IN_H++/* Define to 1 if you have the <netinet/tcp.h> header file. */+#undef HAVE_NETINET_TCP_H++/* Define to 1 if you have the `readlink' function. */+#undef HAVE_READLINK++/* Define to 1 if you have the <stdint.h> header file. */+#undef HAVE_STDINT_H++/* Define to 1 if you have the <stdlib.h> header file. */+#undef HAVE_STDLIB_H++/* Define to 1 if you have the <strings.h> header file. */+#undef HAVE_STRINGS_H++/* Define to 1 if you have the <string.h> header file. */+#undef HAVE_STRING_H++/* Define to 1 if `msg_accrights' is member of `struct msghdr'. */+#undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS++/* Define to 1 if `msg_control' is member of `struct msghdr'. */+#undef HAVE_STRUCT_MSGHDR_MSG_CONTROL++/* Define to 1 if `sa_len' is member of `struct sockaddr'. */+#undef HAVE_STRUCT_SOCKADDR_SA_LEN++/* Define to 1 if you have both SO_PEERCRED and struct ucred. */+#undef HAVE_STRUCT_UCRED++/* Define to 1 if you have the `symlink' function. */+#undef HAVE_SYMLINK++/* Define to 1 if you have the <sys/socket.h> header file. */+#undef HAVE_SYS_SOCKET_H++/* Define to 1 if you have the <sys/stat.h> header file. */+#undef HAVE_SYS_STAT_H++/* Define to 1 if you have the <sys/types.h> header file. */+#undef HAVE_SYS_TYPES_H++/* Define to 1 if you have the <sys/uio.h> header file. */+#undef HAVE_SYS_UIO_H++/* Define to 1 if you have the <sys/un.h> header file. */+#undef HAVE_SYS_UN_H++/* Define to 1 if you have the <unistd.h> header file. */+#undef HAVE_UNISTD_H++/* Define to 1 if you have the <winsock2.h> header file. */+#undef HAVE_WINSOCK2_H++/* Define to 1 if you have the <ws2tcpip.h> header file. */+#undef HAVE_WS2TCPIP_H++/* Define to 1 if you have the <wspiapi.h> header file. */+#undef HAVE_WSPIAPI_H++/* Define to 1 if the `getaddrinfo' function needs WINVER set. */+#undef NEED_WINVER_XP++/* Define to the address where bug reports for this package should be sent. */+#undef PACKAGE_BUGREPORT++/* Define to the full name of this package. */+#undef PACKAGE_NAME++/* Define to the full name and version of this package. */+#undef PACKAGE_STRING++/* Define to the one symbol short name of this package. */+#undef PACKAGE_TARNAME++/* Define to the version of this package. */+#undef PACKAGE_VERSION++/* Define to 1 if you have the ANSI C header files. */+#undef STDC_HEADERS++/* Define to empty if `const' does not conform to ANSI C. */+#undef const
+ install-sh view
@@ -0,0 +1,295 @@+#!/bin/sh+# install - install a program, script, or datafile++scriptversion=2003-09-24.23++# This originates from X11R5 (mit/util/scripts/install.sh), which was+# later released in X11R6 (xc/config/util/install.sh) with the+# following copyright and license.+#+# Copyright (C) 1994 X Consortium+#+# Permission is hereby granted, free of charge, to any person obtaining a copy+# of this software and associated documentation files (the "Software"), to+# deal in the Software without restriction, including without limitation the+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or+# sell copies of the Software, and to permit persons to whom the Software is+# furnished to do so, subject to the following conditions:+#+# The above copyright notice and this permission notice shall be included in+# all copies or substantial portions of the Software.+#+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+#+# Except as contained in this notice, the name of the X Consortium shall not+# be used in advertising or otherwise to promote the sale, use or other deal-+# ings in this Software without prior written authorization from the X Consor-+# tium.+#+#+# FSF changes to this file are in the public domain.+#+# Calling this script install-sh is preferred over install.sh, to prevent+# `make' implicit rules from creating a file called install from it+# when there is no Makefile.+#+# This script is compatible with the BSD install script, but was written+# from scratch. It can only install one file at a time, a restriction+# shared with many OS's install programs.++# set DOITPROG to echo to test this script++# Don't use :- since 4.3BSD and earlier shells don't like it.+doit="${DOITPROG-}"++# put in absolute paths if you don't have them in your path; or use env. vars.++mvprog="${MVPROG-mv}"+cpprog="${CPPROG-cp}"+chmodprog="${CHMODPROG-chmod}"+chownprog="${CHOWNPROG-chown}"+chgrpprog="${CHGRPPROG-chgrp}"+stripprog="${STRIPPROG-strip}"+rmprog="${RMPROG-rm}"+mkdirprog="${MKDIRPROG-mkdir}"++transformbasename=+transform_arg=+instcmd="$mvprog"+chmodcmd="$chmodprog 0755"+chowncmd=+chgrpcmd=+stripcmd=+rmcmd="$rmprog -f"+mvcmd="$mvprog"+src=+dst=+dir_arg=++usage="Usage: $0 [OPTION]... SRCFILE DSTFILE+ or: $0 -d DIR1 DIR2...++In the first form, install SRCFILE to DSTFILE, removing SRCFILE by default.+In the second, create the directory path DIR.++Options:+-b=TRANSFORMBASENAME+-c copy source (using $cpprog) instead of moving (using $mvprog).+-d create directories instead of installing files.+-g GROUP $chgrp installed files to GROUP.+-m MODE $chmod installed files to MODE.+-o USER $chown installed files to USER.+-s strip installed files (using $stripprog).+-t=TRANSFORM+--help display this help and exit.+--version display version info and exit.++Environment variables override the default commands:+ CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG+"++while test -n "$1"; do+ case $1 in+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`+ shift+ continue;;++ -c) instcmd=$cpprog+ shift+ continue;;++ -d) dir_arg=true+ shift+ continue;;++ -g) chgrpcmd="$chgrpprog $2"+ shift+ shift+ continue;;++ --help) echo "$usage"; exit 0;;++ -m) chmodcmd="$chmodprog $2"+ shift+ shift+ continue;;++ -o) chowncmd="$chownprog $2"+ shift+ shift+ continue;;++ -s) stripcmd=$stripprog+ shift+ continue;;++ -t=*) transformarg=`echo $1 | sed 's/-t=//'`+ shift+ continue;;++ --version) echo "$0 $scriptversion"; exit 0;;++ *) if test -z "$src"; then+ src=$1+ else+ # this colon is to work around a 386BSD /bin/sh bug+ :+ dst=$1+ fi+ shift+ continue;;+ esac+done++if test -z "$src"; then+ echo "$0: no input file specified." >&2+ exit 1+fi++# Protect names starting with `-'.+case $src in+ -*) src=./$src ;;+esac++if test -n "$dir_arg"; then+ dst=$src+ src=++ if test -d "$dst"; then+ instcmd=:+ chmodcmd=+ else+ instcmd=$mkdirprog+ fi+else+ # Waiting for this to be detected by the "$instcmd $src $dsttmp" command+ # might cause directories to be created, which would be especially bad+ # if $src (and thus $dsttmp) contains '*'.+ if test ! -f "$src" && test ! -d "$src"; then+ echo "$0: $src does not exist." >&2+ exit 1+ fi++ if test -z "$dst"; then+ echo "$0: no destination specified." >&2+ exit 1+ fi++ # Protect names starting with `-'.+ case $dst in+ -*) dst=./$dst ;;+ esac++ # If destination is a directory, append the input filename; won't work+ # if double slashes aren't ignored.+ if test -d "$dst"; then+ dst=$dst/`basename "$src"`+ fi+fi++# This sed command emulates the dirname command.+dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`++# Make sure that the destination directory exists.++# Skip lots of stat calls in the usual case.+if test ! -d "$dstdir"; then+ defaultIFS='+ '+ IFS="${IFS-$defaultIFS}"++ oIFS=$IFS+ # Some sh's can't handle IFS=/ for some reason.+ IFS='%'+ set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`+ IFS=$oIFS++ pathcomp=++ while test $# -ne 0 ; do+ pathcomp=$pathcomp$1+ shift+ test -d "$pathcomp" || $mkdirprog "$pathcomp"+ pathcomp=$pathcomp/+ done+fi++if test -n "$dir_arg"; then+ $doit $instcmd "$dst" \+ && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \+ && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }++else+ # If we're going to rename the final executable, determine the name now.+ if test -z "$transformarg"; then+ dstfile=`basename "$dst"`+ else+ dstfile=`basename "$dst" $transformbasename \+ | sed $transformarg`$transformbasename+ fi++ # don't allow the sed command to completely eliminate the filename.+ test -z "$dstfile" && dstfile=`basename "$dst"`++ # Make a couple of temp file names in the proper directory.+ dsttmp=$dstdir/_inst.$$_+ rmtmp=$dstdir/_rm.$$_++ # Trap to clean up those temp files at exit.+ trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0+ trap '(exit $?); exit' 1 2 13 15++ # Move or copy the file name to the temp name+ $doit $instcmd "$src" "$dsttmp" &&++ # and set any options; do chmod last to preserve setuid bits.+ #+ # If any of these fail, we abort the whole thing. If we want to+ # ignore errors from any of these, just make sure not to ignore+ # errors from the above "$doit $instcmd $src $dsttmp" command.+ #+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \+ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&++ # Now remove or move aside any old file at destination location. We+ # try this two ways since rm can't unlink itself on some systems and+ # the destination file might be busy for other reasons. In this case,+ # the final cleanup might fail but the new file should still install+ # successfully.+ {+ if test -f "$dstdir/$dstfile"; then+ $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \+ || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \+ || {+ echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2+ (exit 1); exit+ }+ else+ :+ fi+ } &&++ # Now rename the file to the real destination.+ $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"+fi &&++# The final little trick to "correctly" pass the exit status to the exit trap.+{+ (exit 0); exit+}++# Local variables:+# eval: (add-hook 'write-file-hooks 'time-stamp)+# time-stamp-start: "scriptversion="+# time-stamp-format: "%:y-%02m-%02d.%02H"+# time-stamp-end: "$"+# End:
+ network.buildinfo.in view
@@ -0,0 +1,5 @@+executable: cabal+ghc-options: -DCALLCONV=@CALLCONV@ @EXTRA_CPPFLAGS@+cc-options: -DCALLCONV=@CALLCONV@ @EXTRA_CPPFLAGS@+c-sources: @EXTRA_SRCS@+extra-libraries: @EXTRA_LIBS@