# scanvc.pl
# 07/07/2010 - Add ability to set debug items - svc_set_dbg_item(val)
# 2010.05/31 - Minor fix to '$cf_dn_no_s' - or in, not '&', and add 'scanvc' to WARNINGS...
# 2010/05/06 - fix bug [dbg_v32] 015:048-059: VCCLCompilerTool:CompileAs: set [-NEW_RT-] from [/MDd] to [/MDd /]
# 2010/05/05 - minor fix for item like '"2">', which should be fixed upstream...
# 2010/05/01 - added 'strict' and 'warnings', and fixed lots of things
# 2010/04/23 - handle different xml lining - added sub get_clean_seek_item($) to ensure trailing '>' also removed
# 2010/03/08 - Remove write tempvcx.xml from scanvc.pl, or only to perl base directory...
# 2010/03/01 - if vcproj contains IgnoreDefaultLibraryNames="MSVCRT", add /nodefaultlib:"MSVCRT" to DSP
#   Add this to the -NEW_OUT- substitution parameter - that is to the # ADD LINK32 line.
#   Can be DIFFERENT per configuration.
# 2010/01/14 = Can be 'Description="&quot;Copy Library to plib directory&quot;"' Note double QUOTES as '"', AND '&quot;', OR WORSE!!!
# 2009/10/25 - move out of fgscanvc03.pl
# Subroutine name list - generated by chkperl.pl, on 20091029
# svc_get_dbg_stg(), get_default_ref_hash($), get_item_from_hash($$), get_per_file_hash($), get_last_src_offset($),
# get_current_xline_stg($), get_project_configs($), svc_get_project_name_stg($), get_project_srcs_ref($), add_vs_project_items($$$$),
# extract_curr_items($$$$$$$$$), svc_add_props_to_hash($$$$), add_vs_config_item($), show_hash_results3($), massage_command3($),
# get_line_hash_missed($$$), get_ct_line_hash_missed($$), process_vc_conf_tool_tag($), process_vc_file_tool_tag($),
# get_cfgs_array_order($$), add_any_per_file_items3($), get_xln_stg($$), process_VC_lines($$$), svc_xml_to_lines($$),
# process_VCPROJ3
# === End sub name list ===
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;

# 2010/05/01 - additions
my %compileas = (
	0 => "",
	1 => "TC",
	2 => "TP"
	);

my $write_temp_xml = 0;     # only really needed for DEBUG, and should be to perl base directory if done...
my $sc_last_intdir = '';

# CURR_FLAG values
my $cf_apptype = 0x00000001;    # signal, we have an APP TYPE - VERY IMPORTANT
my $cf_apptyp2 = 0x00000002;    # APP TYPE
my $cf_dn_lt_t = 0x80000000;    # if (!$dn_lt_noo)
my $cf_dn_no_s = 0x40000000;    # DOES NOT CONTAIN(7) 'SubSystem' NOR 'ImportLibrary' only ONCE

my $flist_done_once = 0; # only once for prtw( "WARNING:$xln DOES NOT CONTAIN(12) '$seek'? [$line] [$fil]\n" );
my $g_sc_inconfigs = 0;
my $g_sc_infiles = 0;
my $g_sc_infconf = 0;
my $g_sc_act_vcproj = '';
my ($g_sc_act_name,$g_sc_act_path); # = fileparse($g_sc_act_vcproj);

# DEBUG
my $dbg_v01 = 0;    # prt( "[dbg_v01] $xln Bgn VisualStudioProject name=[$val1], type=[$val3], vers=[$val2]\n" ) if ($dbg_v01);
my $dbg_v02 = 0;    # prt( "[dbg_v02] $xln Bgn Configurations ($tag)\n" ) if ($dbg_v02);
my $dbg_v03 = 0;    # prt( "[dbg_v03] seek=PreprocessorDefinitions = [$ppdefs] [$fil]\n" ) if ($dbg_v03);
my $dbg_v04 = 0;    # prt( "[dbg_v04] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(2) 'PreprocessorDefinitions' [$line]\n" ) if ($dbg_v04);
my $dbg_v05 = 0;    # prt( "[dbg_v05] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(3) 'RuntimeLibrary' [$line]\n" ) if ($dbg_v05);
my $dbg_v06 = 0;    # prt( "[dbg_v06] $xln Bgn ConfigurationName=[$confname] conf=[$conf] [$line] ...($tag)\n" ) if ($dbg_v06);
my $dbg_v07 = 0;    # prt( "[dbg_v07] $xln Bgn Filter name=[$fname] list=[$flist] ($tag)\n" ) if ($dbg_v07);
my $dbg_v08 = 0;    # prt( "[dbg_v08] $xln Bgn File [$last_src] ($tag)\n" ) if ($dbg_v08);
my $dbg_v09 = 0;    # prt( "[dbg_v09] $xln Bgn FileConfiguration [$last_src] ($tag)\n" ) if ($dbg_v09);
my $dbg_v10 = 0;    # prt( "[dbg_v10] $xln Last = $last_src=[".${$src_ref}[-1][0]."] $fconf ExcludedFromBuild = $adddefs\n" ) if ($dbg_v10);
my $dbg_v11 = 0;    # prt( "[dbg_v11] $xln NO keys in per file hash...\n") if ($dbg_v11);
my $dbg_v12 = 0;    # prt( "[dbg_v12] $xln Bgn Files ($tag)\n" ) if ($dbg_v12);
my $dbg_v13 = 0;    # prt( "[dbg_v13] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(1) 'AdditionalIncludeDirectories' [$line]\n" ) if ($dbg_v13);
my $dbg_v15 = 0;    # prt( "[dbg_v15] $xln got POST event [$ppdefs]\n" ) if ($dbg_v15);
my $dbg_v16 = 0;    # prtw( "WARNING:$xln 'VCCLinkerTool' DOES NOT CONTAIN(4) '$seek' [$line] [$fil]\n" ) if ($dbg_v16);

my $dbg_v20 = 0;    # prt( "[dbg_v20] $xln Changed(2) [IntermediateDirectory] from [".${$dsp_sub_sub}{$var2}."] to [".add_quotes($adddeps)."]\n" ) if ($dbg_v20);
my $dbg_v22 = 0;    # prt( "[dbg_v22] $xln seek=AdditionalDependencies: Set [$var1] to [$adddeps]\n" ) if ($dbg_v22);
my $dbg_v23 = 0;    # prt( "[dbg_v23] $xln seek=AdditionalLibraryDirectories: Set [$var1] to [$tmp] from [$adddeps]\n" ) if ($dbg_v23);
my $dbg_v24 = 0;    # prt( "[dbg_v24] $xln seek=OutputFile: Set [$var2] to [$newval]\n" ) if ($dbg_v24);
my $dbg_v25 = 0;    # prt( "[dbg_v25] " ); prt( "$xln VCCLCompilerTool:AdditionalIncludeDirectories: RESET [$var1] to [$newval] from [$curval]\n" );
my $dbg_v26 = 0;    # prt( "[dbg_v26] Processing prop file(s) [$fils] cnt=$cnt\n" ) if ($dbg_v26); and MORE
my $dbg_v27 = 0;    # prt( "[dbg_v27] $xln got CUSTOM event [$pname] [$fconf] [$adddefs]\n" ) if ($dbg_v27);
my $dbg_v28 = 0;    # prt( "[dbg_v28] CUSTOM event [$tmp]\n" ) if ($dbg_v28);
my $dbg_v29 = 0;    # prt( "[dbg_v29] $xln Added CUSTOM build for source [$key]\n" ) if ($dbg_v29);
my $dbg_v30 = 0;    # prt( "[dbg_v30] CUSTOM build [$tmp]\n" ) if ($dbg_v30);
my $dbg_v31 = 0;    # prt( "[dbg_v31] " ); prt( "$xln conf=[$confname]:VCCLCompilerTool: [$line]\n seeks=[$doneseeks]\n missed=[$missed] ($lhlen)\n" );
my $dbg_v32 = 0;    # prt( "[dbg_v32] $xln VCCLCompilerTool:CompileAs: set [$var3] to ".${$dsp_sub_sub}{$var3}."\n" ) if ($dbg_v32);
my $dbg_v33 = 0;    # prt( "[dbg_v33] $xln Changed(1) [OutputDirectory] from [".${$dsp_sub_sub}{$var1}."] to [".add_quotes($adddeps)."]\n" ) if ($dbg_v33);
my $dbg_v34 = 0;    # prt( "[dbg_v34] $xln file: 'VCCLCompilerTool' [$line] IGNORED CHECK ($lhlen)\n" ) if ($dbg_v34); AND 'VCResourceCompilerTool'
my $dbg_v35 = 0;    # prt( "[dbg_v35] $xln Project [$projname], APP_TYPE CONFIRMED per 'ImportLibrary' set to [$dnapptype]...\n" ) if ($dbg_v35);
my $dbg_v36 = 0;    # prt("[dbg_v36] Got description: [$ppdefs]\n") if ($dbg_v36); and more... 2010/01/14
my $dbg_v37 = 0;    # prt("[dbg_v37] Done process_VC_lines...\n") if ($dbg_v37);
my $dbg_v38 = 0;    # prt( "[dbg_v38] Processing project [$projname]...\n" ) if ($dbg_v38);
my $dbg_v39 = 0;    # prt( "[dbg_v39] $xln seek=IgnoreDefaultLibraryNames: Set [$var1] to [$tmp] from [$adddeps] prev [$curval]\n" ) if ($dbg_v39);
my $dbg_v40 = 0;    # prt("[dbg_v40] STORE:$tmp: In rcfgs (ra)[$confname], [$var1], [$conf], & \$dsp_sub_sub ] )\n") if ($dbg_v40); # ONLY STORE OF 'PROJECT_CFGS'

# forward
sub svc_xml_to_lines($$);

