Unixutils 1.22 → 1.27
raw patch · 8 files changed
+117/−96 lines, 8 filesdep +mtldep +regex-tdfadep −regex-compatdep ~basedep ~parallelsetup-changed
Dependencies added: mtl, regex-tdfa
Dependencies removed: regex-compat
Dependency ranges changed: base, parallel
Files
- Setup.hs +1/−1
- System/Unix/Directory.hs +2/−2
- System/Unix/FilePath.hsc +1/−1
- System/Unix/Process.hs +33/−59
- System/Unix/ProcessExtra.hs +49/−0
- System/Unix/Progress.hs +4/−2
- System/Unix/SpecialDevice.hs +24/−28
- Unixutils.cabal +3/−3
Setup.hs view
@@ -2,4 +2,4 @@ import Distribution.Simple -main = defaultMainWithHooks defaultUserHooks+main = defaultMainWithHooks simpleUserHooks
System/Unix/Directory.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE ForeignFunctionInterface, ScopedTypeVariables #-} module System.Unix.Directory ( find , removeRecursiveSafely@@ -49,7 +49,7 @@ traverse path f d m = do result <- try $ getSymbolicLinkStatus path- either (\ _ -> return ()) (doPath path) result+ either (\ (_ :: SomeException) -> return ()) (doPath path) result where doPath path status = if isDirectory status then
System/Unix/FilePath.hsc view
@@ -12,7 +12,7 @@ import Data.List import System.FilePath (makeRelative, (</>), takeFileName, dropFileName)-import Text.Regex+--import Text.Regex import Foreign.C import Foreign.Marshal.Array
System/Unix/Process.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS -Wall -fno-warn-name-shadowing -fno-warn-missing-signatures -Werror #-} -- |functions for killing processes, running processes, etc module System.Unix.Process (@@ -36,29 +37,26 @@ , killByCwd -- FilePath -> IO [(String, Maybe String)] ) where -import Control.Monad+import Control.Concurrent (threadDelay)+import Control.Monad (liftM, filterM) import Control.Exception hiding (catch)-import Control.Parallel.Strategies-import Data.Char+import Control.Parallel.Strategies (rnf)+import Data.Char (isDigit) import qualified Data.ByteString as B---import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Char8 as C-import qualified Data.ByteString.Lazy.Char8 as L---import qualified Data.ByteString.Internal as I+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Lazy.Char8 as C import Data.ByteString.Internal(toForeignPtr) -- for hPutNonBlocking only-import Data.List-import Data.Word-import Data.Int-import System.Process-import System.IO-import System.IO.Unsafe-import System.Directory-import System.Exit-import System.Posix.Files-import System.Posix.Signals-import System.Posix.Unistd (usleep)-import Foreign.Marshal.Alloc (allocaBytes)-import Foreign.Marshal.Array (peekArray, pokeArray)+import Data.List (isPrefixOf)+import Data.Int (Int64)+import qualified GHC.IO.Exception as E+import System.Process (ProcessHandle, waitForProcess, runInteractiveProcess, runInteractiveCommand)+import System.IO (Handle, hSetBinaryMode, hReady, hPutBufNonBlocking, hClose, hGetContents)+import System.IO.Unsafe (unsafeInterleaveIO)+import System.Directory (getDirectoryContents)+import System.Exit (ExitCode(ExitFailure, ExitSuccess))+import System.Posix.Files (readSymbolicLink)+import System.Posix.Signals (signalProcess, sigTERM)+ import Foreign.Ptr (plusPtr) -- for hPutNonBlocking only import Foreign.ForeignPtr (withForeignPtr) -- for hPutNonBlocking only @@ -102,6 +100,8 @@ simpleProcess :: FilePath -> [String] -> IO (String, String, ExitCode) simpleProcess exec args = do (inp,out,err,pid) <- runInteractiveProcess exec args Nothing Nothing+ hSetBinaryMode out True+ hSetBinaryMode err True hClose inp outStr <- hGetContents out errStr <- hGetContents err@@ -167,42 +167,10 @@ | Result ExitCode deriving Show --- | Display up to thirty characters of a ByteString followed by an--- ellipsis if some of it was omitted.-showBrief :: B.ByteString -> String-showBrief s = - let l = B.length s in- show (B.take (min 30 l) s) ++- if l > 30 then " ... (" ++ show (l - 30) ++ " additional bytes)" else ""--{--instance Show Output where- show (Stdout s) =- let l = B.length s in- "Stdout: " ++ showBrief s- show (Stderr s) =- let l = B.length s in- "Stderr: " ++ showBrief s- -- show (Sleep n) = "Slept " ++ show n ++ " usec"- show (Result e) = show e--}- bufSize = 65536 -- maximum chunk size uSecs = 8 -- minimum wait time, doubles each time nothing is ready maxUSecs = 100000 -- maximum wait time (microseconds) ---stringToByteString = B.pack . map (fromInteger . toInteger . ord)---- | Debugging output-ePut :: Int -> String -> IO ()-ePut minv s = if curv >= minv then hPutStr stderr s else return ()-ePut0 = ePut 0-ePut1 = ePut 1-ePut2 = ePut 2---- | Current verbosity level.-curv = 0- -- | Create a process with 'runInteractiveCommand' and run it with 'lazyRun'. lazyCommand :: String -> L.ByteString -> IO [Output] lazyCommand cmd input = runInteractiveCommand cmd >>= lazyRun input@@ -218,6 +186,9 @@ -- the lazy list of 'Output' objects. lazyRun :: L.ByteString -> Process -> IO [Output] lazyRun input (inh, outh, errh, pid) =+ hSetBinaryMode inh True >>+ hSetBinaryMode outh True >>+ hSetBinaryMode errh True >> elements (L.toChunks input, Just inh, Just outh, Just errh, []) where elements :: ([B.ByteString], Maybe Handle, Maybe Handle, Maybe Handle, [Output]) -> IO [Output]@@ -239,7 +210,10 @@ data Readyness = Ready | Unready | EndOfFile hReady' :: Handle -> IO Readyness-hReady' h = (hReady h >>= (\ flag -> return (if flag then Ready else Unready))) `catch` (\ e -> return EndOfFile)+hReady' h = (hReady h >>= (\ flag -> return (if flag then Ready else Unready))) `catch` (\ (e :: IOError) ->+ case E.ioe_type e of+ E.EOF -> return EndOfFile+ _ -> error (show e)) -- | Wait until at least one handle is ready and then write input or -- read output. Note that there is no way to check whether the input@@ -261,7 +235,7 @@ -- Input handle closed and there are no ready output handles, -- wait a bit ([], Nothing, Unready, Unready) ->- do usleep uSecs+ do threadDelay waitUSecs --ePut0 ("Slept " ++ show uSecs ++ " microseconds\n") ready (min maxUSecs (2 * waitUSecs)) (input, inh, outh, errh, elems) -- Input is available and there are no ready output handles@@ -273,11 +247,11 @@ do count' <- hPutNonBlocking handle input >>= return . fromInteger . toInteger case count' of -- Input buffer is full too, sleep.- 0 -> do usleep uSecs+ 0 -> do threadDelay uSecs ready (min maxUSecs (2 * waitUSecs)) (input : etc, inh, outh, errh, elems) -- We wrote some input, discard it and continue- n -> do let input' = B.drop count' input : etc- return (input', Just handle, outh, errh, elems)+ _n -> do let input' = B.drop count' input : etc+ return (input', Just handle, outh, errh, elems) -- One or both output handles are ready, try to read from them _ -> do (out1, errh') <- nextOut errh errReady Stderr@@ -299,7 +273,7 @@ 0 -> do hClose handle return ([], Nothing) -- Got some input- n -> return ([constructor a], Just handle)+ _n -> return ([constructor a], Just handle) -- | Filter everything except stdout from the output list. stdoutOnly :: [Output] -> L.ByteString@@ -424,4 +398,4 @@ collectOutputUnpacked :: [Output] -> (String, String, [ExitCode]) collectOutputUnpacked = unpack . collectOutput- where unpack (out, err, result) = (L.unpack out, L.unpack err, result)+ where unpack (out, err, result) = (C.unpack out, C.unpack err, result)
+ System/Unix/ProcessExtra.hs view
@@ -0,0 +1,49 @@+-- |A place to collect and hopefully retire all the random ways of+-- running shell commands that have accumulated over the years.+module System.Unix.ProcessExtra where++import Control.Exception (ErrorCall(ErrorCall), try)+import Control.Monad.Trans (liftIO)+import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.ByteString as B+import Data.List (intercalate)+import System.Exit+import qualified System.IO as IO+import System.Unix.Process (Output(..), lazyCommand, collectOutput, lazyProcess)++cmdOutput :: String -> IO (Either ErrorCall L.ByteString)+cmdOutput cmd =+ do (out, err, code) <- lazyCommand cmd L.empty >>= return . collectOutput+ case code of+ (ExitSuccess : _) -> return (Right out)+ _ -> return . Left . ErrorCall $ "Failure: " ++ show cmd ++ " -> " ++ show code ++ "\n\nstdpout:\n\n" ++ show (L.unpack out) ++ "\n\nstderr:\n\n" ++ show (L.unpack err)++cmdOutputStrict :: String -> IO (Either ErrorCall B.ByteString)+cmdOutputStrict cmd =+ do (out, err, code) <- lazyCommand cmd L.empty >>= return . f . collectOutput+ case code of+ (ExitSuccess : _) -> return (Right out)+ _ -> return . Left . ErrorCall $ "Failure: " ++ show cmd ++ " -> " ++ show code ++ "\n\nstdpout:\n\n" ++ show (B.unpack out) ++ "\n\nstderr:\n\n" ++ show (B.unpack err)+ where+ f :: (L.ByteString, L.ByteString, [ExitCode]) -> (B.ByteString, B.ByteString, [ExitCode])+ f (o, e, c) = (toStrict o, toStrict e, c)++toLazy :: B.ByteString -> L.ByteString+toLazy b = L.fromChunks [b]++toStrict :: L.ByteString -> B.ByteString+toStrict b = B.concat (L.toChunks b)++echoCommand :: String -> L.ByteString -> IO [Output]+echoCommand command input =+ ePutStrBl ("# " ++ command) >>+ liftIO (lazyCommand command input)++-- |Echo the process arguments and then run the process+echoProcess :: FilePath -> [String] -> L.ByteString -> IO [Output]+echoProcess exec args input =+ ePutStrBl (intercalate " " ("#" : exec : args)) >>+ liftIO (lazyProcess exec args Nothing Nothing input)++ePutStrBl :: String -> IO ()+ePutStrBl s = IO.hPutStrLn IO.stderr s
System/Unix/Progress.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE ScopedTypeVariables #-} -- |Run shell commands with various types of progress reporting. -- -- Author: David Fox@@ -133,8 +134,9 @@ finish <- getClockTime let elapsed = diffClockTimes finish start case result of- Left e -> do errorMessage style (show e)- error (show e)+ Left (e :: SomeException) ->+ do errorMessage style (show e)+ error (show e) Right a -> do finishMessage style elapsed return a
System/Unix/SpecialDevice.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE PatternSignatures #-}+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, ScopedTypeVariables, TypeSynonymInstances #-} -- | Construct an ADT representing block and character devices -- (but mostly block devices) by interpreting the contents of -- the Linux sysfs filesystem.@@ -46,7 +46,7 @@ import System.Posix.Types import System.Posix.Files import System.Posix.User-import Text.Regex+import Text.Regex.TDFA data SpecialDevice = BlockDevice DeviceID | CharacterDevice DeviceID@@ -60,7 +60,7 @@ ofPath path = -- Catch the exception thrown on an invalid symlink (try $ getFileStatus path) >>=- return . either (const Nothing) (Just . BlockDevice . deviceID)+ return . either (\ (_ :: SomeException) -> Nothing) (Just . BlockDevice . deviceID) rootPart :: IO (Maybe SpecialDevice) rootPart = ofPath "/"@@ -70,7 +70,7 @@ -- if the node turns out not to be a special device. ofNode :: FilePath -> IO (Maybe SpecialDevice) ofNode "/dev/root" = ofPath "/"-ofNode node = (try $ getFileStatus node) >>= return . either (const Nothing) ofNodeStatus+ofNode node = (try $ getFileStatus node) >>= return . either (\ (_ :: SomeException) -> Nothing) ofNodeStatus ofNodeStatus :: FileStatus -> Maybe SpecialDevice ofNodeStatus status =@@ -182,14 +182,17 @@ return (catMaybes devs) where inGroup group (_, status) = fileGroup status == group- isPart (path, _) = maybe False (const True) (matchRegex (mkRegex "-part[0-9]+$") path) getAllPartitions :: IO [SpecialDevice] getAllPartitions = directory_find True "/dev/disk/by-path" >>= return . filter isPart >>= return . catMaybes . map (ofNodeStatus . snd)- where- isPart (path, _) = maybe False (const True) (matchRegex (mkRegex "-part[0-9]+$") path) +isPart :: (FilePath, FileStatus) -> Bool+isPart (path, _) =+ case path =~ "-part[0-9]+$" of+ x | mrMatch x == "" -> False+ x -> True+ getAllCdroms :: IO [SpecialDevice] getAllCdroms = cdromGroup >>= getDisksInGroup @@ -203,27 +206,20 @@ -- fileStatus) pairs. directory_find :: Bool -> FilePath -> IO [(FilePath, FileStatus)] directory_find follow path =- do- maybeStatus <- - if follow then- -- Catch the exception exception thrown on an invalid symlink- try . getFileStatus $ path else- getSymbolicLinkStatus path >>= return . Right- case maybeStatus of- Left _ -> return []- Right status ->- case isDirectory status of- True -> - do- -- Catch the exception thrown if we lack read permission- subs <- (try $ getDirectoryContents path) >>=- return . either (const []) id >>=- return . map (path </>) . filter (not . flip elem [".", ".."]) >>=- mapM (directory_find follow) >>=- return . concat- return $ (path, status) : subs- False ->- return [(path, status)]+ if follow then fileStatus else linkStatus+ where+ fileStatus = try (getFileStatus path) >>= either (\ (_ :: SomeException) -> return []) useStatus+ linkStatus = getSymbolicLinkStatus path >>= useStatus+ useStatus status+ | isDirectory status =+ do -- Catch the exception thrown if we lack read permission+ subs <- (try $ getDirectoryContents path) >>=+ return . either (\ (_ :: SomeException) -> []) id >>=+ return . map (path </>) . filter (not . flip elem [".", ".."]) >>=+ mapM (directory_find follow) >>=+ return . concat+ return $ (path, status) : subs+ | True = return [(path, status)] dirname path = reverse . tail . snd . break (== '/') . reverse $ path basename path = reverse . fst . break (== '/') . reverse $ path
Unixutils.cabal view
@@ -1,11 +1,11 @@ Name: Unixutils-Version: 1.22+Version: 1.27 License: BSD3 License-File: COPYING Author: Jeremy Shaw Homepage: http://src.seereason.com/haskell-unixutils Category: System-Build-Depends: base >= 3 && < 4, unix, regex-compat, process, bytestring, directory, old-time, parallel, filepath+Build-Depends: base >= 4 && <5, mtl, unix, regex-tdfa, process, bytestring, directory, old-time, parallel, filepath Synopsis: A crude interface between Haskell and Unix-like operating systems Maintainer: jeremy@n-heptane.com Description:@@ -15,7 +15,7 @@ ghc-options: -O2 Exposed-modules: System.Unix.Directory, System.Unix.FilePath, System.Unix.List,- System.Unix.Misc, System.Unix.Mount, System.Unix.Process,+ System.Unix.Misc, System.Unix.Mount, System.Unix.Process, System.Unix.ProcessExtra, System.Unix.Progress, System.Unix.SpecialDevice, System.Unix.Files -- For more complex build options see: