{-
    Copyright 2012-2021 Vidar Holen

    This file is part of ShellCheck.
    https://www.shellcheck.net

    ShellCheck is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    ShellCheck is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
-}
{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.ASTLib where

import ShellCheck.AST
import ShellCheck.Prelude
import ShellCheck.Regex

import Control.Monad.Writer
import Control.Monad
import Data.Char
import Data.Functor
import Data.Functor.Identity
import Data.List
import Data.Maybe
import qualified Data.Map as Map
import Numeric (showHex)

import Test.QuickCheck

arguments :: Token -> [Token]
arguments (T_SimpleCommand Id
_ [Token]
_ (Token
cmd:[Token]
args)) = [Token]
args

-- Is this a type of loop?
isLoop :: Token -> Bool
isLoop Token
t = case Token
t of
        T_WhileExpression {} -> Bool
True
        T_UntilExpression {} -> Bool
True
        T_ForIn {} -> Bool
True
        T_ForArithmetic {} -> Bool
True
        T_SelectIn {}  -> Bool
True
        Token
_ -> Bool
False

-- Will this split into multiple words when used as an argument?
willSplit :: Token -> Bool
willSplit Token
x =
  case Token
x of
    T_DollarBraced {} -> Bool
True
    T_DollarExpansion {} -> Bool
True
    T_Backticked {} -> Bool
True
    T_BraceExpansion {} -> Bool
True
    T_Glob {} -> Bool
True
    T_Extglob {} -> Bool
True
    T_DoubleQuoted Id
_ [Token]
l -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
willBecomeMultipleArgs [Token]
l
    T_NormalWord Id
_ [Token]
l -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
willSplit [Token]
l
    Token
_ -> Bool
False

isGlob :: Token -> Bool
isGlob Token
t = case Token
t of
    T_Extglob {} -> Bool
True
    T_Glob {} -> Bool
True
    T_NormalWord Id
_ [Token]
l -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isGlob [Token]
l Bool -> Bool -> Bool
|| [Token] -> Bool
hasSplitRange [Token]
l
    Token
_ -> Bool
False
  where
    -- foo[x${var}y] gets parsed as foo,[,x,$var,y],
    -- so check if there's such an interval
    hasSplitRange :: [Token] -> Bool
hasSplitRange [Token]
l =
        let afterBracket :: [Token]
afterBracket = forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isHalfOpenRange) [Token]
l
        in forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isClosingRange [Token]
afterBracket

    isHalfOpenRange :: Token -> Bool
isHalfOpenRange Token
t =
        case Token
t of
            T_Literal Id
_ [Char]
"[" -> Bool
True
            Token
_ -> Bool
False

    isClosingRange :: Token -> Bool
isClosingRange Token
t =
        case Token
t of
            T_Literal Id
_ [Char]
str -> Char
']' forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char]
str
            Token
_ -> Bool
False


-- Is this shell word a constant?
isConstant :: Token -> Bool
isConstant Token
token =
    case Token
token of
        -- This ignores some cases like ~"foo":
        T_NormalWord Id
_ (T_Literal Id
_ (Char
'~':[Char]
_) : [Token]
_)  -> Bool
False
        T_NormalWord Id
_ [Token]
l   -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isConstant [Token]
l
        T_DoubleQuoted Id
_ [Token]
l -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isConstant [Token]
l
        T_SingleQuoted Id
_ [Char]
_ -> Bool
True
        T_Literal Id
_ [Char]
_ -> Bool
True
        Token
_ -> Bool
False

-- Is this an empty literal?
isEmpty :: Token -> Bool
isEmpty Token
token =
    case Token
token of
        T_NormalWord Id
_ [Token]
l   -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isEmpty [Token]
l
        T_DoubleQuoted Id
_ [Token]
l -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isEmpty [Token]
l
        T_SingleQuoted Id
_ [Char]
"" -> Bool
True
        T_Literal Id
_ [Char]
"" -> Bool
True
        Token
_ -> Bool
False

-- Quick&lazy oversimplification of commands, throwing away details
-- and returning a list like  ["find", ".", "-name", "${VAR}*" ].
oversimplify :: Token -> [[Char]]
oversimplify Token
token =
    case Token
token of
        (T_NormalWord Id
_ [Token]
l) -> [forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [[Char]]
oversimplify [Token]
l)]
        (T_DoubleQuoted Id
_ [Token]
l) -> [forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [[Char]]
oversimplify [Token]
l)]
        (T_SingleQuoted Id
_ [Char]
s) -> [[Char]
s]
        (T_DollarBraced Id
_ Bool
_ Token
_) -> [[Char]
"${VAR}"]
        (T_DollarArithmetic Id
_ Token
_) -> [[Char]
"${VAR}"]
        (T_DollarExpansion Id
_ [Token]
_) -> [[Char]
"${VAR}"]
        (T_Backticked Id
_ [Token]
_) -> [[Char]
"${VAR}"]
        (T_Glob Id
_ [Char]
s) -> [[Char]
s]
        (T_Pipeline Id
_ [Token]
_ [Token
x]) -> Token -> [[Char]]
oversimplify Token
x
        (T_Literal Id
_ [Char]
x) -> [[Char]
x]
        (T_ParamSubSpecialChar Id
_ [Char]
x) -> [[Char]
x]
        (T_SimpleCommand Id
_ [Token]
vars [Token]
words) -> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [[Char]]
oversimplify [Token]
words
        (T_Redirecting Id
_ [Token]
_ Token
foo) -> Token -> [[Char]]
oversimplify Token
foo
        (T_DollarSingleQuoted Id
_ [Char]
s) -> [[Char]
s]
        (T_Annotation Id
_ [Annotation]
_ Token
s) -> Token -> [[Char]]
oversimplify Token
s
        -- Workaround for let "foo = bar" parsing
        (TA_Sequence Id
_ [TA_Expansion Id
_ [Token]
v]) -> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [[Char]]
oversimplify [Token]
v
        Token
_ -> []


-- Turn a SimpleCommand foo -avz --bar=baz into args "a", "v", "z", "bar",
-- each in a tuple of (token, stringFlag). Non-flag arguments are added with
-- stringFlag == "".
getFlagsUntil :: ([Char] -> Bool) -> Token -> [(Token, [Char])]
getFlagsUntil [Char] -> Bool
stopCondition (T_SimpleCommand Id
_ [Token]
_ (Token
_:[Token]
args)) =
    let tokenAndText :: [(Token, [Char])]
tokenAndText = forall a b. (a -> b) -> [a] -> [b]
map (\Token
x -> (Token
x, forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [[Char]]
oversimplify Token
x)) [Token]
args
        ([(Token, [Char])]
flagArgs, [(Token, [Char])]
rest) = forall a. (a -> Bool) -> [a] -> ([a], [a])
break ([Char] -> Bool
stopCondition forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd) [(Token, [Char])]
tokenAndText
    in
        forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap forall {a}. (a, [Char]) -> [(a, [Char])]
flag [(Token, [Char])]
flagArgs forall a. [a] -> [a] -> [a]
++ forall a b. (a -> b) -> [a] -> [b]
map (\(Token
t, [Char]
_) -> (Token
t, [Char]
"")) [(Token, [Char])]
rest
  where
    flag :: (a, [Char]) -> [(a, [Char])]
flag (a
x, Char
'-':Char
'-':[Char]
arg) = [ (a
x, forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'=') [Char]
arg) ]
    flag (a
x, Char
'-':[Char]
args) = forall a b. (a -> b) -> [a] -> [b]
map (\Char
v -> (a
x, [Char
v])) [Char]
args
    flag (a
x, [Char]
_) = [ (a
x, [Char]
"") ]
getFlagsUntil [Char] -> Bool
_ Token
_ = forall a. HasCallStack => [Char] -> a
error forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
pleaseReport [Char]
"getFlags on non-command"

-- Get all flags in a GNU way, up until --
getAllFlags :: Token -> [(Token, String)]
getAllFlags :: Token -> [(Token, [Char])]
getAllFlags = ([Char] -> Bool) -> Token -> [(Token, [Char])]
getFlagsUntil (forall a. Eq a => a -> a -> Bool
== [Char]
"--")
-- Get all flags in a BSD way, up until first non-flag argument or --
getLeadingFlags :: Token -> [(Token, [Char])]
getLeadingFlags = ([Char] -> Bool) -> Token -> [(Token, [Char])]
getFlagsUntil (\[Char]
x -> [Char]
x forall a. Eq a => a -> a -> Bool
== [Char]
"--" Bool -> Bool -> Bool
|| (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ [Char]
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
x))

-- Check if a command has a flag.
hasFlag :: Token -> [Char] -> Bool
hasFlag Token
cmd [Char]
str = [Char]
str forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ Token -> [(Token, [Char])]
getAllFlags Token
cmd)

-- Is this token a word that starts with a dash?
isFlag :: Token -> Bool
isFlag Token
token =
    case Token -> [Token]
getWordParts Token
token of
        T_Literal Id
_ (Char
'-':[Char]
_) : [Token]
_ -> Bool
True
        [Token]
_ -> Bool
False

-- Is this token a flag where the - is unquoted?
isUnquotedFlag :: Token -> Bool
isUnquotedFlag Token
token = forall a. a -> Maybe a -> a
fromMaybe Bool
False forall a b. (a -> b) -> a -> b
$ do
    [Char]
str <- Token -> Maybe [Char]
getLeadingUnquotedString Token
token
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Char]
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
str

