MissingH 0.18.6 → 1.0.0
raw patch · 30 files changed
+709/−686 lines, 30 filesdep +arraydep +containersdep +directorydep ~basesetup-changed
Dependencies added: array, containers, directory, old-locale, old-time, process, random, unix
Dependency ranges changed: base
Files
- MissingH.cabal +37/−15
- Setup.hs +1/−18
- src/Data/CSV.hs +10/−3
- src/Data/Either/Utils.hs +19/−1
- src/Data/List/Utils.hs +8/−5
- src/Data/MIME/Types.hs +25/−31
- src/Data/Progress/Meter.hs +35/−33
- src/Data/String.hs +0/−100
- src/Data/String/Utils.hs +101/−0
- src/Data/Tuple/Utils.hs +49/−0
- src/Network/Email/Sendmail.hs +8/−7
- src/Network/Utils.hs +9/−10
- src/System/Cmd/Utils.hs +29/−29
- src/System/Daemon.hs +7/−9
- src/System/Debian.hs +5/−10
- src/System/Debian/ControlParser.hs +9/−6
- src/System/FileArchive/GZip.hs +41/−40
- src/System/IO/Binary.hs +25/−29
- src/System/IO/HVFS/Combinators.hs +21/−24
- src/System/IO/HVFS/InstanceHelpers.hs +29/−32
- src/System/IO/HVIO.hs +35/−38
- src/System/IO/StatCompat.hs +10/−10
- src/System/IO/Utils.hs +9/−19
- src/System/Path.hs +4/−6
- src/System/Path/Glob.hs +14/−11
- src/System/Path/NameManip.hs +9/−9
- src/System/Path/WildMatch.hs +6/−8
- src/System/Time/ParseDate.hs +142/−143
- src/Test/HUnit/Utils.hs +12/−15
- testsrc/runtests.hs +0/−25
MissingH.cabal view
@@ -1,10 +1,9 @@--- arch-tag: MissingH main description file Name: MissingH-Version: 0.18.6+Version: 1.0.0 License: GPL Maintainer: John Goerzen <jgoerzen@complete.org> Author: John Goerzen-Copyright: Copyright (c) 2004-2007 John Goerzen+Copyright: Copyright (c) 2004-2008 John Goerzen license-file: COPYRIGHT extra-source-files: COPYING homepage: http://software.complete.org/missingh@@ -14,8 +13,16 @@ Haskell programmers. It is written in pure Haskell and thus should be extremely portable and easy to use. Stability: Beta-Hs-Source-Dirs: src-Exposed-Modules: Data.String, System.IO.Utils, System.IO.Binary, Data.List.Utils,+Build-Type: Simple+Cabal-Version: >=1.2++Flag splitBase+ description: Choose the new smaller, split-up base package.++Library+ Hs-Source-Dirs: src+ Exposed-Modules:+ Data.String.Utils, System.IO.Utils, System.IO.Binary, Data.List.Utils, System.Daemon, Text.ParserCombinators.Parsec.Utils, Test.HUnit.Utils,@@ -34,6 +41,7 @@ Network.SocketServer, Data.Either.Utils, Data.Maybe.Utils,+ Data.Tuple.Utils, Data.Bits.Utils, Data.Hash.CRC32.Posix, Data.Hash.CRC32.GZip, Data.Hash.MD5, Data.Hash.MD5.Zord64_HARD,@@ -48,17 +56,31 @@ System.Debian, System.Debian.ControlParser, Data.MIME.Types, System.Console.GetOpt.Utils-Extensions: ExistentialQuantification, OverlappingInstances, - UndecidableInstances, CPP-Build-Depends: network, parsec, base,++ Extensions: ExistentialQuantification, OverlappingInstances,+ UndecidableInstances, CPP, Rank2Types,+ MultiParamTypeClasses, FlexibleInstances, FlexibleContexts+ -- Hack because ghc-6.6 and the Cabal the comes with ghc-6.8.1+ -- does not understand the PatternSignatures extension.+ -- The Cabal that comes with ghc-6.8.2 does understand it, so+ -- this hack can be dropped if we require Cabal-Version: >=1.2.3+ If impl(ghc >= 6.8)+ GHC-Options: -XPatternSignatures++ Build-Depends: network, parsec, base, haskell98, mtl, HUnit, regex-compat, QuickCheck, filepath, hslogger--- Cabal will automatically add unix here on non-Windows platforms-GHC-Options: -O2+ If flag(splitBase)+ Build-Depends: base >= 3, directory, random, process, old-time,+ containers, old-locale, array+ Else+ Build-Depends: base < 3+ If ! os(windows)+ Build-Depends: unix -Executable: runtests-Buildable: False-Main-Is: runtests.hs-HS-Source-Dirs: testsrc, .-Extensions: ExistentialQuantification, OverlappingInstances,+Executable runtests+ Buildable: False+ Main-Is: runtests.hs+ HS-Source-Dirs: testsrc, .+ Extensions: ExistentialQuantification, OverlappingInstances, UndecidableInstances, CPP
Setup.hs view
@@ -1,22 +1,5 @@ #!/usr/bin/env runhugs import Distribution.Simple-import Distribution.PackageDescription-import Distribution.Simple.LocalBuildInfo-import Distribution.Version-import System.Info-import Data.Maybe-import System.Cmd -missingHooks = defaultUserHooks {confHook = customConfHook}--customConfHook descrip flags =- let mydescrip = case System.Info.os of- "mingw32" -> descrip- _ -> descrip {buildDepends = - (Dependency "unix" AnyVersion) :- buildDepends descrip}- in (confHook defaultUserHooks) mydescrip flags--main = defaultMainWithHooks missingHooks-+main = defaultMain
src/Data/CSV.hs view
@@ -30,14 +30,19 @@ Written by John Goerzen, jgoerzen\@complete.org -} -module Data.CSV(csvFile, genCsvFile) where+module Data.CSV (csvFile, genCsvFile) where import Text.ParserCombinators.Parsec-import Data.List+import Data.List (intersperse) +eol :: forall st. GenParser Char st String eol = (try $ string "\n\r") <|> (try $ string "\r\n") <|> string "\n" <|> string "\r" <?> "End of line"++cell :: GenParser Char st String cell = quotedcell <|> many (noneOf ",\n\r")-quotedchar = noneOf "\"" ++quotedchar :: GenParser Char st Char+quotedchar = noneOf "\"" <|> (try $ do string "\"\"" return '"' )@@ -46,6 +51,8 @@ content <- many quotedchar char '"' return content++line :: GenParser Char st [String] line = sepBy cell (char ',') {- | Parse a Comma-Separated Value (CSV) file. The return value is a list of
src/Data/Either/Utils.hs view
@@ -34,7 +34,8 @@ maybeToEither, forceEither, forceEitherMsg,- eitherToMonadError+ eitherToMonadError,+ fromLeft, fromRight, fromEither ) where import Control.Monad.Error @@ -71,3 +72,20 @@ eitherToMonadError :: MonadError e m => Either e a -> m a eitherToMonadError (Left x) = throwError x eitherToMonadError (Right x) = return x+++-- | Take a Left to a value, crashes on a Right+fromLeft :: Either a b -> a+fromLeft (Left a) = a+fromLeft _ = error "Data.Either.Utils.fromLeft: Right"++-- | Take a Right to a value, crashes on a Left+fromRight :: Either a b -> b+fromRight (Right a) = a+fromRight _ = error "Data.Either.Utils.fromRight: Left"++-- | Take an Either, and return the value inside it+fromEither :: Either a a -> a+fromEither (Left a) = a+fromEither (Right a) = a+
src/Data/List/Utils.hs view
@@ -59,9 +59,7 @@ -- sub, ) where import Data.List(intersperse, concat, isPrefixOf, isSuffixOf, elemIndices,- elemIndex, elemIndices, tails, find, findIndex)-import IO-import System.IO.Unsafe+ elemIndex, elemIndices, tails, find, findIndex, isInfixOf) import Control.Monad.State(State, get, put) import Data.Maybe(isJust) @@ -229,6 +227,7 @@ genericJoin :: Show a => String -> [a] -> String genericJoin delim l = join delim (map show l) +{-# DEPRECATED contains "Use Data.List.isInfixOf, will be removed in MissingH 1.1.0" #-} {- | Returns true if the given parameter is a sublist of the given list; false otherwise. @@ -236,10 +235,14 @@ > contains "Haskell" "I really like Haskell." -> True > contains "Haskell" "OCaml is great." -> False++This function was submitted to GHC and was applied as+'Data.List.isInfixOf'. This function therefore is deprecated and will+be removed in future versions. -} contains :: Eq a => [a] -> [a] -> Bool-contains substr str = isJust $ find (isPrefixOf substr) (tails str)+contains = isInfixOf -- above function submitted to GHC as Data.List.isInfixOf on 8/31/2006 @@ -473,7 +476,7 @@ This function is not compatible with infinite lists. -} uniq :: Eq a => [a] -> [a] uniq [] = []-uniq (x:xs) = x : filter (/= x) (uniq xs)+uniq (x:xs) = x : uniq (filter (/= x) xs) ----- same as --uniq (x:xs) = x : [y | y <- uniq xs, y /= x]
src/Data/MIME/Types.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -45,7 +45,6 @@ where import qualified Data.Map as Map-import qualified System.Directory import Monad import System.IO import System.IO.Error@@ -55,10 +54,10 @@ import Data.Char ------------------------------------------------------------------------- Basic type decl+-- Basic type declarations ---------------------------------------------------------------------- -data MIMETypeData = MIMETypeData +data MIMETypeData = MIMETypeData { -- | A mapping used to expand common suffixes into equivolent, -- better-parsed versions. For instance, ".tgz" would expand@@ -88,8 +87,6 @@ {- | Read the given mime.types file and add it to an existing object. Returns new object. -}-- readMIMETypes :: MIMETypeData -- ^ Data to work with -> Bool -- ^ Whether to work on strict data -> FilePath -- ^ File to read@@ -103,10 +100,10 @@ -> Bool -- ^ Whether to work on strict data -> Handle -- ^ Handle to read from -> IO MIMETypeData -- ^ New object-hReadMIMETypes mtd strict h = +hReadMIMETypes mtd strict h = let parseline :: MIMETypeData -> String -> MIMETypeData parseline obj line =- let l1 = words line + let l1 = words line procwords [] = [] procwords (('#':_) :_) = [] procwords (x:xs) = x : procwords xs@@ -119,29 +116,28 @@ foldl (\o suff -> addType o strict thetype ('.' : suff)) obj suffixlist else obj in- do- lines <- hGetLines h- return (foldl parseline mtd lines)+ do+ lines <- hGetLines h+ return (foldl parseline mtd lines) {- | Guess the type of a file given a filename or URL. The file is not opened; only the name is considered. -}- guessType :: MIMETypeData -- ^ Source data for guessing -> Bool -- ^ Whether to limit to strict data -> String -- ^ File or URL name to consider -> MIMEResults -- ^ Result of guessing (see 'MIMEResults' for details on interpreting it)-guessType mtd strict fn = - let mapext (base, ext) =- case Map.lookup ext (suffixMap mtd) of- Nothing -> (base, ext)+guessType mtd strict fn =+ let mapext (base, ex) =+ case Map.lookup ex (suffixMap mtd) of+ Nothing -> (base, ex) Just x -> mapext (splitExt (base ++ x))- checkencodings (base, ext) =- case Map.lookup ext (encodingsMap mtd) of- Nothing -> (base, ext, Nothing)+ checkencodings (base, ex) =+ case Map.lookup ex (encodingsMap mtd) of+ Nothing -> (base, ex, Nothing) Just x -> (fst (splitExt base), snd (splitExt base), Just x)- (base, ext, enc) = checkencodings . mapext $ splitExt fn+ (_, ext, enc) = checkencodings . mapext $ splitExt fn typemap = getStrict mtd strict in case Map.lookup ext typemap of@@ -160,7 +156,7 @@ -> Bool -- ^ Whether to limit to strict data -> String -- ^ MIME type to consider -> Maybe String -- ^ Result of guessing, or Nothing if no match possible-guessExtension mtd strict fn = +guessExtension mtd strict fn = case guessAllExtensions mtd strict fn of [] -> Nothing (x:_) -> Just x@@ -176,22 +172,21 @@ themap = getStrict mtd strict in flippedLookupM mimetype themap- + {- | Adds a new type to the data structures, replacing whatever data may exist about it already. That is, it overrides existing information about the given extension, but the same type may occur more than once. -}- addType :: MIMETypeData -- ^ Source data -> Bool -- ^ Whether to add to strict data set -> String -- ^ MIME type to add -> String -- ^ Extension to add -> MIMETypeData -- ^ Result of addition-addType mtd strict thetype theext = +addType mtd strict thetype theext = setStrict mtd strict (\m -> Map.insert theext thetype m) {- | Default MIME type data to use -} defaultmtd :: MIMETypeData-defaultmtd = +defaultmtd = MIMETypeData {suffixMap = default_suffix_map, encodingsMap = default_encodings_map, typesMap = default_types_map,@@ -202,7 +197,7 @@ readSystemMIMETypes :: MIMETypeData -> IO MIMETypeData readSystemMIMETypes mtd = let tryread :: MIMETypeData -> String -> IO MIMETypeData- tryread inputobj filename = + tryread inputobj filename = do fn <- try (openFile filename ReadMode) case fn of@@ -213,12 +208,11 @@ return x in do- foldM tryread mtd defaultfilelocations+ foldM tryread mtd defaultfilelocations ---------------------------------------------------------------------- -- Internal utilities ----------------------------------------------------------------------- getStrict :: MIMETypeData -> Bool -> Map.Map String String getStrict mtd True = typesMap mtd getStrict mtd False = Map.union (typesMap mtd) (commonTypesMap mtd)@@ -230,7 +224,7 @@ ---------------------------------------------------------------------- -- Default data structures -----------------------------------------------------------------------+defaultfilelocations :: [String] defaultfilelocations = [ "/etc/mime.types",@@ -240,13 +234,13 @@ "/usr/local/etc/mime.types" -- Apache 1.3 ] -+default_encodings_map, default_suffix_map, default_types_map, default_common_types :: Map.Map String String default_encodings_map = Map.fromList [ (".Z", "compress"), (".gz", "gzip"), (".bz2", "bzip2") ]- + default_suffix_map = Map.fromList [ (".tgz", ".tar.gz"), (".tz", ".tar.gz"),
src/Data/Progress/Meter.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -39,7 +39,7 @@ addComponent, removeComponent, setWidth,- + -- * Rendering and Output renderMeter, displayMeter,@@ -50,17 +50,16 @@ ) where import Data.Progress.Tracker-import Control.Concurrent.MVar import Control.Concurrent-import Control.Monad(when)-import Data.String-import System.Time.Utils-import Data.Quantity+import Control.Monad (when)+import Data.String.Utils (join)+import System.Time.Utils (renderSecs)+import Data.Quantity (renderNums, binaryOpts) import System.IO-import Control.Monad(filterM)+import Control.Monad (filterM) {- | The main data type for the progress meter. -}-data ProgressMeterR = +data ProgressMeterR = ProgressMeterR {masterP :: Progress, -- ^ The master 'Progress' object for overall status components :: [Progress], -- ^ Individual component statuses width :: Int, -- ^ Width of the meter@@ -91,7 +90,7 @@ -> Int -- ^ Width of the terminal -- usually 80 -> ([Integer] -> [String])-- ^ A function to render sizes -> IO ProgressMeter-newMeter tracker u w rfunc = +newMeter tracker u w rfunc = newMVar $ ProgressMeterR {masterP = tracker, components = [], width = w, renderer = rfunc, autoDisplayers = [], unit = u}@@ -102,7 +101,7 @@ {- | Add a new component to the list of components. -} addComponent :: ProgressMeter -> Progress -> IO ()-addComponent meter component = +addComponent meter component = modifyMVar_ meter (\m -> return $ m {components = component : components m}) {- | Remove a component by name. -}@@ -116,9 +115,9 @@ setWidth :: ProgressMeter -> Int -> IO () setWidth meter w = modifyMVar_ meter (\m -> return $ m {width = w}) -{- | Like renderMeter, but prints it to the screen instead of returning it. +{- | Like renderMeter, but prints it to the screen instead of returning it. -This function will output CR, then the meter. +This function will output CR, then the meter. Pass stdout as the handle for regular display to the screen. -} displayMeter :: Handle -> ProgressMeter -> IO ()@@ -130,15 +129,15 @@ -- lock the IO and prevent IO from stomping on each other. {- | Clears the meter -- outputs CR, spaces equal to the width - 1,-then another CR. +then another CR. Pass stdout as the handle for regular display to the screen. -} clearMeter :: Handle -> ProgressMeter -> IO ()-clearMeter h pm = withMVar pm $ \m -> +clearMeter h pm = withMVar pm $ \m -> do hPutStr h (clearmeterstr m) hFlush h -{- | Clears the meter, writes the given string, then restores the meter. +{- | Clears the meter, writes the given string, then restores the meter. The string is assumed to contain a trailing newline. Pass stdout as the handle for regular display to the screen. -}@@ -150,11 +149,11 @@ hPutStr h s hFlush h +clearmeterstr :: ProgressMeterR -> String clearmeterstr m = "\r" ++ replicate (width m - 1) ' ' ++ "\r" - {- | Starts a thread that updates the meter every n seconds by calling-the specified function. Note: @displayMeter stdout@ +the specified function. Note: @displayMeter stdout@ is an ideal function here. Save this threadID and use it later to call 'stopAutoDisplayMeter'.@@ -185,7 +184,7 @@ You should probably call 'clearMeter' after a call to this. -} killAutoDisplayMeter :: ProgressMeter -> ThreadId -> IO ()-killAutoDisplayMeter pm t = +killAutoDisplayMeter pm t = modifyMVar_ pm (\p -> return $ p {autoDisplayers = filter (/= t) (autoDisplayers p)}) {- | Render the current status. -}@@ -194,10 +193,10 @@ renderMeterR :: ProgressMeterR -> IO String renderMeterR meter =- do overallpct <- renderpct (masterP meter)- components <- mapM (rendercomponent (renderer meter))+ do overallpct <- renderpct $ masterP meter+ compnnts <- mapM (rendercomponent $ renderer meter) (components meter)- let componentstr = case join " " components of+ let componentstr = case join " " compnnts of [] -> "" x -> x ++ " " rightpart <- renderoverall (renderer meter) (masterP meter)@@ -206,25 +205,28 @@ if padwidth < 1 then return $ take (width meter - 1) $ leftpart ++ rightpart else return $ leftpart ++ replicate padwidth ' ' ++ rightpart- - where renderpct pt = ++ where+ u = unit meter+ renderpct pt = withStatus pt renderpctpts- renderpctpts pts = + renderpctpts pts = if (totalUnits pts == 0) then return "0%" else return $ show (((completedUnits pts) * 100) `div` (totalUnits pts)) ++ "%"- rendercomponent :: ([Integer] -> [String]) -> Progress -> IO String- rendercomponent rfunc pt = withStatus pt $ \pts ->+ rendercomponent :: ([Integer] -> [String]) -> Progress -> IO String+ rendercomponent rfunc pt = withStatus pt $ \pts -> do pct <- renderpctpts pts- let u = unit meter let renders = rfunc [totalUnits pts, completedUnits pts] return $ "[" ++ trackerName pts ++ " " ++ (renders !! 1) ++ u ++ "/" ++ head renders ++ u ++ " " ++ pct ++ "]"- renderoverall rfunc pt = withStatus pt $ \pts ->- do etr <- getETR pts- speed <- getSpeed pts- return $ head (rfunc [floor speed]) ++ (unit meter) ++ - "/s " ++ renderSecs etr++ renderoverall :: (ProgressStatuses a (IO [Char])) => ([Integer] -> [[Char]]) -> a -> IO [Char]+ renderoverall rfunc pt = withStatus pt $ \pts ->+ do etr <- getETR pts+ speed <- getSpeed pts+ return $ head (rfunc [floor (speed :: Double)]) ++ u +++ "/s " ++ renderSecs etr
− src/Data/String.hs
@@ -1,100 +0,0 @@-{- arch-tag: String utilities main file-Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>--This program is free software; you can redistribute it and\/or modify-it under the terms of the GNU General Public License as published by-the Free Software Foundation; either version 2 of the License, or-(at your option) any later version.--This program is distributed in the hope that it will be useful,-but WITHOUT ANY WARRANTY; without even the implied warranty of-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-GNU General Public License for more details.--You should have received a copy of the GNU General Public License-along with this program; if not, write to the Free Software-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA--}--{- |- Module : Data.String- Copyright : Copyright (C) 2004-2006 John Goerzen- License : GNU GPL, version 2 or above-- Maintainer : John Goerzen <jgoerzen@complete.org>- Stability : provisional- Portability: portable--This module provides various helpful utilities for dealing with strings.--Written by John Goerzen, jgoerzen\@complete.org--}--module Data.String- (-- * Whitespace Removal- strip, lstrip, rstrip,- -- * Tests- -- | Note: These functions are aliases for functions- -- in "Data.List.Utils".- startswith, endswith,- -- * Conversions- -- | Note: Some of these functions are aliases for functions- -- in "Data.List.Utils".- join, split, splitWs, replace, escapeRe- ) where-import Data.List.Utils(startswith, endswith, join, split, replace)-import Data.Char-import Text.Regex--wschars = " \t\r\n"--{- | Removes any whitespace characters that are present at the start-or end of a string. Does not alter the internal contents of a-string. If no whitespace characters are present at the start or end-of a string, returns the original string unmodified. Safe to use on-any string.--Note that this may differ from some other similar-functions from other authors in that:--1. If multiple whitespace-characters are present all in a row, they are all removed;--2. If no-whitespace characters are present, nothing is done.--}--strip :: String -> String-strip = lstrip . rstrip---- | Same as 'strip', but applies only to the left side of the string.-lstrip :: String -> String-lstrip s = case s of- [] -> []- (x:xs) -> if elem x wschars- then lstrip xs- else s---- | Same as 'strip', but applies only to the right side of the string.-rstrip :: String -> String-rstrip = reverse . lstrip . reverse--{- | Splits a string around whitespace. Empty elements in the result-list are automatically removed. -}-splitWs :: String -> [String]-splitWs = filter (\x -> x /= []) . splitRegex (mkRegex "[ \t\n\r\v\f]+")--{- | Escape all characters in the input pattern that are not alphanumeric.--Does not make special allowances for NULL, which isn't valid in a-Haskell regular expression pattern. -}-escapeRe :: String -> String-escapeRe [] = []-escapeRe (x:xs)- -- Chars that we never escape- | x `elem` ['\'', '`'] = x : escapeRe xs- -- General rules for chars we never escape- | isDigit x || (isAscii x && isAlpha x) || x `elem` ['<', '>'] - = x : escapeRe xs- -- Escape everything else- | otherwise = '\\' : x : escapeRe xs
+ src/Data/String/Utils.hs view
@@ -0,0 +1,101 @@+{- arch-tag: String utilities main file+Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>++This program is free software; you can redistribute it and\/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation; either version 2 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+-}++{- |+ Module : Data.String.Utils+ Copyright : Copyright (C) 2004-2008 John Goerzen+ License : GNU GPL, version 2 or above++ Maintainer : John Goerzen <jgoerzen@complete.org>+ Stability : provisional+ Portability: portable++This module provides various helpful utilities for dealing with strings.++Written by John Goerzen, jgoerzen\@complete.org+-}++module Data.String.Utils+ (-- * Whitespace Removal+ strip, lstrip, rstrip,+ -- * Tests+ -- | Note: These functions are aliases for functions+ -- in "Data.List.Utils".+ startswith, endswith,+ -- * Conversions+ -- | Note: Some of these functions are aliases for functions+ -- in "Data.List.Utils".+ join, split, splitWs, replace, escapeRe+ ) where++import Data.List.Utils (startswith, endswith, join, split, replace)+import Data.Char (isAlpha, isAscii, isDigit)+import Text.Regex (mkRegex, splitRegex)++wschars :: String+wschars = " \t\r\n"++{- | Removes any whitespace characters that are present at the start+or end of a string. Does not alter the internal contents of a+string. If no whitespace characters are present at the start or end+of a string, returns the original string unmodified. Safe to use on+any string.++Note that this may differ from some other similar+functions from other authors in that:++1. If multiple whitespace+characters are present all in a row, they are all removed;++2. If no+whitespace characters are present, nothing is done.+-}+strip :: String -> String+strip = lstrip . rstrip++-- | Same as 'strip', but applies only to the left side of the string.+lstrip :: String -> String+lstrip s = case s of+ [] -> []+ (x:xs) -> if elem x wschars+ then lstrip xs+ else s++-- | Same as 'strip', but applies only to the right side of the string.+rstrip :: String -> String+rstrip = reverse . lstrip . reverse++{- | Splits a string around whitespace. Empty elements in the result+list are automatically removed. -}+splitWs :: String -> [String]+splitWs = filter (\x -> x /= []) . splitRegex (mkRegex "[ \t\n\r\v\f]+")++{- | Escape all characters in the input pattern that are not alphanumeric.++Does not make special allowances for NULL, which isn't valid in a+Haskell regular expression pattern. -}+escapeRe :: String -> String+escapeRe [] = []+escapeRe (x:xs)+ -- Chars that we never escape+ | x `elem` ['\'', '`'] = x : escapeRe xs+ -- General rules for chars we never escape+ | isDigit x || (isAscii x && isAlpha x) || x `elem` ['<', '>']+ = x : escapeRe xs+ -- Escape everything else+ | otherwise = '\\' : x : escapeRe xs
+ src/Data/Tuple/Utils.hs view
@@ -0,0 +1,49 @@+{- arch-tag: Tuple utilities main file+Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>++This program is free software; you can redistribute it and/or modify+it under the terms of the GNU General Public License as published by+the Free Software Foundation; either version 2 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+GNU General Public License for more details.++You should have received a copy of the GNU General Public License+along with this program; if not, write to the Free Software+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA+-}++{- |+ Module : Data.Tuple.Utils+ Copyright : Copyright (C) 2004-2008 John Goerzen+ License : GNU GPL, version 2 or above++ Maintainer : John Goerzen <jgoerzen@complete.org> + Stability : provisional+ Portability: portable++This module provides various helpful utilities for dealing with lists.++Written by Neil Mitchell, <http://www.cs.york.ac.uk/~ndm/>+-}++module Data.Tuple.Utils(+ -- * Extraction+ fst3, snd3, thd3+ ) where+++-- | Take the first item out of a 3 element tuple+fst3 :: (a,b,c) -> a+fst3 (a,b,c) = a++-- | Take the second item out of a 3 element tuple+snd3 :: (a,b,c) -> b+snd3 (a,b,c) = b++-- | Take the third item out of a 3 element tuple+thd3 :: (a,b,c) -> c+thd3 (a,b,c) = c
src/Network/Email/Sendmail.hs view
@@ -22,7 +22,7 @@ Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -45,6 +45,7 @@ import System.IO import System.IO.Error +sendmails :: [String] sendmails = ["/usr/sbin/sendmail", "/usr/local/sbin/sendmail", "/usr/local/bin/sendmail",@@ -90,12 +91,12 @@ -> IO () sendmail _ [] _ = fail "sendmail: no recipients specified" sendmail Nothing recipients msg = sendmail_worker recipients msg-sendmail (Just from) recipients msg = +sendmail (Just from) recipients msg = sendmail_worker (("-f" ++ from) : recipients) msg- + sendmail_worker :: [String] -> String -> IO () sendmail_worker args msg =- let func h = hPutStr h msg + let func h = hPutStr h msg in do --pOpen WriteToPipe "/usr/sbin/sendmail" args func@@ -104,8 +105,8 @@ Right x -> return x Left _ -> do sn <- findsendmail- rv <- pOpen WriteToPipe sn args func- return $! rv- + r <- pOpen WriteToPipe sn args func+ return $! r+ #endif
src/Network/Utils.hs view
@@ -15,13 +15,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -}- {- | Module : Network.Utils Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: systems with networking @@ -31,10 +30,10 @@ -} -module Network.Utils(niceSocketsDo, connectTCP, connectTCPAddr,- listenTCPAddr, showSockAddr- )-where+module Network.Utils (niceSocketsDo, connectTCP, connectTCPAddr,+ listenTCPAddr, showSockAddr)+ where+ import Network import Network.Socket import Network.BSD@@ -43,11 +42,11 @@ {- | Sets up the system for networking. Similar to the built-in withSocketsDo (and actually, calls it), but also sets the SIGPIPE-handler so that signal is ignored. +handler so that signal is ignored. Example: -> main = niceSocketsDo $ do { ... } +> main = niceSocketsDo $ do { ... } -} -- FIXME integrate with WebCont.Util.UDP@@ -56,7 +55,7 @@ niceSocketsDo func = do #ifndef mingw32_HOST_OS -- No signals on Windows anyway- System.Posix.Signals.installHandler + System.Posix.Signals.installHandler System.Posix.Signals.sigPIPE System.Posix.Signals.Ignore Nothing@@ -74,7 +73,7 @@ s <- socket AF_INET Stream proto connect s addr return s- + listenTCPAddr :: SockAddr -> Int -> IO Socket listenTCPAddr addr queuelen = do proto <- getProtocolNumber "tcp"
src/System/Cmd/Utils.hs view
@@ -23,7 +23,7 @@ Copyright : Copyright (C) 2004-2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable to platforms with POSIX process\/signal tools @@ -99,7 +99,7 @@ pOpen, pOpen3, pOpen3Raw #endif #endif- )+ ) where -- FIXME - largely obsoleted by 6.4 - convert to wrappers.@@ -122,6 +122,7 @@ data PipeMode = ReadFromPipe | WriteToPipe +logbase :: String logbase = "System.Cmd.Utils" {- | Return value from 'pipeFrom', 'pipeLinesFrom', 'pipeTo', or@@ -129,7 +130,7 @@ executed. If you prefer not to use 'forceSuccess' on the result of one of these pipe calls, you can use (processID ph), assuming ph is your 'PipeHandle', as a parameter to 'System.Posix.Process.getProcessStatus'. -}-data PipeHandle = +data PipeHandle = PipeHandle { processID :: ProcessID, phCommand :: FilePath, phArgs :: [String],@@ -152,7 +153,10 @@ #endif #endif +logRunning :: String -> FilePath -> [String] -> IO () logRunning func fp args = debugM (logbase ++ "." ++ func) (showCmd fp args)++warnFail :: [Char] -> FilePath -> [String] -> [Char] -> IO t warnFail funcname fp args msg = let m = showCmd fp args ++ ": " ++ msg in do warningM (logbase ++ "." ++ funcname) m@@ -170,7 +174,7 @@ Not available on Windows or with Hugs. -} hPipeFrom :: FilePath -> [String] -> IO (PipeHandle, Handle)-hPipeFrom fp args = +hPipeFrom fp args = do pipepair <- createPipe logRunning "pipeFrom" fp args let childstuff = do dupTo (snd pipepair) stdOutput@@ -180,7 +184,7 @@ -- parent pid <- case p of Right x -> return x- Left e -> warnFail "pipeFrom" fp args $ + Left e -> warnFail "pipeFrom" fp args $ "Error in fork: " ++ show e closeFd (snd pipepair) h <- fdToHandle (fst pipepair)@@ -229,7 +233,7 @@ -- parent pid <- case p of Right x -> return x- Left e -> warnFail "pipeTo" fp args $ + Left e -> warnFail "pipeTo" fp args $ "Error in fork: " ++ show e closeFd (fst pipepair) h <- fdToHandle (snd pipepair)@@ -319,7 +323,7 @@ of the given process ID. If the process terminated normally, does nothing. Otherwise, raises an exception with an appropriate error message. -This call will block waiting for the given pid to terminate. +This call will block waiting for the given pid to terminate. Not available on Windows. -} forceSuccess :: PipeHandle -> IO ()@@ -329,12 +333,12 @@ case status of Nothing -> warnfail fp args $ "Got no process status" Just (Exited (ExitSuccess)) -> return ()- Just (Exited (ExitFailure fc)) -> + Just (Exited (ExitFailure fc)) -> cmdfailed funcname fp args fc- Just (Terminated sig) -> + Just (Terminated sig) -> warnfail fp args $ "Terminated by signal " ++ show sig- Just (Stopped sig) -> - warnfail fp args $ "Stopped by signal " ++ show sig + Just (Stopped sig) ->+ warnfail fp args $ "Stopped by signal " ++ show sig #endif {- | Invokes the specified command in a subprocess, waiting for the result.@@ -344,7 +348,7 @@ Implemented in terms of 'posixRawSystem' where supported, and System.Posix.rawSystem otherwise. -} safeSystem :: FilePath -> [String] -> IO ()-safeSystem command args = +safeSystem command args = do debugM (logbase ++ ".safeSystem") ("Running: " ++ command ++ " " ++ (show args)) #if defined(__HUGS__) || defined(mingw32_HOST_OS)@@ -381,7 +385,7 @@ oldset <- getSignalMask blockSignals sigset childpid <- forkProcess (childaction oldint oldquit oldset)- + mps <- getProcessStatus True False childpid restoresignals oldint oldquit oldset let retval = case mps of@@ -392,9 +396,9 @@ (program ++ ": exited with " ++ show retval) return retval - where childaction oldint oldquit oldset = + where childaction oldint oldquit oldset = do restoresignals oldint oldquit oldset- executeFile program True args Nothing + executeFile program True args Nothing restoresignals oldint oldquit oldset = do installHandler sigINT oldint Nothing installHandler sigQUIT oldquit Nothing@@ -418,14 +422,12 @@ do debugM (logbase ++ ".forkRawSystem") ("Running: " ++ program ++ " " ++ (show args)) forkProcess childaction-- where childaction =- executeFile program True args Nothing + where+ childaction = executeFile program True args Nothing #endif #endif - cmdfailed :: String -> FilePath -> [String] -> Int -> IO a cmdfailed funcname command args failcode = do let errormsg = "Command " ++ command ++ " " ++ (show args) ++@@ -452,12 +454,12 @@ Passes the handle on to the specified function. -The 'PipeMode' specifies what you will be doing. That is, specifing 'ReadFromPipe' +The 'PipeMode' specifies what you will be doing. That is, specifing 'ReadFromPipe' sets up a pipe from stdin, and 'WriteToPipe' sets up a pipe from stdout. Not available on Windows. -}-pOpen :: PipeMode -> FilePath -> [String] -> +pOpen :: PipeMode -> FilePath -> [String] -> (Handle -> IO a) -> IO a pOpen pm fp args func = do@@ -474,7 +476,7 @@ return $! x pOpen3 Nothing (Just (snd pipepair)) Nothing fp args callfunc (closeFd (fst pipepair))- WriteToPipe -> do + WriteToPipe -> do let callfunc _ = do closeFd (fst pipepair) h <- fdToHandle (snd pipepair)@@ -488,7 +490,7 @@ #ifndef mingw32_HOST_OS #ifndef __HUGS__-{- | Runs a command, redirecting things to pipes. +{- | Runs a command, redirecting things to pipes. Not available on Windows. @@ -503,7 +505,7 @@ -> (ProcessID -> IO a) -- ^ Action to run in parent -> IO () -- ^ Action to run in child before execing (if you don't need something, set this to @return ()@) -- IGNORED IN HUGS -> IO a-pOpen3 pin pout perr fp args func childfunc = +pOpen3 pin pout perr fp args func childfunc = do pid <- pOpen3Raw pin pout perr fp args childfunc retval <- func $! pid let rv = seq retval retval@@ -514,7 +516,7 @@ #ifndef mingw32_HOST_OS #ifndef __HUGS__-{- | Runs a command, redirecting things to pipes. +{- | Runs a command, redirecting things to pipes. Not available on Windows. @@ -554,7 +556,7 @@ func p -} in- do + do p <- try (forkProcess childstuff) pid <- case p of Right x -> return x@@ -564,7 +566,5 @@ #endif #endif - showCmd :: FilePath -> [String] -> String-showCmd fp args =- fp ++ " " ++ show args+showCmd fp args = fp ++ " " ++ show args
src/System/Daemon.hs view
@@ -22,7 +22,7 @@ Copyright : Copyright (C) 2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable to platforms with POSIX process\/signal tools @@ -47,8 +47,8 @@ #ifndef mingw32_HOST_OS detachDaemon #endif- )-where+ )+ where #ifndef mingw32_HOST_OS import System.Posix.Process@@ -58,6 +58,7 @@ import System.Exit +trap :: IO a -> IO a trap = traplogging "System.Daemon" ERROR "detachDaemon" {- | Detach the process from a controlling terminal and run it in the@@ -67,7 +68,7 @@ * The PID of the running process will change - * stdin, stdout, and stderr will not work (they'll be set to + * stdin, stdout, and stderr will not work (they'll be set to \/dev\/null) * CWD will be changed to \/@@ -76,10 +77,9 @@ Note that this is not intended for a daemon invoked from inetd(1). -}- detachDaemon :: IO ()-detachDaemon = trap $ - do forkProcess child1 +detachDaemon = trap $+ do forkProcess child1 exitImmediately ExitSuccess child1 :: IO ()@@ -95,6 +95,4 @@ nullFd <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags mapM_ (dupTo nullFd) [stdInput, stdOutput, stdError] closeFd nullFd-- #endif
src/System/Debian.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2004 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -36,11 +36,9 @@ -- * Version Number Utilities DebVersion, compareDebVersion, checkDebVersion )-where+ where+ import System.Cmd-import System.Debian.ControlParser-import System.Cmd.Utils-import Data.String import System.IO.Unsafe import System.Exit @@ -48,9 +46,6 @@ or any control-like file (such as the output from apt-cache show, etc.) -} type ControlFile = [(String, String)] -splitComma :: String -> [String]-splitComma = map strip . split ","- ---------------------------------------------------------------------- -- VERSION NUMBERS ----------------------------------------------------------------------@@ -60,7 +55,7 @@ data DebVersion = DebVersion String deriving (Eq) instance Ord DebVersion where- compare (DebVersion v1) (DebVersion v2) = + compare (DebVersion v1) (DebVersion v2) = {- This is OK since compareDebVersion should always be the same. -} unsafePerformIO $ compareDebVersion v1 v2 @@ -82,6 +77,6 @@ -> IO Bool checkDebVersion v1 op v2 = do ec <- rawSystem "dpkg" ["--compare-versions", v1, op, v2]- case ec of + case ec of ExitSuccess -> return True ExitFailure _ -> return False
src/System/Debian/ControlParser.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2004 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -33,18 +33,20 @@ module System.Debian.ControlParser(control, depPart) where+ import Text.ParserCombinators.Parsec-import Data.String+import Data.String.Utils (split) +eol, extline :: GenParser Char st String eol = (try (string "\r\n")) <|> string "\n" <?> "EOL" extline = try (do char ' ' content <- many (noneOf "\r\n") eol- return content- )+ return content ) +entry :: GenParser Char st (String, String) entry = do key <- many1 (noneOf ":\r\n") char ':' val <- many (noneOf "\r\n")@@ -58,6 +60,7 @@ retval <- many entry return retval +headerPGP, blankLine, header, headerHash :: GenParser Char st () headerPGP = do string "-----BEGIN PGP" manyTill (noneOf "\r\n") eol return ()@@ -69,9 +72,9 @@ return () header = (try headerPGP) <|> (try blankLine) <|> (try headerHash) -{- | Dependency parser. +{- | Dependency parser. -Returns (package name, Maybe version, arch list+Returns (package name, Maybe version, arch list) version is (operator, operand) -} depPart :: CharParser a (String, (Maybe (String, String)), [String])
src/System/FileArchive/GZip.hs view
@@ -46,17 +46,16 @@ read_header, read_section )-where+ where -import Data.Compression.Inflate-import Data.Hash.CRC32.GZip-import Data.List-import Data.Bits-import Control.Monad.Error-import Data.Char-import Data.Word-import Data.Bits.Utils-import System.IO+import Data.Compression.Inflate (inflate_string_remainder)+import Data.Hash.CRC32.GZip (update_crc)+import Data.Bits ((.&.))+import Control.Monad.Error -- (Error(), strMsg, throwError)+import Data.Char (ord)+import Data.Word (Word32())+import Data.Bits.Utils (fromBytes)+import System.IO (hGetContents, hPutStr, Handle()) data GZipError = CRCError -- ^ CRC-32 check failed | NotGZIPFile -- ^ Couldn't find a GZip header@@ -69,14 +68,16 @@ strMsg = UnknownError -- | First two bytes of file+magic :: String magic = "\x1f\x8b" -- | Flags-fFTEXT = 1::Int-fFHCRC = 2::Int-fFEXTRA = 4::Int-fFNAME = 8::Int-fFCOMMENT = 16::Int+fFHCRC, fFEXTRA, fFNAME, fFCOMMENT :: Int+-- fFTEXT = 1 :: Int+fFHCRC = 2+fFEXTRA = 4+fFNAME = 8+fFCOMMENT = 16 {- | The data structure representing the GZip header. This occurs at the beginning of each 'Section' on disk. -}@@ -117,7 +118,7 @@ hDecompress :: Handle -- ^ Input handle -> Handle -- ^ Output handle -> IO (Maybe GZipError)-hDecompress infd outfd = +hDecompress infd outfd = do inc <- hGetContents infd let (outstr, err) = decompress inc hPutStr outfd outstr@@ -131,15 +132,15 @@ -} decompress :: String -> (String, Maybe GZipError) {--decompress s = +decompress s = do x <- read_header s let rem = snd x return $ inflate_string rem -}-decompress s = +decompress s = let procs :: [Section] -> (String, Bool) procs [] = ([], True)- procs ((_, content, foot):xs) = + procs ((_, content, foot):xs) = let (nexth, nextb) = procs xs in (content ++ nexth, (crc32valid foot) && nextb) in case read_sections s of@@ -152,7 +153,7 @@ return $ concatMap (\(_, x, _) -> x) x -} --- | Read all sections. +-- | Read all sections. read_sections :: String -> Either GZipError [Section] read_sections [] = Right [] read_sections s =@@ -170,34 +171,34 @@ read_section s = do x <- read_header s let headerrem = snd x- let (decompressed, crc32, remainder) = read_data headerrem- let (crc32str, rem) = splitAt 4 remainder- let (sizestr, rem2) = splitAt 4 rem+ let (decompressed, crc, remainder) = read_data headerrem+ let (crc32str, rm) = splitAt 4 remainder+ let (sizestr, rem2) = splitAt 4 rm let filecrc32 = parseword crc32str let filesize = parseword sizestr return ((fst x, decompressed, Footer {size = filesize, crc32 = filecrc32,- crc32valid = filecrc32 == crc32})+ crc32valid = filecrc32 == crc}) ,rem2) -- | Read the file's compressed data, returning -- (Decompressed, Calculated CRC32, Remainder) read_data :: String -> (String, Word32, String)-read_data x = +read_data x = let (decompressed1, remainder) = inflate_string_remainder x (decompressed, crc32) = read_data_internal decompressed1 0 in- (decompressed, crc32, remainder)+ (decompressed, crc32, remainder) where- read_data_internal [] ck = ([], ck)- read_data_internal (x:xs) ck =- let newcrc = update_crc ck x- n = newcrc `seq` read_data_internal xs newcrc+ read_data_internal [] ck = ([], ck)+ read_data_internal (y:ys) ck =+ let newcrc = update_crc ck y+ n = newcrc `seq` read_data_internal ys newcrc in- (x : fst n, snd n)- + (y : fst n, snd n) + {- | Read the GZip header. Return (Header, Remainder). -} read_header :: String -> Either GZipError (Header, String)@@ -217,22 +218,22 @@ let mtime = parseword mtimea let (xfla, rem3b) = split1 rem3a let xfl = ord xfla- let (osa, rem3c) = split1 rem3b+ let (osa, _) = split1 rem3b let os = ord osa -- skip modtime (4), extraflag (1), and os (1) let rem4 = drop 6 rem3- - let (extra, rem5) = ++ let (extra, rem5) = if (flag .&. fFEXTRA /= 0) -- Skip past the extra field if we have it.- then let (xlen_S, rem4a) = split1 rem4+ then let (xlen_S, _) = split1 rem4 (xlen2_S, rem4b) = split1 rem4 xlen = (ord xlen_S) + 256 * (ord xlen2_S) (ex, rrem) = splitAt xlen rem4b in (Just ex, rrem) else (Nothing, rem4)- - let (filename, rem6) = ++ let (filename, rem6) = if (flag .&. fFNAME /= 0) -- Skip past the null-terminated filename then let fn = takeWhile (/= '\x00') rem5@@ -245,12 +246,12 @@ then let cm = takeWhile (/= '\x00') rem6 in (Just cm, drop ((length cm) + 1) rem6) else (Nothing, rem6)- + rem8 <- if (flag .&. fFHCRC /= 0) -- Skip past the header CRC then return $ drop 2 rem7 else return rem7- + return (Header {method = ord method, flags = flag, extra = extra,
src/System/IO/Binary.hs view
@@ -21,14 +21,14 @@ Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable to platforms supporting binary I\/O This module provides various helpful utilities for dealing with binary input and output. -You can use this module to deal with binary blocks of data as either Strings +You can use this module to deal with binary blocks of data as either Strings or lists of Word8. The BinaryConvertible class provides this abstraction. Wherever you see HVIO, you can transparently substite a regular Handle.@@ -44,7 +44,7 @@ is the support status: * GHC 6.2 and above: yes- + * GHC 6.x, earlier versions: unknown * GHC 5.x: no@@ -80,17 +80,16 @@ hFullBlockInteract, fullBlockInteract ) where -import Foreign.Ptr+import Data.Word (Word8())+import Foreign.C.String (peekCStringLen, withCString)+import Foreign.C.Types (CChar()) import Foreign.ForeignPtr-import Foreign.C.String-import Foreign.C.Types-import Foreign.Storable-import Foreign.Marshal.Array-import Data.Word-import System.IO.Unsafe+import Foreign.Marshal.Array (peekArray, withArray)+import Foreign.Ptr import System.IO-import System.IO.HVIO import System.IO.HVFS+import System.IO.HVIO+import System.IO.Unsafe (unsafeInterleaveIO) {- | Provides support for handling binary blocks with convenient types.@@ -103,7 +102,7 @@ instance BinaryConvertible Char where toBuf = withCString- fromBuf len func = + fromBuf len func = do fbuf <- mallocForeignPtrArray (len + 1) withForeignPtr fbuf handler where handler ptr =@@ -120,19 +119,18 @@ peekArray bytesread ptr --- . **************************************************--- . Binary Files--- . **************************************************-+-- **************************************************+-- Binary Files+-- ************************************************** {- | As a wrapper around the standard function 'System.IO.hPutBuf', this function takes a standard Haskell 'String' instead of the far less-convenient 'Ptr a'. The entire contents of the string will be written+convenient @Ptr a@. The entire contents of the string will be written as a binary buffer using 'hPutBuf'. The length of the output will be-the length of the passed String or list..+the length of the passed String or list. If it helps, you can thing of this function as being of type-@Handle -> String -> IO ()@. -}+@Handle -> String -> IO ()@ -} hPutBufStr :: (HVIO a, BinaryConvertible b) => a -> [b] -> IO () hPutBufStr f s = toBuf s (\cs -> vPutBuf f cs (length s)) @@ -159,7 +157,7 @@ {- | Like 'hGetBufStr', but guarantees that it will only return fewer than the requested number of bytes when EOF is encountered. -} hFullGetBufStr :: (HVIO a, BinaryConvertible b) => a -> Int -> IO [b]-hFullGetBufStr f 0 = return []+hFullGetBufStr _ 0 = return [] hFullGetBufStr f count = do thisstr <- hGetBufStr f count if thisstr == []@@ -186,9 +184,9 @@ hPutBufStr h x hPutBlocks h xs --- | An alias for 'hPutBlocks' 'stdout'+{- | An alias for 'hPutBlocks' 'stdout' putBlocks :: (BinaryConvertible b) => [[b]] -> IO ()-putBlocks = hPutBlocks stdout+putBlocks = hPutBlocks stdout -} {- | Returns a lazily-evaluated list of all blocks in the input file, as read by 'hGetBufStr'. There will be no 0-length block in this list.@@ -210,14 +208,13 @@ hGetBlocksUtil :: (HVIO a, BinaryConvertible b) => (a -> Int -> IO [b]) -> a -> Int -> IO [[b]] hGetBlocksUtil readfunc h count =- unsafeInterleaveIO (do+ unsafeInterleaveIO $ do block <- readfunc h count if block == [] then return [] else do remainder <- hGetBlocksUtil readfunc h count return (block : remainder)- ) {- | Binary block-based interaction. This is useful for scenarios that take binary blocks, manipulate them in some way, and then write them@@ -240,11 +237,11 @@ hFullBlockInteract = hBlockInteractUtil hFullGetBlocks -- | An alias for 'hFullBlockInteract' over 'stdin' and 'stdout'-fullBlockInteract :: (BinaryConvertible b, BinaryConvertible c) => +fullBlockInteract :: (BinaryConvertible b, BinaryConvertible c) => Int -> ([[b]] -> [[c]]) -> IO () fullBlockInteract x = hFullBlockInteract x stdin stdout -hBlockInteractUtil :: (HVIO a, HVIO d, BinaryConvertible b, BinaryConvertible c) => +hBlockInteractUtil :: (HVIO a, HVIO d, BinaryConvertible b, BinaryConvertible c) => (a -> Int -> IO [[b]]) -> Int -> a -> d -> ([[b]] -> [[c]]) -> IO () hBlockInteractUtil blockreader blocksize hin hout func =@@ -253,7 +250,7 @@ hPutBlocks hout (func blocks) {- | Copies everything from the input handle to the output handle using binary-blocks of the given size. This was once the following +blocks of the given size. This was once the following beautiful implementation: > hBlockCopy bs hin hout = hBlockInteract bs hin hout id@@ -264,9 +261,8 @@ In more recent versions of MissingH, it uses a more optimized routine that avoids ever having to convert the binary buffer at all. -}- hBlockCopy :: (HVIO a, HVIO b) => Int -> a -> b -> IO ()-hBlockCopy bs hin hout = +hBlockCopy bs hin hout = do (fbuf::ForeignPtr CChar) <- mallocForeignPtrArray (bs + 1) withForeignPtr fbuf handler where handler ptr =
src/System/IO/HVFS/Combinators.hs view
@@ -16,13 +16,12 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -}- {- | Module : System.IO.HVFS.Combinators Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -32,31 +31,25 @@ -} -module System.IO.HVFS.Combinators(- -- * Restrictions+module System.IO.HVFS.Combinators ( -- * Restrictions HVFSReadOnly(..),- HVFSChroot, newHVFSChroot- )-where+ HVFSChroot, newHVFSChroot)+ where -import System.IO.HVFS-import System.IO.HVIO-import System.IO.HVFS.InstanceHelpers import System.IO import System.IO.Error+import System.IO.HVFS+import System.IO.HVFS.InstanceHelpers (getFullPath) #ifndef mingw32_HOST_OS-import System.Posix.Files+import System.Posix.Files -- This actually needed? -Wall doesn't seem to think+ -- so, but I'm not sure... #endif-import System.Posix.Types-import System.Time-import System.Directory-import System.Path-import System.Path.NameManip+import System.Path (secureAbsNormPath)+import System.Path.NameManip (normalise_path) ---------------------------------------------------------------------- -- Providing read-only access ----------------------------------------------------------------------- {- | Restrict access to the underlying filesystem to be strictly read-only. Any write-type operations will cause an error. @@ -68,7 +61,8 @@ withro :: HVFS a => (a -> b) -> HVFSReadOnly a -> b withro f (HVFSReadOnly x) = f x -roerror h = +roerror :: (HVFS a) => HVFSReadOnly a -> IO c+roerror h = let err x = vRaiseError x permissionErrorType "Read-only virtual filesystem" Nothing in withro err h@@ -92,14 +86,13 @@ vCreateLink h _ _ = roerror h instance HVFSOpenable a => HVFSOpenable (HVFSReadOnly a) where- vOpen fh fp mode = + vOpen fh fp mode = case mode of ReadMode -> withro (\h -> vOpen h fp mode) fh _ -> roerror fh ---------------------------------------------------------------------- -- Restricting to a subdirectory ----------------------------------------------------------------------- {- | Access a subdirectory of a real filesystem as if it was the root of that filesystem. -} data HVFS a => HVFSChroot a = HVFSChroot String a@@ -121,16 +114,18 @@ (Just full) {- | Get the embedded object -}+dch :: (HVFS t) => HVFSChroot t -> t dch (HVFSChroot _ a) = a {- | Convert a local (chroot) path to a full path. -}-dch2fp mainh@(HVFSChroot fp h) locfp = +dch2fp, fp2dch :: (HVFS t) => HVFSChroot t -> String -> IO String+dch2fp mainh@(HVFSChroot fp h) locfp = do full <- case (head locfp) of '/' -> return (fp ++ locfp)- x -> do y <- getFullPath mainh locfp+ _ -> do y <- getFullPath mainh locfp return $ fp ++ y case secureAbsNormPath fp full of- Nothing -> vRaiseError h doesNotExistErrorType + Nothing -> vRaiseError h doesNotExistErrorType ("Trouble normalizing path in chroot") (Just (fp ++ "," ++ full)) Just x -> return x@@ -149,9 +144,11 @@ else let newpath2 = drop (length fp) newpath in return $ normalise_path ("/" ++ newpath2) -dch2fph func fh@(HVFSChroot fp h) locfp =+dch2fph :: (HVFS t) => (t -> String -> IO t1) -> HVFSChroot t -> [Char] -> IO t1+dch2fph func fh@(HVFSChroot _ h) locfp = do newfp <- dch2fp fh locfp func h newfp+ instance HVFS a => HVFS (HVFSChroot a) where vGetCurrentDirectory x = do fp <- vGetCurrentDirectory (dch x) fp2dch x fp
src/System/IO/HVFS/InstanceHelpers.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2004 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -41,20 +41,19 @@ MemoryEntry(..), -- * Utilities nice_slice, getFullPath,- getFullSlice- )-where+ getFullSlice)+ where++import Data.IORef (newIORef, readIORef, writeIORef, IORef())+import Data.List (genericLength)+import System.IO -- (ReadMode)+import System.IO.Error (doesNotExistErrorType, illegalOperationErrorType, permissionErrorType) import System.IO.HVFS-import Data.IORef-import Data.List-import System.Path-import System.Path.NameManip-import Control.Monad.Error-import System.IO.Error-import System.IO-import System.IO.HVIO+import System.IO.HVIO (newStreamReader)+import System.Path (absNormPath)+import System.Path.NameManip (slice_path) -{- | A simple "System.IO.HVFS.HVFSStat" +{- | A simple "System.IO.HVFS.HVFSStat" class that assumes that everything is either a file or a directory. -} data SimpleStat = SimpleStat {@@ -69,7 +68,6 @@ ---------------------------------------------------------------------- -- In-Memory Tree Types ----------------------------------------------------------------------- {- | The basic node of a 'MemoryVFS'. The String corresponds to the filename, and the entry to the contents. -} type MemoryNode = (String, MemoryEntry)@@ -81,8 +79,7 @@ {- | An in-memory read\/write filesystem. Think of it as a dynamically resizable ramdisk written in Haskell. -}--data MemoryVFS = MemoryVFS +data MemoryVFS = MemoryVFS { content :: IORef [MemoryNode], cwd :: IORef FilePath }@@ -124,7 +121,7 @@ getFullPath :: HVFS a => a -> String -> IO String getFullPath fs path = do cwd <- vGetCurrentDirectory fs- case absNormPath cwd path of+ case (absNormPath cwd path) of Nothing -> vRaiseError fs doesNotExistErrorType ("Trouble normalizing path " ++ path) (Just (cwd ++ "/" ++ path)) Just newpath -> return newpath@@ -145,7 +142,7 @@ t = tail sliced1 newh = if (h /= "/") && head h == '/' then tail h else h sliced2 = newh : t- + -- Walk the tree walk :: MemoryEntry -> [String] -> Either String MemoryEntry -- Empty list -- return the item we have@@ -153,14 +150,14 @@ -- Root directory -- return the item we have walk y ["/"] = Right y -- File but stuff: error- walk (MemoryFile _) (x : _) = - Left $ "Attempt to look up name " ++ x ++ " in file"- walk (MemoryDirectory y) (x : xs) =- let newentry = case lookup x y of- Nothing -> Left $ "Couldn't find entry " ++ x- Just z -> Right z+ walk (MemoryFile _) (z : _) =+ Left $ "Attempt to look up name " ++ z ++ " in file"+ walk (MemoryDirectory y) (z : zs) =+ let newentry = case lookup z y of+ Nothing -> Left $ "Couldn't find entry " ++ z+ Just a -> Right a in do newobj <- newentry- walk newobj xs+ walk newobj zs in do c <- readIORef $ content x case walk (MemoryDirectory c) (sliced2) of@@ -169,7 +166,7 @@ -- | Find an element on the tree, normalizing the path first getMelem :: MemoryVFS -> String -> IO MemoryEntry-getMelem x s = +getMelem x s = do base <- readIORef $ cwd x case absNormPath base s of Nothing -> vRaiseError x doesNotExistErrorType@@ -182,17 +179,17 @@ do curpath <- vGetCurrentDirectory x -- Make sure new dir is valid newdir <- getMelem x fp- case newdir of - (MemoryFile _) -> vRaiseError x doesNotExistErrorType + case newdir of+ (MemoryFile _) -> vRaiseError x doesNotExistErrorType ("Attempt to cwd to non-directory " ++ fp) (Just fp)- (MemoryDirectory _) -> + (MemoryDirectory _) -> case absNormPath curpath fp of Nothing -> -- should never happen due to above getMelem call vRaiseError x illegalOperationErrorType "Bad internal error" (Just fp) Just y -> writeIORef (cwd x) y- vGetFileStatus x fp = + vGetFileStatus x fp = do elem <- getMelem x fp case elem of (MemoryFile y) -> return $ HVFSStatEncap $@@ -210,9 +207,9 @@ MemoryDirectory c -> return $ map fst c instance HVFSOpenable MemoryVFS where- vOpen x fp (ReadMode) = + vOpen x fp (ReadMode) = do elem <- getMelem x fp- case elem of + case elem of MemoryDirectory _ -> vRaiseError x doesNotExistErrorType "Can't open a directory" (Just fp)
src/System/IO/HVIO.hs view
@@ -21,7 +21,7 @@ Copyright : Copyright (C) 2004-2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -87,7 +87,7 @@ 'MemoryBuffer' is a similar class, but with a different purpose. It provides a full interface like Handle (it implements 'HVIOReader', 'HVIOWriter',-and 'HVIOSeeker'). However, it maintains an in-memory buffer with the +and 'HVIOSeeker'). However, it maintains an in-memory buffer with the contents of the file, rather than an actual on-disk file. You can access the entire contents of this buffer at any time. This can be quite useful for testing I\/O code, or for cases where existing APIs use I\/O, but you@@ -111,7 +111,7 @@ -} module System.IO.HVIO(-- * Implementation Classes- HVIO(..), + HVIO(..), -- * Standard HVIO Implementations -- ** Handle@@ -121,7 +121,7 @@ StreamReader, newStreamReader, -- ** Memory Buffer- MemoryBuffer, newMemoryBuffer, + MemoryBuffer, newMemoryBuffer, mbDefaultCloseFunc, getMemoryBuffer, -- ** Haskell Pipe@@ -155,9 +155,8 @@ Implementators of writable objects must provide at least 'vPutChar' and 'vIsWritable'. -Implementators of seekable objects must provide at least +Implementators of seekable objects must provide at least 'vIsSeekable', 'vTell', and 'vSeek'.- -} class (Show a) => HVIO a where -- | Close a file@@ -201,12 +200,12 @@ a 'vGetChar' after a 'vGetContents' may return some undefined result instead of the error you would normally get. You should use caution to make sure your code doesn't fall into that trap,- or make sure to test your code with Handle or one of the + or make sure to test your code with Handle or one of the default instances defined in this module. Also, some implementations may essentially provide a complete close after a call to 'vGetContents'. The bottom line: after a call to 'vGetContents', you should do nothing else with the object save closing it with 'vClose'.- + For implementators, you are highly encouraged to provide a correct implementation. -} vGetContents :: a -> IO String@@ -225,7 +224,7 @@ -- | Write a string representation of the argument, plus a newline. vPrint :: Show b => a -> b -> IO () -- | Flush any output buffers.- -- Note: implementations should assure that a vFlush is automatically + -- Note: implementations should assure that a vFlush is automatically -- performed -- on file close, if necessary to ensure all data sent is written. vFlush :: a -> IO ()@@ -290,8 +289,8 @@ vIsReadable _ = return False - vGetLine h = - let loop accum = + vGetLine h =+ let loop accum = let func = do c <- vGetChar h case c of '\n' -> return accum@@ -306,7 +305,7 @@ x -> loop [x] vGetContents h =- let loop = + let loop = let func = do c <- vGetChar h next <- loop c `seq` return (c : next)@@ -315,7 +314,7 @@ in catch func handler in do loop- + vReady h = do vTestEOF h return True @@ -329,7 +328,7 @@ vPutStrLn h s = vPutStr h (s ++ "\n") vPrint h s = vPutStrLn h (show s)- + vFlush = vTestOpen @@ -341,13 +340,13 @@ vSeek h _ _ = vThrow h illegalOperationErrorType vTell h = vThrow h illegalOperationErrorType vGetChar h = vThrow h illegalOperationErrorType- + vPutBuf h buf len = do str <- peekCStringLen (castPtr buf, len) vPutStr h str - vGetBuf h b l = + vGetBuf h b l = worker b l 0 where worker _ 0 accum = return accum worker buf len accum =@@ -363,7 +362,6 @@ ---------------------------------------------------------------------- -- Handle instances ----------------------------------------------------------------------- instance HVIO Handle where vClose = hClose vIsEOF = hIsEOF@@ -393,7 +391,6 @@ vIsOpen = hIsOpen vIsClosed = hIsClosed - ---------------------------------------------------------------------- -- VIO Support ----------------------------------------------------------------------@@ -414,7 +411,6 @@ ---------------------------------------------------------------------- -- Stream Readers ----------------------------------------------------------------------- {- | Simulate I\/O based on a string buffer. When a 'StreamReader' is created, it is initialized based on the contents of@@ -434,7 +430,9 @@ newStreamReader s = do ref <- newIORef (True, s) return (StreamReader ref) +srv :: StreamReader -> VIOCloseSupport String srv (StreamReader x) = x+ instance Show StreamReader where show _ = "<StreamReader>" @@ -451,7 +449,7 @@ let retval = head c vioc_set (srv h) (tail c) return retval- + vGetContents h = do vTestEOF h c <- vioc_get (srv h) vClose h@@ -461,13 +459,12 @@ ---------------------------------------------------------------------- -- Buffers ----------------------------------------------------------------------- {- | A 'MemoryBuffer' simulates true I\/O, but uses an in-memory buffer instead of on-disk storage. It provides a full interface like Handle (it implements 'HVIOReader', 'HVIOWriter',-and 'HVIOSeeker'). However, it maintains an in-memory buffer with the +and 'HVIOSeeker'). However, it maintains an in-memory buffer with the contents of the file, rather than an actual on-disk file. You can access the entire contents of this buffer at any time. This can be quite useful for testing I\/O code, or for cases where existing APIs use I\/O, but you@@ -501,9 +498,10 @@ mbDefaultCloseFunc :: String -> IO () mbDefaultCloseFunc _ = return () +vrv :: MemoryBuffer -> VIOCloseSupport (Int, String) vrv (MemoryBuffer _ x) = x -{- | Grab the entire contents of the buffer as a string. +{- | Grab the entire contents of the buffer as a string. Unlike 'vGetContents', this has no effect on the open status of the item, the EOF status, or the current position of the file pointer. -} getMemoryBuffer :: MemoryBuffer -> IO String@@ -546,9 +544,9 @@ vIsWritable _ = return True vTell h = do v <- vioc_get (vrv h) return . fromIntegral $ (fst v)- vSeek h seekmode seekposp = + vSeek h seekmode seekposp = do (pos, buf) <- vioc_get (vrv h)- let seekpos = fromInteger seekposp + let seekpos = fromInteger seekposp let newpos = case seekmode of AbsoluteSeek -> seekpos RelativeSeek -> pos + seekpos@@ -562,7 +560,6 @@ ---------------------------------------------------------------------- -- Pipes ----------------------------------------------------------------------- {- | Create a Haskell pipe. These pipes are analogous to the Unix@@ -574,9 +571,7 @@ pipes are implemented completely with existing Haskell threading primitives, and require no special operating system support. Unlike Unix pipes, these pipes cannot be used across a fork(). Also unlike Unix pipes, these pipes-are portable and interact well with Haskell threads.--}-+are portable and interact well with Haskell threads. -} newHVIOPipe :: IO (PipeReader, PipeWriter) newHVIOPipe = do mv <- newEmptyMVar readerref <- newIORef (True, mv)@@ -584,7 +579,7 @@ writerref <- newIORef (True, reader) return (reader, PipeWriter writerref) -data PipeBit = PipeBit Char +data PipeBit = PipeBit Char | PipeEOF deriving (Eq, Show) @@ -599,12 +594,13 @@ ------------------------------ -- Pipe Reader -------------------------------+prv :: PipeReader -> VIOCloseSupport (MVar PipeBit) prv (PipeReader x) = x instance Show PipeReader where- show x = "<PipeReader>"+ show _ = "<PipeReader>" +pr_getc :: PipeReader -> IO PipeBit pr_getc h = do mv <- vioc_get (prv h) takeMVar mv @@ -618,11 +614,11 @@ vGetChar h = do vTestEOF h c <- pr_getc h- case c of + case c of PipeBit x -> return x -- vTestEOF should eliminate this case _ -> fail "Internal error in HVIOReader vGetChar"- vGetContents h = + vGetContents h = let loop = do c <- pr_getc h case c of PipeEOF -> return []@@ -631,16 +627,17 @@ in do vTestEOF h loop vIsReadable _ = return True- + ------------------------------ -- Pipe Writer -------------------------------+pwv :: PipeWriter -> VIOCloseSupport PipeReader pwv (PipeWriter x) = x++pwmv :: PipeWriter -> IO (MVar PipeBit) pwmv (PipeWriter x) = do mv1 <- vioc_get x vioc_get (prv mv1) - instance Show PipeWriter where show _ = "<PipeWriter>" @@ -659,7 +656,7 @@ vPutChar h c = do vTestOpen h child <- vioc_get (pwv h) copen <- vIsOpen child- if copen + if copen then do mv <- pwmv h putMVar mv (PipeBit c) else fail "PipeWriter: Couldn't write to pipe because child end is closed"
src/System/IO/StatCompat.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE CPP #-}-{- +{- Copyright (C) 2005,2006 John Goerzen <jgoerzen@complete.org> This program is free software; you can redistribute it and/or modify@@ -22,7 +22,7 @@ Copyright : Copyright (C) 2005-2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -32,24 +32,22 @@ Copyright (c) 2005-2006 John Goerzen, jgoerzen\@complete.org -} -module System.IO.StatCompat +module System.IO.StatCompat where import System.Posix.Types import System.Posix.Consts #ifndef mingw32_HOST_OS import System.Posix.Files(intersectFileModes) #endif-import Data.Bits+import Data.Bits ((.&.)) #ifdef mingw32_HOST_OS type LinkCount = Int type UserID = Int type GroupID = Int- #endif --data FileStatusCompat = +data FileStatusCompat = FileStatusCompat {deviceID :: DeviceID, fileID :: FileID, fileMode :: FileMode,@@ -62,10 +60,12 @@ modificationTime :: EpochTime, statusChangeTime :: EpochTime }- -sc_helper comp stat = ++sc_helper :: FileMode -> FileStatusCompat -> Bool+sc_helper comp stat = (fileMode stat `intersectFileModes` fileTypeModes) == comp- ++isBlockDevice,isCharacterDevice,isNamedPipe,isRegularFile,isDirectory,isSymbolicLink,isSocket :: FileStatusCompat -> Bool isBlockDevice = sc_helper blockSpecialMode isCharacterDevice = sc_helper characterSpecialMode isNamedPipe = sc_helper namedPipeMode
src/System/IO/Utils.hs view
@@ -21,11 +21,9 @@ Copyright : Copyright (C) 2004-2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable--asdf. -} module System.IO.Utils(-- * Entire File Handle Utilities@@ -46,15 +44,14 @@ optimizeForBatch, optimizeForInteraction ) where -import System.IO.Unsafe+import System.IO.Unsafe (unsafeInterleaveIO) import System.IO-import Data.List+import Data.List (genericLength) import System.IO.HVIO {- | Given a list of strings, output a line containing each item, adding newlines as appropriate. The list is not expected to have newlines already. -}- hPutStrLns :: HVIO a => a -> [String] -> IO () hPutStrLns h = mapM_ $ vPutStrLn h @@ -72,17 +69,16 @@ -} --- FIXME does hGetContents h >>= return.lines not work?+-- FIXME: does hGetContents h >>= return . lines not work? hGetLines :: HVIO a => a -> IO [String] hGetLines h = unsafeInterleaveIO (do ieof <- vIsEOF h- if (ieof) + if (ieof) then return [] else do line <- vGetLine h remainder <- hGetLines h- return (line : remainder)- )+ return (line : remainder)) {- | This is similar to the built-in 'System.IO.interact', but works@@ -117,7 +113,7 @@ One could view this function like this: -> hLineInteract finput foutput func = +> hLineInteract finput foutput func = > let newf = unlines . func . lines in > hInteract finput foutput newf @@ -128,7 +124,6 @@ > lines <- hGetLines finput > hPutStrLns foutput (func lines) -}- hLineInteract :: (HVIO a, HVIO b) => a -> b -> ([String] -> [String]) -> IO () hLineInteract finput foutput func = do@@ -146,8 +141,7 @@ {- | Copies from one handle to another in raw mode (using hGetContents). Takes a function to provide progress updates to the user. -}--hCopyProgress :: (HVIO b, HVIO c, Integral a) => +hCopyProgress :: (HVIO b, HVIO c, Integral a) => b -- ^ Input handle -> c -- ^ Output handle -> (Maybe a -> Integer -> Bool -> IO ()) -- ^ Progress function -- the bool is always False unless this is the final call@@ -178,13 +172,11 @@ > hLineCopy hin hout = hLineInteract hin hout id -}- hLineCopy :: (HVIO a, HVIO b) => a -> b -> IO() hLineCopy hin hout = hLineInteract hin hout id {- | Copies from 'stdin' to 'stdout' using lines. An alias for 'hLineCopy' over 'stdin' and 'stdout'. -}- lineCopy :: IO () lineCopy = hLineCopy stdin stdout @@ -194,7 +186,6 @@ need to adjust them after the copy yourself. This function is implemented using 'hLineCopy' internally. -}- copyFileLinesToFile :: FilePath -> FilePath -> IO () copyFileLinesToFile infn outfn = do hin <- openFile infn ReadMode@@ -213,8 +204,7 @@ hSetBuffering stdout (BlockBuffering (Just 4096)) {- | Sets stdin and stdout to be line-buffered. This saves resources-on stdout, but not many on stdin, since it it still looking for newlines.--}+on stdout, but not many on stdin, since it it still looking for newlines. -} optimizeForInteraction :: IO () optimizeForInteraction = do hSetBuffering stdin LineBuffering
src/System/Path.hs view
@@ -22,7 +22,7 @@ Copyright : Copyright (C) 2004-2005 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -59,11 +59,9 @@ {- | Splits a pathname into a tuple representing the root of the name and the extension. The extension is considered to be all characters from the last dot after the last slash to the end. Either returned string may be empty. -}---- FIXME - See 6.4 API when released.-+-- FIXME: See 6.4 API when released. splitExt :: String -> (String, String)-splitExt path = +splitExt path = let dotindex = alwaysElemRIndex '.' path slashindex = alwaysElemRIndex '/' path in@@ -140,7 +138,7 @@ an exception. -} brackettmpdir :: String -> (String -> IO a) -> IO a brackettmpdir x action = do tmpdir <- mktmpdir x- finally (action tmpdir) + finally (action tmpdir) (recursiveRemove SystemFS tmpdir) {- | Changes the current working directory to the given path,
src/System/Path/Glob.hs view
@@ -20,7 +20,7 @@ Copyright : Copyright (C) 2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -31,15 +31,16 @@ -} -module System.Path.Glob(glob, vGlob) where-import Data.List.Utils-import System.IO+module System.Path.Glob (glob, vGlob)+ where+import Data.List.Utils (hasAny) import System.IO.HVFS-import System.FilePath-import Control.Exception-import System.Path.WildMatch-import Data.List+import System.FilePath (splitFileName)+import Control.Exception (tryJust, ioErrors)+import System.Path.WildMatch (wildCheckCase)+import Data.List (isSuffixOf) +hasWild :: String -> Bool hasWild = hasAny "*?[" {- | Takes a pattern. Returns a list of names that match that pattern.@@ -59,7 +60,7 @@ {- | Like 'glob', but works on both the system ("real") and HVFS virtual filesystems. -} vGlob :: HVFS a => a -> FilePath -> IO [FilePath]-vGlob fs fn = +vGlob fs fn = if not (hasWild fn) -- Don't try globbing if there are no wilds then do de <- vDoesExist fs fn if de@@ -79,17 +80,19 @@ return $ concat r else do r <- mapM expandNormalBase dirlist return $ concat r- + where (dirnameslash, basename) = splitFileName fn dirname = case dirnameslash of "/" -> "/" x -> if isSuffixOf "/" x then take (length x - 1) x else x+ expandWildBase :: FilePath -> IO [FilePath] expandWildBase dname = do dirglobs <- runGlob fs dname basename return $ map (\globfn -> dname ++ "/" ++ globfn) dirglobs+ expandNormalBase :: FilePath -> IO [FilePath] expandNormalBase dname = do isdir <- vDoesDirectoryExist fs dname@@ -103,7 +106,7 @@ runGlob fs "" patt = runGlob fs "." patt runGlob fs dirname patt = do r <- tryJust ioErrors (vGetDirectoryContents fs dirname)- case r of + case r of Left _ -> return [] Right names -> let matches = filter (wildCheckCase patt) $ names in if head patt == '.'
src/System/Path/NameManip.hs view
@@ -7,7 +7,7 @@ Copyright : Copyright (C) 2004 Volker Wysk License : GNU LGPL, version 2.1 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -18,8 +18,8 @@ module System.Path.NameManip where -import Data.List-import System.Directory+import Data.List (intersperse)+import System.Directory (getCurrentDirectory) {- | Split a path in components. Repeated \"@\/@\" characters don\'t lead to empty components. \"@.@\" path components are removed. If the path is absolute, the first component@@ -48,10 +48,10 @@ (c:cs) -> (('/':c):cs) _ -> slice_path' p where- slice_path' p = filter (\c -> c /= "" && c /= ".") (split p)+ slice_path' o = filter (\c -> c /= "" && c /= ".") (split o) split "" = []- split ('/':p) = "" : split p+ split ('/':o) = "" : split o split (x:xs) = case split xs of [] -> [[x]] (y:ys) -> ((x:y):ys)@@ -371,7 +371,7 @@ -} absolute_path :: String -- ^ The path to be made absolute -> IO String -- ^ Absulte path-absolute_path path@('/':p) = return path+absolute_path path@('/':_) = return path absolute_path path = do cwd <- getCurrentDirectory return (cwd ++ "/" ++ path)@@ -385,7 +385,7 @@ absolute_path_by :: String -- ^ The directory relative to which the path is made absolute -> String -- ^ The path to be made absolute -> String -- ^ Absolute path-absolute_path_by dir path@('/':p) = path+absolute_path_by _ path@('/':_) = path absolute_path_by dir path = dir ++ "/" ++ path @@ -399,7 +399,7 @@ absolute_path' :: String -- ^ The path to be made absolute -> String -- ^ The directory relative to which the path is made absolute -> String -- ^ Absolute path-absolute_path' path@('/':p) dir = path+absolute_path' path@('/':_) _ = path absolute_path' path dir = dir ++ "/" ++ path @@ -427,5 +427,5 @@ -} guess_dotdot :: String -- ^ Path to be normalised -> Maybe String -- ^ In case the path could be transformed, the normalised, @\"..\"@-component free form of the path.-guess_dotdot = +guess_dotdot = fmap unslice_path . guess_dotdot_comps . slice_path
src/System/Path/WildMatch.hs view
@@ -20,7 +20,7 @@ Copyright : Copyright (C) 2006 John Goerzen License : GNU GPL, version 2 or above - Maintainer : John Goerzen <jgoerzen@complete.org> + Maintainer : John Goerzen <jgoerzen@complete.org> Stability : provisional Portability: portable @@ -61,15 +61,13 @@ module System.Path.WildMatch (-- * Wildcard matching wildCheckCase,- wildToRegex- )--where+ wildToRegex)+ where import Text.Regex-import Data.String+import Data.String.Utils -{- | Convert a wildcard to an (uncompiled) regular expression. +{- | Convert a wildcard to an (uncompiled) regular expression. -} wildToRegex :: String -> String@@ -88,7 +86,6 @@ Just _ -> True -- This is SO MUCH CLEANER than the python implementation!- convwild :: String -> String convwild [] = [] convwild ('*':xs) = ".*" ++ convwild xs@@ -102,3 +99,4 @@ convpat ('\\':xs) = "\\\\" ++ convpat xs convpat (']':xs) = ']' : convwild xs convpat (x:xs) = x : convpat xs+convpat [] = []
src/System/Time/ParseDate.hs view
@@ -11,130 +11,126 @@ -} module System.Time.ParseDate (parseCalendarTime) where -import Control.Monad+import Control.Monad (liftM) import Data.Char (isSpace)-import Data.List (elemIndex)-import Data.Maybe (fromJust) import System.Locale import System.Time import Text.ParserCombinators.Parsec +{- | Parse a date string as formatted by 'formatCalendarTime'. --- | Parse a date string as formatted by 'formatCalendarTime'.------ The resulting 'CalendarTime' will only have those fields set that--- are represented by a format specifier in the format string, and those--- fields will be set to the values given in the date string.--- If the same field is specified multiple times, the rightmost--- occurence takes precedence.------ The resulting date is not neccessarily a valid date. For example,--- if there is no day of the week specifier in the format string,--- the value of 'ctWDay' will most likely be invalid.------ Format specifiers are % followed by some character. All other--- characters are treated literally. Whitespace in the format string--- matches zero or more arbitrary whitespace characters.------ Format specifiers marked with * are matched, but do not set any--- field in the output.------ Some of the format specifiers are marked as space-padded or --- zero-padded. Regardless of this, space-padded, zero-padded --- or unpadded inputs are accepted. Note that strings using --- unpadded fields without separating the fields may cause--- strange parsing.------ Supported format specfiers:------ [%%] a % character.------ [%a] locale's abbreviated weekday name (Sun ... Sat)------ [%A] locale's full weekday name (Sunday .. Saturday)------ [%b] locale's abbreviated month name (Jan..Dec)------ [%B] locale's full month name (January..December)------ [%c] locale's date and time format (Thu Mar 25 17:47:03 CET 2004)------ [%C] century [00-99]------ [%d] day of month, zero padded (01..31)------ [%D] date (%m\/%d\/%y)------ [%e] day of month, space padded ( 1..31)------ [%h] same as %b------ [%H] hour, 24-hour clock, zero padded (00..23)------ [%I] hour, 12-hour clock, zero padded (01..12)------ [%j] day of the year, zero padded (001..366)------ [%k] hour, 24-hour clock, space padded ( 0..23)------ [%l] hour, 12-hour clock, space padded ( 1..12)------ [%m] month, zero padded (01..12)------ [%M] minute, zero padded (00..59)------ [%n] a newline character------ [%p] locale's AM or PM indicator------ [%r] locale's 12-hour time format (hh:mm:ss AM\/PM)------ [%R] hours and minutes, 24-hour clock (hh:mm)------ [%s] * seconds since '00:00:00 1970-01-01 UTC'------ [%S] seconds, zero padded (00..59)------ [%t] a horizontal tab character------ [%T] time, 24-hour clock (hh:mm:ss)------ [%u] numeric day of the week (1=Monday, 7=Sunday)------ [%U] * week number, weeks starting on Sunday, zero padded (01-53)------ [%V] * week number (as per ISO-8601),--- week 1 is the first week with a Thursday,--- zero padded, (01-53)------ [%w] numeric day of the week, (0=Sunday, 6=Monday)------ [%W] * week number, weeks starting on Monday, zero padded (01-53)------ [%x] locale's preferred way of printing dates (%m\/%d\/%y)------ [%X] locale's preferred way of printing time. (%H:%M:%S)------ [%y] year, within century, zero padded (00..99)------ [%Y] year, including century. Not padded --- (this is probably a bug, but formatCalendarTime does--- it this way). (0-9999)------ [%Z] time zone abbreviation (e.g. CET) or RFC-822 style numeric --- timezone (-0500)-parseCalendarTime :: + The resulting 'CalendarTime' will only have those fields set that+ are represented by a format specifier in the format string, and those+ fields will be set to the values given in the date string.+ If the same field is specified multiple times, the rightmost+ occurence takes precedence.++ The resulting date is not neccessarily a valid date. For example,+ if there is no day of the week specifier in the format string,+ the value of 'ctWDay' will most likely be invalid.++ Format specifiers are % followed by some character. All other+ characters are treated literally. Whitespace in the format string+ matches zero or more arbitrary whitespace characters.++ Format specifiers marked with * are matched, but do not set any+ field in the output.++ Some of the format specifiers are marked as space-padded or+ zero-padded. Regardless of this, space-padded, zero-padded+ or unpadded inputs are accepted. Note that strings using+ unpadded fields without separating the fields may cause+ strange parsing.++ Supported format specfiers:++ [%%] a % character.++ [%a] locale's abbreviated weekday name (Sun ... Sat)++ [%A] locale's full weekday name (Sunday .. Saturday)++ [%b] locale's abbreviated month name (Jan..Dec)++ [%B] locale's full month name (January..December)++ [%c] locale's date and time format (Thu Mar 25 17:47:03 CET 2004)++ [%C] century [00-99]++ [%d] day of month, zero padded (01..31)++ [%D] date (%m\/%d\/%y)++ [%e] day of month, space padded ( 1..31)++ [%h] same as %b++ [%H] hour, 24-hour clock, zero padded (00..23)++ [%I] hour, 12-hour clock, zero padded (01..12)++ [%j] day of the year, zero padded (001..366)++ [%k] hour, 24-hour clock, space padded ( 0..23)++ [%l] hour, 12-hour clock, space padded ( 1..12)++ [%m] month, zero padded (01..12)++ [%M] minute, zero padded (00..59)++ [%n] a newline character++ [%p] locale's AM or PM indicator++ [%r] locale's 12-hour time format (hh:mm:ss AM\/PM)++ [%R] hours and minutes, 24-hour clock (hh:mm)++ [%s] * seconds since '00:00:00 1970-01-01 UTC'++ [%S] seconds, zero padded (00..59)++ [%t] a horizontal tab character++ [%T] time, 24-hour clock (hh:mm:ss)++ [%u] numeric day of the week (1=Monday, 7=Sunday)++ [%U] * week number, weeks starting on Sunday, zero padded (01-53)++ [%V] * week number (as per ISO-8601),+ week 1 is the first week with a Thursday,+ zero padded, (01-53)++ [%w] numeric day of the week, (0=Sunday, 6=Monday)++ [%W] * week number, weeks starting on Monday, zero padded (01-53)++ [%x] locale's preferred way of printing dates (%m\/%d\/%y)++ [%X] locale's preferred way of printing time. (%H:%M:%S)++ [%y] year, within century, zero padded (00..99)++ [%Y] year, including century. Not padded+ (this is probably a bug, but formatCalendarTime does+ it this way). (0-9999)++ [%Z] time zone abbreviation (e.g. CET) or RFC-822 style numeric+ timezone (-0500) -}+parseCalendarTime :: TimeLocale -- ^ Time locale -> String -- ^ Date format -> String -- ^ String to parse -> Maybe CalendarTime -- ^ 'Nothing' if parsing failed.-parseCalendarTime l fmt s = +parseCalendarTime l fmt s = case runParser parser epoch "<date string>" s of- Left err -> Nothing- Right p -> Just p+ Left _ -> Nothing+ Right p -> Just p where parser = pCalendarTime l fmt >> getState - -- FIXME: verify input -- FIXME: years outside 1000-9999 probably don't work -- FIXME: what about extra whitespace in input?@@ -159,7 +155,7 @@ doFmt ('%':c:cs) = decode c >> doFmt cs doFmt (c:cs) = char c >> doFmt cs doFmt "" = return ()- + decode '%' = char '%' >> return () decode 'a' = (parseEnum $ map snd $ wDays l) >>= setWDay decode 'A' = (parseEnum $ map fst $ wDays l) >>= setWDay@@ -185,12 +181,12 @@ -- FIXME: strptime(3) accepts "arbitrary whitespace" for %n decode 'n' = char '\n' >> return () decode 'p' = do- x <- (string am >> return 0) <|> (string pm >> return 12)- updateHour (\h -> x + h `rem` 12)- where (am,pm) = amPm l + x <- (string am >> return 0) <|> (string pm >> return 12)+ updateHour (\h -> x + h `rem` 12)+ where (am,pm) = amPm l decode 'r' = doFmt (time12Fmt l) decode 'R' = doFmt "%H:%M"- -- FIXME: implement %s. + -- FIXME: implement %s. -- FIXME: implement %s in formatCalendarTime decode 's' = int >> return () decode 'S' = read2 >>= setSec@@ -198,16 +194,16 @@ decode 't' = char '\t' >> return () decode 'T' = doFmt "%H:%M:%S" decode 'u' = readN 1 >>= setWDay . toEnum . (\w -> if w == 7 then 0 else w)- -- FIXME: implement %U. + -- FIXME: implement %U. decode 'U' = read2 >> return ()- -- FIXME: implement %V. + -- FIXME: implement %V. decode 'V' = read2 >> return () decode 'w' = readN 1 >>= setWDay . toEnum -- FIXME: implement %W. decode 'W' = read2 >> return () decode 'x' = doFmt (dateFmt l) decode 'X' = doFmt (timeFmt l)- -- FIXME: should probably be zero padded, + -- FIXME: should probably be zero padded, -- need to change formatCalendarTime too decode 'Y' = int >>= setYear -- FIXME: maybe 04 should be 2004, not 1904?@@ -216,53 +212,56 @@ -- FIXME: set ctTZ when parsing timezone name and -- ctTZName when parsing offset decode 'Z' = tzname <|> tzoffset- where tzname = many1 (oneOf ['A'..'Z']) >>= setTZName - tzoffset = do - s <- sign- h <- read2- m <- read2- setTZ (s * (h * 3600 + m * 60))+ where tzname = many1 (oneOf ['A'..'Z']) >>= setTZName+ tzoffset = do+ s <- sign+ h <- read2+ m <- read2+ setTZ (s * (h * 3600 + m * 60)) -- following the example of strptime(3), -- whitespace matches zero or more whitespace -- characters in the input string decode c | isSpace c = spaces >> return () decode c = char c >> return () -- epoch :: CalendarTime epoch = CalendarTime {- ctYear = 1970,- ctMonth = January,- ctDay = 1,- ctHour = 0,- ctMin = 0,- ctSec = 0,- ctPicosec = 0,- ctWDay = Thursday,- ctYDay = 1,- ctTZName = "UTC",- ctTZ = 0,- ctIsDST = False- }+ ctYear = 1970,+ ctMonth = January,+ ctDay = 1,+ ctHour = 0,+ ctMin = 0,+ ctSec = 0,+ ctPicosec = 0,+ ctWDay = Thursday,+ ctYDay = 1,+ ctTZName = "UTC",+ ctTZ = 0,+ ctIsDST = False+ } parseEnum :: Enum a => [String] -> CharParser st a parseEnum ss = choice (zipWith tryString ss (enumFrom (toEnum 0))) where tryString s x = try (string s) >> return x -+setYear,setDay,setHour,setHour12,setMin,setSec,setYDay,setTZ :: Int -> GenParser tok CalendarTime () setYear x = updateState (\t -> t{ ctYear = x })+setMonth :: Month -> GenParser tok CalendarTime () setMonth x = updateState (\t -> t{ ctMonth = x }) setDay x = updateState (\t -> t{ ctDay = x }) setHour x = updateState (\t -> t{ ctHour = x }) setMin x = updateState (\t -> t{ ctMin = x }) setSec x = updateState (\t -> t{ ctSec = x })+setWDay :: Day -> GenParser tok CalendarTime () setWDay x = updateState (\t -> t{ ctWDay = x }) setYDay x = updateState (\t -> t{ ctYDay = x })+setTZName :: String -> GenParser tok CalendarTime () setTZName x = updateState (\t -> t{ ctTZName = x }) setTZ x = updateState (\t -> t{ ctTZ = x }) +updateYear :: (Int -> Int) -> GenParser tok CalendarTime () updateYear f = updateState (\t -> t{ ctYear = f (ctYear t) })+updateHour :: (Int -> Int) -> GenParser tok CalendarTime () updateHour f = updateState (\t -> t{ ctHour = f (ctHour t) }) setHour12 x = updateHour (\h -> (h `quot` 12) * 12 + from12 x)@@ -272,10 +271,10 @@ read2 = readN 2 read3 = readN 3 --- | Read up to a given number of digits, optionally left-padded +-- | Read up to a given number of digits, optionally left-padded -- with whitespace and interpret them as an 'Int'. readN :: Int -> GenParser Char st Int-readN n = +readN n = liftM read (spaces >> choice [try (count m digit) | m <- [n,n-1..1]]) int :: GenParser Char st Int
src/Test/HUnit/Utils.hs view
@@ -30,16 +30,14 @@ Written by John Goerzen, jgoerzen\@complete.org -} ---module Test.HUnit.Utils(assertRaises, mapassertEqual, qccheck, qctest) where+module Test.HUnit.Utils (assertRaises, mapassertEqual, qccheck, qctest)+ where import Test.HUnit import Test.QuickCheck as QC import qualified Control.Exception import System.Random {- | Asserts that a specific exception is raised by a given action. -}- assertRaises :: Show a => String -> Control.Exception.Exception -> IO a -> IO () assertRaises msg selector action = let thetest e = if e == selector then return ()@@ -49,18 +47,17 @@ r <- Control.Exception.try action case r of Left e -> thetest e- Right x -> assertFailure $ msg ++ "\nReceived no exception, but was expecting exception: " ++ (show selector)+ Right _ -> assertFailure $ msg ++ "\nReceived no exception, but was expecting exception: " ++ (show selector) mapassertEqual :: (Show b, Eq b) => String -> (a -> b) -> [(a, b)] -> [Test]-mapassertEqual descrip func [] = []+mapassertEqual _ _ [] = [] mapassertEqual descrip func ((inp,result):xs) = (TestCase $ assertEqual descrip result (func inp)) : mapassertEqual descrip func xs -- * Turn QuickCheck tests into HUnit tests---- |qccheck turns the quickcheck test into an hunit test-qccheck :: (QC.Testable a) => - Config -- ^ quickcheck config+-- | qccheck turns the quickcheck test into an hunit test+qccheck :: (QC.Testable a) =>+ QC.Config -- ^ quickcheck config -> String -- ^ label for the property -> a -- ^ quickcheck property -> Test@@ -69,14 +66,14 @@ do rnd <- newStdGen tests config (evaluate property) rnd 0 0 [] --- |qctest is equivalent to 'qccheck defaultConfig'+-- | qctest is equivalent to 'qccheck defaultConfig' qctest :: (QC.Testable a) => String -> a -> Test-qctest lbl property = qccheck defaultConfig lbl property+qctest lbl = qccheck defaultConfig lbl --- |modified version of the tests function from Test.QuickCheck-tests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO () +-- | modified version of the tests function from Test.QuickCheck+tests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO () tests config gen rnd0 ntest nfail stamps- | ntest == configMaxTest config = return () + | ntest == configMaxTest config = return () | nfail == configMaxFail config = assertFailure $ "Arguments exhausted after " ++ show ntest ++ " tests." | otherwise = do putStr (configEvery config ntest (arguments result))
− testsrc/runtests.hs
@@ -1,25 +0,0 @@-{- arch-tag: Test runner-Copyright (C) 2004 John Goerzen <jgoerzen@complete.org>--This program is free software; you can redistribute it and/or modify-it under the terms of the GNU General Public License as published by-the Free Software Foundation; either version 2 of the License, or-(at your option) any later version.--This program is distributed in the hope that it will be useful,-but WITHOUT ANY WARRANTY; without even the implied warranty of-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-GNU General Public License for more details.--You should have received a copy of the GNU General Public License-along with this program; if not, write to the Free Software-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA--}--module Main where --import Test.HUnit-import Tests--main = runTestTT tests-