Moved shebang verification to parser

This commit is contained in:
Vidar Holen 2013-07-08 09:39:54 -07:00
parent a08e60cd07
commit 599beff5b1
2 changed files with 68 additions and 31 deletions

View File

@ -54,35 +54,22 @@ checksFor Bash = map runBasicAnalysis [
runAllAnalytics root m = addToMap notes m
where shell = determineShell root
unsupported = (getId root, Note ErrorC "ShellCheck only handles Bourne based shells, sorry!")
notes = case shell of
Nothing -> [ unsupported ]
Just sh -> checkList ((checksFor sh) ++ genericChecks) root
notes = checkList ((checksFor shell) ++ genericChecks) root
checkList l t = concatMap (\f -> f t) l
addToMap list map = foldr (\(id,note) m -> Map.adjust (\(Metadata pos notes) -> Metadata pos (note:notes)) id m) map list
prop_determineShell0 = determineShell (T_Script (Id 0) "#!/bin/sh" []) == Just Sh
prop_determineShell1 = determineShell (T_Script (Id 0) "#!/usr/bin/env ksh" []) == Just Ksh
prop_determineShell2 = determineShell (T_Script (Id 0) "" []) == Just Bash
prop_determineShell0 = determineShell (T_Script (Id 0) "#!/bin/sh" []) == Sh
prop_determineShell1 = determineShell (T_Script (Id 0) "#!/usr/bin/env ksh" []) == Ksh
prop_determineShell2 = determineShell (T_Script (Id 0) "" []) == Bash
determineShell (T_Script _ shebang _) = normalize $ shellFor shebang
where shellFor s | "/env " `isInfixOf` s = head ((drop 1 $ words s)++[""])
shellFor s = reverse . takeWhile (/= '/') . reverse $ s
normalize "csh" = Nothing
normalize "tcsh" = Nothing
normalize "sh" = return Sh
normalize "ksh" = return Ksh
normalize "zsh" = return Zsh
normalize "bash" = return Bash
normalize x | any (`isPrefixOf` x) [
"csh"
,"tcsh"
,"perl"
,"awk"
,"python"
,"ruby"
] = Nothing
normalize _ = return Bash
normalize "sh" = Sh
normalize "ksh" = Ksh
normalize "zsh" = Zsh
normalize "bash" = Bash
normalize _ = Bash
runBasicAnalysis f t = snd $ runState (doAnalysis f t) []
basicChecks = [

View File

@ -24,7 +24,7 @@ import Text.Parsec
import Debug.Trace
import Control.Monad
import Data.Char
import Data.List (isInfixOf, isSuffixOf, partition, sortBy, intercalate, nub)
import Data.List (isPrefixOf, isInfixOf, isSuffixOf, partition, sortBy, intercalate, nub)
import qualified Data.Map as Map
import qualified Control.Monad.State as Ms
import Data.Maybe
@ -1558,9 +1558,14 @@ readShebang = do
prop_readScript1 = isOk readScript "#!/bin/bash\necho hello world\n"
prop_readScript2 = isWarning readScript "#!/bin/bash\r\necho hello world\n"
prop_readScript3 = isWarning readScript "#!/bin/bash\necho hello\xA0world"
prop_readScript4 = isWarning readScript "#!/usr/bin/perl\nfoo=("
readScript = do
id <- getNextId
pos <- getPosition
sb <- option "" readShebang
verifyShell pos (getShell sb)
if (isValidShell $ getShell sb) /= Just False
then
do {
allspacing;
commands <- readTerm;
@ -1570,6 +1575,51 @@ readScript = do
parseProblem WarningC "Couldn't read any commands.";
return $ T_Script id sb $ [T_EOF id];
}
else do
many anyChar
return $ T_Script id sb $ [T_EOF id];
where
basename s = reverse . takeWhile (/= '/') . reverse $ s
getShell sb =
case words sb of
[] -> ""
[x] -> basename x
(first:second:_) ->
if basename first == "env"
then second
else basename first
verifyShell pos s =
case isValidShell s of
Just True -> return ()
Just False -> parseProblemAt pos ErrorC "ShellCheck only supports Bourne based shell scripts, sorry!"
Nothing -> parseProblemAt pos InfoC "This shebang was unrecognized. Note that ShellCheck only handles Bourne based shells."
isValidShell s =
let good = s == "" || any (`isPrefixOf` s) goodShells
bad = any (`isPrefixOf` s) badShells
in
if good
then Just True
else if bad
then Just False
else Nothing
goodShells = [
"sh",
"bash",
"ksh",
"zsh"
]
badShells = [
"awk",
"csh",
"perl",
"python",
"ruby",
"tcsh"
]
rp p filename contents = Ms.runState (runParserT p initialState filename contents) ([], [])