#!/perl -w
# NAME: findmarrays.pl
# AIM: VERY SPECIFIC - In a folder, open C/C++ files, and search for multiple arrays array[a][b]...
# 2009/09/26  - geoff mclane - http://geoffair.net/mperl/
use strict;
use warnings;
use File::Basename;
use File::stat;
unshift(@INC, 'C:/GTools/perl');
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
    my @tmpsp = split(/\\/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = "temp.$pgmname.txt";
open_log($outfile);

my $in_dir = "c:\\Projects\\hb\\ffmpeg\\libavcodec";

# features
my $recursive = 0;
my $load_log = 1;

my @file_list = ();
my $total_files = 0;
my $total_lines = 0;
my $total_finds = 0;

# debug
my $dbg01 = 0;  # show prt( "[dbg01] Done $lnn lines, found $fndlines, of total $fnd, minus $fndd digits only, $nomm no _MSC_VER and M_ALLOC...\n" ) if ($dbg01);
my $dbg02 = 0;  # show prt("[dbg02] $lnn:$fnd: [$tl] ($ar1,$ar2)\n") if ($dbg02);
my $dbg03 = 0;  # show prt( "[dbg03] Got $lncnt lines, from [$fil]...\n" ) if ($dbg03);

prt( "$0 ... processing $in_dir ...\n" );

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        prt($msg);
    }
    close_log($outfile,$load_log);
    exit($val)
}


sub got_2_closing($) {
    my ($tx) = shift;
    return 1 if ($tx =~ /\](.*)\]/);
    return 0;
}

sub check_line($$$) {
    my ($lev,$txt,$ra) = @_;
    my ($len,$i,$c,$a1,$dep);
    $len = length($txt);
    $dep = 0;
    for ($i = 0; $i < $len ;$i++) {
        $c = substr($txt,$i,1);
        if ($c eq '[') {
            # got open square brackets
            $a1 = '';
            $i++;
            for (; $i < $len ;$i++) {
                $c = substr($txt,$i,1);
                if ($c eq ']') {
                    # got CLOSE of square brackets
                    $a1 = trim_all($a1);
                    if (length($a1)) {
                        push(@{$ra},$a1);
                        $dep++;
                        if ($dep == $lev) {
                            return 1;
                        }
                    } else {
                        return 0;
                    }
                    last;
                } elsif ($c eq '[') {
                    if ( got_2_closing(substr($txt,$i) ) ) {
                        $i++;
                        for (; $i < $len ;$i++) {
                            $c = substr($txt,$i,1);
                            last if ($c eq ']');
                            if ($c =~ /(=|\{|\})/) {
                                return 0;
                            }
                        }
                        next;
                    } else {
                        return 0;
                    }
                } elsif ($c =~ /(=|\{|\})/) {
                    return 0;
                }
                $a1 .= $c;
            }
        } elsif ($dep) {
            # had first [...], so watch for other 'killers'
            # while waiting for next [...] - that is between [.] ... [.]
            # in fact, only allow SPACE between [.] and [.] blocks
            # if ($c =~ /(=|\{|\}|\|)/) 
            if ( !($c =~ /\s/) ) {
                return 0;
            }
        }
    }
    return 0;
}

sub is_c_source_ext($) {
   my ($fil) = shift;
	my ($nm, $dir, $ext) = fileparse( $fil, qr/\.[^.]*/ );
   my $lce = lc($ext);
   if (($lce eq '.c') || ($lce eq '.cxx') || ($lce eq '.cpp') || ($lce eq '.cc')) {
      return 1;
   }
   return 0;
}

sub is_h_source_ext($) {
   my ($fil) = shift;
	my ($nm, $dir, $ext) = fileparse( $fil, qr/\.[^.]*/ );
   my $lce = lc($ext);
   if (($lce eq '.h') || ($lce eq '.hxx') || ($lce eq '.hpp')) {
      return 1;
   }
   return 0;
}


sub process_dir($$$) {
    my ($dir,$lev,$rfl) = @_;
    my ($DIR,$fcnt,$fil,@fils,$ff,@dirs,$typ);
    @dirs = ();
	if (opendir $DIR, $dir) {
		@fils = readdir($DIR);
		closedir $DIR;
        $fcnt = scalar @fils;
        $dir .= "\\" if ( !($dir =~ /(\\|\/)$/) );
        prt( "Got $fcnt files from [$dir]... scanning...\n" );
        foreach $fil (@fils) {
            next if (($fil eq '.')||($fil eq '..'));
            $ff = $dir.$fil;
            if (-d $ff) {
                push(@dirs,$ff);
            } else {
                $typ = (is_c_source_ext($fil)) ? 1 : (is_h_source_ext($fil)) ? 2 : 0;
                if ($typ) {
                    push( @{$rfl}, [ $ff, $dir, $lev, $typ, 0, 0 ] );
                } else {
                    prt("Skipping [$fil], in dir $dir...\n");
                }
            }
        }
    } else {
        prt("ERROR: Unable to open dir [$dir]...\n");
    }
    if ($recursive && @dirs) {
        foreach $fil (@dirs) {
            process_dir($fil,$lev+1,$rfl);
        }
    }
    if ($lev == 0) {
        my $cnt = scalar @{$rfl};
        prt( "Returning $cnt files...\n" );
    }
    return $rfl;
}