sub svc_get_dbg_stg() {
    my $dbs = '';
    if ($dbg_v01) { $dbs .= "v01 "; } if ($dbg_v02) { $dbs .= "v02 "; } if ($dbg_v03) { $dbs .= "v03 "; } 
    if ($dbg_v04) { $dbs .= "v04 "; } if ($dbg_v05) { $dbs .= "v05 "; } if ($dbg_v06) { $dbs .= "v06 "; }
    if ($dbg_v07) { $dbs .= "v07 "; } if ($dbg_v08) { $dbs .= "v08 "; } if ($dbg_v09) { $dbs .= "v09 "; }
    if ($dbg_v10) { $dbs .= "v10 "; } if ($dbg_v11) { $dbs .= "v11 "; } if ($dbg_v12) { $dbs .= "v12 "; }
    if ($dbg_v13) { $dbs .= "v13 "; } if ($dbg_v15) { $dbs .= "v15 "; } if ($dbg_v16) { $dbs .= "v16 "; }
    if ($dbg_v20) { $dbs .= "v20 "; } if ($dbg_v22) { $dbs .= "v22 "; } if ($dbg_v23) { $dbs .= "v23 "; }
    if ($dbg_v24) { $dbs .= "v24 "; } if ($dbg_v25) { $dbs .= "v25 "; } if ($dbg_v26) { $dbs .= "v26 "; }
    if ($dbg_v27) { $dbs .= "v27 "; } if ($dbg_v28) { $dbs .= "v28 "; } if ($dbg_v29) { $dbs .= "v29 "; }
    if ($dbg_v30) { $dbs .= "v30 "; } if ($dbg_v31) { $dbs .= "v31 "; } if ($dbg_v32) { $dbs .= "v32 "; }
    if ($dbg_v33) { $dbs .= "v33 "; } if ($dbg_v34) { $dbs .= "v34 "; } if ($dbg_v35) { $dbs .= "v35 "; }
    if ($dbg_v36) { $dbs .= "v36 "; } if ($dbg_v37) { $dbs .= "v37 "; } if ($dbg_v38) { $dbs .= "v38 "; }
    if ($dbg_v39) { $dbs .= "v39 "; } if ($dbg_v40) { $dbs .= "v40 "; } 
    return $dbs;
}
sub svc_set_dbg_val($) {
    my ($v) = shift;
    $dbg_v01 = $v; $dbg_v02 = $v; $dbg_v03 = $v; $dbg_v04 = $v; $dbg_v05 = $v; $dbg_v06 = $v; $dbg_v07 = $v; $dbg_v08 = $v;
    $dbg_v09 = $v; $dbg_v10 = $v; $dbg_v11 = $v; $dbg_v12 = $v; $dbg_v13 = $v; $dbg_v15 = $v; $dbg_v20 = $v; $dbg_v22 = $v;
    $dbg_v23 = $v; $dbg_v24 = $v; $dbg_v25 = $v; $dbg_v26 = $v; $dbg_v27 = $v; $dbg_v28 = $v; $dbg_v29 = $v; $dbg_v30 = $v;
    $dbg_v31 = $v; $dbg_v32 = $v; $dbg_v33 = $v; $dbg_v34 = $v; $dbg_v35 = $v; $dbg_v36 = $v; $dbg_v37 = $v; $dbg_v38 = $v;
    $dbg_v39 = $v; $dbg_v40 = $v;
}
sub svc_set_dbg_on()  { svc_set_dbg_val(1); }
sub svc_set_dbg_off() { svc_set_dbg_val(0); }
sub svc_set_dbg_item($$) {
    my ($val,$of) = @_;
    if ($val == 40) {
        $dbg_v40 = 1;
        return 0;
    }
    my $var = '$dbg_v'.$val;
    prt("Setting var [$var] to [$of]\n");
    my $v = eval $var;
    if (defined $v) {
        my $stg = "$var"." = $of;";
        eval $stg;
        return 0;
    }
    return 1;
}

sub get_default_ref_hash($) {
    my ($fil) = @_;
    my %hash = ();
    my $rh = \%hash;
    my ($nam,$dir,$ext) = fileparse($fil,qr/\.[^.]*/);
    $dir = cwd() if ($dir =~ /^\.(\\|\/)$/);
    $dir .= "\\" if (!($dir =~ /(\\|\/)$/));
    $dir =~ s/\//\\/g;
    ${$rh}{'PROJECT_VERS'} = 1; # version of the HASH
    ${$rh}{'PROJECT_FILE'} = $nam.$ext;  # 2010/05/07 - was $fil
    ${$rh}{'PROJECT_FDIR'} = $dir;
    ${$rh}{'PROJECT_DSPF'} = $nam.".dsp";
    ${$rh}{'PROJECT_FLAG'} = 0;
    ${$rh}{'PROJECT_APTP'} = '';
    ${$rh}{'PROJECT_CCNT'} = 0; # count of configurations
    ${$rh}{'PROJECT_CFGS'} = [ ];
    ${$rh}{'PROJECT_SRCS'} = [ ];
    ${$rh}{'CURR_FLAG'}    = 0;
    ${$rh}{'CURR_LOFF'}    = 0; # last/current source OFFSET
    ${$rh}{'CURR_LINE'}    = '<not started>';
    return $rh;
}

my %ignore_in_ct_lh = ( 
    'AssemblerListingLocation' => 1,
    'EnableFunctionLevelLinking' => 2,
    'InlineFunctionExpansion' => 3,
    'ObjectFile' => 4,
    'Optimization' => 5,
    'PrecompiledHeaderFile' => 6,
    'ProgramDataBaseFileName' => 7,
    'RuntimeTypeInfo' => 8,
    'StringPooling' => 9,
    'SuppressStartupBanner' => 10,
    'WarningLevel' => 11,
    '<Tool' => 12,
    'Name' => 13,
    '/>' => 14,
    'BasicRuntimeChecks' => 14,
    'DebugInformationFormat' => 15,
    'MinimalRebuild' => 16
    );

sub get_item_from_hash($$) {
    my ($rh,$key) = @_;
    my ($item);
    if (defined ${$rh}{$key}) {
        $item = ${$rh}{$key};
    } else {
        prtw("ERROR:scanvc: Key [$key] NOT present in HASH!\n");
        pgm_exit(1,"ENSURE key [$key] ADDED TO HASH!\n");
    }
    return $item;
}

sub get_per_file_hash($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'CURR_PFHR');
}

sub get_last_src_offset($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'CURR_LOFF'); # last/current source OFFSET
}

sub get_current_xline_stg($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'CURR_LINE'); # = $xln
}

sub get_project_configs($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'PROJECT_CFGS');
}

sub svc_get_project_name_stg($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'PROJECT_NAME');
}

sub get_project_srcs_ref($) {
    my ($rh) = shift;
    return get_item_from_hash($rh,'PROJECT_SRCS');
}



sub add_vs_project_items($$$$) {
    my ($rh, $rah, $xln, $fil) = @_;
    my ($seek,$val1,$val2,$val3);
    $seek = 'Name';
    if (!defined ${$rah}{$seek} ) {
         mydie( "ERROR: 'VisualStudioProject' DOES NOT CONTAIN(14) a project NAME! NO GO!! ABORTING!!! [$fil]\n" );
    }
    $val1 = strip_quotes(${$rah}{$seek});   # get the (all important) PROJECT NAME
    $val2 = '';
    $val3 = '';
    $seek = 'ProjectType';
    if (defined ${$rah}{$seek}) {
        $val3 = strip_dotrel(strip_quotes(trim_all(${$rah}{$seek})));
    }
    $seek = 'Version';
    if (defined ${$rah}{$seek}) {
        $val2 = strip_dotrel(strip_quotes(trim_all(${$rah}{$seek})));
    }
    ${$rh}{'PROJECT_NAME'} = $val1; # SET the PROJECT NAME!
    ${$rh}{'PROJECT_MSCV'} = $val2;
    ${$rh}{'PROJECT_TYPE'} = $val3;
    prt( "[dbg_v01] $xln Bgn VisualStudioProject name=[$val1], type=[$val3], vers=[$val2]\n" ) if ($dbg_v01);
}

# extract_curr_items($rh,\$xln,\$rah,\$tag,\$pname,\$fil,\$projname,\$line,\$flag);
sub extract_curr_items($$$$$$$$$) {
    my ($rh,$rxln,$rrah,$rtag,$rpnm,$rfil,$rnam,$rlin,$rflg) = @_;
    ${$rxln} = ${$rh}{'CURR_LINE'};
    ${$rrah} = ${$rh}{'CURR_HASH'};
    ${$rtag} = ${$rh}{'CURR_TAG'};
    ${$rpnm} = ${$rh}{'CURR_NAME'};
    ${$rlin} = ${$rh}{'CURR_TEXT'};
    ${$rfil} = ${$rh}{'PROJECT_FILE'};
    ${$rnam} = ${$rh}{'PROJECT_NAME'};
    ${$rflg} = ${$rh}{'CURR_FLAG'};
}

# Specifically interested in addeding
# $seek = 'OutputDirectory';
# $seek = 'IntermediateDirectory';
# %comspec% /k ""C:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"" x86
# $(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops
sub svc_add_props_to_hash($$$$) {     # ( $adddeps, \%atthash );
    my ($fil,$fils,$rh,$props) = @_;
    my (@arr,$cnt,$i,$nm,$dir,$itm,$ff,%lnmap,$xlncnt,@lines,$lncnt,$lnnum);
    my ($line,$xln,@attribs,$ratthash,$key,$val);
    my ($val2, $tkey, $tag, $idir);
    my @parr = split(";",$props);
    ($nm,$dir) = fileparse($fil);
    $dir = strip_dotrel($dir);
    @arr = split(/[;,]/,$fils); # can be MORE THAN ONE property sheet
    $cnt = scalar @arr;
    prt( "[dbg_v26] Processing prop file(s) [$fils] cnt=$cnt\n" ) if ($dbg_v26);
    for ($i = 0; $i < $cnt; $i++) {
        $itm = strip_dotrel(trim_all(strip_quotes(trim_all($arr[$i]))));
        if ($itm =~ /^\$\(VCInstallDir\)/i) {
            $idir = '';
            if ( get_vs_install_dir(\$idir) && length($idir) ) {
                $itm =~ s/^\$\(VCInstallDir\)//i;
                $dir = $idir."\\VC";
                prt( "[dbg_v26] Setting dir to [$dir], for item [$itm]\n" ) if ($dbg_v26);
            } else {
                prtw("WARNING:scanvc: Failed to get VCINSTALLDIR!\n");
            }
        }
        # ===========================================================================
        $ff = $dir;
        $ff .= "\\" if ( length($dir) && !(($dir =~ /(\\|\/)$/)||($itm =~ /^(\\|\/)/)) );
        $ff .= $itm;
        ###prt( "Finding [$ff], from [$dir]/[$itm]...\n" );
        # ===========================================================================
        if (-f $ff) {
            prt( "[dbg_v26] Loading inherited props [$ff]..\n" ) if ($dbg_v26);
            if (open INF, "<$ff") {
                %lnmap = ();
                @lines = <INF>;
                close INF;
                $xlncnt = scalar @lines;
                @lines = svc_xml_to_lines(\%lnmap, \@lines);
                $lncnt = scalar @lines;
                $lnnum = 0;
                prt( "[dbg_v26] Processing file [$ff], $lncnt lines ($xlncnt) ...\n" ) if ($dbg_v26);
                foreach $line (@lines) {
                    $lnnum++;
                    $xln = $lnmap{$lnnum};
                    $ratthash = line_2_hash_on_equals($line,$lnnum);
                    $tag = $attribs[0];
                    foreach $key (sort keys %{$ratthash}) {
                        $val = ${$ratthash}{$key};
                        $tkey = substr($key,16);
                        if (length($val) && is_in_array($tkey,@parr)) {
                            if (defined ${$rh}{$tkey}) {
                                $val2 = ${$rh}{$tkey};
                                prtw( "WARNING:scanvc: key=[$tkey], value=[$val2] REPLACED with [$val] [$fil]\n" );
                            } else {
                                prt( "[dbg_v26] Added key=[$tkey], value=[$val]\n" ) if ($dbg_v26);
                                ${$rh}{$tkey} = $val;
                            }
                        }
                    }
                }
            } else {
                prtw( "WARNING:scanvc: FAILED to OPEN inherited props [$ff] [$fil]\n" );
            }
        } else {
            prtw( "WARNING:scanvc: FAILED to FIND inherited props [$itm] [$ff]! in [$fil]\n" );
        }
    }
}

