Hashell (empty) → 0.15
raw patch · 16 files changed
+1309/−0 lines, 16 filesdep +basedep +directorydep +ghcsetup-changed
Dependencies added: base, directory, ghc, haskell98, parsec, process, readline, regex-compat, unix
Files
- BUGS +5/−0
- CHANGELOG +33/−0
- Hashell.cabal +26/−0
- Hashell/ConfigFile.hs +48/−0
- Hashell/Environment.hs +62/−0
- Hashell/Eval.hs +183/−0
- Hashell/Exec.hs +219/−0
- Hashell/Main.hs +29/−0
- Hashell/Options.hs +61/−0
- Hashell/Parse.hs +117/−0
- Hashell/Process.hs +105/−0
- Hashell/Signals.hs +27/−0
- LICENSE +340/−0
- README +45/−0
- Setup.hs +2/−0
- config_example +7/−0
+ BUGS view
@@ -0,0 +1,5 @@+*Control-d does not exit the shell; only "quit" does.+*Haskell expression seem to get evaluated, but their result is not shown!+*Control constructs like && and & or || do not seem to work.+*Every successful commands exits with 'user error (1)'.+*The GHC API requires an absolute path to the GHC lib/ directory. This is currently hardcoded in Eval.hs, but should be in the configuration file.
+ CHANGELOG view
@@ -0,0 +1,33 @@++ Changelog - Hashell (0.011a -> 0.013a)++ -- Part of the parsing is now done with Parsec. + Parsec will partially substitute the majority of the parsing tasks.+ -- Added a new exception handler for haskell expressions+ evaluation. This fixes a bug that aborted hashell.+ -- Added redirection of standard error.+ -- Fixed 'quit' built-in command bug.+ -- Added number identifiers to the redirections operators.+ -- Added pre-liminar support for environment variables.+ The support is contained into its own module.+ -- Changed (_yet_ again) the syntax for sub-process evaluation + from '<|proc|>' to '(-proc-)' (without the quotes).+ -- Type constraints for haskell expressions evaluations are now limited+ to the ':<s,p,n,u>' notation. Values:+ 's' String without newline.+ 'p' Non-String with newline.+ 'n' Non-String without newline.+ 'u' String with newline.+ -- The 'execSplitToken' function has been re-written. It is now+ split into several smaller functions and with better exception handling+ support.+ -- Hashell will now automatically read the configuration file with name+ .hashell_config if it exists inside HOME.+ -- The comments format in the hashell configuration file has changed+ from '*' to '-'.+ -- The command composition feature was removed.+ -- Others smaller bugs have been fixed too.++ Author: Luis F. Araujo+ luis@arjox.org+Sat Jan 7 14:55:04 VET 2006
+ Hashell.cabal view
@@ -0,0 +1,26 @@+Name: Hashell+Version: 0.15+Synopsis: Simple shell written in Haskell+Description: A simple shell written in Haskell; through the GHC API, it allows+ evaluation of Haskell expressions.+Category: User Interfaces++License: GPL+License-File: LICENSE+Author: Luis Francisco Araujo+Maintainer: luis@arjox.org+Stability: alpha++Tested-With: GHC+Build-Depends: base, unix, haskell98, readline, parsec, regex-compat, process, ghc,+ directory+Build-Type: Simple+Extra-Source-Files: BUGS, CHANGELOG, config_example, README++Executable: hashell+Main-Is: Main.hs+hs-source-dirs: Hashell/+Other-Modules: ConfigFile, Environment, Eval, Exec, Options, Parse, Process+ Signals+Ghc-options: -O2 -Wall -Werror -optl-Wl,-s+Ghc-prof-options: -prof -auto-all
+ Hashell/ConfigFile.hs view
@@ -0,0 +1,48 @@+{-+ Functions to handle the hashell configuration file.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module ConfigFile+ where++import Environment (getEnvVariable)+import System.Posix.Files (fileExist)++{-+ | Read and parse configuration file.+-}+----------------------------------------------------------------------------------+-- | Take a filepath and return all the+-- information in a list of tuples in the form+-- of (Variable , Value).+globalConfig :: [FilePath] -> IO [(String,String)]+globalConfig [] = do+ home <- getEnvVariable "HOME"+ let cfg = (home ++ "/.hashell_config")+ fileExist cfg >>=+ \ s -> case s of+ True -> readConfig cfg >>=+ makeVarValTuples+ False -> return []+globalConfig (cfg:_) = readConfig cfg >>= makeVarValTuples++-- | Read and filter comments from file.+-- Return each line of the configuration file+-- inside a list as strings.+readConfig :: FilePath -> IO [String]+readConfig = (return . filterComment . lines =<<) . readFile++----------------------------------------------------------------------------------+-- | Filter all the comments lines.+filterComment :: [String] -> [String]+filterComment [] = []+filterComment (('-':_):xss) = filterComment xss+filterComment ([]:xss) = filterComment xss+filterComment ((' ':_):xss) = filterComment xss+filterComment (xs:xss) = xs : filterComment xss++-- | Make a list of (Variables, Values) out of+-- the list containing the configuration file information.+makeVarValTuples :: [String] -> IO [(String,String)]+makeVarValTuples xs = return [ (v,r) | x <- xs , let (v,(_:r)) = break (== ' ') x ]
+ Hashell/Environment.hs view
@@ -0,0 +1,62 @@+{-+ Environment variables functions.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Environment+ where++import System.Posix.Env (getEnv, putEnv)+import Text.Regex (mkRegex, splitRegex)++data Alias = Alias { command+ , newcommand :: String }+ deriving (Show, Eq)++type Prompt = String+type FileOpts = [(String, String)]+type ExecutionEnv = (Prompt , [Alias])++{-+ | Setup and get specific values from environment variables.+-}+----------------------------------------------------------------------------------+-- | Setup all the enviromment variables specified in the configuration file.+getVariables :: FileOpts -> IO ()+getVariables xs = mapM_ (\ (a , b) -> putEnv (a ++ "=" ++ b)) xs++-- | Setup environment variables.+setEnvVar :: IO ExecutionEnv+setEnvVar = do+ prompt <- getShellPrompt+ lalias <- getAlias+ return (prompt, lalias)++----------------------------------------------------------------------------------+-- | PROMPT variable.+getShellPrompt :: IO String+getShellPrompt = do+ sp <- getEnv "PROMPT"+ case sp of+ Nothing -> return []+ Just a -> return a++----------------------------------------------------------------------------------+-- | ALIAS variable.+getAlias :: IO [Alias]+getAlias = do+ v <- getEnv "ALIAS"+ alias <- case v of{ Nothing -> return [] ; Just a -> return a }+ return $ map (\ s -> Alias { command = s !! 0, newcommand = s !! 1 })+ (aliaslist alias)+ where+ aliaslist = map (splitRegex (mkRegex ":")) . splitRegex (mkRegex "\\|")++----------------------------------------------------------------------------------+-- | Get the value of any variable.+getEnvVariable :: String -> IO String+getEnvVariable v = do+ env <- getEnv v+ case env of+ Nothing -> return []+ Just n -> return n
+ Hashell/Eval.hs view
@@ -0,0 +1,183 @@+{-+ Function to evaluate haskell and command <-> haskell expressions.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Eval (funcProcess, hEval)+ where++import Parse++import Control.Monad+import Data.Maybe (fromJust, isJust)+import System.Exit+import System.IO+import System.Process++import qualified GHC+-- import qualified Outputable+import qualified Packages+import qualified Module++import qualified Control.Exception as E++type Modules = [String]+type Expression = String+type Files = [String]+type Import = String+++{- the path of our GHC 6.8 installation; this obviously needs to be edited to+ point to the right place - the current value happens to be right for me, but+ for you it might be in /usr/lib/, ~/lib, ~/bin/lib... depending on where your+ GHC is installed. -}+ghcPath :: FilePath+ghcPath = "/usr/lib/ghc-6.8.2"++-- Good enough+-- path :: [FilePath]+-- path = ["/home/gwern/bin/bin", "/usr/local/bin", "/usr/lib64/ccache/bin", "/usr/bin", "/bin", "/opt/bin", "/usr/x86_64-pc-linux-gnu/gcc-bin/4.2.2", "/usr/games", "/usr/games/bin", "/usr/sbin", "/sbin", "/usr/share/surfraw"]++eval :: String -> [Import] -> IO (Maybe String)+-- eval = undefined+eval src _ = do+ -- start a new interactive session using the path specified above+ session <- GHC.newSession $ Just ghcPath+ -- initialize the default packages+ dflags1 <- GHC.getSessionDynFlags session++ (dflags2, _) <- Packages.initPackages dflags1+ GHC.setSessionDynFlags session dflags2{GHC.hscTarget=GHC.HscInterpreted}+-- loadImports session imps++ -- 2) load the standard prelude+ let modules = ["Prelude"]+ loadModules session modules+ result <- GHC.runStmt session src GHC.SingleStep+ case result of+ GHC.RunOk _ -> return $ Just "RunOK"+ _ -> return Nothing+-- where loadImports s as = mapM_ (load s) as+-- load sess i = do target <- GHC.guessTarget (i ++ ".hs") Nothing+-- GHC.addTarget sess target+-- GHC.load sess GHC.LoadAllTargets++-- Perhaps don't blindly load modules and claim they all succeeded...+loadModules :: GHC.Session -> [String] -> IO ()+loadModules session modul = do modules <- mapM (\m -> GHC.findModule session (GHC.mkModuleName m) Nothing) modul+ GHC.setContext session [] modules++----------------------------------------------------------------------------------++-- | Parse and evaluate a Haskell expression.+hEval :: Expression -> IO (Maybe String)+hEval expr = (getProcessTokens . getEvalOpt) expr >>= eMFEval++{-+ | Split and order the hashell expression into a haskell+ expression, a list of haskell modules and a list of files.+-}++-- | Split the hashell expression into Expression , Modules, Files.+getEvalOpt :: Expression -> (Expression, Modules, Files)+getEvalOpt (x:':':'(':xs) = let (ex, modules, files) = breakEvalOpt xs+ in+ (([x] ++ ex), modules, files)+getEvalOpt ('(':xs) = let (ex, modules, files) = breakEvalOpt xs+ in+ (ex, modules, files)+getEvalOpt xs = (xs, [], [])++breakEvalOpt :: String -> (Expression, Modules, Files)+breakEvalOpt xs = let (optexpr, ex) = (takeWhile (/= ')') xs, tail $ dropWhile (/= ')') xs)+ (modules, files) = splitEvalOpt (break (== '|') optexpr)+ in+ (ex, modules, files)++splitEvalOpt :: (String, String) -> (Modules, Files)+splitEvalOpt ([], []) = ([], [])+splitEvalOpt (zs, []) = (splitInto ',' zs , [])+splitEvalOpt ([], ys) = ([], splitInto ',' $ tail ys)+splitEvalOpt (zs, ys) = (splitInto ',' zs, splitInto ',' $ tail ys)++-- | Interpret the command haskell expressions, and concatenate its+-- result with the rest of the haskell expression.+getProcessTokens :: (Expression, Modules, Files) ->+ IO (Expression, Modules, Files)+getProcessTokens (a, b, xs)+ | findSubStr "(-" a && findSubStr "-)" a =+ do+ s <- iterProcess a+ return (s, b, xs)+ | otherwise = return (a, b, xs)++iterProcess :: String -> IO String+iterProcess [] = return []+iterProcess e+ | findSubStr "(-" e && findSubStr "-)" e+ = do+ let (b, p, a) = parseCH e+ processoutput <- funcProcess p+ iter <- iterProcess a+ return (b ++ (show processoutput) ++ iter)+ | otherwise = return e++-- | Run a commmand and get its output as a string.+-- This is the main function to bring command results into Hashell.+funcProcess :: String -> IO String+funcProcess fcmd = do+ (_,out,_,ph) <- runInteractiveProcess (getCmd fcmd) (getArg fcmd)+ Nothing Nothing+ output <- hGetContents out+ E.evaluate (length output)+ (waitForProcess ph) `E.catch` (\ _ -> return ExitSuccess)+ return output++----------------------------------------------------------------------------------+{- | Evaluate Expression, Modules and Files.+Return a string containing the proper haskell evaluation,+or nothing in case of errors.++All the different kind of evalutions return a string. -}+eMFEval :: (Expression, Modules, Files) -> IO (Maybe String)+eMFEval (ex, modul, files) =+ do+ hSetBuffering stdout NoBuffering+ case (ex `esc` '%') of -- | Clean special percents | --+ ('s':e)+ -> (eval (e) modul) >>= writeStdout+ ('n':e)+ -> (eval ("show $ " ++ e) modul) >>= writeStdout+ ('p':e)+ -> (eval ("show $ " ++ e) modul) >>= writeStdoutLn+ ('u':e)+ -> (eval e modul) >>= writeStdoutLn+ e -> (eval (e) modul :: IO (Maybe String)) >>= returnExpr+ where+ writeStdout = wrtS putStr files []+ writeStdoutLn = wrtS putStrLn files "\n"+ returnExpr n+ | isJust n = if null files+ then return n+ else writeProcFile (fromJust n) files >> return n+ | otherwise = return Nothing++{-+ | Functions to determine how to write the eMFEval result+-}++-- | Combine different ways of representing the eMFEval result.+wrtS :: (String -> IO ())+ -> [String]+ -> String+ -> Maybe String+ -> IO (Maybe String)+wrtS func files delim evalexpr+ | isJust evalexpr = if null files+ then func (fromJust evalexpr) >> return Nothing+ else writeProcFile ((++ delim) $ fromJust evalexpr) files >> return Nothing+ | otherwise = hPutStrLn stderr "error : eval returned Nothing." >> return Nothing++-- | Write expression evaluation to a list of files.+writeProcFile :: String -> (Files -> IO ())+writeProcFile s = mapM_ (flip (writeFile) s . concat . hParse)
+ Hashell/Exec.hs view
@@ -0,0 +1,219 @@+{-+ Command/Expression main entry execution fuctions.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Exec (mainExec)+ where++import Process (pipe, redirect)+import Eval (hEval, funcProcess)+import Parse (findSubStr, esc, getArg, getCmd, restoreWhiteSpace)+import Environment (ExecutionEnv(), setEnvVar, getEnvVariable, command, newcommand, getVariables, Alias())++import Data.Char (isAlpha, isSpace)+import Data.List (isPrefixOf)+import Data.Maybe (fromJust)+import System.Cmd (rawSystem)+import System.Console.Readline (addHistory, readline)+import System.Exit -- (ExitCode, ExitSuccess, ExitSuccess, ExitFailure)+import System.IO (hPutStr, stderr, hPutStrLn)+import System.Posix.Directory (changeWorkingDirectory)+import System.Posix.Env (putEnv)+import System.Posix.Files (fileExist, getFileStatus, isDirectory)+import Text.Regex (mkRegex, splitRegex)+import qualified Control.Exception as E (catch)++----------------------------------------------------------------------------------+-- | Basic command execution.+exec :: String -> IO ExitCode+exec e = rawSystem (getCmd e) (getArg e)++execA :: String -> [Alias] -> IO ExitCode+execA e [] = exec e+execA e (x:xs)+ | (command x) == (getCmd e) = exec $ (newcommand x) ++ (snd $ break (== ' ') e)+ | null xs = exec e+execA e (_:xs) = execA e xs++{-+ | Exec combination.+ These functions combine computations between each other+ to produce Haskell -> Process -> Haskell ... effects.+-}+----------------------------------------------------------------------------------+-- | Execute a Haskell expression.+execH :: String -> IO String+execH s = hEval s >>=+ (\ n -> case n of+ Just r+ -> return r+ Nothing+ -> ioError $ userError "hEval (execH): nothing.")++-- | Execute a process composed of sub-processes.+execP:: String -> IO String+execP es = if (length splitpexpr > 1)+ then+ hPComposition (reverse splitpexpr)+ else+ funcProcess es+ where+ splitpexpr = splitRegex (mkRegex " && ") es++-- | Iterates over a list of sub-processes.+-- Return the result of all the combinations as string.+hPComposition :: [String] -> IO String+hPComposition [] = return []+hPComposition (x:xs) = do+ s <- funcProcess x+ iterateProcess xs s+ where+ iterateProcess [] _ = return ""+ iterateProcess (y:[]) hr = funcProcess ((restoreWhiteSpace y) ++ hr)+ iterateProcess (y:ys) hr = do+ s' <- funcProcess ((restoreWhiteSpace y) ++ hr)+ iterateProcess ys s'++-- | Recursively combines the output from haskell expressions+-- evaluations and processes output execution.+haskellAndProcess :: [String] -> IO String+haskellAndProcess [] = return []+haskellAndProcess (x:xs) =+ case xs of+ [] -> do h' <- execH h+ putStr "[]"+ execP (p ++ h')+ ys | findSubStr "!" x -> do s <- haskellAndProcess ys+ h' <- execH (h ++ (show s))+ putStr s+ execP (p ++ h')+ | otherwise -> do s <- haskellAndProcess ys+ putStr $ s ++ " s2"+ execH (x ++ (show s))+ where+ [p, h] = splitRegex (mkRegex "!") x++----------------------------------------------------------------------------------+-- | 'cd' command implementation.+-- Only change to other directory if it exists.+changeWDIfExist :: String -> IO ExitCode+changeWDIfExist path =+ fileExist p >>=+ \ s -> case s of+ True ->+ do+ fs <- getFileStatus p+ case isDirectory fs of+ True -> changeWorkingDirectory p >>+ return ExitSuccess+ False -> putStrLn (p ++ " is not a directory.") >>+ return (ExitFailure 1)+ False -> do+ putStrLn $ "File " ++ p ++ " does not exist."+ return $ ExitFailure 1+ where+ p = filter (/= ' ') path++----------------------------------------------------------------------------------+-- | Evaluate the code for the shell prompt.+setupShellToolBar :: String -> IO [ExitCode]+setupShellToolBar pexpr = mapExec ([], []) pexpr++---------------------------------------------------------------------------------+-- | Add commands to readline history (omit blank lines).+promptHistory :: Maybe String -> IO String+promptHistory Nothing = return ""+promptHistory h =+ let fj = fromJust h in+ case fj of+ a | (words a /= []) -> addHistory a >> return a+ | otherwise -> return a++----------------------------------------------------------------------------------+-- | Entry function from Main.hs+-- It interfaces processing external options to the shell.+mainExec :: [(String, String)] -> IO ExitCode+mainExec cfg = getVariables cfg >> setEnvVar >>= execRoutine++{-+ | Main Hashell process.+ Functions mutually recursives.+ They cooperate to create the main loop of input -> eval -> output.+-}+----------------------------------------------------------------------------------+-- | Execute the shell.+-- Prompt the user for processing input.+execRoutine :: ExecutionEnv -> IO ExitCode+execRoutine execenv = setupShellToolBar (fst execenv) >>+ readline "$ " >>=+ promptHistory >>=+ mapExec execenv >>+ execRoutine execenv++----------------------------------------------------------------------------------+-- | Map a list of expressions into its execution+-- using the enviromment of the shell.+mapExec :: ExecutionEnv -> String -> IO [ExitCode]+mapExec execenv expr = mapM (execExpr execenv) xs+ where+ xs = map (dropWhile isSpace) (splitRegex (mkRegex ";") expr)++----------------------------------------------------------------------------------+-- | These functions work together to execute built-in commands+-- and evaluate complete hashell expressions, using the+-- shell environment.+execExpr :: ExecutionEnv -> String -> IO ExitCode+execExpr _ [] = return ExitSuccess -- Empty string passed to mapExec+execExpr execenv expr+ | "quit" `isPrefixOf` expr = exitWith ExitSuccess+ | "cd " `isPrefixOf` expr = changeWDIfExist (drop 3 expr)+ | "set " `isPrefixOf` expr = putEnv ((drop 4 expr) `esc` '\\') >>+ setEnvVar >>=+ execRoutine+ | "get " `isPrefixOf` expr = getEnvVariable (drop 4 expr) >>=+ putStrLn >>+ return ExitSuccess+ -- If it isn't a built-in, is a Haskell expression and goes to eval Expr+ | otherwise = evalExpr execenv expr++evalExpr :: ExecutionEnv -> String -> IO ExitCode+-- execExpr handles [] already+evalExpr _ (':':ep) -- look for :+ = (hEval ep+ `E.catch` (\err -> hPutStr stderr (show err) >>+ return Nothing ))+ >> return ExitSuccess+evalExpr execenv e+ -- Anything in "!()" will be evaluated as a Haskell expression.+ | findSubStr "!(" e =+ do let t = splitRegex (mkRegex " #") e+ hakproc <- haskellAndProcess t+ `E.catch` (\_ -> execRoutine execenv >> return [])+ putStr $ hakproc ++ " hakproc"+ return ExitSuccess+ | findSubStr " | " e && anyAlpha "\\|"+ = pipe e+ | findSubStr " > " e && anyAlpha ">"+ = redirect e ">"+ | findSubStr " 1> " e && anyAlpha "1>"+ = redirect e "1>"+ | findSubStr " < " e && anyAlpha "<"+ = redirect e "<"+ | findSubStr " 0> " e && anyAlpha "0>"+ = redirect e "0>"+ | findSubStr " >> " e && anyAlpha ">>"+ = redirect e ">>"+ | findSubStr " 2> " e && anyAlpha "2>"+ = redirect e "2>"+ | otherwise = (execA e (snd execenv) >>= execError)+ `E.catch` (\err -> hPutStrLn stderr (show err) >>+ execRoutine execenv)+ where+ anyAlpha s = any isAlpha $ last $ splitRegex (mkRegex s) e++----------------------------------------------------------------------------------+-- | Notify any error for the basic command execution.+execError :: ExitCode -> IO ExitCode+execError (ExitFailure c) = ioError $ userError (show c)+execError _ = return ExitSuccess
+ Hashell/Main.hs view
@@ -0,0 +1,29 @@+{-+ Main function. Starts shell.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Main+ where++import ConfigFile (globalConfig)+import Exec (mainExec)+import Options (programOptions)+import Signals (handler, setSignalHandler)+import System.Posix.Signals (Handler())++-- | Main function. Initialize the signal handlers and enter the main loop+-- of the program.+main :: IO ()+main = do+ initialize+ opts <- programOptions+ cfg <- globalConfig opts+ fhsLoop cfg+ where+ fhsLoop :: [(String, String)] -> IO b+ fhsLoop cfg = do handler $ mainExec cfg+ fhsLoop cfg++ initialize :: IO System.Posix.Signals.Handler+ initialize = setSignalHandler
+ Hashell/Options.hs view
@@ -0,0 +1,61 @@+{-+ Command line options.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Options+ where++import Data.List+import System.Console.GetOpt+import System.Environment+import System.Exit++----------------------------------------------------------------------------------+-- | Options data type.+data Flag = Help+ | Version+ | ConfigFile String+ deriving (Show, Eq, Ord)++----------------------------------------------------------------------------------+-- | Describe available command line options.+options :: [OptDescr Flag]+options = [ Option ['v'] ["version"] (NoArg Version) "version information" ,+ Option ['h'] ["help"] (NoArg Help) "this help" ,+ Option ['c'] ["configfile"] (ReqArg ConfigFile "cfgpath") "absolute path for hashell configuration file" ]++-- | Get the options from command line.+cOpts :: [String] -> IO [Flag]+cOpts argv =+ case getOpt Permute options argv of+ (opts, _, []) -> return . sort $ opts+ (_, _, errs) -> do putStr (concat errs)+ ioError $ userError "Incorrect options"++-- | Specify what to do with each option.+parseOpts :: [Flag] -> IO [String]+parseOpts [] = return []+parseOpts (Version:_) = do putStrLn version+ exitWith ExitSuccess+parseOpts (Help:_) = do putStrLn help+ exitWith ExitSuccess+parseOpts (ConfigFile cfg:_) = return [cfg]++-- | Represent the command line options passed to the shell+-- in such a way that can be used by other functions.+programOptions :: IO [String]+programOptions = getArgs >>= cOpts >>= parseOpts++----------------------------------------------------------------------------------+-- | Version information.+version :: String+version = "hashell 0.014a - haskell shell\n" +++ "http://www.haskell.org/hashell\n" +++ "Copyright (c) Luis Araujo , luis@arjox.org.\n" +++ "This software is released under the GPL.\n"++-- | Help menu generation.+help :: String+help = version ++ "\n" +++ (usageInfo "use: hashell [OPTIONS]" options)
+ Hashell/Parse.hs view
@@ -0,0 +1,117 @@+{-+ Parsing functions.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Parse+ where++import Text.ParserCombinators.Parsec+import qualified Text.ParserCombinators.Parsec.Prim as P (try)++----------------------------------------------------------------------------------+-- | Get command name.+getCmd :: String -> String+getCmd = concat . take 1 . words++-- | Get command line arguments.+getArg :: String -> [String]+getArg s = if all (== ' ') s then [] else tail $ hParse s++----------------------------------------------------------------------------------+-- | Main entry for parsing expressions (quote , special characters etc).+-- It takes the expression and returns it tokenized.+hParse :: String -> [String]+hParse = map (`esc` '\\')+ . filter (not . null)+ . concat+ . quotes++----------------------------------------------------------------------------------+-- | Use this function to restore a whitespace+-- in some expressions needed. (Those parsed with+-- splitRegex mainly).+restoreWhiteSpace :: (String -> String)+restoreWhiteSpace = (++ " ")++----------------------------------------------------------------------------------+-- | Split into a specific element.+splitInto :: (Ord a) => a -> [a] -> [[a]]+splitInto _ [] = []+splitInto c e = let (l , e') = break (== c) e+ in+ l : case e' of+ [] -> []+ (_:e'') -> splitInto c e''++----------------------------------------------------------------------------------+-- | Returns a boolean value if it finds+-- all the elements of a list.+findSubStr :: (Eq a) => [a] -> [a] -> Bool+findSubStr [] [] = False+findSubStr as bs = let f [] _ = True+ f _ [] = False+ f (a:as') (b:bs')+ | a == b = f as' bs'+ | a /= b = f as bs'+ f _ _ = False+ in+ f as bs++----------------------------------------------------------------------------------+-- | Concatenate a list with a specifc delimiter.+joinWith :: [String] -> String -> String+joinWith xs y = foldr (\ a b -> a ++ (if null b then [] else y ++ b)) [] xs++----------------------------------------------------------------------------------+-- | Escape the special character 'c' in the expression.+esc :: String -> Char -> String+esc [] _ = []+esc (x:y:xs) c+ | c == x = y : esc xs c+esc (x:xs) c = if x == c then [] else x : esc xs c++-- | Parse command haskell expressions.+type Terna = (String, String, String)++gParser :: Parser Terna -> String -> Terna+gParser p = f+ where+ f e = case (parse p "" e) of+ Left _ -> ("", "", (show e))+ Right t -> t++parseCH :: String -> Terna+parseCH = gParser comHaskell++comHaskell :: Parser Terna+comHaskell = do{ b <- manyTill anyChar (P.try (string "(-"))+ ; p <- manyTill anyChar (P.try (string "-)"))+ ; a <- many anyChar+ ; return (b, p, a)+ }++-- | Parse quotes.+parseQuote :: String -> Terna+parseQuote = gParser quote++quotes :: String -> [[String]]+quotes [] = []+quotes e | null b && null q && null a = []+ | otherwise = ((words b) : [q] : quotes a)+ where+ (b, q, a) = parseQuote e++escapeseq :: Parser String+escapeseq = (P.try $ string "''") <|>+ (P.try $ string "\\'")++quote :: Parser Terna+quote = do{ b <- many (noneOf ['\''])+ ; many (char '\'')+ ; s <- many $ (escapeseq <|> (noneOf "'"+ >>= (\x -> return [x])))+ ; many (char '\'')+ ; a <- many anyChar+ ; return (b, (concat s), a)+ }
+ Hashell/Process.hs view
@@ -0,0 +1,105 @@+{-+ Shell processes implementation.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Process+ where++import Parse++import Data.Char+import System.Exit+import System.IO+import System.Posix.Files (fileExist)+import System.Posix.IO+import System.Process+import Text.Regex++{-+ | Basic standard input (<) , output (>)+ redirection operations.+-}+----------------------------------------------------------------------------------+-- | Openfile and choose the type of redirection.+redirect :: String -> String -> IO ExitCode+redirect expr red+ | (red == ">" || red == "1>") = openFile filep WriteMode >>=+ (\ f -> chooseRedirect cmd Nothing (Just f) Nothing) >>+ return ExitSuccess+ | (red == "<" || red == "0>") = do+ v <- fileExist filep+ if v == True+ then openFile filep ReadMode >>=+ ( \ f -> chooseRedirect cmd (Just f) Nothing Nothing) >>+ return ExitSuccess+ else putStrLn "File does not exist." >>+ return (ExitFailure 1)+ | (red == ">>") = openFile filep AppendMode >>=+ ( \ f -> chooseRedirect cmd Nothing (Just f) Nothing) >>+ return ExitSuccess+ | (red == "2>") = openFile filep WriteMode >>=+ ( \ f -> chooseRedirect cmd Nothing Nothing (Just f)) >>+ return ExitSuccess+ | otherwise = return (ExitFailure 1) -- Not asked to do anything sensible. Try again.+ where+ redtokens = [ x | x <- splitRegex (mkRegex red) expr , isAlpha `any` x]+ filep = if length redtokens > 1+ then (!! 0) $ hParse (redtokens !! 1)+ else ""+ cmd = redtokens !! 0++chooseRedirect :: String+ -> Maybe Handle+ -> Maybe Handle+ -> Maybe Handle+ -> IO ExitCode+chooseRedirect cmdl inh outh errh = runProcess (getCmd cmdl) (getArg cmdl)+ Nothing Nothing inh outh errh >>=+ waitForProcess++{-+ | Basic pipe.+-}+----------------------------------------------------------------------------------+-- | Split into '|' and processes get connected through pipes.+pipe :: String -> IO ExitCode+pipe cmd = newPipe pipetokens Nothing+ where+ pipetokens = splitInto '|' cmd++-- | Iteratively process and connect each command with+-- standard unix pipes through filedescriptors.+newPipe :: [String] -> Maybe Handle -> IO ExitCode+newPipe (x:[]) (Just h) = runProcess (getCmd x) (getArg x)+ Nothing Nothing (Just h)+ Nothing Nothing >>= waitForProcess+newPipe (x:xs) (Just h) = do+ (hrf,hwt) <- initHandle+ runProcess (getCmd x) (getArg x)+ Nothing Nothing (Just h)+ (Just hwt) Nothing >>= waitForProcess+ newPipe xs (Just hrf)+newPipe (x:xs) Nothing = do+ (hrf, hwt) <- initHandle+ runProcess (getCmd x) (getArg x)+ Nothing Nothing Nothing+ (Just hwt) Nothing >>= waitForProcess+ newPipe xs (Just hrf)+newPipe _ _ = return ExitSuccess -- If not asked to do anything, we can do that easily.++-- | Create new pipes and connect them+-- to output and input filedescriptors.+initHandle :: IO (Handle,Handle)+initHandle = do+ (rf,wt) <- createPipe+ hrf <- fdToHandle rf+ hwt <- fdToHandle wt+ return (hrf,hwt)++----------------------------------------------------------------------------------+-- | Concatenate a string with the first element+-- of a list.+addAtFirst :: [String] -> String -> [String]+addAtFirst (x:xs) a = (x ++ " " ++ a) : xs+addAtFirst [] _ = [""]
+ Hashell/Signals.hs view
@@ -0,0 +1,27 @@+{-+ Signals handlers routines.+ Copyright (C) 2005, 2008 Luis Francisco Araujo+-}++module Signals+ where++import System.IO (hPutStrLn, stderr)+import System.Exit -- (ExitFailure, exitFailure)+import Control.Exception -- (ExitException, handle)+import System.Posix.Signals -- (Handler(), installHandler, sigINT, Catch())++----------------------------------------------------------------------------------++{- | Set the signal-handler in the program.+ It is used for catching the SIGINT signal+ and preserves the shell running. -}+setSignalHandler :: IO Handler+setSignalHandler = installHandler sigINT (Catch signalHandler) Nothing++signalHandler :: IO ()+signalHandler = return $ throw (ExitException (ExitFailure 2))++handler :: IO a -> IO a+handler = handle (\e -> do hPutStrLn stderr (show e)+ exitFailure)
+ LICENSE view
@@ -0,0 +1,340 @@+ GNU GENERAL PUBLIC LICENSE+ Version 2, June 1991++ Copyright (C) 1989, 1991 Free Software Foundation, Inc.+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+License is intended to guarantee your freedom to share and change free+software--to make sure the software is free for all its users. This+General Public License applies to most of the Free Software+Foundation's software and to any other program whose authors commit to+using it. (Some other Free Software Foundation software is covered by+the GNU Library General Public License instead.) You can apply it to+your programs, too.++ When we speak of free software, we are referring to freedom, not+price. Our General Public Licenses are designed to make sure that you+have the freedom to distribute copies of free software (and charge for+this service if you wish), that you receive source code or can get it+if you want it, that you can change the software or use pieces of it+in new free programs; and that you know you can do these things.++ To protect your rights, we need to make restrictions that forbid+anyone to deny you these rights or to ask you to surrender the rights.+These restrictions translate to certain responsibilities for you if you+distribute copies of the software, or if you modify it.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must give the recipients all the rights that+you have. You must make sure that they, too, receive or can get the+source code. And you must show them these terms so they know their+rights.++ We protect your rights with two steps: (1) copyright the software, and+(2) offer you this license which gives you legal permission to copy,+distribute and/or modify the software.++ Also, for each author's protection and ours, we want to make certain+that everyone understands that there is no warranty for this free+software. If the software is modified by someone else and passed on, we+want its recipients to know that what they have is not the original, so+that any problems introduced by others will not reflect on the original+authors' reputations.++ Finally, any free program is threatened constantly by software+patents. We wish to avoid the danger that redistributors of a free+program will individually obtain patent licenses, in effect making the+program proprietary. To prevent this, we have made it clear that any+patent must be licensed for everyone's free use or not licensed at all.++ The precise terms and conditions for copying, distribution and+modification follow.++ GNU GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License applies to any program or other work which contains+a notice placed by the copyright holder saying it may be distributed+under the terms of this General Public License. The "Program", below,+refers to any such program or work, and a "work based on the Program"+means either the Program or any derivative work under copyright law:+that is to say, a work containing the Program or a portion of it,+either verbatim or with modifications and/or translated into another+language. (Hereinafter, translation is included without limitation in+the term "modification".) Each licensee is addressed as "you".++Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running the Program is not restricted, and the output from the Program+is covered only if its contents constitute a work based on the+Program (independent of having been made by running the Program).+Whether that is true depends on what the Program does.++ 1. You may copy and distribute verbatim copies of the Program's+source code as you receive it, in any medium, provided that you+conspicuously and appropriately publish on each copy an appropriate+copyright notice and disclaimer of warranty; keep intact all the+notices that refer to this License and to the absence of any warranty;+and give any other recipients of the Program a copy of this License+along with the Program.++You may charge a fee for the physical act of transferring a copy, and+you may at your option offer warranty protection in exchange for a fee.++ 2. You may modify your copy or copies of the Program or any portion+of it, thus forming a work based on the Program, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) You must cause the modified files to carry prominent notices+ stating that you changed the files and the date of any change.++ b) You must cause any work that you distribute or publish, that in+ whole or in part contains or is derived from the Program or any+ part thereof, to be licensed as a whole at no charge to all third+ parties under the terms of this License.++ c) If the modified program normally reads commands interactively+ when run, you must cause it, when started running for such+ interactive use in the most ordinary way, to print or display an+ announcement including an appropriate copyright notice and a+ notice that there is no warranty (or else, saying that you provide+ a warranty) and that users may redistribute the program under+ these conditions, and telling the user how to view a copy of this+ License. (Exception: if the Program itself is interactive but+ does not normally print such an announcement, your work based on+ the Program is not required to print an announcement.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Program,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Program, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Program.++In addition, mere aggregation of another work not based on the Program+with the Program (or with a work based on the Program) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may copy and distribute the Program (or a work based on it,+under Section 2) in object code or executable form under the terms of+Sections 1 and 2 above provided that you also do one of the following:++ a) Accompany it with the complete corresponding machine-readable+ source code, which must be distributed under the terms of Sections+ 1 and 2 above on a medium customarily used for software interchange; or,++ b) Accompany it with a written offer, valid for at least three+ years, to give any third party, for a charge no more than your+ cost of physically performing source distribution, a complete+ machine-readable copy of the corresponding source code, to be+ distributed under the terms of Sections 1 and 2 above on a medium+ customarily used for software interchange; or,++ c) Accompany it with the information you received as to the offer+ to distribute corresponding source code. (This alternative is+ allowed only for noncommercial distribution and only if you+ received the program in object code or executable form with such+ an offer, in accord with Subsection b above.)++The source code for a work means the preferred form of the work for+making modifications to it. For an executable work, complete source+code means all the source code for all modules it contains, plus any+associated interface definition files, plus the scripts used to+control compilation and installation of the executable. However, as a+special exception, the source code distributed need not include+anything that is normally distributed (in either source or binary+form) with the major components (compiler, kernel, and so on) of the+operating system on which the executable runs, unless that component+itself accompanies the executable.++If distribution of executable or object code is made by offering+access to copy from a designated place, then offering equivalent+access to copy the source code from the same place counts as+distribution of the source code, even though third parties are not+compelled to copy the source along with the object code.++ 4. You may not copy, modify, sublicense, or distribute the Program+except as expressly provided under this License. Any attempt+otherwise to copy, modify, sublicense or distribute the Program is+void, and will automatically terminate your rights under this License.+However, parties who have received copies, or rights, from you under+this License will not have their licenses terminated so long as such+parties remain in full compliance.++ 5. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Program or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Program (or any work based on the+Program), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Program or works based on it.++ 6. Each time you redistribute the Program (or any work based on the+Program), the recipient automatically receives a license from the+original licensor to copy, distribute or modify the Program subject to+these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties to+this License.++ 7. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Program at all. For example, if a patent+license would not permit royalty-free redistribution of the Program by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Program.++If any portion of this section is held invalid or unenforceable under+any particular circumstance, the balance of the section is intended to+apply and the section as a whole is intended to apply in other+circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system, which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 8. If the distribution and/or use of the Program is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Program under this License+may add an explicit geographical distribution limitation excluding+those countries, so that distribution is permitted only in or among+countries not thus excluded. In such case, this License incorporates+the limitation as if written in the body of this License.++ 9. The Free Software Foundation may publish revised and/or new versions+of the General Public License from time to time. Such new versions will+be similar in spirit to the present version, but may differ in detail to+address new problems or concerns.++Each version is given a distinguishing version number. If the Program+specifies a version number of this License which applies to it and "any+later version", you have the option of following the terms and conditions+either of that version or of any later version published by the Free+Software Foundation. If the Program does not specify a version number of+this License, you may choose any version ever published by the Free Software+Foundation.++ 10. If you wish to incorporate parts of the Program into other free+programs whose distribution conditions are different, write to the author+to ask for permission. For software which is copyrighted by the Free+Software Foundation, write to the Free Software Foundation; we sometimes+make exceptions for this. Our decision will be guided by the two goals+of preserving the free status of all derivatives of our free software and+of promoting the sharing and reuse of software generally.++ NO WARRANTY++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,+REPAIR OR CORRECTION.++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE+POSSIBILITY OF SUCH DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software; you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation; either version 2 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program; if not, write to the Free Software+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA+++Also add information on how to contact you by electronic and paper mail.++If the program is interactive, make it output a short notice like this+when it starts in an interactive mode:++ Gnomovision version 69, Copyright (C) year name of author+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, the commands you use may+be called something other than `show w' and `show c'; they could even be+mouse-clicks or menu items--whatever suits your program.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the program, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program+ `Gnomovision' (which makes passes at compilers) written by James Hacker.++ <signature of Ty Coon>, 1 April 1989+ Ty Coon, President of Vice++This General Public License does not permit incorporating your program into+proprietary programs. If your program is a subroutine library, you may+consider it more useful to permit linking proprietary applications with the+library. If this is what you want to do, use the GNU Library General+Public License instead of this License.
+ README view
@@ -0,0 +1,45 @@+Hashell : A Haskell shell+-----------------------------------------+Version: 0.014a+Status: Alpha++Dependencies:++*The Glasgow Haskell Compiler+ >= 6.8 <http://haskell.org/ghc>+*Cabal+ (any version should work fine; <http://haskell.org/cabal>)+*Parsec+ >= 1.0 <http://www.cs.uu.nl/people/daan/parsec.html>++You can build and install Hashell like any other Haskell package, through Cabal; the following example installs as the current user to one's ~/bin directory, but to install elsewhere or as root, simply change the directories or remove the --user flags, respectively:++ $ runhaskell Setup configure --verbose=2 --user --prefix=$HOME/bin --datadir=$HOME/bin/share+ $ runhaskell Setup build+ $ runhaskell Setup install --user;++You need to have Cabal installed in your system.+Cabal will often be part of your GHC installation, but you can get a stand-alone version of Cabal from <http://haskell.org/cabal>.++NOTE! The current version of Hashell assumes you have GHC 6.8.2 installed, and that the output of 'ghc --print-libdir' = "/usr/lib/ghc-6.8.2/". If that's not the right directory, you must edit Hashell/Eval.hs and update the constant 'ghcPath'.++Usage:++ $ hashell++ or++ $ hashell -c <configurationfile>++You always can use the --help option for further help.++ $ hashell --help++The syntax is still fluid, but roughly, ';' is a terminator; an expression starting with ':' will be treated as a load-file command; one starting with "quit" will exit Hashell; one starting with "cd" will change the working directory; and an expression enclosed in a "!()" will be passed through the GHC API and evaluated as an arbitrary Haskell expression. All other commands should be treated as programs to be run.++License:+ GPL++Author:+ Luis Francisco Araujo+ luis@arjox.org
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ config_example view
@@ -0,0 +1,7 @@+-- Comments go after two hyphens++-- Set up the shell's prompt+PROMPT tput setaf 2 ;; echo \\ hashell ;; tput setaf 9++-- Aliases+ALIAS ls:ls --color|c:clear