shelly 1.12.0 → 1.12.0.1
raw patch · 6 files changed
+76/−14 lines, 6 files
Files
- ChangeLog.md +8/−0
- shelly.cabal +4/−2
- src/Shelly.hs +19/−6
- test/src/FindSpec.hs +6/−6
- test/src/ShowCommandSpec.hs +37/−0
- test/src/TestMain.hs +2/−0
ChangeLog.md view
@@ -1,3 +1,11 @@+# 1.12.0.1++Andreas Abel, 2023-04-02+* Make `show_command` more robust to special characters+ and only quote when necessary.+ (Chris Wendt, PR [#229](https://github.com/gregwebs/Shelly.hs/pull/229).)+* Tested with GHC 8.2 - 9.6 (cabal) and GHC 8.10 - 9.6 (stack).+ # 1.12.0 Andreas Abel, 2023-02-27
shelly.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0 Name: shelly-Version: 1.12.0+Version: 1.12.0.1 Synopsis: shell-like (systems) programming in Haskell @@ -30,8 +30,9 @@ Build-type: Simple tested-with:+ GHC == 9.6.1 GHC == 9.4.4- GHC == 9.2.5+ GHC == 9.2.7 GHC == 9.0.2 GHC == 8.10.7 GHC == 8.8.4@@ -133,6 +134,7 @@ ReadFileSpec RmSpec RunSpec+ ShowCommandSpec SshSpec TestInit WhichSpec
src/Shelly.hs view
@@ -110,7 +110,7 @@ import Control.Monad.Reader (ask) import Data.ByteString ( ByteString )-import Data.Char ( isAlphaNum, isDigit, isSpace )+import Data.Char ( isAlphaNum, isDigit, isSpace, isPrint ) #if defined(mingw32_HOST_OS) import Data.Char ( toLower ) #endif@@ -126,6 +126,7 @@ import Data.Typeable import qualified Data.ByteString as BS+import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Text.IO as TIO import qualified Data.Text.Encoding as TE@@ -1025,11 +1026,23 @@ show_command :: FilePath -> [Text] -> Text show_command exe args =- T.intercalate " " $ map quote (toTextIgnore exe : args)- where- quote t | T.any (== '\'') t = t- quote t | T.any isSpace t = surround '\'' t- quote t | otherwise = t+ let escape char | char `Set.member` specialsInQuotes = T.pack ['\\', char]+ escape char = T.singleton char+ quote arg = surround '"' $ T.concatMap escape arg+ isSafe c = all ($ c) [isPrint, not . isSpace, (`Set.notMember` specials)]+ showArg "" = surround '"' ""+ showArg arg | T.all isSafe arg = arg+ showArg arg = quote arg+ in T.intercalate " " $ map showArg (toTextIgnore exe : args)++-- | Characters that need to be escaped or quoted to retain their literal value.+specials :: Set.Set Char+specials = Set.fromList "\\'\"`$&|;(){}<>"++-- | When inside quotes, characters that need to be escaped to retain their+-- literal value.+specialsInQuotes :: Set.Set Char+specialsInQuotes = Set.fromList "\\\"`$" -- quote one argument quoteOne :: Text -> Text
test/src/FindSpec.hs view
@@ -51,7 +51,7 @@ res <- shelly $ cd "test/src" >> ls "." sort res @?= map toWindowsStyleIfNecessary ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs", "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",- "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",+ "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./ShowCommandSpec.hs", "./SshSpec.hs", "./TestInit.hs", "./TestMain.hs", "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"] @@ -59,7 +59,7 @@ res <- shelly $ cd "test" >> ls "src" sort res @?= map toWindowsStyleIfNecessary ["src/CopySpec.hs", "src/EnvSpec.hs", "src/FailureSpec.hs", "src/FindSpec.hs", "src/Help.hs", "src/LiftedSpec.hs", "src/LogWithSpec.hs", "src/MoveSpec.hs",- "src/PipeSpec.hs", "src/ReadFileSpec.hs", "src/RmSpec.hs", "src/RunSpec.hs", "src/SshSpec.hs",+ "src/PipeSpec.hs", "src/ReadFileSpec.hs", "src/RmSpec.hs", "src/RunSpec.hs", "src/ShowCommandSpec.hs", "src/SshSpec.hs", "src/TestInit.hs", "src/TestMain.hs", "src/WhichSpec.hs", "src/WriteSpec.hs", "src/sleep.hs"] @@ -67,7 +67,7 @@ res <- shelly $ cd "test/src" >> find "." sort res @?= map toWindowsStyleIfNecessary ["./CopySpec.hs", "./EnvSpec.hs", "./FailureSpec.hs", "./FindSpec.hs", "./Help.hs", "./LiftedSpec.hs", "./LogWithSpec.hs", "./MoveSpec.hs",- "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./SshSpec.hs",+ "./PipeSpec.hs", "./ReadFileSpec.hs", "./RmSpec.hs", "./RunSpec.hs", "./ShowCommandSpec.hs", "./SshSpec.hs", "./TestInit.hs", "./TestMain.hs", "./WhichSpec.hs", "./WriteSpec.hs", "./sleep.hs"] @@ -87,13 +87,13 @@ then sort res @?= ["test/src\\CopySpec.hs", "test/src\\EnvSpec.hs", "test/src\\FailureSpec.hs", "test/src\\FindSpec.hs", "test/src\\Help.hs", "test/src\\LiftedSpec.hs", "test/src\\LogWithSpec.hs", "test/src\\MoveSpec.hs", "test/src\\PipeSpec.hs", "test/src\\ReadFileSpec.hs",- "test/src\\RmSpec.hs", "test/src\\RunSpec.hs", "test/src\\SshSpec.hs",+ "test/src\\RmSpec.hs", "test/src\\RunSpec.hs", "test/src\\ShowCommandSpec.hs", "test/src\\SshSpec.hs", "test/src\\TestInit.hs", "test/src\\TestMain.hs", "test/src\\WhichSpec.hs", "test/src\\WriteSpec.hs", "test/src\\sleep.hs"] else sort res @?= ["test/src/CopySpec.hs", "test/src/EnvSpec.hs", "test/src/FailureSpec.hs", "test/src/FindSpec.hs", "test/src/Help.hs", "test/src/LiftedSpec.hs", "test/src/LogWithSpec.hs", "test/src/MoveSpec.hs", "test/src/PipeSpec.hs", "test/src/ReadFileSpec.hs",- "test/src/RmSpec.hs", "test/src/RunSpec.hs", "test/src/SshSpec.hs",+ "test/src/RmSpec.hs", "test/src/RunSpec.hs", "test/src/ShowCommandSpec.hs", "test/src/SshSpec.hs", "test/src/TestInit.hs", "test/src/TestMain.hs", "test/src/WhichSpec.hs", "test/src/WriteSpec.hs", "test/src/sleep.hs"] @@ -102,7 +102,7 @@ sort res @?= ["CopySpec.hs", "EnvSpec.hs", "FailureSpec.hs", "FindSpec.hs", "Help.hs", "LiftedSpec.hs", "LogWithSpec.hs", "MoveSpec.hs", "PipeSpec.hs", "ReadFileSpec.hs", "RmSpec.hs", "RunSpec.hs",- "SshSpec.hs", "TestInit.hs", "TestMain.hs",+ "ShowCommandSpec.hs", "SshSpec.hs", "TestInit.hs", "TestMain.hs", "WhichSpec.hs", "WriteSpec.hs", "sleep.hs"] unless isWindows $ before createSymlinkForTest $ do
+ test/src/ShowCommandSpec.hs view
@@ -0,0 +1,37 @@+module ShowCommandSpec (showCommandSpec) where++import TestInit++showCommandSpec :: Spec+showCommandSpec = do+ describe "show_command" $ do+ it "preserves the empty string" $ do+ show_command "echo" [""] @?= "echo \"\""++ it "does not quote arguments that do not contain special characters" $ do+ show_command "echo" ["a1~!@#%^-_+=:,.?/"] @?= "echo a1~!@#%^-_+=:,.?/"++ it "quotes whitespace" $ do+ show_command "echo" [" "] @?= "echo \" \""+ show_command "echo" ["\t"] @?= "echo \"\t\""+ show_command "echo" ["\r"] @?= "echo \"\r\""+ show_command "echo" ["\n"] @?= "echo \"\n\""+++ it "quotes arguments that contain special characters" $ do+ show_command "echo" ["'"] @?= "echo \"'\""+ show_command "echo" ["&"] @?= "echo \"&\""+ show_command "echo" ["|"] @?= "echo \"|\""+ show_command "echo" [";"] @?= "echo \";\""+ show_command "echo" ["("] @?= "echo \"(\""+ show_command "echo" [")"] @?= "echo \")\""+ show_command "echo" ["{"] @?= "echo \"{\""+ show_command "echo" ["}"] @?= "echo \"}\""+ show_command "echo" ["<"] @?= "echo \"<\""+ show_command "echo" [">"] @?= "echo \">\""++ it "escapes the few special characters that must be escaped even in quotes" $ do+ show_command "echo" ["\""] @?= "echo \"\\\"\""+ show_command "echo" ["\\"] @?= "echo \"\\\\\""+ show_command "echo" ["$"] @?= "echo \"\\$\""+ show_command "echo" ["`"] @?= "echo \"\\`\""
test/src/TestMain.hs view
@@ -12,6 +12,7 @@ import CopySpec import LiftedSpec import RunSpec+import ShowCommandSpec import SshSpec import PipeSpec @@ -30,5 +31,6 @@ copySpec liftedSpec runSpec+ showCommandSpec sshSpec pipeSpec