sub add_vs_config_item($) {
    my ($rh) = @_;
    my ($xln,$rah,$tag,$pname,$fil,$projname,$line,$flag);
    my ($tmp);  # 2010/05/05 
    extract_curr_items($rh,\$xln,\$rah,\$tag,\$pname,\$fil,\$projname,\$line,\$flag);
    my $conf = $pname;              # like ReleaseSSE|WIN32
    my @arr = split(/\|/,$pname);   # split it
    my $confname = $arr[0];         # get just 'ReleaseSSE' 
    ${$rh}{'CURR_CONF'} = $confname;
    ${$rh}{'CURR_CON1'} = $conf;
    my $dnapptype = ${$rh}{'PROJECT_APTP'};
    # 28/09/2008 - note, has quotes added
    # "-NEW_OUTD-" $out_dir
    # "-NEW_INTER-" $int_dir
    my $var1 = "-NEW_OUTD-";
    my $var2 = "-NEW_INTER-";
    my ($dsp_sub_sub, $seek, $adddeps, $apptype);
    if ($conf =~ /Debug/i) {
        $dsp_sub_sub = get_default_sub3(1);
    } else {
        $dsp_sub_sub = get_default_sub3(0);
        # 2010/05/05 - assume 'DLL' means RELEASE without WARNING
        if (!(($conf =~ /Release/i)||($conf =~ /DLL/))) {
            prtw( "WARNING:scanvc:$xln: pname=[$conf] NOT Debug or Release - def to REL! [$fil]\n" );
        }
    }
    my $rcfgs = get_project_configs($rh);   # 'PROJECT_CFGS'
    $tmp = ${$rh}{'PROJECT_CCNT'};
    $tmp++;
    prt("[dbg_v40] STORE:$tmp: In rcfgs (ra)[$confname], [$var1], [$conf], & \$dsp_sub_sub ] )\n") if ($dbg_v40); # ONLY STORE OF 'PROJECT_CFGS'
    push(@{$rcfgs}, [ $confname, $var1, $conf, $dsp_sub_sub ]); # ONLY STORE OF 'PROJECT_CFGS'
    ${$rh}{'PROJECT_CCNT'}++;   # count of stored 'PROJECT_CFGS
    ${$rh}{'CURR_DSUB'} = $dsp_sub_sub;
    #push(@configs,[ $pname, $var1, $conf ]);
    #  // This is an internal type to Visual Studio, it seems that:
    #  // 4 == static library
    #  // 2 == dll
    #  // 1 == executable - note, subsytem decides if this remains 'Application' or becomes 'Console Application'
    #  // 10 == utility
    $seek = 'ConfigurationType';
    if (defined ${$rah}{$seek}) {
        $tmp = trim_all(${$rah}{$seek});
        $tmp =~ s/>$//; # remove any trailing
        $adddeps = strip_quotes(trim_all($tmp));
        $apptype = get_app_conf_type($adddeps);
        $flag |= $cf_apptype;  # signal, we have an APP TYPE - VERY IMPORTANT
        if (length($dnapptype)) {
            if ($flag & $cf_apptyp2) {
                $apptype = $dnapptype;
            }
            if ($dnapptype ne $apptype) {
                prt( "$xln Project [$projname], APP_TYPE RESET per 'ConfigurationType' [$apptype] from [$dnapptype]...\n" );
                $dnapptype = $apptype;
            }
        } else {
            prt( "$xln Project [$projname], APP_TYPE per 'ConfigurationType' [$apptype]...\n" );
            $dnapptype = $apptype;
        }
        ${$rh}{'PROJECT_APTP'} = $dnapptype;
        ${$rh}{'CURR_FLAG'} = $flag;
    } else {
        prtw( "WARNING:scanvc:$xln: DOES NOT CONTAIN(8) 'ConfigurationType' [$line] [$fil]\n" );
    }
    # 2009/10/15 - handle better 'InheritedPropertySheets=".\vc9_lame_config.vsprops;.\arch_nasm.vsprops"'
    # and this PROPS (vsprops) MAY contain the following items
    $seek = 'InheritedPropertySheets';
    if (defined ${$rah}{$seek}) {
        $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
        svc_add_props_to_hash( $fil, $adddeps, $rah, "OutputDirectory;IntermediateDirectory" );
    }
    $seek = 'OutputDirectory';
    if (defined ${$rah}{$seek}) {
        # 20090808 - improved to handle
        # EG: 9:18-24: Changed(1) OutputDirectory from ["Debug"]
        # to ["$(SolutionDir)$(ConfigurationName)"]
        #$adddeps = add_quotes(strip_dotrel(strip_quotes(trim_all($atthash{$seek}))));
        $adddeps = strip_dotrel(strip_quotes(trim_all(${$rah}{$seek})));
        $adddeps =~ s/\$\(SolutionDir\)//;
        $adddeps =~ s/\$\(ConfigurationName\)/$confname/;
        if ( ${$dsp_sub_sub}{$var1} ne add_quotes($adddeps)) {
            prt( "[dbg_v33] $xln Changed(1) [OutputDirectory] from [".${$dsp_sub_sub}{$var1}."] to [".add_quotes($adddeps)."]\n" ) if ($dbg_v33);
        }
        ${$dsp_sub_sub}{$var1} = add_quotes($adddeps);
    } else {
        prtw( "WARNING:scanvc:xln: DOES NOT CONTAIN(9) '$seek' [$line] [$fil]\n" );
    }
    $seek = 'IntermediateDirectory';
    if (defined ${$rah}{$seek}) {
        # 20090808 - improved to handle
        # 9:18-24: Changed(2) IntermediateDirectory from ["Debug"] 
        # to ["$(ConfigurationName)"]
        # $adddeps = add_quotes(strip_dotrel(strip_quotes(trim_all($atthash{$seek}))));
        $adddeps = strip_dotrel(strip_quotes(trim_all(${$rah}{$seek})));
        $adddeps =~ s/\$\(SolutionDir\)//;
        $adddeps =~ s/\$\(ConfigurationName\)/$confname/;
        if (${$dsp_sub_sub}{$var2} ne add_quotes($adddeps)) {
            prt( "[dbg_v20] $xln Changed(2) [IntermediateDirectory] from [".${$dsp_sub_sub}{$var2}."] to [".add_quotes($adddeps)."]\n" ) if ($dbg_v20);
        }
        $sc_last_intdir = $adddeps;
        ${$dsp_sub_sub}{$var2} = add_quotes($sc_last_intdir);
    } else {
        prtw( "WARNING:scanvc:$xln DOES NOT CONTAIN(10) '$seek' [$line] [$fil]\n" );
    }
    prt( "[dbg_v06] $xln Bgn ConfigurationName=[$confname] conf=[$conf] [$line] ...($tag)\n" ) if ($dbg_v06);
}

#    my %prj_key_info = (
#        'PROJECT_CFGS' => '<A> Configurations found',
#        'PROJECT_NAME' => '<S> Name of project:',
#        'PROJECT_CCNT' => '<N> Count of configs',
#        'PROJECT_FILE' => '<S> Input File',
#        'PROJECT_SRCS' => '<A> Array of sources',
#        'PROJECT_TYPE' => '<S> MSVS type',
#        'PROJECT_VERS' => '<N> HASH Version',
#        'PROJECT_MSCV' => '<S> MSVC Version',
#        'PROJECT_APTP' => '<S> Application Type',
#        'PROJECT_FLAG' => '<N> Project flag'
#    );
sub get_prj_key_info_ref() {
    my %prj_key_info = (
        'PROJECT_CFGS' => '<A> Configs found:    ',
        'PROJECT_NAME' => '<S> Name of project:  ',
        'PROJECT_CCNT' => '<N> Count of configs: ',
        'PROJECT_FILE' => '<S> Input File:       ',
        'PROJECT_FDIR' => '<S> Input Directory:  ',
        'PROJECT_DSPF' => '<S> Output File:      ',
        'PROJECT_SRCS' => '<A> Array of sources: ',
        'PROJECT_TYPE' => '<S> MSVS type:        ',
        'PROJECT_VERS' => '<N> HASH Version:     ',
        'PROJECT_MSCV' => '<S> MSVC Version:     ',
        'PROJECT_APTP' => '<S> Application Type: ',
        'PROJECT_FLAG' => '<N> Project flag:     '
    );
    return \%prj_key_info;
}

#sub show_new_hash_ref($) {
sub show_hash_results3($) {
    my ($nhr) = @_;
    my @arr = keys( %{$nhr} );
    my $cnt = scalar @arr;
    prt( "Show of NEW hash reference, with up to $cnt keys...\n" );
    my ($key,$val,$inf,$acnt,$src);
    my ($grp,$flt,$min1,$min2,$len);
    my ($i,$i2,$msg,$rsb,$scnt,@karr);
    my ($ky,$v2,$sdir,$ff,$ok);
    $key = 'PROJECT_FDIR';
    $sdir = ${$nhr}{$key};
    $cnt = 0;
    my $rpki = get_prj_key_info_ref();
    foreach $key (@arr) {
        $val = ${$nhr}{$key};
        if ( $key =~ /^CURR_/ ) {
            # ignore these CURRENT state items
        } else {
            $cnt++;
            if (defined ${$rpki}{$key}) {
                $inf = ${$rpki}{$key};
                if (($inf =~ /^<S>/)||($inf =~ /^<N>/)) {
                    prt( "$key $inf =[$val]\n" );
                } else {
                    $acnt = scalar @{$val};
                    prt( "$key $inf =(ARRAY of count $acnt)\n" );
                    if ($key eq 'PROJECT_SRCS') {
                        $min1 = 0;
                        $min2 = 0;
                        for ($i = 0; $i < $acnt; $i++) {
                            $src = ${$val}[$i][0];
                            $grp = ${$val}[$i][1];
                            $flt = ${$val}[$i][2];
                            $len = length($src);
                            $min1 = $len if ($len > $min1);
                            $len = length($grp);
                            $min2 = $len if ($len > $min2);
                        }
                        for ($i = 0; $i < $acnt; $i++) {
                            $i2 = $i + 1;
                            $src = ${$val}[$i][0];
                            $grp = ${$val}[$i][1];
                            $flt = ${$val}[$i][2];
                            $ff = fix_rel_path3($sdir.$src,'show_hash_results3'); # in fgutils02.pl
                            $ok = (-f $ff) ? 'ok' : 'NF';
                            $src .= ' ' while(length($src) < $min1);
                            $grp .= ' ' while(length($grp) < $min2);
                            $msg = sprintf("%3d:",$i2);
                            prt( "$msg $src $grp $flt $ok\n" );
                        }
                    } elsif ($key eq 'PROJECT_CFGS') {
                        # my $rcfgs = ${$rh}{'PROJECT_CFGS'};
                        #                   0       1      2      3
                        # push(@{$rcfgs}, [ $pname, $var1, $conf, $dsp_sub_sub ]);
                        for ($i = 0; $i < $acnt; $i++) {
                            $i2 = $i + 1;
                            $src = ${$val}[$i][0];
                            $grp = ${$val}[$i][1];
                            $flt = ${$val}[$i][2];
                            $rsb = ${$val}[$i][3];
                            @karr = keys(%{$rsb});
                            $scnt = scalar @karr;
                            $msg = sprintf("%3d:",$i2);
                            prt( "$msg $src $grp $flt $scnt\n" );
                            foreach $ky (@karr) {
                                $v2 = ${$rsb}{$ky};
                                prt( "  $ky = [$v2]\n" );
                            }
                        }

                    }
                }
            } else {
                prtw( "WARNING:scanvc: [$key] NOT IN INFO LIST, so do not know type! see sub get_prj_key_info_ref() & \%prj_key_info\n" );
            }
        }
    }
    prt("Done show of $cnt keys...\n");
}

