packages feed

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 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