summaryrefslogtreecommitdiff
path: root/support/checkbashisms
diff options
context:
space:
mode:
Diffstat (limited to 'support/checkbashisms')
-rwxr-xr-xsupport/checkbashisms170
1 files changed, 170 insertions, 0 deletions
diff --git a/support/checkbashisms b/support/checkbashisms
new file mode 100755
index 0000000..ea6cc14
--- /dev/null
+++ b/support/checkbashisms
@@ -0,0 +1,170 @@
+#! /usr/bin/perl -w
+
+# This script is essentially copied from /usr/share/lintian/checks/scripts,
+# which is:
+# Copyright (C) 1998 Richard Braakman
+# Copyright (C) 2002 Josip Rodin
+# This version is
+# Copyright (C) 2003 Julian Gilbey
+#
+# This program 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.
+#
+# This program 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 <http://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+(my $progname = $0) =~ s|.*/||;
+
+my $usage = <<"EOF";
+Usage: $progname [-n] script ...
+ or: $progname --help
+ or: $progname --version
+This script performs basic checks for the presence of bashisms
+in /bin/sh scripts.
+EOF
+
+my $version = <<"EOF";
+This is $progname, from the Debian devscripts package, version 2.10.7ubuntu5
+This code is copyright 2003 by Julian Gilbey <jdg\@debian.org>,
+based on original code which is copyright 1998 by Richard Braakman
+and copyright 2002 by Josip Rodin.
+This program comes with ABSOLUTELY NO WARRANTY.
+You are free to redistribute this code under the terms of the
+GNU General Public License, version 3, or (at your option) any later version.
+EOF
+
+my $opt_echo = 0;
+
+##
+## handle command-line options
+##
+if (int(@ARGV) == 0 or $ARGV[0] =~ /^(--help|-h)$/) { print $usage; exit 0; }
+if (@ARGV and $ARGV[0] =~ /^(--version|-v)$/) { print $version; exit 0; }
+if (@ARGV and $ARGV[0] =~ /^(--newline|-n)$/) { $opt_echo = 1; }
+
+
+my $status = 0;
+
+foreach my $filename (@ARGV) {
+ if ($filename eq '-n' or $filename eq '--newline') {
+ next;
+ }
+ unless (open C, "$filename") {
+ warn "cannot open script $filename for reading: $!\n";
+ $status |= 2;
+ next;
+ }
+
+ my $cat_string = "";
+
+ while (<C>) {
+ if ($. == 1) { # This should be an interpreter line
+ if (m,^\#!\s*(\S+),) {
+ my $interpreter = $1;
+ if ($interpreter =~ m,/bash$,) {
+ warn "script $filename is already a bash script; skipping\n";
+ $status |= 2;
+ last; # end this file
+ }
+ elsif ($interpreter !~ m,/(sh|ash|dash)$,) {
+ warn "script $filename does not appear to be a /bin/sh script; skipping\n";
+ $status |= 2;
+ last;
+ }
+ } else {
+ warn "script $filename does not appear to have a \#! interpreter line;\nyou may get strange results\n";
+ }
+ }
+
+ next if m,^\s*\#,; # skip comment lines
+ chomp;
+ my $orig_line = $_;
+
+ s/(?<!\\)\#.*$//; # eat comments
+
+ if (m/(?:^|\s+)cat\s*\<\<\s*(\w+)/) {
+ $cat_string = $1;
+ }
+ elsif ($cat_string ne "" and m/^$cat_string/) {
+ $cat_string = "";
+ }
+ my $within_another_shell = 0;
+ if (m,(^|\s+)((/usr)?/bin/)?((b|d)?a|k|z|t?c)sh\s+-c\s*.+,) {
+ $within_another_shell = 1;
+ }
+ # if cat_string is set, we are in a HERE document and need not
+ # check for things
+ if ($cat_string eq "" and !$within_another_shell) {
+ my $found = 0;
+ my $match = '';
+ my $explanation = '';
+ my %bashisms = (
+ '(?:^|\s+)function\s+\w+' => q<'function' is useless>,
+ '(?:^|\s+)select\s+\w+' => q<'select' is not POSIX>,
+ '(?:^|\s+)source\s+(?:\.\/|\/|\$)[^\s]+' =>
+ q<should be '.', not 'source'>,
+ '(\[|test|-o|-a)\s*[^\s]+\s+==\s' =>
+ q<should be 'b = a'>,
+ '\s\|\&' => q<pipelining is not POSIX>,
+ '\$\[\w+\]' => q<arithmetic not allowed>,
+ '\$\{\w+\:\d+(?::\d+)?\}' => q<${foo:3[:1]}>,
+ '\$\{!\w+[@*]\}' => q<${!prefix[*|@]>,
+ '\$\{!\w+\}' => q<${!name}>,
+ '\$\{\w+(/.+?){1,2}\}' => q<${parm/?/pat[/str]}>,
+ '[^\\\]\{([^\s]+?,)+[^\\\}\s]+\}' =>
+ q<brace expansion>,
+ '(?:^|\s+)\w+\[\d+\]=' => q<bash arrays, H[0]>,
+ '\$\{\#?\w+\[[0-9\*\@]+\]\}' => q<bash arrays, ${name[0|*|@]}>,
+ '(?:^|\s+)(read\s*(?:;|$))' => q<read without variable>,
+ '\$\(\([A-Za-z]' => q<cnt=$((cnt + 1)) does not work in dash>,
+ 'echo\s+-[e]' => q<echo -e>,
+ 'exec\s+-[acl]' => q<exec -c/-l/-a name>,
+ '\blet\s' => q<let ...>,
+ '\$RANDOM\b' => q<$RANDOM>,
+ '(?<!\$)\(\(' => q<'((' should be '$(('>,
+ );
+
+ if ($opt_echo) {
+ $bashisms{'echo\s+-[n]'} = 'q<echo -n>';
+ }
+
+ while (my ($re,$expl) = each %bashisms) {
+ if (m/($re)/) {
+ $found = 1;
+ $match = $1;
+ $explanation = $expl;
+ last;
+ }
+ }
+ # since this test is ugly, I have to do it by itself
+ # detect source (.) trying to pass args to the command it runs
+ if (not $found and m/^\s*(\.\s+[^\s]+\s+([^\s]+))/) {
+ if ($2 eq '&&' || $2 eq '||') {
+ # everything is ok
+ ;
+ } else {
+ $found = 1;
+ $match = $1;
+ }
+ }
+ unless ($found == 0) {
+ warn "possible bashism in $filename line $. ($explanation):\n$orig_line\n";
+ $status |= 1;
+ }
+ }
+ }
+
+ close C;
+}
+
+exit $status;