Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/Streamly/Internal/System/Command.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,14 @@ module Streamly.Internal.System.Command
, foreground
, daemon

-- * Helpers
, quotedWord
-- * Low-level Functions
, shellWord
, runWith
, streamWith
, pipeWith

-- * Deprecated
, quotedWord
)
where

Expand All @@ -63,7 +66,10 @@ import qualified Streamly.Internal.System.Process as Process

#include "DocTestCommand.hs"

-- | Posix compliant quote escaping:
-- | Posix compliant quoted shell command string parsing with escaping:
--
-- Quotes and escapes are parsed as in a standard POSIX shell.
-- Examples (using shell @echo@ command):
--
-- $ echo 'hello\\"world'
-- hello\\"world
Expand All @@ -73,9 +79,9 @@ import qualified Streamly.Internal.System.Process as Process
--
-- $ echo 'hello\'
-- hello\
{-# INLINE quotedWord #-}
quotedWord :: MonadCatch m => Parser Char m String
quotedWord =
{-# INLINE shellWord #-}
shellWord :: MonadCatch m => Parser Char m String
shellWord =
let toRQuote x =
case x of
'"' -> Just x
Expand All @@ -92,6 +98,11 @@ quotedWord =
trEsc _ _ = Nothing
in Parser.wordWithQuotes False trEsc '\\' toRQuote isSpace Fold.toList

{-# DEPRECATED quotedWord "Use shellWord instead." #-}
{-# INLINE quotedWord #-}
quotedWord :: MonadCatch m => Parser Char m String
quotedWord = shellWord

-- | A modifier for stream generation APIs in "Streamly.System.Process" to
-- generate streams from command strings.
--
Expand All @@ -110,7 +121,7 @@ streamWith f cmd =
Stream.concatEffect $ do
xs <- Stream.fold Fold.toList
$ Stream.catRights
$ Stream.parseMany quotedWord
$ Stream.parseMany shellWord
$ Stream.fromList cmd
case xs of
y:ys -> return $ f y ys
Expand All @@ -133,7 +144,7 @@ runWith :: MonadCatch m =>
runWith f cmd = do
xs <- Stream.fold Fold.toList
$ Stream.catRights
$ Stream.parseMany quotedWord
$ Stream.parseMany shellWord
$ Stream.fromList cmd
case xs of
y:ys -> f y ys
Expand Down Expand Up @@ -161,7 +172,7 @@ pipeWith f cmd input =
Stream.concatEffect $ do
xs <- Stream.fold Fold.toList
$ Stream.catRights
$ Stream.parseMany quotedWord
$ Stream.parseMany shellWord
$ Stream.fromList cmd
case xs of
y:ys -> return $ f y ys input
Expand Down
22 changes: 11 additions & 11 deletions test/Streamly/System/Process.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import qualified Streamly.System.Process as Proc
import qualified Streamly.Data.Stream as Stream

import qualified Streamly.Internal.FileSystem.Handle as FH (putBytes, read)
import qualified Streamly.Internal.System.Command as Cmd (quotedWord)
import qualified Streamly.Internal.System.Command as Cmd (shellWord)

-------------------------------------------------------------------------------
-- Compatibility
Expand Down Expand Up @@ -486,12 +486,12 @@ pipeChunksEither4 = monadicIO $ run checkFailAction

checkFailAction = catch action failAction

quotedWordTest :: String -> [String] -> IO ()
quotedWordTest inp expected = do
shellWordTest :: String -> [String] -> IO ()
shellWordTest inp expected = do
res <-
Stream.fold Fold.toList
$ Stream.catRights
$ Stream.parseMany Cmd.quotedWord $ Stream.fromList inp
$ Stream.parseMany Cmd.shellWord $ Stream.fromList inp
res `shouldBe` expected

main :: IO ()
Expand Down Expand Up @@ -551,14 +551,14 @@ main = do
prop "toBytesEither cat = FH.toBytes" toBytes1
prop "toBytesEither on failing executable" toBytes2

describe "quotedWord" $ do
describe "shellWord" $ do
it "Single quote test" $
quotedWordTest "'hello\\\\\"world'" ["hello\\\\\"world"]
shellWordTest "'hello\\\\\"world'" ["hello\\\\\"world"]
it "Double quote test" $
quotedWordTest
shellWordTest
"\"hello\\\"\\\\w\\'orld\""
["hello\"\\w\\'orld"]
-- TODO: We need to let the escape character be at the end
-- "wordWithQuotes" needs to be fixed!
-- it "Double quote test" $
-- quotedWordTest "'hello\'" ["hello\\"]
#if MIN_VERSION_streamly_core(0,3,0)
it "Double quote test, escape at end" $
shellWordTest "'hello\\'" ["hello\\"]
#endif