Extra 1.42 → 1.45
raw patch · 12 files changed
+108/−645 lines, 12 filesdep +pureMD5dep ~Unixutilsdep ~networknew-uploader
Dependencies added: pureMD5
Dependency ranges changed: Unixutils, network
Files
- Extra.cabal +42/−6
- Extra/CIO.hs +0/−308
- Extra/Either.hs +3/−15
- Extra/Files.hs +3/−3
- Extra/IO.hs +1/−1
- Extra/List.hs +1/−0
- Extra/Lock.hs +27/−27
- Extra/Misc.hs +16/−151
- Extra/SSH.hs +14/−18
- Extra/TIO.hs +0/−112
- Extra/URI.hs +0/−3
- Test/QUnit.hs +1/−1
Extra.cabal view
@@ -1,5 +1,5 @@ Name: Extra-Version: 1.42+Version: 1.45 License: BSD3 License-File: COPYING Author: David Fox@@ -7,17 +7,53 @@ Description: A hodge-podge of functions and modules that do not have a better home Maintainer: David Fox <david@seereason.com> Homepage: http://src.seereason.com/haskell-extra-Build-Depends: base < 5, unix, regex-compat, time >= 1.1, Unixutils >= 1.32 , mtl, network, pretty, directory, bytestring, process, containers, old-time, old-locale, QuickCheck >= 2 && < 3, HUnit, random, filepath, zlib, bzlib+Build-Depends:+ base < 5,+ bytestring,+ bzlib,+ containers,+ directory,+ filepath,+ HUnit,+ mtl,+ network >= 2.4,+ old-locale,+ old-time,+ pretty,+ process,+ pureMD5,+ QuickCheck >= 2 && < 3,+ random,+ regex-compat,+ time >= 1.1,+ unix,+ Unixutils >= 1.51,+ zlib Synopsis: A grab bag of modules. ghc-options: -O2 -W C-Sources: cbits/gwinsz.c Include-Dirs: cbits Install-Includes: gwinsz.h Exposed-modules:- Extra.Bool, Extra.Either, Extra.Exit, Extra.Files,- Extra.GPGSign,Extra.List, Extra.HughesPJ, Extra.Lock, Extra.Misc, Extra.Net, Extra.SSH,- Extra.Time, Extra.Terminal, Extra.CIO, Extra.TIO, Extra.IO, Extra.URI, Extra.URIQuery,- Test.QUnit, Test.QuickCheck.Properties, Extra.IOThread+ Extra.Bool,+ Extra.Either,+ Extra.Exit,+ Extra.Files,+ Extra.GPGSign,+ Extra.List,+ Extra.HughesPJ,+ Extra.Lock,+ Extra.Misc,+ Extra.Net,+ Extra.SSH,+ Extra.Time,+ Extra.Terminal,+ Extra.IO,+ Extra.URI,+ Extra.URIQuery,+ Test.QUnit,+ Test.QuickCheck.Properties,+ Extra.IOThread Build-Type: Simple -- For more complex build options see:
− Extra/CIO.hs
@@ -1,308 +0,0 @@--- |CIO is a type class for the TIO monad, which tracks the cursor--- position of the console so that indentation and prefixes can be--- added to the output. TIO also has a style component which lets you--- control the output verbosity and the appearance of the prefix.--- There is an instance for the regular IO monad which doesn't use any--- of these features, to allow functions which do not use the TIO--- monad call functions in the Debian library.------ NOTE: a copy of this library is in the Extra library as--- well. Please update both locations.--- --- This code is provided for backwards compatibility, I don't--- endorse its use in new projects.-module Extra.CIO - ( -- * The CIO class- CIO(..)-- -- * Style constructors and transformers- , TStyle(..)- , defStyle- , withStyle-- , setVerbosity- , addVerbosity- , setPrefix- , addPrefix- , appPrefix- , setPrefixes- , addPrefixes- , appPrefixes- , hGetPrefix-- -- * Output functions- , putStr- , ePutStr- , vPutStr- , vEPutStr-- , hPutChar- , putChar- , ePutChar- , vHPutChar- , vPutChar- , vEPutChar-- , hPutStrBl- , putStrBl- , ePutStrBl- , vHPutStrBl- , vPutStrBl- , vEPutStrBl-- , hPutStrLn- , putStrLn- , ePutStrLn- , vHPutStrLn- , vPutStrLn- , vEPutStrLn-- , bol- , eBOL- , vHBOL- , vBOL- , vEBOL-- , hColor- , blue- , green- , red- , magenta- ) where--import Prelude hiding (putStr, putChar, putStrLn)-import qualified System.IO as IO-import Control.Monad.Trans-import Control.Exception---- |Class representing ways of doing console (terminal?) output.-class MonadIO m => CIO m where- -- |Write output to a handle.- hPutStr :: IO.Handle -> String -> m ()- -- |If we are not already at the beginning of a line, move the cursor- -- to the beginning of the next one.- hBOL :: IO.Handle -> m ()- -- |Return the \"effective verbosity\" for a task. If the argument- -- is 2 it means the caller is computing ev for a task that- -- normally does output when the verbosity level is 2 or higher.- -- If the verbosity of the current style is 1, then the ev or- -- effective verbosity is 2-1 = -1, so the output should be- -- quieter.- ev :: Int -> m Int- -- |Modify the current output style.- setStyle :: (TStyle -> TStyle) -> m a -> m a- -- |Implementation of try for this monad- tryCIO :: m a -> m (Either SomeException a)---- |A record used to hold the output style information for a task.--- This The prefixes that will appear at the beginning of each line,--- and the desired verbosity level. Suggested verbosity level policy:------ * -1: No output of any kind, as if you were directing all output to /dev/null--- --- * 0: Error output only, suitable for a run whose log you might examine later--- --- * 1: casual progress reporting - if you were running on a console but didn't--- expect anything to go wrong------ * 2: detailed progress reporting - show more progress, particularly things--- that might fail during the normal operation of the autobuilder: patches--- that fail to apply, dpkg-buildpackage runs that return errors, etc.------ * 3: Debugging output - use this level or higher if you suspect the--- autobuilder itself is failing, or you are doing development work on--- the autobuilder.-data TStyle- = TStyle { prefix :: String -- ^ Add this string at the beginning of each line- , verbosity :: Int -- ^ Ignore v functions whose argument is more than this- , hPrefix :: [(IO.Handle, String)] -- ^ Per-handle prefix- } deriving Show--defStyle :: TStyle-defStyle = TStyle { prefix = ": "- , hPrefix = []- , verbosity = 0- }---- |Use a new style for the TIO action-withStyle :: CIO m => TStyle -> m a -> m a-withStyle newStyle = setStyle (const newStyle)--setVerbosity :: Int -> TStyle -> TStyle-setVerbosity n style = style {verbosity = n}-addVerbosity :: Int -> TStyle -> TStyle-addVerbosity n style = setVerbosity (n + verbosity style) style---- |Set the output style for a handle to prefixed.-setPrefix :: String -> TStyle -> TStyle-setPrefix prefix style = style {prefix = prefix}---- | Prepend some text to the prefix.-addPrefix :: String -> TStyle -> TStyle-addPrefix newPrefix style = style {prefix = newPrefix ++ prefix style}---- | Append some text to the prefix.-appPrefix :: String -> TStyle -> TStyle-appPrefix newPrefix style = style {prefix = prefix style ++ newPrefix}--hSetPrefix :: IO.Handle -> String -> TStyle -> TStyle-hSetPrefix handle string style =- style {hPrefix = (handle, string) : filter ((/= handle) . fst) (hPrefix style)}--hAddPrefix :: IO.Handle -> String -> TStyle -> TStyle-hAddPrefix handle string style =- hSetPrefix handle (string ++ prefix) style- where prefix = maybe "" id (lookup handle (hPrefix style))--hAppPrefix :: IO.Handle -> String -> TStyle -> TStyle-hAppPrefix handle string style =- hSetPrefix handle (prefix ++ string) style- where prefix = maybe "" id (lookup handle (hPrefix style))---- |Get the current prefix for a particular handle-hGetPrefix :: IO.Handle -> TStyle -> String-hGetPrefix handle style = prefix style ++ maybe "" id (lookup handle (hPrefix style))---- |Set the output style for the stdout and stderr handle to prefixed,--- using whatever prefixes were most recently set (default is [1] and [2].)-setPrefixes :: String -> String -> TStyle -> TStyle-setPrefixes stdoutPrefix stderrPrefix style =- hSetPrefix IO.stdout stdoutPrefix . hSetPrefix IO.stderr stderrPrefix $ style---- |Switch to prefixed mode and modify both the stdout and stderr prefixes.-addPrefixes :: String -> String -> TStyle -> TStyle-addPrefixes oPrefix ePrefix style =- hAddPrefix IO.stdout oPrefix . hAddPrefix IO.stderr ePrefix $ style--appPrefixes :: String -> String -> TStyle -> TStyle-appPrefixes oPrefix ePrefix style =- hAppPrefix IO.stdout oPrefix . hAppPrefix IO.stderr ePrefix $ style---- |Perform an action if the effective verbosity level is >= 0,--- otherwise return the default value d.-vIO :: CIO m => Int -> a -> m a -> m a-vIO v d f =- do v' <- ev v- if v' >= 0 then f else return d---- |Write a string to stdout.-putStr :: CIO m => String -> m ()-putStr = hPutStr IO.stdout---- |Write a string to stderr.-ePutStr :: CIO m => String -> m ()-ePutStr = hPutStr IO.stderr---- |Verbosity controlled version of ePutStr-vEPutStr :: CIO m => Int -> String -> m ()-vEPutStr = vHPutStr IO.stderr---- |Write a string to stdout depending on the verbosity level.-vPutStr :: CIO m => Int -> String -> m ()-vPutStr = vHPutStr IO.stdout---- |Write a character.-hPutChar :: CIO m => IO.Handle -> Char -> m ()-hPutChar h c = hPutStr h [c]---- |Write a character to stdout.-putChar :: CIO m => Char -> m ()-putChar = hPutChar IO.stdout---- |Write a character to stderr.-ePutChar :: CIO m => Char -> m ()-ePutChar = hPutChar IO.stderr---- |Verbosity controlled version of hPutStr-vHPutStr :: CIO m => IO.Handle -> Int -> String -> m ()-vHPutStr h v s = vIO v () (hPutStr h s)---- |Verbosity controlled version of hPutChar.-vHPutChar :: CIO m => IO.Handle -> Int -> Char -> m ()-vHPutChar h v c = vHPutStr h v [c]---- |Verbosity controlled version of putChar-vPutChar :: CIO m => Int -> Char -> m ()-vPutChar = vHPutChar IO.stdout---- |Verbosity controlled version of ePutChar-vEPutChar :: CIO m => Int -> Char -> m ()-vEPutChar = vHPutChar IO.stderr---- |Move to beginning of next line (if necessary) and output a string.-hPutStrBl :: CIO m => IO.Handle -> String -> m ()-hPutStrBl h s = hBOL h >> hPutStr h s---- |hPutStrBl to stdout.-putStrBl :: CIO m => String -> m ()-putStrBl = hPutStrBl IO.stdout---- |hPutStrBl to stderr.-ePutStrBl :: CIO m => String -> m ()-ePutStrBl = hPutStrBl IO.stderr---- |Verbosity controlled version of hPutStrBl-vHPutStrBl :: CIO m => IO.Handle -> Int -> String -> m ()-vHPutStrBl h v s = vHBOL h v >> vHPutStr h v s---- |Verbosity controlled version of putStrBl-vPutStrBl :: CIO m => Int -> String -> m ()-vPutStrBl = vHPutStrBl IO.stdout ---- |Verbosity controlled version of ePutStrBl-vEPutStrBl :: CIO m => Int -> String -> m ()-vEPutStrBl = vHPutStrBl IO.stderr---- |Write a newline character and a string.-hPutStrLn :: CIO m => IO.Handle -> String -> m ()-hPutStrLn h s = hBOL h >> hPutStr h s---- |hPutStrLn to stdout.-putStrLn :: CIO m => String -> m ()-putStrLn s = hPutStrLn IO.stdout s---- |hPutStrLn to stderr.-ePutStrLn :: CIO m => String -> m ()-ePutStrLn = hPutStrLn IO.stderr---- |Verbosity controlled version of hPutStrLn.-vHPutStrLn :: CIO m => IO.Handle -> Int -> String -> m ()-vHPutStrLn h v s = vHBOL h v >> vHPutStr h v s---- |Verbosity controlled version of putStrLn-vPutStrLn :: CIO m => Int -> String -> m ()-vPutStrLn = vHPutStrLn IO.stdout ---- |Verbosity controlled version of ePutStrLn-vEPutStrLn :: CIO m => Int -> String -> m ()-vEPutStrLn = vHPutStrLn IO.stderr---- |hBOL to stdout.-bol :: CIO m => m ()-bol = hBOL IO.stdout---- |hBOL to stderr.-eBOL :: CIO m => m ()-eBOL = hBOL IO.stderr--vHBOL :: CIO m => IO.Handle -> Int -> m ()-vHBOL h v = vIO v () (hBOL h)---- |Verbosity controlled version of BOL-vBOL :: CIO m => Int -> m ()-vBOL = vHBOL IO.stdout---- |Verbosity controlled version of eBOL-vEBOL :: CIO m => Int -> m ()-vEBOL = vHBOL IO.stderr---- These only work in a terminal, not in an emacs shell.-hColor h s = case h of- _ | h == IO.stdout -> green s- _ | h == IO.stdout -> red s- _ -> magenta s--blue s = "\ESC[34m" ++ s ++ "\ESC[30m"-green s = "\ESC[32m" ++ s ++ "\ESC[30m"-red s = "\ESC[31m" ++ s ++ "\ESC[30m"-magenta s = "\ESC[35m" ++ s ++ "\ESC[30m"
Extra/Either.hs view
@@ -1,10 +1,6 @@ module Extra.Either where -lefts :: [Either a b] -> [a]-lefts xs = fst (partitionEithers xs)--rights :: [Either a b] -> [b]-rights xs = snd (partitionEithers xs)+import Data.Either (partitionEithers, rights) isRight (Right _) = True isRight (Left _) = False@@ -18,15 +14,7 @@ ([], rs) -> Right rs (ls, _) -> Left ls --- |Return a pair of the lefts and the rights-partitionEithers :: [Either a b] -> ([a], [b])-partitionEithers xs =- part ([], []) (reverse xs)- where- part (ls, rs) [] = (ls, rs)- part (ls, rs) (Left l : more) = part (l : ls, rs) more- part (ls, rs) (Right r : more) = part (ls, r : rs) more---- Deprecated+{-# DEPRECATED rightOnly "Use rights" #-} rightOnly = rights+{-# DEPRECATED eitherFromList "Use concatEithers" #-} eitherFromList = concatEithers
Extra/Files.hs view
@@ -24,7 +24,7 @@ import qualified Codec.Compression.GZip as GZip import qualified Codec.Compression.BZip as BZip-import Control.Exception hiding (catch)+import Control.Exception import Control.Monad import qualified Data.ByteString.Lazy as B import Data.List@@ -151,7 +151,7 @@ -- |like removeLink, but does not fail if link did not exist forceRemoveLink :: FilePath -> IO ()-forceRemoveLink fp = removeLink fp `Prelude.catch` (\e -> unless (isDoesNotExistError e) (ioError e))+forceRemoveLink fp = removeLink fp `Control.Exception.catch` (\e -> unless (isDoesNotExistError e) (ioError e)) -- | Write out three versions of a file, regular, gzipped, and bzip2ed. writeAndZipFileWithBackup :: FilePath -> B.ByteString -> IO (Either [String] ())@@ -242,7 +242,7 @@ f where f :: IO ()- f = removeFile path `catch` (\ e -> if isDoesNotExistError e then return () else ioError e) >> writeFile path text+ f = removeFile path `Control.Exception.catch` (\ e -> if isDoesNotExistError e then return () else ioError e) >> writeFile path text -- Try something n times, returning the first Right or the last Left -- if it never succeeds. Sleep between tries.
Extra/IO.hs view
@@ -1,4 +1,4 @@-module Extra.IO where+module Extra.IO {-# DEPRECATED "CIO is deprecated" #-} where import Extra.CIO import qualified System.IO as IO
Extra/List.hs view
@@ -16,6 +16,7 @@ import Control.Monad import Data.List +{-# DEPRECATED consperse "Use intercalate" #-} consperse :: [a] -> [[a]] -> [a] -- ^ The mighty consperse function - e.g. consperse "," ["a", "b"] -> "a,b" -- consperse = MissingH.List.join
Extra/Lock.hs view
@@ -6,49 +6,44 @@ import Control.Exception import Control.Monad.RWS+import Prelude hiding (catch) import System.Directory import System.IO-import System.IO.Error hiding (try)+import System.IO.Error hiding (try, catch) import System.Posix.Files import System.Posix.IO import System.Posix.Unistd ---withLock :: (MonadIO m) => FilePath -> m a -> m (Either Exception a)+withLock :: (MonadIO m) => FilePath -> m a -> m a withLock path task =- liftIO checkLock >>= liftIO . takeLock >>= doTask task >>= liftIO . dropLock+ liftIO (checkLock >> takeLock) >> task >>= \ result -> liftIO dropLock >> return result where -- Return True if file is locked by a running process, false otherwise --checkLock :: IO (Either Exception ())- checkLock = try (readFile path) >>= either (return . checkReadError) (processRunning . lines)- checkReadError (e :: IOException) | isDoesNotExistError e = (Right ())- checkReadError e = Left e+ checkLock = readFile path `catch` checkReadError >>= processRunning . lines+ checkReadError :: IOError -> IO String+ checkReadError e | isDoesNotExistError e = return ""+ checkReadError e = throw e+ processRunning :: [String] -> IO () processRunning (pid : _) = do exists <- doesDirectoryExist ("/proc/" ++ pid) case exists of- True -> return (Left (lockedBy pid))+ True -> throw (lockedBy pid path) False -> breakLock processRunning [] = breakLock- lockedBy pid = mkIOError alreadyInUseErrorType ("Locked by " ++ pid) Nothing (Just path)- breakLock = do try (removeFile path) >>= return . either checkBreakError (const (Right ()))- checkBreakError (e :: IOException) | isDoesNotExistError e = (Right ())- checkBreakError e = Left e- --takeLock :: Either Exception () -> IO (Either Exception ())- takeLock (Right ()) =+ breakLock = removeFile path `catch` checkBreakError+ checkBreakError (e :: IOException) | isDoesNotExistError e = return ()+ checkBreakError e = throw e+ takeLock :: IO ()+ takeLock = -- Try to create the lock file in exclusive mode, if this -- succeeds then we have a lock. Then write the process ID -- into the lock and close.- try (openFd path ReadWrite (Just 0o600) (defaultFileFlags {exclusive = True, trunc = True})) >>=- either (return . Left)- (\ fd -> do h <- fdToHandle fd- processID >>= hPutStrLn h >> hClose h >> return (Right ()))- takeLock (Left e) = return (Left e)- doTask task (Right ()) = task >>= return . Right- doTask _ (Left e) = return (Left e)- dropLock (Right a) = try (removeFile path) >>= return . checkDrop a- dropLock (Left e) = return (Left e)- checkDrop a (Right ()) = Right a- checkDrop a (Left (e :: IOException)) | isDoesNotExistError e = Right a- checkDrop _ (Left e) = Left e+ openFd path ReadWrite (Just 0o600) (defaultFileFlags {exclusive = True, trunc = True}) >>=+ fdToHandle >>= \ h -> processID >>= hPutStrLn h >> hClose h+ dropLock = removeFile path `catch` checkDrop+ checkDrop (e :: IOException) | isDoesNotExistError e = return ()+ checkDrop e = throw e -- |Like withLock, but instead of giving up immediately, try n times -- with a wait between each.@@ -56,8 +51,13 @@ awaitLock tries usecs path task = attempt 0 where - attempt n | n >= tries = return (Left (ErrorCall "Too many failures"))- attempt n = withLock path task >>= either (\ _ -> liftIO (usleep usecs) >> attempt (n + 1)) (return . Right)+ attempt n | n >= tries = error "awaitLock: too many failures"+ attempt n = withLock path task `catch` checkLockError+ where+ checkLockError e | isAlreadyInUseError e = liftIO (usleep usecs) >> attempt (n + 1)+ checkLockError e = throw e processID :: IO String processID = readSymbolicLink "/proc/self"++lockedBy pid path = mkIOError alreadyInUseErrorType ("Locked by " ++ pid) Nothing (Just path)
Extra/Misc.hs view
@@ -1,5 +1,5 @@ module Extra.Misc- (-- * List functions+ ( -- * String functions columns , justify@@ -11,39 +11,35 @@ -- * Map and Set functions , listMap , listDiff- -- * Either functions- -- * System.IO functions- --ePut,- --ePutList, -- * System.Posix , checkSuperUser- -- removeRecursiveSafely, , md5sum , sameInode , sameMd5sum , tarDir -- * Processes- -- , Extra.Misc.processOutput- -- , processOutput2- , splitOutput- -- ByteString+ -- , splitOutput+ -- * ByteString , cd- -- Debugging+ -- * Debugging , read' ) where import Control.Exception import qualified Data.ByteString.Lazy.Char8 as B+import qualified Data.Digest.Pure.MD5 import Data.List import qualified Data.Map as Map import Data.Maybe import qualified Data.Set as Set import Extra.List+import System.Exit import System.FilePath-import System.Unix.Process import System.Directory import System.Posix.Files import System.Posix.User (getEffectiveUserID)+import System.Process (readProcessWithExitCode)+-- import System.Process.Progress (keepStdout, keepStderr, keepResult) import Text.Regex mapSnd :: (b -> c) -> (a, b) -> (a, c)@@ -51,22 +47,6 @@ -- Control file stuff -{--mergeControls :: [Control] -> Control-mergeControls (Control p1 : Control p2 : etc) = mergeControls (Control (p1 ++ p2) : etc)-mergeControls [Control p1] = Control p1-mergeControls [] = Control [] --fieldValue :: String -> Paragraph -> Maybe String-fieldValue fieldName paragraph =- maybe Nothing value (lookupP fieldName paragraph)- where value (Field (_, value)) = (Just . stripWS) value--hasField :: String -> String -> Paragraph -> Bool-hasField field value paragraph =- maybe False (== value) (fieldValue field paragraph)--}- -- |Pad strings so the columns line up. The argument and return value -- elements are the rows of a table. Do not pad the rightmost column. columns :: [[String]] -> [[String]]@@ -91,11 +71,6 @@ parentPath :: FilePath -> FilePath parentPath path = fst (splitFileName path) -{--baseName :: FilePath -> String-baseName path = snd (splitFileName path)--}- -- |Turn a list of (k, a) pairs into a map from k -> [a]. The order of the elements in -- the a list is preserved. listMap :: (Ord k) => [(k, a)] -> Map.Map k [a]@@ -121,20 +96,10 @@ merge (x : xs) = x : merge xs merge [] = [] +{-# DEPRECATED md5sum "Use Data.ByteString.Lazy.Char8.readFile path >>= return . show . Data.Digest.Pure.MD5.md5" #-} -- | Run md5sum on a file and return the resulting checksum as text.-md5sum :: FilePath -> IO (Either String String)-md5sum path =- do output <- lazyCommand cmd B.empty- let result =- case exitCodeOnly output of- ExitFailure n -> Left ("Error " ++ show n ++ " running '" ++ cmd ++ "'")- ExitSuccess ->- case listToMaybe . words . B.unpack . stdoutOnly $ output of- Nothing -> Left $ "Error in output of '" ++ cmd ++ "'"- Just checksum -> Right checksum- return result- where- cmd = "md5sum " ++ path+md5sum :: FilePath -> IO String+md5sum path = B.readFile path >>= return . show . Data.Digest.Pure.MD5.md5 -- | Predicate to decide if two files have the same inode. sameInode :: FilePath -> FilePath -> IO Bool@@ -153,127 +118,27 @@ return (asum == bsum) {---- | Backwards compatibility functions.-processOutput :: String -> IO (Either Int String)-processOutput command =- do- output <- lazyCommand command B.empty- case exitCodeOnly output of- ExitSuccess -> return . Right . B.unpack . stdoutOnly $ output- ExitFailure n -> return . Left $ n- _ -> error "My.processOutput: Internal error 13"--processOutput2 :: String -> IO (String, ExitCode)-processOutput2 command =- do- output <- lazyCommand command B.empty- return ((B.unpack . stdoutOnly $ output), exitCodeOnly output)+splitOutput :: [Output B.ByteString] -> (B.ByteString, B.ByteString, [ExitCode])+splitOutput output = (B.concat (keepStdout output), B.concat (keepStderr output), keepResult output) -} -splitOutput :: [Output] -> (B.ByteString, B.ByteString, ExitCode)-splitOutput output = (stdoutOnly output, stderrOnly output, exitCodeOnly output)- -- |A version of read with a more helpful error message. read' s = case reads s of [] -> error $ "read - no parse: " ++ show s ((x, _s) : _) -> x -{--type DryRunFn = IO () -> (Bool, String) -> IO ()--(-?-) :: DryRunFn--- ^ If this is a dry run (dryRun params is True) do *not* evaluate f,--- but instead print a message.-(-?-) f (dryRun, "") = if dryRun then return () else f-(-?-) f (dryRun, msg) =- do- hPutStrLn stderr msg- if dryRun then return () else f-infixr 9 -?---}--{--(+/+) :: FilePath -> FilePath -> FilePath-(+/+) path1 "" = path1-(+/+) "" path2 = path2-(+/+) path1 path2 =- case (last path1, head path2) of- ('/', '/') -> path1 +/+ (tail path2)- (_, '/') -> path1 ++ path2- ('/', _) -> path1 ++ path2- (_, _) -> path1 ++ "/" ++ path2--}--{---------------- From MissingH ------------------ | Split the path into directory and file name------ Examples:------ \[Posix\]------ > splitFileName "/" == ("/", ".")--- > splitFileName "/foo/bar.ext" == ("/foo", "bar.ext")--- > splitFileName "bar.ext" == (".", "bar.ext")--- > splitFileName "/foo/." == ("/foo", ".")--- > splitFileName "/foo/.." == ("/foo", "..")------ \[Windows\]------ > splitFileName "\\" == ("\\", "")--- > splitFileName "c:\\foo\\bar.ext" == ("c:\\foo", "bar.ext")--- > splitFileName "bar.ext" == (".", "bar.ext")--- > splitFileName "c:\\foo\\." == ("c:\\foo", ".")--- > splitFileName "c:\\foo\\.." == ("c:\\foo", "..")------ The first case in the Windows examples returns an empty file name.--- This is a special case because the \"\\\\\" path doesn\'t refer to--- an object (file or directory) which resides within a directory.--splitFileName :: FilePath -> (String, String)-splitFileName p = (reverse path1, reverse fname1)- where- (fname,path) = break isPathSeparator (reverse p)- path1 = case path of- "" -> "."- _ -> case dropWhile isPathSeparator path of- "" -> [pathSeparator]- p -> p- fname1 = case fname of- "" -> "."- _ -> fname---- | Checks whether the character is a valid path separator for the host--- platform. The valid character is a 'pathSeparator' but since the Windows--- operating system also accepts a slash (\"\/\") since DOS 2, the function--- checks for it on this platform, too.-isPathSeparator :: Char -> Bool-isPathSeparator ch =- ch == '/'---- | Provides a platform-specific character used to separate directory levels in--- a path string that reflects a hierarchical file system organization. The--- separator is a slash (@\"\/\"@) on Unix and Macintosh, and a backslash--- (@\"\\\"@) on the Windows operating system.-pathSeparator :: Char-pathSeparator = '/'--}-- checkSuperUser :: IO Bool checkSuperUser = getEffectiveUserID >>= return . (== 0) -- | Given a tarball, return the name of the top directory. tarDir :: FilePath -> IO (Maybe String) tarDir path =- lazyCommand cmd B.empty >>= \ output ->- case exitCodeOnly output of- ExitSuccess -> return . dir . lines . B.unpack . stdoutOnly $ output+ readProcessWithExitCode "tar" ["tfz", path] "" >>= \ (code, out, _) ->+ case code of+ ExitSuccess -> return . dir . lines $ out _ -> return Nothing where- cmd = "tar tfz '" ++ path ++ "'" dir [] = Nothing dir (file : _) = case wordsBy (== '/') file of [] -> Nothing
Extra/SSH.hs view
@@ -1,6 +1,6 @@ module Extra.SSH ( sshVerify- , sshExport+ , sshExportDeprecated , sshCopy ) where @@ -10,12 +10,10 @@ import System.Environment import System.Exit import System.IO-import qualified Data.ByteString.Lazy.Char8 as B-import System.Unix.Process-+import System.Process (readProcessWithExitCode, showCommandForUser) -- |Set up access to destination (user\@host).-sshExport :: String -> Maybe Int -> IO (Either String ())-sshExport dest port =+sshExportDeprecated :: String -> Maybe Int -> IO (Either String ())+sshExportDeprecated dest port = generatePublicKey >>= either (return . Left) (testAccess dest port) >>= either (return . Left) (openAccess dest port)@@ -35,11 +33,10 @@ True -> return . Right $ keypath False -> do hPutStrLn stderr $ "generatePublicKey " ++ " -> " ++ keypath- result <- lazyCommand cmd B.empty- case exitCodeOnly result of+ code <- system cmd+ case code of ExitFailure n ->- return . Left $ "Failure: " ++ show cmd ++ " -> " ++ show n ++- "\n\noutput: " ++ B.unpack (outputOnly result)+ return . Left $ "Failure: " ++ show cmd ++ " -> " ++ show n _ -> return . Right $ keypath -- |See if we already have access to the destination (user\@host).@@ -61,20 +58,19 @@ True -> return . Right $ Nothing False -> return . Right . Just $ keypath --- |Try to set up the keys so we have access to the account+-- |Try to set up the keys so we have access to the account. I don't+-- think we use this any more, and I don't think you should either. openAccess :: String -> Maybe Int -> Maybe FilePath -> IO (Either String ()) openAccess _ _ Nothing = return . Right $ () openAccess dest port (Just keypath) = do hPutStrLn stderr $ "openAccess " ++ show dest ++ " " ++ show port ++ " " ++ show keypath- let cmd = sshOpenCmd dest port keypath- result <- lazyCommand cmd B.empty- case exitCodeOnly result of- ExitFailure n -> return . Left $ "Failure: " ++ show cmd ++ " -> " ++ show n ++- "\n\noutput: " ++ B.unpack (outputOnly result)+ let args = maybe [] (\ x -> ["-p", show x]) port ++ [show dest, sshOpenRemoteCmd]+ (code, out, err) <- readFile keypath >>= readProcessWithExitCode "ssh" args+ case code of+ ExitFailure n -> return . Left $ "Failure: " ++ showCommandForUser "ssh" args ++ " -> " ++ show n +++ "\n\nstdout: " ++ out ++ "\n\nstderr: " ++ err _ -> return . Right $ () where- sshOpenCmd dest port keypath =- "cat " ++ keypath ++ " | " ++ "ssh " ++ (maybe "" ((++ "-p ") . show) port) ++ " " ++ show dest ++ " '" ++ sshOpenRemoteCmd ++ "'" sshOpenRemoteCmd = ("chmod g-w . && " ++ -- Ssh will not work if the permissions aren't just so "chmod o-rwx . && " ++
− Extra/TIO.hs
@@ -1,112 +0,0 @@-{-# LANGUAGE TypeSynonymInstances #-}--- |A value of type TIO represents the state of the terminal I/O--- system. The 'bol' flag keeps track of whether we are at the--- beginning of line on the console. This is computed in terms of--- what we have sent to the console, but it should be remembered that--- the order that stdout and stderr are sent to the console may not be--- the same as the order in which they show up there. However, in--- practice this seems to work as one would hope.-module Extra.TIO- ( module Extra.CIO- -- * The TIO monad- , TIO- , runTIO- , tryTIO- --, liftTIO- ) where--import Extra.CIO-import Prelude hiding (putStr, putChar, putStrLn)-import Control.Exception-import Control.Monad.RWS-import qualified System.IO as IO--data TState- = TState { cursor :: Position -- ^ Is the console at beginning of line?- } deriving Show--data Position- = BOL -- Beginning of line- | MOL -- Middle of line- | EOL -- End of line- deriving (Show, Eq)--type TIOT = RWST TStyle () TState-type TIO = TIOT IO---- |Perform a TIO monad task in the IO monad.-runTIO :: TStyle -> TIO a -> IO a-runTIO style action = (runRWST action) style initState >>= \ (a, _, _) -> return a---- |Catch exceptions in a TIO action.---tryTIO :: TIO a -> TIO (Either Exception a)-tryTIO task =- do state <- get- liftTIO (try' state) task- where- try' state task =- do result <- try task- case result of- Left e -> return (Left e, state, ())- Right (a, s, _) -> return (Right a, s, ())--liftTIO :: (IO (a, TState, ()) -> IO (b, TState, ())) -> TIO a -> TIO b-liftTIO f = mapRWST f---- |The initial output state - at the beginning of the line, no special handle--- state information, no repositories in the repository map.-initState :: TState-initState = TState {cursor = BOL}---- |The TIO instance of CIO adds some features to the normal console--- output. By tracking the cursor position it is able to insert a--- prefix to each line, and to implement a "beginning of line" (BOL)--- function which only adds a newline when the cursor is not already--- at BOL. It also allows verbosity controlled output, where a verbosity--- level is stored in the monad state and output requests are given a--- verbosity which must be greater or equal to the monad's verbosity--- level for the output to appear.-instance CIO TIO where- hPutStr h s =- do style <- ask- state <- get- case (cursor state, break (== '\n') s) of- (_, ("", "")) -> return ()- (BOL, ("", (_ : b))) -> prefix style >> newline >> hPutStr h b- (MOL, ("", (_ : b))) -> newline >> put (state {cursor = BOL}) >> hPutStr h b- (EOL, ("", (_ : b))) -> newline >> put (state {cursor = BOL}) >> hPutStr h b- (BOL, (a, b)) -> prefix style >> write a >> put (state {cursor = MOL}) >> hPutStr h b- (MOL, (a, b)) -> write a >> hPutStr h b- (EOL, (a, b)) -> newline >> prefix style >> write a >> put (state {cursor = MOL}) >> hPutStr h b- where- prefix style = liftIO (IO.hPutStr IO.stderr (hGetPrefix h style)) -- >> io (IO.hFlush h)- newline = liftIO (IO.hPutStr IO.stderr "\n") -- >> io (IO.hFlush IO.stderr)- write s = liftIO (IO.hPutStr IO.stderr s) -- >> io (IO.hFlush h)- -- | A "virtual" newline, this puts us into the EOL state. From- -- this state, a newline will be inserted before the next output,- -- unless that output itself begins with a newline.- hBOL _h =- do state <- get- put (state {cursor = if cursor state == BOL then BOL else EOL})- -- |Return the "effective verbosity", or perhaps the effective- -- quietness. If this value is zero or less the output will be- -- complete. By convention, if it is one, the output will be brief.- -- If it is a two or more no output is generated.- ev v =- do style <- ask- return (verbosity style - v)- -- |Modify the current style for this action- setStyle styleFn = local styleFn- -- |Implementation of try for the TIO monad- tryCIO = tryTIO--_test :: IO ()-_test =- runTIO (defStyle {prefix = "% "})- (putStr "hello\nworld\n" >>- bol >> -- No extra newline- putStr "(some text)" >>- putStr "(some more on same line)" >> -- This should be right after abc- bol >>- putStr "(newline)\n\n(after a blank line)" >> -- This should show up on a new line- bol) -- Newline before exit
Extra/URI.hs view
@@ -89,6 +89,3 @@ (a : _) -> a goodURIs = takeWhile isJust moreURIs (badURIs, moreURIs) = span isNothing allURIs--instance Ord URI where- compare a b = compare (uriToString id a "") (uriToString id b "")
Test/QUnit.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} ----------------------------------------------------------------------------- -- | -- Module : Test.QUnit