From 33839117e10bba8283971166f0cd2ea19aefd6c5 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 16 Oct 2012 04:57:36 +0200 Subject: [PATCH] factor out parser code into a standalone, reusable module ... and add a demo app using it. this one was used to find C files that define functions that are specified in headers in an attempt to find and disable libc replacement functions in gnulib and libiberty. --- CParser-test.pl | 15 +++++ CParser.pm | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++ find-in-headers.pl | 29 +++++++++ 3 files changed, 229 insertions(+) create mode 100755 CParser-test.pl create mode 100644 CParser.pm create mode 100755 find-in-headers.pl diff --git a/CParser-test.pl b/CParser-test.pl new file mode 100755 index 0000000..1772ae7 --- /dev/null +++ b/CParser-test.pl @@ -0,0 +1,15 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Basename; +use Cwd 'abs_path'; +use lib dirname(abs_path($0)); +use CParser; +use Data::Dump qw(dump); + +my $file = $ARGV[0] or die ("need filename"); + +my $p = CParser->new($file); +$p->parse(); +dump($p); \ No newline at end of file diff --git a/CParser.pm b/CParser.pm new file mode 100644 index 0000000..395c266 --- /dev/null +++ b/CParser.pm @@ -0,0 +1,185 @@ +package CParser; + +use strict; +use warnings; + +sub file_ext { + my $x = shift; + my $l = length($x); + $l-- while($l && substr($x, $l, 1) ne "."); + return substr($x, $l) if($l); + return ""; +} + + +sub new { + my ($pkg, @args) = @_; + die("need C file name as arg") if(scalar(@args) != 1); + my $isheader = file_ext($args[0]) eq ".h"; + my $self = { + cfile => $args[0], + isheader => $isheader, + printerrors => 0, + + openbraces => 0, + incomment => 0, + line => "", + + includes => [], + statics => [], + extern => [], + typedefs_macros => [], + funcs => undef, + }; + bless $self, $pkg; +} + +sub addfunc { + my ($self, $funcname, $code) = @_; + $self->{funcs}->{$funcname} = $code; +} + +sub handlesub { + my $self = shift; + my $_ = shift; + my $name = ""; + my $wasstatic = 0; + while(!$name) { + my $x = 0; + $x++ while(substr($_, $x, 1) !~ /\s/); + my $word = substr($_, 0, $x); + my $extern = 1 if($word eq "extern"); + if($word eq "static" || $word eq "inline") { + $_ = substr($_, $x); + s/^\s+//; + $wasstatic = 1 unless $extern; + next; + } else { + if(/(.*?)([\w_]+)\s*\(([\w\s_,\*\[\]\.\(\)]*?)\)\s*\{/ + || ($self->{isheader} && + /(.*?)([\w_]+)\s*\((.*?)\)\s*;/) + ) { + $name = $2; + my $decl = $1 . $name . "(" . $3 . ");"; + push @{$self->{statics}}, $decl if($wasstatic); + #print $name , "\n" if $wasstatic; + $self->addfunc($name, $_); + #print "function $name\n$_"; + } elsif($self->{isheader}) { + return; + } else { + warn "ERROR: $_\n" if $self->{printerrors}; + return; + } + } + } +} + +sub scanbraces { + my $self = shift; + my $shit = shift; + my @chars = split //, $shit; + for my $c(@chars) { + if ($c eq "{") { + $self->{openbraces}++; + } elsif($c eq "}") { + $self->{openbraces}--; + } + } +} + +sub parseline { + my $self = shift; + $_ = shift; + #printf "parse line: %s, is header: %d\n", $_, $self->{isheader}; + #print "PL: length line: ". length($line) . "\n"; + return unless defined $_; + return if $_ eq ""; + if(/^\s*#\s*(\w+)/) { + my $kw = $1; + if($kw eq "if" || $kw eq "ifdef" || $kw eq "elif" || $kw eq "else" || $kw eq "endif") { + push @{$self->{typedefs_macros}}, $_; + return; + } +# $self->{line} = "" if $self->{isheader}; +# return; + } + if($_ =~ /extern \"C\"/) { +# $self->{line} = ""; + return; + } + #$self->{line} .= $_ . "\n" if(!$self->{openbraces} || $self->{line} ne ""); + #printf "%d\n", $self->{openbraces}; + $self->{line} .= $_ . "\n" if(!$self->{openbraces} || $self->{line} ne ""); + $self->scanbraces($_) unless $self->{line} =~ /^\s*#define/; + + #print "$_ , line is $self->{line}\n"; + + if($self->{line} ne "" && !$self->{openbraces}) { + #print "A $self->{line}\n"; + if($self->{line} =~ /^\s*#/) { + if($self->{line} =~ /\\\s*$/) { + + } else { + push @{$self->{typedefs_macros}}, $self->{line}; + $self->{line} = "" + } + } elsif($self->{line} =~ /([;\}]{1})\s*\n*$/) { + if($1 eq ";") { + #print $self->{line}; + if ($self->{line} =~ /=/ || $self->{line} =~ /^\s*static[\s\n]+/) { + #print "extern!\n"; + $self->{line} =~ s/^\s*static\s*//; + push @{$self->{extern}}, $self->{line}; + } elsif($self->{isheader}) { + $self->handlesub($self->{line}); + } else { + push @{$self->{typedefs_macros}}, $self->{line}; + } + $self->{line} = ""; + return; + } + $self->handlesub($self->{line}); + $self->{line} = ""; + } + } #elsif($self->{isheader} && !$self->{openbraces} && $self->{line} eq "" && + # /(extern\w){0, 1} +} + +sub parse { + + my $self = shift; + + my $f; + open($f, "<", $self->{cfile}); + + while(<$f>) { + + # print; + chomp; + # print "$openbraces, $incomment\n"; + if (/^\s*#\s*include\s+[<\"]{1}[\w_\-\/\.]+[>\"]{1}/) { + push @{$self->{includes}}, $_; + } else { + next if(/^\s*$/); #skip empty lines + next if(/^\s*\/\//); #skip one line comments. + # normal source code line. + if (!$self->{incomment} && /(.*?)\/\*(.*?)$/) { + $self->parseline($1); + my $rest = $2; + $self->{incomment} = 1 unless $rest =~ /\*\//; + } elsif($self->{incomment}) { + if(/\*\/(.*?)$/) { + $self->parseline($2); + $self->{incomment} = 0; + } + } else { + $self->parseline($_); + } + } + } + close $f; + +} + +1; \ No newline at end of file diff --git a/find-in-headers.pl b/find-in-headers.pl new file mode 100755 index 0000000..d9e575e --- /dev/null +++ b/find-in-headers.pl @@ -0,0 +1,29 @@ +#!/usr/bin/env perl +use strict; +use warnings; + +use File::Basename; +use Cwd 'abs_path'; +use lib dirname(abs_path($0)); +use CParser; +use Data::Dump qw(dump); + +my $file = $ARGV[0] or die ("need filename"); +my $dir = $ARGV[1] or die ("need dir with headers as second arg"); + +my $p = CParser->new($file); +$p->parse(); + +my @hdrs=`find $dir -name '*.h'`; +for(@hdrs) { + chomp; + #print "processing $_\n"; + #next unless /unistd/; + my $h = CParser->new($_); + $h->parse(); + #dump $h; + #exit; + for(keys %{$p->{funcs}}) { + print "$_ defined in $file and $h->{cfile}\n" if(defined($h->{funcs}->{$_})); + } +} -- 2.11.4.GIT