packages feed

hsfcsh-0.0.1: hsfcsh_do.hs

module Main
(
main
)
where

import Prelude hiding (catch)
import Network
import System.IO
import System.Process
import System.Environment
import System.Console.GetOpt
import Control.Monad
import Control.Exception
import Control.Concurrent
import Data.Maybe
import Data.Char

data Options = Options {
  optHsfcsh :: String,
  optPort :: PortNumber
} deriving Show

defaultOptions = Options {
  optHsfcsh = "hsfcsh",
  optPort = 1234
}

options :: [OptDescr (Options -> Options)]
options = [
  Option ['c'] ["with-hsfcsh"] (ReqArg (\o opts -> opts {optHsfcsh = o}) "CMD") "hsfcsh command",
  Option ['p'] ["port"] (ReqArg (\o opts -> opts {optPort = fromIntegral $ read o}) "PORT") "port to cennect to"
 ]

parseOpts :: [String] -> IO (Options, [String])
parseOpts argv =
  case getOpt Permute options argv of
    (o, n, []) -> return (foldl (flip id) defaultOptions o, n)
    (_, _, errs) -> ioError (userError $ concat errs ++ usageInfo header options)

header :: String
header = "Usage: hsfcsh_do [OPTION...] (spawn | exit | compile \"ARGS\")"

main :: IO ()
main = withSocketsDo main'

main' = do
  args <- getArgs
  (opts, args) <- parseOpts args
  case length args of
    1 -> case head args of
           "spawn" -> spawn (optHsfcsh opts)
           "exit" -> do
                       h <- connect (optPort opts)
                       let handle = fromJust h
                       when (isJust h) $ hPutStrLn handle "q" >> hFlush handle >> hClose handle
           _ -> putStrLn $ usageInfo header options
    2 -> case head args of
           "compile" -> do
                       handle <- connectOrSpawn (optHsfcsh opts) (optPort opts)
                       let tgt = args!!1
                       id <- findId handle tgt
                       let id' = fromJust id
                       if isNothing id
                         then do
                                putStrLn "id not found, will assing"
                                hPutStr handle $ "t\nmxmlc " ++ tgt ++ "\n"
                                hFlush handle
                          else do
                                putStrLn $ "found id = " ++ show id'
                                hPutStr handle $ "t\n" ++ "compile " ++ show id' ++ "\n"
                                hFlush handle
                       readRes handle
                       hPutStrLn handle "n"
                       hFlush handle
                       hClose handle
           _ -> putStrLn $ usageInfo header options
    _ -> putStrLn $ usageInfo header options

findId :: Handle -> String -> IO (Maybe Int)
findId handle tgt = do
  hPutStr handle "t\ninfo\n"
  hFlush handle
  res <- readRes' handle
  print res
  return $ fnd res
  where
  fnd :: [String] -> Maybe Int
  fnd [] = Nothing
  fnd [_] = Nothing
  fnd (x1:x2:xs) =
    if trim tgt' == trim tgt
      then Just id
      else fnd xs
    where
    id :: Int
    id = read $ drop 4 x1
    tgt' = drop 7 x2

trim :: String -> String
trim = f . f
  where f = reverse . dropWhile isSpace

readRes :: Handle -> IO ()
readRes handle = lp
  where
  lp = do
    l <- hGetLine handle
    if l == "<FCSH END>"
      then return ()
      else putStrLn l >> hFlush stdout >> lp

readRes' :: Handle -> IO [String]
readRes' handle = lp []
  where
  lp :: [String] -> IO [String]
  lp ls = do
    l <- hGetLine handle
    if l == "<FCSH END>"
      then return (reverse ls)
      else lp (l:ls)

connectOrSpawn :: String -> PortNumber -> IO Handle
connectOrSpawn cmd port = do
  h <- connect port
  if isNothing h
    then spawn cmd >> connect port >>= return . fromJust
    else return $ fromJust h

connect :: PortNumber -> IO (Maybe Handle)
connect port = catch (fmap Just (connectTo "localhost" (PortNumber port))) (\e -> print (e :: SomeException) >> return Nothing)

spawn :: String -> IO ()
spawn cmd = runCommand cmd >> yield >> threadDelay 2000000