packages feed

yi-0.6.0: HConf/Utils.hs

-- | Utility functions/instances for HConf for which a better place has not
-- been found yet.
module HConf.Utils (shellWords, (+++), io) where

import Control.Monad.Trans (MonadIO, liftIO)
import Data.List (unfoldr)
import System.Console.GetOpt (ArgDescr(..), OptDescr(..))

(+++) :: Maybe [a] -> Maybe [a] -> Maybe [a]
Nothing +++ x = x
x +++ Nothing = x
(Just x) +++ (Just y) = Just (x ++ y)

-- | Lift an IO action
io :: MonadIO m => IO a -> m a
io = liftIO

-- | Break up a string the way a shell breaks up a command into arguments.
-- Similar to 'words', but respects quotes and escaped spaces.  TODO: Verify
-- this function.

shellWords :: String -> [String]
shellWords = filter (not.null) . unfoldr maybeLex

-- Helper functions for shellWords

maybeLex :: String -> Maybe (String,String)
maybeLex "" = Nothing
maybeLex s  = Just (shellLex s)

shellLex :: String -> (String,String)
shellLex ""          = ("","")
shellLex (' ':s)     = ("",s)
shellLex (c:s) | c `elem` ['\'','"']
                     = quotes c s
shellLex ('\\':x:s)  = (x:lexed,rest)       where (lexed,rest) = shellLex s
shellLex (x:s)       = (x:lexed,rest)       where (lexed,rest) = shellLex s

quotes :: Char -> String -> (String,String)
quotes _ ""             = ("","")  -- Unterminated quote (an error really)
quotes c (x:s) | x == c = ("",s)
quotes c ('\\':s)       = backslash c s
quotes c (x:s)          = (x:lexed,rest) where (lexed,rest) = quotes c s

backslash :: Char -> String -> (String,String)
backslash _ ""                = ("\\","") -- Trailing backslash?
backslash c (x:s) | c == x    = (x:lexed, rest)
                  | otherwise = ('\\' : x : lexed, rest)
                    where (lexed,rest) = quotes c s

-- Functor instances for data types from System.Console.GetOpt

instance Functor ArgDescr where
    fmap f (NoArg x)    = NoArg (f x)
    fmap f (ReqArg x s) = ReqArg (f . x) s
    fmap f (OptArg x s) = OptArg (f . x) s

instance Functor OptDescr where
    fmap f (Option short long argdescr usage) =
            Option short long (fmap f argdescr) usage