our
@ISA
=
qw/Scanner/
;
BEGIN {
*ACT
= \
&TextSubs::CONST0
;
*PREV_ACT
= \
&TextSubs::CONST1
;
*EXPR
= \
&TextSubs::CONST2
;
*UNKNOWN
= \
&TextSubs::CONST3
;
}
our
$dont_scan_hook
;
sub
dont_scan {
my
(
$guard
,
$dont_scan
) =
FileInfo::build_info_string
$_
[1],
qw(GUARD DONT_SCAN)
;
return
1
if
defined
$guard
&&
exists
$_
[0]{VARS}{
$guard
};
if
(
$dont_scan
) {
$_
[0]{VARS}{
$guard
} =
''
if
defined
$guard
;
return
1;
}
if
(
$dont_scan_hook
&&
&$dont_scan_hook
(
$_
[2] )) {
::
log
SCAN_C_NOT
=>
$_
[1]
if
$::log_level;
return
1;
}
&Scanner::dont_scan
;
}
sub
push_scope {
die
"@{$_[1]}"
unless
$_
[1][ACT]==0 ||
$_
[1][PREV_ACT]==1 ||
$_
[1][ACT]==
$_
[1][PREV_ACT];
my
$act
=
$_
[0]{ACTIVE};
if
(
$act
== 1) {
$act
=
$_
[1][ACT];
}
elsif
(
$_
[1][ACT] == 0) {
$act
= 0;
}
$_
[0]{ACTIVE} =
$_
[1][ACT] =
$act
;
$_
[1][EXPR] ||=
$_
[0]{SCOPES}[-1][EXPR]
if
$act
== UNKNOWN;
die
"@{$_[1]}"
if
(
$_
[1][ACT] == UNKNOWN ||
$_
[1][PREV_ACT] == UNKNOWN) && !
defined
(
$_
[1][EXPR]);
push
(@{
$_
[0]{SCOPES}},
$_
[1]);
}
sub
pop_scope {
my
$self
=
$_
[0];
my
$result
=
$self
->SUPER::pop_scope();
$self
->{ACTIVE}=
$self
->{SCOPES}[-1][ACT];
$result
;
}
sub
reset
{
my
$self
=
shift
;
$self
->SUPER::
reset
(
@_
);
unless
(
@_
> 0) {
$self
->{SCOPES}=[[1, 0]];
}
$self
->{ACTIVE}=
$self
->{SCOPES}[-1][ACT];
}
sub
new {
my
$class
=
shift
;
my
$self
=
$class
->SUPER::new(
@_
);
$self
->
reset
;
$self
;
}
sub
get_bad_expr {
$_
[0]{SCOPES}[-1][EXPR];
}
sub
update_scope {
my
(
$self
,
$else
,
$line
,
$expr
)=
@_
;
my
@state
= @{
$self
->{SCOPES}[-1]};
if
(
$else
) {
@state
= @{
$self
->pop_scope};
$state
[ACT] =
$state
[PREV_ACT];
$state
[ACT] = !
$state
[ACT]
unless
$state
[ACT] == UNKNOWN;
}
else
{
die
unless
defined
(
$expr
);
$state
[PREV_ACT] = 0;
}
@state
= (0,0)
unless
$self
->{ACTIVE};
if
(
$state
[ACT] ) {
my
$go
=
defined
(
$expr
) ?
&eval_condition
(
$self
->expand_defines(
$expr
)) : 1;
$state
[ACT] =
$go
unless
$go
==1 &&
$state
[PREV_ACT];
$state
[EXPR] =
$line
.
"\"$expr\""
if
$go
== UNKNOWN;
die
if
$state
[PREV_ACT] == 1;
$state
[PREV_ACT] =
$go
unless
$go
== 0;
}
$self
->push_scope(\
@state
);
}
sub
get_macro {
if
(/\G(\`*)([a-z_]\w*)/igc) {
return
($1,
""
, $2);
}
return
;
}
sub
expand_defines {
my
(
$self
,
$expr
,
$vis
) =
@_
;
$expr
=~ s/
defined
\s*(\(?)\s*(\w+)/$1 . (
defined
(
$self
->get_var( $2 )) ? 1 : 0)/eg;
return
$self
->expand_macros(
$expr
,
$vis
);
}
sub
eval_condition {
my
$cond
=
$_
[0];
$cond
=~
tr
/`//d;
$cond
=~ s/\b([_a-z]\w*)/ 0 /ig;
my
$funny
;
local
$SIG
{__WARN__} =
sub
{
$funny
= 1 };
$cond
=
eval
$cond
;
return
UNKNOWN
if
$@ ||
$funny
;
$cond
? 1 : 0;
}
sub
expand_macros {
my
(
$self
,
$expr
,
$visited
) =
@_
;
$visited
||= {};
local
$_
=
$expr
;
$expr
=
''
;
pos
(
$_
) = 0;
while
(1) {
last
if
length
(
$_
) <=
pos
(
$_
);
if
(/\G(\\.)/sgc) {
$expr
.= $1;
}
elsif
(/\G(\
"(?:[^"
]*\\\
")*[^"
]*\")/gc) {
$expr
.= $1;
}
elsif
(/\G
'((\\)(?:\d+|.)|.)'
/gc) {
$expr
.=
ord
( $2 ?
eval
"\"$1\""
: $1 );
}
elsif
(/\G(\d+(?:\.\d+)?)[luf]?/gic) {
$expr
.= $1;
}
elsif
(
my
(
$prefix
,
$key_prefix
,
$key
) =
$self
->get_macro) {
$expr
.=
$prefix
;
my
$x
=
$self
->get_var(
$key
)
unless
$visited
->{
$key
};
if
(
defined
$x
) {
my
%v
=
%$visited
;
$v
{
$key
}=1;
my
$pos
=
pos
(
$_
);
$expr
.=
$self
->expand_defines(
$x
, \
%v
);
pos
(
$_
) =
$pos
;
}
else
{
$expr
.=
$key_prefix
.
$key
;
}
}
else
{
/\G([^\w\\
"`']+)/igc or die "
$_
\n";
$expr
.= $1;
}
}
$expr
;
}
sub
get_directive {
if
(s/^\s*\
return
$1;
}
return
;
}
*other_directive
= \
&TextSubs::CONST0
;
sub
xscan_file {
my
(
$self
,
$cp
,
undef
,
$finfo
,
$conditional
,
$fh
)=
@_
;
my
$absname
= absolute_filename(
$finfo
);
my
(
$go
,
$pending_comment
,
$continued_comment
);
my
$guard_scope
;
my
$guarded
;
my
$guard
;
my
$scanworthy
;
my
$line_so_far
=
''
;
LINE:
while
(<
$fh
>) {
s/\r*$//;
my
$continuation
= s/\\\s*$//;
if
(
$continued_comment
) {
undef
$continued_comment
unless
$continuation
;
next
LINE;
}
if
(
$pending_comment
) {
if
(s!^.*?\*/!!) {
undef
$pending_comment
;
}
else
{
next
LINE;
}
}
s!/(/.*|\*.*?\*/)!
$continued_comment
=1
if
$continuation
&&
ord
$1 ==
ord
'/'
;
' '
!eg;
if
( s!/\*.*! ! and
$pending_comment
= 1 or
$continuation
) {
chomp
;
$line_so_far
.=
$_
;
next
LINE;
}
else
{
$_
=
$line_so_far
.
$_
;
$line_so_far
=
''
;
}
$go
=
$self
->{ACTIVE};
if
(
my
$directive
=
$self
->get_directive ) {
s/\s*$//;
my
$ret
=
$self
->other_directive(
$cp
,
$finfo
,
$conditional
,
$directive
, \
$scanworthy
);
defined
$ret
or
return
undef
;
if
(
$ret
) {
warn
"$absname:$.: Ignoring trailing cruft \"$_\"\n"
if
$_
;
}
elsif
(
$directive
eq
'include'
) {
$_
=
$self
->expand_macros(
$_
)
if
$conditional
;
my
$userinc
= s/^\
"([^"
]*)\"//;
if
(
$userinc
|| s/^\<([^>]*)\>//) {
local
$_
;
warn
"$absname:$.: File $1 included because condition "
,
$self
->get_bad_expr,
" cannot be evaluated\n"
if
(
$go
== UNKNOWN);
$self
->include(
$cp
,
$userinc
?
'user'
:
'sys'
, $1,
$finfo
)
or
return
undef
;
}
$scanworthy
= 1;
warn
"$absname:$.: Ignoring trailing cruft \"$_\"\n"
if
/\S/;
}
elsif
(
$conditional
) {
if
(
$go
&&
$directive
eq
'define'
&& /^(\w+)\s*(.*)/ ) {
$self
->set_var($1, $2);
if
(
defined
$guard_scope
&&
$guard_scope
==
$self
->{SCOPES}[-1] &&
$guard
eq $1 ) {
$guarded
= 1;
next
LINE;
}
$scanworthy
= 1;
}
elsif
(
$go
&&
$directive
eq
'undef'
&& /^\w+$/ ) {
$self
->set_var(
$_
,
undef
);
$scanworthy
= 1;
}
elsif
( (
my
$no
=
$directive
eq
'ifndef'
) ||
$directive
eq
'ifdef'
and /^\w+$/ ) {
my
$def
=
defined
$self
->get_var(
$_
);
$go
=
$no
? !
$def
:
$def
;
$go
=
$go
? 1 : 0;
$self
->push_scope([
$go
,
$go
]);
if
(
$no
&& !
defined
$guarded
&& !
defined
$guard
) {
$guard_scope
=
$self
->{SCOPES}[-1];
$guard
=
$_
;
next
LINE;
}
}
elsif
(
$directive
eq
'else'
) {
$self
->update_scope(1);
warn
"$absname:$.: Ignoring trailing cruft \"$_\"\n"
if
$_
;
}
elsif
(
$directive
eq
'endif'
) {
$guarded
=
$guard_scope
= 0
if
$guarded
&&
$guard_scope
==
$self
->{SCOPES}[-1];
$self
->pop_scope();
warn
"$absname:$.: Ignoring trailing cruft \"$_\"\n"
if
$_
;
next
LINE;
}
elsif
(
$directive
eq
'if'
) {
my
$maybe_guard
= $1
if
!
defined
$guarded
&& !
defined
$guard
&& /^!\s
*defined
\s*\(?\s*(\w+)\s*\)?$/;
$self
->update_scope(
undef
,
"$absname:$.:"
,
$_
);
if
(
$maybe_guard
) {
$guard_scope
=
$self
->{SCOPES}[-1];
$guard
=
$maybe_guard
;
next
LINE;
}
}
elsif
(
$directive
eq
'elif'
) {
$self
->update_scope(1,
"$absname:$.:"
,
$_
);
}
elsif
( !
$scanworthy
&& !
$go
&& (
$directive
eq
'define'
||
$directive
eq
'undef'
) ) {
$scanworthy
= 1;
}
}
}
$scanworthy
||= (
defined
$guard
&&
$guard
ne
''
),
$guard
=
''
if
defined
&& !
$guarded
&& (!
defined
$guard
||
$guard
ne
''
) && /\S/;
}
my
@build_info
;
@build_info
= (
GUARD
=>
$guard
)
if
!
$guarded
&&
defined
$guard
&&
$guard
ne
''
;
push
@build_info
,
DONT_SCAN
=> 1
if
$conditional
&& !
$scanworthy
;
FileInfo::set_build_info_string
$finfo
,
@build_info
if
@build_info
;
1;
}
1;