#!/usr/bin/perl -w
# NAME: showincs.pl
# AIM: Read a C/C++ file and show included files...
# 17/05/2013 - Change list spearator depending on OS - win=';', unix=':'
# 17/03/2013 - Add summary at end, and no search for incs in 'windows' includes
# 23/05/2012 - Exclude searching linux includes - 
# 12/11/2011 - Maybe some MACRO expansion
# 04/12/2010 - Added scan of directories to find the file...
# 02/12/2010 - Update - added INC directories...
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use File::Spec; # File::Spec->rel2abs($rel); # we are IN the SLN directory, get ABSOLUTE from RELATIVE
use Cwd;
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.7 2013-05-17";
###my $VERS = "0.0.6 2013-03-17";
###my $VERS = "0.17/05/20130.5 2012-05-23";
my $load_log = 0;
my $g_in_file = '';
my $g_in_root = '';
my $include_linux = 0;
my $exclude_externals = 1;
my $g_out_file = '';
my $do_directory_scan = 0;  # seems wasteful
my $scan_all_files = 0; # do NOT scan windows/sdk/linux includes for includes
my $abort_on_missed = 0;
my @find_list = ();

### program variables
my @finds = ();
my $total_lines = 0;
my %scanned_files = ();
my @warnings = ();
my $cwd = cwd();
#my %g_files_done = ();
my $done_dir_scans = 0;
my %ref_dir_scans = ();

my @g_INC_dirs = ();
my @missing_files = ();

my $verbosity = 0;
my $debug_on = 0;
my $def_file = 'C:\FG\17\3rdParty\include\gdal\gdal.h';
#my $def_file = 'C:\FGCVS\boost-trunk\boost\tr1\unordered_set.hpp';
my $def_root = ''; # no root to scan 'C:\FGCVS\boost-trunk';
#my $def_file = 'C:\Projects\giflib\lib\dgif_lib.c';
#my $def_root = 'C:\Projects\giflib';
#my $def_file = 'C:\Projects\libsigc\libsigc++-2.2.8\sigc++\signal.cc';
#my $def_root = 'C:\Projects\libsigc\libsigc++-2.2.8';

sub VERB1() { return ($verbosity > 0); }
sub VERB2() { return ($verbosity > 1); }
sub VERB5() { return ($verbosity > 4); }
sub VERB9() { return ($verbosity > 8); }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } elsif ($val) {
        #prt( "\nNo warnings issued.\n\n" );
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    show_warnings($val);
    if (length($msg)) {
        $msg =~ s/\n$//;
        prt("$msg\n");
    }
    # prt("$msg ".localtime(time())."\n");
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}

sub si_process_in_file($$);

sub path_per_os($) {
    my $path = shift;
    if ($os =~ /Win/i) {
        $path = path_u2d($path);
    } else {
        $path = path_d2u($path);
    }
    return $path;
}

sub fix_dir_string($) {
    my ($rdir) = @_;
    if (! ( ${$rdir} =~ /(\\|\/)$/) ) {
        ${$rdir} .= $PATH_SEP;
    }
}

sub begins_with {
    my ($rt, $pt) = @_;
    my $ln = length($rt);
    my ($i);
    if (length($pt) >= $ln) {
        for ($i = 0; $i < $ln; $i++) {
            return 0 if (substr($rt,$i,1) ne substr($pt,$i,1));
        }
        return 1; # does indeed begin with...
    }
    return 0;
}

sub sub_first_from_second_if($$) {
    my ($d1,$d2) = @_;
    if (being_with($d1,$d2)) {
        $d2 = substr($d2, length($d1));
    }
    return $d2;
}

# FIXES FOR THE FULL FILE NAME
# 1. ensure ALL DOS format
# 2. if given a FULL PATH name, remove C:\FG\20\FlightGear
# 3. if any removal, ensure any beginning '\' is removed
sub sub_root_dir($) {
    my ($ff) = shift;   # = $a_dir.$src
    $ff = path_per_os($ff);
    my $rd = $g_in_root; # get_root_dir();
    if (begins_with($rd, $ff)) {
        $ff = substr($ff, length($rd));
    }
    return $ff;
}

########################################################
### DIRECTORY SCANNING ###
sub si_do_dir_scan($$$);

sub si_do_dir_scan($$$) {
    my ($rparams,$dir,$lv) = @_;
    my $rda = ${$rparams}{'SI_CURR_DIR_SCAN'};
    $dir .= "\\" if !($dir =~ /(\\|\/)$/);
    $dir = path_per_os($dir);
    my ($file,$ff,$n,$d);
    my @dirs = ();
    prt("Moment, doing full directory scan of [$dir]...\n") if (($lv == 0) && VERB1());
    if (opendir(DIR,$dir)) {
        my @files = readdir(DIR);
        closedir(DIR);
        foreach $file (@files) {
            next if (($file eq '.')||($file eq '..'));
            $ff = $dir.$file;
            if (-d $ff) {
                push(@dirs,$ff);
                next;
            }
            #             0     1   2 3
            push(@{$rda},[$file,$ff,0,0]);
        }
    }
    if (@dirs) {
        foreach $file (@dirs) {
            si_do_dir_scan($rparams,$file,$lv+1);
        }
    }
    if ($lv == 0) {
        $file = scalar @{$rda};
        prt("Done scan... got $file files...\n") if (VERB5());
        ${$rparams}{'SI_CURR_DONE_SCAN'} = 1;
    }
}