# seems MSVC8, and maybe others, when converting the MSVC6 DSP
# to a VCPROJ file can NOT tollerate a command
# ending in a '\' character, without quotes around it
# #########################################################
sub massage_command3($) {
    my ($txt) = shift;
    if ($txt =~ /\\$/) {
        my ($len, $ch, $bgn, $end);
        # need to back up to previous space,
        # and add quotes around the last command
        $len = length($txt);
        while ($len) {
            $len--;
            $ch = substr($txt,$len,1);
            if ($ch eq ' ') {
                last;
            }
        }
        if ($len) {
            $len++;
            $bgn = substr($txt,0,$len);
            $end = substr($txt,$len);
            $txt = $bgn.add_quotes($end);
        }
    }
    return $txt;
}

sub get_line_hash_missed($$$) {
    my ($rah,$dnsks,$ign) = @_;
    my @arr1 = split(/\|/,$dnsks);
    my @arr2 = sort(keys(%{$rah}));
    my $missed = '';
    my ($kitm,$ditm,,$fnd);
    foreach $kitm (@arr2) {  # process the keys
        if ( !defined ${$ign}{$kitm} ) {
            $fnd = 0;
            foreach $ditm (@arr1) {
                if ($kitm eq $ditm) {
                    $fnd = 1;
                    last;
                }
            }
            if (!$fnd) {
                $missed .= '|' if (length($missed));
                $missed .= $kitm;
            }
        }
    }
    #prtw( "WARNING:scanvc: MISSED [$missed]!\n" ) if (length($missed));
    return $missed;
}

sub get_ct_line_hash_missed($$) {
    my ($rah,$dnsks) = @_;
    return get_line_hash_missed($rah,$dnsks,\%ignore_in_ct_lh);
}

sub get_nodeflib_string($) {
    my ($add) = @_;
    my $rstg = '';
    if (length($add)) {
        $rstg = "/nodefaultlib:\"$add\"";
    }
    return $rstg;
}