-- getGnuOpts "erd:u:" will parse a list of arguments tokens like `read`
--     -re -d : -u 3 bar
-- into
--     Just [("r", (-re, -re)), ("e", (-re, -re)), ("d", (-d,:)), ("u", (-u,3)), ("", (bar,bar))]
--
-- Each string flag maps to a tuple of (flag, argument), where argument=flag if it
-- doesn't take a specific one.
--
-- Any unrecognized flag will result in Nothing. The exception is if arbitraryLongOpts
-- is set, in which case --anything will map to "anything".
getGnuOpts :: String -> [Token] -> Maybe [(String, (Token, Token))]
getGnuOpts :: [Char] -> [Token] -> Maybe [([Char], (Token, Token))]
getGnuOpts [Char]
str [Token]
args = (Bool, Bool)
-> [Char]
-> [([Char], Bool)]
-> [Token]
-> Maybe [([Char], (Token, Token))]
getOpts (Bool
True, Bool
False) [Char]
str [] [Token]
args

-- As above, except the first non-arg string will treat the rest as arguments
getBsdOpts :: String -> [Token] -> Maybe [(String, (Token, Token))]
getBsdOpts :: [Char] -> [Token] -> Maybe [([Char], (Token, Token))]
getBsdOpts [Char]
str [Token]
args = (Bool, Bool)
-> [Char]
-> [([Char], Bool)]
-> [Token]
-> Maybe [([Char], (Token, Token))]
getOpts (Bool
False, Bool
False) [Char]
str [] [Token]
args

-- Tests for this are in Commands.hs where it's more frequently used
getOpts ::
    -- Behavioral config: gnu style, allow arbitrary long options
    (Bool, Bool)
    -- A getopts style string
    -> String
    -- List of long options and whether they take arguments
    -> [(String, Bool)]
    -- List of arguments (excluding command)
    -> [Token]
    -- List of flags to tuple of (optionToken, valueToken)
    -> Maybe [(String, (Token, Token))]

getOpts :: (Bool, Bool)
-> [Char]
-> [([Char], Bool)]
-> [Token]
-> Maybe [([Char], (Token, Token))]
getOpts (Bool
gnu, Bool
arbitraryLongOpts) [Char]
string [([Char], Bool)]
longopts [Token]
args = [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
args
  where
    flagList :: [Char] -> [([Char], Bool)]
flagList (Char
c:Char
':':[Char]
rest) = ([Char
c], Bool
True) forall a. a -> [a] -> [a]
: [Char] -> [([Char], Bool)]
flagList [Char]
rest
    flagList (Char
c:[Char]
rest)     = ([Char
c], Bool
False) forall a. a -> [a] -> [a]
: [Char] -> [([Char], Bool)]
flagList [Char]
rest
    flagList []           = [([Char], Bool)]
longopts
    flagMap :: Map [Char] Bool
flagMap = forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList forall a b. (a -> b) -> a -> b
$ ([Char]
"", Bool
False) forall a. a -> [a] -> [a]
: [Char] -> [([Char], Bool)]
flagList [Char]
string

    process :: [Token] -> Maybe [([Char], (Token, Token))]
process [] = forall (m :: * -> *) a. Monad m => a -> m a
return []
    process (Token
token:[Token]
rest) = do
        case [Char] -> Token -> [Char]
getLiteralStringDef [Char]
"\0" Token
token of
            [Char]
"--" -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall {b}. [b] -> [([Char], (b, b))]
listToArgs [Token]
rest
            Char
'-':Char
'-':[Char]
word -> do
                let ([Char]
name, [Char]
arg) = forall a. (a -> Bool) -> [a] -> ([a], [a])
span (forall a. Eq a => a -> a -> Bool
/= Char
'=') [Char]
word
                Bool
needsArg <-
                    if Bool
arbitraryLongOpts
                    then forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Bool
False [Char]
name Map [Char] Bool
flagMap
                    else forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup [Char]
name Map [Char] Bool
flagMap

                if Bool
needsArg Bool -> Bool -> Bool
&& forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
arg
                  then
                    case [Token]
rest of
                        (Token
arg:[Token]
rest2) -> do
                            [([Char], (Token, Token))]
more <- [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
rest2
                            forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char]
name, (Token
token, Token
arg)) forall a. a -> [a] -> [a]
: [([Char], (Token, Token))]
more
                        [Token]
_ -> forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
"Missing arg"
                  else do
                    [([Char], (Token, Token))]
more <- [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
rest
                    -- Consider splitting up token to get arg
                    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char]
name, (Token
token, Token
token)) forall a. a -> [a] -> [a]
: [([Char], (Token, Token))]
more
            Char
'-':[Char]
opts -> [Char] -> Token -> [Token] -> Maybe [([Char], (Token, Token))]
shortToOpts [Char]
opts Token
token [Token]
rest
            [Char]
arg ->
                if Bool
gnu
                then do
                    [([Char], (Token, Token))]
more <- [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
rest
                    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char]
"", (Token
token, Token
token))forall a. a -> [a] -> [a]
:[([Char], (Token, Token))]
more
                else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall {b}. [b] -> [([Char], (b, b))]
listToArgs (Token
tokenforall a. a -> [a] -> [a]
:[Token]
rest)

    shortToOpts :: [Char] -> Token -> [Token] -> Maybe [([Char], (Token, Token))]
shortToOpts [Char]
opts Token
token [Token]
args =
        case [Char]
opts of
            Char
c:[Char]
rest -> do
                Bool
needsArg <- forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup [Char
c] Map [Char] Bool
flagMap
                case () of
                    ()
_ | Bool
needsArg Bool -> Bool -> Bool
&& forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
rest -> do
                        (Token
next:[Token]
restArgs) <- forall (m :: * -> *) a. Monad m => a -> m a
return [Token]
args
                        [([Char], (Token, Token))]
more <- [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
restArgs
                        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char
c], (Token
token, Token
next))forall a. a -> [a] -> [a]
:[([Char], (Token, Token))]
more
                    ()
_ | Bool
needsArg -> do
                        [([Char], (Token, Token))]
more <- [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
args
                        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char
c], (Token
token, Token
token))forall a. a -> [a] -> [a]
:[([Char], (Token, Token))]
more
                    ()
_ -> do
                        [([Char], (Token, Token))]
more <- [Char] -> Token -> [Token] -> Maybe [([Char], (Token, Token))]
shortToOpts [Char]
rest Token
token [Token]
args
                        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ([Char
c], (Token
token, Token
token))forall a. a -> [a] -> [a]
:[([Char], (Token, Token))]
more
            [] -> [Token] -> Maybe [([Char], (Token, Token))]
process [Token]
args

    listToArgs :: [b] -> [([Char], (b, b))]
listToArgs = forall a b. (a -> b) -> [a] -> [b]
map (\b
x -> ([Char]
"", (b
x, b
x)))


-- Generic getOpts that doesn't rely on a format string, but may also be inaccurate.
-- This provides a best guess interpretation instead of failing when new options are added.
--
--    "--" is treated as end of arguments
--    "--anything[=foo]" is treated as a long option without argument
--    "-any" is treated as -a -n -y, with the next arg as an option to -y unless it starts with -
--    anything else is an argument
getGenericOpts :: [Token] -> [(String, (Token, Token))]
getGenericOpts :: [Token] -> [([Char], (Token, Token))]
getGenericOpts = [Token] -> [([Char], (Token, Token))]
process
  where
    process :: [Token] -> [([Char], (Token, Token))]
process (Token
token:[Token]
rest) =
        case [Char] -> Token -> [Char]
getLiteralStringDef [Char]
"\0" Token
token of
            [Char]
"--" -> forall a b. (a -> b) -> [a] -> [b]
map (\Token
c -> ([Char]
"", (Token
c,Token
c))) [Token]
rest
            Char
'-':Char
'-':[Char]
word -> (forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Char]
"\0=") [Char]
word, (Token
token, Token
token)) forall a. a -> [a] -> [a]
: [Token] -> [([Char], (Token, Token))]
process [Token]
rest
            Char
'-':[Char]
optString ->
                let opts :: [Char]
opts = forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'\0') [Char]
optString
                in
                    case [Token]
rest of
                        Token
next:[Token]
_ | [Char]
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char] -> Token -> [Char]
getLiteralStringDef [Char]
"\0" Token
next  ->
                            forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> ([Char
c], (Token
token, Token
token))) [Char]
opts forall a. [a] -> [a] -> [a]
++ [Token] -> [([Char], (Token, Token))]
process [Token]
rest
                        Token
next:[Token]
remainder ->
                            case forall a. [a] -> [a]
reverse [Char]
opts of
                                Char
last:[Char]
initial ->
                                    forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> ([Char
c], (Token
token, Token
token))) (forall a. [a] -> [a]
reverse [Char]
initial)
                                        forall a. [a] -> [a] -> [a]
++ [([Char
last], (Token
token, Token
next))]
                                        forall a. [a] -> [a] -> [a]
++ [Token] -> [([Char], (Token, Token))]
process [Token]
remainder
                                [] -> [Token] -> [([Char], (Token, Token))]
process [Token]
remainder
                        [] -> forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> ([Char
c], (Token
token, Token
token))) [Char]
opts
            [Char]
_ -> ([Char]
"", (Token
token, Token
token)) forall a. a -> [a] -> [a]
: [Token] -> [([Char], (Token, Token))]
process [Token]
rest
    process [] = []


-- Is this an expansion of multiple items of an array?
isArrayExpansion :: Token -> Bool
isArrayExpansion (T_DollarBraced Id
_ Bool
_ Token
l) =
    let string :: [Char]
string = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [[Char]]
oversimplify Token
l in
        [Char]
"@" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
string Bool -> Bool -> Bool
||
            Bool -> Bool