sub si_is_file_in_scan($$$$) {
    my ($rparams,$test,$inc,$ra) = @_;
    if (! ${$rparams}{'SI_CURR_DONE_SCAN'} ) {
        my $dir = ${$rparams}{'SI_CURR_IN_ROOT'};
        if (-d $dir) {
            si_do_dir_scan($rparams,$dir,0);
        } else {
            prtw("WARNING: Directory SEARCH FAILED! [$dir] NOT found!\n");
            return 0;
        }
    }
    my $rda = ${$rparams}{'SI_CURR_DIR_SCAN'};
    my $cnt = scalar @{$rda};
    my ($i,$file,$fcnt,$ff);
    $fcnt = 0;
    for ($i = 0; $i < $cnt; $i++) {
        $file = ${$rda}[$i][0];
        #if ($test eq $file) {
        if ($inc eq $file) {
            $ff = ${$rda}[$i][1];
            # could now check if $fd at least partially matches, but for now
            push(@{$ra},$ff);
            $fcnt++;
        }
    }
    #prt("Directory SEARCH for [$inc] [$test], found $fcnt. Only 1st used.\n") if ($fcnt); # if ($dbg_lac20);
    return $fcnt;
}

########################################################
### DIRECTORY SCANNING ###
sub do_dir_scan($$$);
sub do_dir_scan($$$) {
    my ($rparams,$dir,$lv) = @_;
    my $rda = ${$rparams}{'CURR_DIR_SCAN'};
    $dir = path_per_os($dir);
    fix_dir_string(\$dir);
    my ($file,$ff,$n,$d);
    my @dirs = ();
    prt("Moment, doing full directory scan of [$dir]...\n") if (($lv == 0) && VERB9());
    if (opendir(DIR,$dir)) {
        my @files = readdir(DIR);
        closedir(DIR);
        foreach $file (@files) {
            next if (($file eq '.')||($file eq '..'));
            $ff = $dir.$file;
            if (-d $ff) {
                push(@dirs,$ff);
                next;
            }
            #             0     1   2 3
            push(@{$rda},[$file,$ff,0,0]);
        }
    } else {
        prtw("WARNING: FAILED to OPEN [$dir]\n");
    }
    foreach $file (@dirs) {
        do_dir_scan($rparams,$file,$lv+1);
    }
    if ($lv == 0) {
        $file = scalar @{$rda};
        prt("Done scan... got $file files...\n") if (VERB9());
        ${$rparams}{'CURR_DONE_SCAN'} = 1;
        ${$rparams}{'CURR_FILE_COUNT'} = $file;
    }
}

sub get_anon_hash() {
    my %h = ();
    return \%h;
}

sub get_dir_scan_rh($) {
    my ($dir) = @_;
    my $rparams = get_anon_hash();
    ${$rparams}{'CURR_DONE_SCAN'} = 0;
    ##             0     1   2 3
    #push(@{$rda},[$file,$ff,0,0]);
    ${$rparams}{'CURR_DIR_SCAN'} = [];
    do_dir_scan($rparams,$dir,0);
    return $rparams;
}


sub get_dir_scan_hash($) {
    my ($src) = @_;
    $src = path_per_os($src);
    if (defined $ref_dir_scans{$src}) {
        return $ref_dir_scans{$src};
    }
    $ref_dir_scans{$src} = get_dir_scan_rh($src);
    $done_dir_scans++;
    return $ref_dir_scans{$src};
}

my $shown_msvc_incs = 0; # prt("Got $cnt 'MSVC' directories... using vcvarsall.bat\n") if (VERB1());
my $shown_msvc_cnt = 0; # prt("$dir - with $cnt files...\n");

sub get_msvc_includes() {
    my ($insdir);
    my @vc8 = ();
    my %dirs = ();
    my $cnt = 0;
    my %h = ();
    $h{'*CURR_TOTAL_CNT*'} = 0;
    if (vc_get_include_dirs2(\$insdir,0) ) {
        $cnt = scalar @{$insdir};
        # prt("OK got $cnt\n");
        my $rd = \%dirs;
        my ($dir,$min,$len);
        $cnt = 0;
        $min = 0;
        foreach $dir (@{$insdir}) {
            if (-d $dir) {
                # if ( add_to_files_lc($dir,$rd) ) # check NOT duplicate
                if (defined ${$rd}{$dir}) {
                    # already in LIST
                } else {
                    push(@vc8,$dir);
                    $cnt++;
                    ${$rd}{$dir} = $cnt;
                    $len = length($dir);
                    $min = $len if ($len > $min);
                }
            }
        }
        if (@vc8) {
            $cnt = scalar @vc8;
            prt("Got $cnt 'MSVC' directories... using vcvarsall.bat\n") if (VERB1() && !$shown_msvc_incs );
            $shown_msvc_incs = 1;
            $min = 0;
            foreach $dir (@vc8) {
                $len = length($dir);
                $min = $len if ($len > $min);
            }
            foreach $dir (@vc8) {
                # my $rh = get_dir_scan_rh($dir);
                my $rh = get_dir_scan_hash($dir);
                $h{$dir} = $rh;
                $cnt = ${$rh}{'CURR_FILE_COUNT'};
                $h{'*CURR_TOTAL_CNT*'} += $cnt;
                if (VERB9() && !$shown_msvc_cnt) {
                    $dir .= " " while (length($dir) < $min);
                    prt("$dir - with $cnt files...\n");
                    $shown_msvc_cnt = 1;
                }
            }
        }
    } else {
        prt("Failed to get include directories!\n");
    }
    $h{'*MSVC_DIR_LIST*'} = \@vc8;  # array list of KEYS to directory scan hash
    return \%h;
}

