mirror of
https://github.com/koalaman/shellcheck.git
synced 2025-10-01 01:09:18 +08:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1ff67a61b4 | ||
|
349dfdab35 | ||
|
1ab29ddb39 | ||
|
09b7788412 | ||
|
ef2135f3aa | ||
|
d10c3b2709 | ||
|
ca37794b7c | ||
|
8b8b48ef55 | ||
|
aea0310a07 | ||
|
7fff088ce9 | ||
|
65ab8c8ecb | ||
|
3a041954d1 | ||
|
828378cdff | ||
|
509cda4dcf | ||
|
6076f0b1da | ||
|
1d26c280d6 | ||
|
c785d43e34 | ||
|
4c3e731445 | ||
|
3940462da3 | ||
|
bb7ef5834b |
141
LICENSE
141
LICENSE
@@ -1,5 +1,5 @@
|
|||||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Version 3, 19 November 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
@@ -7,15 +7,17 @@
|
|||||||
|
|
||||||
Preamble
|
Preamble
|
||||||
|
|
||||||
The GNU Affero General Public License is a free, copyleft license for
|
The GNU General Public License is a free, copyleft license for
|
||||||
software and other kinds of works, specifically designed to ensure
|
software and other kinds of works.
|
||||||
cooperation with the community in the case of network server software.
|
|
||||||
|
|
||||||
The licenses for most software and other practical works are designed
|
The licenses for most software and other practical works are designed
|
||||||
to take away your freedom to share and change the works. By contrast,
|
to take away your freedom to share and change the works. By contrast,
|
||||||
our General Public Licenses are intended to guarantee your freedom to
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
share and change all versions of a program--to make sure it remains free
|
share and change all versions of a program--to make sure it remains free
|
||||||
software for all its users.
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
When we speak of free software, we are referring to freedom, not
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
|
|||||||
want it, that you can change the software or use pieces of it in new
|
want it, that you can change the software or use pieces of it in new
|
||||||
free programs, and that you know you can do these things.
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
Developers that use our General Public Licenses protect your rights
|
To protect your rights, we need to prevent others from denying you
|
||||||
with two steps: (1) assert copyright on the software, and (2) offer
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
you this License which gives you legal permission to copy, distribute
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
and/or modify the software.
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
A secondary benefit of defending all users' freedom is that
|
For example, if you distribute copies of such a program, whether
|
||||||
improvements made in alternate versions of the program, if they
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
receive widespread use, become available for other developers to
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
incorporate. Many developers of free software are heartened and
|
or can get the source code. And you must show them these terms so they
|
||||||
encouraged by the resulting cooperation. However, in the case of
|
know their rights.
|
||||||
software used on network servers, this result may fail to come about.
|
|
||||||
The GNU General Public License permits making a modified version and
|
|
||||||
letting the public access it on a server without ever releasing its
|
|
||||||
source code to the public.
|
|
||||||
|
|
||||||
The GNU Affero General Public License is designed specifically to
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
ensure that, in such cases, the modified source code becomes available
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
to the community. It requires the operator of a network server to
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
provide the source code of the modified version running there to the
|
|
||||||
users of that server. Therefore, public use of a modified version, on
|
|
||||||
a publicly accessible server, gives the public access to the source
|
|
||||||
code of the modified version.
|
|
||||||
|
|
||||||
An older license, called the Affero General Public License and
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
published by Affero, was designed to accomplish similar goals. This is
|
that there is no warranty for this free software. For both users' and
|
||||||
a different license, not a version of the Affero GPL, but Affero has
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
released a new version of the Affero GPL which permits relicensing under
|
changed, so that their problems will not be attributed erroneously to
|
||||||
this license.
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
The precise terms and conditions for copying, distribution and
|
||||||
modification follow.
|
modification follow.
|
||||||
@@ -60,7 +72,7 @@ modification follow.
|
|||||||
|
|
||||||
0. Definitions.
|
0. Definitions.
|
||||||
|
|
||||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
works, such as semiconductor masks.
|
works, such as semiconductor masks.
|
||||||
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
|
|||||||
the Program, the only way you could satisfy both those terms and this
|
the Program, the only way you could satisfy both those terms and this
|
||||||
License would be to refrain entirely from conveying the Program.
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, if you modify the
|
|
||||||
Program, your modified version must prominently offer all users
|
|
||||||
interacting with it remotely through a computer network (if your version
|
|
||||||
supports such interaction) an opportunity to receive the Corresponding
|
|
||||||
Source of your version by providing access to the Corresponding Source
|
|
||||||
from a network server at no charge, through some standard or customary
|
|
||||||
means of facilitating copying of software. This Corresponding Source
|
|
||||||
shall include the Corresponding Source for any work covered by version 3
|
|
||||||
of the GNU General Public License that is incorporated pursuant to the
|
|
||||||
following paragraph.
|
|
||||||
|
|
||||||
Notwithstanding any other provision of this License, you have
|
Notwithstanding any other provision of this License, you have
|
||||||
permission to link or combine any covered work with a work licensed
|
permission to link or combine any covered work with a work licensed
|
||||||
under version 3 of the GNU General Public License into a single
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
combined work, and to convey the resulting work. The terms of this
|
combined work, and to convey the resulting work. The terms of this
|
||||||
License will continue to apply to the part which is the covered work,
|
License will continue to apply to the part which is the covered work,
|
||||||
but the work with which it is combined will remain governed by version
|
but the special requirements of the GNU Affero General Public License,
|
||||||
3 of the GNU General Public License.
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
14. Revised Versions of this License.
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
The Free Software Foundation may publish revised and/or new versions of
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
the GNU Affero General Public License from time to time. Such new versions
|
the GNU General Public License from time to time. Such new versions will
|
||||||
will be similar in spirit to the present version, but may differ in detail to
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
address new problems or concerns.
|
address new problems or concerns.
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the
|
Each version is given a distinguishing version number. If the
|
||||||
Program specifies that a certain numbered version of the GNU Affero General
|
Program specifies that a certain numbered version of the GNU General
|
||||||
Public License "or any later version" applies to it, you have the
|
Public License "or any later version" applies to it, you have the
|
||||||
option of following the terms and conditions either of that numbered
|
option of following the terms and conditions either of that numbered
|
||||||
version or of any later version published by the Free Software
|
version or of any later version published by the Free Software
|
||||||
Foundation. If the Program does not specify a version number of the
|
Foundation. If the Program does not specify a version number of the
|
||||||
GNU Affero General Public License, you may choose any version ever published
|
GNU General Public License, you may choose any version ever published
|
||||||
by the Free Software Foundation.
|
by the Free Software Foundation.
|
||||||
|
|
||||||
If the Program specifies that a proxy can decide which future
|
If the Program specifies that a proxy can decide which future
|
||||||
versions of the GNU Affero General Public License can be used, that proxy's
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
public statement of acceptance of a version permanently authorizes you
|
public statement of acceptance of a version permanently authorizes you
|
||||||
to choose that version for the Program.
|
to choose that version for the Program.
|
||||||
|
|
||||||
@@ -633,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||||||
Copyright (C) <year> <name of author>
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
If your software can interact with users remotely through a computer
|
If the program does terminal interaction, make it output a short
|
||||||
network, you should also make sure that it provides a way for users to
|
notice like this when it starts in an interactive mode:
|
||||||
get its source. For example, if your program is a web application, its
|
|
||||||
interface could display a "Source" link that leads users to an archive
|
<program> Copyright (C) <year> <name of author>
|
||||||
of the code. There are many ways you could offer source, and different
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
solutions will be better for different programs; see section 13 for the
|
This is free software, and you are welcome to redistribute it
|
||||||
specific requirements.
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
<http://www.gnu.org/licenses/>.
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
|
24
README.md
24
README.md
@@ -3,7 +3,7 @@
|
|||||||
http://www.shellcheck.net
|
http://www.shellcheck.net
|
||||||
|
|
||||||
Copyright 2012-2015, Vidar 'koala_man' Holen
|
Copyright 2012-2015, Vidar 'koala_man' Holen
|
||||||
Licensed under the GNU Affero General Public License, v3
|
Licensed under the GNU General Public License, v3
|
||||||
|
|
||||||
The goals of ShellCheck are:
|
The goals of ShellCheck are:
|
||||||
|
|
||||||
@@ -57,6 +57,10 @@ On Mac OS X with MacPorts (http://www.macports.org/):
|
|||||||
|
|
||||||
port install hs-cabal-install
|
port install hs-cabal-install
|
||||||
|
|
||||||
|
On native Windows (https://www.haskell.org/platform/):
|
||||||
|
|
||||||
|
Download and install the latest version of the Haskell Platform.
|
||||||
|
|
||||||
Let cabal update itself, in case your distro version is outdated:
|
Let cabal update itself, in case your distro version is outdated:
|
||||||
|
|
||||||
$ cabal update
|
$ cabal update
|
||||||
@@ -66,17 +70,29 @@ With cabal installed, cd to the ShellCheck source directory and:
|
|||||||
|
|
||||||
$ cabal install
|
$ cabal install
|
||||||
|
|
||||||
This will install ShellCheck to your ~/.cabal/bin directory.
|
This will install ShellCheck to your `~/.cabal/bin` directory.
|
||||||
|
|
||||||
Add the directory to your PATH (for bash, add this to your ~/.bashrc file):
|
Add the directory to your `PATH` (for bash, add this to your `~/.bashrc`):
|
||||||
|
|
||||||
export PATH=$HOME/.cabal/bin:$PATH
|
export PATH="$HOME/.cabal/bin:$PATH"
|
||||||
|
|
||||||
Verify that your PATH is set up correctly:
|
Verify that your PATH is set up correctly:
|
||||||
|
|
||||||
$ which shellcheck
|
$ which shellcheck
|
||||||
~/.cabal/bin/shellcheck
|
~/.cabal/bin/shellcheck
|
||||||
|
|
||||||
|
On native Windows, the `PATH` should already be set up, but the system
|
||||||
|
may use a legacy codepage. In `cmd.exe`, `powershell.exe` and Powershell ISE,
|
||||||
|
make sure to use a TrueType font, not a Raster font, and set the active
|
||||||
|
codepage to UTF-8 (65001) with `chcp`:
|
||||||
|
|
||||||
|
> chcp 65001
|
||||||
|
Active code page: 65001
|
||||||
|
|
||||||
|
In Powershell ISE, you may need to additionally update the output encoding:
|
||||||
|
|
||||||
|
> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
## Running tests
|
## Running tests
|
||||||
|
|
||||||
To run the unit test suite:
|
To run the unit test suite:
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
Name: ShellCheck
|
Name: ShellCheck
|
||||||
Version: 0.3.7
|
Version: 0.3.8
|
||||||
Synopsis: Shell script analysis tool
|
Synopsis: Shell script analysis tool
|
||||||
License: AGPL-3
|
License: GPL-3
|
||||||
License-file: LICENSE
|
License-file: LICENSE
|
||||||
Category: Static Analysis
|
Category: Static Analysis
|
||||||
Author: Vidar Holen
|
Author: Vidar Holen
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
module ShellCheck.AST where
|
module ShellCheck.AST where
|
||||||
@@ -53,7 +53,7 @@ data Token =
|
|||||||
| T_Backticked Id [Token]
|
| T_Backticked Id [Token]
|
||||||
| T_Bang Id
|
| T_Bang Id
|
||||||
| T_Banged Id Token
|
| T_Banged Id Token
|
||||||
| T_BraceExpansion Id String
|
| T_BraceExpansion Id [Token]
|
||||||
| T_BraceGroup Id [Token]
|
| T_BraceGroup Id [Token]
|
||||||
| T_CLOBBER Id
|
| T_CLOBBER Id
|
||||||
| T_Case Id
|
| T_Case Id
|
||||||
@@ -171,6 +171,7 @@ analyze f g i =
|
|||||||
delve (T_DoubleQuoted id list) = dl list $ T_DoubleQuoted id
|
delve (T_DoubleQuoted id list) = dl list $ T_DoubleQuoted id
|
||||||
delve (T_DollarDoubleQuoted id list) = dl list $ T_DollarDoubleQuoted id
|
delve (T_DollarDoubleQuoted id list) = dl list $ T_DollarDoubleQuoted id
|
||||||
delve (T_DollarExpansion id list) = dl list $ T_DollarExpansion id
|
delve (T_DollarExpansion id list) = dl list $ T_DollarExpansion id
|
||||||
|
delve (T_BraceExpansion id list) = dl list $ T_BraceExpansion id
|
||||||
delve (T_Backticked id list) = dl list $ T_Backticked id
|
delve (T_Backticked id list) = dl list $ T_Backticked id
|
||||||
delve (T_DollarArithmetic id c) = d1 c $ T_DollarArithmetic id
|
delve (T_DollarArithmetic id c) = d1 c $ T_DollarArithmetic id
|
||||||
delve (T_DollarBracket id c) = d1 c $ T_DollarBracket id
|
delve (T_DollarBracket id c) = d1 c $ T_DollarBracket id
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE TemplateHaskell, FlexibleContexts #-}
|
{-# LANGUAGE TemplateHaskell, FlexibleContexts #-}
|
||||||
@@ -203,6 +203,7 @@ nodeChecks = [
|
|||||||
,checkFindExecWithSingleArgument
|
,checkFindExecWithSingleArgument
|
||||||
,checkReturn
|
,checkReturn
|
||||||
,checkMaskedReturns
|
,checkMaskedReturns
|
||||||
|
,checkInjectableFindSh
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -376,6 +377,7 @@ dist a b
|
|||||||
lab = length a - length b
|
lab = length a - length b
|
||||||
min3 x y z = if x < y then x else min y z
|
min3 x y z = if x < y then x else min y z
|
||||||
|
|
||||||
|
hasFloatingPoint params = shellType params == Ksh
|
||||||
|
|
||||||
prop_checkEchoWc3 = verify checkEchoWc "n=$(echo $foo | wc -c)"
|
prop_checkEchoWc3 = verify checkEchoWc "n=$(echo $foo | wc -c)"
|
||||||
checkEchoWc _ (T_Pipeline id _ [a, b]) =
|
checkEchoWc _ (T_Pipeline id _ [a, b]) =
|
||||||
@@ -914,13 +916,14 @@ prop_checkShorthandIf = verify checkShorthandIf "[[ ! -z file ]] && scp file ho
|
|||||||
prop_checkShorthandIf2 = verifyNot checkShorthandIf "[[ ! -z file ]] && { scp file host || echo 'Eek'; }"
|
prop_checkShorthandIf2 = verifyNot checkShorthandIf "[[ ! -z file ]] && { scp file host || echo 'Eek'; }"
|
||||||
prop_checkShorthandIf3 = verifyNot checkShorthandIf "foo && bar || echo baz"
|
prop_checkShorthandIf3 = verifyNot checkShorthandIf "foo && bar || echo baz"
|
||||||
prop_checkShorthandIf4 = verifyNot checkShorthandIf "foo && a=b || a=c"
|
prop_checkShorthandIf4 = verifyNot checkShorthandIf "foo && a=b || a=c"
|
||||||
|
prop_checkShorthandIf5 = verifyNot checkShorthandIf "foo && rm || printf b"
|
||||||
checkShorthandIf _ (T_AndIf id _ (T_OrIf _ _ (T_Pipeline _ _ t)))
|
checkShorthandIf _ (T_AndIf id _ (T_OrIf _ _ (T_Pipeline _ _ t)))
|
||||||
| not $ isOk t =
|
| not $ isOk t =
|
||||||
info id 2015 "Note that A && B || C is not if-then-else. C may run when A is true."
|
info id 2015 "Note that A && B || C is not if-then-else. C may run when A is true."
|
||||||
where
|
where
|
||||||
isOk [t] = isAssignment t || fromMaybe False (do
|
isOk [t] = isAssignment t || fromMaybe False (do
|
||||||
name <- getCommandBasename t
|
name <- getCommandBasename t
|
||||||
return $ name `elem` ["echo", "exit", "return"])
|
return $ name `elem` ["echo", "exit", "return", "printf"])
|
||||||
isOk _ = False
|
isOk _ = False
|
||||||
checkShorthandIf _ _ = return ()
|
checkShorthandIf _ _ = return ()
|
||||||
|
|
||||||
@@ -949,7 +952,7 @@ prop_checkUnquotedDollarAt8 = verifyNot checkUnquotedDollarAt "echo \"${args[@]:
|
|||||||
checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (parentMap p) word =
|
checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (parentMap p) word =
|
||||||
forM_ (take 1 $ filter isArrayExpansion parts) $ \x ->
|
forM_ (take 1 $ filter isArrayExpansion parts) $ \x ->
|
||||||
err (getId x) 2068
|
err (getId x) 2068
|
||||||
"Double quote array expansions, otherwise they're like $* and break on spaces."
|
"Double quote array expansions to avoid re-splitting elements."
|
||||||
checkUnquotedDollarAt _ _ = return ()
|
checkUnquotedDollarAt _ _ = return ()
|
||||||
|
|
||||||
prop_checkConcatenatedDollarAt1 = verify checkConcatenatedDollarAt "echo \"foo$@\""
|
prop_checkConcatenatedDollarAt1 = verify checkConcatenatedDollarAt "echo \"foo$@\""
|
||||||
@@ -989,6 +992,7 @@ prop_checkArrayWithoutIndex1 = verifyTree checkArrayWithoutIndex "foo=(a b); ech
|
|||||||
prop_checkArrayWithoutIndex2 = verifyNotTree checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}"
|
prop_checkArrayWithoutIndex2 = verifyNotTree checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}"
|
||||||
prop_checkArrayWithoutIndex3 = verifyTree checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo"
|
prop_checkArrayWithoutIndex3 = verifyTree checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo"
|
||||||
prop_checkArrayWithoutIndex4 = verifyTree checkArrayWithoutIndex "coproc tail -f log; echo $COPROC"
|
prop_checkArrayWithoutIndex4 = verifyTree checkArrayWithoutIndex "coproc tail -f log; echo $COPROC"
|
||||||
|
prop_checkArrayWithoutIndex5 = verifyTree checkArrayWithoutIndex "a[0]=foo; echo $a"
|
||||||
checkArrayWithoutIndex params _ =
|
checkArrayWithoutIndex params _ =
|
||||||
concat $ doVariableFlowAnalysis readF writeF Map.empty (variableFlow params)
|
concat $ doVariableFlowAnalysis readF writeF Map.empty (variableFlow params)
|
||||||
where
|
where
|
||||||
@@ -1004,20 +1008,43 @@ checkArrayWithoutIndex params _ =
|
|||||||
writeF _ t name (DataArray _) = do
|
writeF _ t name (DataArray _) = do
|
||||||
modify (Map.insert name t)
|
modify (Map.insert name t)
|
||||||
return []
|
return []
|
||||||
writeF _ _ name _ = do
|
writeF _ expr name _ = do
|
||||||
modify (Map.delete name)
|
if isIndexed expr
|
||||||
|
then modify (Map.insert name expr)
|
||||||
|
else modify (Map.delete name)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
isIndexed expr =
|
||||||
|
case expr of
|
||||||
|
T_Assignment _ _ _ (Just _) _ -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
prop_checkStderrRedirect = verify checkStderrRedirect "test 2>&1 > cow"
|
prop_checkStderrRedirect = verify checkStderrRedirect "test 2>&1 > cow"
|
||||||
prop_checkStderrRedirect2 = verifyNot checkStderrRedirect "test > cow 2>&1"
|
prop_checkStderrRedirect2 = verifyNot checkStderrRedirect "test > cow 2>&1"
|
||||||
checkStderrRedirect _ (T_Redirecting _ [
|
prop_checkStderrRedirect3 = verifyNot checkStderrRedirect "test 2>&1 > file | grep stderr"
|
||||||
|
prop_checkStderrRedirect4 = verifyNot checkStderrRedirect "errors=$(test 2>&1 > file)"
|
||||||
|
prop_checkStderrRedirect5 = verifyNot checkStderrRedirect "read < <(test 2>&1 > file)"
|
||||||
|
prop_checkStderrRedirect6 = verify checkStderrRedirect "foo | bar 2>&1 > /dev/null"
|
||||||
|
checkStderrRedirect params redir@(T_Redirecting _ [
|
||||||
T_FdRedirect id "2" (T_IoFile _ (T_GREATAND _) (T_NormalWord _ [T_Literal _ "1"])),
|
T_FdRedirect id "2" (T_IoFile _ (T_GREATAND _) (T_NormalWord _ [T_Literal _ "1"])),
|
||||||
T_FdRedirect _ _ (T_IoFile _ op _)
|
T_FdRedirect _ _ (T_IoFile _ op _)
|
||||||
] _) = case op of
|
] _) = case op of
|
||||||
T_Greater _ -> error
|
T_Greater _ -> error
|
||||||
T_DGREAT _ -> error
|
T_DGREAT _ -> error
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
where error = err id 2069 "The order of the 2>&1 and the redirect matters. The 2>&1 has to be last."
|
where
|
||||||
|
usesOutput t =
|
||||||
|
case t of
|
||||||
|
(T_Pipeline _ _ list) -> length list > 1 && not (isParentOf (parentMap params) (last list) redir)
|
||||||
|
(T_ProcSub {}) -> True
|
||||||
|
(T_DollarExpansion {}) -> True
|
||||||
|
(T_Backticked {}) -> True
|
||||||
|
_ -> False
|
||||||
|
isCaptured = any usesOutput $ getPath (parentMap params) redir
|
||||||
|
|
||||||
|
error = unless isCaptured $
|
||||||
|
err id 2069 "The order of the 2>&1 and the redirect matters. The 2>&1 has to be last."
|
||||||
|
|
||||||
checkStderrRedirect _ _ = return ()
|
checkStderrRedirect _ _ = return ()
|
||||||
|
|
||||||
lt x = trace ("FAILURE " ++ show x) x
|
lt x = trace ("FAILURE " ++ show x) x
|
||||||
@@ -1271,8 +1298,18 @@ checkConstantNoary _ _ = return ()
|
|||||||
|
|
||||||
prop_checkBraceExpansionVars1 = verify checkBraceExpansionVars "echo {1..$n}"
|
prop_checkBraceExpansionVars1 = verify checkBraceExpansionVars "echo {1..$n}"
|
||||||
prop_checkBraceExpansionVars2 = verifyNot checkBraceExpansionVars "echo {1,3,$n}"
|
prop_checkBraceExpansionVars2 = verifyNot checkBraceExpansionVars "echo {1,3,$n}"
|
||||||
checkBraceExpansionVars _ (T_BraceExpansion id s) | "..$" `isInfixOf` s =
|
checkBraceExpansionVars _ (T_BraceExpansion id list) = mapM_ check list
|
||||||
warn id 2051 "Bash doesn't support variables in brace range expansions."
|
where
|
||||||
|
check element =
|
||||||
|
when ("..$" `isInfixOf` toString element) $
|
||||||
|
warn id 2051 "Bash doesn't support variables in brace range expansions."
|
||||||
|
literalExt t =
|
||||||
|
case t of
|
||||||
|
T_DollarBraced {} -> return "$"
|
||||||
|
T_DollarExpansion {} -> return "$"
|
||||||
|
T_DollarArithmetic {} -> return "$"
|
||||||
|
otherwise -> return "-"
|
||||||
|
toString t = fromJust $ getLiteralStringExt literalExt t
|
||||||
checkBraceExpansionVars _ _ = return ()
|
checkBraceExpansionVars _ _ = return ()
|
||||||
|
|
||||||
prop_checkForDecimals = verify checkForDecimals "((3.14*c))"
|
prop_checkForDecimals = verify checkForDecimals "((3.14*c))"
|
||||||
@@ -1285,8 +1322,10 @@ checkForDecimals _ _ = return ()
|
|||||||
|
|
||||||
prop_checkDivBeforeMult = verify checkDivBeforeMult "echo $((c/n*100))"
|
prop_checkDivBeforeMult = verify checkDivBeforeMult "echo $((c/n*100))"
|
||||||
prop_checkDivBeforeMult2 = verifyNot checkDivBeforeMult "echo $((c*100/n))"
|
prop_checkDivBeforeMult2 = verifyNot checkDivBeforeMult "echo $((c*100/n))"
|
||||||
checkDivBeforeMult _ (TA_Binary _ "*" (TA_Binary id "/" _ _) _) =
|
prop_checkDivBeforeMult3 = verifyNot checkDivBeforeMult "echo $((c/10*10))"
|
||||||
info id 2017 "Increase precision by replacing a/b*c with a*c/b."
|
checkDivBeforeMult params (TA_Binary _ "*" (TA_Binary id "/" _ x) y)
|
||||||
|
| not (hasFloatingPoint params) && x /= y =
|
||||||
|
info id 2017 "Increase precision by replacing a/b*c with a*c/b."
|
||||||
checkDivBeforeMult _ _ = return ()
|
checkDivBeforeMult _ _ = return ()
|
||||||
|
|
||||||
prop_checkArithmeticDeref = verify checkArithmeticDeref "echo $((3+$foo))"
|
prop_checkArithmeticDeref = verify checkArithmeticDeref "echo $((3+$foo))"
|
||||||
@@ -1337,8 +1376,10 @@ prop_checkComparisonAgainstGlob = verify checkComparisonAgainstGlob "[[ $cow ==
|
|||||||
prop_checkComparisonAgainstGlob2 = verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
|
prop_checkComparisonAgainstGlob2 = verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
|
||||||
prop_checkComparisonAgainstGlob3 = verify checkComparisonAgainstGlob "[ $cow = *foo* ]"
|
prop_checkComparisonAgainstGlob3 = verify checkComparisonAgainstGlob "[ $cow = *foo* ]"
|
||||||
prop_checkComparisonAgainstGlob4 = verifyNot checkComparisonAgainstGlob "[ $cow = foo ]"
|
prop_checkComparisonAgainstGlob4 = verifyNot checkComparisonAgainstGlob "[ $cow = foo ]"
|
||||||
checkComparisonAgainstGlob _ (TC_Binary _ DoubleBracket op _ (T_NormalWord id [T_DollarBraced _ _])) | op == "=" || op == "==" =
|
prop_checkComparisonAgainstGlob5 = verify checkComparisonAgainstGlob "[[ $cow != $bar ]]"
|
||||||
warn id 2053 "Quote the rhs of = in [[ ]] to prevent glob interpretation."
|
checkComparisonAgainstGlob _ (TC_Binary _ DoubleBracket op _ (T_NormalWord id [T_DollarBraced _ _]))
|
||||||
|
| op `elem` ["=", "==", "!="] =
|
||||||
|
warn id 2053 $ "Quote the rhs of " ++ op ++ " in [[ ]] to prevent glob matching."
|
||||||
checkComparisonAgainstGlob _ (TC_Binary _ SingleBracket op _ word)
|
checkComparisonAgainstGlob _ (TC_Binary _ SingleBracket op _ word)
|
||||||
| (op == "=" || op == "==") && isGlob word =
|
| (op == "=" || op == "==") && isGlob word =
|
||||||
err (getId word) 2081 "[ .. ] can't match globs. Use [[ .. ]] or grep."
|
err (getId word) 2081 "[ .. ] can't match globs. Use [[ .. ]] or grep."
|
||||||
@@ -1440,7 +1481,6 @@ isQuoteFreeNode strict tree t =
|
|||||||
T_DollarDoubleQuoted _ _ -> return True
|
T_DollarDoubleQuoted _ _ -> return True
|
||||||
T_CaseExpression {} -> return True
|
T_CaseExpression {} -> return True
|
||||||
T_HereDoc {} -> return True
|
T_HereDoc {} -> return True
|
||||||
T_HereString {} -> return True
|
|
||||||
T_DollarBraced {} -> return True
|
T_DollarBraced {} -> return True
|
||||||
-- When non-strict, pragmatically assume it's desirable to split here
|
-- When non-strict, pragmatically assume it's desirable to split here
|
||||||
T_ForIn {} -> return (not strict)
|
T_ForIn {} -> return (not strict)
|
||||||
@@ -1484,6 +1524,9 @@ getPath tree t = t :
|
|||||||
Nothing -> []
|
Nothing -> []
|
||||||
Just parent -> getPath tree parent
|
Just parent -> getPath tree parent
|
||||||
|
|
||||||
|
isParentOf tree parent child =
|
||||||
|
elem (getId parent) . map getId $ getPath tree child
|
||||||
|
|
||||||
parents params = getPath (parentMap params)
|
parents params = getPath (parentMap params)
|
||||||
|
|
||||||
--- Command specific checks
|
--- Command specific checks
|
||||||
@@ -1532,6 +1575,16 @@ getUnquotedLiteral (T_NormalWord _ list) =
|
|||||||
str _ = Nothing
|
str _ = Nothing
|
||||||
getUnquotedLiteral _ = Nothing
|
getUnquotedLiteral _ = Nothing
|
||||||
|
|
||||||
|
-- Return a list of NormalWords resulting from brace expansion
|
||||||
|
braceExpand (T_NormalWord id list) = take 1000 $ do
|
||||||
|
items <- mapM part list
|
||||||
|
return $ T_NormalWord id items
|
||||||
|
where
|
||||||
|
part (T_BraceExpansion id items) = do
|
||||||
|
item <- items
|
||||||
|
braceExpand item
|
||||||
|
part x = return x
|
||||||
|
|
||||||
isCommand token str = isCommandMatch token (\cmd -> cmd == str || ('/' : str) `isSuffixOf` cmd)
|
isCommand token str = isCommandMatch token (\cmd -> cmd == str || ('/' : str) `isSuffixOf` cmd)
|
||||||
isUnqualifiedCommand token str = isCommandMatch token (== str)
|
isUnqualifiedCommand token str = isCommandMatch token (== str)
|
||||||
|
|
||||||
@@ -1827,7 +1880,7 @@ prop_checkIndirectExpansion4 = verify checkIndirectExpansion "${var${n}_$((i%2))
|
|||||||
prop_checkIndirectExpansion5 = verifyNot checkIndirectExpansion "${bar}"
|
prop_checkIndirectExpansion5 = verifyNot checkIndirectExpansion "${bar}"
|
||||||
checkIndirectExpansion _ (T_DollarBraced i (T_NormalWord _ contents)) =
|
checkIndirectExpansion _ (T_DollarBraced i (T_NormalWord _ contents)) =
|
||||||
when (isIndirection contents) $
|
when (isIndirection contents) $
|
||||||
err i 2082 "To expand via indirection, use name=\"foo$n\"; echo \"${!name}\"."
|
err i 2082 "To expand via indirection, use arrays, ${!name} or (for sh only) eval."
|
||||||
where
|
where
|
||||||
isIndirection vars =
|
isIndirection vars =
|
||||||
let list = mapMaybe isIndirectionPart vars in
|
let list = mapMaybe isIndirectionPart vars in
|
||||||
@@ -1888,7 +1941,7 @@ prop_checkTildeInQuotes5 = verifyNot checkTildeInQuotes "echo '/~foo/cow'"
|
|||||||
prop_checkTildeInQuotes6 = verifyNot checkTildeInQuotes "awk '$0 ~ /foo/'"
|
prop_checkTildeInQuotes6 = verifyNot checkTildeInQuotes "awk '$0 ~ /foo/'"
|
||||||
checkTildeInQuotes _ = check
|
checkTildeInQuotes _ = check
|
||||||
where
|
where
|
||||||
verify id ('~':'/':_) = warn id 2088 "Note that ~ does not expand in quotes."
|
verify id ('~':'/':_) = warn id 2088 "Tilde does not expand in quotes. Use $HOME."
|
||||||
verify _ _ = return ()
|
verify _ _ = return ()
|
||||||
check (T_NormalWord _ (T_SingleQuoted id str:_)) =
|
check (T_NormalWord _ (T_SingleQuoted id str:_)) =
|
||||||
verify id str
|
verify id str
|
||||||
@@ -2174,7 +2227,7 @@ getModifiedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Literal
|
|||||||
"export" ->
|
"export" ->
|
||||||
if "f" `elem` flags then [] else concatMap getModifierParamString rest
|
if "f" `elem` flags then [] else concatMap getModifierParamString rest
|
||||||
|
|
||||||
"declare" -> declaredVars
|
"declare" -> if any (`elem` flags) ["F", "f", "p"] then [] else declaredVars
|
||||||
"typeset" -> declaredVars
|
"typeset" -> declaredVars
|
||||||
|
|
||||||
"local" -> concatMap getModifierParamString rest
|
"local" -> concatMap getModifierParamString rest
|
||||||
@@ -2185,6 +2238,9 @@ getModifiedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Literal
|
|||||||
|
|
||||||
"printf" -> maybeToList $ getPrintfVariable rest
|
"printf" -> maybeToList $ getPrintfVariable rest
|
||||||
|
|
||||||
|
"mapfile" -> maybeToList $ getMapfileArray base rest
|
||||||
|
"readarray" -> maybeToList $ getMapfileArray base rest
|
||||||
|
|
||||||
_ -> []
|
_ -> []
|
||||||
where
|
where
|
||||||
flags = map snd $ getAllFlags base
|
flags = map snd $ getAllFlags base
|
||||||
@@ -2236,6 +2292,15 @@ getModifiedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Literal
|
|||||||
f (_:rest) = f rest
|
f (_:rest) = f rest
|
||||||
f [] = fail "not found"
|
f [] = fail "not found"
|
||||||
|
|
||||||
|
-- mapfile has some curious syntax allowing flags plus 0..n variable names
|
||||||
|
-- where only the first non-option one is used if any. Here we cheat and
|
||||||
|
-- just get the last one, if it's a variable name.
|
||||||
|
getMapfileArray base arguments = do
|
||||||
|
lastArg <- listToMaybe (reverse arguments)
|
||||||
|
name <- getLiteralString lastArg
|
||||||
|
guard $ isVariableName name
|
||||||
|
return (base, lastArg, name, DataArray $ SourceExternal)
|
||||||
|
|
||||||
getModifiedVariableCommand _ = []
|
getModifiedVariableCommand _ = []
|
||||||
|
|
||||||
prop_getBracedReference1 = getBracedReference "foo" == "foo"
|
prop_getBracedReference1 = getBracedReference "foo" == "foo"
|
||||||
@@ -2399,8 +2464,9 @@ prop_checkSpacefulness20= verifyNotTree checkSpacefulness "n+='foo bar'"
|
|||||||
prop_checkSpacefulness21= verifyNotTree checkSpacefulness "select foo in $bar; do true; done"
|
prop_checkSpacefulness21= verifyNotTree checkSpacefulness "select foo in $bar; do true; done"
|
||||||
prop_checkSpacefulness22= verifyNotTree checkSpacefulness "echo $\"$1\""
|
prop_checkSpacefulness22= verifyNotTree checkSpacefulness "echo $\"$1\""
|
||||||
prop_checkSpacefulness23= verifyNotTree checkSpacefulness "a=(1); echo ${a[@]}"
|
prop_checkSpacefulness23= verifyNotTree checkSpacefulness "a=(1); echo ${a[@]}"
|
||||||
prop_checkSpacefulness24= verifyNotTree checkSpacefulness "a='a b'; cat <<< $a"
|
prop_checkSpacefulness24= verifyTree checkSpacefulness "a='a b'; cat <<< $a"
|
||||||
prop_checkSpacefulness25= verifyTree checkSpacefulness "a='s/[0-9]//g'; sed $a"
|
prop_checkSpacefulness25= verifyTree checkSpacefulness "a='s/[0-9]//g'; sed $a"
|
||||||
|
prop_checkSpacefulness26= verifyTree checkSpacefulness "a='foo bar'; echo {1,2,$a}"
|
||||||
|
|
||||||
checkSpacefulness params t =
|
checkSpacefulness params t =
|
||||||
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params)
|
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params)
|
||||||
@@ -2589,6 +2655,9 @@ prop_checkUnused20= verifyNotTree checkUnusedAssignments "a=1; PS1='$a'"
|
|||||||
prop_checkUnused21= verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' INT"
|
prop_checkUnused21= verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' INT"
|
||||||
prop_checkUnused22= verifyNotTree checkUnusedAssignments "a=1; [ -v a ]"
|
prop_checkUnused22= verifyNotTree checkUnusedAssignments "a=1; [ -v a ]"
|
||||||
prop_checkUnused23= verifyNotTree checkUnusedAssignments "a=1; [ -R a ]"
|
prop_checkUnused23= verifyNotTree checkUnusedAssignments "a=1; [ -R a ]"
|
||||||
|
prop_checkUnused24= verifyNotTree checkUnusedAssignments "mapfile -C a b; echo ${b[@]}"
|
||||||
|
prop_checkUnused25= verifyNotTree checkUnusedAssignments "readarray foo; echo ${foo[@]}"
|
||||||
|
prop_checkUnused26= verifyNotTree checkUnusedAssignments "declare -F foo"
|
||||||
checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
|
checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
|
||||||
where
|
where
|
||||||
flow = variableFlow params
|
flow = variableFlow params
|
||||||
@@ -2900,11 +2969,15 @@ prop_checkCatastrophicRm5 = verifyNot checkCatastrophicRm "rm -r /home/${USER:-t
|
|||||||
prop_checkCatastrophicRm6 = verify checkCatastrophicRm "rm --recursive /etc/*$config*"
|
prop_checkCatastrophicRm6 = verify checkCatastrophicRm "rm --recursive /etc/*$config*"
|
||||||
prop_checkCatastrophicRm8 = verify checkCatastrophicRm "rm -rf /home"
|
prop_checkCatastrophicRm8 = verify checkCatastrophicRm "rm -rf /home"
|
||||||
prop_checkCatastrophicRm9 = verifyNot checkCatastrophicRm "rm -rf -- /home"
|
prop_checkCatastrophicRm9 = verifyNot checkCatastrophicRm "rm -rf -- /home"
|
||||||
|
prop_checkCatastrophicRm10= verifyNot checkCatastrophicRm "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}"
|
||||||
|
prop_checkCatastrophicRm11= verify checkCatastrophicRm "rm -r /{bin,sbin}/$exec"
|
||||||
|
prop_checkCatastrophicRm12= verify checkCatastrophicRm "rm -r /{{usr,},{bin,sbin}}/$exec"
|
||||||
|
prop_checkCatastrophicRm13= verifyNot checkCatastrophicRm "rm -r /{{a,b},{c,d}}/$exec"
|
||||||
prop_checkCatastrophicRmA = verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg"
|
prop_checkCatastrophicRmA = verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg"
|
||||||
prop_checkCatastrophicRmB = verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*"
|
prop_checkCatastrophicRmB = verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*"
|
||||||
checkCatastrophicRm params t@(T_SimpleCommand id _ tokens) | t `isCommand` "rm" =
|
checkCatastrophicRm params t@(T_SimpleCommand id _ tokens) | t `isCommand` "rm" =
|
||||||
when (any isRecursiveFlag simpleArgs) $
|
when (any isRecursiveFlag simpleArgs) $
|
||||||
mapM_ checkWord tokens
|
mapM_ (mapM_ checkWord . braceExpand) tokens
|
||||||
where
|
where
|
||||||
simpleArgs = deadSimple t
|
simpleArgs = deadSimple t
|
||||||
|
|
||||||
@@ -2993,6 +3066,7 @@ prop_checkUnpassedInFunctions7 = verifyTree checkUnpassedInFunctions "foo() { ec
|
|||||||
prop_checkUnpassedInFunctions8 = verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
|
prop_checkUnpassedInFunctions8 = verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
|
||||||
prop_checkUnpassedInFunctions9 = verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
|
prop_checkUnpassedInFunctions9 = verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
|
||||||
prop_checkUnpassedInFunctions10= verifyNotTree checkUnpassedInFunctions "foo() { echo $!; }; foo;"
|
prop_checkUnpassedInFunctions10= verifyNotTree checkUnpassedInFunctions "foo() { echo $!; }; foo;"
|
||||||
|
prop_checkUnpassedInFunctions11= verifyNotTree checkUnpassedInFunctions "foo() { bar() { echo $1; }; bar baz; }; foo;"
|
||||||
checkUnpassedInFunctions params root =
|
checkUnpassedInFunctions params root =
|
||||||
execWriter $ mapM_ warnForGroup referenceGroups
|
execWriter $ mapM_ warnForGroup referenceGroups
|
||||||
where
|
where
|
||||||
@@ -3004,7 +3078,7 @@ checkUnpassedInFunctions params root =
|
|||||||
findFunction t@(T_Function id _ _ name body) =
|
findFunction t@(T_Function id _ _ name body) =
|
||||||
let flow = getVariableFlow (shellType params) (parentMap params) body
|
let flow = getVariableFlow (shellType params) (parentMap params) body
|
||||||
in
|
in
|
||||||
if any isPositionalReference flow && not (any isPositionalAssignment flow)
|
if any (isPositionalReference t) flow && not (any isPositionalAssignment flow)
|
||||||
then return t
|
then return t
|
||||||
else Nothing
|
else Nothing
|
||||||
findFunction _ = Nothing
|
findFunction _ = Nothing
|
||||||
@@ -3013,11 +3087,15 @@ checkUnpassedInFunctions params root =
|
|||||||
case x of
|
case x of
|
||||||
Assignment (_, _, str, _) -> isPositional str
|
Assignment (_, _, str, _) -> isPositional str
|
||||||
_ -> False
|
_ -> False
|
||||||
isPositionalReference x =
|
isPositionalReference function x =
|
||||||
case x of
|
case x of
|
||||||
Reference (_, _, str) -> isPositional str
|
Reference (_, t, str) -> isPositional str && t `isDirectChildOf` function
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
|
isDirectChildOf child parent = fromMaybe False $ do
|
||||||
|
function <- find (\x -> case x of T_Function {} -> True; _ -> False) $ getPath (parentMap params) child
|
||||||
|
return $ getId parent == getId function
|
||||||
|
|
||||||
referenceList :: [(String, Bool, Token)]
|
referenceList :: [(String, Bool, Token)]
|
||||||
referenceList = execWriter $
|
referenceList = execWriter $
|
||||||
doAnalysis (fromMaybe (return ()) . checkCommand) root
|
doAnalysis (fromMaybe (return ()) . checkCommand) root
|
||||||
@@ -3311,6 +3389,30 @@ checkMaskedReturns _ t@(T_SimpleCommand id _ (cmd:rest)) = potentially $ do
|
|||||||
_ -> False
|
_ -> False
|
||||||
checkMaskedReturns _ _ = return ()
|
checkMaskedReturns _ _ = return ()
|
||||||
|
|
||||||
|
prop_checkInjectableFindSh1 = verify checkInjectableFindSh "find . -exec sh -c 'echo {}' \\;"
|
||||||
|
prop_checkInjectableFindSh2 = verify checkInjectableFindSh "find . -execdir bash -c 'rm \"{}\"' ';'"
|
||||||
|
prop_checkInjectableFindSh3 = verifyNot checkInjectableFindSh "find . -exec sh -c 'rm \"$@\"' _ {} \\;"
|
||||||
|
checkInjectableFindSh _ = checkCommand "find" (const check)
|
||||||
|
where
|
||||||
|
check args = do
|
||||||
|
let idStrings = map (\x -> (getId x, onlyLiteralString x)) args
|
||||||
|
match pattern idStrings
|
||||||
|
|
||||||
|
match _ [] = return ()
|
||||||
|
match [] (next:_) = action next
|
||||||
|
match (p:tests) ((id, arg):args) = do
|
||||||
|
when (p arg) $ match tests args
|
||||||
|
match (p:tests) args
|
||||||
|
|
||||||
|
pattern = [
|
||||||
|
(`elem` ["-exec", "-execdir"]),
|
||||||
|
(`elem` ["sh", "bash", "ksh"]),
|
||||||
|
(== "-c")
|
||||||
|
]
|
||||||
|
action (id, arg) =
|
||||||
|
when ("{}" `isInfixOf` arg) $
|
||||||
|
warn id 2156 "Injecting filenames is fragile and insecure. Use parameters."
|
||||||
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
|
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
yOU should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE NoMonomorphismRestriction, TemplateHaskell, FlexibleContexts #-}
|
{-# LANGUAGE NoMonomorphismRestriction, TemplateHaskell, FlexibleContexts #-}
|
||||||
@@ -237,6 +237,12 @@ attempting rest branch =
|
|||||||
orFail parser errorAction =
|
orFail parser errorAction =
|
||||||
try parser <|> (errorAction >>= fail)
|
try parser <|> (errorAction >>= fail)
|
||||||
|
|
||||||
|
-- Construct a node with a parser, e.g. T_Literal `withParser` (readGenericLiteral ",")
|
||||||
|
withParser node parser = do
|
||||||
|
id <- getNextId
|
||||||
|
contents <- parser
|
||||||
|
return $ node id contents
|
||||||
|
|
||||||
wasIncluded p = option False (p >> return True)
|
wasIncluded p = option False (p >> return True)
|
||||||
|
|
||||||
acceptButWarn parser level code note =
|
acceptButWarn parser level code note =
|
||||||
@@ -765,14 +771,14 @@ readDollarBracedLiteral = do
|
|||||||
|
|
||||||
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
||||||
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
||||||
|
prop_readProcSub3 = isOk readProcSub "<( # nothing here \n)"
|
||||||
readProcSub = called "process substitution" $ do
|
readProcSub = called "process substitution" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
dir <- try $ do
|
dir <- try $ do
|
||||||
x <- oneOf "<>"
|
x <- oneOf "<>"
|
||||||
char '('
|
char '('
|
||||||
return [x]
|
return [x]
|
||||||
allspacing
|
list <- readCompoundListOrEmpty
|
||||||
list <- readCompoundList
|
|
||||||
allspacing
|
allspacing
|
||||||
char ')'
|
char ')'
|
||||||
return $ T_ProcSub id dir list
|
return $ T_ProcSub id dir list
|
||||||
@@ -837,19 +843,23 @@ readBackTicked = called "backtick expansion" $ do
|
|||||||
suggestForgotClosingQuote startPos endPos "backtick expansion"
|
suggestForgotClosingQuote startPos endPos "backtick expansion"
|
||||||
|
|
||||||
-- Result positions may be off due to escapes
|
-- Result positions may be off due to escapes
|
||||||
result <- subParse subStart readTermOrNone (unEscape subString)
|
result <- subParse subStart subParser (unEscape subString)
|
||||||
return $ T_Backticked id result
|
return $ T_Backticked id result
|
||||||
where
|
where
|
||||||
unEscape [] = []
|
unEscape [] = []
|
||||||
unEscape ('\\':x:rest) | x `elem` "$`\\" = x : unEscape rest
|
unEscape ('\\':x:rest) | x `elem` "$`\\" = x : unEscape rest
|
||||||
unEscape ('\\':'\n':rest) = unEscape rest
|
unEscape ('\\':'\n':rest) = unEscape rest
|
||||||
unEscape (c:rest) = c : unEscape rest
|
unEscape (c:rest) = c : unEscape rest
|
||||||
|
subParser = do
|
||||||
|
cmds <- readCompoundListOrEmpty
|
||||||
|
verifyEof
|
||||||
|
return cmds
|
||||||
backtick =
|
backtick =
|
||||||
disregard (char '`') <|> do
|
disregard (char '`') <|> do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
char '´'
|
char '´'
|
||||||
parseProblemAt pos ErrorC 1077
|
parseProblemAt pos ErrorC 1077
|
||||||
"For command expansion, the tick should slant left (` vs ´)."
|
"For command expansion, the tick should slant left (` vs ´). Use $(..) instead."
|
||||||
|
|
||||||
subParse pos parser input = do
|
subParse pos parser input = do
|
||||||
lastPosition <- getPosition
|
lastPosition <- getPosition
|
||||||
@@ -1045,16 +1055,29 @@ readGenericEscaped = do
|
|||||||
|
|
||||||
prop_readBraced = isOk readBraced "{1..4}"
|
prop_readBraced = isOk readBraced "{1..4}"
|
||||||
prop_readBraced2 = isOk readBraced "{foo,bar,\"baz lol\"}"
|
prop_readBraced2 = isOk readBraced "{foo,bar,\"baz lol\"}"
|
||||||
readBraced = try $ do
|
prop_readBraced3 = isOk readBraced "{1,\\},2}"
|
||||||
let strip (T_Literal _ s) = return ("\"" ++ s ++ "\"")
|
prop_readBraced4 = isOk readBraced "{1,{2,3}}"
|
||||||
id <- getNextId
|
prop_readBraced5 = isOk readBraced "{JP{,E}G,jp{,e}g}"
|
||||||
char '{'
|
prop_readBraced6 = isOk readBraced "{foo,bar,$((${var}))}"
|
||||||
str <- many1 ((readDoubleQuotedLiteral >>= strip) <|> readGenericLiteral1 (oneOf "}\"" <|> whitespace))
|
readBraced = try braceExpansion
|
||||||
char '}'
|
where
|
||||||
let result = concat str
|
braceExpansion =
|
||||||
unless (',' `elem` result || ".." `isInfixOf` result) $
|
T_BraceExpansion `withParser` do
|
||||||
fail "Not a brace expression"
|
char '{'
|
||||||
return $ T_BraceExpansion id result
|
elements <- bracedElement `sepBy1` char ','
|
||||||
|
char '}'
|
||||||
|
return elements
|
||||||
|
bracedElement =
|
||||||
|
T_NormalWord `withParser` do
|
||||||
|
many $ choice [
|
||||||
|
braceExpansion,
|
||||||
|
readDollarExpression,
|
||||||
|
readSingleQuoted,
|
||||||
|
readDoubleQuoted,
|
||||||
|
braceLiteral
|
||||||
|
]
|
||||||
|
braceLiteral =
|
||||||
|
T_Literal `withParser` readGenericLiteral1 (oneOf "{}\"$'," <|> whitespace)
|
||||||
|
|
||||||
readNormalDollar = readDollarExpression <|> readDollarDoubleQuote <|> readDollarSingleQuote <|> readDollarLonely
|
readNormalDollar = readDollarExpression <|> readDollarDoubleQuote <|> readDollarSingleQuote <|> readDollarLonely
|
||||||
readDoubleQuotedDollar = readDollarExpression <|> readDollarLonely
|
readDoubleQuotedDollar = readDollarExpression <|> readDollarLonely
|
||||||
@@ -1078,7 +1101,6 @@ readDollarDoubleQuote = do
|
|||||||
doubleQuote <?> "end of translated double quoted string"
|
doubleQuote <?> "end of translated double quoted string"
|
||||||
return $ T_DollarDoubleQuoted id x
|
return $ T_DollarDoubleQuoted id x
|
||||||
|
|
||||||
|
|
||||||
prop_readDollarArithmetic = isOk readDollarArithmetic "$(( 3 * 4 +5))"
|
prop_readDollarArithmetic = isOk readDollarArithmetic "$(( 3 * 4 +5))"
|
||||||
prop_readDollarArithmetic2 = isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
|
prop_readDollarArithmetic2 = isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
|
||||||
readDollarArithmetic = called "$((..)) expression" $ do
|
readDollarArithmetic = called "$((..)) expression" $ do
|
||||||
@@ -1114,11 +1136,13 @@ readDollarBraced = called "parameter expansion" $ do
|
|||||||
char '}'
|
char '}'
|
||||||
return $ T_DollarBraced id word
|
return $ T_DollarBraced id word
|
||||||
|
|
||||||
prop_readDollarExpansion = isOk readDollarExpansion "$(echo foo; ls\n)"
|
prop_readDollarExpansion1= isOk readDollarExpansion "$(echo foo; ls\n)"
|
||||||
|
prop_readDollarExpansion2= isOk readDollarExpansion "$( )"
|
||||||
|
prop_readDollarExpansion3= isOk readDollarExpansion "$( command \n#comment \n)"
|
||||||
readDollarExpansion = called "command expansion" $ do
|
readDollarExpansion = called "command expansion" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "$(")
|
try (string "$(")
|
||||||
cmds <- readCompoundList
|
cmds <- readCompoundListOrEmpty
|
||||||
char ')' <?> "end of $(..) expression"
|
char ')' <?> "end of $(..) expression"
|
||||||
return $ T_DollarExpansion id cmds
|
return $ T_DollarExpansion id cmds
|
||||||
|
|
||||||
@@ -1827,6 +1851,9 @@ readCompoundCommand = do
|
|||||||
|
|
||||||
|
|
||||||
readCompoundList = readTerm
|
readCompoundList = readTerm
|
||||||
|
readCompoundListOrEmpty = do
|
||||||
|
allspacing
|
||||||
|
readTerm <|> return []
|
||||||
|
|
||||||
readCmdPrefix = many1 (readIoRedirect <|> readAssignmentWord)
|
readCmdPrefix = many1 (readIoRedirect <|> readAssignmentWord)
|
||||||
readCmdSuffix = many1 (readIoRedirect <|> readCmdWord)
|
readCmdSuffix = many1 (readIoRedirect <|> readCmdWord)
|
||||||
@@ -2030,10 +2057,25 @@ readShebang = do
|
|||||||
parseProblemAt pos ErrorC 1084
|
parseProblemAt pos ErrorC 1084
|
||||||
"Use #!, not !#, for the shebang."
|
"Use #!, not !#, for the shebang."
|
||||||
|
|
||||||
|
verifyEof = eof <|> choice [
|
||||||
|
ifParsable g_Lparen $
|
||||||
|
parseProblem ErrorC 1088 "Parsing stopped here. Invalid use of parentheses?",
|
||||||
|
|
||||||
|
ifParsable readKeyword $
|
||||||
|
parseProblem ErrorC 1089 "Parsing stopped here. Is this keyword correctly matched up?",
|
||||||
|
|
||||||
|
parseProblem ErrorC 1070 "Parsing stopped here. Mismatched keywords or invalid parentheses?"
|
||||||
|
]
|
||||||
|
where
|
||||||
|
ifParsable p action = do
|
||||||
|
try (lookAhead p)
|
||||||
|
action
|
||||||
|
|
||||||
prop_readScript1 = isOk readScript "#!/bin/bash\necho hello world\n"
|
prop_readScript1 = isOk readScript "#!/bin/bash\necho hello world\n"
|
||||||
prop_readScript2 = isWarning readScript "#!/bin/bash\r\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_readScript3 = isWarning readScript "#!/bin/bash\necho hello\xA0world"
|
||||||
prop_readScript4 = isWarning readScript "#!/usr/bin/perl\nfoo=("
|
prop_readScript4 = isWarning readScript "#!/usr/bin/perl\nfoo=("
|
||||||
|
prop_readScript5 = isOk readScript "#!/bin/bash\n#This is an empty script\n\n"
|
||||||
readScript = do
|
readScript = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
@@ -2044,19 +2086,13 @@ readScript = do
|
|||||||
sb <- option "" readShebang
|
sb <- option "" readShebang
|
||||||
verifyShell pos (getShell sb)
|
verifyShell pos (getShell sb)
|
||||||
if isValidShell (getShell sb) /= Just False
|
if isValidShell (getShell sb) /= Just False
|
||||||
then
|
then do
|
||||||
do {
|
commands <- readCompoundListOrEmpty
|
||||||
allspacing;
|
verifyEof
|
||||||
commands <- readTerm;
|
return $ T_Script id sb commands
|
||||||
eof <|> parseProblem ErrorC 1070 "Parsing stopped here because of parsing errors.";
|
else do
|
||||||
return $ T_Script id sb commands;
|
many anyChar
|
||||||
} <|> do {
|
|
||||||
parseProblem WarningC 1014 "Couldn't read any commands.";
|
|
||||||
return $ T_Script id sb []
|
return $ T_Script id sb []
|
||||||
}
|
|
||||||
else do
|
|
||||||
many anyChar
|
|
||||||
return $ T_Script id sb [];
|
|
||||||
|
|
||||||
where
|
where
|
||||||
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
{-# LANGUAGE TemplateHaskell #-}
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
@@ -3,16 +3,16 @@
|
|||||||
http://www.vidarholen.net/contents/shellcheck
|
http://www.vidarholen.net/contents/shellcheck
|
||||||
|
|
||||||
ShellCheck is free software: you can redistribute it and/or modify
|
ShellCheck is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
ShellCheck is distributed in the hope that it will be useful,
|
ShellCheck is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Affero General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
-}
|
-}
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
@@ -21,6 +21,7 @@ import Control.Monad.Trans
|
|||||||
import Control.Monad.Trans.Error
|
import Control.Monad.Trans.Error
|
||||||
import Control.Monad.Trans.List
|
import Control.Monad.Trans.List
|
||||||
import Data.Char
|
import Data.Char
|
||||||
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Monoid
|
import Data.Monoid
|
||||||
import GHC.Exts
|
import GHC.Exts
|
||||||
@@ -34,6 +35,7 @@ import System.Console.GetOpt
|
|||||||
import System.Directory
|
import System.Directory
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.Exit
|
import System.Exit
|
||||||
|
import System.Info
|
||||||
import System.IO
|
import System.IO
|
||||||
import Text.JSON
|
import Text.JSON
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
@@ -154,7 +156,8 @@ forTty options files = do
|
|||||||
|
|
||||||
getColorFunc = do
|
getColorFunc = do
|
||||||
term <- hIsTerminalDevice stdout
|
term <- hIsTerminalDevice stdout
|
||||||
return $ if term then colorComment else const id
|
let windows = "mingw" `isPrefixOf` os
|
||||||
|
return $ if term && not windows then colorComment else const id
|
||||||
|
|
||||||
forJson :: AnalysisOptions -> [FilePath] -> IO Status
|
forJson :: AnalysisOptions -> [FilePath] -> IO Status
|
||||||
forJson options files = catchExceptions $ do
|
forJson options files = catchExceptions $ do
|
||||||
@@ -350,5 +353,5 @@ verifyFiles files =
|
|||||||
printVersion = do
|
printVersion = do
|
||||||
putStrLn "ShellCheck - shell script analysis tool"
|
putStrLn "ShellCheck - shell script analysis tool"
|
||||||
putStrLn $ "version: " ++ shellcheckVersion
|
putStrLn $ "version: " ++ shellcheckVersion
|
||||||
putStrLn "license: GNU Affero General Public License, version 3"
|
putStrLn "license: GNU General Public License, version 3"
|
||||||
putStrLn "website: http://www.shellcheck.net"
|
putStrLn "website: http://www.shellcheck.net"
|
||||||
|
Reference in New Issue
Block a user