not ([Char]
"#" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
string) Bool -> Bool -> Bool
&& [Char]
"[@]" forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` [Char]
string
isArrayExpansion Token
_ = Bool
False

-- Is it possible that this arg becomes multiple args?
mayBecomeMultipleArgs :: Token -> Bool
mayBecomeMultipleArgs Token
t = Token -> Bool
willBecomeMultipleArgs Token
t Bool -> Bool -> Bool
|| Bool -> Token -> Bool
f Bool
False Token
t
  where
    f :: Bool -> Token -> Bool
f Bool
quoted (T_DollarBraced Id
_ Bool
_ Token
l) =
        let string :: [Char]
string = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [[Char]]
oversimplify Token
l in
            Bool -> Bool
not Bool
quoted Bool -> Bool -> Bool
|| [Char]
"!" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [Char]
string
    f Bool
quoted (T_DoubleQuoted Id
_ [Token]
parts) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Token -> Bool
f Bool
True) [Token]
parts
    f Bool
quoted (T_NormalWord Id
_ [Token]
parts) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Bool -> Token -> Bool
f Bool
quoted) [Token]
parts
    f Bool
_ Token
_ = Bool
False

-- Is it certain that this word will becomes multiple words?
willBecomeMultipleArgs :: Token -> Bool
willBecomeMultipleArgs Token
t = Token -> Bool
willConcatInAssignment Token
t Bool -> Bool -> Bool
|| Token -> Bool
f Token
t
  where
    f :: Token -> Bool
f T_Extglob {} = Bool
True
    f T_Glob {} = Bool
True
    f T_BraceExpansion {} = Bool
True
    f (T_NormalWord Id
_ [Token]
parts) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
f [Token]
parts
    f Token
_ = Bool
False

-- This does token cause implicit concatenation in assignments?
willConcatInAssignment :: Token -> Bool
willConcatInAssignment Token
token =
    case Token
token of
        t :: Token
t@T_DollarBraced {} -> Token -> Bool
isArrayExpansion Token
t
        (T_DoubleQuoted Id
_ [Token]
parts) -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
willConcatInAssignment [Token]
parts
        (T_NormalWord Id
_ [Token]
parts) -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
willConcatInAssignment [Token]
parts
        Token
_ -> Bool
False

-- Maybe get the literal string corresponding to this token
getLiteralString :: Token -> Maybe String
getLiteralString :: Token -> Maybe [Char]
getLiteralString = forall (m :: * -> *).
Monad m =>
(Token -> m [Char]) -> Token -> m [Char]
getLiteralStringExt (forall a b. a -> b -> a
const forall a. Maybe a
Nothing)

-- Definitely get a literal string, with a given default for all non-literals
getLiteralStringDef :: String -> Token -> String
getLiteralStringDef :: [Char] -> Token -> [Char]
getLiteralStringDef [Char]
x = forall a. Identity a -> a
runIdentity forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
(Token -> m [Char]) -> Token -> m [Char]
getLiteralStringExt (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
x)

-- Definitely get a literal string, skipping over all non-literals
onlyLiteralString :: Token -> String
onlyLiteralString :: Token -> [Char]
onlyLiteralString = [Char] -> Token -> [Char]
getLiteralStringDef [Char]
""

-- Maybe get a literal string, but only if it's an unquoted argument.
getUnquotedLiteral :: Token -> Maybe [Char]
getUnquotedLiteral (T_NormalWord Id
_ [Token]
list) =
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Token -> Maybe [Char]
str [Token]
list
  where
    str :: Token -> Maybe [Char]
str (T_Literal Id
_ [Char]
s) = forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
    str Token
_ = forall a. Maybe a
Nothing
getUnquotedLiteral Token
_ = forall a. Maybe a
Nothing

isQuotes :: Token -> Bool
isQuotes Token
t =
    case Token
t of
        T_DoubleQuoted {} -> Bool
True
        T_SingleQuoted {} -> Bool
True
        Token
_ -> Bool
False

-- Get the last unquoted T_Literal in a word like "${var}foo"THIS
-- or nothing if the word does not end in an unquoted literal.
getTrailingUnquotedLiteral :: Token -> Maybe Token
getTrailingUnquotedLiteral :: Token -> Maybe Token
getTrailingUnquotedLiteral Token
t =
    case Token
t of
        (T_NormalWord Id
_ list :: [Token]
list@(Token
_:[Token]
_)) ->
            Token -> Maybe Token
from (forall a. [a] -> a
last [Token]
list)
        Token
_ -> forall a. Maybe a
Nothing
  where
    from :: Token -> Maybe Token
from Token
t =
        case Token
t of
            T_Literal {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Token
t
            Token
_ -> forall a. Maybe a
Nothing

-- Get the leading, unquoted, literal string of a token (if any).
getLeadingUnquotedString :: Token -> Maybe String
getLeadingUnquotedString :: Token -> Maybe [Char]
getLeadingUnquotedString Token
t =
    case Token
t of
        T_NormalWord Id
_ ((T_Literal Id
_ [Char]
s) : [Token]
rest) -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Char]
s forall a. [a] -> [a] -> [a]
++ [Token] -> [Char]
from [Token]
rest
        Token
_ -> forall a. Maybe a
Nothing
  where
    from :: [Token] -> [Char]
from ((T_Literal Id
_ [Char]
s):[Token]
rest) = [Char]
s forall a. [a] -> [a] -> [a]
++ [Token] -> [Char]
from [Token]
rest
    from [Token]
_ = [Char]
""

-- Maybe get the literal string of this token and any globs in it.
getGlobOrLiteralString :: Token -> Maybe [Char]
getGlobOrLiteralString = forall (m :: * -> *).
Monad m =>
(Token -> m [Char]) -> Token -> m [Char]
getLiteralStringExt Token -> Maybe [Char]
f
  where
    f :: Token -> Maybe [Char]
f (T_Glob Id
_ [Char]
str) = forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
str
    f Token
_ = forall a. Maybe a
Nothing


prop_getLiteralString1 :: Bool
prop_getLiteralString1 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\x01") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\1"
prop_getLiteralString2 :: Bool
prop_getLiteralString2 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\xyz") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\\xyz"
prop_getLiteralString3 :: Bool
prop_getLiteralString3 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\x1") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\x1"
prop_getLiteralString4 :: Bool
prop_getLiteralString4 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\x1y") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\x1y"
prop_getLiteralString5 :: Bool
prop_getLiteralString5 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\xy") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\\xy"
prop_getLiteralString6 :: Bool
prop_getLiteralString6 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\x") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\\x"
prop_getLiteralString7 :: Bool
prop_getLiteralString7 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\1x") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\1x"
prop_getLiteralString8 :: Bool
prop_getLiteralString8 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\12x") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\o12x"
prop_getLiteralString9 :: Bool
prop_getLiteralString9 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\123x") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\o123x"
prop_getLiteralString10 :: Bool
prop_getLiteralString10 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\1234") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\o123\&4"
prop_getLiteralString11 :: Bool
prop_getLiteralString11 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\1") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\1"
prop_getLiteralString12 :: Bool
prop_getLiteralString12 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\12") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\o12"
prop_getLiteralString13 :: Bool
prop_getLiteralString13 = Token -> Maybe [Char]
getLiteralString (Id -> [Char] -> Token
T_DollarSingleQuoted (Int -> Id
Id Int
0) [Char]
"\\123") forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just [Char]
"\o123"

-- Maybe get the literal value of a token, using a custom function
-- to map unrecognized Tokens into strings.
getLiteralStringExt :: Monad m => (Token -> m String) -> Token -> m String
getLiteralStringExt :: forall (m :: * -> *).
Monad m =>
(Token -> m [Char]) -> Token -> m [Char]
getLiteralStringExt Token -> m [Char]
more = Token -> m [Char]
g
  where
    allInList :: [Token] -> m [Char]
allInList = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Token -> m [Char]
g
    g :: Token -> m [Char]
g (T_DoubleQuoted Id
_ [Token]
l) = [Token] -> m [Char]
allInList [Token]
l
    g (T_DollarDoubleQuoted Id
_ [Token]
l) = [Token] -> m [Char]
allInList [Token]
l
    g (T_NormalWord Id
_ [Token]
l) = [Token] -> m [Char]
allInList [Token]
l
    g (TA_Expansion Id
_ [Token]
l) = [Token] -> m [Char]
allInList [Token]
l
    g (T_SingleQuoted Id
_ [Char]
s) = forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
    g (T_Literal Id
_ [Char]
s) = forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
    g (T_ParamSubSpecialChar Id
_ [Char]
s) = forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
s
    g (T_DollarSingleQuoted Id
_ [Char]
s) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Char] -> [Char]
decodeEscapes [Char]
s
    g Token
x = Token -> m [Char]
more Token
x

    -- Bash style $'..' decoding
    decodeEscapes :: [Char] -> [Char]
decodeEscapes (Char
'\\':Char
c:[Char]
cs) =
        case Char
c of
            Char
'a' -> Char
'\a' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'b' -> Char
'\b' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'e' -> Char
'\x1B' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'f' -> Char
'\f' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'n' -> Char
'\n' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'r' -> Char
'\r' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
't' -> Char
'\t' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'v' -> Char
'\v' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'\'' -> Char
'\'' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'"' -> Char
'"' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'\\' -> Char
'\\' forall a. a -> [a] -> [a]
: [Char]
rest
            Char
'x' ->
                case [Char]
cs of
                    (Char
x:Char
y:[Char]
more) | Char -> Bool
isHexDigit Char
x Bool -> Bool -> Bool
&& Char -> Bool
isHexDigit Char
y ->
                        Int -> Char
chr (Int
16forall a. Num a => a -> a -> a
*(Char -> Int
digitToInt Char
x) forall a. Num a => a -> a -> a
+ (Char -> Int
digitToInt Char
y)) forall a. a -> [a] -> [a]
: [Char] -> [Char]
decodeEscapes [Char]
more
                    (Char
x:[Char]
more) | Char -> Bool
isHexDigit Char
x ->
                        Int -> Char
chr (Char -> Int
digitToInt Char
x) forall a. a -> [a] -> [a]
: [Char] -> [Char]
decodeEscapes [Char]
more
                    [Char]
more -> Char
'\\' forall a. a -> [a] -> [a]
: Char
'x' forall a. a -> [a] -> [a]
: [Char] -> [Char]
decodeEscapes [Char]
more
            Char
_ | Char -> Bool
isOctDigit Char
c ->
                let ([Char]
digits, [Char]
more) = forall {a}. (a -> Bool) -> Int -> [a] -> ([a], [a])
spanMax Char -> Bool
isOctDigit Int
3 (Char
cforall a. a -> [a] -> [a]
:[Char]
cs)
                    num :: Int
num = ([Char] -> Int
parseOct [Char]
digits) forall a. Integral a => a -> a -> a
`mod` Int
256
                in (Int -> Char
chr Int
num) forall a. a -> [a] -> [a]
: [Char] -> [Char]
decodeEscapes [Char]
more
            Char
_ -> Char
'\\' forall a. a -> [a] -> [a]
: Char
c forall a. a -> [a] -> [a]
: [Char]
rest
      where
        rest :: [Char]
rest = [Char] -> [Char]
decodeEscapes [Char]
cs
        parseOct :: [Char] -> Int
parseOct = Int -> [Char] -> Int
f Int
0
          where
            f :: Int -> [Char] -> Int
f Int
n [Char]
"" = Int
n
            f Int
n (Char
c:[Char]
rest) = Int -> [Char] -> Int
f (Int
n forall a. Num a => a -> a -> a
* Int
8 forall a. Num a => a -> a -> a
+ Char -> Int
digitToInt Char
c) [Char]
rest
        spanMax :: (a -> Bool) -> Int -> [a] -> ([a], [a])
spanMax a -> Bool
f Int
n [a]
list =
            let ([a]
first, [a]
second) = forall a. (a -> Bool) -> [a] -> ([a], [a])
span a -> Bool
f [a]
list
                ([a]
prefix, [a]
suffix) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
n [a]
first
            in
                ([a]
prefix, [a]
suffix forall a. [a] -> [a] -> [a]
++ [a]
second)
    decodeEscapes (Char
c:[Char]
cs) = Char
c forall a. a -> [a] -> [a]
: [Char] -> [Char]
decodeEscapes [Char]
cs
    decodeEscapes [] = []

-- Is this token a string literal?
isLiteral :: Token -> Bool
isLiteral Token
t = forall a. Maybe a -> Bool
isJust forall a b. (a -> b) -> a -> b
$ Token -> Maybe [Char]
getLiteralString Token
t

-- Escape user data for messages.
-- Messages generally avoid repeating user data, but sometimes it's helpful.
e4m :: [Char] -> [Char]
e4m = [Char] -> [Char]
escapeForMessage
escapeForMessage :: String -> String
escapeForMessage :: [Char] -> [Char]
escapeForMessage [Char]
str = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> [Char]
f [Char]
str
  where
    f :: Char -> [Char]
f Char
'\\' = [Char]
"\\\\"
    f Char
'\n' = [Char]
"\\n"
    f Char
'\r' = [Char]
"\\r"
    f Char
'\t' = [Char]
"\\t"
    f Char
'\x1B' = [Char]
"\\e"
    f Char
c =
        if Char -> Bool
shouldEscape Char
c
        then
            if Char -> Int
ord Char
c forall a. Ord a => a -> a -> Bool
< Int
256
            then [Char]
"\\x" forall a. [a] -> [a] -> [a]
++ (Int -> [Char] -> [Char]
pad0 Int
2 forall a b. (a -> b) -> a -> b
$ Char -> [Char]
toHex Char
c)
            else [Char]
"\\U" forall a. [a] -> [a] -> [a]
++ (Int -> [Char] -> [Char]
pad0 Int
4 forall a b. (a -> b) -> a -> b
$ Char -> [Char]
toHex Char
c)
        else [Char
c]

    shouldEscape :: Char -> Bool
shouldEscape Char
c =
        (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ Char -> Bool
isPrint Char
c)
        Bool -> Bool -> Bool
|| (Bool -> Bool
not (Char -> Bool
isAscii Char
c) Bool -> Bool -> Bool
&& Bool -> Bool
not (Char -> Bool
isLetter Char
c))

    pad0 :: Int -> String -> String
    pad0 :: Int -> [Char] -> [Char]
pad0 Int
n [Char]
s =
        let l :: Int
l = forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
s in
            if Int
l forall a. Ord a => a -> a -> Bool
< Int
n
            then (forall a. Int -> a -> [a]
replicate (Int
nforall a. Num a => a -> a -> a
-Int
l) Char
'0') forall a. [a] -> [a] -> [a]
++ [Char]
s
            else [Char]
s
    toHex :: Char -> String
    toHex :: Char -> [Char]
toHex Char
c = forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper forall a b. (a -> b) -> a -> b
$ forall a. (Integral a, Show a) => a -> [Char] -> [Char]
showHex (Char -> Int
ord Char
c) [Char]
""

-- Turn a NormalWord like foo="bar $baz" into a series of constituent elements like [foo=,bar ,$baz]
getWordParts :: Token -> [Token]
getWordParts (T_NormalWord Id
_ [Token]
l)   = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
getWordParts [Token]
l
getWordParts (T_DoubleQuoted Id
_ [Token]
l) = [Token]
l
-- TA_Expansion is basically T_NormalWord for arithmetic expressions
getWordParts (TA_Expansion Id
_ [Token]
l)   = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
getWordParts [Token]
l
getWordParts Token
other                = [Token
other]

-- Return a list of NormalWords that would result from brace expansion
braceExpand :: Token -> [Token]
braceExpand (T_NormalWord Id
id [Token]
list) = forall a. Int -> [a] -> [a]
take Int
1000 forall a b. (a -> b) -> a -> b
$ do
    [Token]
items <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Token -> [Token]
part [Token]
list
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Id -> [Token] -> Token
T_NormalWord Id
id [Token]
items
  where
    part :: Token -> [Token]
part (T_BraceExpansion Id
id [Token]
items) = do
        Token
item <- [Token]
items
        Token -> [Token]
braceExpand Token
item
    part Token
x = forall (m :: * -> *) a. Monad m => a -> m a
return Token
x

-- Maybe get a SimpleCommand from immediate wrappers like T_Redirections
getCommand :: Token -> Maybe Token
getCommand Token
t =
    case Token
t of
        T_Redirecting Id
_ [Token]
_ Token
w -> Token -> Maybe Token
getCommand Token
w
        T_SimpleCommand Id
_ [Token]
_ (Token
w:[Token]
_) -> forall (m :: * -> *) a. Monad m => a -> m a
return Token
t
        T_Annotation Id
_ [Annotation]
_ Token
t -> Token -> Maybe Token
getCommand Token
t
        Token
_ -> forall a. Maybe a
Nothing

-- Maybe get the command name string of a token representing a command
getCommandName :: Token -> Maybe String
getCommandName :: Token -> Maybe [Char]
getCommandName = forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Token -> (Maybe [Char], Token)
getCommandNameAndToken Bool
False

-- Maybe get the name+arguments of a command.
getCommandArgv :: Token -> Maybe [Token]
getCommandArgv Token
t = do
    (T_SimpleCommand Id
_ [Token]
_ args :: [Token]
args@(Token
_:[Token]
_)) <- Token -> Maybe Token
getCommand Token
t
    forall (m :: * -> *) a. Monad m => a -> m a
return [Token]
args

-- Get the command name token from a command, i.e.
-- the token representing 'ls' in 'ls -la 2> foo'.
-- If it can't be determined, return the original token.
getCommandTokenOrThis :: Token -> Token
getCommandTokenOrThis = forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Token -> (Maybe [Char], Token)
getCommandNameAndToken Bool
False

-- Given a command, get the string and token that represents the command name.
-- If direct, return the actual command (e.g. exec in 'exec ls')
-- If not, return the logical command (e.g. 'ls' in 'exec ls')

getCommandNameAndToken :: Bool -> Token -> (Maybe String, Token)
getCommandNameAndToken :: Bool -> Token -> (Maybe [Char], Token)
getCommandNameAndToken Bool
direct Token
t = forall a. a -> Maybe a -> a
fromMaybe (forall a. Maybe a
Nothing, Token
t) forall a b. (a -> b) -> a -> b
$ do
    cmd :: Token
cmd@(T_SimpleCommand Id
_ [Token]
_ (Token
w:[Token]
rest)) <- Token -> Maybe Token
getCommand Token
t
    [Char]
s <- Token -> Maybe [Char]
getLiteralString Token
w
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a -> a
fromMaybe (forall a. a -> Maybe a
Just [Char]
s, Token
w) forall a b. (a -> b) -> a -> b
$ do
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not Bool
direct
        Token
actual <- forall {p}. [Char] -> p -> [Token] -> Maybe Token
getEffectiveCommandToken [Char]
s Token
cmd [Token]
rest
        forall (m :: * -> *) a. Monad m => a -> m a
return (Token -> Maybe [Char]
getLiteralString Token
actual, Token
actual)
  where
    getEffectiveCommandToken :: [Char] -> p -> [Token] -> Maybe Token
getEffectiveCommandToken [Char]
str p
cmd [Token]
args =
        let
            firstArg :: Maybe Token
firstArg = do
                Token
arg <- forall a. [a] -> Maybe a
listToMaybe [Token]
args
                forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ Token -> Bool
isFlag Token
arg
                forall (m :: * -> *) a. Monad m => a -> m a
return Token
arg
        in
            case [Char]
str of
                [Char]
"busybox" -> Maybe Token
firstArg
                [Char]
"builtin" -> Maybe Token
firstArg
                [Char]
"command" -> Maybe Token
firstArg
                [Char]
"run" -> Maybe Token
firstArg -- Used by bats
                [Char]
"exec" -> do
                    [([Char], (Token, Token))]
opts <- [Char] -> [Token] -> Maybe [([Char], (Token, Token))]
getBsdOpts [Char]
"cla:" [Token]
args
                    ([Char]
_, (Token
t, Token
_)) <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) [([Char], (Token, Token))]
opts
                    forall (m :: * -> *) a. Monad m => a -> m a
return Token
t
                [Char]
_ -> forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
""

-- If a command substitution is a single command, get its name.
--  $(date +%s) = Just "date"
getCommandNameFromExpansion :: Token -> Maybe String
getCommandNameFromExpansion :: Token -> Maybe [Char]
getCommandNameFromExpansion Token
t =
    case Token
t of
        T_DollarExpansion Id
_ [Token
c] -> Token -> Maybe [Char]
extract Token
c
        T_Backticked Id
_ [Token
c] -> Token -> Maybe [Char]
extract Token
c
        T_DollarBraceCommandExpansion Id
_ [Token
c] -> Token -> Maybe [Char]
extract Token
c
        Token
_ -> forall a. Maybe a
Nothing
  where
    extract :: Token -> Maybe [Char]
extract (T_Pipeline Id
_ [Token]
_ [Token
cmd]) = Token -> Maybe [Char]
getCommandName Token
cmd
    extract Token
_ = forall a. Maybe a
Nothing

-- Get the basename of a token representing a command
getCommandBasename :: Token -> Maybe [Char]
getCommandBasename = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Char] -> [Char]
basename forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Maybe [Char]
getCommandName

basename :: [Char] -> [Char]
basename = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'/') forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse

isAssignment :: Token -> Bool
isAssignment Token
t =
    case Token
t of
        T_Redirecting Id
_ [Token]
_ Token
w -> Token -> Bool
isAssignment Token
w
        T_SimpleCommand Id
_ (Token
w:[Token]
_) [] -> Bool
True
        T_Assignment {} -> Bool
True
        T_Annotation Id
_ [Annotation]
_ Token
w -> Token -> Bool
isAssignment Token
w
        Token
_ -> Bool
False

isOnlyRedirection :: Token -> Bool
isOnlyRedirection Token
t =
    case Token
t of
        T_Pipeline Id
_ [Token]
_ [Token
x] -> Token -> Bool
isOnlyRedirection Token
x
        T_Annotation Id
_ [Annotation]
_ Token
w -> Token -> Bool
isOnlyRedirection Token
w
        T_Redirecting Id
_ (Token
_:[Token]
_) Token
c -> Token -> Bool
isOnlyRedirection Token
c
        T_SimpleCommand Id
_ [] [] -> Bool
True
        Token
_ -> Bool
False

isFunction :: Token -> Bool
isFunction Token
t = case Token
t of T_Function {} -> Bool
True; Token
_ -> Bool
False

-- Bats tests are functions for the purpose of 'local' and such
isFunctionLike :: Token -> Bool
isFunctionLike Token
t =
    case Token
t of
        T_Function {} -> Bool
True
        T_BatsTest {} -> Bool
True
        Token
_ -> Bool
False


isBraceExpansion :: Token -> Bool
isBraceExpansion Token
t = case Token
t of T_BraceExpansion {} -> Bool
True; Token
_ -> Bool
False

-- Get the lists of commands from tokens that contain them, such as
-- the conditions and bodies of while loops or branches of if statements.
getCommandSequences :: Token -> [[Token]]
getCommandSequences :: Token -> [[Token]]
getCommandSequences Token
t =
    case Token
t of
        T_Script Id
_ Token
_ [Token]
cmds -> [[Token]
cmds]
        T_BraceGroup Id
_ [Token]
cmds -> [[Token]
cmds]
        T_Subshell Id
_ [Token]
cmds -> [[Token]
cmds]
        T_WhileExpression Id
_ [Token]
cond [Token]
cmds -> [[Token]
cond, [Token]
cmds]
        T_UntilExpression Id
_ [Token]
cond [Token]
cmds -> [[Token]
cond, [Token]
cmds]
        T_ForIn Id
_ [Char]
_ [Token]
_ [Token]
cmds -> [[Token]
cmds]
        T_ForArithmetic Id
_ Token
_ Token
_ Token
_ [Token]
cmds -> [[Token]
cmds]
        T_IfExpression Id
_ [([Token], [Token])]
thens [Token]
elses -> (forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\([Token]
a,[Token]
b) -> [[Token]
a,[Token]
b]) [([Token], [Token])]
thens) forall a. [a] -> [a] -> [a]
++ [[Token]
elses]
        T_Annotation Id
_ [Annotation]
_ Token
t -> Token -> [[Token]]
getCommandSequences Token
t

        T_DollarExpansion Id
_ [Token]
cmds -> [[Token]
cmds]
        T_DollarBraceCommandExpansion Id
_ [Token]
cmds -> [[Token]
cmds]
        T_Backticked Id
_ [Token]
cmds -> [[Token]
cmds]
        Token
_ -> []

-- Get a list of names of associative arrays
getAssociativeArrays :: Token -> [[Char]]
getAssociativeArrays Token
t =
    forall a. Eq a => [a] -> [a]
nub forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall w a. Writer w a -> w
execWriter forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis Token -> Writer [[Char]] ()
f Token
t
  where
    f :: Token -> Writer [String] ()
    f :: Token -> Writer [[Char]] ()
f t :: Token
t@T_SimpleCommand {} = forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ forall a b. (a -> b) -> a -> b
$ do
        [Char]
name <- Token -> Maybe [Char]
getCommandName Token
t
        let assocNames :: [[Char]]
assocNames = [[Char]
"declare",[Char]
"local",[Char]
"typeset"]
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ [Char]
name forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
assocNames
        let flags :: [(Token, [Char])]
flags = Token -> [(Token, [Char])]
getAllFlags Token
t
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ [Char]
"A" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd [(Token, [Char])]
flags
        let args :: [Token]
args = [Token
arg | (Token
arg, [Char]
"") <- [(Token, [Char])]
flags]
        let names :: [[Char]]
names = forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (forall (m :: * -> *).
Monad m =>
(Token -> m [Char]) -> Token -> m [Char]
getLiteralStringExt Token -> Maybe [Char]
nameAssignments) [Token]
args
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [[Char]]
names
    f Token
_ = forall (m :: * -> *) a. Monad m => a -> m a
return ()

    nameAssignments :: Token -> Maybe [Char]
nameAssignments Token
t =
        case Token
t of
            T_Assignment Id
_ AssignmentMode
_ [Char]
name [Token]
_ Token
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
name
            Token
_ -> forall a. Maybe a
Nothing

-- A Pseudoglob is a wildcard pattern used for checking if a match can succeed.
-- For example, [[ $(cmd).jpg == [a-z] ]] will give the patterns *.jpg and ?, which
-- can be proven never to match.
data PseudoGlob = PGAny | PGMany | PGChar Char
    deriving (PseudoGlob -> PseudoGlob -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PseudoGlob -> PseudoGlob -> Bool
$c/= :: PseudoGlob -> PseudoGlob -> Bool
== :: PseudoGlob -> PseudoGlob -> Bool
$c== :: PseudoGlob -> PseudoGlob -> Bool
Eq, Int -> PseudoGlob -> [Char] -> [Char]
[PseudoGlob] -> [Char] -> [Char]
PseudoGlob -> [Char]
forall a.
(Int -> a -> [Char] -> [Char])
-> (a -> [Char]) -> ([a] -> [Char] -> [Char]) -> Show a
showList :: [PseudoGlob] -> [Char] -> [Char]
$cshowList :: [PseudoGlob] -> [Char] -> [Char]
show :: PseudoGlob -> [Char]
$cshow :: PseudoGlob -> [Char]
showsPrec :: Int -> PseudoGlob -> [Char] -> [Char]
$cshowsPrec :: Int -> PseudoGlob -> [Char] -> [Char]
Show)

-- Turn a word into a PG pattern, replacing all unknown/runtime values with
-- PGMany.
wordToPseudoGlob :: Token -> [PseudoGlob]
wordToPseudoGlob :: Token -> [PseudoGlob]
wordToPseudoGlob = forall a. a -> Maybe a -> a
fromMaybe [PseudoGlob
PGMany] forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Token -> Maybe [PseudoGlob]
wordToPseudoGlob' Bool
False

-- Turn a word into a PG pattern, but only if we can preserve
-- exact semantics.
wordToExactPseudoGlob :: Token -> Maybe [PseudoGlob]
wordToExactPseudoGlob :: Token -> Maybe [PseudoGlob]
wordToExactPseudoGlob = Bool -> Token -> Maybe [PseudoGlob]
wordToPseudoGlob' Bool
True

wordToPseudoGlob' :: Bool -> Token -> Maybe [PseudoGlob]
wordToPseudoGlob' :: Bool -> Token -> Maybe [PseudoGlob]
wordToPseudoGlob' Bool
exact Token
word =
    [PseudoGlob] -> [PseudoGlob]
simplifyPseudoGlob forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Token -> Maybe [PseudoGlob]
toGlob Token
word
  where
    toGlob :: Token -> Maybe [PseudoGlob]
    toGlob :: Token -> Maybe [PseudoGlob]
toGlob Token
word =
        case Token
word of
            T_NormalWord Id
_ (T_Literal Id
_ (Char
'~':[Char]
str):[Token]
rest) -> do
                forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not Bool
exact
                let this :: [PseudoGlob]
this = (PseudoGlob
PGMany forall a. a -> [a] -> [a]
: (forall a b. (a -> b) -> [a] -> [b]
map Char -> PseudoGlob
PGChar forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
dropWhile (forall a. Eq a => a -> a -> Bool
/= Char
'/') [Char]
str))
                [PseudoGlob]
tail <- forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall {m :: * -> *}. MonadFail m => Token -> m [PseudoGlob]
f forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
getWordParts [Token]
rest)
                forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [PseudoGlob]
this forall a. [a] -> [a] -> [a]
++ [PseudoGlob]
tail
            Token
_ -> forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall {m :: * -> *}. MonadFail m => Token -> m [PseudoGlob]
f forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
word)

    f :: Token -> m [PseudoGlob]
f Token
x = case Token
x of
        T_Literal Id
_ [Char]
s      -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Char -> PseudoGlob
PGChar [Char]
s
        T_SingleQuoted Id
_ [Char]
s -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Char -> PseudoGlob
PGChar [Char]
s
        T_Glob Id
_ [Char]
"?"       -> forall (m :: * -> *) a. Monad m => a -> m a
return [PseudoGlob
PGAny]
        T_Glob Id
_ [Char]
"*"       -> forall (m :: * -> *) a. Monad m => a -> m a
return [PseudoGlob
PGMany]
        T_Glob Id
_ (Char
'[':[Char]
_) | Bool -> Bool
not Bool
exact -> forall (m :: * -> *) a. Monad m => a -> m a
return [PseudoGlob
PGAny]
        Token
_ -> if Bool
exact then forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
"" else forall (m :: * -> *) a. Monad m => a -> m a
return [PseudoGlob
PGMany]


-- Reorder a PseudoGlob for more efficient matching, e.g.
-- f?*?**g -> f??*g
simplifyPseudoGlob :: [PseudoGlob] -> [PseudoGlob]
simplifyPseudoGlob :: [PseudoGlob] -> [PseudoGlob]
simplifyPseudoGlob = [PseudoGlob] -> [PseudoGlob]
f
  where
    f :: [PseudoGlob] -> [PseudoGlob]
f [] = []
    f (x :: PseudoGlob
x@(PGChar Char
_) : [PseudoGlob]
rest ) = PseudoGlob
x forall a. a -> [a] -> [a]
: [PseudoGlob] -> [PseudoGlob]
f [PseudoGlob]
rest
    f [PseudoGlob]
list =
        let ([PseudoGlob]
anys, [PseudoGlob]
rest) = forall a. (a -> Bool) -> [a] -> ([a], [a])
span (\PseudoGlob
x -> PseudoGlob
x forall a. Eq a => a -> a -> Bool
== PseudoGlob
PGMany Bool -> Bool -> Bool
|| PseudoGlob
x forall a. Eq a => a -> a -> Bool
== PseudoGlob
PGAny) [PseudoGlob]
list in
            [PseudoGlob] -> [PseudoGlob]
order [PseudoGlob]
anys forall a. [a] -> [a] -> [a]
++ [PseudoGlob] -> [PseudoGlob]
f [PseudoGlob]
rest

    order :: [PseudoGlob] -> [PseudoGlob]
order [PseudoGlob]
s = let ([PseudoGlob]
any, [PseudoGlob]
many) = forall a. (a -> Bool) -> [a] -> ([a], [a])
partition (forall a. Eq a => a -> a -> Bool
== PseudoGlob
PGAny) [PseudoGlob]
s in
        [PseudoGlob]
any forall a. [a] -> [a] -> [a]
++ forall a. Int -> [a] -> [a]
take Int
1 [PseudoGlob]
many

-- Check whether the two patterns can ever overlap.
pseudoGlobsCanOverlap :: [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobsCanOverlap :: [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobsCanOverlap = [PseudoGlob] -> [PseudoGlob] -> Bool
matchable
  where
    matchable :: [PseudoGlob] -> [PseudoGlob] -> Bool
matchable x :: [PseudoGlob]
x@(PseudoGlob
xf:[PseudoGlob]
xs) y :: [PseudoGlob]
y@(PseudoGlob
yf:[PseudoGlob]
ys) =
        case (PseudoGlob
xf, PseudoGlob
yf) of
            (PseudoGlob
PGMany, PseudoGlob
_) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
x [PseudoGlob]
ys Bool -> Bool -> Bool
|| [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
y
            (PseudoGlob
_, PseudoGlob
PGMany) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
x [PseudoGlob]
ys Bool -> Bool -> Bool
|| [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
y
            (PseudoGlob
PGAny, PseudoGlob
_) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
ys
            (PseudoGlob
_, PseudoGlob
PGAny) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
ys
            (PseudoGlob
_, PseudoGlob
_) -> PseudoGlob
xf forall a. Eq a => a -> a -> Bool
== PseudoGlob
yf Bool -> Bool -> Bool
&& [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
ys

    matchable [] [] = Bool
True
    matchable (PseudoGlob
PGMany : [PseudoGlob]
rest) [] = [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
rest []
    matchable (PseudoGlob
_:[PseudoGlob]
_) [] = Bool
False
    matchable [] [PseudoGlob]
r = [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
r []

-- Check whether the first pattern always overlaps the second.
pseudoGlobIsSuperSetof :: [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobIsSuperSetof :: [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobIsSuperSetof = [PseudoGlob] -> [PseudoGlob] -> Bool
matchable
  where
    matchable :: [PseudoGlob] -> [PseudoGlob] -> Bool
matchable x :: [PseudoGlob]
x@(PseudoGlob
xf:[PseudoGlob]
xs) y :: [PseudoGlob]
y@(PseudoGlob
yf:[PseudoGlob]
ys) =
        case (PseudoGlob
xf, PseudoGlob
yf) of
            (PseudoGlob
PGMany, PseudoGlob
PGMany) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
x [PseudoGlob]
ys
            (PseudoGlob
PGMany, PseudoGlob
_) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
x [PseudoGlob]
ys Bool -> Bool -> Bool
|| [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
y
            (PseudoGlob
_, PseudoGlob
PGMany) -> Bool
False
            (PseudoGlob
PGAny, PseudoGlob
_) -> [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
ys
            (PseudoGlob
_, PseudoGlob
PGAny) -> Bool
False
            (PseudoGlob
_, PseudoGlob
_) -> PseudoGlob
xf forall a. Eq a => a -> a -> Bool
== PseudoGlob
yf Bool -> Bool -> Bool
&& [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
xs [PseudoGlob]
ys

    matchable [] [] = Bool
True
    matchable (PseudoGlob
PGMany : [PseudoGlob]
rest) [] = [PseudoGlob] -> [PseudoGlob] -> Bool
matchable [PseudoGlob]
rest []
    matchable [PseudoGlob]
_ [PseudoGlob]
_ = Bool
False

wordsCanBeEqual :: Token -> Token -> Bool
wordsCanBeEqual Token
x Token
y = [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobsCanOverlap (Token -> [PseudoGlob]
wordToPseudoGlob Token
x) (Token -> [PseudoGlob]
wordToPseudoGlob Token
y)

-- Is this an expansion that can be quoted,
-- e.g. $(foo) `foo` $foo (but not {foo,})?
isQuoteableExpansion :: Token -> Bool
isQuoteableExpansion Token
t = case Token
t of
    T_DollarBraced {} -> Bool
True
    Token
_ -> Token -> Bool
isCommandSubstitution Token
t

isCommandSubstitution :: Token -> Bool
isCommandSubstitution Token
t = case Token
t of
    T_DollarExpansion {} -> Bool
True
    T_DollarBraceCommandExpansion {} -> Bool
True
    T_Backticked {} -> Bool
True
    Token
_ -> Bool
False

-- Is this an expansion that results in a simple string?
isStringExpansion :: Token -> Bool
isStringExpansion Token
t = Token -> Bool
isCommandSubstitution Token
t Bool -> Bool -> Bool
|| case Token
t of
    T_DollarArithmetic {} -> Bool
True
    T_DollarBraced {} -> Bool -> Bool
not (Token -> Bool
isArrayExpansion Token
t)
    Token
_ -> Bool
False

-- Is this a T_Annotation that ignores a specific code?
isAnnotationIgnoringCode :: Integer -> Token -> Bool
isAnnotationIgnoringCode Integer
code Token
t =
    case Token
t of
        T_Annotation Id
_ [Annotation]
anns Token
_ -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Annotation -> Bool
hasNum [Annotation]
anns
        Token
_ -> Bool
False
  where
    hasNum :: Annotation -> Bool
hasNum (DisableComment Integer
from Integer
to) = Integer
code forall a. Ord a => a -> a -> Bool
>= Integer
from Bool -> Bool -> Bool
&& Integer
code forall a. Ord a => a -> a -> Bool
< Integer
to
    hasNum Annotation
_                   = Bool
False

prop_executableFromShebang1 :: Bool
prop_executableFromShebang1 = [Char] -> [Char]
executableFromShebang [Char]
"/bin/sh" forall a. Eq a => a -> a -> Bool
== [Char]
"sh"
prop_executableFromShebang2 :: Bool
prop_executableFromShebang2 = [Char] -> [Char]
executableFromShebang [Char]
"/bin/bash" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang3 :: Bool
prop_executableFromShebang3 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env ksh" forall a. Eq a => a -> a -> Bool
== [Char]
"ksh"
prop_executableFromShebang4 :: Bool
prop_executableFromShebang4 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env -S foo=bar bash -x" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang5 :: Bool
prop_executableFromShebang5 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env --split-string=bash -x" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang6 :: Bool
prop_executableFromShebang6 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env --split-string=foo=bar bash -x" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang7 :: Bool
prop_executableFromShebang7 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env --split-string bash -x" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang8 :: Bool
prop_executableFromShebang8 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env --split-string foo=bar bash -x" forall a. Eq a => a -> a -> Bool
== [Char]
"bash"
prop_executableFromShebang9 :: Bool
prop_executableFromShebang9 = [Char] -> [Char]
executableFromShebang [Char]
"/usr/bin/env foo=bar dash" forall a. Eq a => a -> a -> Bool
== [Char]
"dash"
prop_executableFromShebang10 :: Bool
prop_executableFromShebang10 = [Char] -> [Char]
executableFromShebang [Char]
"/bin/busybox sh" forall a. Eq a => a -> a -> Bool
== [Char]
"ash"
prop_executableFromShebang11 :: Bool
prop_executableFromShebang11 = [Char] -> [Char]
executableFromShebang [Char]
"/bin/busybox ash" forall a. Eq a => a -> a -> Bool
== [Char]
"ash"

-- Get the shell executable from a string like '/usr/bin/env bash'
executableFromShebang :: String -> String
executableFromShebang :: [Char] -> [Char]
executableFromShebang = [Char] -> [Char]
shellFor
  where
    re :: Regex
re = [Char] -> Regex
mkRegex [Char]
"/env +(-S|--split-string=?)? *(.*)"
    shellFor :: [Char] -> [Char]
shellFor [Char]
s | [Char]
s [Char] -> Regex -> Bool
`matches` Regex
re =
        case Regex -> [Char] -> Maybe [[Char]]
matchRegex Regex
re [Char]
s of
            Just [[Char]
flag, [Char]
shell] -> [[Char]] -> [Char]
fromEnvArgs ([Char] -> [[Char]]
words [Char]
shell)
            Maybe [[Char]]
_ -> [Char]
""
    shellFor [Char]
sb =
        case [Char] -> [[Char]]
words [Char]
sb of
            [] -> [Char]
""
            [[Char]
x] -> [Char] -> [Char]
basename [Char]
x
            ([Char]
first:[Char]
second:[[Char]]
args) | [Char] -> [Char]
basename [Char]
first forall a. Eq a => a -> a -> Bool
== [Char]
"busybox" ->
                case [Char] -> [Char]
basename [Char]
second of
                   [Char]
"sh" -> [Char]
"ash" -- busybox sh is ash
                   [Char]
x -> [Char]
x
            ([Char]
first:[[Char]]
args) | [Char] -> [Char]
basename [Char]
first forall a. Eq a => a -> a -> Bool
== [Char]
"env" ->
                [[Char]] -> [Char]
fromEnvArgs [[Char]]
args
            ([Char]
first:[[Char]]
_) -> [Char] -> [Char]
basename [Char]
first

    fromEnvArgs :: [[Char]] -> [Char]
fromEnvArgs [[Char]]
args = forall a. a -> Maybe a -> a
fromMaybe [Char]
"" forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
notElem Char
'=') forall a b. (a -> b) -> a -> b
$ [[Char]] -> [[Char]]
skipFlags [[Char]]
args
    basename :: [Char] -> [Char]
basename [Char]
s = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'/') forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$ [Char]
s
    skipFlags :: [[Char]] -> [[Char]]
skipFlags = forall a. (a -> Bool) -> [a] -> [a]
dropWhile ([Char]
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`)