my %msvc_checked = ();
sub in_msvc_includes2($$) {
    my ($inc,$rff) = @_;
    my ($ra,$dir,$ff);
    if (defined $msvc_checked{$inc}) {
        $ff = $msvc_checked{$inc};
        if (length($ff)) {
            ${$rff} = $ff;
            return 1;
        }
        return 0;
    }
    if (vc_get_include_dirs2(\$ra,0) ) {
        foreach $dir (@{$ra}) {
            fix_dir_string(\$dir);
            $ff = $dir.$inc;
            ###prt("Checking for [$ff] ");
            if (-f $ff) {
                ${$rff} = $ff;
                ###prt("Found [$inc] [$ff]\n");
                $msvc_checked{$inc} = $ff;
                return 1;
            }
            #prt("Check for [$ff] FAILED!\n");
        }
    }
    $msvc_checked{$inc} = '';
    return 0;
}


sub in_msvc_includes($) {
    my ($inc) = @_;
    my $rh = get_msvc_includes();
    # my $rdsh = get_dir_scan_rh($dir);
    # stored $h{$dir} = $rdsh;
    my $tcnt = ${$rh}{'*CURR_TOTAL_CNT*'};
    my $rkya = ${$rh}{'*MSVC_DIR_LIST*'};  # array list of KEYS to directory scan hash
    my ($key,$fil,$dir,$cnt);
    my ($i,$fn,$ff,$lcfn);
    my $lcinc = path_per_os($inc); # file name to OS form
    $lcinc = lc($lcinc) if ($os =~ /Win/i); #all lower case in WIN32
    foreach $key (@{$rkya}) {
        if (defined ${$rh}{$key}) {
            my $rka = ${$rh}{$key}; # list of files in this directory
            #my $rda = ${$rparams}{'CURR_DIR_SCAN'};
            #${$rparams}{'CURR_DONE_SCAN'} = 1;
            $cnt = ${$rka}{'CURR_FILE_COUNT'};
            my $rda = ${$rka}{'CURR_DIR_SCAN'};
            $cnt = scalar @{$rda};  # should equal CURR_FILE_COUNT
            $dir = path_per_os($key);
            fix_dir_string(\$dir);
            for ($i = 0; $i < $cnt; $i++) {
                $fn = ${$rda}[$i][0];  # get FILE name
                $lcfn = lc($fn);
                $ff = $dir.$inc;
                if ($lcfn eq $lcinc) {
                    return 1;
                } elsif ( -f $ff ) {
                    return 2;
                }
            }
        }
    }
    return 0;
}

sub in_unix_includes2($$) {
    my ($inc,$rff) = @_;
    my $dir = "C:\\Projects\\include";
    return 0 if (! -d $dir);
    fix_dir_string(\$dir);
    my $ff = $dir.$inc;
    if (-f $ff) {
        ${$rff} = $ff;
        return 1;
    }
    return 0;
}

sub in_unix_includes($) {
    my ($inc) = @_;
    my $dir = "C:\\Projects\\include";
    my $rka = get_dir_scan_hash($dir);
    my $cnt = ${$rka}{'CURR_FILE_COUNT'};
    ##             0     1   2 3
    #push(@{$rda},[$file,$ff,0,0]);
    my $rda = ${$rka}{'CURR_DIR_SCAN'};
    $cnt = scalar @{$rda};
    my ($i,$fn,$lcfn,$lcinc,$ff);
    $lcinc = lc($inc);
    $dir .= "\\" if (!($dir =~ /\\$/));
    for ($i = 0; $i < $cnt; $i++) {
        $fn = ${$rda}[$i][0];  # get FILE name
        $lcfn = lc($fn);
        $ff = $dir.$fn;
        if ($lcfn eq $lcinc) {
            return 1;
        } elsif (-f $ff) {
            return 2;
        }
    }
    return 0;
}

sub get_PSDK_directory() {
    my $dir = 'C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Include';
    return $dir if (-d $dir);
    # 20120529 - Add Win7_-PC platform SDK directory
    $dir = 'C:\Program Files\Microsoft SDKs\Windows\v7.1\include';
    return $dir if (-d $dir);
    prtw("WARNING: Platform SDK directory NOT found! FIX ME!!\n");
    return "";
}

