@@ -67,6 +67,7 @@ sub new {
6767 bless {
6868 parser => $parser ,
6969 buff => $s ,
70+ lineno => 1,
7071 heretags => []
7172 } => $class ;
7273}
@@ -97,7 +98,9 @@ sub scan_op {
9798sub scan_sqstring {
9899 my $self = shift @_ ;
99100 ${$self -> {buff }} =~ / \G ([^']*'|.*\z )/sgc ;
100- return " '" . $1 ;
101+ my $s = $1 ;
102+ $self -> {lineno } += () = $s =~ / \n /sg ;
103+ return " '" . $s ;
101104}
102105
103106sub scan_dqstring {
@@ -115,14 +118,15 @@ sub scan_dqstring {
115118 if ($c eq ' \\ ' ) {
116119 $s .= ' \\ ' , last unless $$b =~ / \G (.)/sgc ;
117120 $c = $1 ;
118- next if $c eq " \n " ; # line splice
121+ $self -> { lineno }++, next if $c eq " \n " ; # line splice
119122 # backslash escapes only $, `, ", \ in dq-string
120123 $s .= ' \\ ' unless $c =~ / ^[\$ `"\\ ]$ / ;
121124 $s .= $c ;
122125 next ;
123126 }
124127 die (" internal error scanning dq-string '$c '\n " );
125128 }
129+ $self -> {lineno } += () = $s =~ / \n /sg ;
126130 return $s ;
127131}
128132
@@ -137,6 +141,7 @@ sub scan_balanced {
137141 $depth --;
138142 last if $depth == 0;
139143 }
144+ $self -> {lineno } += () = $s =~ / \n /sg ;
140145 return $s ;
141146}
142147
@@ -163,20 +168,24 @@ sub swallow_heredocs {
163168 my $b = $self -> {buff };
164169 my $tags = $self -> {heretags };
165170 while (my $tag = shift @$tags ) {
171+ my $start = pos ($$b );
166172 my $indent = $tag =~ s / ^\t // ? ' \\ s*' : ' ' ;
167173 $$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 ;
168176 }
169177}
170178
171179sub scan_token {
172180 my $self = shift @_ ;
173181 my $b = $self -> {buff };
174182 my $token = ' ' ;
175- my $start ;
183+ my ( $start , $startln ) ;
176184RESTART:
185+ $startln = $self -> {lineno };
177186 $$b =~ / \G [ \t ]+/gc ; # skip whitespace (but not newline)
178187 $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
180189 while (1) {
181190 # slurp up non-special characters
182191 $token .= $1 if $$b =~ / \G ([^\\ ;&|<>(){}'"\$\s ]+)/gc ;
@@ -188,20 +197,20 @@ sub scan_token {
188197 $token .= $self -> scan_sqstring(), next if $c eq " '" ;
189198 $token .= $self -> scan_dqstring(), next if $c eq ' "' ;
190199 $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 " ;
192201 $token = $self -> scan_op($c ), last if $c =~ / ^[;&|<>]$ / ;
193202 $token = $c , last if $c =~ / ^[(){}]$ / ;
194203 if ($c eq ' \\ ' ) {
195204 $token .= ' \\ ' , last unless $$b =~ / \G (.)/sgc ;
196205 $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
199208 $token .= ' \\ ' . $c ;
200209 next ;
201210 }
202211 die (" internal error scanning character '$c '\n " );
203212 }
204- return length ($token ) ? [$token , $start , pos ($$b )] : undef ;
213+ return length ($token ) ? [$token , $start , pos ($$b ), $startln , $self -> { lineno } ] : undef ;
205214}
206215
207216# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
@@ -605,6 +614,7 @@ sub check_test {
605614 my $problems = $parser -> {problems };
606615 return unless $emit_all || @$problems ;
607616 my $c = main::fd_colors(1);
617+ my $lineno = $_ [1]-> [3];
608618 my $start = 0;
609619 my $checked = ' ' ;
610620 for (sort {$a -> [1]-> [2] <=> $b -> [1]-> [2]} @$problems ) {
@@ -614,10 +624,12 @@ sub check_test {
614624 $start = $pos ;
615625 }
616626 $checked .= substr ($body , $start );
617- $checked =~ s / ^\n // ;
627+ $checked =~ s / ^/ $lineno ++ . ' '/ mge ;
628+ $checked =~ s / ^\d + \n // ;
618629 $checked =~ s / (\s ) \? !/ $1 ?!/ mg ;
619630 $checked =~ s /\? ! (\s )/ ?!$1 / mg ;
620631 $checked =~ s / (\? ![^?]+\? !)/ $c ->{rev}$c ->{red}$1 $c ->{reset}/ mg ;
632+ $checked =~ s / ^\d +/ $c ->{dim}$& $c ->{reset}/ mg ;
621633 $checked .= " \n " unless $checked =~ / \n $ / ;
622634 push (@{$self -> {output }}, " $c ->{blue}# chainlint: $title$c ->{reset}\n $checked " );
623635}
@@ -649,25 +661,39 @@ package main;
649661# thread and ignore %ENV changes in subthreads.
650662$ENV {TERM } = $ENV {USER_TERM } if $ENV {USER_TERM };
651663
652- my @NOCOLORS = (bold => ' ' , rev => ' ' , reset => ' ' , blue => ' ' , green => ' ' , red => ' ' );
664+ my @NOCOLORS = (bold => ' ' , rev => ' ' , dim => ' ' , reset => ' ' , blue => ' ' , green => ' ' , red => ' ' );
653665my %COLORS = ();
654666sub get_colors {
655667 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 })) {
661669 %COLORS = @NOCOLORS ;
662670 return \%COLORS ;
663671 }
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 ;
671697 return \%COLORS ;
672698}
673699
0 commit comments