From 442ef9361c7627e6a6b7ceb9d92dddeb728cf10b Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Sun, 31 Jul 2022 08:21:56 +0100 Subject: [PATCH] feat: api: quoteForCommandLine: some very shady CLI escaping --- hledger-lib/Hledger/Utils/String.hs | 36 ++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/hledger-lib/Hledger/Utils/String.hs b/hledger-lib/Hledger/Utils/String.hs index eca20c59a..6edab75c2 100644 --- a/hledger-lib/Hledger/Utils/String.hs +++ b/hledger-lib/Hledger/Utils/String.hs @@ -10,6 +10,7 @@ module Hledger.Utils.String ( -- quoting quoteIfNeeded, singleQuoteIfNeeded, + quoteForCommandLine, -- quotechars, -- whitespacechars, words', @@ -118,15 +119,44 @@ quoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars++redirectchars) = escapeQuotes (c:cs) x = showChar c $ escapeQuotes cs x -- | Single-quote this string if it contains whitespace or double-quotes. --- No good for strings containing single quotes. +-- Does not work for strings containing single quotes. singleQuoteIfNeeded :: String -> String -singleQuoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars) = "'"++s++"'" +singleQuoteIfNeeded s | any (`elem` s) (quotechars++whitespacechars) = singleQuote s | otherwise = s -quotechars, whitespacechars, redirectchars :: [Char] +-- | Prepend and append single quotes to a string. +singleQuote :: String -> String +singleQuote s = "'"++s++"'" + +-- | Try to single- and backslash-quote a string as needed to make it usable +-- as an argument on a (sh/bash) shell command line. At least, well enough +-- to handle common currency symbols, like $. Probably broken in many ways. +-- +-- >>> quoteForCommandLine "a" +-- "a" +-- >>> quoteForCommandLine "\"" +-- "'\"'" +-- >>> quoteForCommandLine "$" +-- "'\\$'" +-- +quoteForCommandLine :: String -> String +quoteForCommandLine s + | any (`elem` s) (quotechars++whitespacechars++shellchars) = singleQuote $ quoteShellChars 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 + where + escapeShellChar c | c `elem` shellchars = ['\\',c] + escapeShellChar c = [c] + +quotechars, whitespacechars, redirectchars, shellchars :: [Char] quotechars = "'\"" whitespacechars = " \t\n\r" redirectchars = "<>" +shellchars = "<>(){}[]$7?#!~`" -- | 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.