fix an error when running an addon with an argument containing a single quote

More crazy quote engineering.. seems more robust than before.
This commit is contained in:
Simon Michael 2014-04-15 11:45:30 -07:00
parent 8454299666
commit 0d1d3ed057
3 changed files with 26 additions and 15 deletions

View File

@ -32,7 +32,7 @@ import Hledger.Utils
type RawOpts = [(String,String)]
setopt :: String -> String -> RawOpts -> RawOpts
setopt name val = (++ [(name,singleQuoteIfNeeded val)])
setopt name val = (++ [(name, quoteIfNeeded val)])
setboolopt :: String -> RawOpts -> RawOpts
setboolopt name = (++ [(name,"")])

View File

@ -33,6 +33,7 @@ module Hledger.Query (
matchesAccount,
matchesMixedAmount,
matchesAmount,
words'',
-- * tests
tests_Hledger_Query
)
@ -169,18 +170,17 @@ tests_parseQuery = [
words'' :: [String] -> String -> [String]
words'' prefixes = fromparse . parsewith maybeprefixedquotedphrases -- XXX
where
maybeprefixedquotedphrases = choice' [prefixedQuotedPattern, quotedPattern, pattern] `sepBy` many1 spacenonewline
maybeprefixedquotedphrases = choice' [prefixedQuotedPattern, singleQuotedPattern, doubleQuotedPattern, pattern] `sepBy` many1 spacenonewline
prefixedQuotedPattern = do
not' <- fromMaybe "" `fmap` (optionMaybe $ string "not:")
let allowednexts | null not' = prefixes
| otherwise = prefixes ++ [""]
next <- choice' $ map string allowednexts
let prefix = not' ++ next
p <- quotedPattern
p <- singleQuotedPattern <|> doubleQuotedPattern
return $ prefix ++ stripquotes p
quotedPattern = do
p <- between (oneOf "'\"") (oneOf "'\"") $ many $ noneOf "'\""
return $ stripquotes p
singleQuotedPattern = between (char '\'') (char '\'') (many $ noneOf "'") >>= return . stripquotes
doubleQuotedPattern = between (char '"') (char '"') (many $ noneOf "\"") >>= return . stripquotes
pattern = many (noneOf " \n\r")
tests_words'' = [

View File

@ -91,7 +91,7 @@ underline s = s' ++ replicate (length s) '-' ++ "\n"
| last s == '\n' = s
| otherwise = s ++ "\n"
-- | Wrap a string in single quotes, and \-prefix any embedded single
-- | Wrap a string in double quotes, and \-prefix any embedded single
-- quotes, if it contains whitespace and is not already single- or
-- double-quoted.
quoteIfSpaced :: String -> String
@ -99,6 +99,22 @@ quoteIfSpaced s | isSingleQuoted s || isDoubleQuoted s = s
| not $ any (`elem` s) whitespacechars = s
| otherwise = "'"++escapeSingleQuotes s++"'"
-- | Double-quote this string if it contains whitespace, single quotes
-- or double-quotes, escaping the quotes as needed.
quoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars) = "\"" ++ escapeDoubleQuotes s ++ "\""
| otherwise = s
-- | Single-quote this string if it contains whitespace or double-quotes.
-- No good for strings containing single quotes.
singleQuoteIfNeeded s | any (`elem` s) whitespacechars = "'"++s++"'"
| otherwise = s
quotechars = "'\""
whitespacechars = " \t\n\r"
escapeDoubleQuotes :: String -> String
escapeDoubleQuotes = regexReplace "\"" "\""
escapeSingleQuotes :: String -> String
escapeSingleQuotes = regexReplace "'" "\'"
@ -111,22 +127,17 @@ words' :: String -> [String]
words' "" = []
words' s = map stripquotes $ fromparse $ parsewith p s
where
p = do ss <- (quotedPattern <|> pattern) `sepBy` many1 spacenonewline
p = do ss <- (singleQuotedPattern <|> doubleQuotedPattern <|> pattern) `sepBy` many1 spacenonewline
-- eof
return ss
pattern = many (noneOf whitespacechars)
quotedPattern = between (oneOf "'\"") (oneOf "'\"") $ many $ noneOf "'\""
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 singleQuoteIfNeeded
-- | Single-quote this string if it contains whitespace or double-quotes
singleQuoteIfNeeded s | any (`elem` s) whitespacechars = "'"++s++"'"
| otherwise = s
whitespacechars = " \t\n\r"
-- | 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 $ tail s else s