{-# LANGUAGE OverloadedStrings , FlexibleInstances , StandaloneDeriving #-} {-| Utilities for working with shell script. -} module System.Posix.ARX.Sh ( Val(), val, Var(), var, setEU, Render(..), Raw(..) ) where import Control.Monad import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as Bytes import Data.Monoid import qualified Blaze.ByteString.Builder as Blaze import qualified Text.ShellEscape as Esc setEU :: Blaze.Builder setEU = "set -e -u\n" {-| Valid shell string values contain any byte but null. -} newtype Val = Val ByteString deriving instance Eq Val deriving instance Ord Val deriving instance Show Val instance Render Val where render (Val bytes) = (Blaze.fromByteString . Esc.bytes . Esc.sh) bytes instance Raw Val where raw (Val bytes) = bytes val :: ByteString -> Maybe Val val bytes = guard (Bytes.all (/= '\0') bytes) >> Just (Val bytes) {-| Valid shell variable names consist of a leading letter or underscore and then any number of letters, underscores or digits. -} newtype Var = Var ByteString deriving instance Eq Var deriving instance Ord Var deriving instance Show Var instance Render Var where render (Var bytes) = Blaze.fromByteString bytes instance Raw Var where raw (Var bytes) = bytes var :: ByteString -> Maybe Var var "" = Nothing var bytes = guard (leading h && Bytes.all body t) >> Just (Var bytes) where (h, t) = (Bytes.head bytes, Bytes.tail bytes) body c = leading c || (c >= '0' && c <= '9') leading c = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' instance Render [(Var, Val)] where render [ ] = mempty render ((k,v):t) = exportStatement `mappend` render t where exportStatement = mconcat ["export ", render k, "=", render v, "\n"] instance Render [Val] where render = mconcat . map (mappend " " . render) class Render t where render :: t -> Blaze.Builder class Raw t where raw :: t -> ByteString