#!/perl -w
# NAME: chkrt.pl
# AIM: Check the RUNTIME of -
# DSW set of files		MSVC6
# DSP single file		MSVC6
# SLN set of files		MSVC7 or 8
# vcproj single file	MSVC7 or 8
# 24/09/2007 - Also SHOW output file ...
use strict;
use warnings;
use File::Basename;
##############################################################
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 = "C:\\GTools\\perl\\temp.$pgmname.txt";
open_log($outfile);
my $in_file = '';
my $dbg_inp = 1;	# on to run WITHOUT command line ..
my $def_file = 'C:\GTools\ConApps\testap1\testap1.sln';
##my $def_file = 'C:\GTools\Tools\testmem\testmem.dsw';
##my $def_file = 'C:\GTools\Tools\testmem\testmem.sln';
##my $def_file = 'C:\FG\FGCOM\xmlrpc-c\Windows\xmlrpc.dsw';
##my $def_file = 'C:\FG\FGCOM\iaxclient\contrib\win\vs2005\iaxclient.sln';
##my $def_file = "C:\\FG\\FGCOM\\fgcom\\build\\fgcom\\fgcom.sln";
##my $def_file = "C:\\FG\\FGCOM\\expat\\expat.sln";
##my $def_file = "C:\\FG\\14\\fgfs\\fgfs.sln";
#C:\FG\FGCOM\iaxclient\contrib\win\vs2003\iaxclient_lib.sln
#C:\FG\FGCOM\portaudio\bindings\cpp\build\vc7_1\devs_example.sln
#C:\FG\FGCOM\portaudio\bindings\cpp\build\vc7_1\sine_example.sln
#C:\FG\FGCOM\portaudio\bindings\cpp\build\vc7_1\static_library.sln
#C:\FG\FGCOM\iaxclient\simpleclient\IAXClientDotNetWrapper\IAXClientDotNetWrapper.sln

my $do_set = 0;	# set to do SET
my @sln_set = qw(C:\FG\FGCOM\asterisk\contrib\msvc\asterisk\asterisk.sln
C:\FG\FGCOM\expat\expat.sln
C:\FG\FGCOM\fgcom\build\fgcom\fgcom.sln
C:\FG\FGCOM\iaxclient\contrib\tcl\win\iaxclient.sln
C:\FG\FGCOM\iaxclient\contrib\win\vs2005\iaxclient.sln
C:\FG\FGCOM\libxr\build\libxr\libxr.sln
C:\FG\FGCOM\portaudio\build\msvc\portaudio.sln
C:\FG\FGCOM\xmlrpc++\XmlRpc.sln
C:\FG\FGCOM\xmlrpc-c\Windows\xmlrpc.sln);

# features
my $excl_excluded = 1;	# check for PROP # PROP Exclude_From_Build 1
my $showfiles = 0;	# also OUTPUT the file list
my $showfilter = 0;	# also OUTPUT the filter list
my $showenter = 0;	# show enter and exit - more for debug

my ($fil_nm,$fil_dir,$fil_ext);
my @lines = ();
my $line = '';
my @warnings = ();	# push of ALL WARNINGS ...
my $wrnmsg = '';
my $show_out_file = 1;	# show the OUTPUT file also
my $vcprojcnt = 0;
my $dbg_out = 0;	# write the RE-LINED XML to a file
my $projcount = 0;
#########################################################
my %dswprojs = ();
#########################################
### DSP handling sub clear_per_dsp {
my %macros = ();	# clear DSP macro set
my %dspmacros = ();
my $customdbg = '';
my $custonrel = '';
my @discardedsrcs = ();
# constants
my $COMMENT_PATTERN = "^#";
my $MACRO_PATTERN2 = "^([A-Za-z][A-Za-z0-9_]+)[ \t]*=[ \t]*(.*)\$";

my %runtimes = ();
my %rtlines = ();
my %rtfiles = ();

my $mtrts = 0;
my $dllrts = 0;
my $otherrts = 0;
my $loadtxt = 0;

#########################################################
# debug
# DSP debug items
my $dbg5 = 0;	# show "Got Project: $pn, $ff ...
my $dbg6 = 0;	# show "Split is [$if0] == [$if1]
my $dbg8 = 0;	# show "Entered IF [$1] $inanif
my $dbg9 = 0;	# show "SET: MACRO $1, to $2 ...
my $dbg10 = 0;	# show "Begin Group: $1
my $dbg11 = 0;	# show "File $f contains $lncnt lines ...
my $dbg30 = 0;	# show "Discarding [$ls] due to Exclude_From_Build ... if $excl_excluded ON
my $dbg31 = 0;	# show "WARNING: CHECK Discarded [$fline]"
my $dgb32 = 0;	# show "Use_Debug_Libraries $1
my $dgb33 = 0;	# show "RUNTIME: $form $rt
my $dbg34 = 0;	# show "WARNING: Duplicated MACRO $1, now $2, was $macros{$1} ...
my $dbg35 = 0;	# show "$vcprojcnt:$cfgcnt: End Configuration ...

my $dbg40 = 0;  # show "Processing $lc lines ...
my $dbg41 = 0;  # show "Done $lc lines ... $cnt projects ...
my $dbg42 = 0;  # show "\n$vcprojcnt: Processing VCPROJ file [$fil] ...
my $dbg43 = 0;  # show 	"$vcprojcnt: Processing $cnt lines ...
my $dbg44 = 0;  # show "$vcprojcnt: Done $cnt lines ... got $ncnt new lines ...
my $dbg45 = 0;  # show "$vcprojcnt: Project Name = $actname ($actvers)...
my $dbg46 = 0;  # show "$vcprojcnt:$cfgcnt: Config Name = $actcfg ...
my $dbg47 = 0;  # show "$vcprojcnt:$cfgcnt: Preprocessor = $actpp
my $dbg48 = 0;  # show "$cnt: Project [".$arr[0]."] File [".$arr[1]."] ...
my $dbg49 = 0;  # show "$vcprojcnt:$cfgcnt: Runtime = $rt ($rts) ... for $actcfg ($rng)
my $dbg50 = 0;  # show "$vcprojcnt:$cfgcnt: Output = $1
my $dbg51 = 0;  # show "Processing CONCLUDED without WARNINGS!
my $dbg52 = 0;  # show "$vcprojcnt:$cfgcnt: AdditionalDependencies = ...