sub in_sdk_includes2($$) {
    my ($inc,$rff) = @_;
    my $dir = get_PSDK_directory();
    return 0 if (length($dir) == 0);
    fix_dir_string(\$dir);
    my $ff = $dir.$inc;
    if (-f $ff) {
        ${$rff} = $ff;
        return 1;
    }
    return 0;
}


sub in_sdk_includes($) {
    my $inc = shift;
    my $dir = get_PSDK_directory();
    return 0 if (length($dir) == 0);
    my $rka = get_dir_scan_hash($dir);
    my $cnt = ${$rka}{'CURR_FILE_COUNT'};
    ##             0     1   2 3
    #push(@{$rda},[$file,$ff,0,0]);
    my $rda = ${$rka}{'CURR_DIR_SCAN'};
    $cnt = scalar @{$rda};
    my ($i,$fn,$lcfn,$lcinc,$ff);
    my $iswin = ($os =~ /Win/i) ? 1 : 0;
    $lcinc = ($iswin ? lc($inc) : $inc);
    fix_dir_string(\$dir);
    for ($i = 0; $i < $cnt; $i++) {
        $fn = ${$rda}[$i][0];  # get FILE name
        $lcfn = ($iswin ? lc($fn) : $fn);
        $ff = $dir.$fn;
        if ($lcfn eq $lcinc) {
            return 1;
        } elsif (-f $ff) {
            return 2;
        }
    }
    return 0;
}

sub check_finds($$$) {
    my ($inf,$lnn,$tline) = @_;
    my ($find);
    foreach $find (@find_list) {
        if ($tline =~ /$find/i) {
            push(@finds,[$inf,$lnn,$find,$tline]);
        }
    }
    $total_lines++;
    $scanned_files{$inf} = 1;
}

