@@ -67,6 +67,7 @@ sub new {
67
67
bless {
68
68
parser => $parser ,
69
69
buff => $s ,
70
+ lineno => 1,
70
71
heretags => []
71
72
} => $class ;
72
73
}
@@ -97,7 +98,9 @@ sub scan_op {
97
98
sub scan_sqstring {
98
99
my $self = shift @_ ;
99
100
${$self -> {buff }} =~ / \G ([^']*'|.*\z )/sgc ;
100
- return " '" . $1 ;
101
+ my $s = $1 ;
102
+ $self -> {lineno } += () = $s =~ / \n /sg ;
103
+ return " '" . $s ;
101
104
}
102
105
103
106
sub scan_dqstring {
@@ -115,14 +118,15 @@ sub scan_dqstring {
115
118
if ($c eq ' \\ ' ) {
116
119
$s .= ' \\ ' , last unless $$b =~ / \G (.)/sgc ;
117
120
$c = $1 ;
118
- next if $c eq " \n " ; # line splice
121
+ $self -> { lineno }++, next if $c eq " \n " ; # line splice
119
122
# backslash escapes only $, `, ", \ in dq-string
120
123
$s .= ' \\ ' unless $c =~ / ^[\$ `"\\ ]$ / ;
121
124
$s .= $c ;
122
125
next ;
123
126
}
124
127
die (" internal error scanning dq-string '$c '\n " );
125
128
}
129
+ $self -> {lineno } += () = $s =~ / \n /sg ;
126
130
return $s ;
127
131
}
128
132
@@ -137,6 +141,7 @@ sub scan_balanced {
137
141
$depth --;
138
142
last if $depth == 0;
139
143
}
144
+ $self -> {lineno } += () = $s =~ / \n /sg ;
140
145
return $s ;
141
146
}
142
147
@@ -163,20 +168,24 @@ sub swallow_heredocs {
163
168
my $b = $self -> {buff };
164
169
my $tags = $self -> {heretags };
165
170
while (my $tag = shift @$tags ) {
171
+ my $start = pos ($$b );
166
172
my $indent = $tag =~ s / ^\t // ? ' \\ s*' : ' ' ;
167
173
$$b =~ / (?:\G |\n )$indent \Q $tag \E (?:\n |\z )/gc ;
174
+ my $body = substr ($$b , $start , pos ($$b ) - $start );
175
+ $self -> {lineno } += () = $body =~ / \n /sg ;
168
176
}
169
177
}
170
178
171
179
sub scan_token {
172
180
my $self = shift @_ ;
173
181
my $b = $self -> {buff };
174
182
my $token = ' ' ;
175
- my $start ;
183
+ my ( $start , $startln ) ;
176
184
RESTART:
185
+ $startln = $self -> {lineno };
177
186
$$b =~ / \G [ \t ]+/gc ; # skip whitespace (but not newline)
178
187
$start = pos ($$b ) || 0;
179
- return [" \n " , $start , pos ($$b )] if $$b =~ / \G #[^\n ]*(?:\n |\z )/gc ; # comment
188
+ $self -> { lineno }++, return [" \n " , $start , pos ($$b ), $startln , $startln ] if $$b =~ / \G #[^\n ]*(?:\n |\z )/gc ; # comment
180
189
while (1) {
181
190
# slurp up non-special characters
182
191
$token .= $1 if $$b =~ / \G ([^\\ ;&|<>(){}'"\$\s ]+)/gc ;
@@ -188,20 +197,20 @@ sub scan_token {
188
197
$token .= $self -> scan_sqstring(), next if $c eq " '" ;
189
198
$token .= $self -> scan_dqstring(), next if $c eq ' "' ;
190
199
$token .= $c . $self -> scan_dollar(), next if $c eq ' $' ;
191
- $self -> swallow_heredocs(), $token = $c , last if $c eq " \n " ;
200
+ $self -> { lineno }++, $self -> swallow_heredocs(), $token = $c , last if $c eq " \n " ;
192
201
$token = $self -> scan_op($c ), last if $c =~ / ^[;&|<>]$ / ;
193
202
$token = $c , last if $c =~ / ^[(){}]$ / ;
194
203
if ($c eq ' \\ ' ) {
195
204
$token .= ' \\ ' , last unless $$b =~ / \G (.)/sgc ;
196
205
$c = $1 ;
197
- next if $c eq " \n " && length ($token ); # line splice
198
- goto RESTART if $c eq " \n " ; # line splice
206
+ $self -> { lineno }++, next if $c eq " \n " && length ($token ); # line splice
207
+ $self -> { lineno }++, goto RESTART if $c eq " \n " ; # line splice
199
208
$token .= ' \\ ' . $c ;
200
209
next ;
201
210
}
202
211
die (" internal error scanning character '$c '\n " );
203
212
}
204
- return length ($token ) ? [$token , $start , pos ($$b )] : undef ;
213
+ return length ($token ) ? [$token , $start , pos ($$b ), $startln , $self -> { lineno } ] : undef ;
205
214
}
206
215
207
216
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
@@ -605,6 +614,7 @@ sub check_test {
605
614
my $problems = $parser -> {problems };
606
615
return unless $emit_all || @$problems ;
607
616
my $c = main::fd_colors(1);
617
+ my $lineno = $_ [1]-> [3];
608
618
my $start = 0;
609
619
my $checked = ' ' ;
610
620
for (sort {$a -> [1]-> [2] <=> $b -> [1]-> [2]} @$problems ) {
@@ -614,10 +624,12 @@ sub check_test {
614
624
$start = $pos ;
615
625
}
616
626
$checked .= substr ($body , $start );
617
- $checked =~ s / ^\n // ;
627
+ $checked =~ s / ^/ $lineno ++ . ' '/ mge ;
628
+ $checked =~ s / ^\d + \n // ;
618
629
$checked =~ s / (\s ) \? !/ $1 ?!/ mg ;
619
630
$checked =~ s /\? ! (\s )/ ?!$1 / mg ;
620
631
$checked =~ s / (\? ![^?]+\? !)/ $c ->{rev}$c ->{red}$1 $c ->{reset}/ mg ;
632
+ $checked =~ s / ^\d +/ $c ->{dim}$& $c ->{reset}/ mg ;
621
633
$checked .= " \n " unless $checked =~ / \n $ / ;
622
634
push (@{$self -> {output }}, " $c ->{blue}# chainlint: $title$c ->{reset}\n $checked " );
623
635
}
@@ -649,25 +661,39 @@ package main;
649
661
# thread and ignore %ENV changes in subthreads.
650
662
$ENV {TERM } = $ENV {USER_TERM } if $ENV {USER_TERM };
651
663
652
- my @NOCOLORS = (bold => ' ' , rev => ' ' , reset => ' ' , blue => ' ' , green => ' ' , red => ' ' );
664
+ my @NOCOLORS = (bold => ' ' , rev => ' ' , dim => ' ' , reset => ' ' , blue => ' ' , green => ' ' , red => ' ' );
653
665
my %COLORS = ();
654
666
sub get_colors {
655
667
return \%COLORS if %COLORS ;
656
- if (exists ($ENV {NO_COLOR }) ||
657
- system (" tput sgr0 >/dev/null 2>&1" ) != 0 ||
658
- system (" tput bold >/dev/null 2>&1" ) != 0 ||
659
- system (" tput rev >/dev/null 2>&1" ) != 0 ||
660
- system (" tput setaf 1 >/dev/null 2>&1" ) != 0) {
668
+ if (exists ($ENV {NO_COLOR })) {
661
669
%COLORS = @NOCOLORS ;
662
670
return \%COLORS ;
663
671
}
664
- %COLORS = (bold => ` tput bold` ,
665
- rev => ` tput rev` ,
666
- reset => ` tput sgr0` ,
667
- blue => ` tput setaf 4` ,
668
- green => ` tput setaf 2` ,
669
- red => ` tput setaf 1` );
670
- chomp (%COLORS );
672
+ if ($ENV {TERM } =~ / xterm|xterm-\d +color|xterm-new|xterm-direct|nsterm|nsterm-\d +color|nsterm-direct/ ) {
673
+ %COLORS = (bold => " \e [1m" ,
674
+ rev => " \e [7m" ,
675
+ dim => " \e [2m" ,
676
+ reset => " \e [0m" ,
677
+ blue => " \e [34m" ,
678
+ green => " \e [32m" ,
679
+ red => " \e [31m" );
680
+ return \%COLORS ;
681
+ }
682
+ if (system (" tput sgr0 >/dev/null 2>&1" ) == 0 &&
683
+ system (" tput bold >/dev/null 2>&1" ) == 0 &&
684
+ system (" tput rev >/dev/null 2>&1" ) == 0 &&
685
+ system (" tput dim >/dev/null 2>&1" ) == 0 &&
686
+ system (" tput setaf 1 >/dev/null 2>&1" ) == 0) {
687
+ %COLORS = (bold => ` tput bold` ,
688
+ rev => ` tput rev` ,
689
+ dim => ` tput dim` ,
690
+ reset => ` tput sgr0` ,
691
+ blue => ` tput setaf 4` ,
692
+ green => ` tput setaf 2` ,
693
+ red => ` tput setaf 1` );
694
+ return \%COLORS ;
695
+ }
696
+ %COLORS = @NOCOLORS ;
671
697
return \%COLORS ;
672
698
}
673
699
0 commit comments