sub process_vc_conf_tool_tag($) {
    my ($rh) = @_;
    my ($xln,$rah,$tag,$pname,$fil,$projname,$line,$flag);
    extract_curr_items($rh,\$xln,\$rah,\$tag,\$pname,\$fil,\$projname,\$line,\$flag);
    my ($var1,$var2,$var3,$seek);
    my ($ppdefs,$curval,$newval);
    my ($adddeps,$doneseeks,$missed);
    my ($tmp);  # 2010/05/01 
    my $last_src = ${$rh}{'CURR_LSRC'};
    my $last_nm  = ${$rh}{'CURR_LNME'};
    my $last_dir = ${$rh}{'CURR_LDIR'};
    my $last_ext = ${$rh}{'CURR_LEXT'};
    my $confname = ${$rh}{'CURR_CONF'};
    my $dsp_sub_sub = ${$rh}{'CURR_DSUB'};
    my $dnapptype = ${$rh}{'PROJECT_APTP'};
    my $lhlen = scalar keys(%{$rah});   # get item count in line hash
    my ($apptype,$outfile);

    $doneseeks = '';
    $outfile = '';
    if ($pname eq 'VCCLCompilerTool') {
        $var1 = '-NEW_INCS-';
        $var2 = '-NEW_DEFS-';
        $var3 = '-NEW_RT-';
        # '<Tool';
        # Name="VCCLCompilerTool"
        # Optimization="0"
        # AdditionalIncludeDirectories="../lib,../lib/curl_transport,...
        # PreprocessorDefinitions="_DEBUG;WIN32;_LIB;ABYSS_WIN32;CURL_STATICLIB" 
        # MinimalRebuild="true" 
        # BasicRuntimeChecks="3" 
        # RuntimeLibrary="1" 
        # PrecompiledHeaderFile=".\Debug\xmlrpccpp/xmlrpccpp.pch" 
        # AssemblerListingLocation=".\Debug\xmlrpccpp/" 
        # ObjectFile=".\Debug\xmlrpccpp/" 
        # ProgramDataBaseFileName=".\Debug\xmlrpccpp/" 
        # WarningLevel="3" 
        # SuppressStartupBanner="true" 
        # DebugInformationFormat="4" />]
        # WATCHOUT FOR 
        # AdditionalIncludeDirectories="..\..;&quot;..\..\..\zlib-1.2.3&quot;"
        
        $seek = 'AdditionalIncludeDirectories'; # -NEW_INCS_[DBG|REL]-
        $doneseeks = $seek;
        if ( defined ${$rah}{$seek} ) {
            $ppdefs = strip_quotes(trim_all(${$rah}{$seek}));
            $ppdefs =~ s/&amp;/&/g;     # 20081205 - added
            $ppdefs =~ s/&quot;/"/g;
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var1}) {
               $curval = ${$dsp_sub_sub}{$var1};
            }
            $newval = get_includes_string($ppdefs);
            if ($curval ne $newval) {
               ${$dsp_sub_sub}{$var1} = $newval;
               if ($dbg_v25) {
                   prt( "[dbg_v25] " );
                  if (length($curval)) {
                     prt( "$xln VCCLCompilerTool:AdditionalIncludeDirectories: RESET [$var1] to [$newval] from [$curval]\n" );
                  } else {
                     prt( "$xln VCCLCompilerTool:AdditionalIncludeDirectories: set [$var1] to [$newval]\n" );
                  }
               }
            }
        } else { 
            ${$dsp_sub_sub}{$var1} = '';
            prt( "[dbg_v13] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(1) 'AdditionalIncludeDirectories' [$line]\n" ) if ($dbg_v13);
        }

        $seek = 'PreprocessorDefinitions';
        $doneseeks .= "|$seek";
        if ( defined ${$rah}{$seek} ) {
            $ppdefs = strip_quotes(trim_all(${$rah}{$seek}));
            prt( "[dbg_v03] seek=PreprocessorDefinitions = [$ppdefs] [$fil]\n" ) if ($dbg_v03);
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var1}) {
               $curval = ${$dsp_sub_sub}{$var1};
            }
            $newval = get_defines_string($ppdefs);  # lib function - fgutils02.pl
            ${$dsp_sub_sub}{$var2} = $newval;
        } else {
            # 2009/09/22 - downgraded from WARNING, to a simple ADVICE
            # prtw( "WARNING:$lnnum:$xln 'VCCLCompilerTool' DOES NOT CONTAIN(2) '$seek' [$line]\n" );
            prt( "[dbg_v04] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(2) 'PreprocessorDefinitions' [$line]\n" ) if ($dbg_v04);
        }

        $seek = 'RuntimeLibrary';
        $doneseeks .= "|$seek";
        if ( defined ${$rah}{$seek} ) {
            $ppdefs = strip_quotes(trim_all(${$rah}{$seek}));
            ${$dsp_sub_sub}{$var3} = fg_get_runtime_val_2_lts($ppdefs);
        } else {
            # 2009/09/22 - downgraded from WARNING, to a simple ADVICE
            # prtw( "WARNING:$lnnum:$xln 'VCCLCompilerTool' DOES NOT CONTAIN(3) '$seek' [$line]\n" );
            prt( "[dbg_v05] $xln 'VCCLCompilerTool' DOES NOT CONTAIN(3) 'RuntimeLibrary' [$line]\n" ) if ($dbg_v05);
        }

        $seek = 'CompileAs';
        $doneseeks .= "|$seek";
        if ( defined ${$rah}{$seek} ) {
            $ppdefs = strip_quotes(trim_all(${$rah}{$seek}));
            $adddeps = ${$dsp_sub_sub}{$var3};
            if (defined $compileas{$ppdefs} ) {
               $tmp = $compileas{$ppdefs};
               if (length($tmp)) {
                   $adddeps .= " " if length($adddeps);
                   $adddeps .= "/$tmp";
               }
               $curval = '';
               if (defined ${$dsp_sub_sub}{$var3}) {
                  $curval = ${$dsp_sub_sub}{$var3};
               }
               $newval = $adddeps;
               if ($curval ne $newval) {
                  ${$dsp_sub_sub}{$var3} = $newval;
                  prt( "[dbg_v32] $xln VCCLCompilerTool:CompileAs: set [$var3] from [$curval] to [".${$dsp_sub_sub}{$var3}."]\n" ) if ($dbg_v32);
               } else {
                  prt( "[dbg_v32] $xln VCCLCompilerTool:CompileAs: NO CHANGE [$var3] as [$curval]\n" ) if ($dbg_v32);
               }
            } else {
               prtw( "WARNING:scanvc:$xln: VCCLCompilerTool:$seek: [$var3] NOT BEING SET! [$ppdefs] NOT IN 'compileas' hash [$fil]\n" );
            }
        }
        $missed = get_ct_line_hash_missed($rah,$doneseeks);
        if ($dbg_v31) {
            prt( "[dbg_v31] " );
            if (length($missed)) {
                prt( "$xln conf=[$confname]:VCCLCompilerTool: [$line]\n seeks=[$doneseeks]\n missed=[$missed] ($lhlen)\n" );
            } else {
                prt( "$xln conf=[$confname]:VCCLCompilerTool: [$line] ($lhlen)\n" ); # if ($dbg_v10);
            }
        }
    } elsif ($pname eq 'VCCustomBuildTool') {
        if (length($pname) && ($lhlen < 4)) {
            # this is ONLY <tag Name="blah" /> - so IGNORE
        } else {
            prtw( "WARNING:scanvc: MISSED CUSTOM BUILD: Check this conf=[$confname] TAG=$tag -\n [$line]\n" );
        }
    } elsif ($pname eq 'VCLinkerTool') {
        $var1 = '-NEW_LIBS-';
        $var2 = '-NEW_OUT-';
        # <Tool
        #  Name="VCLinkerTool"
        #  LinkLibraryDependencies="false"
        #  AdditionalDependencies="wsock32.lib SimGear.lib sg_d.lib ... "
        #  OutputFile="..\bin\FlightGearD.exe"
        #  LinkIncremental="0"
        #  SuppressStartupBanner="true"
        #  AdditionalLibraryDirectories="..\SimGear\$(IntDir); ..\PLIB, ..."
        #  IgnoreDefaultLibraryNames="MSVCRT" - may ONLY be in DEBUG
        #  GenerateDebugInformation="true"
        #  ProgramDatabaseFile=".\Debug/FlightGear.pdb"
        #  SubSystem="1"
        #  RandomizedBaseAddress="1"
        #  DataExecutionPrevention="0"
        #  TargetMachine="1" />
        $seek = 'AdditionalDependencies';
        if (defined ${$rah}{$seek} ) {
            $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
            $adddeps =~ s/&amp;/&/g;     # 20081205 - added
            $adddeps =~ s/&quot;/"/g;    # 20081205 - added
            $newval = $adddeps;
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var1}) {
               $curval = ${$dsp_sub_sub}{$var1};
            }
            if ($newval ne $curval) {
               ${$dsp_sub_sub}{$var1} = $adddeps;
               prt( "[dbg_v22] $xln seek=AdditionalDependencies: Set [$var1] to [$adddeps]\n" ) if ($dbg_v22);
            }
        } else {
            prtw( "WARNING:scanvc:$xln: 'VCCLinkerTool' DOES NOT CONTAIN(4) '$seek' [$line] [$fil]\n" ) if ($dbg_v16);
        }

        $seek = 'OutputFile';
        if (defined ${$rah}{$seek} ) {
            $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
            # 20090808 - handle substitution
            # EG: 39:138-149: seek=OutputFile: Set [-NEW_OUT_REL-] 
            # to [.\$(ProjectName).exe]
            $adddeps =~ s/\$\(ProjectName\)/$projname/;
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var2}) {
               $curval = ${$dsp_sub_sub}{$var2};
            }
            $newval = '/out:'.$adddeps;
            if ($curval ne $newval) {
               ${$dsp_sub_sub}{$var2} = $newval;
               prt( "[dbg_v24] $xln seek=OutputFile: Set [$var2] to [$newval]\n" ) if ($dbg_v24);
            }
            $outfile = $adddeps;
        } else {
           # 20090912 - downgraded to a NOTE only, and ONLY once per file
           if ( !($flag & $cf_dn_lt_t) ) {  # $dn_lt_noo
               # prtw( "WARNING:$lnnum:$xln 'VCCLinkerTool' DOES NOT CONTAIN(5) '$seek' [$line]\n" ) if ($dbg_v21);
               prt( "NOTE:scanvc:$xln: 'VCCLinkerTool' DOES NOT CONTAIN(5) 'OutputFile' [$line]\n" ); # if ($dbg_v21);
                $flag |= $cf_dn_lt_t;    # $dn_lt_noo = 1;
                ${$rh}{'CURR_FLAG'} |= $cf_dn_lt_t;
           }
        }

        $seek = 'AdditionalLibraryDirectories';
        if (defined ${$rah}{$seek} ) {
            # must deal with a crazy string like this -
            # AdditionalLibraryDirectories=
            # "&amp;quot,..\fltk-1.1.7\lib&amp;quot,..\SimGear.plib\Debug,
            # ..\zlib-1.2.3&amp;quot,..\plib,
            # ..\pthreads-w32-2-7-0-release&amp;quot,..\gettext\lib"
            # after quotes removed, and the first substitution
            # &quot,..\fltk-1.1.7\lib&quot,..\SimGear.plib\Debug,
            # ..\zlib-1.2.3&quot,..\plib,
            # ..\pthreads-w32-2-7-0-release&quot,..\gettext\lib
            # after next pair of subs
            # ",..\fltk-1.1.7\lib",..\SimGear.plib\Debug,
            # ..\zlib-1.2.3",..\plib,
            # ..\pthreads-w32-2-7-0-release",..\gettext\lib
            # then removing the auotes
            # ,..\fltk-1.1.7\lib,..\SimGear.plib\Debug,
            # ..\zlib-1.2.3,..\plib,
            # ..\pthreads-w32-2-7-0-release,..\gettext\lib
            $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
            $adddeps =~ s/&amp;/&/g;    # 20081205 - added
            $adddeps =~ s/&quot;/"/g;   # 20081205 - added
            $adddeps =~ s/&quot/"/g;    # 20081205 - added
            $adddeps =~ s/"//g;         # 20081205 - added
            $adddeps =~ s/^,//;         # 20081205 - added
            $tmp = get_libpaths_string($adddeps);
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var1}) {
               $curval = ${$dsp_sub_sub}{$var1};
            }
            ${$dsp_sub_sub}{$var1} .= ' '.$tmp;
            prt( "[dbg_v23] $xln seek=AdditionalLibraryDirectories: Set [$var1] to [$tmp] from [$adddeps]\n" ) if ($dbg_v23);
        } else {
            # This is very likely to be TRUE in DLL project especially, since they are DYNAMIC,
            # and do not link with any other libriaries at compile time
            prt( "[dbg_v23] $xln 'VCCLinkerTool' DOES NOT CONTAIN(6) 'AdditionalLibraryDirectories' [$line] [$fil]\n" ) if ($dbg_v23);
        }
        $seek = 'IgnoreDefaultLibraryNames';
        if (defined ${$rah}{$seek}) {
            # $var1 = '-NEW_LIBS-';
            # this would also be an indication that this was a 'exe' type project
            # Note, it may be a BLANK
            $adddeps = trim_all(strip_quotes(trim_all(${$rah}{$seek})));
            if (length($adddeps)) {
                $tmp = get_nodeflib_string($adddeps);
                $curval = '';
                if (defined ${$dsp_sub_sub}{$var1}) {
                   $curval = ${$dsp_sub_sub}{$var1};
                   ${$dsp_sub_sub}{$var1} .= ' ';
                }
                ${$dsp_sub_sub}{$var1} .= $tmp;
                prt( "[dbg_v39] $xln seek=IgnoreDefaultLibraryNames: Set [$var1] to [$tmp] from [$adddeps] prev [$curval]\n" ) if ($dbg_v39);
            }
        }

        $seek = 'SubSystem';
        if (defined ${$rah}{$seek} ) {
            $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
            $apptype = adjust_app_type_per_subsystem( $dnapptype, $adddeps, $dbg_v05 );
            $flag |= $cf_apptyp2;  # signal, we have an APP TYPE - VERY IMPORTANT
            # subsystem determines different between 'Application' and 'Console Application'
            if (length($dnapptype)) {
               if ($dnapptype ne $apptype) {
                  prt( "$xln Project [$projname], APP_TYPE RESET per 'SubSystem' [$apptype] from [$dnapptype]...\n" );
                  $dnapptype = $apptype;
               }
            } else {
               prt( "$xln Project [$projname], APP_TYPE per 'SubSystem' [$apptype]...\n" );
               $dnapptype = $apptype;
            }
            ${$rh}{'PROJECT_APTP'} = $dnapptype;
            ${$rh}{'CURR_FLAG'} |= $flag;
        } else {
            # it seems DLL vcproj do NOT have a 'SubSystem'
            # <Tool Name="VCLinkerTool"
            #  AdditionalOptions="/out:$(OutDir)/alut.dll"
            #  AdditionalDependencies="comctl32.lib Msimg32.lib Winmm.lib openal32.lib" 
            #  OutputFile=".\Debug/alut.dll" 
            #  LinkIncremental="2" 
            #  SuppressStartupBanner="true" 
            #  AdditionalLibraryDirectories="C:\Program Files\OpenAL 1.1 SDK\libs\Win32" 
            #  GenerateDebugInformation="true" 
            #  ProgramDatabaseFile=".\Debug/alut.pdb" 
            #  RandomizedBaseAddress="1" 
            #  DataExecutionPrevention="0" 
            #  ImportLibrary=".\Debug/alut.lib" 
            #  TargetMachine="1" />]
            # from [c:\FG\32\alut\admin\VisualStudioDotNET\alut\alut.vcproj]
            $seek = 'ImportLibrary';    # seems this IMPORT LIBRARY entry would be a good DLL indication!
            if (defined ${$rah}{$seek}) {
                $apptype = '';
                if (get_app_type_4_short('DLL', \$apptype) && length($apptype)) {
                    $flag |= $cf_apptyp2;  # signal, we have an APP TYPE - VERY IMPORTANT
                    # should be DLL
                    if (length($dnapptype)) {
                       if ($dnapptype ne $apptype) {
                          prt( "$xln Project [$projname], APP_TYPE RESET per 'ImportLibrary' [$apptype] from [$dnapptype]...\n" );
                          $dnapptype = $apptype;
                       } else {
                          prt( "[dbg_v35] $xln Project [$projname], APP_TYPE CONFIRMED per 'ImportLibrary' set to [$dnapptype]...\n" ) if ($dbg_v35);
                       }
                    } else {
                       prt( "$xln Project [$projname], APP_TYPE per 'ImportLibrary' [$apptype]...\n" );
                       $dnapptype = $apptype;
                    }
                    ${$rh}{'PROJECT_APTP'} = $dnapptype;
                    ${$rh}{'CURR_FLAG'} |= $flag;
                } else {
                    prtw( "WARNING:scanvc:$xln: FAILED TO GET 'DLL' application type string!\n" ); # if ($dbg_v18);
                    pgm_exit(1,"INTERNAL FAILURE");
                }
            } else {
                # ANOTHER STRONG indication of a DLL would be the presence of a DEF (definition) file
                $seek = 'ModuleDefinitionFile';
                if (defined ${$rah}{$seek}) {
                    $apptype = '';
                    if (get_app_type_4_short('DLL', \$apptype) && length($apptype)) {
                        $flag |= $cf_apptyp2;  # signal, we have an APP TYPE - VERY IMPORTANT
                        # should be DLL
                        if (length($dnapptype)) {
                           if ($dnapptype ne $apptype) {
                              prt( "$xln Project [$projname], APP_TYPE RESET per 'ModuleDefinitionFile' [$apptype] from [$dnapptype]...\n" );
                              $dnapptype = $apptype;
                           } else {
                              prt( "[dbg_v35] $xln Project [$projname], APP_TYPE CONFIRMED per 'ModuleDefinitionFile' set to [$dnapptype]...\n" ) if ($dbg_v35);
                           }
                        } else {
                           prt( "$xln Project [$projname], APP_TYPE per 'ModuleDefinitionFile' [$apptype]...\n" );
                           $dnapptype = $apptype;
                        }
                        ${$rh}{'PROJECT_APTP'} = $dnapptype;
                        ${$rh}{'CURR_FLAG'} |= $flag;
                    } else {
                        prtw( "WARNING:scanv:$xln: FAILED TO GET 'DLL' application type string!\n" ); # if ($dbg_v18);
                        pgm_exit(1,"INTERNAL FAILURE");
                    }
                } else {
                    # FINALLY ANOTHER STRONG indication of a DLL would be that OutputFile ends in ".dll"
                    $seek = 'OutputFile';
                    if ( length($outfile) && ($outfile =~ /\.dll$/i)) {
                        $apptype = '';
                        if (get_app_type_4_short('DLL', \$apptype) && length($apptype)) {
                            $flag |= $cf_apptyp2;  # signal, we have an APP TYPE - VERY IMPORTANT
                            if (length($dnapptype)) {
                               if ($dnapptype ne $apptype) {
                                  prt( "$xln Project [$projname], APP_TYPE RESET per 'OutputFile' [$apptype] from [$dnapptype]...\n" );
                                  $dnapptype = $apptype;
                               } else {
                                  prt( "[dbg_v35] $xln Project [$projname], APP_TYPE CONFIRMED per 'OutputFile' set to [$dnapptype]...\n" ) if ($dbg_v35);
                               }
                            } else {
                               prt( "$xln Project [$projname], APP_TYPE per 'OutputFile' [$apptype]...\n" );
                               $dnapptype = $apptype;
                            }
                            ${$rh}{'PROJECT_APTP'} = $dnapptype;
                            ${$rh}{'CURR_FLAG'} |= $flag;
                        } else {
                            prtw( "WARNING:scanv:$xln: FAILED TO GET 'DLL' application type string!\n" ); # if ($dbg_v18);
                            pgm_exit(1,"INTERNAL FAILURE");
                        }
                    } else {
                        if (!($flag & $cf_dn_no_s)) {
                            prtw( "WARNING:scanvc:$xln: 'VCCLinkerTool' DOES NOT CONTAIN(7) 'SubSystem' NOR 'ModuleDefinitionFile' NOR 'OutputFile' [$line] [$fil]\n" ); # if ($dbg_v18);
                            $flag |= $cf_dn_no_s;
                            ${$rh}{'CURR_FLAG'} |= $flag;
                        }
                    }
                }
            }
        }
    } elsif ($pname eq 'VCLibrarianTool' ) {
       # <Tool Name="VCLibrarianTool"
       # OutputFile="Win32_LIB_Debug\zlibd.lib"
       # SuppressStartupBanner="true" />
        $var2 = '-NEW_OUT-';
        $seek = 'OutputFile';
        if (defined ${$rah}{$seek} ) {
            $adddeps = strip_quotes(trim_all(${$rah}{$seek}));
            $curval = '';
            if (defined ${$dsp_sub_sub}{$var2}) {
               $curval = ${$dsp_sub_sub}{$var2};
            }
            $newval = '/out:'.$adddeps;
            if ($curval ne $newval) {
               ${$dsp_sub_sub}{$var2} = $newval;
               prt( "[dbg_v24] $xln seek=OutputFile: Set [$var2] to [$newval]\n" ) if ($dbg_v24);
            }
        } else {
           # 20090914 - set as 15, downgraded to a NOTE only, and ONLY once per file
           # it seems this will DEFAULT TO '$(OutDir)\$(ProjectName).lib'!!!
           if ( !($flag & $cf_dn_lt_t) ) {  # $dn_lt_noo
               # prtw( "WARNING:$lnnum:$xln 'VCCLinkerTool' DOES NOT CONTAIN(5) '$seek' [$line]\n" ) if ($dbg_v21);
               prt( "NOTE:scanvc:$xln: 'VCCLinkerTool' DOES NOT CONTAIN(15) '$seek' [$line]\n" ); # if ($dbg_v21);
                $flag |= $cf_dn_lt_t;    # $dn_lt_noo = 1;
                ${$rh}{'CURR_FLAG'} |= $cf_dn_lt_t;
           }
        }
    } elsif ($pname eq 'VCPostBuildEventTool') {
            $var1 = '-NEW_POST-';
            #if ( !(($confname =~ /Debug/i)||($confname =~ /Release/i)) ) {
            #    prtw( "WARNING:$xln pname=[$confname] NOT Debug or Release - def to REL! [$fil]\n" );
            #}
            # <Tool
            #  Name="VCPostBuildEventTool"
            #  Description="Copy Library to plib directory"
            #  CommandLine="copy debug\*.lib ..\..\*.*&#x0D;&#x0A;copy ul.h ..\..\ul.h&#x0D;&#x0A;copy ulRTTI.h ..\..\ulRTTI.h&#x0D;&#x0A;"
            #  />
            # this will be output to the DSP, under the paticular, config, something like
            # # Begin Special Build Tool
            # SOURCE="$(InputPath)"
            # PostBuild_Desc=Copy to bin
            # PostBuild_Cmds=copy Release\*exe bin\.	copy test.h bin\.
            # # End Special Build Tool
            # 2010/01/14 = Can be 'Description="&quot;Copy Library to plib directory&quot;"' Note double QUOTES as '"', AND '&quot;'
            # or even '&quot;&amp;quot;Copy Library to plib directory&amp;quot;&quot;'
            if (( defined ${$rah}{'Description'} ) || ( defined ${$rah}{'CommandLine'} ) ) {
                $ppdefs = ""; # to 'PostBuild_Desc=';
                if ( defined ${$rah}{'Description'} ) {
                    #$ppdefs .= add_quotes(strip_quotes(trim_all(${$rah}{'Description'})));
                    $ppdefs = strip_quotes(trim_all(${$rah}{'Description'}));
                    prt("[dbg_v36] Got description: [$ppdefs]\n") if ($dbg_v36);
                    $ppdefs =~ s/&quot;/"/g;
                    $ppdefs = strip_quotes($ppdefs);
                    $ppdefs =~ s/&amp;/&/g;
                    # could strip it ALL with added, but not for now...
                    #$ppdefs =~ s/&quot;/"/g;
                    #$ppdefs = strip_quotes($ppdefs);
                    $ppdefs = add_quotes($ppdefs);
                    prt("[dbg_v36] Massaged to: [$ppdefs]\n") if ($dbg_v36);
                }
                $adddeps = ""; # to ' PostBuild_Cmds=';
                if ( defined ${$rah}{'CommandLine'} ) {
                    $adddeps = strip_quotes(trim_ends(${$rah}{'CommandLine'}));
                    $adddeps =~ s/&quot;/"/g;   # put back the quotes, if any
                    $adddeps =~ s/&#x0D;&#x0A;/\t/g;    # new line to TAB for VC6
                    $adddeps =~ s/\t$//;    # clear any END tab
                }
                prt( "[dbg_v15] $xln got POST event [$ppdefs]\n" ) if ($dbg_v15);
                $tmp = "# Begin Special Build Tool\n";
                $tmp .= "SOURCE=\"\$(InputPath)\"\n";
                $tmp .= "PostBuild_Desc=$ppdefs\n";
                # sometimes need a little 'massaging' of the command ...
                $tmp .= "PostBuild_Cmds=".massage_command3($adddeps)."\n";
                $tmp .= "# End Special Build Tool\n";
                ${$dsp_sub_sub}{$var1} = $tmp;
            }
    } elsif ($pname eq 'VCMIDLTool') {
        # perhaps to be done *TBD*
    } elsif ($pname eq 'VCResourceCompilerTool') {
        # perhaps to be done *TBD*
    } elsif ($pname eq 'VCBscMakeTool') {
        # perhaps to be done *TBD*
    } else {
        if (length($pname) && ($lhlen < 4)) {
            # this is ONLY <tag Name="blah" /> - so IGNORE
        } else {
            prtw( "WARNING:scanvc:$xln: conf=[$confname] Tag=[$tag] [$pname] NOT HANDLED! [$line] ($lhlen)\n" );
        }
    }
}

