#!/perl -w
# NAME: getmissing.pl
# AIM: VERY SPECIAL - read getmissing.txt, containing MSVC output, and list the 'unresolved'
# 9/14/2009 - geoff mclane - http://geoffair.net/mperl/
use strict;
use warnings;
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);
prt( "$0 ... Hello, World ...\n" );

my $load_log = 1;
my $in_file = 'getmissing.txt';

# DEBUG
my $dbg01 = 0;  # show as found...
my $dbg02 = 0;  # show fuctions as found...

my %res_words = (
    'void' => 1,
    '__cdecl' => 2,
    'const' => 3,
    'unsigned' => 4,
    'char' => 5,
    'int' => 6,
    'struct' => 7,
    'float' => 8,
    'short' => 9,
    'long' => 10,
    '__int64' => 11,
    'double' => 12
    );


sub remove_res_words($) {
    my ($txt) = shift;
    my @arr = split(/\s/,$txt);
    my $ntxt = '';
    foreach my $itm (@arr) {
        if ( defined $res_words{$itm} ) {
            # skip this
        } elsif ($itm eq '*') {
            # skip this
        } else {
            # add it
            $ntxt .= " " if length($ntxt);
            $ntxt .= $itm;
        }
    }
    return $txt if (length($ntxt) == 0);
    my $ind = index($ntxt, '(');
    $ntxt = substr($ntxt,0,$ind) if ($ind > 1);
    if ($ntxt =~ /^\(\s*\*\s*(\w+)\s*\)/) {
        $ntxt = $1;
    }
    for (my $i = 0; $i < 4; $i++) {
        $ntxt =~ s/"$//;    # drop any trailing double quote
        $ntxt = substr($ntxt,0,length($ntxt)-1) while ($ntxt =~ /\s$/); # drop trailing spacey
    }
    return $txt if (length($ntxt) == 0);
    return $ntxt;
}


sub ignore_lines_NOT_USED($) {
    my ($line) = shift;
    my $xcnt = 0;
    if ($line =~ /^------ Build started/) {
        $xcnt++;
    } elsif ($line =~ /^Linking/) {
        $xcnt++;
    } elsif ($line =~ /^Build log was saved/) {
        $xcnt++;
    } elsif ($line =~ /^========== Build:/) {
        $xcnt++;
    } elsif ($line =~ /.+-\s+(\d+)\s+error\(s\),\s+(\d+)\s+warning\(s\)/) {
        $xcnt++;
    } elsif ($line =~ /.+\s+:\s+fatal\s+error\s+.+:\s+(\d+)\s+unresolved/) {
        $xcnt++;
    } elsif ($line =~ /^LINK\s+:\s+/) {
        $xcnt++;
    }
    return $xcnt;
}


