diff --git a/hledger-lib/Hledger/Data/RawOptions.hs b/hledger-lib/Hledger/Data/RawOptions.hs index 43de5be10..33b4c77fc 100644 --- a/hledger-lib/Hledger/Data/RawOptions.hs +++ b/hledger-lib/Hledger/Data/RawOptions.hs @@ -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,"")]) diff --git a/hledger-lib/Hledger/Query.hs b/hledger-lib/Hledger/Query.hs index 5a8dff3f3..edc5dc432 100644 --- a/hledger-lib/Hledger/Query.hs +++ b/hledger-lib/Hledger/Query.hs @@ -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'' = [ diff --git a/hledger-lib/Hledger/Utils.hs b/hledger-lib/Hledger/Utils.hs index f9ce36112..34d66c43c 100644 --- a/hledger-lib/Hledger/Utils.hs +++ b/hledger-lib/Hledger/Utils.hs @@ -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