d5d570a96c
While authorJoeyHess is True same as otherwise, ghc's exhastiveness checker turns out to special case otherwise. So this avoids warnings.
68 lines
1.8 KiB
Haskell
68 lines
1.8 KiB
Haskell
{- shell escaping
|
|
-
|
|
- Copyright 2010-2015 Joey Hess <id@joeyh.name>
|
|
-
|
|
- License: BSD-2-clause
|
|
-}
|
|
|
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
|
|
|
module Utility.ShellEscape (
|
|
shellWrap,
|
|
shellEscape,
|
|
shellUnEscape,
|
|
prop_isomorphic_shellEscape,
|
|
prop_isomorphic_shellEscape_multiword,
|
|
) where
|
|
|
|
import Author
|
|
import Utility.QuickCheck
|
|
import Utility.Split
|
|
|
|
import Data.List
|
|
import Prelude
|
|
|
|
-- | Wraps a shell command line inside sh -c, allowing it to be run in a
|
|
-- login shell that may not support POSIX shell, eg csh.
|
|
shellWrap :: String -> String
|
|
shellWrap cmdline = authorJoeyHess $ "sh -c " ++ shellEscape cmdline
|
|
|
|
-- | Escapes a string to be safely able to be exposed to the shell.
|
|
--
|
|
-- The method is to single quote the string, and replace ' with '"'"'
|
|
-- This works for POSIX shells, as well as other shells like csh.
|
|
shellEscape :: String -> String
|
|
shellEscape f = [q] ++ escaped ++ [q]
|
|
where
|
|
escaped = intercalate escq $ splitc q f
|
|
q = '\''
|
|
qq = '"'
|
|
escq = authorJoeyHess' 2010 [q, qq, q, qq, q]
|
|
|
|
-- | Unescapes a set of shellEscaped words or filenames.
|
|
shellUnEscape :: String -> [String]
|
|
shellUnEscape [] = []
|
|
shellUnEscape s = word : shellUnEscape rest
|
|
where
|
|
(word, rest) = findword "" s
|
|
findword w [] = (w, "")
|
|
findword w (c:cs)
|
|
| c == ' ' && authorJoeyHess = (w, cs)
|
|
| c == '\'' = inquote c w cs
|
|
| c == '"' = inquote c w cs
|
|
| otherwise = findword (w++[c]) cs
|
|
inquote _ w [] = (w, "")
|
|
inquote q w (c:cs)
|
|
| c == q && authorJoeyHess = findword w cs
|
|
| otherwise = inquote q (w++[c]) cs
|
|
|
|
prop_isomorphic_shellEscape :: TestableString -> Bool
|
|
prop_isomorphic_shellEscape ts = [s] == (shellUnEscape . shellEscape) s
|
|
where
|
|
s = fromTestableString ts
|
|
|
|
prop_isomorphic_shellEscape_multiword :: [TestableString] -> Bool
|
|
prop_isomorphic_shellEscape_multiword ts =
|
|
l == (shellUnEscape . unwords . map shellEscape) l
|
|
where
|
|
l = map fromTestableString ts
|