-- Determining if a name is a variable
isVariableStartChar :: Char -> Bool
isVariableStartChar Char
x = Char
x forall a. Eq a => a -> a -> Bool
== Char
'_' Bool -> Bool -> Bool
|| Char -> Bool
isAsciiLower Char
x Bool -> Bool -> Bool
|| Char -> Bool
isAsciiUpper Char
x
isVariableChar :: Char -> Bool
isVariableChar Char
x = Char -> Bool
isVariableStartChar Char
x Bool -> Bool -> Bool
|| Char -> Bool
isDigit Char
x
isSpecialVariableChar :: Char -> Bool
isSpecialVariableChar = (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char]
"*@#?-$!")
variableNameRegex :: Regex
variableNameRegex = [Char] -> Regex
mkRegex [Char]
"[_a-zA-Z][_a-zA-Z0-9]*"

prop_isVariableName1 :: Bool
prop_isVariableName1 = [Char] -> Bool
isVariableName [Char]
"_fo123"
prop_isVariableName2 :: Bool
prop_isVariableName2 = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ [Char] -> Bool
isVariableName [Char]
"4"
prop_isVariableName3 :: Bool
prop_isVariableName3 = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ [Char] -> Bool
isVariableName [Char]
"test: "
isVariableName :: [Char] -> Bool
isVariableName (Char
x:[Char]
r) = Char -> Bool
isVariableStartChar Char
x Bool -> Bool -> Bool
&& forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isVariableChar [Char]
r
isVariableName [Char]
_     = Bool
False