if (@ARGV) {
	$in_file = pop @ARGV;
} elsif ($dbg_inp) {
	$in_file = $def_file;
}

if (! -f $in_file) {
	mydie( "ERROR: Unable to locate [$in_file] file ... $1 ...\n" );
}

if (@sln_set && $do_set) {
	foreach $in_file (@sln_set) {
		($fil_nm,$fil_dir,$fil_ext) = fileparse( $in_file, qr/\.[^.]*/ );
		if (lc($fil_ext) eq '.dsw') {
			process_dsw( $in_file );
		} elsif (lc($fil_ext) eq '.dsp') {
			process_dsp( $in_file );
		} elsif (lc($fil_ext) eq '.sln') {
			process_sln( $in_file );
		} elsif (lc($fil_ext) eq '.vcproj') {
			process_vcproj( $in_file );
		} elsif (lc($fil_ext) eq '.csproj') {
			$wrnmsg = "WARNING: Input file [$in_file] is C#, NOT handled ...";
			prt( "$wrnmsg\n" );
			push(@warnings, $wrnmsg);
		} else {
			$wrnmsg = "WARNING: Input file [$in_file] NOT of know extension ...";
			prt( "$wrnmsg\n" );
			push(@warnings, $wrnmsg);
		}
	}
} else {
	($fil_nm,$fil_dir,$fil_ext) = fileparse( $in_file, qr/\.[^.]*/ );
	if (lc($fil_ext) eq '.dsw') {
		process_dsw( $in_file );
	} elsif (lc($fil_ext) eq '.dsp') {
		process_dsp( $in_file );
	} elsif (lc($fil_ext) eq '.sln') {
		process_sln( $in_file );
	} elsif (lc($fil_ext) eq '.vcproj') {
		process_vcproj( $in_file );
	} elsif (lc($fil_ext) eq '.csproj') {
		$wrnmsg = "WARNING: Input file [$in_file] is C#, NOT handled ...";
		prt( "$wrnmsg\n" );
		push(@warnings, $wrnmsg);
	} else {
		$wrnmsg = "WARNING: Input file [$in_file] NOT of know extension ...";
		prt( "$wrnmsg\n" );
		push(@warnings, $wrnmsg);
	}
}
foreach my $key (keys %dswprojs) {
	$in_file = $dswprojs{$key};
	process_dsp($in_file);
}

show_runtimes();

if (@warnings) {
	prt( "WARNING: During processing got following WARNINGS!\n" );
	foreach $line (@warnings) {
		prt( "$line\n" );
	}
} else {
	prt( "Processing CONCLUDED without WARNINGS!\n" ) if ($dbg51);
}

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

## if (lc($fil_ext) eq 'dsw') {
sub	process_dsw {
	my ($fil) = shift;
	my ($msg, $lc, $lin, $pcnt, $ln, $bal, $pn, $pf, $ff);
	prt( "Processing DSW file [$fil] ...\n" );
	if ( !open INF, "<$fil" ) {
		$msg = "WARNING: Unable to open [$fil] ...";
		prt( "$msg\n" );
		push(@warnings, $msg);
		return 0;
	}
	@lines = <INF>;
	close INF;
	$lc = scalar @lines;
	prt( "Processing $lc lines ...\n" );
	my ($nm, $dir, $ext) = fileparse( $fil, qr/\.[^.]*/ );
	$pcnt = 0;
	$ln = 0;
	foreach $lin (@lines) {
		# seeking Project: "gennmtab"=".\gennmtab.dsp" - Package Owner=<4>
		chomp $lin;
		$ln++;
		##prt( "$ln [$lin]\n" );
		if ($lin =~ /^Project:\s+(.*)/) {
			$bal = $1;
			###prt( "Got Project: [$bal]...\n" );
			if ($bal =~ /\"(\w+)\"=\"*([\w\.\\\/]+)\"*/) {
				$pn = $1;
				$pf = $2;
				$ff = fix_rel($dir . $pf);
				###prt( "Name = [$pn], file = [$pf][$ff] " );
				prt( "Got Project: $pn, $ff ...\n" ) if ($dbg5);
				if (defined $dswprojs{$pn} ) {
					$msg = "WARNING: Duplicate PROJECT [%pn] ... $pf versus ".$dswprojs{$pn};
					prt( "$msg\n" );
					push(@warnings,$msg);
				} else {
					$dswprojs{$pn} = $ff;	# keep project DSP file
					$pcnt++;
					if ( !(-f $ff) ) {
						$msg = "WARNING: [$ff] CAN NOT BE FOUND ...";
						prt( "$msg\n" );
						push(@warnings,$msg);
					}
				}
			}
		}
	}
	prt( "Done $lc lines ... got $pcnt projects \n" );
	return $lc;
}


## } elsif (lc($fil_ext) eq 'dsp') {
sub	process_dsp {
	my ($fil) = shift;
	my ($lc, $wmsg);
	prt( "\nProcessing DSP file [$fil] ...\n" );
	load_dsp($fil);
}