sub si_process_in_file($$) {
    my ($rparams,$lev) = @_;
    my $inf = ${$rparams}{'SI_CURR_IN_FILE'};
    my $rfd = ${$rparams}{'SI_REF_FILES_DONE'};  # \%files_done
    my $rdo = ${$rparams}{'SI_RA_FILES_DONE'};
    my $rid = ${$rparams}{'SI_REF_INCS_DONE'};
    my $rincs = ${$rparams}{'SI_REF_INCS_ARRAY'};
    my $cin_root = ${$rparams}{'SI_CURR_IN_ROOT'};
    my $rincdirs = ${$rparams}{'SI_REF_INCLUDE_DIRS'};
    # already done - fix_dir_string(\$cin_root);

    my $dbg_01 = ${$rparams}{'SI_CURR_DBG_SI01'};
    my $dbg_02 = ${$rparams}{'SI_CURR_DBG_SI02'};
    my $dbg_03 = ${$rparams}{'SI_CURR_DBG_SI03'};

    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("\n[01] Processing $lncnt lines, from [$inf]...\n") if ($dbg_01 || VERB9());
    my ($line,$inc,$lnn,$name,$dir,$ff,$ch,$ok);
    my ($n,$d,$cnt,$i,$fcnt,@arr);
    my ($ifitem,$tline,$defitem,$impitem,$pragitem,$erritem,$unditem,$useitem);
    my ($fnd,$icnt,$ra);
    ($name,$dir) = fileparse($inf);
    $dir = $cwd if ($dir =~ /^\.(\\|\/)$/);
    $dir .= "\\" if (!($dir =~ /(\\|\/)$/));
    $lnn = 0;
    my @incs = ();
    $icnt = scalar @{$rincdirs};
    for ($i = 0; $i < $lncnt; $i++) {
        $line = $lines[$i];
        chomp $line;
        $tline = trim_all($line);
        $lnn++;
        check_finds($inf,$lnn,$tline);
        if ($line =~ /\s*#\s*include\s+(.+)$/) {
            $inc = $1;
    		$inc =~ s/\/\/.*$//;
    		$inc =~ s/\/\*.*$//;
            $inc = trim_all($inc);
            $ch = substr($inc,0,1);
            if ($inc =~ /<(.+)>/) {
                $inc =~ s/<(.+)>/$1/;
            } elsif ($inc =~ /"(.+)"/) {
                $inc =~ s/"(.+)"/$1/;
            }
            $ok = "NEW";
            $fnd = 0;
            ($n,$d) = fileparse($inc);
            if ( defined ${$rid}{$inc} ) {
                prt("[01] Line $lnn: [$inc] via [$ch] DONE\n") if ($dbg_01 || VERB9());
                $fnd = -1;
            } else {
                prt("[01] Line $lnn: [$inc] via [$ch] $ok\n") if ($dbg_01 || VERB9());
                $ff = $dir.$inc;
                $ok = 'NOT FOUND';
                if (-f $ff) {
                    $ok = 'ok 1';
                    prt("[01] Line $lnn: [$inc] found 1 [$ff]\n") if ($dbg_01 || VERB9());
                    $fnd = 1;
                } else {
                    if ($inc =~ /(\\|\/)/) {
                        ($n,$d) = fileparse($inc);
                        $ff = $dir.$n;
                        if (-f $ff) {
                            $ok = 'ok 2';
                            prt("[01] Line $lnn: [$inc] found 2 [$ff]\n") if ($dbg_01 || VERB9());
                            $fnd = 2;
                        } else {
                            $ff = $cin_root.$n;
                            if (-f $ff) {
                                $ok = 'ok 3';
                                prt("[01] Line $lnn: [$inc] found 3 [$ff]\n") if ($dbg_01 || VERB9());
                                $fnd = 3;
                            } elsif ($do_directory_scan) {
                                @arr = ();
                                prt("[01] Line $lnn: Checking [$inc] [$ff] in directory scan...\n") if ($dbg_01 || VERB9());
                                if ( si_is_file_in_scan($rparams,$ff,$inc,\@arr) ) {
                                    $fcnt = scalar @arr;
                                    $ff = $arr[0];
                                    prt("[01] Line $lnn: Returned $fcnt, trying [$ff] ") if ($dbg_01 || VERB9());
                                    if (-f $ff) {
                                        $ok = 'ok 4';
                                        $fnd = 4;
                                    }
                                    prt(" $ok\n") if ($dbg_01 || VERB9());
                                } elsif (($inc =~ /(\\|\/)/) && $do_directory_scan) {
                                    ($n,$d) = fileparse($inc);
                                    if ( si_is_file_in_scan($rparams,$ff,$n,\@arr) ) {
                                        $fcnt = scalar @arr;
                                        $ff = $arr[0];
                                        prt("[01] Line $lnn: Returned $fcnt, trying [$ff] ") if ($dbg_01 || VERB9());
                                        if (-f $ff) {
                                            $ok = 'ok 5';
                                            $fnd = 5;
                                        }
                                        prt(" $ok\n") if ($dbg_01 || VERB9());
                                    }
                                } else {
                                    prt("[01] Line $lnn: [$inc] NOT FOUND [$ff]\n") if ($dbg_01 || VERB9());
                                }
                            }
                        }
                    } else {
                        $ff = $cin_root.$n;
                        if (-f $ff) {
                            $ok = 'ok 6';
                            prt("[01] Line $lnn: [$inc] found 6 [$ff]\n") if ($dbg_01 || VERB9());
                            $fnd = 6;
                        } elsif ($do_directory_scan) {
                            @arr = ();
                            prt("[01] Line $lnn: Checking [$inc] [$ff] in directory scan...\n") if ($dbg_01 || VERB9());
                            if ( si_is_file_in_scan($rparams,$ff,$inc,\@arr) ) {
                                $fcnt = scalar @arr;
                                $ff = $arr[0];
                                prt("[01] Line $lnn: Returned $fcnt, trying [$ff] ") if ($dbg_01 || VERB9());
                                if (-f $ff) {
                                    $ok = 'ok 7';
                                    $fnd = 7;
                                }
                                prt(" $ok\n") if ($dbg_01 || VERB9());
                            } else {
                                prt("[01] Line $lnn: [$inc] NOT FOUND [$ff]\n") if ($dbg_01 || VERB9());
                            }
                        }
                    }
                }
                if ($fnd == 0) {
                    if ($icnt) {
                        foreach $d (@{$rincdirs}) {
                            $d .= "\\" if ( !($d =~ /(\\|\/)$/) );
                            $ff = $d.$inc;
                            if (-f $ff) {
                                $ok = 'ok 8';
                                $fnd = 8;
                                last;
                            }
                        }
                    }
                }
                if ($fnd == 0) {
                    if (in_msvc_includes2($inc,\$ff)) {
                        $ok = "<windows> [$ff]";
                        $fnd = 9;
                    }
                }
                if ($fnd == 0) {
                    if (in_sdk_includes2($inc,\$ff)) {
                        $ok = "<sdk> [$ff]";
                        $fnd = 10;
                    }
                }
                if (($fnd == 0) && $include_linux) {
                    if (in_unix_includes2($inc,\$ff)) {
                        $ok = "<linux> [$ff]";
                        $fnd = 11;
                    }
                }
                if ($fnd) {
                    $ff = path_per_os($ff);
                } else {
                    $ff = $inc;
                }

                ${$rid}{$inc} = $inf;
                ${$rfd}{$ff} = $inf;
                push(@{$rdo},$inf);
                #               0   1    2   3    4    5   6
                push(@{$rincs},[$ff,$inc,$ch,$lnn,$inf,$ok,$fnd]);
                push(@incs,    [$ff,$inc,$ch,$lnn,$inf,$ok,$fnd]);
                prt("[01] $lev: Stored:$ok:$fnd: [$ff] [$inc] [$ch] [$lnn] [$inf]\n") if ($dbg_01 || $dbg_03 || VERB9());
            }
        } elsif ($line =~ /\s*#\s*if\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #if [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*ifdef\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #ifdef [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*ifndef\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #ifndef [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*else\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #else [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*elif\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #elif [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*endif\s+(.+)$/) {
            $ifitem = $1;
    		$ifitem =~ s/\/\/.*$//;
    		$ifitem =~ s/\/\*.*$//;
            prt("[02] #endif [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*define\s+(.+)$/) {
            $defitem = $1;
    		$defitem =~ s/\/\/.*$//;
    		$defitem =~ s/\/\*.*$//;
            prt("[02] #define [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*import\s+(.+)$/) {
            $impitem = $1;
    		$impitem =~ s/\/\/.*$//;
    		$impitem =~ s/\/\*.*$//;
            prt("[02] #import [$impitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*pragma\s+(.+)$/) {
            $pragitem = $1;
    		$pragitem =~ s/\/\/.*$//;
    		$pragitem =~ s/\/\*.*$//;
            prt("[02] #pragma [$ifitem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*error\s+(.+)$/) {
            $erritem = $1;
    		$erritem =~ s/\/\/.*$//;
    		$erritem =~ s/\/\*.*$//;
            prt("[02] #error [$erritem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*undef\s+(.+)$/) {
            $unditem = $1;
    		$unditem =~ s/\/\/.*$//;
    		$unditem =~ s/\/\*.*$//;
            prt("[02] #undef [$unditem]\n") if ($dbg_02);
        } elsif ($line =~ /\s*#\s*use\s+(.+)$/) {
            $useitem = $1;
    		$useitem =~ s/\/\/.*$//;
    		$useitem =~ s/\/\*.*$//;
            prt("[02] #use [$useitem]\n") if ($dbg_02);
        }
    }

    # now process the includes, for includes
    $cnt = scalar @incs;
    for ($i = 0; $i < $cnt; $i++) {
        #                0   1    2   3    4    5   6
        #push(@incs,    [$ff,$inc,$ch,$lnn,$inf,$ok,$fnd]);
        $ra  = $incs[$i];
        $ff  = ${$ra}[0]; #$incs[$i][0];
        $ok  = ${$ra}[5]; #$incs[$i][5];
        $fnd = ${$ra}[6]; #$incs[$i][6];
        push(@missing_files,$ra) if ($fnd == 0);
        if (-f $ff) {
            if (($fnd < 9) || $scan_all_files) {
                ${$rparams}{'SI_CURR_IN_FILE'} = $ff;
                si_process_in_file($rparams,($lev+1));
            }
        } elsif ($do_directory_scan) {
            $inc = $incs[$i][1];
            $ch  = $incs[$i][2];
            prt("[01] Unable to locate [$inc]! [$ch]... Searching dir scan...\n") if ($dbg_01 || VERB9());
            @arr = ();
            if ( si_is_file_in_scan($rparams,$ff,$inc,\@arr) ) {
                $fcnt = scalar @arr;
                prt("[01] Returned $fcnt, trying each...\n") if ($dbg_01 || VERB9());
                foreach $ff (@arr) {
                    ${$rparams}{'SI_CURR_IN_FILE'} = $ff;
                    si_process_in_file($rparams,($lev+1));
                }
            } elsif ($inc =~ /(\\|\/)/) {
                ($n,$d) = fileparse($inc);
                if ( si_is_file_in_scan($rparams,$ff,$n,\@arr) ) {
                    $fcnt = scalar @arr;
                    prt("[01] Returned $fcnt, trying each...\n") if ($dbg_01 || VERB9());
                    foreach $ff (@arr) {
                        ${$rparams}{'SI_CURR_IN_FILE'} = $ff;
                        si_process_in_file($rparams,($lev+1));
                    }
                }
            }
        }
    }
}

sub compare_paths($$) {
    my ($p1,$p2) = @_;
    if ($os =~ /win/i) {
        $p1 = lc(path_u2d($p1));
        $p2 = lc(path_u2d($p2));
    } else {
        $p1 = path_d2u($p1);
        $p2 = path_d2u($p2);
    }
    return 1 if ($p1 eq $p2);
    return 0;
}

sub si_show_found_list($) {
    my ($rparams) = @_;
    my $rh = ${$rparams}{'SI_REF_FILES_DONE'};  # \%files_done
    my $inf = ${$rparams}{'SI_MAIN_IN_FILE'};
    my $rdo = ${$rparams}{'SI_RA_FILES_DONE'};
    my $rincs = ${$rparams}{'SI_REF_INCS_ARRAY'};
    my ($key,$val,$k2,$v2,$file,$cnt,$done,$icnt,$key2);
    my ($ricnt,$ifile,$i,$ok,$min,$len,$vcnt,$ccnt);
    my %h = ();
    my $rh2 = \%h;
    $cnt = scalar @{$rdo};
    $ricnt = scalar @{$rincs};
    my %fdone = ();

    prt("\nIn the processing of [$inf]... found $cnt includes...\n") if (VERB1());
    foreach $key (keys %{$rh}) {
        $val = ${$rh}{$key};    # get the file in which it was found
        ${$rh2}{$val} = [] if (!defined ${$rh2}{$val});
        $v2 = ${$rh2}{$val};
        push(@{$v2},$key);  # store what was found in that file
    }

    # now per the files searched
    $icnt = 0;
    %fdone = ();
    $min = 0;
    foreach $key2 (@{$rdo}) {
        if ( ! defined $fdone{$key2} ) {
            $fdone{$key2} = 1;
            $done = 0;
            foreach $key (keys %{$rh2}) {
                if (compare_paths($key,$key2)) {
                    $val = ${$rh2}{$key};
                    $file = sub_root_dir($key);
                    # prt("File [$file] has...\n");
                    foreach $k2 (@{$val}) {
                        $icnt++;
                        $ok = 'Missed';
                        $file = sub_root_dir($k2);
                        #prt("Checking [$k2] in all incs..\n");
                        for ($i = 0; $i < $ricnt; $i++) {
                            #            0   1    2   3    4    5
                            #push(@incs,[$ff,$inc,$ch,$lnn,$inf,$ok]);
                            $ifile = ${$rincs}[$i][0];   # FULL FILE
                            #prt(" With [$ifile]...\n");
                            if ($ifile eq $k2) {
                                $ok = ${$rincs}[$i][5];
                                $file = ${$rincs}[$i][1];
                                last;
                            }
                        }
                        #prt(" $icnt: $file $ok\n");
                        $len = length($file);
                        $min = $len if ($len > $min);
                    }
                    $done = 1;
                }
            }
            if (!$done) {
                if ($abort_on_missed) {
                    prt("ERROR: Not found [$key2]! Searched list\n");
                    my @arr = keys %{$rh2};
                    prt(join("\n",@arr)."\n");
                    pgm_exit(1,"ERROR INTERNAL: Missed [$key2]! WHY? FIX THIS!!!\n");
                }
            }
        }
    }

    $icnt = 0;
    %fdone = ();
    my @found = ();
    foreach $key2 (@{$rdo}) {
        if ( ! defined $fdone{$key2} ) {
            $fdone{$key2} = 1;
            $done = 0;
            foreach $key (keys %{$rh2}) {
                if ($key eq $key2) {
                    $val = ${$rh2}{$key};
                    $file = sub_root_dir($key);
                    $vcnt = scalar @{$val};
                    prt("\n") if (VERB9());
                    prt("File [$file] has $vcnt 'include'...\n");
                    foreach $k2 (@{$val}) {
                        $icnt++;
                        $ok = 'Missed';
                        $file = sub_root_dir($k2); # dummy include
                        #prt("Checking [$k2] in all incs..\n");
                        for ($i = 0; $i < $ricnt; $i++) {
                            #            0   1    2   3    4    5
                            #push(@incs,[$ff,$inc,$ch,$lnn,$inf,$ok]);
                            $ifile = ${$rincs}[$i][0];   # FULL FILE
                            #prt(" With [$ifile]...\n");
                            if ($ifile eq $k2) {
                                $ok = ${$rincs}[$i][5];
                                $file = ${$rincs}[$i][1];   # get original INCLUDE
                                last;
                            }
                        }

                        $file .= ' ' while (length($file) < $min);
                        $ok .= " [$k2]" if ($ok eq 'ok');
                        $ccnt = sprintf("%4d",$icnt);
                        if ($exclude_externals && !VERB9()) {
                            if ($ok =~ /^ok/) {
                                prt(" $ccnt: $file $ok\n") if (VERB5());
                                push(@found,$k2);
                            }
                        } elsif (VERB9()) {
                            prt(" $ccnt: $file $ok\n");
                            push(@found,$k2);
                        }
                    }
                    $done = 1;
                }
            }
            if (!$done) {
                if ($abort_on_missed) {
                    #prt("Missed [$key2]! WHY?\n");
                    pgm_exit(1,"ERROR INTERNAL: Missed [$key2]! WHY? FIX THIS!!! 2\n");
                }
            }
        }
    }
    return \@found;
}

sub setup_params($$) {
    my ($inf,$inr) = @_;
    my %params = ();
    my $rparams = \%params;
    ${$rparams}{'SI_MAIN_IN_FILE'} = $inf;
    ${$rparams}{'SI_CURR_IN_FILE'} = $inf;
    ${$rparams}{'SI_CURR_IN_ROOT'} = $inr;
    my %files_done = ();
    my $rfd = \%files_done;
    ${$rparams}{'SI_REF_FILES_DONE'} = $rfd;
    my @done_order = ();
    my $rdo = \@done_order;
    ${$rparams}{'SI_RA_FILES_DONE'} = $rdo;
    my @incs = ();
    ${$rparams}{'SI_REF_INCS_ARRAY'} = \@incs;
    my %incs_done = ();
    my $rid = \%incs_done;
    ${$rparams}{'SI_REF_INCS_DONE'} = $rid;

    ${$rparams}{'SI_CURR_DBG_SI01'} = 0;
    ${$rparams}{'SI_CURR_DBG_SI02'} = 0;
    ${$rparams}{'SI_CURR_DBG_SI03'} = 0;

    ${$rparams}{'SI_CURR_DONE_SCAN'} = 0;
    ${$rparams}{'SI_CURR_DIR_SCAN'} = [];

    ${$rparams}{'SI_REF_INCLUDE_DIRS'} = \@g_INC_dirs;
    return $rparams;
}

sub si_show_count($) {
    my $ra = shift;
    my $cnt = scalar @{$ra};
    prt("List $cnt include files ");
    prt("excluding externals ") if ($exclude_externals && !VERB9());
    prt("no output (-o) given ") if (length($g_out_file) == 0);
    prt("\n");
    my $msg = join("\n",sort @{$ra});
    $msg .= "\n";
    if (length($g_out_file)) {
        write2file($msg,$g_out_file);
        prt("List written to [$g_out_file]\n");
    }
    prt($msg) if (VERB1());

    $cnt = scalar @missing_files;
    prt("\nMissed $cnt includes...\n");
    my ($i,$inf,$len,$min,$lnn);
    $min = 0;
    for ($i = 0; $i < $cnt; $i++) {
        $ra = $missing_files[$i];
        $msg = ${$ra}[0];
        $len = length($msg);
        $min = $len if ($len > $min);
    }
    #                 0   1    2   3    4    5   6
    # push(@{$rincs},[$ff,$inc,$ch,$lnn,$inf,$ok,$fnd]);
    for ($i = 0; $i < $cnt; $i++) {
        $ra = $missing_files[$i];
        $msg = ${$ra}[0];
        $lnn = ${$ra}[3];
        $inf = ${$ra}[4];

        $msg .= ' ' while (length($msg) < $min);
        prt("$msg in [$inf] at $lnn\n");
    }
}

sub si_show_finds() {
    my $cnt = scalar @finds;
    my $fcnt = scalar keys(%scanned_files);
    my ($inf,$lnn,$find,$ra,$tline);
    if ($cnt) {
        prt("Have $cnt finds... scanned $total_lines lines from $fcnt files...\n");
        foreach $ra (@finds) {
            $inf = ${$ra}[0];
            $lnn = ${$ra}[1];
            $find = ${$ra}[2];
            $tline = ${$ra}[3];
            prt("$lnn: $inf, found [$find]\n");
            prt("Line: [$tline]\n") if (VERB1());
        }
    }
}

#########################################
### MAIN ###

parse_args(@ARGV);

my $rph = setup_params($g_in_file,$g_in_root);

si_process_in_file($rph,0);

my $ra = si_show_found_list($rph);

si_show_count($ra);

si_show_finds();

pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg,$tmp,@arr,$cnt);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^f/) {
                need_arg(@av);
                shift @av;
                $sarg = strip_quotes($av[0]);
                push(@find_list,$sarg);
                prt("Added [$sarg] to find list\n") if (VERB1());
            } elsif ($sarg =~ /^i/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $g_in_file = File::Spec->rel2abs($sarg);
                prt("Set INPUT file to [$g_in_file]\n") if (VERB1());
            } elsif ($sarg =~ /^I/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                if ($os =~ /win/i) {
                    @arr = split(/;/,$sarg);
                } else {
                    @arr = split(/:/,$sarg);
                }
                foreach $sarg (@arr) {
                    $tmp = File::Spec->rel2abs($sarg);
                    if (-d $tmp) {
                        push(@g_INC_dirs,$tmp);
                        prt("Added INC directory [$tmp]\n") if (VERB1());
                    } else {
                        pgm_exit(1,"ERROR: Directory [$tmp], rel [$sarg] does NOT exist!\n");
                    }
                }
            } elsif ($sarg =~ /^l/) {
                $load_log = 1;
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $g_out_file = $sarg;
                prt("Set output to [$g_out_file]\n") if (VERB1());
            } elsif ($sarg =~ /^r/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $g_in_root = File::Spec->rel2abs($sarg);
                prt("Set root to [$g_in_root]\n") if (VERB1());
            } elsif ($sarg =~ /^v/i) {
                if ($sarg =~ /^v(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/i) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Set verbosity to $verbosity\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $g_in_file = File::Spec->rel2abs($arg);
            prt("Set input to [$g_in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON\n");

        if (length($g_in_file) ==  0) {
            $g_in_file = File::Spec->rel2abs($def_file);
            prt("[debug_on] Set input to DEFAULT [$g_in_file]\n");
        }
        if (length($g_in_root) ==  0) {
            if (length($def_root)) {
                $g_in_root = File::Spec->rel2abs($def_root);
            } else {
                ($arg,$g_in_root) = fileparse($g_in_file);
            }
            prt("[debug_on] Set root to DEFAULT [$g_in_root]\n");
        }
        $verbosity = 9;
        $load_log = 1;
    }
    if (length($g_in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $g_in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$g_in_file]! Check name, location...\n");
    }
    if (length($g_in_root) == 0) {
        ($arg,$g_in_root) = fileparse($g_in_file);
    }
    fix_dir_string(\$g_in_root);
    if (! -d $g_in_root) {
        pgm_exit(1,"ERROR: Unable to find in directory [$g_in_root]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --in <file>   (-i) = Alternative to set INPUT file.\n");
    prt(" --INC <dirs>  (-I) = Set include search directories.\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --LINUX       (-L) = Include search of 'linux' include folder.\n");
    prt(" --out file    (-o) = Output the list to this file.\n");
    prt(" --root <dir>  (-r) = Set ROOT directory.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --find word   (-f) = Find this 'word' in the files.\n");
    prt("\n");
    prt("Purpose:\n");
    prt(" Treat the input files as a C/C++ file, and search for 'include' items.\n");
    prt("  Then each 'included' file will be searched, and a summary shown at the end.\n");
    prt("Notes:\n");
    prt(" If no root directory is given, the the directory of the in-file will be used as\n");
    prt("  the root. Obviously both the in-file and root directory MUST exist.\n");
    prt("The -I <dirs> can be a list, separated with ';' in win, and ':' in unix.\n");
}

# eof - showincs.pl