sub process_file($) {
    my ($fil) = @_;
    my %hash = ();
    my (@lines, $lncnt, $line, $lnn, $llnn, $missed, $icnt, $xcnt, $acnt);
    my ($type,$pmiss);
    if (!open INF, "<$fil") {
        prt("ERROR: Can not open file [$fil]\n");
        return \%hash;
    }
    @lines = <INF>;
    close INF;
    $lncnt = scalar @lines;
    prt( "Processing $lncnt lines, from [$fil]\n" );
    $lnn = 0;
    $llnn = 0;
    $xcnt = 0;
    $acnt = 0;
    $pmiss = '';
    foreach $line (@lines) {
        chomp $line;
        $lnn++;
        $type = 0;
        # find like libtheora_static.lib(enquant.obj) : error LNK2001: unresolved external symbol _oggpackB_write
        if ($line =~ /\s+unresolved\s+external\s+symbol\s+/) {
            $missed = '';
            if ($line =~ /\s+unresolved\s+external\s+symbol\s+(\w+)\s*/) {
                $missed = $1;
                prt("$lnn: $missed\n" ) if ($dbg01);
                $llnn = $lnn + 1;
            } elsif ($line =~ /\s+unresolved\s+external\s+symbol\s+"(.+)"\s*/) {
                $missed = remove_res_words($1);
                prt("$lnn: [$missed]\n" ) if ($dbg01);
                $llnn = $lnn + 1;
                $type = 1;
            } else {
                prt("$lnn: What is this? [$line]?\n");
            }
            if (length($missed)) {
                if (defined $hash{$missed}) {
                    $hash{$missed}++;
                } else {
                    $hash{$missed} = 1;
                    if ($type == 1) {
                        prt("$lnn: [$missed] ln=[$line]\n" ) if ($dbg02);
                    }
                    if ($pmiss eq $missed) {
                        prt("How come NOT in HASH???\n");
                        exit(1);
                    }
                    $pmiss = $missed;
                }
                $acnt++;
            }
        } elsif ($line =~ /^------ Build started/) {
            $llnn = $lnn + 1;
            $xcnt++;
        } elsif ($line =~ /^Linking/) {
            $llnn = $lnn + 1;
            $xcnt++;
        } elsif ($line =~ /^Build log was saved/) {
            $llnn = $lnn + 1;
            $xcnt++;
        } elsif ($line =~ /^========== Build:/) {
            $llnn = $lnn + 1;
            $xcnt++;
        } else {
            #   3: No find in [LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library]?
            # 383: No find in [bin\HandBrakeCLID.exe : fatal error LNK1120: 293 unresolved externals]?
            # 385: No find in [HandBrakeCLI - 380 error(s), 1 warning(s)]?
            if ($line =~ /.+-\s+(\d+)\s+error\(s\),\s+(\d+)\s+warning\(s\)/) {
                $llnn = $lnn + 1;
                $xcnt++;
            } elsif ($line =~ /.+\s+:\s+fatal\s+error\s+.+:\s+(\d+)\s+unresolved/) {
                $llnn = $lnn + 1;
                $xcnt++;
            } elsif ($line =~ /^LINK\s+:\s+/) {
                $llnn = $lnn + 1;
                $xcnt++;
            } else {
                prt("$lnn: No case for [$line]?\n");
            }
        }
    }

    $icnt = scalar keys(%hash);
    prt("In $lncnt lines, excluding $xcnt, got $acnt items, $icnt different... (".($lncnt - $xcnt).")\n");
    return \%hash;
}

sub show_hash($) {
    my ($hr) = @_;
    my $keys = scalar keys(%{$hr});
    my ($k,$v,$wrap,$max,$grp,$tmp);
    my $mpl = 5;
    my %grps = ();
    prt( "Got $keys keys...\n" );

    $wrap = 0;
    $max = 0;
    prt( "Single items...\n" );
    foreach $k (sort keys %{$hr}) {
        $grp = (length($k) > 4 ? substr($k,0,4) : $k);
        $v = ${$hr}{$k};
        if ($k =~ /^\w+$/) {
            prt( "$k($v) ");
            $max = $v if ($v > $max);
            $wrap++;
            if ($wrap == $mpl) {
                $wrap = 0;
                prt("\n");
            }
            if (defined $grps{$grp}) {
                $grps{$grp}++;
            } else {
                $grps{$grp} = 1;
            }
        }
    }
    prt("\n") if ($wrap);

    prt( "\nFunctions...\n" );
    foreach $k (keys %{$hr}) {
        $v = ${$hr}{$k};
        if ( !($k =~ /^\w+$/) ) {
            prt( "$k($v)\n");
        }
    }

    # most
    $wrap = 0;
    foreach $k (sort keys %{$hr}) {
        $v = ${$hr}{$k};
        if ($k =~ /^\w+$/) {
            if ($v == $max) {
                $wrap++;
            }
        }
    }
    if ($wrap) {
        prt("\n$wrap Item(s) with highest count...\n" );
        $wrap = 0;
        foreach $k (sort keys %{$hr}) {
            $v = ${$hr}{$k};
            if ($k =~ /^\w+$/) {
                if ($v == $max) {
                    $wrap++;
                    prt( "$wrap: $k = $v\n");
                }
            }
        }
    }

    $wrap = scalar keys(%grps);
    prt( "\nKeys can be divided in $wrap groups...\n" );
    foreach $grp (sort keys %grps) {
        $v = $grps{$grp};
        prt("Group $grp($v): ");
        foreach $k (keys %{$hr}) {
            $tmp = (length($k) > 4 ? substr($k,0,4) : $k);
            if ($k =~ /^\w+$/) {
                if ($tmp eq $grp) {
                    prt("$k ");
                }
            }
        }
        prt("\n");
    }
}

my $hash_ref = process_file($in_file);
show_hash($hash_ref);

close_log($outfile,$load_log);
exit(0);

# eof
