shell-escape 0.1.2 → 0.2.0
raw patch · 15 files changed
+265/−248 lines, 15 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.ByteString.ShellEscape: bash :: ByteString -> Bash
+ Data.ByteString.ShellEscape: bytes :: Escape t => t -> ByteString
+ Data.ByteString.ShellEscape: class Escape t
+ Data.ByteString.ShellEscape: data Bash
+ Data.ByteString.ShellEscape: data Sh
+ Data.ByteString.ShellEscape: escape :: Escape t => ByteString -> t
+ Data.ByteString.ShellEscape: sh :: ByteString -> Sh
+ Data.ByteString.ShellEscape: unescape :: Escape t => t -> ByteString
Files
- Data/ByteString/ShellEscape.hs +16/−0
- Data/ByteString/ShellEscape/Bash.hs +78/−0
- Data/ByteString/ShellEscape/Escape.hs +21/−0
- Data/ByteString/ShellEscape/EscapeVector.hs +39/−0
- Data/ByteString/ShellEscape/Put.hs +24/−0
- Data/ByteString/ShellEscape/Sh.hs +69/−0
- Text/ShellEscape.hs +8/−8
- Text/ShellEscape/Bash.hs +0/−78
- Text/ShellEscape/Escape.hs +0/−21
- Text/ShellEscape/EscapeVector.hs +0/−39
- Text/ShellEscape/Put.hs +0/−24
- Text/ShellEscape/Sh.hs +0/−69
- bench/Bench.hs +1/−1
- shell-escape.cabal +8/−7
- test/Echo.hs +1/−1
+ Data/ByteString/ShellEscape.hs view
@@ -0,0 +1,16 @@++{-| Typed shell escaping for Bourne Shell and Bash.+ -}++module Data.ByteString.ShellEscape+ ( Data.ByteString.ShellEscape.Escape.Escape(..)+ , Data.ByteString.ShellEscape.Sh.Sh()+ , Data.ByteString.ShellEscape.Sh.sh+ , Data.ByteString.ShellEscape.Bash.Bash()+ , Data.ByteString.ShellEscape.Bash.bash+ ) where++import Data.ByteString.ShellEscape.Escape+import Data.ByteString.ShellEscape.Sh+import Data.ByteString.ShellEscape.Bash+
+ Data/ByteString/ShellEscape/Bash.hs view
@@ -0,0 +1,78 @@++module Data.ByteString.ShellEscape.Bash where++import Data.Maybe+import Data.Char+import Text.Printf+import Data.ByteString (ByteString)++import qualified Data.Vector as Vector++import Data.ByteString.ShellEscape.Escape+import qualified Data.ByteString.ShellEscape.Put as Put+import Data.ByteString.ShellEscape.EscapeVector++{-| A Bash escaped 'ByteString'. The strings are wrapped in @$\'...\'@ if any+ bytes within them must be escaped; otherwise, they are left as is.+ Newlines and other control characters are represented as ANSI escape+ sequences. High bytes are represented as hex codes. Thus Bash escaped+ strings will always fit on one line and never contain non-ASCII bytes.+ -}+newtype Bash = Bash (EscapeVector EscapingMode)+ deriving (Eq, Ord, Show)++{-| Construct a Bash escaped intermediate form.+ -}+bash :: ByteString -> Bash+bash = escape++instance Escape Bash where+ escape = Bash . escWith classify+ unescape (Bash v) = stripEsc v+ bytes (Bash v) | literal v = stripEsc v+ | otherwise = interpretEsc v renderANSI' end (begin, Literal)+ where+ literal = isNothing . Vector.find ((/= Literal) . snd)+ begin = [ Put.putString "$'"]+ end = const (Put.putChar '\'')+ renderANSI' _ (c, e) = (renderANSI c, e)+++{-| Bash escaping modes.+ -}+data EscapingMode = ANSIHex | ANSIBackslash | Literal | Quoted+ deriving (Eq, Ord, Show)++renderANSI c =+ case classify c of+ Literal -> Put.putChar c+ Quoted -> Put.putChar c+ ANSIHex -> Put.putString $ hexify c+ ANSIBackslash -> Put.putString $ backslashify c++backslashify :: Char -> String+backslashify c = (take 2 . drop 1 . show) c++hexify :: Char -> String+hexify = printf "\\x%02X" . ord++classify :: Char -> EscapingMode+classify c | c <= '\ACK' = ANSIHex+ | c <= '\r' = ANSIBackslash+ | c <= '\US' = ANSIHex+ | c <= '&' = Quoted+ | c == '\'' = ANSIBackslash+ | c <= '+' = Quoted+ | c <= '9' = Literal+ | c <= '?' = Quoted+ | c <= 'Z' = Literal+ | c == '[' = Quoted+ | c == '\\' = ANSIBackslash+ | c <= ']' = Quoted+ | c == '_' = Literal+ | c <= '`' = Quoted+ | c <= 'z' = Literal+ | c <= '~' = Quoted+ | c == '\DEL' = ANSIHex+ | otherwise = ANSIHex+
+ Data/ByteString/ShellEscape/Escape.hs view
@@ -0,0 +1,21 @@++module Data.ByteString.ShellEscape.Escape where++import Data.ByteString++{-| A type class for objects that represent an intermediate state of+ escaping.+ -}+class Escape t where+ -- | Transform a 'ByteString' into the escaped intermediate form.+ escape :: ByteString -> t+ -- | Recover the original 'ByteString'.+ unescape :: t -> ByteString+ -- | Yield the escaped 'ByteString'.+ bytes :: t -> ByteString++instance Escape ByteString where+ escape = id+ unescape = id+ bytes = id+
+ Data/ByteString/ShellEscape/EscapeVector.hs view
@@ -0,0 +1,39 @@++module Data.ByteString.ShellEscape.EscapeVector where++import Data.ByteString.Char8 (ByteString)+import qualified Data.ByteString.Char8 as ByteString++import Data.Vector (Vector)+import qualified Data.Vector as Vector+import qualified Data.Vector.Mutable as Vector (new, write)++import qualified Data.ByteString.ShellEscape.Put as Put+++type EscapeVector escapingMode = Vector (Char, escapingMode)++escWith :: (Char -> escapingMode) -> ByteString -> EscapeVector escapingMode+escWith cf b = Vector.create $ do+ v <- Vector.new (ByteString.length b)+ sequence_ . snd $ ByteString.foldl' (f v) (0, []) b+ return v+ where+ f v (i, ops) c = (i + 1, Vector.write v i (c, cf c) : ops)+++stripEsc :: Vector (Char, escapingMode) -> ByteString+stripEsc v = ByteString.unfoldr f . fst $ Vector.unzip v+ where+ f v | Vector.null v = Nothing+ | otherwise = Just (Vector.unsafeHead v, Vector.unsafeTail v)++interpretEsc v f finish init = (eval . (finish lastMode :)) instructions+ where+ eval = Put.runPut' . sequence_ . reverse+ (instructions, lastMode) = Vector.foldl' f' init v+ where+ f' (list, mode) (c, e) = (put:list, mode')+ where+ (put, mode') = f mode (c, e)+
+ Data/ByteString/ShellEscape/Put.hs view
@@ -0,0 +1,24 @@++module Data.ByteString.ShellEscape.Put+ ( module Data.Binary.Put+ , putChar+ , putString+ , runPut'+ ) where++import Prelude hiding (putChar)+import Data.Binary.Put+import Data.Char+import Data.ByteString.Lazy+import Data.ByteString+import Data.ByteString.Char8++putChar :: Char -> Put+putChar = putWord8 . toEnum . fromEnum++putString :: String -> Put+putString = putByteString . Data.ByteString.Char8.pack++runPut' :: Put -> Data.ByteString.ByteString+runPut' = Data.ByteString.concat . Data.ByteString.Lazy.toChunks . runPut +
+ Data/ByteString/ShellEscape/Sh.hs view
@@ -0,0 +1,69 @@++module Data.ByteString.ShellEscape.Sh where++import Data.ByteString (ByteString)++import Data.ByteString.ShellEscape.Escape+import qualified Data.ByteString.ShellEscape.Put as Put+import Data.ByteString.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)+
Text/ShellEscape.hs view
@@ -3,14 +3,14 @@ -} module Text.ShellEscape- ( Text.ShellEscape.Escape.Escape(..)- , Text.ShellEscape.Sh.Sh()- , Text.ShellEscape.Sh.sh- , Text.ShellEscape.Bash.Bash()- , Text.ShellEscape.Bash.bash+ ( Data.ByteString.ShellEscape.Escape.Escape(..)+ , Data.ByteString.ShellEscape.Sh.Sh()+ , Data.ByteString.ShellEscape.Sh.sh+ , Data.ByteString.ShellEscape.Bash.Bash()+ , Data.ByteString.ShellEscape.Bash.bash ) where -import Text.ShellEscape.Escape-import Text.ShellEscape.Sh-import Text.ShellEscape.Bash+import Data.ByteString.ShellEscape.Escape+import Data.ByteString.ShellEscape.Sh+import Data.ByteString.ShellEscape.Bash
− Text/ShellEscape/Bash.hs
@@ -1,78 +0,0 @@--module Text.ShellEscape.Bash where--import Data.Maybe-import Data.Char-import Text.Printf-import Data.ByteString (ByteString)--import qualified Data.Vector as Vector--import Text.ShellEscape.Escape-import qualified Text.ShellEscape.Put as Put-import Text.ShellEscape.EscapeVector--{-| A Bash escaped 'ByteString'. The strings are wrapped in @$\'...\'@ if any- bytes within them must be escaped; otherwise, they are left as is.- Newlines and other control characters are represented as ANSI escape- sequences. High bytes are represented as hex codes. Thus Bash escaped- strings will always fit on one line and never contain non-ASCII bytes.- -}-newtype Bash = Bash (EscapeVector EscapingMode)- deriving (Eq, Ord, Show)--{-| Construct a Bash escaped intermediate form.- -}-bash :: ByteString -> Bash-bash = escape--instance Escape Bash where- escape = Bash . escWith classify- unescape (Bash v) = stripEsc v- bytes (Bash v) | literal v = stripEsc v- | otherwise = interpretEsc v renderANSI' end (begin, Literal)- where- literal = isNothing . Vector.find ((/= Literal) . snd)- begin = [ Put.putString "$'"]- end = const (Put.putChar '\'')- renderANSI' _ (c, e) = (renderANSI c, e)---{-| Bash escaping modes.- -}-data EscapingMode = ANSIHex | ANSIBackslash | Literal | Quoted- deriving (Eq, Ord, Show)--renderANSI c =- case classify c of- Literal -> Put.putChar c- Quoted -> Put.putChar c- ANSIHex -> Put.putString $ hexify c- ANSIBackslash -> Put.putString $ backslashify c--backslashify :: Char -> String-backslashify c = (take 2 . drop 1 . show) c--hexify :: Char -> String-hexify = printf "\\x%02X" . ord--classify :: Char -> EscapingMode-classify c | c <= '\ACK' = ANSIHex- | c <= '\r' = ANSIBackslash- | c <= '\US' = ANSIHex- | c <= '&' = Quoted- | c == '\'' = ANSIBackslash- | c <= '+' = Quoted- | c <= '9' = Literal- | c <= '?' = Quoted- | c <= 'Z' = Literal- | c == '[' = Quoted- | c == '\\' = ANSIBackslash- | c <= ']' = Quoted- | c == '_' = Literal- | c <= '`' = Quoted- | c <= 'z' = Literal- | c <= '~' = Quoted- | c == '\DEL' = ANSIHex- | otherwise = ANSIHex-
− Text/ShellEscape/Escape.hs
@@ -1,21 +0,0 @@--module Text.ShellEscape.Escape where--import Data.ByteString--{-| A type class for objects that represent an intermediate state of- escaping.- -}-class Escape t where- -- | Transform a 'ByteString' into the escaped intermediate form.- escape :: ByteString -> t- -- | Recover the original 'ByteString'.- unescape :: t -> ByteString- -- | Yield the escaped 'ByteString'.- bytes :: t -> ByteString--instance Escape ByteString where- escape = id- unescape = id- bytes = id-
− Text/ShellEscape/EscapeVector.hs
@@ -1,39 +0,0 @@--module Text.ShellEscape.EscapeVector where--import Data.ByteString.Char8 (ByteString)-import qualified Data.ByteString.Char8 as ByteString--import Data.Vector (Vector)-import qualified Data.Vector as Vector-import qualified Data.Vector.Mutable as Vector (new, write)--import qualified Text.ShellEscape.Put as Put---type EscapeVector escapingMode = Vector (Char, escapingMode)--escWith :: (Char -> escapingMode) -> ByteString -> EscapeVector escapingMode-escWith cf b = Vector.create $ do- v <- Vector.new (ByteString.length b)- sequence_ . snd $ ByteString.foldl' (f v) (0, []) b- return v- where- f v (i, ops) c = (i + 1, Vector.write v i (c, cf c) : ops)---stripEsc :: Vector (Char, escapingMode) -> ByteString-stripEsc v = ByteString.unfoldr f . fst $ Vector.unzip v- where- f v | Vector.null v = Nothing- | otherwise = Just (Vector.unsafeHead v, Vector.unsafeTail v)--interpretEsc v f finish init = (eval . (finish lastMode :)) instructions- where- eval = Put.runPut' . sequence_ . reverse- (instructions, lastMode) = Vector.foldl' f' init v- where- f' (list, mode) (c, e) = (put:list, mode')- where- (put, mode') = f mode (c, e)-
− Text/ShellEscape/Put.hs
@@ -1,24 +0,0 @@--module Text.ShellEscape.Put- ( module Data.Binary.Put- , putChar- , putString- , runPut'- ) where--import Prelude hiding (putChar)-import Data.Binary.Put-import Data.Char-import Data.ByteString.Lazy-import Data.ByteString-import Data.ByteString.Char8--putChar :: Char -> Put-putChar = putWord8 . toEnum . fromEnum--putString :: String -> Put-putString = putByteString . Data.ByteString.Char8.pack--runPut' :: Put -> Data.ByteString.ByteString-runPut' = Data.ByteString.concat . Data.ByteString.Lazy.toChunks . runPut -
− Text/ShellEscape/Sh.hs
@@ -1,69 +0,0 @@--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)-
bench/Bench.hs view
@@ -6,7 +6,7 @@ import Criterion.Main -import Text.ShellEscape+import Data.ByteString.ShellEscape strings :: [ByteString]
shell-escape.cabal view
@@ -1,5 +1,5 @@ name : shell-escape-version : 0.1.2+version : 0.2.0 category : Text license : BSD3 license-file : LICENSE@@ -24,10 +24,11 @@ , containers , vector >= 0.6.0.2 , bytestring >= 0.9- exposed-modules : Text.ShellEscape- other-modules : Text.ShellEscape.Escape- Text.ShellEscape.EscapeVector- Text.ShellEscape.Sh- Text.ShellEscape.Bash- Text.ShellEscape.Put+ exposed-modules : Data.ByteString.ShellEscape+ Text.ShellEscape+ other-modules : Data.ByteString.ShellEscape.Escape+ Data.ByteString.ShellEscape.EscapeVector+ Data.ByteString.ShellEscape.Sh+ Data.ByteString.ShellEscape.Bash+ Data.ByteString.ShellEscape.Put
test/Echo.hs view
@@ -18,7 +18,7 @@ import Test.QuickCheck import Test.QuickCheck.Monadic -import Text.ShellEscape hiding (sh, bash)+import Data.ByteString.ShellEscape hiding (sh, bash) -- It is best to implement the echo test with `printf':