module Extra.Misc
(
columns
, justify
, mapSnd
, parentPath
, canon
, listMap
, listDiff
, checkSuperUser
, md5sum
, sameInode
, sameMd5sum
, tarDir
, cd
, 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.Directory
import System.Posix.Files
import System.Posix.User (getEffectiveUserID)
import System.Process (readProcessWithExitCode)
import Text.Regex
mapSnd :: (b -> c) -> (a, b) -> (a, c)
mapSnd f (a, b) = (a, f b)
columns :: [[String]] -> [[String]]
columns rows =
map (map pad . zip (widths ++ [0])) rows
where
widths = map (fromJust . listMax) . transpose . map (map length . init) $ rows
listMax l = foldl (\ a b -> Just . maybe b (max b) $ a) Nothing l
pad (width, field) = field ++ replicate (max 0 (width length field)) ' '
justify :: String -> Int -> [[String]]
justify s n =
foldr doWord [[]] (words s)
where doWord w [] = [[w]]
doWord w (ws : etc) =
if length (concat (intersperse " " (w:ws))) <= n then
(w : ws) : etc else
[w] : ws : etc
parentPath :: FilePath -> FilePath
parentPath path = fst (splitFileName path)
listMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
listMap pairs =
foldl insertPair Map.empty (reverse pairs)
where insertPair m (k,a) = Map.insert k (a : (Map.findWithDefault [] k m)) m
listDiff :: Ord a => [a] -> [a] -> [a]
listDiff a b = Set.toList (Set.difference (Set.fromList a) (Set.fromList b))
canon :: FilePath -> FilePath
canon path =
let re = mkRegex "/" in
let names = splitRegex re path in
concat (intersperse "/" (merge names))
where
merge (".." : xs) = ".." : (merge xs)
merge ("." : xs) = "." : (merge xs)
merge (_ : ".." : xs) = (merge xs)
merge (x : "." : xs) = (merge (x : xs))
merge (x : xs) = x : merge xs
merge [] = []
md5sum :: FilePath -> IO String
md5sum path = B.readFile path >>= return . show . Data.Digest.Pure.MD5.md5
sameInode :: FilePath -> FilePath -> IO Bool
sameInode a b =
do
aStatus <- getFileStatus a
bStatus <- getFileStatus b
return (deviceID aStatus == deviceID bStatus && fileID aStatus == fileID bStatus)
sameMd5sum :: FilePath -> FilePath -> IO Bool
sameMd5sum a b =
do
asum <- md5sum a
bsum <- md5sum b
return (asum == bsum)
read' s =
case reads s of
[] -> error $ "read - no parse: " ++ show s
((x, _s) : _) -> x
checkSuperUser :: IO Bool
checkSuperUser = getEffectiveUserID >>= return . (== 0)
tarDir :: FilePath -> IO (Maybe String)
tarDir path =
readProcessWithExitCode "tar" ["tfz", path] "" >>= \ (code, out, _) ->
case code of
ExitSuccess -> return . dir . lines $ out
_ -> return Nothing
where
dir [] = Nothing
dir (file : _) = case wordsBy (== '/') file of
[] -> Nothing
("" : _) -> Nothing
(s : _) -> Just s
cd :: FilePath -> IO a -> IO a
cd name m =
bracket
(do cwd <- getCurrentDirectory
setCurrentDirectory name
return cwd)
(\oldwd -> do setCurrentDirectory oldwd)
(const m)