1*49cdfc7eSAndroid Build Coastguard Worker#!/usr/bin/perl 2*49cdfc7eSAndroid Build Coastguard Worker 3*49cdfc7eSAndroid Build Coastguard Worker# This script is essentially copied from /usr/share/lintian/checks/scripts, 4*49cdfc7eSAndroid Build Coastguard Worker# which is: 5*49cdfc7eSAndroid Build Coastguard Worker# Copyright (C) 1998 Richard Braakman 6*49cdfc7eSAndroid Build Coastguard Worker# Copyright (C) 2002 Josip Rodin 7*49cdfc7eSAndroid Build Coastguard Worker# This version is 8*49cdfc7eSAndroid Build Coastguard Worker# Copyright (C) 2003 Julian Gilbey 9*49cdfc7eSAndroid Build Coastguard Worker# 10*49cdfc7eSAndroid Build Coastguard Worker# This program is free software; you can redistribute it and/or modify 11*49cdfc7eSAndroid Build Coastguard Worker# it under the terms of the GNU General Public License as published by 12*49cdfc7eSAndroid Build Coastguard Worker# the Free Software Foundation; either version 2 of the License, or 13*49cdfc7eSAndroid Build Coastguard Worker# (at your option) any later version. 14*49cdfc7eSAndroid Build Coastguard Worker# 15*49cdfc7eSAndroid Build Coastguard Worker# This program is distributed in the hope that it will be useful, 16*49cdfc7eSAndroid Build Coastguard Worker# but WITHOUT ANY WARRANTY; without even the implied warranty of 17*49cdfc7eSAndroid Build Coastguard Worker# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18*49cdfc7eSAndroid Build Coastguard Worker# GNU General Public License for more details. 19*49cdfc7eSAndroid Build Coastguard Worker# 20*49cdfc7eSAndroid Build Coastguard Worker# You should have received a copy of the GNU General Public License 21*49cdfc7eSAndroid Build Coastguard Worker# along with this program. If not, see <https://www.gnu.org/licenses/>. 22*49cdfc7eSAndroid Build Coastguard Worker 23*49cdfc7eSAndroid Build Coastguard Workeruse strict; 24*49cdfc7eSAndroid Build Coastguard Workeruse warnings; 25*49cdfc7eSAndroid Build Coastguard Workeruse Getopt::Long qw(:config bundling permute no_getopt_compat); 26*49cdfc7eSAndroid Build Coastguard Workeruse File::Temp qw/tempfile/; 27*49cdfc7eSAndroid Build Coastguard Worker 28*49cdfc7eSAndroid Build Coastguard Workersub init_hashes; 29*49cdfc7eSAndroid Build Coastguard Worker 30*49cdfc7eSAndroid Build Coastguard Worker(my $progname = $0) =~ s|.*/||; 31*49cdfc7eSAndroid Build Coastguard Worker 32*49cdfc7eSAndroid Build Coastguard Workermy $usage = <<"EOF"; 33*49cdfc7eSAndroid Build Coastguard WorkerUsage: $progname [-n] [-f] [-x] [-e] script ... 34*49cdfc7eSAndroid Build Coastguard Worker or: $progname --help 35*49cdfc7eSAndroid Build Coastguard Worker or: $progname --version 36*49cdfc7eSAndroid Build Coastguard WorkerThis script performs basic checks for the presence of bashisms 37*49cdfc7eSAndroid Build Coastguard Workerin /bin/sh scripts and the lack of bashisms in /bin/bash ones. 38*49cdfc7eSAndroid Build Coastguard WorkerEOF 39*49cdfc7eSAndroid Build Coastguard Worker 40*49cdfc7eSAndroid Build Coastguard Workermy $version = <<"EOF"; 41*49cdfc7eSAndroid Build Coastguard WorkerThis is $progname, from the Debian devscripts package, version 2.20.5 42*49cdfc7eSAndroid Build Coastguard WorkerThis code is copyright 2003 by Julian Gilbey <jdg\@debian.org>, 43*49cdfc7eSAndroid Build Coastguard Workerbased on original code which is copyright 1998 by Richard Braakman 44*49cdfc7eSAndroid Build Coastguard Workerand copyright 2002 by Josip Rodin. 45*49cdfc7eSAndroid Build Coastguard WorkerThis program comes with ABSOLUTELY NO WARRANTY. 46*49cdfc7eSAndroid Build Coastguard WorkerYou are free to redistribute this code under the terms of the 47*49cdfc7eSAndroid Build Coastguard WorkerGNU General Public License, version 2, or (at your option) any later version. 48*49cdfc7eSAndroid Build Coastguard WorkerEOF 49*49cdfc7eSAndroid Build Coastguard Worker 50*49cdfc7eSAndroid Build Coastguard Workermy ($opt_echo, $opt_force, $opt_extra, $opt_posix, $opt_early_fail); 51*49cdfc7eSAndroid Build Coastguard Workermy ($opt_help, $opt_version); 52*49cdfc7eSAndroid Build Coastguard Workermy @filenames; 53*49cdfc7eSAndroid Build Coastguard Worker 54*49cdfc7eSAndroid Build Coastguard Worker# Detect if STDIN is a pipe 55*49cdfc7eSAndroid Build Coastguard Workerif (scalar(@ARGV) == 0 && (-p STDIN or -f STDIN)) { 56*49cdfc7eSAndroid Build Coastguard Worker push(@ARGV, '-'); 57*49cdfc7eSAndroid Build Coastguard Worker} 58*49cdfc7eSAndroid Build Coastguard Worker 59*49cdfc7eSAndroid Build Coastguard Worker## 60*49cdfc7eSAndroid Build Coastguard Worker## handle command-line options 61*49cdfc7eSAndroid Build Coastguard Worker## 62*49cdfc7eSAndroid Build Coastguard Worker$opt_help = 1 if int(@ARGV) == 0; 63*49cdfc7eSAndroid Build Coastguard Worker 64*49cdfc7eSAndroid Build Coastguard WorkerGetOptions( 65*49cdfc7eSAndroid Build Coastguard Worker "help|h" => \$opt_help, 66*49cdfc7eSAndroid Build Coastguard Worker "version|v" => \$opt_version, 67*49cdfc7eSAndroid Build Coastguard Worker "newline|n" => \$opt_echo, 68*49cdfc7eSAndroid Build Coastguard Worker "force|f" => \$opt_force, 69*49cdfc7eSAndroid Build Coastguard Worker "extra|x" => \$opt_extra, 70*49cdfc7eSAndroid Build Coastguard Worker "posix|p" => \$opt_posix, 71*49cdfc7eSAndroid Build Coastguard Worker "early-fail|e" => \$opt_early_fail, 72*49cdfc7eSAndroid Build Coastguard Worker ) 73*49cdfc7eSAndroid Build Coastguard Worker or die 74*49cdfc7eSAndroid Build Coastguard Worker"Usage: $progname [options] filelist\nRun $progname --help for more details\n"; 75*49cdfc7eSAndroid Build Coastguard Worker 76*49cdfc7eSAndroid Build Coastguard Workerif ($opt_help) { print $usage; exit 0; } 77*49cdfc7eSAndroid Build Coastguard Workerif ($opt_version) { print $version; exit 0; } 78*49cdfc7eSAndroid Build Coastguard Worker 79*49cdfc7eSAndroid Build Coastguard Worker$opt_echo = 1 if $opt_posix; 80*49cdfc7eSAndroid Build Coastguard Worker 81*49cdfc7eSAndroid Build Coastguard Workermy $mode = 0; 82*49cdfc7eSAndroid Build Coastguard Workermy $issues = 0; 83*49cdfc7eSAndroid Build Coastguard Workermy $status = 0; 84*49cdfc7eSAndroid Build Coastguard Workermy $makefile = 0; 85*49cdfc7eSAndroid Build Coastguard Workermy (%bashisms, %string_bashisms, %singlequote_bashisms); 86*49cdfc7eSAndroid Build Coastguard Worker 87*49cdfc7eSAndroid Build Coastguard Workermy $LEADIN 88*49cdfc7eSAndroid Build Coastguard Worker = qr'(?:(?:^|[`&;(|{])\s*|(?:(?:if|elif|while)(?:\s+!)?|then|do|shell)\s+)'; 89*49cdfc7eSAndroid Build Coastguard Workerinit_hashes; 90*49cdfc7eSAndroid Build Coastguard Worker 91*49cdfc7eSAndroid Build Coastguard Workermy @bashisms_keys = sort keys %bashisms; 92*49cdfc7eSAndroid Build Coastguard Workermy @string_bashisms_keys = sort keys %string_bashisms; 93*49cdfc7eSAndroid Build Coastguard Workermy @singlequote_bashisms_keys = sort keys %singlequote_bashisms; 94*49cdfc7eSAndroid Build Coastguard Worker 95*49cdfc7eSAndroid Build Coastguard Workerforeach my $filename (@ARGV) { 96*49cdfc7eSAndroid Build Coastguard Worker my $check_lines_count = -1; 97*49cdfc7eSAndroid Build Coastguard Worker 98*49cdfc7eSAndroid Build Coastguard Worker my $display_filename = $filename; 99*49cdfc7eSAndroid Build Coastguard Worker 100*49cdfc7eSAndroid Build Coastguard Worker if ($filename eq '-') { 101*49cdfc7eSAndroid Build Coastguard Worker my $tmp_fh; 102*49cdfc7eSAndroid Build Coastguard Worker ($tmp_fh, $filename) 103*49cdfc7eSAndroid Build Coastguard Worker = tempfile("chkbashisms_tmp.XXXX", TMPDIR => 1, UNLINK => 1); 104*49cdfc7eSAndroid Build Coastguard Worker while (my $line = <STDIN>) { 105*49cdfc7eSAndroid Build Coastguard Worker print $tmp_fh $line; 106*49cdfc7eSAndroid Build Coastguard Worker } 107*49cdfc7eSAndroid Build Coastguard Worker close($tmp_fh); 108*49cdfc7eSAndroid Build Coastguard Worker $display_filename = "(stdin)"; 109*49cdfc7eSAndroid Build Coastguard Worker } 110*49cdfc7eSAndroid Build Coastguard Worker 111*49cdfc7eSAndroid Build Coastguard Worker if (!$opt_force) { 112*49cdfc7eSAndroid Build Coastguard Worker $check_lines_count = script_is_evil_and_wrong($filename); 113*49cdfc7eSAndroid Build Coastguard Worker } 114*49cdfc7eSAndroid Build Coastguard Worker 115*49cdfc7eSAndroid Build Coastguard Worker if ($check_lines_count == 0 or $check_lines_count == 1) { 116*49cdfc7eSAndroid Build Coastguard Worker warn 117*49cdfc7eSAndroid Build Coastguard Worker"script $display_filename does not appear to be a /bin/sh script; skipping\n"; 118*49cdfc7eSAndroid Build Coastguard Worker next; 119*49cdfc7eSAndroid Build Coastguard Worker } 120*49cdfc7eSAndroid Build Coastguard Worker 121*49cdfc7eSAndroid Build Coastguard Worker if ($check_lines_count != -1) { 122*49cdfc7eSAndroid Build Coastguard Worker warn 123*49cdfc7eSAndroid Build Coastguard Worker"script $display_filename appears to be a shell wrapper; only checking the first " 124*49cdfc7eSAndroid Build Coastguard Worker . "$check_lines_count lines\n"; 125*49cdfc7eSAndroid Build Coastguard Worker } 126*49cdfc7eSAndroid Build Coastguard Worker 127*49cdfc7eSAndroid Build Coastguard Worker unless (open C, '<', $filename) { 128*49cdfc7eSAndroid Build Coastguard Worker warn "cannot open script $display_filename for reading: $!\n"; 129*49cdfc7eSAndroid Build Coastguard Worker $status |= 2; 130*49cdfc7eSAndroid Build Coastguard Worker next; 131*49cdfc7eSAndroid Build Coastguard Worker } 132*49cdfc7eSAndroid Build Coastguard Worker 133*49cdfc7eSAndroid Build Coastguard Worker $issues = 0; 134*49cdfc7eSAndroid Build Coastguard Worker $mode = 0; 135*49cdfc7eSAndroid Build Coastguard Worker my $cat_string = ""; 136*49cdfc7eSAndroid Build Coastguard Worker my $cat_indented = 0; 137*49cdfc7eSAndroid Build Coastguard Worker my $quote_string = ""; 138*49cdfc7eSAndroid Build Coastguard Worker my $last_continued = 0; 139*49cdfc7eSAndroid Build Coastguard Worker my $continued = 0; 140*49cdfc7eSAndroid Build Coastguard Worker my $found_rules = 0; 141*49cdfc7eSAndroid Build Coastguard Worker my $buffered_orig_line = ""; 142*49cdfc7eSAndroid Build Coastguard Worker my $buffered_line = ""; 143*49cdfc7eSAndroid Build Coastguard Worker my %start_lines; 144*49cdfc7eSAndroid Build Coastguard Worker 145*49cdfc7eSAndroid Build Coastguard Worker while (<C>) { 146*49cdfc7eSAndroid Build Coastguard Worker next unless ($check_lines_count == -1 or $. <= $check_lines_count); 147*49cdfc7eSAndroid Build Coastguard Worker 148*49cdfc7eSAndroid Build Coastguard Worker if ($. == 1) { # This should be an interpreter line 149*49cdfc7eSAndroid Build Coastguard Worker if (m,^\#!\s*(?:\S+/env\s+)?(\S+),) { 150*49cdfc7eSAndroid Build Coastguard Worker my $interpreter = $1; 151*49cdfc7eSAndroid Build Coastguard Worker 152*49cdfc7eSAndroid Build Coastguard Worker if ($interpreter =~ m,(?:^|/)make$,) { 153*49cdfc7eSAndroid Build Coastguard Worker init_hashes if !$makefile++; 154*49cdfc7eSAndroid Build Coastguard Worker $makefile = 1; 155*49cdfc7eSAndroid Build Coastguard Worker } else { 156*49cdfc7eSAndroid Build Coastguard Worker init_hashes if $makefile--; 157*49cdfc7eSAndroid Build Coastguard Worker $makefile = 0; 158*49cdfc7eSAndroid Build Coastguard Worker } 159*49cdfc7eSAndroid Build Coastguard Worker next if $opt_force; 160*49cdfc7eSAndroid Build Coastguard Worker 161*49cdfc7eSAndroid Build Coastguard Worker if ($interpreter =~ m,(?:^|/)bash$,) { 162*49cdfc7eSAndroid Build Coastguard Worker $mode = 1; 163*49cdfc7eSAndroid Build Coastguard Worker } elsif ($interpreter !~ m,(?:^|/)(sh|dash|posh)$,) { 164*49cdfc7eSAndroid Build Coastguard Worker### ksh/zsh? 165*49cdfc7eSAndroid Build Coastguard Worker warn 166*49cdfc7eSAndroid Build Coastguard Worker"script $display_filename does not appear to be a /bin/sh script; skipping\n"; 167*49cdfc7eSAndroid Build Coastguard Worker $status |= 2; 168*49cdfc7eSAndroid Build Coastguard Worker last; 169*49cdfc7eSAndroid Build Coastguard Worker } 170*49cdfc7eSAndroid Build Coastguard Worker } else { 171*49cdfc7eSAndroid Build Coastguard Worker warn 172*49cdfc7eSAndroid Build Coastguard Worker"script $display_filename does not appear to have a \#! interpreter line;\nyou may get strange results\n"; 173*49cdfc7eSAndroid Build Coastguard Worker } 174*49cdfc7eSAndroid Build Coastguard Worker } 175*49cdfc7eSAndroid Build Coastguard Worker 176*49cdfc7eSAndroid Build Coastguard Worker chomp; 177*49cdfc7eSAndroid Build Coastguard Worker my $orig_line = $_; 178*49cdfc7eSAndroid Build Coastguard Worker 179*49cdfc7eSAndroid Build Coastguard Worker # We want to remove end-of-line comments, so need to skip 180*49cdfc7eSAndroid Build Coastguard Worker # comments that appear inside balanced pairs 181*49cdfc7eSAndroid Build Coastguard Worker # of single or double quotes 182*49cdfc7eSAndroid Build Coastguard Worker 183*49cdfc7eSAndroid Build Coastguard Worker # Remove comments in the "quoted" part of a line that starts 184*49cdfc7eSAndroid Build Coastguard Worker # in a quoted block? The problem is that we have no idea 185*49cdfc7eSAndroid Build Coastguard Worker # whether the program interpreting the block treats the 186*49cdfc7eSAndroid Build Coastguard Worker # quote character as part of the comment or as a quote 187*49cdfc7eSAndroid Build Coastguard Worker # terminator. We err on the side of caution and assume it 188*49cdfc7eSAndroid Build Coastguard Worker # will be treated as part of the comment. 189*49cdfc7eSAndroid Build Coastguard Worker # s/^(?:.*?[^\\])?$quote_string(.*)$/$1/ if $quote_string ne ""; 190*49cdfc7eSAndroid Build Coastguard Worker 191*49cdfc7eSAndroid Build Coastguard Worker # skip comment lines 192*49cdfc7eSAndroid Build Coastguard Worker if ( m,^\s*\#, 193*49cdfc7eSAndroid Build Coastguard Worker && $quote_string eq '' 194*49cdfc7eSAndroid Build Coastguard Worker && $buffered_line eq '' 195*49cdfc7eSAndroid Build Coastguard Worker && $cat_string eq '') { 196*49cdfc7eSAndroid Build Coastguard Worker next; 197*49cdfc7eSAndroid Build Coastguard Worker } 198*49cdfc7eSAndroid Build Coastguard Worker 199*49cdfc7eSAndroid Build Coastguard Worker # Remove quoted strings so we can more easily ignore comments 200*49cdfc7eSAndroid Build Coastguard Worker # inside them 201*49cdfc7eSAndroid Build Coastguard Worker s/(^|[^\\](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; 202*49cdfc7eSAndroid Build Coastguard Worker s/(^|[^\\](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; 203*49cdfc7eSAndroid Build Coastguard Worker 204*49cdfc7eSAndroid Build Coastguard Worker # If inside a quoted string, remove everything before the quote 205*49cdfc7eSAndroid Build Coastguard Worker s/^.+?\'// 206*49cdfc7eSAndroid Build Coastguard Worker if ($quote_string eq "'"); 207*49cdfc7eSAndroid Build Coastguard Worker s/^.+?[^\\]\"// 208*49cdfc7eSAndroid Build Coastguard Worker if ($quote_string eq '"'); 209*49cdfc7eSAndroid Build Coastguard Worker 210*49cdfc7eSAndroid Build Coastguard Worker # If the remaining string contains what looks like a comment, 211*49cdfc7eSAndroid Build Coastguard Worker # eat it. In either case, swap the unmodified script line 212*49cdfc7eSAndroid Build Coastguard Worker # back in for processing. 213*49cdfc7eSAndroid Build Coastguard Worker if (m/(?:^|[^[\\])[\s\&;\(\)](\#.*$)/) { 214*49cdfc7eSAndroid Build Coastguard Worker $_ = $orig_line; 215*49cdfc7eSAndroid Build Coastguard Worker s/\Q$1\E//; # eat comments 216*49cdfc7eSAndroid Build Coastguard Worker } else { 217*49cdfc7eSAndroid Build Coastguard Worker $_ = $orig_line; 218*49cdfc7eSAndroid Build Coastguard Worker } 219*49cdfc7eSAndroid Build Coastguard Worker 220*49cdfc7eSAndroid Build Coastguard Worker # Handle line continuation 221*49cdfc7eSAndroid Build Coastguard Worker if (!$makefile && $cat_string eq '' && m/\\$/) { 222*49cdfc7eSAndroid Build Coastguard Worker chop; 223*49cdfc7eSAndroid Build Coastguard Worker $buffered_line .= $_; 224*49cdfc7eSAndroid Build Coastguard Worker $buffered_orig_line .= $orig_line . "\n"; 225*49cdfc7eSAndroid Build Coastguard Worker next; 226*49cdfc7eSAndroid Build Coastguard Worker } 227*49cdfc7eSAndroid Build Coastguard Worker 228*49cdfc7eSAndroid Build Coastguard Worker if ($buffered_line ne '') { 229*49cdfc7eSAndroid Build Coastguard Worker $_ = $buffered_line . $_; 230*49cdfc7eSAndroid Build Coastguard Worker $orig_line = $buffered_orig_line . $orig_line; 231*49cdfc7eSAndroid Build Coastguard Worker $buffered_line = ''; 232*49cdfc7eSAndroid Build Coastguard Worker $buffered_orig_line = ''; 233*49cdfc7eSAndroid Build Coastguard Worker } 234*49cdfc7eSAndroid Build Coastguard Worker 235*49cdfc7eSAndroid Build Coastguard Worker if ($makefile) { 236*49cdfc7eSAndroid Build Coastguard Worker $last_continued = $continued; 237*49cdfc7eSAndroid Build Coastguard Worker if (/[^\\]\\$/) { 238*49cdfc7eSAndroid Build Coastguard Worker $continued = 1; 239*49cdfc7eSAndroid Build Coastguard Worker } else { 240*49cdfc7eSAndroid Build Coastguard Worker $continued = 0; 241*49cdfc7eSAndroid Build Coastguard Worker } 242*49cdfc7eSAndroid Build Coastguard Worker 243*49cdfc7eSAndroid Build Coastguard Worker # Don't match lines that look like a rule if we're in a 244*49cdfc7eSAndroid Build Coastguard Worker # continuation line before the start of the rules 245*49cdfc7eSAndroid Build Coastguard Worker if (/^[\w%-]+:+\s.*?;?(.*)$/ 246*49cdfc7eSAndroid Build Coastguard Worker and !($last_continued and !$found_rules)) { 247*49cdfc7eSAndroid Build Coastguard Worker $found_rules = 1; 248*49cdfc7eSAndroid Build Coastguard Worker $_ = $1 if $1; 249*49cdfc7eSAndroid Build Coastguard Worker } 250*49cdfc7eSAndroid Build Coastguard Worker 251*49cdfc7eSAndroid Build Coastguard Worker last 252*49cdfc7eSAndroid Build Coastguard Worker if m%^\s*(override\s|export\s)?\s*SHELL\s*:?=\s*(/bin/)?bash\s*%; 253*49cdfc7eSAndroid Build Coastguard Worker 254*49cdfc7eSAndroid Build Coastguard Worker # Remove "simple" target names 255*49cdfc7eSAndroid Build Coastguard Worker s/^[\w%.-]+(?:\s+[\w%.-]+)*::?//; 256*49cdfc7eSAndroid Build Coastguard Worker s/^\t//; 257*49cdfc7eSAndroid Build Coastguard Worker s/(?<!\$)\$\((\w+)\)/\${$1}/g; 258*49cdfc7eSAndroid Build Coastguard Worker s/(\$){2}/$1/g; 259*49cdfc7eSAndroid Build Coastguard Worker s/^[\s\t]*[@-]{1,2}//; 260*49cdfc7eSAndroid Build Coastguard Worker } 261*49cdfc7eSAndroid Build Coastguard Worker 262*49cdfc7eSAndroid Build Coastguard Worker if ( 263*49cdfc7eSAndroid Build Coastguard Worker $cat_string ne "" 264*49cdfc7eSAndroid Build Coastguard Worker && (m/^\Q$cat_string\E$/ 265*49cdfc7eSAndroid Build Coastguard Worker || ($cat_indented && m/^\t*\Q$cat_string\E$/)) 266*49cdfc7eSAndroid Build Coastguard Worker ) { 267*49cdfc7eSAndroid Build Coastguard Worker $cat_string = ""; 268*49cdfc7eSAndroid Build Coastguard Worker next; 269*49cdfc7eSAndroid Build Coastguard Worker } 270*49cdfc7eSAndroid Build Coastguard Worker my $within_another_shell = 0; 271*49cdfc7eSAndroid Build Coastguard Worker if (m,(^|\s+)((/usr)?/bin/)?((b|d)?a|k|z|t?c)sh\s+-c\s*.+,) { 272*49cdfc7eSAndroid Build Coastguard Worker $within_another_shell = 1; 273*49cdfc7eSAndroid Build Coastguard Worker } 274*49cdfc7eSAndroid Build Coastguard Worker # if cat_string is set, we are in a HERE document and need not 275*49cdfc7eSAndroid Build Coastguard Worker # check for things 276*49cdfc7eSAndroid Build Coastguard Worker if ($cat_string eq "" and !$within_another_shell) { 277*49cdfc7eSAndroid Build Coastguard Worker my $found = 0; 278*49cdfc7eSAndroid Build Coastguard Worker my $match = ''; 279*49cdfc7eSAndroid Build Coastguard Worker my $explanation = ''; 280*49cdfc7eSAndroid Build Coastguard Worker my $line = $_; 281*49cdfc7eSAndroid Build Coastguard Worker 282*49cdfc7eSAndroid Build Coastguard Worker # Remove "" / '' as they clearly aren't quoted strings 283*49cdfc7eSAndroid Build Coastguard Worker # and not considering them makes the matching easier 284*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\])(\'\')+/$1/g; 285*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\])(\"\")+/$1/g; 286*49cdfc7eSAndroid Build Coastguard Worker 287*49cdfc7eSAndroid Build Coastguard Worker if ($quote_string ne "") { 288*49cdfc7eSAndroid Build Coastguard Worker my $otherquote = ($quote_string eq "\"" ? "\'" : "\""); 289*49cdfc7eSAndroid Build Coastguard Worker # Inside a quoted block 290*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ /(?:^|^.*?[^\\])$quote_string(.*)$/) { 291*49cdfc7eSAndroid Build Coastguard Worker my $rest = $1; 292*49cdfc7eSAndroid Build Coastguard Worker my $templine = $line; 293*49cdfc7eSAndroid Build Coastguard Worker 294*49cdfc7eSAndroid Build Coastguard Worker # Remove quoted strings delimited with $otherquote 295*49cdfc7eSAndroid Build Coastguard Worker $templine 296*49cdfc7eSAndroid Build Coastguard Worker =~ s/(^|[^\\])$otherquote[^$quote_string]*?[^\\]$otherquote/$1/g; 297*49cdfc7eSAndroid Build Coastguard Worker # Remove quotes that are themselves quoted 298*49cdfc7eSAndroid Build Coastguard Worker # "a'b" 299*49cdfc7eSAndroid Build Coastguard Worker $templine 300*49cdfc7eSAndroid Build Coastguard Worker =~ s/(^|[^\\])$otherquote.*?$quote_string.*?[^\\]$otherquote/$1/g; 301*49cdfc7eSAndroid Build Coastguard Worker # "\"" 302*49cdfc7eSAndroid Build Coastguard Worker $templine 303*49cdfc7eSAndroid Build Coastguard Worker =~ s/(^|[^\\])$quote_string\\$quote_string$quote_string/$1/g; 304*49cdfc7eSAndroid Build Coastguard Worker 305*49cdfc7eSAndroid Build Coastguard Worker # After all that, were there still any quotes left? 306*49cdfc7eSAndroid Build Coastguard Worker my $count = () = $templine =~ /(^|[^\\])$quote_string/g; 307*49cdfc7eSAndroid Build Coastguard Worker next if $count == 0; 308*49cdfc7eSAndroid Build Coastguard Worker 309*49cdfc7eSAndroid Build Coastguard Worker $count = () = $rest =~ /(^|[^\\])$quote_string/g; 310*49cdfc7eSAndroid Build Coastguard Worker if ($count % 2 == 0) { 311*49cdfc7eSAndroid Build Coastguard Worker # Quoted block ends on this line 312*49cdfc7eSAndroid Build Coastguard Worker # Ignore everything before the closing quote 313*49cdfc7eSAndroid Build Coastguard Worker $line = $rest || ''; 314*49cdfc7eSAndroid Build Coastguard Worker $quote_string = ""; 315*49cdfc7eSAndroid Build Coastguard Worker } else { 316*49cdfc7eSAndroid Build Coastguard Worker next; 317*49cdfc7eSAndroid Build Coastguard Worker } 318*49cdfc7eSAndroid Build Coastguard Worker } else { 319*49cdfc7eSAndroid Build Coastguard Worker # Still inside the quoted block, skip this line 320*49cdfc7eSAndroid Build Coastguard Worker next; 321*49cdfc7eSAndroid Build Coastguard Worker } 322*49cdfc7eSAndroid Build Coastguard Worker } 323*49cdfc7eSAndroid Build Coastguard Worker 324*49cdfc7eSAndroid Build Coastguard Worker # Check even if we removed the end of a quoted block 325*49cdfc7eSAndroid Build Coastguard Worker # in the previous check, as a single line can end one 326*49cdfc7eSAndroid Build Coastguard Worker # block and begin another 327*49cdfc7eSAndroid Build Coastguard Worker if ($quote_string eq "") { 328*49cdfc7eSAndroid Build Coastguard Worker # Possible start of a quoted block 329*49cdfc7eSAndroid Build Coastguard Worker for my $quote ("\"", "\'") { 330*49cdfc7eSAndroid Build Coastguard Worker my $templine = $line; 331*49cdfc7eSAndroid Build Coastguard Worker my $otherquote = ($quote eq "\"" ? "\'" : "\""); 332*49cdfc7eSAndroid Build Coastguard Worker 333*49cdfc7eSAndroid Build Coastguard Worker # Remove balanced quotes and their content 334*49cdfc7eSAndroid Build Coastguard Worker while (1) { 335*49cdfc7eSAndroid Build Coastguard Worker my ($length_single, $length_double) = (0, 0); 336*49cdfc7eSAndroid Build Coastguard Worker 337*49cdfc7eSAndroid Build Coastguard Worker # Determine which one would match first: 338*49cdfc7eSAndroid Build Coastguard Worker if ($templine 339*49cdfc7eSAndroid Build Coastguard Worker =~ m/(^.+?(?:^|[^\\\"](?:\\\\)*)\')[^\']*\'/) { 340*49cdfc7eSAndroid Build Coastguard Worker $length_single = length($1); 341*49cdfc7eSAndroid Build Coastguard Worker } 342*49cdfc7eSAndroid Build Coastguard Worker if ($templine 343*49cdfc7eSAndroid Build Coastguard Worker =~ m/(^.*?(?:^|[^\\\'](?:\\\\)*)\")(?:\\.|[^\\\"])+\"/ 344*49cdfc7eSAndroid Build Coastguard Worker ) { 345*49cdfc7eSAndroid Build Coastguard Worker $length_double = length($1); 346*49cdfc7eSAndroid Build Coastguard Worker } 347*49cdfc7eSAndroid Build Coastguard Worker 348*49cdfc7eSAndroid Build Coastguard Worker # Now simplify accordingly (shorter is preferred): 349*49cdfc7eSAndroid Build Coastguard Worker if ( 350*49cdfc7eSAndroid Build Coastguard Worker $length_single != 0 351*49cdfc7eSAndroid Build Coastguard Worker && ( $length_single < $length_double 352*49cdfc7eSAndroid Build Coastguard Worker || $length_double == 0) 353*49cdfc7eSAndroid Build Coastguard Worker ) { 354*49cdfc7eSAndroid Build Coastguard Worker $templine =~ s/(^|[^\\\"](?:\\\\)*)\'[^\']*\'/$1/; 355*49cdfc7eSAndroid Build Coastguard Worker } elsif ($length_double != 0) { 356*49cdfc7eSAndroid Build Coastguard Worker $templine 357*49cdfc7eSAndroid Build Coastguard Worker =~ s/(^|[^\\\'](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1/; 358*49cdfc7eSAndroid Build Coastguard Worker } else { 359*49cdfc7eSAndroid Build Coastguard Worker last; 360*49cdfc7eSAndroid Build Coastguard Worker } 361*49cdfc7eSAndroid Build Coastguard Worker } 362*49cdfc7eSAndroid Build Coastguard Worker 363*49cdfc7eSAndroid Build Coastguard Worker # Don't flag quotes that are themselves quoted 364*49cdfc7eSAndroid Build Coastguard Worker # "a'b" 365*49cdfc7eSAndroid Build Coastguard Worker $templine =~ s/$otherquote.*?$quote.*?$otherquote//g; 366*49cdfc7eSAndroid Build Coastguard Worker # "\"" 367*49cdfc7eSAndroid Build Coastguard Worker $templine =~ s/(^|[^\\])$quote\\$quote$quote/$1/g; 368*49cdfc7eSAndroid Build Coastguard Worker # \' or \" 369*49cdfc7eSAndroid Build Coastguard Worker $templine =~ s/\\[\'\"]//g; 370*49cdfc7eSAndroid Build Coastguard Worker my $count = () = $templine =~ /(^|(?!\\))$quote/g; 371*49cdfc7eSAndroid Build Coastguard Worker 372*49cdfc7eSAndroid Build Coastguard Worker # If there's an odd number of non-escaped 373*49cdfc7eSAndroid Build Coastguard Worker # quotes in the line it's almost certainly the 374*49cdfc7eSAndroid Build Coastguard Worker # start of a quoted block. 375*49cdfc7eSAndroid Build Coastguard Worker if ($count % 2 == 1) { 376*49cdfc7eSAndroid Build Coastguard Worker $quote_string = $quote; 377*49cdfc7eSAndroid Build Coastguard Worker $start_lines{'quote_string'} = $.; 378*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/^(.*)$quote.*$/$1/; 379*49cdfc7eSAndroid Build Coastguard Worker last; 380*49cdfc7eSAndroid Build Coastguard Worker } 381*49cdfc7eSAndroid Build Coastguard Worker } 382*49cdfc7eSAndroid Build Coastguard Worker } 383*49cdfc7eSAndroid Build Coastguard Worker 384*49cdfc7eSAndroid Build Coastguard Worker # since this test is ugly, I have to do it by itself 385*49cdfc7eSAndroid Build Coastguard Worker # detect source (.) trying to pass args to the command it runs 386*49cdfc7eSAndroid Build Coastguard Worker # The first expression weeds out '. "foo bar"' 387*49cdfc7eSAndroid Build Coastguard Worker if ( not $found 388*49cdfc7eSAndroid Build Coastguard Worker and not 389*49cdfc7eSAndroid Build Coastguard Workerm/$LEADIN\.\s+(\"[^\"]+\"|\'[^\']+\'|\$\([^)]+\)+(?:\/[^\s;]+)?)\s*(\&|\||\d?>|<|;|\Z)/o 390*49cdfc7eSAndroid Build Coastguard Worker and m/$LEADIN(\.\s+[^\s;\`:]+\s+([^\s;]+))/o) { 391*49cdfc7eSAndroid Build Coastguard Worker if ($2 =~ /^(\&|\||\d?>|<)/) { 392*49cdfc7eSAndroid Build Coastguard Worker # everything is ok 393*49cdfc7eSAndroid Build Coastguard Worker ; 394*49cdfc7eSAndroid Build Coastguard Worker } else { 395*49cdfc7eSAndroid Build Coastguard Worker $found = 1; 396*49cdfc7eSAndroid Build Coastguard Worker $match = $1; 397*49cdfc7eSAndroid Build Coastguard Worker $explanation = "sourced script with arguments"; 398*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 399*49cdfc7eSAndroid Build Coastguard Worker $explanation); 400*49cdfc7eSAndroid Build Coastguard Worker } 401*49cdfc7eSAndroid Build Coastguard Worker } 402*49cdfc7eSAndroid Build Coastguard Worker 403*49cdfc7eSAndroid Build Coastguard Worker # Remove "quoted quotes". They're likely to be inside 404*49cdfc7eSAndroid Build Coastguard Worker # another pair of quotes; we're not interested in 405*49cdfc7eSAndroid Build Coastguard Worker # them for their own sake and removing them makes finding 406*49cdfc7eSAndroid Build Coastguard Worker # the limits of the outer pair far easier. 407*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\\'\"])\"\'\"/$1/g; 408*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\\'\"])\'\"\'/$1/g; 409*49cdfc7eSAndroid Build Coastguard Worker 410*49cdfc7eSAndroid Build Coastguard Worker foreach my $re (@singlequote_bashisms_keys) { 411*49cdfc7eSAndroid Build Coastguard Worker my $expl = $singlequote_bashisms{$re}; 412*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/($re)/) { 413*49cdfc7eSAndroid Build Coastguard Worker $found = 1; 414*49cdfc7eSAndroid Build Coastguard Worker $match = $1; 415*49cdfc7eSAndroid Build Coastguard Worker $explanation = $expl; 416*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 417*49cdfc7eSAndroid Build Coastguard Worker $explanation); 418*49cdfc7eSAndroid Build Coastguard Worker } 419*49cdfc7eSAndroid Build Coastguard Worker } 420*49cdfc7eSAndroid Build Coastguard Worker 421*49cdfc7eSAndroid Build Coastguard Worker my $re = '(?<![\$\\\])\$\'[^\']+\''; 422*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/(.*)($re)/o) { 423*49cdfc7eSAndroid Build Coastguard Worker my $count = () = $1 =~ /(^|[^\\])\'/g; 424*49cdfc7eSAndroid Build Coastguard Worker if ($count % 2 == 0) { 425*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 426*49cdfc7eSAndroid Build Coastguard Worker q<$'...' should be "$(printf '...')">); 427*49cdfc7eSAndroid Build Coastguard Worker } 428*49cdfc7eSAndroid Build Coastguard Worker } 429*49cdfc7eSAndroid Build Coastguard Worker 430*49cdfc7eSAndroid Build Coastguard Worker # $cat_line contains the version of the line we'll check 431*49cdfc7eSAndroid Build Coastguard Worker # for heredoc delimiters later. Initially, remove any 432*49cdfc7eSAndroid Build Coastguard Worker # spaces between << and the delimiter to make the following 433*49cdfc7eSAndroid Build Coastguard Worker # updates to $cat_line easier. However, don't remove the 434*49cdfc7eSAndroid Build Coastguard Worker # spaces if the delimiter starts with a -, as that changes 435*49cdfc7eSAndroid Build Coastguard Worker # how the delimiter is searched. 436*49cdfc7eSAndroid Build Coastguard Worker my $cat_line = $line; 437*49cdfc7eSAndroid Build Coastguard Worker $cat_line =~ s/(<\<-?)\s+(?!-)/$1/g; 438*49cdfc7eSAndroid Build Coastguard Worker 439*49cdfc7eSAndroid Build Coastguard Worker # Ignore anything inside single quotes; it could be an 440*49cdfc7eSAndroid Build Coastguard Worker # argument to grep or the like. 441*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\\"](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; 442*49cdfc7eSAndroid Build Coastguard Worker 443*49cdfc7eSAndroid Build Coastguard Worker # As above, with the exception that we don't remove the string 444*49cdfc7eSAndroid Build Coastguard Worker # if the quote is immediately preceded by a < or a -, so we 445*49cdfc7eSAndroid Build Coastguard Worker # can match "foo <<-?'xyz'" as a heredoc later 446*49cdfc7eSAndroid Build Coastguard Worker # The check is a little more greedy than we'd like, but the 447*49cdfc7eSAndroid Build Coastguard Worker # heredoc test itself will weed out any false positives 448*49cdfc7eSAndroid Build Coastguard Worker $cat_line =~ s/(^|[^<\\\"-](?:\\\\)*)\'(?:\\.|[^\\\'])+\'/$1''/g; 449*49cdfc7eSAndroid Build Coastguard Worker 450*49cdfc7eSAndroid Build Coastguard Worker $re = '(?<![\$\\\])\$\"[^\"]+\"'; 451*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/(.*)($re)/o) { 452*49cdfc7eSAndroid Build Coastguard Worker my $count = () = $1 =~ /(^|[^\\])\"/g; 453*49cdfc7eSAndroid Build Coastguard Worker if ($count % 2 == 0) { 454*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 455*49cdfc7eSAndroid Build Coastguard Worker q<$"foo" should be eval_gettext "foo">); 456*49cdfc7eSAndroid Build Coastguard Worker } 457*49cdfc7eSAndroid Build Coastguard Worker } 458*49cdfc7eSAndroid Build Coastguard Worker 459*49cdfc7eSAndroid Build Coastguard Worker foreach my $re (@string_bashisms_keys) { 460*49cdfc7eSAndroid Build Coastguard Worker my $expl = $string_bashisms{$re}; 461*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/($re)/) { 462*49cdfc7eSAndroid Build Coastguard Worker $found = 1; 463*49cdfc7eSAndroid Build Coastguard Worker $match = $1; 464*49cdfc7eSAndroid Build Coastguard Worker $explanation = $expl; 465*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 466*49cdfc7eSAndroid Build Coastguard Worker $explanation); 467*49cdfc7eSAndroid Build Coastguard Worker } 468*49cdfc7eSAndroid Build Coastguard Worker } 469*49cdfc7eSAndroid Build Coastguard Worker 470*49cdfc7eSAndroid Build Coastguard Worker # We've checked for all the things we still want to notice in 471*49cdfc7eSAndroid Build Coastguard Worker # double-quoted strings, so now remove those strings as well. 472*49cdfc7eSAndroid Build Coastguard Worker $line =~ s/(^|[^\\\'](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; 473*49cdfc7eSAndroid Build Coastguard Worker $cat_line =~ s/(^|[^<\\\'-](?:\\\\)*)\"(?:\\.|[^\\\"])+\"/$1""/g; 474*49cdfc7eSAndroid Build Coastguard Worker foreach my $re (@bashisms_keys) { 475*49cdfc7eSAndroid Build Coastguard Worker my $expl = $bashisms{$re}; 476*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/($re)/) { 477*49cdfc7eSAndroid Build Coastguard Worker $found = 1; 478*49cdfc7eSAndroid Build Coastguard Worker $match = $1; 479*49cdfc7eSAndroid Build Coastguard Worker $explanation = $expl; 480*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 481*49cdfc7eSAndroid Build Coastguard Worker $explanation); 482*49cdfc7eSAndroid Build Coastguard Worker } 483*49cdfc7eSAndroid Build Coastguard Worker } 484*49cdfc7eSAndroid Build Coastguard Worker # This check requires the value to be compared, which could 485*49cdfc7eSAndroid Build Coastguard Worker # be done in the regex itself but requires "use re 'eval'". 486*49cdfc7eSAndroid Build Coastguard Worker # So it's better done in its own 487*49cdfc7eSAndroid Build Coastguard Worker if ($line =~ m/$LEADIN((?:exit|return)\s+(\d{3,}))/o && $2 > 255) { 488*49cdfc7eSAndroid Build Coastguard Worker $explanation = 'exit|return status code greater than 255'; 489*49cdfc7eSAndroid Build Coastguard Worker output_explanation($display_filename, $orig_line, 490*49cdfc7eSAndroid Build Coastguard Worker $explanation); 491*49cdfc7eSAndroid Build Coastguard Worker } 492*49cdfc7eSAndroid Build Coastguard Worker 493*49cdfc7eSAndroid Build Coastguard Worker # Only look for the beginning of a heredoc here, after we've 494*49cdfc7eSAndroid Build Coastguard Worker # stripped out quoted material, to avoid false positives. 495*49cdfc7eSAndroid Build Coastguard Worker if ($cat_line 496*49cdfc7eSAndroid Build Coastguard Worker =~ m/(?:^|[^<])\<\<(\-?)\s*(?:(?!<|'|")((?:[^\s;>|]+(?:(?<=\\)[\s;>|])?)+)|[\'\"](.*?)[\'\"])/ 497*49cdfc7eSAndroid Build Coastguard Worker ) { 498*49cdfc7eSAndroid Build Coastguard Worker $cat_indented = ($1 && $1 eq '-') ? 1 : 0; 499*49cdfc7eSAndroid Build Coastguard Worker my $quoted = defined($3); 500*49cdfc7eSAndroid Build Coastguard Worker $cat_string = $quoted ? $3 : $2; 501*49cdfc7eSAndroid Build Coastguard Worker unless ($quoted) { 502*49cdfc7eSAndroid Build Coastguard Worker # Now strip backslashes. Keep the position of the 503*49cdfc7eSAndroid Build Coastguard Worker # last match in a variable, as s/// resets it back 504*49cdfc7eSAndroid Build Coastguard Worker # to undef, but we don't want that. 505*49cdfc7eSAndroid Build Coastguard Worker my $pos = 0; 506*49cdfc7eSAndroid Build Coastguard Worker pos($cat_string) = $pos; 507*49cdfc7eSAndroid Build Coastguard Worker while ($cat_string =~ s/\G(.*?)\\/$1/) { 508*49cdfc7eSAndroid Build Coastguard Worker # position += length of match + the character 509*49cdfc7eSAndroid Build Coastguard Worker # that followed the backslash: 510*49cdfc7eSAndroid Build Coastguard Worker $pos += length($1) + 1; 511*49cdfc7eSAndroid Build Coastguard Worker pos($cat_string) = $pos; 512*49cdfc7eSAndroid Build Coastguard Worker } 513*49cdfc7eSAndroid Build Coastguard Worker } 514*49cdfc7eSAndroid Build Coastguard Worker $start_lines{'cat_string'} = $.; 515*49cdfc7eSAndroid Build Coastguard Worker } 516*49cdfc7eSAndroid Build Coastguard Worker } 517*49cdfc7eSAndroid Build Coastguard Worker } 518*49cdfc7eSAndroid Build Coastguard Worker 519*49cdfc7eSAndroid Build Coastguard Worker warn 520*49cdfc7eSAndroid Build Coastguard Worker"error: $display_filename: Unterminated heredoc found, EOF reached. Wanted: <$cat_string>, opened in line $start_lines{'cat_string'}\n" 521*49cdfc7eSAndroid Build Coastguard Worker if ($cat_string ne ''); 522*49cdfc7eSAndroid Build Coastguard Worker warn 523*49cdfc7eSAndroid Build Coastguard Worker"error: $display_filename: Unterminated quoted string found, EOF reached. Wanted: <$quote_string>, opened in line $start_lines{'quote_string'}\n" 524*49cdfc7eSAndroid Build Coastguard Worker if ($quote_string ne ''); 525*49cdfc7eSAndroid Build Coastguard Worker warn "error: $display_filename: EOF reached while on line continuation.\n" 526*49cdfc7eSAndroid Build Coastguard Worker if ($buffered_line ne ''); 527*49cdfc7eSAndroid Build Coastguard Worker 528*49cdfc7eSAndroid Build Coastguard Worker close C; 529*49cdfc7eSAndroid Build Coastguard Worker 530*49cdfc7eSAndroid Build Coastguard Worker if ($mode && !$issues) { 531*49cdfc7eSAndroid Build Coastguard Worker warn "could not find any possible bashisms in bash script $filename\n"; 532*49cdfc7eSAndroid Build Coastguard Worker $status |= 4; 533*49cdfc7eSAndroid Build Coastguard Worker } 534*49cdfc7eSAndroid Build Coastguard Worker} 535*49cdfc7eSAndroid Build Coastguard Worker 536*49cdfc7eSAndroid Build Coastguard Workerexit $status; 537*49cdfc7eSAndroid Build Coastguard Worker 538*49cdfc7eSAndroid Build Coastguard Workersub output_explanation { 539*49cdfc7eSAndroid Build Coastguard Worker my ($filename, $line, $explanation) = @_; 540*49cdfc7eSAndroid Build Coastguard Worker 541*49cdfc7eSAndroid Build Coastguard Worker if ($mode) { 542*49cdfc7eSAndroid Build Coastguard Worker # When examining a bash script, just flag that there are indeed 543*49cdfc7eSAndroid Build Coastguard Worker # bashisms present 544*49cdfc7eSAndroid Build Coastguard Worker $issues = 1; 545*49cdfc7eSAndroid Build Coastguard Worker } else { 546*49cdfc7eSAndroid Build Coastguard Worker warn "possible bashism in $filename line $. ($explanation):\n$line\n"; 547*49cdfc7eSAndroid Build Coastguard Worker if ($opt_early_fail) { 548*49cdfc7eSAndroid Build Coastguard Worker exit 1; 549*49cdfc7eSAndroid Build Coastguard Worker } 550*49cdfc7eSAndroid Build Coastguard Worker $status |= 1; 551*49cdfc7eSAndroid Build Coastguard Worker } 552*49cdfc7eSAndroid Build Coastguard Worker} 553*49cdfc7eSAndroid Build Coastguard Worker 554*49cdfc7eSAndroid Build Coastguard Worker# Returns non-zero if the given file is not actually a shell script, 555*49cdfc7eSAndroid Build Coastguard Worker# just looks like one. 556*49cdfc7eSAndroid Build Coastguard Workersub script_is_evil_and_wrong { 557*49cdfc7eSAndroid Build Coastguard Worker my ($filename) = @_; 558*49cdfc7eSAndroid Build Coastguard Worker my $ret = -1; 559*49cdfc7eSAndroid Build Coastguard Worker # lintian's version of this function aborts if the file 560*49cdfc7eSAndroid Build Coastguard Worker # can't be opened, but we simply return as the next 561*49cdfc7eSAndroid Build Coastguard Worker # test in the calling code handles reporting the error 562*49cdfc7eSAndroid Build Coastguard Worker # itself 563*49cdfc7eSAndroid Build Coastguard Worker open(IN, '<', $filename) or return $ret; 564*49cdfc7eSAndroid Build Coastguard Worker my $i = 0; 565*49cdfc7eSAndroid Build Coastguard Worker my $var = "0"; 566*49cdfc7eSAndroid Build Coastguard Worker my $backgrounded = 0; 567*49cdfc7eSAndroid Build Coastguard Worker local $_; 568*49cdfc7eSAndroid Build Coastguard Worker while (<IN>) { 569*49cdfc7eSAndroid Build Coastguard Worker chomp; 570*49cdfc7eSAndroid Build Coastguard Worker next if /^#/o; 571*49cdfc7eSAndroid Build Coastguard Worker next if /^$/o; 572*49cdfc7eSAndroid Build Coastguard Worker last if (++$i > 55); 573*49cdfc7eSAndroid Build Coastguard Worker if ( 574*49cdfc7eSAndroid Build Coastguard Worker m~ 575*49cdfc7eSAndroid Build Coastguard Worker # the exec should either be "eval"ed or a new statement 576*49cdfc7eSAndroid Build Coastguard Worker (^\s*|\beval\s*[\'\"]|(;|&&|\b(then|else))\s*) 577*49cdfc7eSAndroid Build Coastguard Worker 578*49cdfc7eSAndroid Build Coastguard Worker # eat anything between the exec and $0 579*49cdfc7eSAndroid Build Coastguard Worker exec\s*.+\s* 580*49cdfc7eSAndroid Build Coastguard Worker 581*49cdfc7eSAndroid Build Coastguard Worker # optionally quoted executable name (via $0) 582*49cdfc7eSAndroid Build Coastguard Worker .?\$$var.?\s* 583*49cdfc7eSAndroid Build Coastguard Worker 584*49cdfc7eSAndroid Build Coastguard Worker # optional "end of options" indicator 585*49cdfc7eSAndroid Build Coastguard Worker (--\s*)? 586*49cdfc7eSAndroid Build Coastguard Worker 587*49cdfc7eSAndroid Build Coastguard Worker # Match expressions of the form '${1+$@}', '${1:+"$@"', 588*49cdfc7eSAndroid Build Coastguard Worker # '"${1+$@', "$@", etc where the quotes (before the dollar 589*49cdfc7eSAndroid Build Coastguard Worker # sign(s)) are optional and the second (or only if the $1 590*49cdfc7eSAndroid Build Coastguard Worker # clause is omitted) parameter may be $@ or $*. 591*49cdfc7eSAndroid Build Coastguard Worker # 592*49cdfc7eSAndroid Build Coastguard Worker # Finally the whole subexpression may be omitted for scripts 593*49cdfc7eSAndroid Build Coastguard Worker # which do not pass on their parameters (i.e. after re-execing 594*49cdfc7eSAndroid Build Coastguard Worker # they take their parameters (and potentially data) from stdin 595*49cdfc7eSAndroid Build Coastguard Worker .?(\$\{1:?\+.?)?(\$(\@|\*))?~x 596*49cdfc7eSAndroid Build Coastguard Worker ) { 597*49cdfc7eSAndroid Build Coastguard Worker $ret = $. - 1; 598*49cdfc7eSAndroid Build Coastguard Worker last; 599*49cdfc7eSAndroid Build Coastguard Worker } elsif (/^\s*(\w+)=\$0;/) { 600*49cdfc7eSAndroid Build Coastguard Worker $var = $1; 601*49cdfc7eSAndroid Build Coastguard Worker } elsif ( 602*49cdfc7eSAndroid Build Coastguard Worker m~ 603*49cdfc7eSAndroid Build Coastguard Worker # Match scripts which use "foo $0 $@ &\nexec true\n" 604*49cdfc7eSAndroid Build Coastguard Worker # Program name 605*49cdfc7eSAndroid Build Coastguard Worker \S+\s+ 606*49cdfc7eSAndroid Build Coastguard Worker 607*49cdfc7eSAndroid Build Coastguard Worker # As above 608*49cdfc7eSAndroid Build Coastguard Worker .?\$$var.?\s* 609*49cdfc7eSAndroid Build Coastguard Worker (--\s*)? 610*49cdfc7eSAndroid Build Coastguard Worker .?(\$\{1:?\+.?)?(\$(\@|\*))?.?\s*\&~x 611*49cdfc7eSAndroid Build Coastguard Worker ) { 612*49cdfc7eSAndroid Build Coastguard Worker 613*49cdfc7eSAndroid Build Coastguard Worker $backgrounded = 1; 614*49cdfc7eSAndroid Build Coastguard Worker } elsif ( 615*49cdfc7eSAndroid Build Coastguard Worker $backgrounded 616*49cdfc7eSAndroid Build Coastguard Worker and m~ 617*49cdfc7eSAndroid Build Coastguard Worker # the exec should either be "eval"ed or a new statement 618*49cdfc7eSAndroid Build Coastguard Worker (^\s*|\beval\s*[\'\"]|(;|&&|\b(then|else))\s*) 619*49cdfc7eSAndroid Build Coastguard Worker exec\s+true(\s|\Z)~x 620*49cdfc7eSAndroid Build Coastguard Worker ) { 621*49cdfc7eSAndroid Build Coastguard Worker 622*49cdfc7eSAndroid Build Coastguard Worker $ret = $. - 1; 623*49cdfc7eSAndroid Build Coastguard Worker last; 624*49cdfc7eSAndroid Build Coastguard Worker } elsif (m~\@DPATCH\@~) { 625*49cdfc7eSAndroid Build Coastguard Worker $ret = $. - 1; 626*49cdfc7eSAndroid Build Coastguard Worker last; 627*49cdfc7eSAndroid Build Coastguard Worker } 628*49cdfc7eSAndroid Build Coastguard Worker 629*49cdfc7eSAndroid Build Coastguard Worker } 630*49cdfc7eSAndroid Build Coastguard Worker close IN; 631*49cdfc7eSAndroid Build Coastguard Worker return $ret; 632*49cdfc7eSAndroid Build Coastguard Worker} 633*49cdfc7eSAndroid Build Coastguard Worker 634*49cdfc7eSAndroid Build Coastguard Workersub init_hashes { 635*49cdfc7eSAndroid Build Coastguard Worker 636*49cdfc7eSAndroid Build Coastguard Worker %bashisms = ( 637*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)function [^<>\(\)\[\]\{\};|\s]+(\s|\(|\Z)' => 638*49cdfc7eSAndroid Build Coastguard Worker q<'function' is useless>, 639*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'select\s+\w+' => q<'select' is not POSIX>, 640*49cdfc7eSAndroid Build Coastguard Worker qr'(test|-o|-a)\s*[^\s]+\s+==\s' => q<should be 'b = a'>, 641*49cdfc7eSAndroid Build Coastguard Worker qr'\[\s+[^\]]+\s+==\s' => q<should be 'b = a'>, 642*49cdfc7eSAndroid Build Coastguard Worker qr'\s\|\&' => q<pipelining is not POSIX>, 643*49cdfc7eSAndroid Build Coastguard Worker qr'[^\\\$]\{([^\s\\\}]*?,)+[^\\\}\s]*\}' => q<brace expansion>, 644*49cdfc7eSAndroid Build Coastguard Worker qr'\{\d+\.\.\d+(?:\.\.\d+)?\}' => 645*49cdfc7eSAndroid Build Coastguard Worker q<brace expansion, {a..b[..c]}should be $(seq a [c] b)>, 646*49cdfc7eSAndroid Build Coastguard Worker qr'(?i)\{[a-z]\.\.[a-z](?:\.\.\d+)?\}' => q<brace expansion>, 647*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)\w+\[\d+\]=' => q<bash arrays, H[0]>, 648*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 649*49cdfc7eSAndroid Build Coastguard Worker . qr'read\s+(?:-[a-qs-zA-Z\d-]+)' => 650*49cdfc7eSAndroid Build Coastguard Worker q<read with option other than -r>, 651*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 652*49cdfc7eSAndroid Build Coastguard Worker . qr'read\s*(?:-\w+\s*)*(?:\".*?\"|[\'].*?[\'])?\s*(?:;|$)' => 653*49cdfc7eSAndroid Build Coastguard Worker q<read without variable>, 654*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'echo\s+(-n\s+)?-n?en?\s' => q<echo -e>, 655*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'exec\s+-[acl]' => q<exec -c/-l/-a name>, 656*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'let\s' => q<let ...>, 657*49cdfc7eSAndroid Build Coastguard Worker qr'(?<![\$\(])\(\(.*\)\)' => q<'((' should be '$(('>, 658*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)(\[|test)\s+-a' => q<test with unary -a (should be -e)>, 659*49cdfc7eSAndroid Build Coastguard Worker qr'\&>' => q<should be \>word 2\>&1>, 660*49cdfc7eSAndroid Build Coastguard Worker qr'(<\&|>\&)\s*((-|\d+)[^\s;|)}`&\\\\]|[^-\d\s]+(?<!\$)(?!\d))' => 661*49cdfc7eSAndroid Build Coastguard Worker q<should be \>word 2\>&1>, 662*49cdfc7eSAndroid Build Coastguard Worker qr'\[\[(?!:)' => 663*49cdfc7eSAndroid Build Coastguard Worker q<alternative test command ([[ foo ]] should be [ foo ])>, 664*49cdfc7eSAndroid Build Coastguard Worker qr'/dev/(tcp|udp)' => q</dev/(tcp|udp)>, 665*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'builtin\s' => q<builtin>, 666*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'caller\s' => q<caller>, 667*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'compgen\s' => q<compgen>, 668*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'complete\s' => q<complete>, 669*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'declare\s' => q<declare>, 670*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'dirs(\s|\Z)' => q<dirs>, 671*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'disown\s' => q<disown>, 672*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'enable\s' => q<enable>, 673*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'mapfile\s' => q<mapfile>, 674*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'readarray\s' => q<readarray>, 675*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'shopt(\s|\Z)' => q<shopt>, 676*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'suspend\s' => q<suspend>, 677*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'time\s' => q<time>, 678*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'type\s' => q<type>, 679*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'typeset\s' => q<typeset>, 680*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'ulimit(\s|\Z)' => q<ulimit>, 681*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'set\s+-[BHT]+' => q<set -[BHT]>, 682*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'alias\s+-p' => q<alias -p>, 683*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'unalias\s+-a' => q<unalias -a>, 684*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'local\s+-[a-zA-Z]+' => q<local -opt>, 685*49cdfc7eSAndroid Build Coastguard Worker # function '=' is special-cased due to bash arrays (think of "foo=()") 686*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s)\s*=\s*\(\s*\)\s*([\{|\(]|\Z)' => 687*49cdfc7eSAndroid Build Coastguard Worker q<function names should only contain [a-z0-9_]>, 688*49cdfc7eSAndroid Build Coastguard Workerqr'(?:^|\s)(?<func>function\s)?\s*(?:[^<>\(\)\[\]\{\};|\s]*[^<>\(\)\[\]\{\};|\s\w][^<>\(\)\[\]\{\};|\s]*)(?(<func>)(?=)|(?<!=))\s*(?(<func>)(?:\(\s*\))?|\(\s*\))\s*([\{|\(]|\Z)' 689*49cdfc7eSAndroid Build Coastguard Worker => q<function names should only contain [a-z0-9_]>, 690*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'(push|pop)d(\s|\Z)' => q<(push|pop)d>, 691*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'export\s+-[^p]' => q<export only takes -p as an option>, 692*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)[<>]\(.*?\)' => q<\<() process substitution>, 693*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'readonly\s+-[af]' => q<readonly -[af]>, 694*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'(sh|\$\{?SHELL\}?) -[rD]' => q<sh -[rD]>, 695*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'(sh|\$\{?SHELL\}?) --\w+' => q<sh --long-option>, 696*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'(sh|\$\{?SHELL\}?) [-+]O' => q<sh [-+]O>, 697*49cdfc7eSAndroid Build Coastguard Worker qr'\[\^[^]]+\]' => q<[^] should be [!]>, 698*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 699*49cdfc7eSAndroid Build Coastguard Worker . qr'printf\s+-v' => 700*49cdfc7eSAndroid Build Coastguard Worker q<'printf -v var ...' should be var='$(printf ...)'>, 701*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'coproc\s' => q<coproc>, 702*49cdfc7eSAndroid Build Coastguard Worker qr';;?&' => q<;;& and ;& special case operators>, 703*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'jobs\s' => q<jobs>, 704*49cdfc7eSAndroid Build Coastguard Worker # $LEADIN . qr'jobs\s+-[^lp]\s' => q<'jobs' with option other than -l or -p>, 705*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 706*49cdfc7eSAndroid Build Coastguard Worker . qr'command\s+(?:-[pvV]+\s+)*-(?:[pvV])*[^pvV\s]' => 707*49cdfc7eSAndroid Build Coastguard Worker q<'command' with option other than -p, -v or -V>, 708*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 709*49cdfc7eSAndroid Build Coastguard Worker . qr'setvar\s' => 710*49cdfc7eSAndroid Build Coastguard Worker q<setvar 'foo' 'bar' should be eval 'foo="'"$bar"'"'>, 711*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 712*49cdfc7eSAndroid Build Coastguard Worker . qr'trap\s+["\']?.*["\']?\s+.*(?:ERR|DEBUG|RETURN)' => 713*49cdfc7eSAndroid Build Coastguard Worker q<trap with ERR|DEBUG|RETURN>, 714*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 715*49cdfc7eSAndroid Build Coastguard Worker . qr'(?:exit|return)\s+-\d' => 716*49cdfc7eSAndroid Build Coastguard Worker q<exit|return with negative status code>, 717*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 718*49cdfc7eSAndroid Build Coastguard Worker . qr'(?:exit|return)\s+--' => 719*49cdfc7eSAndroid Build Coastguard Worker q<'exit --' should be 'exit' (idem for return)>, 720*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'hash(\s|\Z)' => q<hash>, 721*49cdfc7eSAndroid Build Coastguard Worker qr'(?:[:=\s])~(?:[+-]|[+-]?\d+)(?:[/\s]|\Z)' => 722*49cdfc7eSAndroid Build Coastguard Worker q<non-standard tilde expansion>, 723*49cdfc7eSAndroid Build Coastguard Worker ); 724*49cdfc7eSAndroid Build Coastguard Worker 725*49cdfc7eSAndroid Build Coastguard Worker %string_bashisms = ( 726*49cdfc7eSAndroid Build Coastguard Worker qr'\$\[[^][]+\]' => q<'$[' should be '$(('>, 727*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{(?:\w+|@|\*)\:(?:\d+|\$\{?\w+\}?)+(?::(?:\d+|\$\{?\w+\}?)+)?\}' 728*49cdfc7eSAndroid Build Coastguard Worker => q<${foo:3[:1]}>, 729*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{!\w+[\@*]\}' => q<${!prefix[*|@]>, 730*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{!\w+\}' => q<${!name}>, 731*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{(?:\w+|@|\*)([,^]{1,2}.*?)\}' => 732*49cdfc7eSAndroid Build Coastguard Worker q<${parm,[,][pat]} or ${parm^[^][pat]}>, 733*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{[@*]([#%]{1,2}.*?)\}' => q<${[@|*]#[#]pat} or ${[@|*]%[%]pat}>, 734*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{#[@*]\}' => q<${#@} or ${#*}>, 735*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{(?:\w+|@|\*)(/.+?){1,2}\}' => q<${parm/?/pat[/str]}>, 736*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{\#?\w+\[.+\](?:[/,:#%^].+?)?\}' => 737*49cdfc7eSAndroid Build Coastguard Worker q<bash arrays, ${name[0|*|@]}>, 738*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?RANDOM\}?\b' => q<$RANDOM>, 739*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?(OS|MACH)TYPE\}?\b' => q<$(OS|MACH)TYPE>, 740*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?HOST(TYPE|NAME)\}?\b' => q<$HOST(TYPE|NAME)>, 741*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?DIRSTACK\}?\b' => q<$DIRSTACK>, 742*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?EUID\}?\b' => q<$EUID should be "$(id -u)">, 743*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?UID\}?\b' => q<$UID should be "$(id -ru)">, 744*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?SECONDS\}?\b' => q<$SECONDS>, 745*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?BASH_[A-Z]+\}?\b' => q<$BASH_SOMETHING>, 746*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?SHELLOPTS\}?\b' => q<$SHELLOPTS>, 747*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?PIPESTATUS\}?\b' => q<$PIPESTATUS>, 748*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?SHLVL\}?\b' => q<$SHLVL>, 749*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?FUNCNAME\}?\b' => q<$FUNCNAME>, 750*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?TMOUT\}?\b' => q<$TMOUT>, 751*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)TMOUT=' => q<TMOUT=>, 752*49cdfc7eSAndroid Build Coastguard Worker qr'\$\{?TIMEFORMAT\}?\b' => q<$TIMEFORMAT>, 753*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)TIMEFORMAT=' => q<TIMEFORMAT=>, 754*49cdfc7eSAndroid Build Coastguard Worker qr'(?<![$\\])\$\{?_\}?\b' => q<$_>, 755*49cdfc7eSAndroid Build Coastguard Worker qr'(?:^|\s+)GLOBIGNORE=' => q<GLOBIGNORE=>, 756*49cdfc7eSAndroid Build Coastguard Worker qr'<<<' => q<\<\<\< here string>, 757*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 758*49cdfc7eSAndroid Build Coastguard Worker . qr'echo\s+(?:-[^e\s]+\s+)?\"[^\"]*(\\[abcEfnrtv0])+.*?[\"]' => 759*49cdfc7eSAndroid Build Coastguard Worker q<unsafe echo with backslash>, 760*49cdfc7eSAndroid Build Coastguard Worker qr'\$\(\([\s\w$*/+-]*\w\+\+.*?\)\)' => 761*49cdfc7eSAndroid Build Coastguard Worker q<'$((n++))' should be '$n; $((n=n+1))'>, 762*49cdfc7eSAndroid Build Coastguard Worker qr'\$\(\([\s\w$*/+-]*\+\+\w.*?\)\)' => 763*49cdfc7eSAndroid Build Coastguard Worker q<'$((++n))' should be '$((n=n+1))'>, 764*49cdfc7eSAndroid Build Coastguard Worker qr'\$\(\([\s\w$*/+-]*\w\-\-.*?\)\)' => 765*49cdfc7eSAndroid Build Coastguard Worker q<'$((n--))' should be '$n; $((n=n-1))'>, 766*49cdfc7eSAndroid Build Coastguard Worker qr'\$\(\([\s\w$*/+-]*\-\-\w.*?\)\)' => 767*49cdfc7eSAndroid Build Coastguard Worker q<'$((--n))' should be '$((n=n-1))'>, 768*49cdfc7eSAndroid Build Coastguard Worker qr'\$\(\([\s\w$*/+-]*\*\*.*?\)\)' => q<exponentiation is not POSIX>, 769*49cdfc7eSAndroid Build Coastguard Worker $LEADIN . qr'printf\s["\'][^"\']*?%q.+?["\']' => q<printf %q>, 770*49cdfc7eSAndroid Build Coastguard Worker ); 771*49cdfc7eSAndroid Build Coastguard Worker 772*49cdfc7eSAndroid Build Coastguard Worker %singlequote_bashisms = ( 773*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 774*49cdfc7eSAndroid Build Coastguard Worker . qr'echo\s+(?:-[^e\s]+\s+)?\'[^\']*(\\[abcEfnrtv0])+.*?[\']' => 775*49cdfc7eSAndroid Build Coastguard Worker q<unsafe echo with backslash>, 776*49cdfc7eSAndroid Build Coastguard Worker $LEADIN 777*49cdfc7eSAndroid Build Coastguard Worker . qr'source\s+[\"\']?(?:\.\/|\/|\$|[\w~.-])\S*' => 778*49cdfc7eSAndroid Build Coastguard Worker q<should be '.', not 'source'>, 779*49cdfc7eSAndroid Build Coastguard Worker ); 780*49cdfc7eSAndroid Build Coastguard Worker 781*49cdfc7eSAndroid Build Coastguard Worker if ($opt_echo) { 782*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'echo\s+-[A-Za-z]*n' } = q<echo -n>; 783*49cdfc7eSAndroid Build Coastguard Worker } 784*49cdfc7eSAndroid Build Coastguard Worker if ($opt_posix) { 785*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'local\s+\w+(\s+\W|\s*[;&|)]|$)' } 786*49cdfc7eSAndroid Build Coastguard Worker = q<local foo>; 787*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'local\s+\w+=' } = q<local foo=bar>; 788*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'local\s+\w+\s+\w+' } = q<local x y>; 789*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'((?:test|\[)\s+.+\s-[ao])\s' } = q<test -a/-o>; 790*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'kill\s+-[^sl]\w*' } = q<kill -[0-9] or -[A-Z]>; 791*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'trap\s+["\']?.*["\']?\s+.*[1-9]' } 792*49cdfc7eSAndroid Build Coastguard Worker = q<trap with signal numbers>; 793*49cdfc7eSAndroid Build Coastguard Worker } 794*49cdfc7eSAndroid Build Coastguard Worker 795*49cdfc7eSAndroid Build Coastguard Worker if ($makefile) { 796*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(\$\(|\`)\s*\<\s*([^\s\)]{2,}|[^DF])\s*(\)|\`)'} 797*49cdfc7eSAndroid Build Coastguard Worker = q<'$(\< foo)' should be '$(cat foo)'>; 798*49cdfc7eSAndroid Build Coastguard Worker } else { 799*49cdfc7eSAndroid Build Coastguard Worker $bashisms{ $LEADIN . qr'\w+\+=' } = q<should be VAR="${VAR}foo">; 800*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(\$\(|\`)\s*\<\s*\S+\s*(\)|\`)'} 801*49cdfc7eSAndroid Build Coastguard Worker = q<'$(\< foo)' should be '$(cat foo)'>; 802*49cdfc7eSAndroid Build Coastguard Worker } 803*49cdfc7eSAndroid Build Coastguard Worker 804*49cdfc7eSAndroid Build Coastguard Worker if ($opt_extra) { 805*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'\$\{?BASH\}?\b'} = q<$BASH>; 806*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)RANDOM='} = q<RANDOM=>; 807*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)(OS|MACH)TYPE='} = q<(OS|MACH)TYPE=>; 808*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)HOST(TYPE|NAME)='} = q<HOST(TYPE|NAME)=>; 809*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)DIRSTACK='} = q<DIRSTACK=>; 810*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)EUID='} = q<EUID=>; 811*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)UID='} = q<UID=>; 812*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)BASH(_[A-Z]+)?='} = q<BASH(_SOMETHING)=>; 813*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'(?:^|\s+)SHELLOPTS='} = q<SHELLOPTS=>; 814*49cdfc7eSAndroid Build Coastguard Worker $string_bashisms{qr'\$\{?POSIXLY_CORRECT\}?\b'} = q<$POSIXLY_CORRECT>; 815*49cdfc7eSAndroid Build Coastguard Worker } 816*49cdfc7eSAndroid Build Coastguard Worker} 817