sub process_vc_file_tool_tag($$) {
    my ($rh,$fconf) = @_;
    my ($xln,$rah,$tag,$pname,$fil,$projname,$line,$flag);
    extract_curr_items($rh,\$xln,\$rah,\$tag,\$pname,\$fil,\$projname,\$line,\$flag);
    my ($seek);
    my ($ppdefs,$curval,$newval);
    my ($adddeps);
    my ($adddefs,$tmp); # 2010/05/01 
    my $last_src = ${$rh}{'CURR_LSRC'};
    my $last_nm  = ${$rh}{'CURR_LNME'};
    my $last_dir = ${$rh}{'CURR_LDIR'};
    my $last_ext = ${$rh}{'CURR_LEXT'};
    my $dsp_sub_sub = ${$rh}{'CURR_DSUB'};
    my $lhlen = scalar keys(%{$rah});   # get item count in line hash

    if ($pname eq 'VCCLCompilerTool') {
        prt( "[dbg_v34] $xln file: 'VCCLCompilerTool' [$line] IGNORED CHECK ($lhlen)\n" ) if ($dbg_v34);
    } elsif ($pname eq 'VCCustomBuildTool') {
        my ($nm,$dir);
        my $pfhr = get_per_file_hash($rh);  # 'CURR_PFHR'
        if (( defined ${$rah}{'Description'} ) || ( defined ${$rah}{'CommandLine'} ) ) {
            $ppdefs = ""; # to 'PostBuild_Desc=';
            $seek = 'Description';
            if ( defined ${$rah}{$seek} ) {
                $ppdefs .= strip_quotes(trim_all(${$rah}{$seek}));
            }
            $seek = 'CommandLine';
            $adddeps = ""; # to ' PostBuild_Cmds=';
            if ( defined ${$rah}{$seek} ) {
                $adddeps = strip_quotes(trim_ends(${$rah}{$seek}));
                $adddeps =~ s/&quot;/"/g;   # put back the quotes, if any
                $adddeps =~ s/&#x0D;&#x0A;/\t/g;    # new line to TAB for VC6
                $adddeps =~ s/\t$//;    # clear any END tab
            }
            $adddefs = '$(IntDir)';
            $seek = 'Outputs';
            if ( defined ${$rah}{$seek} ) {
                $adddefs = strip_quotes(trim_all(${$rah}{$seek}));
            }
            # this seems to be something like -
            # $(IntDir)\asm\$(InputName).obj, but for the DSP file the '\$(InputName).obj' seems ASSUMED,
            # and the $(IndDir) is replaced with an actual DIRECTORY, so...
            if ($adddefs =~ /\.obj$/i) {
                ($nm,$dir) = fileparse( $adddefs );
                $dir =~ s/(\\|\/)$//;
                $adddefs = $dir;
            }
            if ($adddefs =~ /\$\(IntDir\)/) {
                if (defined ${$dsp_sub_sub}{'-NEW_INTER-'}) {
                    $dir = strip_quotes(${$dsp_sub_sub}{'-NEW_INTER-'});
                    $dir =~ s/(\\|\/)$//;
                    $adddefs =~ s/\$\(IntDir\)/$dir/;
                }
            }
            $tmp = "# Begin Custom Build - $ppdefs\n";
            $tmp .= "IntDir=$adddefs\n";
            $tmp .= "Inputpath=$last_src\n";
            $tmp .= "InputName=$last_nm\n\n";
            $tmp .= "\"\$(IntDir)\\\$(InputName).obj\" : \$(SOURCE) \"\$(INTDIR)\" \"\$(OUTDIR)\"\n";
            $tmp .= "\t".massage_command3($adddeps)."\n\n";
            $tmp .= "# End Custom Build\n";
            prt( "[dbg_v27] $xln got CUSTOM event [$pname] [$fconf] [$adddefs]\n" ) if ($dbg_v27);
            prt( "[dbg_v28] CUSTOM event [$tmp]\n" ) if ($dbg_v28);
            if (defined ${$pfhr}{$fconf}) {
                prtw("WARNING:scanvc: Already have [$fconf] in per_file_hash!\n");
            }
            ${$pfhr}{$fconf} = $tmp;  # store it for later
        } else {
            $tmp = scalar keys(%{$rah});
            if (length($pname) && ($tmp < 4)) {
                # ignore is just <tag name="blah" />, but empty otherwise
            } else {
                prtw( "WARNING:scanvc: Deal with TAG=$tag - [$line] NO Decription or Command line! ($tmp)\n file=[$fil]\n" );
            }
        }
    } elsif ($pname eq 'VCResourceCompilerTool') {
        prt( "[dbg_v34] $xln file: 'VCResourceCompilerTool' [$line] IGNORED CHECK (line=$lhlen)\n" ) if ($dbg_v34);
    } else {
        prtw( "WARNING:scanvc:$xln: [$pname] NOT HANDLED! (line=$lhlen)\n" );
    }
}

