#!/usr/bin/perl -w #< find-incs.pl - given an input file, find the #include <"..."> files, and what they include # 20/05/2012 - Further enhancements # 2011-10-29 - Make adding system includes an option, and search local found includes, for includes use strict; use warnings; use File::Basename; # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] ) use Cwd; use File::stat; my $os = $^O; my $perl_dir = '/home/geoff/bin'; my $PATH_SEP = '/'; my $temp_dir = '/tmp'; if ($os =~ /win/i) { $perl_dir = 'C:\GTools\perl'; $temp_dir = $perl_dir; $PATH_SEP = "\\"; } unshift(@INC, $perl_dir); require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n"; # log file stuff our ($LF); my $pgmname = $0; if ($pgmname =~ /(\\|\/)/) { my @tmpsp = split(/(\\|\/)/,$pgmname); $pgmname = $tmpsp[-1]; } my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt"; open_log($outfile); # user variables my $VERS = "0.0.2 2012-05-20"; # my $VERS = "0.0.1 2011-06-13"; my @in_files = (); my @warnings = (); my $load_log = 0; my $verbosity = 0; my $total_dirs = 0; my $total_files = 0; my $total_lines = 0; my $total_bytes = 0; # OPTIONS my $search_system = 0; # search for include my $search_local = 1; # local includes are also searched my @std_include_dirs = qw( /usr/include/ /usr/include/linux/ usr/local/include/ /usr/include/c++/4.4/ ); my @usr_include_dirs = (); my %shtdirs = (); # debug my $dbg01 = 0; # show each directory processed... my $dbg02 = 0; # show sub-directory processed... my %done = (); my %missed = (); my @not_found = (); sub process_dir($$$); sub process_file($$); if ($os =~ /WIN/i) { @std_include_dirs = (); push(@std_include_dirs,"C:\\Program Files\\Microsoft Visual Studio 8\\VC\\include\\"); push(@std_include_dirs,"C:\\Program Files\\Microsoft Platform SDK for Windows Server 2003 R2\\Include\\"); %shtdirs = ( "C:\\Program Files\\Microsoft Visual Studio 8\\VC\\include\\" => "MSVC\\include\\", "C:\\Program Files\\Microsoft Platform SDK for Windows Server 2003 R2\\Include\\" => "PSDK\\Include\\" ); } sub VERB1() { return $verbosity >= 1; } sub VERB2() { return $verbosity >= 2; } sub VERB5() { return $verbosity >= 5; } sub VERB9() { return $verbosity >= 9; } sub prtw($) { my ($tx) = shift; $tx =~ s/\n$//; prt("$tx\n"); push(@warnings,$tx); } sub show_warnings() { if (@warnings) { prt( "\nGot ".scalar @warnings." WARNINGS...\n" ); foreach my $itm (@warnings) { prt("$itm\n"); } prt("\n"); } else { #prt( "\nNo warnings issued.\n\n" ); } } sub pgm_exit($$) { my ($val,$msg) = @_; show_warnings(); prt($msg) if (length($msg)); close_log($outfile,$load_log); exit($val); } sub process_dir($$$) { my ($dir,$ra,$lev) = @_; my @dirs = (); my ($file,$ff,$cnt); if (opendir( DIR, $dir )) { $total_dirs++; prt("Reading [$dir]...\n"); my @files = readdir(DIR); closedir(DIR); $dir .= '/' if !($dir =~ /\/$/); foreach $file (@files) { next if ($file eq '.'); next if ($file eq '..'); $ff = $dir.$file; if ( -d $ff ) { push(@dirs,$ff); } elsif ( -f $ff ) { push(@{$ra},$ff); $total_files++; } else { # a link or .... } } } else { prtw("WARNING: Unable to open folder [$dir]... $!...\n"); } if (@dirs) { my ($cnt,$itm); $cnt = scalar @dirs; prt( "[$dbg02] $lev: Found $cnt subs in [$dir]...\n" ) if ($dbg02); foreach $itm (@dirs) { process_dir($itm,$ra,($lev + 1)); } } if ($lev == 0) { $cnt = scalar @{$ra}; prt("Found $cnt file items, in scan of [$dir]\n"); } return $ra; } sub process_file($$) { my ($in,$par) = @_; my ($i,$line,$tline,$lnn,$inc,$ff,$ok,$sdir,$shd,$sff); my @the_incs = (); if (open FIL, "<$in") { my @lines = ; close FIL; my $cnt = scalar @lines; $in =~ s/^\.(\\|\/)//; $lnn = sprintf("%5d", $cnt); my ($name,$dir) = fileparse($in); $sff = $in; if (defined $shtdirs{$dir}) { $sff = $shtdirs{$dir}.$name; } $sff .= ' ' while (length($sff) < 8+1+3); prt("Got $lnn lines, from [$sff] to process...\n"); $dir .= $PATH_SEP if !($dir =~ /(\/|\\)$/); $total_files++; $par .= ">" if (length($par)); $par .= $name; for ($i = 0; $i < $cnt; $i++) { $total_lines++; $lnn = sprintf("%5d",($i+1)); $line = $lines[$i]; # extract file line $total_bytes += length($line); chomp $line; $tline = trim_all($line); if ($tline =~ /^\#\s*include\s+(.+)$/) { $inc = $1; $inc =~ s/\/\/.*$//; $inc =~ s/\/\*.*$//; $inc = trim_all($inc); $inc =~ s/^$//; $inc =~ s/^"//; $inc =~ s/"$//; $ff = $dir.$inc; $ok = 'NF'; if (-f $ff) { $ok = 'ok - done'; if (!defined $done{$ff}) { push(@the_incs,[$ff,$par]); $done{$ff} = 1; $ok = "ok [$ff] $par"; } } elsif ($search_system) { foreach $sdir (@std_include_dirs) { $shd = $sdir; if (defined $shtdirs{$sdir}) { $shd = $shtdirs{$sdir}; } $ff = $sdir.$inc; $sff = $shd.$inc; if (-f $ff) { $ok = 'ok- done'; if (!defined $done{$ff}) { push(@the_incs,[$ff,$par]); $done{$ff} = 1; $ok = "ok [$sff] $par"; } last; } } if ($ok eq 'NF') { foreach $sdir (@usr_include_dirs) { $ff = $sdir.$inc; if (-f $ff) { $ok = 'ok- done'; if (!defined $done{$ff}) { push(@the_incs,[$ff,$par]); $done{$ff} = 1; $ok = "ok [$ff] $par"; } last; } } } } if ($ok eq 'NF') { if (!defined $missed{$inc}) { push(@not_found, [$inc, $dir, $in]); $missed{$inc} = 1; prt("$lnn: NOT FOUND [$inc] $ok\n"); } } else { prt("$lnn: INCLUDE [$inc] $ok\n"); } } } $cnt = scalar @the_incs; for ($i = 0; $i < $cnt; $i++) { $inc = $the_incs[$i][0]; $par = $the_incs[$i][1]; process_file($inc,$par); } } else { prtw("WARNING: Unable to open file [$in]!\n"); } } sub mycmp_decend { return -1 if ( ${$a}[0] > ${$b}[0] ); return 1 if ( ${$a}[0] < ${$b}[0] ); return 0; } sub process_input() { my ($in); my @files = (); foreach $in (@in_files) { if (-f $in) { process_file($in,""); } elsif (-d $in) { process_dir($in,\@files,0); } else { pgm_exit(1,"ERROR: Input [$in] is NOT file or directory!\n"); } } foreach $in (@files) { process_file($in,""); } } sub show_done() { my ($key); foreach $key (sort keys %done) { prt("$key\n"); } } sub show_not_found() { my $cnt = scalar @not_found; my ($i,$inc,$dir,$in,$len,$min); prt("NOT FOUND: $cnt include files...\n"); $min = 0; for ($i = 0; $i < $cnt; $i++) { $inc = $not_found[$i][0]; $dir = $not_found[$i][1]; $in = $not_found[$i][2]; $len = length($inc); $min = $len if ($len > $min); } for ($i = 0; $i < $cnt; $i++) { $inc = $not_found[$i][0]; $dir = $not_found[$i][1]; $in = $not_found[$i][2]; $inc .= ' ' while (length($inc) < $min); prt("$inc, in $in\n"); } } ######################################### ###### MAIN ###### process_args(@ARGV); process_input(); prt("$total_dirs dirs, $total_files files, $total_lines lines, $total_bytes bytes.\n"); show_done(); prt("$total_dirs dirs, $total_files files, $total_lines lines, $total_bytes bytes.\n"); show_not_found(); pgm_exit(0,"Normal exit\n"); #################################### sub need_arg { my ($arg,@av) = @_; pgm_exit(1,"ERROR: Need argument following [$arg]!\n") if (!@av); } sub process_args { my (@av) = @_; my ($arg,$sarg); while (@av) { $arg = $av[0]; if ($arg =~ /^-/) { $sarg = substr($arg,1); $sarg = substr($sarg,1) while ($sarg =~ /^-/); if (($sarg =~ /^h/) || ($sarg eq '?')) { give_help(); pgm_exit(0,"Help exit 0"); } elsif ($sarg =~ /^i/) { need_arg(@av); shift @av; $sarg = $av[0]; if (-d $sarg) { $sarg .= '/' if ( !($sarg =~ /\/$/) ); prt("adding [$sarg] folder to check includes.\n"); push(@usr_include_dirs,$sarg); } else { pgm_exit(1,"ERROR: Can NOT locate folder [$sarg]!\n"); } } elsif ($sarg =~ /^l/) { $load_log = 1; prt("Set to load log at end.\n"); } elsif ($sarg =~ /^v/) { if ($sarg =~ /^v.*(\d+)$/) { $verbosity = $1; } else { while ($sarg =~ /^v/) { $verbosity++; $sarg = substr($sarg,1); } } prt("Verbosity = $verbosity\n") if (VERB1()); } else { pgm_exit(1,"ERROR: Unknonw option [$arg]! Try -?\n"); } } else { push(@in_files,$arg); prt("Added input [$arg]\n"); } shift @av; } if ( ! @in_files ) { pgm_exit(1,"ERROR: No input found in command!\n"); } } sub give_help { prt("$pgmname version $VERS\n"); prt("Usage: [options] input\n"); prt("Options:\n"); prt(" --help (-h,-?) = This help and exit.\n"); prt(" --load (-l) = Load log at end.\n"); prt(" --inc (-i) = Add this as an include directory.\n"); prt(" --verb[n] (-v) = Bump [or set] verbosity. def=$verbosity\n"); } # eof - find-incs.pl