diff --git a/Annex/UntrustedFilePath.hs b/Annex/UntrustedFilePath.hs index 0b2777ab47..df7ac3bd35 100644 --- a/Annex/UntrustedFilePath.hs +++ b/Annex/UntrustedFilePath.hs @@ -10,6 +10,8 @@ module Annex.UntrustedFilePath where import Data.Char import System.FilePath +import Utility.SafeOutput + {- Given a string that we'd like to use as the basis for FilePath, but that - was provided by a third party and is not to be trusted, returns the closest - sane FilePath. @@ -55,10 +57,7 @@ sanitizeLeadingFilePathCharacter s = s controlCharacterInFilePath :: FilePath -> Bool controlCharacterInFilePath = any (not . safechar) where - safechar c - | not (isControl c) = True - | c == '\t' = True - | otherwise = False + safechar c = safeOutputChar c && c /= '\n' {- ../ is a path traversal, no matter where it appears. - diff --git a/Command/ExamineKey.hs b/Command/ExamineKey.hs index 73dd77f7c5..b3a3cbab21 100644 --- a/Command/ExamineKey.hs +++ b/Command/ExamineKey.hs @@ -16,7 +16,7 @@ import Annex.Link import Backend import Types.Backend import Types.Key -import Utility.SafeOutput +import Utility.Terminal import Data.Char import qualified Data.ByteString as B diff --git a/Command/Find.hs b/Command/Find.hs index 05bd17f76f..5dd6a4aac8 100644 --- a/Command/Find.hs +++ b/Command/Find.hs @@ -19,7 +19,7 @@ import Types.Key import Git.FilePath import qualified Utility.Format import Utility.DataUnits -import Utility.SafeOutput +import Utility.Terminal cmd :: Command cmd = withAnnexOptions [annexedMatchingOptions] $ mkCommand $ diff --git a/Command/FindKeys.hs b/Command/FindKeys.hs index f2a86cde50..e24075dacb 100644 --- a/Command/FindKeys.hs +++ b/Command/FindKeys.hs @@ -10,7 +10,7 @@ module Command.FindKeys where import Command import qualified Command.Find import qualified Utility.Format -import Utility.SafeOutput +import Utility.Terminal cmd :: Command cmd = withAnnexOptions [keyMatchingOptions] $ Command.Find.mkCommand $ diff --git a/Utility/SafeOutput.hs b/Utility/SafeOutput.hs index 275797adda..ee37d2c274 100644 --- a/Utility/SafeOutput.hs +++ b/Utility/SafeOutput.hs @@ -11,55 +11,27 @@ module Utility.SafeOutput ( safeOutput, - IsTerminal(..), - checkIsTerminal, + safeOutputChar, ) where import Data.Char import qualified Data.ByteString as S import System.IO -#ifdef mingw32_HOST_OS -import System.Win32.MinTTY (isMinTTYHandle) -import System.Win32.File -import System.Win32.Types -import Graphics.Win32.Misc -import Control.Exception -#endif class SafeOutputtable t where safeOutput :: t -> t instance SafeOutputtable String where - safeOutput = filter safeChar + safeOutput = filter safeOutputChar instance SafeOutputtable S.ByteString where - safeOutput = S.filter (safeChar . chr . fromIntegral) + safeOutput = S.filter (safeOutputChar . chr . fromIntegral) -safeChar :: Char -> Bool -safeChar c +safeOutputChar :: Char -> Bool +safeOutputChar c | not (isControl c) = True | c == '\n' = True | c == '\t' = True + | c == '\DEL' = False + | ord c > 31 = True | otherwise = False - -newtype IsTerminal = IsTerminal Bool - -checkIsTerminal :: Handle -> IO IsTerminal -checkIsTerminal h = do -#ifndef mingw32_HOST_OS - b <- hIsTerminalDevice h - return (IsTerminal b) -#else - b <- hIsTerminalDevice h - if b - then return (IsTerminal b) - else do - h' <- getStdHandle sTD_OUTPUT_HANDLE - `catch` \(_ :: IOError) -> - return nullHANDLE - if h == nullHANDLE - then return (IsTerminal False) - else do - b' <- isMinTTYHandle h' - return (IsTerminal b) -#endif diff --git a/Utility/Terminal.hs b/Utility/Terminal.hs new file mode 100644 index 0000000000..6835478410 --- /dev/null +++ b/Utility/Terminal.hs @@ -0,0 +1,47 @@ +{- Determining if output is to a terminal. + - + - Copyright 2023 Joey Hess + - + - License: BSD-2-clause + -} + +{-# LANGUAGE CPP #-} +{-# OPTIONS_GHC -fno-warn-tabs #-} + +module Utility.Terminal ( + IsTerminal(..), + checkIsTerminal, +) where + +import Data.Char +import qualified Data.ByteString as S +import System.IO +#ifdef mingw32_HOST_OS +import System.Win32.MinTTY (isMinTTYHandle) +import System.Win32.File +import System.Win32.Types +import Graphics.Win32.Misc +import Control.Exception +#endif + +newtype IsTerminal = IsTerminal Bool + +checkIsTerminal :: Handle -> IO IsTerminal +checkIsTerminal h = do +#ifndef mingw32_HOST_OS + b <- hIsTerminalDevice h + return (IsTerminal b) +#else + b <- hIsTerminalDevice h + if b + then return (IsTerminal b) + else do + h' <- getStdHandle sTD_OUTPUT_HANDLE + `catch` \(_ :: IOError) -> + return nullHANDLE + if h == nullHANDLE + then return (IsTerminal False) + else do + b' <- isMinTTYHandle h' + return (IsTerminal b) +#endif diff --git a/git-annex.cabal b/git-annex.cabal index f249e95af1..c11047d2da 100644 --- a/git-annex.cabal +++ b/git-annex.cabal @@ -1144,6 +1144,7 @@ Executable git-annex Utility.SshHost Utility.Su Utility.SystemDirectory + Utility.Terminal Utility.TimeStamp Utility.TList Utility.Tense