## } elsif (lc($fil_ext) eq 'sln') {
sub process_sln {
	my ($fil) = shift;
	my ($lc, $wmsg);
	prt( "\nProcessing SLN file [$fil] ...\n" );
	if ( !open INF, "<$fil" ) {
		$wmsg = "WARNING: Unable to open [$fil] ...";
		prt( "$wmsg\n" );
		push(@warnings, $wmsg);
		return 0;
	}
	@lines = <INF>;
	close INF;
	$lc = scalar @lines;
	prt( "Processing $lc lines ...\n" ) if ($dbg40);
	my $cnt = 0;
	my @projs = ();
	foreach $line (@lines) {
		$line = trim_all($line);
		if ($line =~ /Project\(.*=(.*)/) {
			$cnt++;
			##prt( "$1\n" );
			my @arr = split(/,/, $1);
			if (scalar @arr >= 2) {
				$arr[0] = trim_all($arr[0]);
				$arr[1] = trim_all($arr[1]);
				$arr[0] = substr($arr[0],1,length($arr[0])-2);
				$arr[1] = substr($arr[1],1,length($arr[1])-2);
				prt( "$cnt: Project [".$arr[0]."] File [".$arr[1]."] ...\n" ) if ($dbg48);
				push(@projs, [ $arr[0], $arr[1] ]);
			} else {
        		$wmsg = "WARNING:$cnt:[$fil]: Scalar count of split only ".scalar @arr."???";
		        prt( "$wmsg\n" );
		        push(@warnings, $wmsg);
            }
		}
	}

	$cnt = scalar @projs;
	prt( "Done $lc lines ... $cnt projects ...\n" ) if ($dbg41);
	for (my $i = 0; $i < $cnt; $i++) {
		my $relfil = fix_rel($fil_dir.$projs[$i][1]);
		my ($fnm,$fdir,$fext) = fileparse( $relfil, qr/\.[^.]*/ );
		if (lc($fext) eq '.vcproj') {
			process_vcproj( $relfil );
		} elsif (lc($fext) eq '.csproj') {
			$wrnmsg = "WARNING: Input file [$relfil] is C#, NOT handled ...";
			prt( "$wrnmsg\n" );
			push(@warnings, $wrnmsg);
		} else {
			$wrnmsg = "WARNING: Input file [$relfil] NOT of know extension ...";
			prt( "$wrnmsg\n" );
			push(@warnings, $wrnmsg);
		}
	}
	return $lc;
}

######################################
## process a VCPROJ file
######################################
sub process_vcproj {
	my ($fil) = shift;
	my ($cnt, $ac, $i, $ln, $actcfg, $j, $bgn, $end, $rt, $rts, $cfgcnt, $incfg, $wmsg);
	my ($fnm,$fdir,$fext) = fileparse( $fil, qr/\.[^.]*/ );
	my (@nlines, $ncnt, @arr, $actname, $actvers, $actpp, $incfgs, $infiles);
	my ($grpname, $grpfilter, $grpfile, $ingrp, $key, $rng, $rtrng);
    my ($ind, @arr2, $tmp1, $tmp2, @adld, $ch);
	$vcprojcnt++;
	prt( "\n$vcprojcnt: Processing VCPROJ file [$fil] ...\n" ) if ($dbg42);
	if ( !open INF, "<$fil" ) {
		$wmsg = "WARNING: Unable to open [$fil] ...";
		prt( "$wmsg\n" );
		push(@warnings, $wmsg);
		return 0;
	}
	@lines = <INF>;
	close INF;
	$cnt = scalar @lines;
	prt( "$vcprojcnt: Processing $cnt lines ...\n" ) if ($dbg43);
	# push(@nlines, [$nline, $bgn, $end]);
	@nlines = reline_xml(@lines);
	$ncnt = scalar @nlines;
	prt( "$vcprojcnt: Done $cnt lines ... got $ncnt new lines ...\n" ) if ($dbg44);
	if ($dbg_out) {
		my $tmp = "C:\\GTools\\perl\\temp.$fnm.$vcprojcnt.txt";
		my $tln = '';
		for ($j = 0; $j < $ncnt; $j++) {
			$line = $nlines[$j][0];
			$tln .= "\n" if (length($tln));
			$tln .= $line;
		}
		write2file( $tln, $tmp );
	}
	$actcfg = '';
	$cfgcnt = 0;
	$incfg = 0;
	$actname = '';	# name of the PROJECT
	$actpp = '';
	$incfgs = 0;
	$infiles = 0;
	$grpname = '';
	$grpfilter = '';
	$ingrp = 0;
	for ($j = 0; $j < $ncnt; $j++) {
		$line = $nlines[$j][0];		# XML line
		$bgn = $nlines[$j][1];		# begin line number
		$end = $nlines[$j][2];		# end line number
		@arr = space_split($line);
		$ac = scalar @arr;
		$rng = "$bgn,$end";
		if ($line =~ /^<VisualStudioProject\s+/) {
			# <VisualStudioProject ProjectType="Visual C++" Version="8.00" Name="testmem" ProjectGUID="{05A9204E-6130-4347-8C31-7A063C09C276}" >
			for ($i = 0; $i < $ac; $i++) {
				$ln = $arr[$i];
				if ($ln =~ /.*Name=\"(.*)\".*/) {
					$actname = $1;
					$projcount++;
				} elsif ($ln =~ /.*Version=\"(.*)\".*/) {
					$actvers = $1;
				}
			}
			prt( "$vcprojcnt: Project Name = $actname ($actvers)...\n" ) if ($dbg45);
		} elsif ($line =~ /^<Configurations>$/) {
			$incfgs = 1;
			prt( "Enter $line\n" ) if ($showenter);
		} elsif ($line =~ /^<\/Configurations>$/) {
			$incfgs = 0;
			prt( "Exit $line\n" ) if ($showenter);
		} elsif ($line =~ /^<Files>$/) {
			$infiles = 1;
			prt( "Enter $line\n" ) if ($showenter);
		} elsif ($line =~ /^<\/Files>$/) {
			$infiles = 0;
			prt( "Exit $line\n" ) if ($showenter);
		} elsif ($line =~ /^<Configuration\s+/) {
			# <Configuration Name="Release|Win32" OutputDirectory=".\Release" IntermediateDirectory=".\Release" ConfigurationType="1" InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="false" CharacterSet="2" >
			for ($i = 0; $i < $ac; $i++) {
				$ln = $arr[$i];
				if ($ln =~ /.*Name=\"(.*)\".*/) {
					$actcfg = $1;
					$cfgcnt++;
					$incfg = 1;
					prt( "$vcprojcnt:$cfgcnt: Config Name = $actcfg ..\n" ) if ($dbg46);
					last;
				}
			}
			if (($incfg == 0) || ($i == $ac)) {
				$wmsg = "WARNING: Got Configuration WITHOUT NAME!!! file=$fil";
				push(@warnings, $wmsg);
				prt( "$wmsg\n" );
			}
		} elsif ($line =~ /^<\/Configuration>/) {
			prt( "$vcprojcnt:$cfgcnt: End Configuration ...\n" ) if ($dbg35);
			$key = $actname.'|'.$actcfg;
			if (defined $runtimes{$key}) {
				$wmsg = "WARNING: $key ALREADY EXISTS!!! file=$fil";
				push(@warnings, $wmsg);
				prt( "$wmsg\n" );
			} else {
				$runtimes{$key} = $rt;		# keep the RUNTIME
				$rtlines{$key} = $rtrng;	# and the RANGE of LINES
				$rtfiles{$key} = $fil;		# and the FILE
			}
			$actcfg = "NO ACTIVE CONFIG!!!";
			$incfg = 0;
		} elsif ($line =~ /^<Tool\s+(.*)/) {
		#} elsif ($line =~ /^<Tool\s+Name=\"VCLinkerTool\"\s+.+OutputFile=\"([\.\\\/\w]*)\"/) {
			$ln = $1;   # get balance of line, after <Tool ...
			if ($incfgs) {
				if ($ln =~ /\s*Name=\"VCCLCompilerTool\"\s*/) {
					# <Tool
					# Name="VCCLCompilerTool"
					# Optimization="2" 
					# InlineFunctionExpansion="1" 
					# PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS" 
					# StringPooling="true" 
					# RuntimeLibrary="0" 
					# EnableFunctionLevelLinking="true" 
					# PrecompiledHeaderFile=".\Release/testmem.pch" 
					# AssemblerListingLocation=".\Release/" 
					# ObjectFile=".\Release/" 
					# ProgramDataBaseFileName=".\Release/" 
					# WarningLevel="3" 
					# SuppressStartupBanner="true"
					# />
					for ($i = 0; $i < $ac; $i++) {
						$ln = $arr[$i];
						if ($ln =~ /.*RuntimeLibrary=\"(\d+)\".*/) {
							# RuntimeLibrary="2"
							$rt = $1;
							$rts = rt_2_switch($rt);
							#prt( "$line\n" );
							if ($incfg) {
								###prt( "$vcprojcnt:$cfgcnt: Runtime = $rt ($rts) ... for $actcfg ($bgn,$end)\n" );
								prt( "$vcprojcnt:$cfgcnt: Runtime = $rt ($rts) ... for $actcfg ($rng)\n" ) if ($dbg49);
								$rtrng = $rng;
							} else {
								$wmsg = "WARNING: NOT IN CONFIG BLOCK!!! file=$fil";
								push(@warnings,$wmsg);
								prt( "$wmsg\n$vcprojcnt:$cfgcnt: Runtime = $rt ($rts) ... for $actcfg ($rng)\n" );
							}
						} elsif ($ln =~ /\s*PreprocessorDefinitions=\"(.*)\"\s*/) { # like WIN32;NDEBUG;_WINDOWS" 
							$actpp = $1;
							if (length($actpp)) {
								prt( "$vcprojcnt:$cfgcnt: Preprocessor = $actpp\n" ) if ($dbg47);
							} else {
								prt( "$vcprojcnt:$cfgcnt:$i:$ac: ZERO Preprocessor = $ln ($line)\n" );
							}
						}
					}
				} elsif ($ln =~ /\s*Name=\"VCLinkerTool\"\s*/ ) {
					#prt( "$vcprojcnt:$cfgcnt: VCLinkerTool = $ln\n" );
                    # <Tool
                    # Name="VCLinkerTool"
                    # AdditionalDependencies="&quot;$(PERL5_LIB)&quot; libtidy.lib"
                    # OutputFile="Tidy.dll"
                    # LinkIncremental="1"
                    # SuppressStartupBanner="true"
                    # AdditionalLibraryDirectories="C:\Projects\Tidy\tidycvs\build\msvc\releaseDLL"
                    # ProgramDatabaseFile=".\Release/Tidy.pdb"
                    # ImportLibrary=".\Release/Tidy.lib"
                    # TargetMachine="1" />
                    @adld = ();
					for ($i = 0; $i < $ac; $i++) {
						$ln = $arr[$i];
                        # AdditionalLibraryDirectories="C:\Projects\Tidy\tidycvs\build\msvc\releaseDLL"
                        if ($ln =~ /AdditionalLibraryDirectories=/) {
                            $ind = index($ln,'"');
                            if ($ind > 0) {
                                $ln = strip_quotes(substr($ln,$ind));
                                @adld = space_split($ln);
                            }
                            last;
                        }
                    }
					for ($i = 0; $i < $ac; $i++) {
						$ln = $arr[$i];
                        if ($ln =~ /OutputFile=\"([\.\\\/\w]+)\"/) {
                            prt( "$vcprojcnt:$cfgcnt: Output = $1\n" ) if ($dbg50);
                        ###} elsif ($ln =~ /AdditionalDependencies="([&;\$\(\)\.\\\/\w]+)"/) {
                        } elsif ($ln =~ /AdditionalDependencies=/) {
                            $ind = index($ln,'"');
                            if ($ind > 0) {
                                $ln = strip_quotes(substr($ln,$ind));
                                @arr2 = space_split($ln);
                                prt( "$vcprojcnt:$cfgcnt: AdditionalDependencies = \n" ) if ($dbg52);
                                for (my $k = 0; $k < scalar @arr2; $k++) {
                                    $ln = $arr2[$k];
                                    $ln =~ s/&quot;/"/g;
                                    $ln = strip_quotes($ln);
                                    # eek, $key not yet done
                                    $wmsg = "$rt: ";
                                    # if ti LOOKS like an ENVIRONMENT variable
                                    if ($ln =~ /^\$\((.+)\)/) {
                                        $tmp1 = $1;
                                        $tmp2 = $ENV{$tmp1};
                                        if (defined $tmp2) {
                                            $ln = $tmp2;
                                        }
                                    }
                                    $wmsg .= $ln;
                                    if (-f $ln) {
                                        $wmsg .= ' ok';
                                    } elsif (@adld) {
                                        foreach $tmp1 (@adld) {
                                            $tmp2 = $tmp1;
                                            $ch = substr($tmp1,-1);
                                            if (($ch ne '/')&&($ch ne "\\")) {
                                                $tmp2 .= "\\";
                                            }
                                            $tmp2 .= $ln;
                                            if (-f $tmp2) {
                                                $wmsg .= ' ok';
                                                last;
                                            }
                                        }
                                    }
                                    prt( "$wmsg\n" ) if ($dbg52);
                                }
                            } else {
                                prt( "$vcprojcnt:$cfgcnt: AdditionalDependencies = $ln\n" ) if ($dbg52);
                            }
                        }
                    }
				} elsif ($ln =~ /\s*Name=\"VCLibrarianTool\"\s*/ ) {
					if ($ln =~ /OutputFile=\"([\.\\\/\w]+)\"/) {
						prt( "$vcprojcnt:$cfgcnt: Output = $1\n" ) if ($dbg50);
					}
				}
			}
		} elsif ($infiles) {
			if ($line =~ /^<Filter\s+(.*)/) {
				# <Filter Name="Source Files" Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" >
				$ln = $1;
				$grpname = '';
				$grpfilter = '';
				for ($i = 0; $i < $ac; $i++) {
					$ln = $arr[$i];
					if ($ln =~ /\s*Name=\"(.*)\"/) {
						$grpname = $1;
					} elsif ($ln =~ /\s*Filter=\"(.*)\"/) {
						$grpfilter = $1;
					}
				}
				prt( "Filter for \"$grpname\" = [$grpfilter]\n" ) if ($showfilter);
				$ingrp = 1;
			} elsif ($line =~ /^<\/Filter.*/) {
				$ingrp = 0;
			} elsif ($line =~ /^<File\s+(.*)/) {
				#<File RelativePath="sprtf.cxx" >
				$ln = $1;
				$grpfile = '';
				for ($i = 0; $i < $ac; $i++) {
					$ln = $arr[$i];
					if ($ln =~ /\s*RelativePath=\"(.*)\"/) {
						$grpfile = $1;
					}
				}
				prt( "File \"$grpfile\"\n" ) if ($showfiles);
			}
		}
	}
	if ($cfgcnt == 0) {
		$wmsg = "WARNING: NO CONFIGURATIONS BLOCKS!!! file=$fil";
		push(@warnings, $wmsg);
	}
	return $cnt;
}

# this could be split(/\s/,$line), but there is a
# problem with name="with space", that this overcomes.
# Slower. but sure the split is as desired.
sub space_split {
	my ($lin) = shift;
	my $ll = length($lin);
	my $tag = '';
	my @rarr = ();
	my $inquots = 0;
	for (my $p = 0; $p < $ll; $p++) {
		my $ch = substr($lin,$p,1);
		if ($inquots) {
			$tag .= $ch;
			if ($ch eq '"') {
				$inquots = 0;
			}
		} else {
			if ($ch =~ /\s/) {
				push(@rarr, $tag) if (length($tag));
				$tag = '';
			} else {
				$tag .= $ch;
				if ($ch eq '"') {
					$inquots = 1;
				}
			}
		}
	}
	push(@rarr, $tag) if (length($tag));
	return @rarr;
}

sub unix_2_dos {
	my ($f) = shift;
	$f =~ s/\//\\/g;
	return $f;
}

sub fix_rel {
	my ($path) = shift;
	$path = unix_2_dos( $path );
	my @a = split(/\\/, $path);
	my $npath = '';
	my $max = scalar @a;
	my @na = ();
	for (my $i = 0; $i < $max; $i++) {
		my $p = $a[$i];
		if ($p eq '.') {
			# ignore this
		} elsif ($p eq '..') {
			if (@na) {
				pop @na;	# discard previous
			} else {
				prt( "WARNING: Got relative .. without previous!!!\n" );
			}
		} else {
			push(@na,$p);
		}
	}
	foreach my $pt (@na) {
		$npath .= "\\" if length($npath);
		$npath .= $pt;
	}
	return $npath;
}

sub reline_xml {
	my (@lns) = @_;
	my ($i, $ln, $len, $j, $ch, $bgn, $end);
	my $cnt = scalar @lns;
	my $nline = '';
	my @nlines = ();
	for ($i = 0; $i < $cnt; $i++) {
		$ln = trim_all( $lns[$i] );
		$len = length($ln);
		for ($j = 0; $j < $len; $j++) {
			$ch = substr($ln,$j,1);
			$bgn = $i if (length($nline) == 0);
			$nline .= $ch;
			if ($ch eq '>') {
				$end = $i;
				push(@nlines, [$nline, $bgn, $end]);
				###prt( "push(\@nlines, [$nline, $bgn, $end])\n" );
				$nline = '';
			}
		}
		$nline .= ' ' if (length($nline));
	}
	push(@nlines, [$nline, $bgn, $end]) if length($nline);
	return @nlines;
}

sub rt_2_switch {
	my ($rt) = shift;
	if ($rt == 0) {
		return "Multi-threaded (/MT)";
	} elsif ($rt == 1) {
		return "Multi-threaded Debug (/MTd)";
	} elsif ($rt == 2) {
		return "Multi-threaded DLL (/MD)";
	} elsif ($rt == 3) {
		return "Multi-threaded Debug DLL (/MDd)";
	}
	return "rt$rt???";
}

#########################################
### DSP handling

sub strip_quotes {
	my ($ln) = shift;
	if ($ln =~ /^".*"$/) {
		$ln = substr($ln,1,length($ln)-2);
	}
	return $ln;
}

sub expand_mac {
	my ($m) = shift;
	if (defined $macros{$m}) {
		return $macros{$m};
	}
	return $m;
}

sub do_if_split {
	my ($ife) = shift;
	my @arr = split(/==/,$ife);
	my ($msg);
	if (scalar @arr == 2) {
		my $if0 = strip_quotes(trim_all($arr[0]));
		my $if1 = strip_quotes(trim_all($arr[1]));
		prt( "Split is [$if0] == [$if1]\n" ) if ($dbg6);
		if ($if0 =~ /^\$\((.+)\)$/) {
			my $mac = $1;
			my $emac = expand_mac($mac);
			if ($emac eq $if1) {
				prt( "Or [$emac] == [$if1] = TRUE\n" ) if ($dbg6);
				return "TRUE";
			} else {
				prt( "Or [$emac] == [$if1] = FALSE\n" ) if ($dbg6);
				return "FALSE";
			}
		}
	} else {
		$msg = "WARNING: Did NOT split! [$ife] - returning UNDETERMINED";
		prt( "$msg\n" );
		push(@warnings,$msg);
	}
	return "UNDETERMINED";
}

sub flip_state {
	my ($st) = shift;
	if ($st eq 'TRUE') {
		$st = 'FALSE';
	} elsif ($st eq 'FALSE') {
		$st = 'TRUE';
	}
	return $st;
}

sub clear_per_dsp {
	%macros = ();	# clear DSP macro set
	%dspmacros = ();
	$customdbg = '';
	$custonrel = '';
}

# scan the line, look for clues of the RUNTIME
# and other things ...
# it seems when MSVC8 loads and converts this MSVC6 file,
# it will DEFAULT to /MT or /MTd if nothing is found.
sub get_runtime {
	my ($txt) = shift;
	my $tl = length($txt);
	my $rt = 'Unknown';
	my $tag = '';
	###my $crt = 'S';
	my $crt = 'T';	# if NONE, default to this ...
	my $isdbg = -1;
	for (my $i = 0; $i < $tl; $i++) {
		my $ch = substr($txt,$i,1);
		if ($ch eq '/') {	# got SWITCH
			$i++;
			for ( ; $i < $tl ; $i++) {
				$ch = substr($txt,$i,1);
				if ($ch eq 'D') {	# got /D
					$i++;
					for ( ; $i < $tl ; $i++) {
						$ch = substr($txt,$i,1);
						if ($ch eq '"') {	# got PARAM
							$i++;
							$tag = '';
							for ( ; $i < $tl ; $i++) {
								$ch = substr($txt,$i,1);
								if ($ch eq '"') {
									last;	# exit next SPACE
								}
								$tag .= $ch;
							}
							if ($tag eq '_DEBUG') {
								$isdbg = 1;
							} elsif ($tag eq 'NDEBUG') {
								$isdbg = 0;
							}
							last;	# end /D "something"
						}
					}
				} elsif ($ch eq 'M') {	# got /M
					$i++;
					my $ccrt = '';
					for ( ; $i < $tl ; $i++) {
						$ch = substr($txt,$i,1);
						if ($ch eq 'D') {
							$crt = 'D';
							$ccrt = 'D';
						} elsif ($ch eq 'T') {
							$crt = 'T';
							$ccrt = 'T';
						} elsif ( ($ch eq 'd') && length($ccrt) ) {
							$isdbg = 1;
						} elsif ($ch =~ /\s/) {
							last;	# exit on SPACE
						}
					}
				}
			}
		}
	}
	if ($crt eq 'S') {
		$rt = 'Single-thread';
	} elsif ($crt eq 'D') {
		$rt = 'Multithreaded DLL';
	} else {
		$rt = 'Multithreaded';
	}
	if ($isdbg == 1) {
		$rt .= ' Debug';
	} elsif ($isdbg == 0) {
		$rt .= ' Release';
	}
	return $rt;
}

sub get_rtnum {
	my ($txt) = shift;
	my $rtn = 0;
	if ($txt =~ /DLL/) {
		if ($txt =~ /Debug/) {
			$rtn = 3;
		} else {
			$rtn = 2;
		}
	} else {
		if ($txt =~ /Debug/) {
			$rtn = 1;
		} else {
			$rtn = 0;
		}
	}
	return $rtn;
}

sub load_dsp {
	my ($f) = shift;
	my @dlns = ();
	my $lncnt = 0;
	my @dsrcs = ();
	my $msg = '';
	if (open FH, "<$f") {
		@dlns = <FH>;
		close FH;
		$lncnt = scalar @dlns;
		prt( "File $f contains $lncnt lines ...\n" ) if ($dbg11);
	} else {
		$msg = "WARNING: FAILED to OPEN [$f] ... $! ...";
		prt( "$msg\n" );
		push(@warnings,$msg);
	}
	my $intarg = 0;
	my @arr = ();
	my $intrue = 0;
	my $inanif = 0;
	my $package = '';
	my $insrc = 0;
	my $group = '';
	my $ifstate = "INDETERMINATE";
	my $prop = '';
	my $ifstr = '';
	my $form = '';
	my $rtdebug = '';
	my $rtrelease = '';
	my $targtyp = '';
	my $targw = '';
	my ($rt, $rtc, $rngdbg, $rngrel, $rtn, $rtdbg, $rtrel, $wmsg);
	my ($dsp_name, $dsp_dir) = fileparse( $f );
	clear_per_dsp();	# like %macros = ();	# clear DSP macro set etc
	for (my $i = 0; $i < $lncnt; $i++) {
		my $fline = $dlns[$i];	# extract the LINE
		chomp $fline;
		my $line = trim_tail($fline);
	    if ( $line =~ /$COMMENT_PATTERN/ ) {
			# starts with '#'
			$line = substr($line,1);
			if ($line =~ /\s+Microsoft\s+Developer\s+Studio\s+Project\s+File\s-\sName="(\w+)"\s+/ ) {
				$package = $1;
				$projcount++;
			} elsif ($line =~ /Microsoft Developer Studio Generated Build File, Format Version 6.00/) {
				# ignored
			} elsif ($line =~ /\*\* DO NOT EDIT \*\*/) {
				# ignored
			} elsif ($line =~ /^\s+TARGTYPE\s+"(.*)"\s+/) {
				# # TARGTYPE "Win32 (x86) Console Application" 0x0103
				$targtyp = $1;
				@arr = split(/\s/, $targtyp);
				$targw = $arr[0];
				prt( "$package TARGET: $targtyp\n" );
			} elsif ($line =~ /^\s+Begin\s+Target/) {
				$intarg = 1;
			} elsif ($line =~ /^\s+End\s+Target/) {
				$intarg = 0;
            } elsif ($line =~ /^\s+Begin\s+Group\s+(.+)/) {
				# like "Source Files"
				$group = strip_quotes($1);
				prt( "Begin Group: $1\n" ) if ($dbg10);
            } elsif ($line =~ /^\s+End\s+Group/) {
				$group = '';
			} elsif ($line =~ /\s*Begin\s+Project/ ) {

			} elsif ($line =~ /\s*End\s+Project/ ) {

			} elsif ($line =~ /Begin\s+Special\s+Build\s+Tool/) {

			} elsif ($line =~ /End\s+Special\s+Build\s+Tool/) {

			} elsif ($line =~ /\s*Name\s+(.+)/) {

			} elsif ($line =~ /\s*Begin\s+Source\s+File/) {
				$insrc = 1;
			} elsif ($line =~ /\s*End\s+Source\s+File/) {
				$insrc = 0;
			} elsif ($line =~ /\s*PROP\s+(.*)/) {
				# like '# PROP BASE Use_Debug_Libraries 1'
				$prop = $1;
				if ($prop =~ /\s*BASE\s+(.*)/) {
					$prop = $1;
				}
				if ($prop =~ /Use_Debug_Libraries\s+(\d+)/) {
					prt( "Use_Debug_Libraries $1\n" ) if ($dgb32);
				}
				if ($excl_excluded && $insrc) {
					# PROP Exclude_From_Build 1
					if ($prop =~ /Exclude_From_Build\s+(\d+)/) {
						if ($1) {
							if (@dsrcs) {
								my $ls = pop(@dsrcs);
								push(@discardedsrcs, [$ls, $package, $f]);
								prt( "Discarding [$ls] due to Exclude_From_Build ...\n" ) if ($dbg30);
							}
						}
					}
				}
			} elsif ($line =~ /\s*ADD\s+(.*)/) {
				# like '# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D
				# and '# ADD CPP /nologo /MDd ...
				$prop = $1;
				if ($prop =~ /\s*BASE\s+(.*)/) {
					$prop = $1;
				}
				if ($prop =~ /CPP\s+(.*)/) {
					$rt = $1;
					$rtc = get_runtime($rt);
					$rtn = get_rtnum($rtc);
					prt( "RUNTIME: $form $rt\n" ) if ($dgb33);
					if ($form eq 'Debug') {
						$rtdebug = $rtc;
						$rngdbg = $i + 1;
						$rtdbg = $rtn;
					} elsif ($form eq 'Release') {
						$rtrelease = $rtc;
						$rngrel = $i + 1;
						$rtrel = $rtn;
					}
				}
			} elsif ($line =~ /\s*SUBTRACT\s+(.*)/) {
				$prop = $1;
			} elsif ($line =~ /\s*Begin\s+Custom\s+Build(.*)/) {
				$msg = $1;
				$i++;
				for (; $i < $lncnt; $i++) {
					$fline = $dlns[$i];	# extract the LINE
					chomp $fline;
					$line = trim_tail($fline);
					$msg .= "\n$fline";
					if ($line =~ /\s*End\s+Custom\s+Build/) {
						last;
					}
				}
			} else {
				$msg = "WARNING: Line [$line] not handled!";
				prt("$msg\n");
				push(@warnings,$msg);
			}
        } elsif ($line =~ /^!/ ) {
			# starts with '!'
			$line = substr($line,1);
			if ($line =~ /^IF\s+(.*)/ ) {
				#' "$(CFG)" == "xmlrpc_sample_add_server - Win32 Release"
				$ifstr = $1;
				$ifstate = do_if_split($ifstr);
				$msg = "Entered IF [$ifstr] ";
				$msg .= $ifstate;
				if ($ifstr =~ /"\$\(CFG\)"/ ) {
					if ($ifstr =~ /\s+-\s+Win32\s+(.*)"/) {
						$form = $1;
						$msg .= " $form ";
					}
				}
				$inanif++;
				prt( "$msg $inanif\n" ) if ($dbg8);
			} elsif ($line =~ /^ELSEIF\s+(.*)/ ) {
				$ifstr = $1;
				$ifstate = do_if_split($ifstr);
				$msg = "Entered ELSEIF [$ifstr] ";
				$msg .= $ifstate;
				if ($ifstr =~ /"\$\(CFG\)"/ ) {
					if ($ifstr =~ /\s+-\s+Win32\s+(.*)"/) {
						$form = $1;
						$msg .= " $form ";
					}
				}
				prt( "$msg $inanif\n" ) if ($dbg8);
			} elsif ($line =~ /^ELSE\s*/ ) {
				$ifstate = flip_state($ifstate);
				prt( "Entered ELSE [$line]\n" ) if ($dbg8);
			} elsif ($line =~ /^ENDIF\s*/ ) {
				prt( "Out IF with ENDIF\n" ) if ($dbg8);
				$inanif = 0;
				$ifstate = 'OUTIF';
			} elsif ($line =~ /^MESSAGE\s*/ ) {
				#prt( "MESSAGE LINE ...\n" );
			} else {
				$msg = "WARNING: What is THIS [$fline]??? in [$f]";
				prt( "$msg\n" );
				push(@warnings,$msg);
			}
		} elsif ($intarg) {
			if( $line =~ /^SOURCE=(.+)/ ) {
				$line = strip_quotes($1);
				my $ff = fix_rel($dsp_dir . $line);
				if (($line =~ /\.cxx$/i) || ($line =~ /\.c$/i) || ($line =~ /\.cpp$/i)) {
					push(@dsrcs, $ff);
				} else {
					if ( !(($line =~ /\.hxx$/i) || ($line =~ /\.h$/i) || ($line =~ /\.hpp$/i)) ) {
						if ($dbg31) {
							$msg = "WARNING: CHECK Discarded [$fline]";
							prt( "$msg\n" );
							push(@warnings,$msg);
						}
					}
				}
			}
		} else {
			# NOT in Begin Target yet
			if ($line =~ /$MACRO_PATTERN2/) {
				if (defined $macros{$1}) {
					if ($macros{$1} ne $2) {
						if ($dbg34) {
							$msg = "WARNING: Duplicated MACRO $1, now $2, was $macros{$1} ...";
							prt( "$msg\n" );
							push(@warnings,$msg);
						}
					}
				} else {
					$macros{$1} = $2;
					prt( "SET: MACRO $1, to $2 ...\n" ) if ($dbg9);
				}
			}
		}
	}
	### establish RUNTIMES
	my $key = $package.'|Debug|Win32';
	if (defined $runtimes{$key}) {
		$wmsg = "WARNING: $key ALREADY EXISTS!!! file=$f";
		push(@warnings, $wmsg);
		prt( "$wmsg\n" );
	} else {
		$runtimes{$key} = $rtdbg;		# keep the RUNTIME
		$rtlines{$key} = "$rngdbg,$rngdbg";	# and the RANGE of LINES
		$rtfiles{$key} = $f;		# and the FILE
	}
	$key = $package.'|Release|Win32';
	if (defined $runtimes{$key}) {
		$wmsg = "WARNING: $key ALREADY EXISTS!!! file=$f";
		push(@warnings, $wmsg);
		prt( "$wmsg\n" );
	} else {
		$runtimes{$key} = $rtrel;		# keep the RUNTIME
		$rtlines{$key} = "$rngrel,$rngrel";	# and the RANGE of LINES
		$rtfiles{$key} = $f;		# and the FILE
	}

	prt( "RUNTIME: Release $rtrelease\n" );
	prt( "RUNTIME: Debug   $rtdebug\n" );
	$lncnt = scalar @dsrcs;
	prt( "File $f contains $lncnt SOURCES ...\n" ) if ($dbg11);
	return @dsrcs;
}

##################
### utils
sub trim_tail {
	my ($ln) = shift;
	while ($ln =~ /\s$/) {
		$ln = substr($ln,0,length($ln) - 1); # remove all TRAILING space
	}
	return $ln;
}


sub show_runtimes {
	prt( "\nIn $projcount projects, found following runtimes ...\n" );
	my ($ky, $rt, $be, $min, $len, $msg, $fi, $lastfil, @arr, $lastpj, $i);
	$min = 0;
    $lastfil = '';
    $lastpj = '';
	foreach $ky (keys %runtimes) {
		$len = length($ky);
		$min = $len if ($len > $min);
		$rt = $runtimes{$ky};
		if (($rt == 0)||($rt == 1)) {
			$mtrts++;
		} elsif (($rt == 2)||($rt == 3)) {
			$dllrts++;
		} else {
			$otherrts++;
		}
	}
	if ($otherrts) {
		prt( "Got $otherrts OTHER RUNTIMES??? MT=$mtrts MD=$dllrts\n" );
	} else {
		if ($mtrts && ($dllrts == 0)) {
			prt( "Got ALL MT RUNTIMES ($mtrts)\n" );
		} elsif ( $dllrts && ($mtrts == 0) ) {
			prt( "Got ALL MD RUNTIMES ($dllrts)\n" );
		} elsif ( ($dllrts == 0) && ($mtrts == 0)) {
			prt( "Got NO RUNTIMES!\n" );
		} else {
			prt( "Got MIXED RUNTIMES - MT=$mtrts MD=$dllrts\n" );
		}
	}
	foreach $ky (sort keys( %runtimes )) {
        @arr = split(/\|/,$ky);
        if ($arr[0] eq $lastpj) {
            $msg = ' ';
    		$msg .= ' ' while (length($msg) < length($lastpj));
            for ($i = 1; $i < scalar @arr; $i++) {
                $msg .= '|';
                $msg .= $arr[$i];
            }
        } else {
            $msg = $ky;
        }
        $lastpj = $arr[0];
		$msg .= ' ' while (length($msg) < $min);
		$rt = $runtimes{$ky};
		$be = $rtlines{$ky};
		$fi = $rtfiles{$ky};
		$msg .= " = ".rt_2_switch($rt);
		$msg .= ' ' while (length($msg) < ($min+3+28));
		$msg .= " ($be) ";
		$msg .= ' ' while (length($msg) < ($min+3+28+12));
        $msg .= $fi if ($lastfil ne $fi);
		prt( "$msg\n" );
        $lastfil = $fi;
	}
	prt("\n");
}

# eof - chkrt.pl