sub get_cfgs_array_order($$) {
    my ($rcfgs,$rkarr) = @_;
    my $cnt = scalar @{$rcfgs};
    my ($i, $cfg, $test, @arr);
    @arr = ();
    push(@arr,-99);
    push(@arr,-99);
    return 0 if ($cnt < 2);
    $test = 0;  # 2010/05/05 - add init of var
    for ($i = 0; $i < $cnt; $i++) {
        $cfg = ${$rcfgs}[$i][0];
        if ($cfg eq 'Release') {
            $test |= 1;
            $arr[0] = $i;
        } elsif ($cfg eq 'Debug') {
            $test |= 2;
            $arr[1] = $i;
        } else {
            push(@arr,$i);
        }
    }
    if ($test == 3) {   # found both 'Release' and 'Debug'
        # then enforce the ORDER 'Release, Debug, Others
        @{$rkarr} = @arr;   # copy the index array
        return 1;
    }
    return 0;
}

#add_any_per_file_items( $rdsp_current_sub, \%per_file_hash, $src_ref,
#                \@configs, $last_fpos, $projname );
sub add_any_per_file_items3($) {
    my ($rh) = @_;
    my $pfhr = get_per_file_hash($rh);  # 'CURR_PFHR'
    my $xln  = get_current_xline_stg($rh);
    my @karr = keys(%{$pfhr});
    my $kcnt = scalar @karr;
    my $modstg = $xln."scanvc:add_any_per_file_items3:";
    my ($cfg);  # 2010/05/01 
    if ($kcnt) {
        my ($rcfgs,$ccnt,$key,$i,$fnd,$tmp,$pname);
        $rcfgs = get_project_configs($rh);   # 'PROJECT_CFGS'
        $pname = svc_get_project_name_stg($rh);  # 'PROJECT_NAME'
        # push(@{$rcfgs}, [ $confname, $var1, $conf, $dsp_sub_sub ]);
        $ccnt = scalar @{$rcfgs};
        $fnd = 0;
        foreach $key (@karr) {
            for ($i = 0; $i < $ccnt; $i++) {
                if (${$rcfgs}[$i][2] eq $key) {
                    $fnd++;
                    last;
                }
            }
        }
        if ($fnd == $kcnt) {
            $tmp = '';
            @karr = (); # clear this array
            if ( get_cfgs_array_order($rcfgs,\@karr) && (scalar @karr == $kcnt) ) {
                $fnd = 0;
                foreach $i (@karr) {
                    $key = ${$rcfgs}[$i][2];
                    $cfg = ${$rcfgs}[$i][0];
                    if ($fnd == 0) {
                        $tmp .= "\n!IF ";   # Release
                    } else {
                        $tmp .= "!ELSEIF "; # Debug, and others
                    }
                    $tmp .= "\"\$(CFG)\" == \"$pname - Win32 $cfg\"\n\n";
                    if (defined ${$pfhr}{$key}) {
                        $tmp .= ${$pfhr}{$key};
                    }
                    $tmp .= "\n";
                    $fnd++;
                }
            } else {
                for ($i = 0; $i < $ccnt; $i++) {
                    $key = ${$rcfgs}[$i][2];
                    $cfg = ${$rcfgs}[$i][0];
                    if ($i == 0) {
                        $tmp .= "\n!IF ";
                    } else {
                        $tmp .= "!ELSEIF ";
                    }
                    $tmp .= "\"\$(CFG)\" == \"$pname - Win32 $cfg\"\n\n";
                    if (defined ${$pfhr}{$key}) {
                        $tmp .= ${$pfhr}{$key};
                    }
                    $tmp .= "\n";
                }
            }
            $tmp .= "!ENDIF \n\n";
            my $srcs = get_project_srcs_ref($rh); # 'PROJECT_SRCS'
            my $coff = get_last_src_offset($rh);  # 'CURR_LOFF' # last/current source OFFSET
            #                     0          1       2       3  4
            # push(@{$src_ref}, [ $last_src, $fname, $flist, 0, '' ]); # and PUSH onto SOURCE stack
            $key = ${$srcs}[$coff][0];
            ${$srcs}[$coff][4] = $tmp;
            prt( "[dbg_v29] $xln Added CUSTOM build for source [$key]\n" ) if ($dbg_v29);
            prt( "[dbg_v30] CUSTOM build [$tmp]\n" ) if ($dbg_v30);
        } else {
            prtw("WARNING:scanvc: $kcnt keys NOT handled in per file hash! Only found $fnd!!\n");
        }
    } else {
        prt("[dbg_v11] $xln NO keys in per file hash...\n") if ($dbg_v11);
    }
}

sub get_xln_stg($$) {
    my ($lnn,$npair) = @_; # "$lnnum:".${$rlm}{$lnnum};
    my @a = split('-',$npair);
    my $xln = sprintf("%03d:%03d-%03d:", $lnn, $a[0], $a[1]);
    return $xln;
}

sub get_clean_seek_item($) {
    my ($txt) = shift;
    $txt =~ s/>$//; # 2010-04-23 - ensure any trailing '>' is removed
    $txt = strip_quotes(trim_all($txt));
    return $txt;
}

