module Text.ShellEscape.Sh where import Data.ByteString (ByteString) import Text.ShellEscape.Escape import qualified Text.ShellEscape.Put as Put import Text.ShellEscape.EscapeVector {-| A Bourne Shell escaped 'ByteString'. An oddity of Bourne shell escaping is the absence of escape codes for newline and other ASCII control characters. These bytes are simply placed literally in single quotes; the effect is that a Bourne Shell escaped string may cover several lines and contain non-ASCII bytes. Runs of bytes that must be escaped are wrapped in @\'...\'@; bytes that are acceptable as literals in Bourne Shell are left as is. -} newtype Sh = Sh (EscapeVector EscapingMode) deriving (Eq, Ord, Show) {-| Construct a Bourne Shell escaped intermediate form. -} sh :: ByteString -> Sh sh = escape instance Escape Sh where escape = Sh . escWith classify unescape (Sh v) = stripEsc v bytes (Sh v) = interpretEsc v act finish ([], Literal) where finish Quote = Put.putChar '\'' finish Backslash = Put.putChar '\\' finish Literal = return () {-| Accept the present escaping mode and desired escaping mode and yield an action and the resulting mode. -} act :: EscapingMode -> (Char, EscapingMode) -> (Put.Put, EscapingMode) act Quote (c, Quote) = (Put.putChar c , Quote) act Quote (c, Literal) = (Put.putChar c , Quote) act Quote (c, Backslash) = (Put.putString ['\'', '\\', c] , Literal) act Backslash (c, Backslash) = (Put.putChar c , Literal) act Backslash (c, Quote) = (Put.putString ['\\', '\'', c] , Quote) act Backslash (c, Literal) = (Put.putString ['\\', c] , Literal) act Literal (c, Literal) = (Put.putChar c , Literal) act Literal (c, Backslash) = (Put.putString ['\\', c] , Literal) act Literal (c, Quote) = (Put.putString ['\'', c] , Quote) classify :: Char -> EscapingMode classify c | c <= '&' = Quote -- 0x00..0x26 | c == '\'' = Backslash -- 0x27 | c <= ',' = Quote -- 0x28..0x2c | c <= '9' = Literal -- 0x2d..0x39 | c <= '?' = Quote -- 0x3a..0x3f | c <= 'Z' = Literal -- 0x40..0x5a | c <= '^' = Quote -- 0x5b..0x5e | c == '_' = Literal -- 0x5f | c == '`' = Quote -- 0x60 | c <= 'z' = Literal -- 0x61..0x7a | c <= '\DEL' = Quote -- 0x7b..0x7f | otherwise = Quote -- 0x80..0xff {-| Bourne Shell escaping modes. -} data EscapingMode = Backslash | Literal | Quote deriving (Eq, Ord, Show)