#if !(defined(mingw32_HOST_OS) || defined(mingw32_TARGET_OS) || defined(__MINGW32__))
#define __HSH_POSIX__
#else
#define __HSH_WINDOWS__
#endif
module HSH.ShellEquivs(
abspath,
appendTo,
basename,
bracketCD,
catFrom,
catBytes,
catBytesFrom,
catTo,
#ifdef __HSH_POSIX__
catToFIFO,
#endif
cd,
cut,
cutR,
dirname,
discard,
echo,
exit,
glob,
grep,
grepV,
egrep,
egrepV,
joinLines,
lower,
upper,
mkdir,
numberLines,
pwd,
#ifdef __HSH_POSIX__
readlink,
readlinkabs,
#endif
rev,
revW,
HSH.Command.setenv,
space,
unspace,
tac,
tee,
#ifdef __HSH_POSIX__
teeFIFO,
#endif
tr,
trd,
wcW,
wcL,
HSH.Command.unsetenv,
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 Control.Exception(evaluate, catch, try, SomeException(..))
#ifdef __HSH_POSIX__
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.Posix.IO
import System.Posix.Error
#endif
import System.Path (absNormPath, bracketCWD)
import System.Exit
import System.IO
import System.Process
import qualified System.Directory as SD
import qualified System.Path.Glob as Glob (glob)
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString as BS
import System.IO.Unsafe(unsafeInterleaveIO)
import HSH.Channel
import HSH.Command(setenv, unsetenv)
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] -> Channel -> IO Channel
catFrom fplist ichan =
do r <- foldM foldfunc BSL.empty fplist
return (toChannel r)
where foldfunc accum fp =
case fp of
"-" -> do c <- chanAsBSL ichan
return (BSL.append accum c)
fn -> do c <- BSL.readFile fn
return (BSL.append accum c)
catBytes :: (Maybe Integer)
-> Channel
-> IO Channel
catBytes count hr = catBytesFrom hr count hr
catBytesFrom :: Channel
-> (Maybe Integer)
-> Channel
-> IO Channel
catBytesFrom (ChanHandle hr) count cignore =
case count of
Nothing -> return (ChanHandle hr)
Just m -> do c <- BSL.hGet hr (fromIntegral m)
return (ChanBSL c)
catBytesFrom cinput count cignore =
case count of
Nothing -> return cinput
Just m -> do r <- chanAsBSL cinput
return (ChanBSL (BSL.take (fromIntegral m) r))
catTo :: FilePath -> Channel -> IO Channel
catTo fp ichan =
do ofile <- openFile fp WriteMode
chanToHandle True ichan ofile
return (ChanString "")
#ifdef __HSH_POSIX__
catToFIFO :: FilePath -> Channel -> IO Channel
catToFIFO fp ichan =
do h <- fifoOpen fp
chanToHandle True ichan h
return (ChanString "")
fifoOpen :: FilePath -> IO Handle
fifoOpen fp =
do fd <- throwErrnoPathIf (< 0) "HSH fifoOpen" fp $
openFd fp WriteOnly Nothing defaultFileFlags
fdToHandle fd
#endif
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]
discard :: Channel -> IO Channel
discard inh =
do c <- chanAsBSL inh
evaluate (BSL.length c)
return (ChanString "")
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 :: Channelizable a => a -> Channel -> IO Channel
echo inp _ = return . toChannel $ 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 (\(e::SomeException) -> Glob.glob rest)
where (username, rest) = span (/= '/') remainder
#ifdef __HSH_POSIX__
expanduser =
do lookupuser <-
if username /= ""
then return username
else getEffectiveUserName
ue <- getUserEntryForName lookupuser
Glob.glob (homeDirectory ue ++ rest)
#else
expanduser = fail "non-posix; will be caught above"
#endif
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
#ifdef __HSH_POSIX__
mkdir :: FilePath -> FileMode -> IO ()
mkdir = createDirectory
#else
mkdir :: FilePath -> a -> IO ()
mkdir fp _ = SD.createDirectory fp
#endif
numberLines :: [String] -> [String]
numberLines = zipWith (printf "%3d %s") [(1::Int)..]
pwd :: IO FilePath
pwd = getCurrentDirectory
#ifdef __HSH_POSIX__
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
#endif
rev, revW :: [String] -> [String]
rev = map reverse
revW = map (unwords . reverse . words)
tac :: [String] -> [String]
tac = reverse
tee :: [FilePath] -> Channel -> IO Channel
tee fplist inp = teeBSGeneric (\fp -> openFile fp WriteMode) fplist inp
#ifdef __HSH_POSIX__
teeFIFO :: [FilePath] -> Channel -> IO Channel
teeFIFO fplist inp = teeBSGeneric fifoOpen fplist inp
#endif
teeBSGeneric :: (FilePath -> IO Handle)
-> [FilePath]
-> Channel -> IO Channel
teeBSGeneric openfunc fplist ichan =
do handles <- mapM openfunc fplist
inp <- chanAsBSL ichan
resultChunks <- hProcChunks handles (BSL.toChunks inp)
return (ChanBSL $ BSL.fromChunks resultChunks)
where hProcChunks :: [Handle] -> [BS.ByteString] -> IO [BS.ByteString]
hProcChunks handles chunks = unsafeInterleaveIO $
case chunks of
[] -> do mapM_ hClose handles
return [BS.empty]
(x:xs) -> do mapM_ (\h -> BS.hPutStr h x) handles
remainder <- hProcChunks handles xs
return (x : remainder)
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 :: [String] -> [String]
space = intersperse ""
unspace :: [String] -> [String]
unspace = filter (not . null)
lower :: String -> String
lower = map toLower
upper :: String -> String
upper = map toUpper
wcL :: [String] -> [String]
wcL inp = [show (genericLength inp :: Integer)]
wcW :: [String] -> [String]
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))