# sub pre_process_VCPROJ($$$) {
sub process_VC_lines($$$) {
    my ($fil, $rlines, $rlm) = @_;
    my ($line, @attribs, %atthash);
    my ($tag, $pname);
    my $invcproj = 0;
    my $projname = '';
    my $adddeps = '';
    my $adddefs = '';
    my $do_pre_pro = 1;
    my $lnnum = 0;
    my $xln = '';
    my $rh = get_default_ref_hash($fil);
    my $infile = 0;
    my $infilter = 0;
    my $inconfig = 0;
    my $fname = '';
    my $flist = '';
    my $fconf = '';
    my ($tmp);
    my $last_src = '';
    my ($last_nm,$last_dir,$last_ext,$src_ref);
    my $confname = '';
    my $conf = '';
    $g_sc_infiles = 0;
    $g_sc_inconfigs = 0;
    $g_sc_infconf = 0;
    my %p_f_h = ();
    my $pfhr = \%p_f_h;
    my ($seek,$var1); # 2010/05/01 
    if ($do_pre_pro) {
        $fname = '<none>';
        $flist = '<none>';
        foreach $line (@{$rlines}) {
            $lnnum++;
            $xln = get_xln_stg($lnnum,${$rlm}{$lnnum}); # "$lnnum:".${$rlm}{$lnnum};
            @attribs = space_split($line);
            %atthash = array_2_hash_on_equals(@attribs);
            $tag = $attribs[0];
            #$pname = (defined $atthash{'Name'}) ? strip_quotes(trim_all($atthash{'Name'})) : '';
            $pname = (defined $atthash{'Name'}) ? get_clean_seek_item($atthash{'Name'}) : '';
            ${$rh}{'CURR_LINE'} = $xln;
            ${$rh}{'CURR_TEXT'} = $line;
            ${$rh}{'CURR_HASH'} = \%atthash;
            ${$rh}{'CURR_TAG'}  = $tag;
            ${$rh}{'CURR_NAME'} = $pname;
            ${$rh}{'CURR_PFHR'} = $pfhr;
            if ($invcproj) {
                if ($tag =~ /<\/VisualStudioProject/) {
                    $invcproj = 0;
                    $tmp     = ${$rh}{'PROJECT_NAME'};
                    $adddefs = ${$rh}{'PROJECT_TYPE'};
                    $adddeps = ${$rh}{'PROJECT_VERS'};
                    prt( "[dbg_v01] $xln End VisualStudioProject name=[$tmp], type=[$adddefs], vers=[$adddeps]\n" ) if ($dbg_v01);
                } else {
                    # =====================================================================
                    # *** IN THE PROJECT ***
                    if ($g_sc_inconfigs) {
                        if ($tag =~ /<\/Configurations/) {
                            $g_sc_inconfigs = 0;
                            prt( "[dbg_v02] $xln End Configurations ($tag)\n" ) if ($dbg_v02);
                        } else {
                            # ==================================================================
                            # *** IN CONFIGURATIONS ***
                            if ($inconfig) {
                                if ($tag =~ /^<\/Configuration>/) {
                                    prt( "[dbg_v06] $xln End ConfigurationName=[$confname] conf=[$conf] [$line] ...($tag)\n" ) if ($dbg_v06);
                                    $inconfig = 0;
                                } else {
                                    if ($tag =~ /^<Tool/) {
                                        process_vc_conf_tool_tag($rh);
                                    } else {
                                        prt( "$xln DEAL with $tag [$conf] [$line] ($tag)\n" );
                                    }
                                }
                            } elsif ($tag =~ /^<Configuration$/) {
                                #$conf = $pname;
                                #@arr = split(/\|/,$pname);
                                #$confname = $arr[0];
                                #prt( "$xln Bgn ConfigurationName=[$confname] conf=[$conf] [$line] ...($tag)\n" );
                                add_vs_config_item($rh);
                                $confname = ${$rh}{'CURR_CONF'};
                                $conf     = ${$rh}{'CURR_CON1'};
                                $inconfig = 1;
                            }
                            # ==================================================================
                        }
                    } elsif ($tag =~ /<Configurations/) {
                        $g_sc_inconfigs = 1;
                        prt( "[dbg_v02] $xln Bgn Configurations ($tag)\n" ) if ($dbg_v02);
                    }
                    if ($g_sc_infiles) {
                        if ($tag =~ /\/Files/) {
                            $g_sc_infiles = 0;
                            prt( "[dbg_v12] $xln End Files\n" ) if ($dbg_v12);
                        } else {
                            # ==================================================================
                            # *** IN FILES ***
                            if ($tag =~ /^<Filter/) {
                                $fname = $pname;
                                $flist = '';
                                $seek = 'Filter';
                                if (defined $atthash{$seek} ) {
                                    $flist = strip_quotes(trim_all($atthash{$seek}));
                                    # handle this Filter="&quot;h;hpp;hxx;hm;inl&quot;&gt;"
                                    $flist =~ s/&gt;//g;    # 20081205 - remove this, if exists
                                    $flist =~ s/&quot;/"/g; # 20081205 - put QUOTES back
                                    $flist = strip_quotes($flist);  # now again remove them
                                } else {
                                    # example: alut.vcproj
                                    # <Filter Name="ALUT Header">
                                    # <File RelativePath="..\..\..\include\AL\alut.h">
                                    # </File></Filter>
                                    if (!$flist_done_once) {
                                        prtw( "WARNING:scanvc:$xln: DOES NOT CONTAIN(12) '$seek'? [$line] [$fil]\n" ); # if ($dbg_v17);
                                        $flist_done_once++;
                                    }
                                }
                                # prt( "$lnnum:$xln ENTER Filter name=[$fname] list=[$flist]\n" ) if ($dbg_v11);
                                prt( "[dbg_v07] $xln Bgn Filter name=[$fname] list=[$flist] ($tag)\n" ) if ($dbg_v07);
                                $infilter = 1;
                            } elsif ($tag =~ /^<\/Filter/) {
                                $infilter = 0;
                                prt( "[dbg_v07] $xln End Filter ($tag)\n" ) if ($dbg_v07);
                                $fname = '<none>';
                                $flist = '<none>';
                            } elsif ($tag =~ /^<File$/) {
                                $seek = 'RelativePath';
                                if (defined $atthash{$seek}) {
                                    $var1 = $atthash{$seek};
                                    $var1 =~ s/>$//;
                                    $last_src = strip_dotrel(strip_quotes(trim_all($var1)));
                                    # <File RelativePath="..\..\..\include\AL\alut.h">
                                    # this is the RELATIVE PATH - relative to the vcproj file being scanned
                                    # Now would be a good time to ADJUST this PATH, for any NEW location
                                    # of the final DSP file!!! BUT, this is done back in vcproj05.pl
                                    # in the sub chk_relative_paths()...
                                    # =========================
                                    ($last_nm,$last_dir,$last_ext) = fileparse( $last_src, qr/\.[^.]*/ );
                                    # =========================
                                    $src_ref = get_project_srcs_ref($rh); # 'PROJECT_SRCS'
                                    ${$rh}{'CURR_LOFF'} = scalar @{$src_ref};   # get current source OFFSET
                                    # *** STORING SOURCE ***
                                    push(@{$src_ref}, [ $last_src, $fname, $flist, 0, '' ]); # and PUSH onto SOURCE stack
                                    ${$rh}{'CURR_LSRC'} = $last_src;
                                    ${$rh}{'CURR_LNME'} = $last_nm;
                                    ${$rh}{'CURR_LDIR'} = $last_dir;
                                    ${$rh}{'CURR_LEXT'} = $last_ext;
                                } else {
                                    prtw( "ERROR:scanvc:$xln: NO ITEM(1) '$seek'? [$line] [$fil]\n" );
                                    pgm_exit(1, "NO FILE NAME FOUND!\n" );
                                }
                                $infile = 1;
                                prt( "[dbg_v08] $xln Bgn File [$last_src] ($tag)\n" ) if ($dbg_v08);
                            } elsif ($tag =~ /^<\/File>/) {
                                prt( "[dbg_v08] $xln End File ($tag)\n" ) if ($dbg_v08);
                                #add_any_per_file_items( $rdsp_current_sub, \%per_file_hash, $src_ref,
                                #                \@configs, $last_fpos, $projname );
                                add_any_per_file_items3($rh);
                                # %p_f_h = ();
                                %{$pfhr} = ();
                                $infile = 0;
                            } elsif ($tag =~ /^<FileConfiguration/) {
                                $g_sc_infconf = 1;
                                prt( "[dbg_v09] $xln Bgn FileConfiguration [$last_src] ($tag)\n" ) if ($dbg_v09);
                                $fconf = $pname;
                                $seek = 'ExcludedFromBuild';
                                $adddefs = '';
                                if (defined $atthash{$seek} ) {
                                    #$adddefs = strip_quotes(trim_all($atthash{$seek}));
                                    $adddefs = get_clean_seek_item($atthash{$seek});
                                    if (length($adddefs) && ($adddefs =~ /true/)) {
                                        ${$src_ref}[-1][3] = 1;
                                        prt( "[dbg_v10] $xln Last = $last_src=[".${$src_ref}[-1][0]."] $fconf ExcludedFromBuild = $adddefs\n" ) if ($dbg_v10);
                                    }
                                }
                            } elsif ($tag =~ /^<\/FileConfiguration/) {
                                $g_sc_infconf = 0;
                                prt( "[dbg_v09] $xln End FileConfiguration [$last_src] ($tag)\n" ) if ($dbg_v09);
                            } elsif ($tag =~ /^<Tool/) {
                                if ($infile && $g_sc_infconf) {
                                    process_vc_file_tool_tag($rh,$fconf);
                                } else {
                                    prtw( "WARNING:scanvc:$xln: In  Tool [$pname] BUT NOT in file and conf! [$last_src] ($tag)\n" );
                                }
                            } else {
                                prtw( "WARNING:scanvc:$xln:infile TAG=[$tag] NOT HANDLED! \n" );
                            }
                            # ==================================================================
                        }
                    } elsif ($tag =~ /<Files/) {
                        $g_sc_infiles = 1;
                        prt( "[dbg_v12] $xln Bgn Files ($tag)\n" ) if ($dbg_v12);
                    }
                    # =====================================================================
                }
            } elsif ($tag =~ /<VisualStudioProject/) {
                $invcproj = 1;
                add_vs_project_items($rh, \%atthash, $xln, $fil);
                if ( (!defined ${$rh}{'PROJECT_NAME'}) || (length(trim_all(${$rh}{'PROJECT_NAME'})) == 0) ) {
                    prt( "ERROR: NO PPROJECT NAME FOUND!\n" );
                    pgm_exit(1,"NO PROJECT NAME!");
                }
                $projname = ${$rh}{'PROJECT_NAME'};
                prt( "[dbg_v38] Processing project [$projname]...\n" ) if ($dbg_v38);
            }
        }
    }
    prt("[dbg_v37] Done process_VC_lines...\n") if ($dbg_v37);
    return $rh;
}

sub svc_xml_to_lines($$) {
    my ($rlm, $rlns) = @_;
    my $intag = 0;
    my $text = '';
    my @nlines = ();
    my ($fln, $ln, $ch, $pch, $nch, $len, $i, $i2, $tag, $xml, $dnx);
    my ($lnnm, $lnb, $nlnm);
    $pch = '';
    $nch = '';
    $tag = '';
    $xml = '';
    $dnx = 0;
    $lnnm = 0;
    $nlnm = 0;
    $lnb = 0;
    my $nxtxt = '';
    foreach $fln (@{$rlns}) {
        chomp $fln;
        $ln = trim_all($fln);
        $len = length($ln);
        $lnnm++;    # count another xml line
        for ($i = 0; $i < $len; $i++) {
            $i2 = $i + 1;
            $ch = substr($ln,$i,1);
            $nch = (($i2 < $len) ? substr($ln,$i2,1) : ' ');
            if ($intag) {
                # on first SPACE
                $tag .= $ch;
                if ($ch eq '>') {
                    # end of XML tag
                    $nlnm++;
                    push(@nlines,$tag);
                    ### prt( "push(\@xlnmap, [ $nlnm, $lnb, $lnnm ]); # each NEW line has BEGIN and END\n" );
                    $$rlm{$nlnm} = "$lnb-$lnnm";    # each NEW line has BEGIN and END
                    $nxtxt .= "$lnb-$lnnm: [$tag]\n";
                    $tag = '';
                    $intag = 0;
                    $xml = '';
                }
            } else {
                if ($ch eq '<') {
                    $tag = $ch; # start a tag line
                    $intag = 1; # signal in a tag
                    $xml = '';
                    $dnx = 0;
                    $lnb = $lnnm;    # set the BEGIN xml line
                }
            }
            $pch = $ch;
        }
        # done a line - this is like a SPACE
        if ($intag && length($tag)) {
            $tag .= ' ' if !($tag =~ /\s$/);
        }
    }
    if (length($tag)) {
        prtw("WARNING:scanvc: xml re-lining error! Left pending tag [$tag]\nin $g_sc_act_vcproj file ...\n");
    }
    $dnx = scalar @nlines;
    if ($dnx < 5) {
        prt("ERROR: Input file [$g_sc_act_vcproj] only has $dnx lines!\n");
        pgm_exit(1, "INPUT TOO SMALL!!!\n");
    }
    if ($write_temp_xml) {
        my $tmpxml = "C:\\GTools\\perl\\tempvcx.xml";
        if (! -f $tmpxml) {
            write2file($nxtxt,$tmpxml);
            prt( "Written relined XML to '$tmpxml'\n" );
        }
    }
    return @nlines;
}


sub process_VCPROJ3 {
    my ($fil) = shift;
    $g_sc_act_vcproj = $fil;
    ($g_sc_act_name,$g_sc_act_path) = fileparse($fil);
    $g_sc_act_path = cwd() if ($g_sc_act_path =~ /^\.(\\|\/)$/);
    $g_sc_act_path .= "\\" if (!($g_sc_act_path =~ /(\\|\/)$/));
    my ($new_ref);
    if (open INF, "<$fil") {
        my %lnmap = ();
        my @lines = <INF>;
        close INF;
        my $xlncnt = scalar @lines;
        @lines = svc_xml_to_lines(\%lnmap, \@lines);
        my $lncnt = scalar @lines;
        prt( "\nProcessing file [$fil], $lncnt lines ($xlncnt) ...\n" );
        $new_ref = process_VC_lines( $fil, \@lines, \%lnmap );
    } else {
        mydie( "ERROR: Unable to open $fil ... $! ... check name, location ...\n" );
    }
    return $new_ref;
}

1;

# scanvc.pl