-- Get the variable name from an expansion like ${var:-foo}
prop_getBracedReference1 :: Bool
prop_getBracedReference1 = [Char] -> [Char]
getBracedReference [Char]
"foo" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference2 :: Bool
prop_getBracedReference2 = [Char] -> [Char]
getBracedReference [Char]
"#foo" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference3 :: Bool
prop_getBracedReference3 = [Char] -> [Char]
getBracedReference [Char]
"#" forall a. Eq a => a -> a -> Bool
== [Char]
"#"
prop_getBracedReference4 :: Bool
prop_getBracedReference4 = [Char] -> [Char]
getBracedReference [Char]
"##" forall a. Eq a => a -> a -> Bool
== [Char]
"#"
prop_getBracedReference5 :: Bool
prop_getBracedReference5 = [Char] -> [Char]
getBracedReference [Char]
"#!" forall a. Eq a => a -> a -> Bool
== [Char]
"!"
prop_getBracedReference6 :: Bool
prop_getBracedReference6 = [Char] -> [Char]
getBracedReference [Char]
"!#" forall a. Eq a => a -> a -> Bool
== [Char]
"#"
prop_getBracedReference7 :: Bool
prop_getBracedReference7 = [Char] -> [Char]
getBracedReference [Char]
"!foo#?" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference8 :: Bool
prop_getBracedReference8 = [Char] -> [Char]
getBracedReference [Char]
"foo-bar" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference9 :: Bool
prop_getBracedReference9 = [Char] -> [Char]
getBracedReference [Char]
"foo:-bar" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference10 :: Bool
prop_getBracedReference10 = [Char] -> [Char]
getBracedReference [Char]
"foo: -1" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
prop_getBracedReference11 :: Bool
prop_getBracedReference11 = [Char] -> [Char]
getBracedReference [Char]
"!os*" forall a. Eq a => a -> a -> Bool
== [Char]
""
prop_getBracedReference11b :: Bool
prop_getBracedReference11b = [Char] -> [Char]
getBracedReference [Char]
"!os@" forall a. Eq a => a -> a -> Bool
== [Char]
""
prop_getBracedReference12 :: Bool
prop_getBracedReference12 = [Char] -> [Char]
getBracedReference [Char]
"!os?bar**" forall a. Eq a => a -> a -> Bool
== [Char]
""
prop_getBracedReference13 :: Bool
prop_getBracedReference13 = [Char] -> [Char]
getBracedReference [Char]
"foo[bar]" forall a. Eq a => a -> a -> Bool
== [Char]
"foo"
getBracedReference :: [Char] -> [Char]
getBracedReference [Char]
s = forall a. a -> Maybe a -> a
fromMaybe [Char]
s forall a b. (a -> b) -> a -> b
$
    [Char] -> Maybe [Char]