sub scan_file($$) {
    my ($fil,$rfnds) = @_;
    my $fndlines = 0;
    my $fnds = '';
    if (open INF, "<$fil") {
        my @lines = <INF>;
        close INF;
        my $lncnt = scalar @lines;
        $total_files++;
        $total_lines += $lncnt;
        prt( "[dbg03] Got $lncnt lines, from [$fil]...\n" ) if ($dbg03);
        my $lnn = 0;
        my ($tl,$fnd,$line,$ar1,$ar2,$fndd, $got_msvc, $got_mall, $nomm);
        my ($ar3);
        $fnd = 0;
        $fndd = 0;
        $got_msvc = 0;
        $got_mall = 0;
        $nomm = 0;
        foreach $line (@lines) {
            chomp $line;
            $lnn++;
            $tl = trim_all($line);
            $got_msvc = 1 if ($tl =~ /_MSC_VER/);
            $got_mall = 1 if ($tl =~ /M_ALLOC/);
            if ($tl =~ /\w+\s*\[{1}(.+)\]{1}\s*\[{1}(.+)\]{1}/) {
                my @arr = ();
                if ( check_line(3, $tl, \@arr) ) {
                    $fnd++;
                    $ar1 = $arr[0];
                    $ar2 = $arr[1];
                    $ar3 = $arr[2];
                    if ( ($ar1 =~ /^\d+$/) && ($ar2 =~ /^\d+$/) && ($ar3 =~ /^\d+$/) ) {
                        # lets SKIP pure digits only
                        $fndd++;
                    } else {
                        if ($got_msvc && $got_mall) {
                            prt("[dbg02] $lnn:$fnd: [$tl] ($ar1,$ar2)\n") if ($dbg02);
                            $fnds .= '|' if (length($fnds));
                            $fnds .= "$lnn";
                            $fndlines++;
                        } else {
                            $nomm++;
                        }
                    }
                }
            }
        }
        prt( "[dbg01] Done $lnn lines, found $fndlines, of total $fnd, minus $fndd digits only, $nomm no _MSC_VER and M_ALLOC...\n" ) if ($dbg01);
    } else {
        prt("ERROR: Unable to open file [$fil]!\n" );
    }
    $total_finds++ if ($fndlines);
    ${$rfnds} = $fnds;
    return $fndlines;
}

sub process_files($) {
    my ($rfl) = shift;
    my ($cnt);
    $cnt = scalar @{$rfl};
    prt( "Got $cnt files to process...\n" );
    my ($fil,$i,$sb,$tm,$fnds);
    my @found = ();
    for ($i = 0; $i < $cnt; $i++) {
        $fil = ${$rfl}[$i][0];
        $fnds = '';
        if( scan_file($fil, \$fnds) ) {
            #  0    1    2     3     4    5     6     7     8
            # ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
            $tm = 0;
            if ($sb = stat($fil)) {
                $tm = $sb->mtime;
            }
            push(@found, [$tm, $fil, $fnds] );
        }
    }
    return \@found;
}

sub mycmp_decend {
   if (${$a}[0] < ${$b}[0]) {
      #prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return 1;
   }
   if (${$a}[0] > ${$b}[0]) {
      #prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return -1;
   }
   #prt( "=[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
   return 0;
}


sub show_finds($) {
    my ($rfa) = @_;
    my $cnt = scalar @{$rfa};
    my @fil_date = sort mycmp_decend @{$rfa};
    prt( "Found $cnt files matching criteria...\n" );
    my ($fil,$tm,$i,$len,$min,@lt, $ctm, $fnds);
    $min = 0;
    for ($i = 0; $i < $cnt; $i++) {
        $fil = ${$rfa}[$i][1];
        $len = length($fil);
        $min = $len if ($len > $min);
    }
    for ($i = 0; $i < $cnt; $i++) {
        #$fil = ${$rfa}[$i][1];
        #$tm  = ${$rfa}[$i][0]
        $fil  = $fil_date[$i][1];
        $tm   = $fil_date[$i][0];
        $fnds = $fil_date[$i][2];
        $fil .= ' ' while (length($fil) < $min);
        @lt = localtime($tm);
        $ctm = sprintf( "%02d/%02d/%04d %02d:%02d", $lt[3], $lt[4]+1, $lt[5]+1900, $lt[2], $lt[1] );
        prt("$fil ($ctm) $fnds\n");
    }
}

my $ref_fl = process_dir($in_dir, 0, \@file_list);
my $ref_fnd = process_files( $ref_fl );
prt("\nProcessed total $total_files files, $total_lines lines, for $total_finds...\n");
show_finds($ref_fnd);

pgm_exit(0,"Normal Exit");

# eof
