Advertisement
Guest User

shell

a guest
Jul 20th, 2013
489
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. module Shell
  2.        where
  3.  
  4. import System.IO
  5. import Data.List
  6. import Data.Char
  7.  
  8. startsWith :: String -> String -> Bool
  9. startsWith [] _ = True
  10. startsWith _ [] = False
  11. startsWith (c:cs) (m:ms) = (c==m) && startsWith cs ms
  12.  
  13. longestPrefix :: [String] -> String
  14. longestPrefix ss = helper ss ""
  15.   where helper [] acc = ""
  16.         helper ss acc | any (=="") ss = acc
  17.                       | otherwise = let x = ss !! 0
  18.                                         b = x!!0
  19.                                     in if all (startsWith [b]) ss
  20.                                        then helper (map tail ss) (acc++[b]) else acc
  21.                                    
  22.                                
  23.  
  24. rmdups :: Eq a => [a] -> [a]
  25. rmdups [] = []
  26. rmdups (e:es) = e:(rmdups (filter (/=e) es))
  27.  
  28. isUp str = (map ord str) == [27,91,65]
  29. isDown str = (map ord str) == [27,91,66]
  30.  
  31. clearStr :: String -> IO ()
  32. clearStr str = putStr ("\r"++ map (\_->' ') str++"\r")
  33.  
  34. data State = Newline (String, [String], [String])
  35.            | Partial (String, String, [String], [String]) -- incomplete input followed by control characters
  36.            | UpSearch (String, [String], [String])
  37.            | DownSearch (String, [String], [String])
  38.            | TabSearch (String, [String], [String])
  39.            | PurePrefix (String, [String], [String]) -- just incomplete input
  40.  
  41. move :: State -> Char -> State
  42. move (PurePrefix (buffer, past, future)) c | c == '\t' = TabSearch (buffer, past, future)
  43.                                            | c == '\n' = Newline (buffer, past, future)
  44.                                            | ord c == 127 = let b = if null buffer then buffer else ((reverse . tail . reverse) buffer)
  45.                                                             in PurePrefix (b, past, future)
  46.                                            | isControl c = Partial (buffer, [c], past, future)
  47.                                            | isPrint c = PurePrefix (buffer++[c], past, future)
  48.                                            | otherwise = PurePrefix (buffer, past, future)
  49. move (Partial (buffer, special, past, future)) c | isUp (special++[c]) = UpSearch (buffer, past, future)
  50.                                                  | isDown (special++[c]) = DownSearch (buffer, past, future)
  51.                                                  | otherwise = Partial (buffer, special ++ [c], past, future)
  52. move (TabSearch (buffer, past, future)) c | c == '\t' = TabSearch (buffer, past, future)
  53.                                           | c == '\n' = Newline (buffer, past, future)
  54.                                           | isPrint c = PurePrefix (buffer++[c], past, future)
  55.                                           | otherwise = error "Control characters not expected in Tab Search mode" -- FIXME
  56.  
  57. move _ _ = error "should be handled by loop"                                                              
  58.  
  59. getBuffer :: State -> String
  60. getBuffer (Newline (b, _, _)) = b
  61. getBuffer (Partial (b, _, _, _)) = b
  62. getBuffer (UpSearch (b, _, _)) = b
  63. getBuffer (DownSearch (b, _, _)) = b
  64. getBuffer (TabSearch (b, _, _)) = b
  65. getBuffer (PurePrefix (b, _, _)) = b
  66.  
  67. type Launcher = String -> IO String
  68.  
  69. shell :: Launcher -> IO ()
  70. shell launcher = do
  71.   putStrLn "hsnsh: a shell in Haskell!"
  72.   hSetEcho stdin False
  73.   hSetBuffering stdin NoBuffering
  74.   hSetBuffering stdout NoBuffering  
  75.   putStr prompt
  76.   loop (PurePrefix ("", [], []))
  77.   where loop state = do
  78.           c <- getChar
  79.           let buff = getBuffer state
  80.           case move state c of
  81.             st@(Partial (b, s, p, f)) -> loop st
  82.             st@(PurePrefix (b, p, f)) -> rewrite buff b >> loop st
  83.             Newline (b, p, f) -> do
  84.               putStr "\n"
  85.               if b=="exit" || b=="quit"
  86.                 then putStrLn "Thanks for using hsnsh!"
  87.                 else do
  88.                 result <- launcher b              
  89.                 putStrLn result
  90.                 putStr prompt
  91.                 loop (PurePrefix ("", b:p, f))
  92.             UpSearch (b, p, f) -> do
  93.               if null p then loop (PurePrefix (b, p, f))
  94.                 else do
  95.                 let new_b = head p
  96.                 rewrite buff new_b
  97.                 loop (PurePrefix (new_b, tail p, buff:f))
  98.             DownSearch (b, p, []) -> loop (PurePrefix (b, p, []))
  99.             DownSearch (b, p, f) -> do              
  100.               let new_b = head f
  101.               rewrite buff new_b                  
  102.               loop (PurePrefix (new_b, buff:p, tail f))
  103.             st@(TabSearch (b, p, f)) -> do
  104.               let variants = rmdups $ filter (startsWith b) (p++f++reserved)
  105.               if null variants then loop (TabSearch (b, p, f))
  106.                 else do
  107.                 putStrLn $ "\nvariants: "++(show variants)
  108.                 let lp = longestPrefix variants
  109.                 let s = if length b < length lp then lp else b
  110.                 putStr s
  111.                 loop (TabSearch (s, p, f))
  112.                
  113.         rewrite :: String -> String -> IO ()
  114.         rewrite old new = clearStr (prompt ++ old) >> putStr (prompt++new)
  115.         prompt = "nsh> "
  116.         reserved = ["quit", "exit"]
  117.  
  118. execute :: String -> IO String
  119. execute "help" = return $ concat $ intersperse "\n" ["modUname", "modLs", "modCat", "modTailF"]
  120. execute command = do -- all the boring stuff: spawn a process, handle signals etc
  121. --  pid <- forkProcess
  122.   return "Success"
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement