2023-04-10 17:38:14 +00:00
|
|
|
{- Safe output to the terminal of possibly attacker-controlled strings,
|
|
|
|
- avoiding displaying control characters.
|
|
|
|
-
|
|
|
|
- Copyright 2023 Joey Hess <id@joeyh.name>
|
|
|
|
-
|
|
|
|
- License: BSD-2-clause
|
|
|
|
-}
|
|
|
|
|
2023-04-11 18:57:09 +00:00
|
|
|
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, CPP #-}
|
2023-04-10 17:38:14 +00:00
|
|
|
{-# OPTIONS_GHC -fno-warn-tabs #-}
|
|
|
|
|
2023-04-11 18:57:09 +00:00
|
|
|
module Utility.SafeOutput (
|
|
|
|
safeOutput,
|
|
|
|
IsTerminal(..),
|
|
|
|
checkIsTerminal,
|
|
|
|
) where
|
2023-04-10 17:38:14 +00:00
|
|
|
|
|
|
|
import Data.Char
|
|
|
|
import qualified Data.ByteString as S
|
2023-04-11 18:57:09 +00:00
|
|
|
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
|
2023-04-10 17:38:14 +00:00
|
|
|
|
|
|
|
class SafeOutputtable t where
|
|
|
|
safeOutput :: t -> t
|
|
|
|
|
|
|
|
instance SafeOutputtable String where
|
|
|
|
safeOutput = filter (not . isControl)
|
|
|
|
|
|
|
|
instance SafeOutputtable S.ByteString where
|
|
|
|
safeOutput = S.filter (not . isControl . chr . fromIntegral)
|
2023-04-11 18:57:09 +00:00
|
|
|
|
|
|
|
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
|