module HSH.ShellEquivs(
abspath,
appendTo,
basename,
bracketCD,
catFrom,
catFromBS,
catTo,
catToBS,
cd,
cut,
cutR,
dirname,
echo,
echoBS,
exit,
glob,
grep,
grepV,
egrep,
egrepV,
joinLines,
lower,
upper,
mkdir,
numberLines,
pwd,
readlink,
readlinkabs,
rev,
revW,
space,
unspace,
tac,
tee,
teeBS,
tr,
trd,
wcW,
wcL,
uniq,
) where
import Data.List (genericLength, intersperse, isInfixOf, nub)
import Data.Char (toLower, toUpper)
import Text.Regex (matchRegex, mkRegex)
import Text.Printf (printf)
import Control.Monad (foldM)
import System.Directory hiding (createDirectory)
import System.Posix.Files (getFileStatus, isSymbolicLink, readSymbolicLink)
import System.Posix.User (getEffectiveUserName, getUserEntryForName, homeDirectory)
import System.Posix.Directory (createDirectory)
import System.Posix.Types (FileMode())
import System.Path (absNormPath, bracketCWD)
import System.Exit
import qualified System.Path.Glob as Glob (glob)
import qualified Data.ByteString.Lazy as BSL
abspath :: FilePath -> IO FilePath
abspath inp =
do p <- pwd
case absNormPath p inp of
Nothing -> fail $ "Cannot make " ++ show inp ++ " absolute within " ++
show p
Just x -> return x
basename :: FilePath -> FilePath
basename = snd . splitpath
dirname :: FilePath -> FilePath
dirname = fst . splitpath
bracketCD :: FilePath -> IO a -> IO a
bracketCD = bracketCWD
catFrom :: [FilePath] -> String -> IO String
catFrom = genericCatFrom readFile (++) ""
catFromBS :: [FilePath] -> BSL.ByteString -> IO BSL.ByteString
catFromBS = genericCatFrom BSL.readFile BSL.append BSL.empty
genericCatFrom :: (FilePath -> IO a) -> (a -> a -> a) -> a -> [FilePath] -> a -> IO a
genericCatFrom readfilefunc appendfunc empty fplist inp =
do r <- foldM foldfunc empty fplist
return r
where foldfunc accum fp =
case fp of
"-" -> return (appendfunc accum inp)
fn -> do c <- readfilefunc fn
return (appendfunc accum c)
catTo :: FilePath -> String -> IO String
catTo fp inp =
do writeFile fp inp
return ""
catToBS :: FilePath -> BSL.ByteString -> IO BSL.ByteString
catToBS fp inp =
do BSL.writeFile fp inp
return (BSL.empty)
appendTo :: FilePath -> String -> IO String
appendTo fp inp =
do appendFile fp inp
return ""
cd :: FilePath -> IO ()
cd = setCurrentDirectory
cut :: Integer -> Char -> String -> String
cut pos = cutR [pos]
cutR :: [Integer] -> Char -> String -> String
cutR nums delim z = drop 1 $ concat [delim:x | (x, y) <- zip string [0..], elem y nums]
where string = split delim z
echo :: String -> String -> String
echo inp _ = inp
echoBS :: BSL.ByteString -> BSL.ByteString -> BSL.ByteString
echoBS inp _ = inp
egrep :: String -> [String] -> [String]
egrep pat = filter (ismatch regex)
where regex = mkRegex pat
ismatch r inp = case matchRegex r inp of
Nothing -> False
Just _ -> True
egrepV :: String -> [String] -> [String]
egrepV pat = filter (not . ismatch regex)
where regex = mkRegex pat
ismatch r inp = case matchRegex r inp of
Nothing -> False
Just _ -> True
exit :: Int -> IO a
exit code
| code == 0 = exitWith ExitSuccess
| otherwise = exitWith (ExitFailure code)
glob :: FilePath -> IO [FilePath]
glob inp@('~':remainder) =
catch expanduser (\_ -> Glob.glob inp)
where (username, rest) = span (/= '/') remainder
expanduser =
do lookupuser <-
if username /= ""
then return username
else getEffectiveUserName
ue <- getUserEntryForName lookupuser
Glob.glob (homeDirectory ue ++ rest)
glob x = Glob.glob x
grep :: String -> [String] -> [String]
grep = filter . isInfixOf
grepV :: String -> [String] -> [String]
grepV needle = filter (not . isInfixOf needle)
joinLines :: [String] -> [String]
joinLines = return . concat
mkdir :: FilePath -> FileMode -> IO ()
mkdir = createDirectory
numberLines :: [String] -> [String]
numberLines = zipWith (printf "%3d %s") [(1::Int)..]
pwd :: IO FilePath
pwd = getCurrentDirectory
readlink :: FilePath -> IO FilePath
readlink fp =
do issym <- (getFileStatus fp >>= return . isSymbolicLink)
if issym
then readSymbolicLink fp
else return fp
readlinkabs :: FilePath -> IO FilePath
readlinkabs inp =
do issym <- (getFileStatus inp >>= return . isSymbolicLink)
if issym
then do rl <- readlink inp
case absNormPath (dirname inp) rl of
Nothing -> fail $ "Cannot make " ++ show rl ++ " absolute within " ++
show (dirname inp)
Just x -> return x
else abspath inp
rev, revW :: [String] -> [String]
rev = map reverse
revW = map (unwords . reverse . words)
tac :: [String] -> [String]
tac = reverse
tee :: [FilePath] -> String -> IO String
tee [] inp = return inp
tee (x:xs) inp = writeFile x inp >> tee xs inp
teeBS :: [FilePath] -> BSL.ByteString -> IO BSL.ByteString
teeBS [] inp = return inp
teeBS (x:xs) inp = BSL.writeFile x inp >> teeBS xs inp
tr :: Char -> Char -> String -> String
tr a b = map (\x -> if x == a then b else x)
trd :: Char -> String -> String
trd = filter . (/=)
uniq :: String -> String
uniq = unlines . nub . lines
space, unspace :: [String] -> [String]
space = intersperse ""
unspace = filter (not . null)
lower, upper :: String -> String
lower = map toLower
upper = map toUpper
wcL, wcW :: [String] -> [String]
wcL inp = [show (genericLength inp :: Integer)]
wcW inp = [show ((genericLength $ words $ unlines inp) :: Integer)]
split :: Char -> String -> [String]
split c s = case rest of
[] -> [chunk]
_:rst -> chunk : split c rst
where (chunk, rest) = break (==c) s
splitpath :: String -> (String, String)
splitpath "" = (".", ".")
splitpath "/" = ("/", "/")
splitpath p
| last p == '/' = splitpath (init p)
| not ('/' `elem` p) = (".", p)
| head p == '/' && length (filter (== '/') p) == 1 = ("/", tail p)
| otherwise = (\(base, dir) -> (reverse (tail dir), reverse base))
(break (== '/') (reverse p))