nameExpansion [Char]
s forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` forall {m :: * -> *}.
(Monad m, Alternative m) =>
[Char] -> m [Char]
takeName [Char]
noPrefix forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` forall {m :: * -> *}. MonadFail m => [Char] -> m [Char]
getSpecial [Char]
noPrefix forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` forall {m :: * -> *}. MonadFail m => [Char] -> m [Char]
getSpecial [Char]
s
  where
    noPrefix :: [Char]
noPrefix = [Char] -> [Char]
dropPrefix [Char]
s
    dropPrefix :: [Char] -> [Char]
dropPrefix (Char
c:[Char]
rest) | Char
c forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char]
"!#" = [Char]
rest
    dropPrefix [Char]
cs = [Char]
cs
    takeName :: [Char] -> m [Char]
takeName [Char]
s = do
        let name :: [Char]
name = forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isVariableChar [Char]
s
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Char]
name
        forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
name
    getSpecial :: [Char] -> m [Char]
getSpecial (Char
c:[Char]
_) | Char -> Bool
isSpecialVariableChar Char
c = forall (m :: * -> *) a. Monad m => a -> m a
return [Char
c]
    getSpecial [Char]
_ = forall (m :: * -> *) a. MonadFail m => [Char] -> m a
fail [Char]
"empty or not special"

    nameExpansion :: [Char] -> Maybe [Char]
nameExpansion (Char
'!':Char
next:[Char]
rest) = do -- e.g. ${!foo*bar*}
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ Char -> Bool
isVariableChar Char
next -- e.g. ${!@}
        Char
first <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isVariableChar) [Char]
rest
        forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ Char
first forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char]
"*?@"
        forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
""
    nameExpansion [Char]
_ = forall a. Maybe a
Nothing

-- Get the variable modifier like /a/b in ${var/a/b}
prop_getBracedModifier1 :: Bool
prop_getBracedModifier1 = [Char] -> [Char]
getBracedModifier [Char]
"foo:bar:baz" forall a. Eq a => a -> a -> Bool
== [Char]
":bar:baz"
prop_getBracedModifier2 :: Bool
prop_getBracedModifier2 = [Char] -> [Char]
getBracedModifier [Char]
"!var:-foo" forall a. Eq a => a -> a -> Bool
== [Char]
":-foo"
prop_getBracedModifier3 :: Bool
prop_getBracedModifier3 = [Char] -> [Char]
getBracedModifier [Char]
"foo[bar]" forall a. Eq a => a -> a -> Bool
== [Char]
"[bar]"
prop_getBracedModifier4 :: Bool
prop_getBracedModifier4 = [Char] -> [Char]
getBracedModifier [Char]
"foo[@]@Q" forall a. Eq a => a -> a -> Bool
== [Char]
"[@]@Q"
prop_getBracedModifier5 :: Bool
prop_getBracedModifier5 = [Char] -> [Char]
getBracedModifier [Char]
"@@Q" forall a. Eq a => a -> a -> Bool
== [Char]
"@Q"
getBracedModifier :: [Char] -> [Char]
getBracedModifier [Char]
s = forall {a}. a -> [a] -> a
headOrDefault [Char]
"" forall a b. (a -> b) -> a -> b
$ do
    let var :: [Char]
var = [Char] -> [Char]
getBracedReference [Char]
s
    [Char]
a <- [Char] -> [[Char]]
dropModifier [Char]
s
    forall {a}. Eq a => [a] -> [a] -> [[a]]
dropPrefix [Char]
var [Char]
a
  where
    dropPrefix :: [a] -> [a] -> [[a]]
dropPrefix [] [a]
t        = forall (m :: * -> *) a. Monad m => a -> m a
return [a]
t
    dropPrefix (a
a:[a]
b) (a
c:[a]
d) | a
a forall a. Eq a => a -> a -> Bool
== a
c = [a] -> [a] -> [[a]]
dropPrefix [a]
b [a]
d
    dropPrefix [a]
_ [a]
_         = []

    dropModifier :: [Char] -> [[Char]]
dropModifier (Char
c:[Char]
rest) | Char
c forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char]
"#!" = [[Char]
rest, Char
cforall a. a -> [a] -> [a]
:[Char]
rest]
    dropModifier [Char]
x        = [[Char]
x]

-- Get the variables from indices like ["x", "y"] in ${var[x+y+1]}
prop_getIndexReferences1 :: Bool
prop_getIndexReferences1 = [Char] -> [[Char]]
getIndexReferences [Char]
"var[x+y+1]" forall a. Eq a => a -> a -> Bool
== [[Char]
"x", [Char]
"y"]
getIndexReferences :: [Char] -> [[Char]]
getIndexReferences [Char]
s = forall a. a -> Maybe a -> a
fromMaybe [] forall a b. (a -> b) -> a -> b
$ do
    [[Char]]
match <- Regex -> [Char] -> Maybe [[Char]]
matchRegex Regex
re [Char]
s
    [Char]
index <- [[Char]]
match forall {a}. [a] -> Int -> Maybe a
!!! Int
0
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Regex -> [Char] -> [[Char]]
matchAllStrings Regex
variableNameRegex [Char]
index
  where
    re :: Regex
re = [Char] -> Regex
mkRegex [Char]
"(\\[.*\\])"

prop_getOffsetReferences1 :: Bool
prop_getOffsetReferences1 = [Char] -> [[Char]]
getOffsetReferences [Char]
":bar" forall a. Eq a => a -> a -> Bool
== [[Char]
"bar"]
prop_getOffsetReferences2 :: Bool
prop_getOffsetReferences2 = [Char] -> [[Char]]
getOffsetReferences [Char]
":bar:baz" forall a. Eq a => a -> a -> Bool
== [[Char]
"bar", [Char]
"baz"]
prop_getOffsetReferences3 :: Bool
prop_getOffsetReferences3 = [Char] -> [[Char]]
getOffsetReferences [Char]
"[foo]:bar" forall a. Eq a => a -> a -> Bool
== [[Char]
"bar"]
prop_getOffsetReferences4 :: Bool
prop_getOffsetReferences4 = [Char] -> [[Char]]
getOffsetReferences [Char]
"[foo]:bar:baz" forall a. Eq a => a -> a -> Bool
== [[Char]
"bar", [Char]
"baz"]
getOffsetReferences :: [Char] -> [[Char]]
getOffsetReferences [Char]
mods = forall a. a -> Maybe a -> a
fromMaybe [] forall a b. (a -> b) -> a -> b
$ do
-- if mods start with [, then drop until ]
    [[Char]]
match <- Regex -> [Char] -> Maybe [[Char]]
matchRegex Regex
re [Char]
mods
    [Char]
offsets <- [[Char]]
match forall {a}. [a] -> Int -> Maybe a
!!! Int
1
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Regex -> [Char] -> [[Char]]
matchAllStrings Regex
variableNameRegex [Char]
offsets
  where
    re :: Regex
re = [Char] -> Regex
mkRegex [Char]
"^(\\[.+\\])? *:([^-=?+].*)"


-- Returns whether a token is a parameter expansion without any modifiers.
-- True for $var ${var} $1 $#
-- False for ${#var} ${var[x]} ${var:-0}
isUnmodifiedParameterExpansion :: Token -> Bool
isUnmodifiedParameterExpansion Token
t =
    case Token
t of
        T_DollarBraced Id
_ Bool
False Token
_ -> Bool
True
        T_DollarBraced Id
_ Bool
_ Token
list ->
            let str :: [Char]
str = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [[Char]]
oversimplify Token
list
            in [Char] -> [Char]
getBracedReference [Char]
str forall a. Eq a => a -> a -> Bool
== [Char]
str
        Token
_ -> Bool
False

--- A list of the element and all its parents up to the root node.
getPath :: Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
t = Token
t forall a. a -> [a] -> [a]
:
    case forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (Token -> Id
getId Token
t) Map Id Token
tree of
        Maybe Token
Nothing     -> []
        Just Token
parent -> Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
parent

isClosingFileOp :: Token -> Bool
isClosingFileOp Token
op =
    case Token
op of
        T_IoDuplicate Id
_ (T_GREATAND Id
_) [Char]
"-" -> Bool
True
        T_IoDuplicate Id
_ (T_LESSAND  Id
_) [Char]
"-" -> Bool
True
        Token
_                                  -> Bool
False

getEnableDirectives :: Token -> [[Char]]
getEnableDirectives Token
root =
    case Token
root of
        T_Annotation Id
_ [Annotation]
list Token
_ -> [[Char]
s | EnableComment [Char]
s <- [Annotation]
list]
        Token
_ -> []

return []
runTests :: IO Bool
runTests = $Bool
[Char]
[([Char], Property)]
[([Char], Property)] -> (Property -> IO Result) -> IO Bool
forall prop. Testable prop => prop -> IO Result
forall prop. Testable prop => prop -> Property
runQuickCheckAll :: [([Char], Property)] -> (Property -> IO Result) -> IO Bool
property :: forall prop. Testable prop => prop -> Property
quickCheckResult :: forall prop. Testable prop => prop -> IO Result
prop_getOffsetReferences4 :: Bool
prop_getOffsetReferences3 :: Bool
prop_getOffsetReferences2 :: Bool
prop_getOffsetReferences1 :: Bool
prop_getIndexReferences1 :: Bool
prop_getBracedModifier5 :: Bool
prop_getBracedModifier4 :: Bool
prop_getBracedModifier3 :: Bool
prop_getBracedModifier2 :: Bool
prop_getBracedModifier1 :: Bool
prop_getBracedReference13 :: Bool
prop_getBracedReference12 :: Bool
prop_getBracedReference11b :: Bool
prop_getBracedReference11 :: Bool
prop_getBracedReference10 :: Bool
prop_getBracedReference9 :: Bool
prop_getBracedReference8 :: Bool
prop_getBracedReference7 :: Bool
prop_getBracedReference6 :: Bool
prop_getBracedReference5 :: Bool
prop_getBracedReference4 :: Bool
prop_getBracedReference3 :: Bool
prop_getBracedReference2 :: Bool
prop_getBracedReference1 :: Bool
prop_isVariableName3 :: Bool
prop_isVariableName2 :: Bool
prop_isVariableName1 :: Bool
prop_executableFromShebang11 :: Bool
prop_executableFromShebang10 :: Bool
prop_executableFromShebang9 :: Bool
prop_executableFromShebang8 :: Bool
prop_executableFromShebang7 :: Bool
prop_executableFromShebang6 :: Bool
prop_executableFromShebang5 :: Bool
prop_executableFromShebang4 :: Bool
prop_executableFromShebang3 :: Bool
prop_executableFromShebang2 :: Bool
prop_executableFromShebang1 :: Bool
prop_getLiteralString13 :: Bool
prop_getLiteralString12 :: Bool
prop_getLiteralString11 :: Bool
prop_getLiteralString10 :: Bool
prop_getLiteralString9 :: Bool
prop_getLiteralString8 :: Bool
prop_getLiteralString7 :: Bool
prop_getLiteralString6 :: Bool
prop_getLiteralString5 :: Bool
prop_getLiteralString4 :: Bool
prop_getLiteralString3 :: Bool
prop_getLiteralString2 :: Bool
prop_getLiteralString1 :: Bool
quickCheckAll