提交 8dd40efb 编写于 作者: V Vidar Holen

Add support for -a: emit for sourced files.

上级 73d06c4f
...@@ -32,7 +32,7 @@ import qualified ShellCheck.Checks.ShellSupport ...@@ -32,7 +32,7 @@ import qualified ShellCheck.Checks.ShellSupport
analyzeScript :: AnalysisSpec -> AnalysisResult analyzeScript :: AnalysisSpec -> AnalysisResult
analyzeScript spec = AnalysisResult { analyzeScript spec = AnalysisResult {
arComments = arComments =
filterByAnnotation (asScript spec) . nub $ filterByAnnotation spec params . nub $
runAnalytics spec runAnalytics spec
++ runChecker params (checkers params) ++ runChecker params (checkers params)
} }
......
...@@ -109,6 +109,7 @@ data VariableState = Dead Token String | Alive deriving (Show) ...@@ -109,6 +109,7 @@ data VariableState = Dead Token String | Alive deriving (Show)
defaultSpec root = AnalysisSpec { defaultSpec root = AnalysisSpec {
asScript = root, asScript = root,
asShellType = Nothing, asShellType = Nothing,
asCheckSourced = False,
asExecutionMode = Executed asExecutionMode = Executed
} }
...@@ -116,7 +117,8 @@ pScript s = ...@@ -116,7 +117,8 @@ pScript s =
let let
pSpec = ParseSpec { pSpec = ParseSpec {
psFilename = "script", psFilename = "script",
psScript = s psScript = s,
psCheckSourced = False
} }
in prRoot . runIdentity $ parseScript (mockedSystemInterface []) pSpec in prRoot . runIdentity $ parseScript (mockedSystemInterface []) pSpec
...@@ -801,9 +803,10 @@ whenShell l c = do ...@@ -801,9 +803,10 @@ whenShell l c = do
when (shell `elem` l ) c when (shell `elem` l ) c
filterByAnnotation token = filterByAnnotation asSpec params =
filter (not . shouldIgnore) filter (not . shouldIgnore)
where where
token = asScript asSpec
idFor (TokenComment id _) = id idFor (TokenComment id _) = id
shouldIgnore note = shouldIgnore note =
any (shouldIgnoreFor (getCode note)) $ any (shouldIgnoreFor (getCode note)) $
...@@ -813,9 +816,9 @@ filterByAnnotation token = ...@@ -813,9 +816,9 @@ filterByAnnotation token =
where where
hasNum (DisableComment ts) = num == ts hasNum (DisableComment ts) = num == ts
hasNum _ = False hasNum _ = False
shouldIgnoreFor _ T_Include {} = True -- Ignore included files shouldIgnoreFor _ T_Include {} = not $ asCheckSourced asSpec
shouldIgnoreFor _ _ = False shouldIgnoreFor _ _ = False
parents = getParentTree token parents = parentMap params
getCode (TokenComment _ (Comment _ c _)) = c getCode (TokenComment _ (Comment _ c _)) = c
-- Is this a ${#anything}, to get string length or array count? -- Is this a ${#anything}, to get string length or array count?
......
...@@ -54,7 +54,8 @@ checkScript sys spec = do ...@@ -54,7 +54,8 @@ checkScript sys spec = do
checkScript contents = do checkScript contents = do
result <- parseScript sys ParseSpec { result <- parseScript sys ParseSpec {
psFilename = csFilename spec, psFilename = csFilename spec,
psScript = contents psScript = contents,
psCheckSourced = csCheckSourced spec
} }
let parseMessages = prComments result let parseMessages = prComments result
let analysisMessages = let analysisMessages =
...@@ -77,6 +78,7 @@ checkScript sys spec = do ...@@ -77,6 +78,7 @@ checkScript sys spec = do
AnalysisSpec { AnalysisSpec {
asScript = root, asScript = root,
asShellType = csShellTypeOverride spec, asShellType = csShellTypeOverride spec,
asCheckSourced = csCheckSourced spec,
asExecutionMode = Executed asExecutionMode = Executed
} }
...@@ -88,14 +90,22 @@ getErrors sys spec = ...@@ -88,14 +90,22 @@ getErrors sys spec =
check = checkWithIncludes [] check = checkWithIncludes []
checkWithSpec includes =
getErrors (mockedSystemInterface includes)
checkWithIncludes includes src = checkWithIncludes includes src =
getErrors checkWithSpec includes emptyCheckSpec {
(mockedSystemInterface includes)
emptyCheckSpec {
csScript = src, csScript = src,
csExcludedWarnings = [2148] csExcludedWarnings = [2148]
} }
checkRecursive includes src =
checkWithSpec includes emptyCheckSpec {
csScript = src,
csExcludedWarnings = [2148],
csCheckSourced = True
}
prop_findsParseIssue = check "echo \"$12\"" == [1037] prop_findsParseIssue = check "echo \"$12\"" == [1037]
prop_commentDisablesParseIssue1 = prop_commentDisablesParseIssue1 =
...@@ -153,6 +163,12 @@ prop_cantSourceDynamic2 = ...@@ -153,6 +163,12 @@ prop_cantSourceDynamic2 =
prop_canSourceDynamicWhenRedirected = prop_canSourceDynamicWhenRedirected =
null $ checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\"" null $ checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\""
prop_recursiveAnalysis =
[2086] == checkRecursive [("lib", "echo $1")] "source lib"
prop_recursiveParsing =
[1037] == checkRecursive [("lib", "echo \"$10\"")] "source lib"
prop_sourceDirectiveDoesntFollowFile = prop_sourceDirectiveDoesntFollowFile =
null $ checkWithIncludes null $ checkWithIncludes
[("foo", "source bar"), ("bar", "baz=3")] [("foo", "source bar"), ("bar", "baz=3")]
......
...@@ -34,14 +34,27 @@ format = return Formatter { ...@@ -34,14 +34,27 @@ format = return Formatter {
putStrLn "<checkstyle version='4.3'>", putStrLn "<checkstyle version='4.3'>",
onFailure = outputError, onFailure = outputError,
onResult = outputResult, onResult = outputResults,
footer = putStrLn "</checkstyle>" footer = putStrLn "</checkstyle>"
} }
outputResult result contents = do outputResults cr sys =
let comments = makeNonVirtual (crComments result) contents if null comments
putStrLn . formatFile (crFilename result) $ comments then outputFile (crFilename cr) "" []
else mapM_ outputGroup fileGroups
where
comments = crComments cr
fileGroups = groupWith sourceFile comments
outputGroup group = do
let filename = sourceFile (head group)
result <- (siReadFile sys) filename
let contents = either (const "") id result
outputFile filename contents group
outputFile filename contents warnings = do
let comments = makeNonVirtual warnings contents
putStrLn . formatFile filename $ comments
formatFile name comments = concat [ formatFile name comments = concat [
"<file ", attr "name" name, ">\n", "<file ", attr "name" name, ">\n",
......
...@@ -25,11 +25,12 @@ import ShellCheck.Interface ...@@ -25,11 +25,12 @@ import ShellCheck.Interface
-- A formatter that carries along an arbitrary piece of data -- A formatter that carries along an arbitrary piece of data
data Formatter = Formatter { data Formatter = Formatter {
header :: IO (), header :: IO (),
onResult :: CheckResult -> String -> IO (), onResult :: CheckResult -> SystemInterface IO -> IO (),
onFailure :: FilePath -> ErrorMessage -> IO (), onFailure :: FilePath -> ErrorMessage -> IO (),
footer :: IO () footer :: IO ()
} }
sourceFile (PositionedComment pos _ _) = posFile pos
lineNo (PositionedComment pos _ _) = posLine pos lineNo (PositionedComment pos _ _) = posLine pos
endLineNo (PositionedComment _ end _) = posLine end endLineNo (PositionedComment _ end _) = posLine end
colNo (PositionedComment pos _ _) = posColumn pos colNo (PositionedComment pos _ _) = posColumn pos
......
...@@ -31,14 +31,25 @@ format = return Formatter { ...@@ -31,14 +31,25 @@ format = return Formatter {
header = return (), header = return (),
footer = return (), footer = return (),
onFailure = outputError, onFailure = outputError,
onResult = outputResult onResult = outputAll
} }
outputError file error = hPutStrLn stderr $ file ++ ": " ++ error outputError file error = hPutStrLn stderr $ file ++ ": " ++ error
outputResult result contents = do outputAll cr sys = mapM_ f groups
let comments = makeNonVirtual (crComments result) contents where
mapM_ (putStrLn . formatComment (crFilename result)) comments comments = crComments cr
groups = groupWith sourceFile comments
f :: [PositionedComment] -> IO ()
f group = do
let filename = sourceFile (head group)
result <- (siReadFile sys) filename
let contents = either (const "") id result
outputResult filename contents group
outputResult filename contents warnings = do
let comments = makeNonVirtual warnings contents
mapM_ (putStrLn . formatComment filename) comments
formatComment filename c = concat [ formatComment filename c = concat [
filename, ":", filename, ":",
......
...@@ -43,15 +43,22 @@ colorForLevel level = ...@@ -43,15 +43,22 @@ colorForLevel level =
"style" -> 32 -- green "style" -> 32 -- green
"message" -> 1 -- bold "message" -> 1 -- bold
"source" -> 0 -- none "source" -> 0 -- none
otherwise -> 0 -- none _ -> 0 -- none
outputError options file error = do outputError options file error = do
color <- getColorFunc $ foColorOption options color <- getColorFunc $ foColorOption options
hPutStrLn stderr $ color "error" $ file ++ ": " ++ error hPutStrLn stderr $ color "error" $ file ++ ": " ++ error
outputResult options result contents = do outputResult options result sys = do
color <- getColorFunc $ foColorOption options color <- getColorFunc $ foColorOption options
let comments = crComments result let comments = crComments result
let fileGroups = groupWith sourceFile comments
mapM_ (outputForFile color sys) fileGroups
outputForFile color sys comments = do
let fileName = sourceFile (head comments)
result <- (siReadFile sys) fileName
let contents = either (const "") id result
let fileLines = lines contents let fileLines = lines contents
let lineCount = fromIntegral $ length fileLines let lineCount = fromIntegral $ length fileLines
let groups = groupWith lineNo comments let groups = groupWith lineNo comments
...@@ -62,7 +69,7 @@ outputResult options result contents = do ...@@ -62,7 +69,7 @@ outputResult options result contents = do
else fileLines !! fromIntegral (lineNum - 1) else fileLines !! fromIntegral (lineNum - 1)
putStrLn "" putStrLn ""
putStrLn $ color "message" $ putStrLn $ color "message" $
"In " ++ crFilename result ++" line " ++ show lineNum ++ ":" "In " ++ fileName ++" line " ++ show lineNum ++ ":"
putStrLn (color "source" line) putStrLn (color "source" line)
mapM_ (\c -> putStrLn (color (severityText c) $ cuteIndent c)) x mapM_ (\c -> putStrLn (color (severityText c) $ cuteIndent c)) x
putStrLn "" putStrLn ""
......
...@@ -33,6 +33,7 @@ newtype SystemInterface m = SystemInterface { ...@@ -33,6 +33,7 @@ newtype SystemInterface m = SystemInterface {
data CheckSpec = CheckSpec { data CheckSpec = CheckSpec {
csFilename :: String, csFilename :: String,
csScript :: String, csScript :: String,
csCheckSourced :: Bool,
csExcludedWarnings :: [Integer], csExcludedWarnings :: [Integer],
csShellTypeOverride :: Maybe Shell csShellTypeOverride :: Maybe Shell
} deriving (Show, Eq) } deriving (Show, Eq)
...@@ -46,6 +47,7 @@ emptyCheckSpec :: CheckSpec ...@@ -46,6 +47,7 @@ emptyCheckSpec :: CheckSpec
emptyCheckSpec = CheckSpec { emptyCheckSpec = CheckSpec {
csFilename = "", csFilename = "",
csScript = "", csScript = "",
csCheckSourced = False,
csExcludedWarnings = [], csExcludedWarnings = [],
csShellTypeOverride = Nothing csShellTypeOverride = Nothing
} }
...@@ -53,7 +55,8 @@ emptyCheckSpec = CheckSpec { ...@@ -53,7 +55,8 @@ emptyCheckSpec = CheckSpec {
-- Parser input and output -- Parser input and output
data ParseSpec = ParseSpec { data ParseSpec = ParseSpec {
psFilename :: String, psFilename :: String,
psScript :: String psScript :: String,
psCheckSourced :: Bool
} deriving (Show, Eq) } deriving (Show, Eq)
data ParseResult = ParseResult { data ParseResult = ParseResult {
...@@ -66,7 +69,8 @@ data ParseResult = ParseResult { ...@@ -66,7 +69,8 @@ data ParseResult = ParseResult {
data AnalysisSpec = AnalysisSpec { data AnalysisSpec = AnalysisSpec {
asScript :: Token, asScript :: Token,
asShellType :: Maybe Shell, asShellType :: Maybe Shell,
asExecutionMode :: ExecutionMode asExecutionMode :: ExecutionMode,
asCheckSourced :: Bool
} }
newtype AnalysisResult = AnalysisResult { newtype AnalysisResult = AnalysisResult {
......
...@@ -47,7 +47,7 @@ import qualified Data.Map as Map ...@@ -47,7 +47,7 @@ import qualified Data.Map as Map
import Test.QuickCheck.All (quickCheckAll) import Test.QuickCheck.All (quickCheckAll)
type SCBase m = Mr.ReaderT (SystemInterface m) (Ms.StateT SystemState m) type SCBase m = Mr.ReaderT (Environment m) (Ms.StateT SystemState m)
type SCParser m v = ParsecT String UserState (SCBase m) v type SCParser m v = ParsecT String UserState (SCBase m) v
backslash :: Monad m => SCParser m Char backslash :: Monad m => SCParser m Char
...@@ -248,12 +248,14 @@ addParseNote n = do ...@@ -248,12 +248,14 @@ addParseNote n = do
shouldIgnoreCode code = do shouldIgnoreCode code = do
context <- getCurrentContexts context <- getCurrentContexts
return $ any disabling context checkSourced <- Mr.asks checkSourced
return $ any (disabling checkSourced) context
where where
disabling (ContextAnnotation list) = disabling checkSourced item =
any disabling' list case item of
disabling (ContextSource _) = True -- Don't add messages for sourced files ContextAnnotation list -> any disabling' list
disabling _ = False ContextSource _ -> not $ checkSourced
_ -> False
disabling' (DisableComment n) = code == n disabling' (DisableComment n) = code == n
disabling' _ = False disabling' _ = False
...@@ -297,6 +299,11 @@ initialSystemState = SystemState { ...@@ -297,6 +299,11 @@ initialSystemState = SystemState {
parseProblems = [] parseProblems = []
} }
data Environment m = Environment {
systemInterface :: SystemInterface m,
checkSourced :: Bool
}
parseProblem level code msg = do parseProblem level code msg = do
pos <- getPosition pos <- getPosition
parseProblemAt pos level code msg parseProblemAt pos level code msg
...@@ -1879,7 +1886,7 @@ readSource pos t@(T_Redirecting _ _ (T_SimpleCommand _ _ (cmd:file:_))) = do ...@@ -1879,7 +1886,7 @@ readSource pos t@(T_Redirecting _ _ (T_SimpleCommand _ _ (cmd:file:_))) = do
"This file appears to be recursively sourced. Ignoring." "This file appears to be recursively sourced. Ignoring."
return t return t
else do else do
sys <- Mr.ask sys <- Mr.asks systemInterface
input <- input <-
if filename == "/dev/null" -- always allow /dev/null if filename == "/dev/null" -- always allow /dev/null
then return (Right "") then return (Right "")
...@@ -2788,16 +2795,22 @@ readScript = do ...@@ -2788,16 +2795,22 @@ readScript = do
-- Interactively run a parser in ghci: -- Interactively run a parser in ghci:
-- debugParse readScript "echo 'hello world'" -- debugParse readScript "echo 'hello world'"
debugParse p string = runIdentity $ do debugParse p string = runIdentity $ do
(res, _) <- runParser (mockedSystemInterface []) p "-" string (res, _) <- runParser testEnvironment p "-" string
return res return res
testEnvironment =
Environment {
systemInterface = (mockedSystemInterface []),
checkSourced = False
}
isOk p s = parsesCleanly p s == Just True -- The string parses with no warnings isOk p s = parsesCleanly p s == Just True -- The string parses with no warnings
isWarning p s = parsesCleanly p s == Just False -- The string parses with warnings isWarning p s = parsesCleanly p s == Just False -- The string parses with warnings
isNotOk p s = parsesCleanly p s == Nothing -- The string does not parse isNotOk p s = parsesCleanly p s == Nothing -- The string does not parse
parsesCleanly parser string = runIdentity $ do parsesCleanly parser string = runIdentity $ do
(res, sys) <- runParser (mockedSystemInterface []) (res, sys) <- runParser testEnvironment
(parser >> eof >> getState) "-" string (parser >> eof >> getState) "-" string
case (res, sys) of case (res, sys) of
(Right userState, systemState) -> (Right userState, systemState) ->
...@@ -2842,22 +2855,22 @@ getStringFromParsec errors = ...@@ -2842,22 +2855,22 @@ getStringFromParsec errors =
Message s -> if null s then Nothing else return $ s ++ "." Message s -> if null s then Nothing else return $ s ++ "."
runParser :: Monad m => runParser :: Monad m =>
SystemInterface m -> Environment m ->
SCParser m v -> SCParser m v ->
String -> String ->
String -> String ->
m (Either ParseError v, SystemState) m (Either ParseError v, SystemState)
runParser sys p filename contents = runParser env p filename contents =
Ms.runStateT Ms.runStateT
(Mr.runReaderT (Mr.runReaderT
(runParserT p initialUserState filename contents) (runParserT p initialUserState filename contents)
sys) env)
initialSystemState initialSystemState
system = lift . lift . lift system = lift . lift . lift
parseShell sys name contents = do parseShell env name contents = do
(result, state) <- runParser sys (parseWithNotes readScript) name contents (result, state) <- runParser env (parseWithNotes readScript) name contents
case result of case result of
Right (script, userstate) -> Right (script, userstate) ->
return ParseResult { return ParseResult {
...@@ -2943,7 +2956,12 @@ posToPos sp = Position { ...@@ -2943,7 +2956,12 @@ posToPos sp = Position {
parseScript :: Monad m => parseScript :: Monad m =>
SystemInterface m -> ParseSpec -> m ParseResult SystemInterface m -> ParseSpec -> m ParseResult
parseScript sys spec = parseScript sys spec =
parseShell sys (psFilename spec) (psScript spec) parseShell env (psFilename spec) (psScript spec)
where
env = Environment {
systemInterface = sys,
checkSourced = psCheckSourced spec
}
return [] return []
......
...@@ -32,6 +32,12 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts. ...@@ -32,6 +32,12 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts.
# OPTIONS # OPTIONS
**-a**,\ **--check-sourced**
: Emit warnings in sourced files. Normally, `shellcheck` will only warn
about issues in the specified files. With this option, any issues in
sourced files files will also be reported.
**-C**[*WHEN*],\ **--color**[=*WHEN*] **-C**[*WHEN*],\ **--color**[=*WHEN*]
: For TTY output, enable colors *always*, *never* or *auto*. The default : For TTY output, enable colors *always*, *never* or *auto*. The default
...@@ -67,6 +73,7 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts. ...@@ -67,6 +73,7 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts.
line (plus `/dev/null`). This option allows following any file the script line (plus `/dev/null`). This option allows following any file the script
may `source`. may `source`.
# FORMATS # FORMATS
**tty** **tty**
......
...@@ -33,8 +33,9 @@ import Control.Monad ...@@ -33,8 +33,9 @@ import Control.Monad
import Control.Monad.Except import Control.Monad.Except
import Data.Bits import Data.Bits
import Data.Char import Data.Char
import Data.Functor
import Data.Either import Data.Either
import Data.Functor
import Data.IORef
import Data.List import Data.List
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe import Data.Maybe
...@@ -75,21 +76,23 @@ defaultOptions = Options { ...@@ -75,21 +76,23 @@ defaultOptions = Options {
usageHeader = "Usage: shellcheck [OPTIONS...] FILES..." usageHeader = "Usage: shellcheck [OPTIONS...] FILES..."
options = [ options = [
Option "e" ["exclude"] Option "a" ["check-sourced"]
(ReqArg (Flag "exclude") "CODE1,CODE2..") "exclude types of warnings", (NoArg $ Flag "sourced" "false") "Include warnings from sourced files",
Option "f" ["format"]
(ReqArg (Flag "format") "FORMAT") $
"output format (" ++ formatList ++ ")",
Option "C" ["color"] Option "C" ["color"]
(OptArg (maybe (Flag "color" "always") (Flag "color")) "WHEN") (OptArg (maybe (Flag "color" "always") (Flag "color")) "WHEN")
"Use color (auto, always, never)", "Use color (auto, always, never)",
Option "e" ["exclude"]
(ReqArg (Flag "exclude") "CODE1,CODE2..") "Exclude types of warnings",
Option "f" ["format"]
(ReqArg (Flag "format") "FORMAT") $
"Output format (" ++ formatList ++ ")",
Option "s" ["shell"] Option "s" ["shell"]
(ReqArg (Flag "shell") "SHELLNAME") (ReqArg (Flag "shell") "SHELLNAME")
"Specify dialect (sh, bash, dash, ksh)", "Specify dialect (sh, bash, dash, ksh)",
Option "x" ["external-sources"]
(NoArg $ Flag "externals" "true") "Allow 'source' outside of FILES.",
Option "V" ["version"] Option "V" ["version"]
(NoArg $ Flag "version" "true") "Print version information" (NoArg $ Flag "version" "true") "Print version information",
Option "x" ["external-sources"]
(NoArg $ Flag "externals" "true") "Allow 'source' outside of FILES"
] ]
printErr = lift . hPutStrLn stderr printErr = lift . hPutStrLn stderr
...@@ -136,7 +139,7 @@ getExclusions options = ...@@ -136,7 +139,7 @@ getExclusions options =
in in
map (Prelude.read . clean) elements :: [Int] map (Prelude.read . clean) elements :: [Int]
toStatus = liftM (either id id) . runExceptT toStatus = fmap (either id id) . runExceptT
getEnvArgs = do getEnvArgs = do
opts <- getEnv "SHELLCHECK_OPTS" `catch` cantWaitForLookupEnv opts <- getEnv "SHELLCHECK_OPTS" `catch` cantWaitForLookupEnv
...@@ -193,19 +196,23 @@ runFormatter sys format options files = do ...@@ -193,19 +196,23 @@ runFormatter sys format options files = do
newStatus <- process file `catch` handler file newStatus <- process file `catch` handler file
return $ status `mappend` newStatus return $ status `mappend` newStatus
handler :: FilePath -> IOException -> IO Status handler :: FilePath -> IOException -> IO Status
handler file e = do handler file e = reportFailure file (show e)
onFailure format file (show e) reportFailure file str = do
onFailure format file str
return RuntimeException return RuntimeException
process :: FilePath -> IO Status process :: FilePath -> IO Status
process filename = do process filename = do
contents <- inputFile filename input <- (siReadFile sys) filename
either (reportFailure filename) check input
where
check contents = do
let checkspec = (checkSpec options) { let checkspec = (checkSpec options) {
csFilename = filename, csFilename = filename,
csScript = contents csScript = contents
} }
result <- checkScript sys checkspec result <- checkScript sys checkspec
onResult format result contents onResult format result sys
return $ return $
if null (crComments result) if null (crComments result)
then NoProblems then NoProblems
...@@ -254,6 +261,13 @@ parseOption flag options = ...@@ -254,6 +261,13 @@ parseOption flag options =
} }
} }
Flag "sourced" _ ->
return options {
checkSpec = (checkSpec options) {
csCheckSourced = True
}
}
_ -> return options _ -> return options
where where
die s = do die s = do
...@@ -268,14 +282,28 @@ parseOption flag options = ...@@ -268,14 +282,28 @@ parseOption flag options =
ioInterface options files = do ioInterface options files = do
inputs <- mapM normalize files inputs <- mapM normalize files
cache <- newIORef emptyCache
return SystemInterface { return SystemInterface {
siReadFile = get inputs siReadFile = get cache inputs
} }
where where
get inputs file = do emptyCache :: Map.Map FilePath String
emptyCache = Map.empty
get cache inputs file = do
map <- readIORef cache
case Map.lookup file map of
Just x -> return $ Right x
Nothing -> fetch cache inputs file
fetch cache inputs file = do
ok <- allowable inputs file ok <- allowable inputs file
if ok if ok
then (Right <$> inputFile file) `catch` handler then (do
(contents, shouldCache) <- inputFile file
when shouldCache $
modifyIORef cache $ Map.insert file contents
return $ Right contents
) `catch` handler
else return $ Left (file ++ " was not specified as input (see shellcheck -x).") else return $ Left (file ++ " was not specified as input (see shellcheck -x).")
where where
...@@ -296,16 +324,19 @@ ioInterface options files = do ...@@ -296,16 +324,19 @@ ioInterface options files = do
fallback path _ = return path fallback path _ = return path
inputFile file = do inputFile file = do
handle <- (handle, shouldCache) <-
if file == "-" if file == "-"
then return stdin then return (stdin, True)
else openBinaryFile file ReadMode else do
h <- openBinaryFile file ReadMode
reopenable <- hIsSeekable h
return (h, not reopenable)
hSetBinaryMode handle True hSetBinaryMode handle True
contents <- decodeString <$> hGetContents handle -- closes handle contents <- decodeString <$> hGetContents handle -- closes handle
seq (length contents) $ seq (length contents) $
return contents return (contents, shouldCache)
-- Decode a char8 string into a utf8 string, with fallback on -- Decode a char8 string into a utf8 string, with fallback on
-- ISO-8859-1. This avoids depending on additional libraries. -- ISO-8859-1. This avoids depending on additional libraries.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册