fix: Only escape special characters by single quoting, not escaping *and* quoting

This commit is contained in:
Caleb Maclennan 2025-10-02 09:50:33 -06:00 committed by Simon Michael
parent 8cbe4c6003
commit 3c731ae2f7
2 changed files with 8 additions and 19 deletions

View File

@ -15,7 +15,6 @@ module Hledger.Utils.String (
-- quotechars,
-- whitespacechars,
words',
unwords',
stripAnsi,
-- * single-line layout
strip,
@ -165,26 +164,25 @@ singleQuote s = "'"++s++"'"
-- >>> quoteForCommandLine "\""
-- "'\"'"
-- >>> quoteForCommandLine "$"
-- "'\\$'"
-- "'$'"
--
quoteForCommandLine :: String -> String
quoteForCommandLine s
| any (`elem` s) (quotechars++whitespacechars++shellchars) = singleQuote $ quoteShellChars s
| any (`elem` s) (quotechars++whitespacechars++shellchars) = singleQuote $ escapeSingleQuotes s
| otherwise = s
-- | Try to backslash-quote common shell-significant characters in this string.
-- Doesn't handle single quotes, & probably others.
quoteShellChars :: String -> String
quoteShellChars = concatMap escapeShellChar
-- | Escape single quotes appearing in a string we're protecting by wrapping in single quotes
escapeSingleQuotes :: String -> String
escapeSingleQuotes = concatMap escapeSingleQuote
where
escapeShellChar c | c `elem` shellchars = ['\\',c]
escapeShellChar c = [c]
escapeSingleQuote c | c `elem` "'" = ['\\',c]
escapeSingleQuote c = [c]
quotechars, whitespacechars, redirectchars, shellchars :: [Char]
quotechars = "'\""
whitespacechars = " \t\n\r"
redirectchars = "<>"
shellchars = "<>(){}[]$&?#!~`"
shellchars = "<>(){}[]$&?#!~`*+\\"
-- | Quote-aware version of words - don't split on spaces which are inside quotes.
-- NB correctly handles "a'b" but not "''a''". Can raise an error if parsing fails.
@ -198,10 +196,6 @@ words' s = map stripquotes $ fromparse $ parsewithString p s -- PARTIAL
singleQuotedPattern = between (char '\'') (char '\'') (many $ noneOf "'")
doubleQuotedPattern = between (char '"') (char '"') (many $ noneOf "\"")
-- | Quote-aware version of unwords - single-quote strings which contain whitespace
unwords' :: [String] -> String
unwords' = unwords . map quoteIfNeeded
-- | Strip one matching pair of single or double quotes on the ends of a string.
stripquotes :: String -> String
stripquotes s = if isSingleQuoted s || isDoubleQuoted s then init $ tailErr s else s -- PARTIAL tailErr won't fail because isDoubleQuoted

View File

@ -23,7 +23,6 @@ module Hledger.Utils.Text
-- escapeSingleQuotes,
-- escapeQuotes,
-- words',
-- unwords',
stripquotes,
-- isSingleQuoted,
-- isDoubleQuoted,
@ -162,10 +161,6 @@ escapeBackslash = T.replace "\\" "\\\\"
-- singleQuotedPattern = between (char '\'') (char '\'') (many $ noneOf "'")
-- doubleQuotedPattern = between (char '"') (char '"') (many $ noneOf "\"")
-- -- | Quote-aware version of unwords - single-quote strings which contain whitespace
-- unwords' :: [Text] -> Text
-- unwords' = T.unwords . map quoteIfNeeded
-- | Strip one matching pair of single or double quotes on the ends of a string.
stripquotes :: Text -> Text
stripquotes s = if isSingleQuoted s || isDoubleQuoted s then T.init $ T.tail s else s