#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)
#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 :: Int
-> (Maybe Integer)
-> Handle
-> Handle
-> IO ()
catBytes chunksize count hr = catBytesFrom chunksize hr count hr
catBytesFrom :: Int
-> Handle
-> (Maybe Integer)
-> Handle
-> Handle
-> IO ()
catBytesFrom chunksize hr count hignore hw =
do buf <- hGetBuffering hr
catBytesFrom' chunksize hr count hignore hw
hSetBuffering hr buf
catBytesFrom' _ _ (Just 0) _ _ = return ()
catBytesFrom' chunksize hr count hignore hw =
do hSetBuffering hr (BlockBuffering (Just readamount))
case count of
Just x -> if x < 1
then do fail $ "catBytesFrom: count < 0 not supported"
else return ()
_ -> return ()
r <- BS.hGet hr readamount
if BS.null r
then return ()
else do BS.hPutStr hw r
catBytesFrom' chunksize hr (newCount (BS.length r)) hignore hw
where readamount =
case count of
Just x -> fromIntegral $ min x (fromIntegral chunksize)
Nothing -> (fromIntegral chunksize)
newCount newlen = case count of
Nothing -> Nothing
Just x -> Just (x (fromIntegral newlen))
catTo :: FilePath -> Channel -> IO Channel
catTo fp ichan =
do ofile <- openFile fp WriteMode
chanToHandle ichan ofile
hClose ofile
return (ChanString "")
#ifdef __HSH_POSIX__
catToFIFO :: FilePath -> Channel -> IO Channel
catToFIFO fp ichan =
do h <- fifoOpen fp
chanToHandle ichan h
hClose 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 :: Handle -> Handle -> IO ()
discard inh outh =
do eof <- hIsEOF inh
if eof
then return ()
else do BS.hGet inh 4096
discard inh outh
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 (\_ -> 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] -> BSL.ByteString -> IO BSL.ByteString
tee fplist inp = teeBSGeneric (\fp -> openFile fp WriteMode) fplist inp
#ifdef __HSH_POSIX__
teeFIFO :: [FilePath] -> BSL.ByteString -> IO BSL.ByteString
teeFIFO fplist inp = teeBSGeneric fifoOpen fplist inp
#endif
teeBSGeneric :: (FilePath -> IO Handle) -> [FilePath] -> BSL.ByteString -> IO BSL.ByteString
teeBSGeneric openfunc fplist inp =
do handles <- mapM openfunc fplist
resultChunks <- hProcChunks handles (BSL.toChunks inp)
return (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, 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))