#!/Perl
# vc8srcs03.pl
# AIM: Source list from MSVC8 project file
# but this version starts with the SOLUTION (.sln) file,
# finds the PROJECT (.vcproj), and gets the PROJECTS, and
# the SOURCES from there, and lists them
#
# 13/11/2007 - geoff mclane - http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;	# to split path into ($name, $dir, $ext)

require "logfile.pl" or die "Missing logfile.pl ...\n"; # my simple log file and some other utility subs
require "amfile01.pl" or die "Missing amfile01.pl ...\n"; # parse AM file ...

# set a DEFAULT input file name
my $root_dir = "C:\\GTools\\tools\\testap3\\";
my $inp_file = $root_dir. "testap3.sln";
#my $root_dir = "C:\\FG\\FGCOMXML\\xmlrpc-c\\";
#my $inp_file = $root_dir. "Windows\\xmlrpc.sln";
##my $root_dir = "C:\\GTools\\Tools\\Dv32\\";
##my $inp_file = $root_dir . "Dv32.sln";
#my $root_dir = "C:\\FGCVS\\iaxclient\\";
#my $inp_file = $root_dir . "contrib\\win\\vs2005\\iaxclient.sln";
#my $root_dir = "C:\\FG\\FGCOM2\\iaxclient\\lib\\";
#my $inp_file = $root_dir . "win\\vs2005\\iaxclient_lib.sln";
###my $root_dir = "C:\\FG\\FGCOM\\xmlrpc-c\\";
###my $inp_file = $root_dir . "Windows\\xmlrpc.sln";
##my $inp_file = 'C:\FG\12\fgfs\fgfs.sln';
##my $inp_file = 'C:\FG\FG0910-8\fgfs\fgfs.sln';
##my $inp_file = 'F:\FG0910-4\flightgear\projects\VC8\FlightGear.sln';
##my $inp_file = 'F:\FG0910-4\simgear\projects\VC8\simgear.sln';
### features
my $AM_COMPARE = 0;	# add read makefile.am for sources, but this does NOT work
# on projects that do NOT use Makefile.am to control SOURCES
my $DSP_COMPARE = 1; # if .DSW and .DSP files found, then extract sourcess
my $USE_ROOT_DIR = 1;	# use $root_dir, not $top_dir ...
my $show_full_source = 1;	# do NOT sutract ROOT folder
my $SHOW_HDRS_MISSED = 1;	# after C sources, show H sources MISSED

# String constants.
my $COMMENT_PATTERN = "^#";
my $MACRO_PATTERN2 = "^([A-Za-z][A-Za-z0-9_]+)[ \t]*=[ \t]*(.*)\$";
###my @incl_c = qw( .cxx .c .inl .cpp .cc .c++ );
###my @incl_h = qw( .hxx .h .hh .hpp .h );
my $long_name = '  portaudio\bindings\cpp\source\portaudiocpp\DirectionSpecificStreamParameters.cxx ';
my $min_len = length($long_name);
#########################################################
# program variables
my @warnings = ();	# keep warnings
my $inp_dir = '';
my $inp_title = '';
my $inp_ext = '';
# debug flags
my $dbg1 = 0;	# show VCPROJ files in SLN file
my $dbg2 = 0;	# show 'Loading ...' VCPROJ file
my $dbg3 = 0;	# show 	"Processing nn lines in $in ..."
my $dbg4 = 0;	# show "Got new and nn count ..."
my $dbg5 = 0;	# show "Got PROJECT ..." from DSW file
my $dbg6 = 0;	# show DSP IF/ELSEIF/ELSE/ENDIF split parsing
my $dbg7 = 0;	# show "Project=$projname, v=$version\n"
my $dbg8 = 0;	# show DSP IF/ELSEIF/ELSE/ENDIF parsing
my $dbg9 = 0;	# show SET macro
my $dbg10 = 0;	# show DSP counts ...
my $dbg11 = 0;	# show Begin Group: "Source Files"
my $dbg11 = 0;	# show "$package TARGET: $1\n" during DSP decode

my $dbg_on1 = 0;	# show LOADING vcproj ...
my $dbg_on2 = 0;	# show AM file processing, if any ...
my $dbg_on3 = 0;	# show adding folder
my $dbg_on4 = 0;	# show directory and found
my $dbg_src1 = 0;	# show each SOURCE, as found
my $dbg_src2 = 0;	# show each HEADER, as found
my $dbg_src3 = 0;	# show each OTHER, as found
# log file stuff
my ($LF);
my $outfile = 'temp.'.$0.'.txt';
open_log($outfile);
my $file = '';
my $fl1 = 'Files';
my $fl2 = 'File';
my $fl3 = 'RelativePath';
my $fl4 = 'Filter';
my $cnt = 0;
my @csrc_array = ();
my @hsrc_array = ();
my @osrc_array = ();
my @cdir_array = ();
my @hdir_array = ();
my @odir_array = ();
my @files = ();
my @lines = ();
my @proj_files = ();
my @proj_dirs = ();
my @not_found = ();
my @not_found2 = ();
my $prev_srcs = 0;
my $prev_hdrs = 0;
my $prev_othe = 0;
my $line = '';
my $try3 = 0;

my %projfiles = ();	# list of SLN projects found, and sources, with FULL path
my %projfilesasis = ();	# exactly as extracted from the VCPROJ file
my %projvcproj = ();
my %projhdrs = ();	# list of SLN projects HEADERS found, with FULL path
my $proj_cnt = 0;
my $dsw_file = '';	# SLN to DSW
my %dswprojs = ();	# Load of DSW file ...
my %dspfiles = ();	# Load of DSP files ...
my %dsphdrs = ();	# DSP headers found

my $no_dsw = 0;		# set to1 if NO DSW file found
my $dsp_cnt = 0;
my @sln_missed = ();
my @dsw_missed = ();
my %macros = ();	# macros found in DSP file

my $top_dir = '';	# get the TOP directory, from all the SOURCE scanning ...
my @all_files = ();	# list of ALL files in $top_dir ...
my $top_cnt = 0;
# TYPE is_my_type CONSTANTS
my $TYPE_C = 1;
my $TYPE_H = 2;
my $TYPE_DSW = 3;
my $TYPE_SLN = 4;

my @am_sources = ();
prt( "$0 ... Hello, World ...\n" );
if (@ARGV) {
	$inp_file = shift @ARGV;
}

($inp_title, $inp_dir, $inp_ext) = fileparse( $inp_file, qr/\.[^.]*/ );
if ( -f $inp_file ) {
	if (is_solution($inp_file)) {
		get_xml_projects($inp_file);
		if ($DSP_COMPARE) {
			$dsw_file = $inp_dir . $inp_title . ".dsw";
			if (-f $dsw_file) {
				$no_dsw = 0;
				get_dsw_projects($dsw_file);
			} else {
				$no_dsw = 1;
				prt( "WARNING: Unable to locate a $inp_title DSW file ...\n" );
			}
		}
	} elsif (is_vcproj($inp_file)) {
		push(@proj_files, $inp_file);
	} else {
		prt( "WARNING: Unknown file type [$inp_file] ...\n" );
		prt( "Proceeding ASSUMING a project (XML) file ...\n" );
		push(@proj_files, $inp_file);
	}
	if (@proj_files) {
		prt("Processing ".scalar @proj_files." file(s) ...\n");
	} else {
		mydie( "ERROR: Have no PROJECT (.vcproj) files to process!\n" );
	}
	foreach $line (@proj_files) {
		# process EACH .vcproj file found in .SLN
		get_xml_sources($line);	# extract XML source from vcproj file
		$prev_srcs = scalar @csrc_array;
		$prev_hdrs = scalar @hsrc_array;
		$prev_othe = scalar @osrc_array;
	}
	if ($DSP_COMPARE) {
		my $dspcnt = scalar keys(%dswprojs);
		if ($dspcnt) {
			prt( "DSP_COMPARE: Loading $dspcnt DSP files from DSW file ...\n" );
		} else {
			if ($no_dsw) {
				prt( "DSP_COMPARE: No DSW file found ...\n" );
			} else {
				prt( "DSP_COMPARE: DSW found, but NO Projects found ...\n" );
			}
		}
		foreach my $key (keys %dswprojs) {
			my @ra = load_dsp( $key, $dswprojs{$key} );
			my $ds = $ra[0][0];
			$dspfiles{$key} = $ds;
			$dsphdrs{$key} = $ra[0][1];
		}
	} else {
		prt( "No DSP compare since \$DSP_COMPARE is OFF ($DSP_COMPARE)\n" );
	}
	if($prev_srcs) {
		process_sources();
	} else {
		prt("ERROR: No C/C++ sources found to process ...\n");
	}
} else {
	prt( "ERROR: Can not locate [$inp_file] ... $! ...\n" );
	if ( -d $inp_dir ) {
		prt( "Note: [$inp_dir] does exist ...\n" );
	} else {
		prt( "Note: [$inp_dir] does not exist ...\n" );
	}
}

if ($USE_ROOT_DIR) {
	if (length($root_dir)) {
		if (-d $root_dir) {
			# we have a VALID TOP DIRECTORY
			get_top_files( $root_dir, 0 );
			$top_cnt = scalar @all_files;
		} else {
			prt( "WARNING: [$root_dir] NOT VALID!!!\n" );
		}
	} else {
		prt( "WARNING: [$root_dir] NOT SET!!!\n" );
	}
} else {
	if (length($top_dir)) {
		if (-d $top_dir) {
			# we have a VALID TOP DIRECTORY
			get_top_files( $top_dir, 0 );
			$top_cnt = scalar @all_files;
		} else {
			prt( "WARNING: [$top_dir] NOT VALID!!!\n" );
		}
	} else {
		prt( "WARNING: top_dir NOT SET!!!\n" );
	}
}

######### SOURCE LIST DISPLAY ############
$proj_cnt = scalar keys( %projfiles );
$dsp_cnt = scalar keys( %dspfiles );
prt( "Top count $top_cnt, sln count $proj_cnt, dsw count $dsp_cnt...\n" );
if ($proj_cnt) {
	show_vc8_sources();
}
if ($dsp_cnt) {
	show_dsp_sources();
}

if (!$no_dsw) {
	show_dsw_compare();
}

show_all_sources();

##########################################

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

##############################################
### program subs

sub show_dsw_compare {
	my $dmcnt = scalar @dsw_missed;
	if ($dmcnt) {
		prt( "\nNOTE: $dmcnt files in SLN, NOT in DSW ...\n" );
		for (my $i = 0; $i < $dmcnt; $i++) {
			prt( "P=$dsw_missed[$i][0] - S=$dsw_missed[$i][1]\n" );
		}
	}
	my $smcnt = scalar @sln_missed;
	prt( "\nNOTE: $smcnt files in DSW, NOT in SLN ...\n" );
	if ($smcnt) {
		for (my $i = 0; $i < $smcnt; $i++) {
			prt( "P=$sln_missed[$i][0] - S=$sln_missed[$i][1]\n" );
		}
	}
}

sub in_dsw_srcs {
	my ($prj, $fil) = @_;
	if (defined $dspfiles{$prj}) {
		my $lcfil = lc($fil);
		my $dfs = $dspfiles{$prj};
		my @df = split(/\*/, $dfs);
		foreach my $f (@df) {
			if (lc($f) eq $lcfil) {
				return 1;
			}
		}
	}
	return 0;
}

sub in_dsw_hdrs {
	my ($prj, $fil) = @_;
	if (defined $dsphdrs{$prj}) {
		my $lcfil = lc($fil);
		my $dfs = $dsphdrs{$prj};
		my @df = split(/\*/, $dfs);
		foreach my $f (@df) {
			if (lc($f) eq $lcfil) {
				return 1;
			}
		}
	}
	return 0;
}


sub in_sln_srcs {
	my ($prj, $fil) = @_;
	if (defined $projfiles{$prj}) {
		my $lcfil = lc($fil);
		my $dfs = $projfiles{$prj};
		my @df = split(/\*/, $dfs);
		foreach my $f (@df) {
			if (lc($f) eq $lcfil) {
				return 1;
			}
		}
	}
	return 0;
}


sub show_vc8_sources {
	prt( "\nList of $proj_cnt VC8 projects, and their SOURCES ... SLN = $inp_file\n" );
	my $msg = '';
	my ($mk, $key, $pfs, $inf, @pf, $cnt, $fl);
	foreach $key (keys %projfiles) {
		$pfs = $projfiles{$key};
		#my $pfs2 = $projfilesasis{$key};
		$inf = $projvcproj{$key};
		@pf = split(/\*/, $pfs);
		#my @pf2 = split(/\*/,$pfs2);
		$cnt = scalar @pf;
		prt( "\nVC8 Project: $key, has $cnt sources ... root = $root_dir\n" );
		foreach $fl (sort @pf) {
			##$msg = "$fl ";
			if ($show_full_source) {
				$msg = " $fl ";
			} else {
				$msg = " ".sub_root($fl)." ";
			}
			if ($dsp_cnt) {
				# we have DSW/DSP sources - COMPARE
				if (in_dsw_srcs($key, $fl)) {
					$msg .= "(in DSW)";
				} else {
					push(@dsw_missed, [$key, $fl]);
					$msg .= "MISSING in DSW";
				}
			}
			$mk = mark_all_files($fl);
			while (length($msg) < $min_len) {
				$msg .= ' ';
			}
			if ($mk) {
				$msg .= ' ok';
			} else {
				$msg .= ' MISSED!';
				if (-f $fl) {
					$msg .= " but exists";
				}
				#my ($nm1,$dr1) = fileparse($fl);
				#foreach my $itm (@pf2) {
				#	my ($nm2,$dr2) = fileparse($itm);
				#	if ($nm1 eq $nm2) {
				#		$msg .= " [$itm] in $inf";
				#		last;
				#	}
				#}
			}
			prt( "$msg\n" );
		}
		if ($SHOW_HDRS_MISSED) {
			if (defined $projhdrs{$key}) {
				$pfs = $projhdrs{$key};	# extract HEADERS
				@pf = split(/\*/, $pfs);	# split them up
				$cnt = scalar @pf;		# get COUNT
				prt( "VC8 Project: $key, has $cnt HEADERS ... root = $root_dir\n" );
				foreach $fl (sort @pf) {
					##$msg = "$fl ";
					if ($show_full_source) {
						$msg = " $fl ";
					} else {
						$msg = " ".sub_root($fl)." ";
					}
					if ($dsp_cnt) {
						# we have DSW/DSP sources - COMPARE
						if (in_dsw_hdrs($key, $fl)) {
							$msg .= "(in DSW)";
						} else {
							push(@dsw_missed, [$key, $fl]);
							$msg .= "MISSING in DSW";
						}
					}
					$mk = mark_all_files($fl);
					while (length($msg) < $min_len) {
						$msg .= ' ';
					}
					if ($mk) {
						$msg .= ' ok';
					} else {
						$msg .= ' MISSED!';
						if (-f $fl) {
							$msg .= " but exists";
						}
					}
					prt( "$msg\n" );
				}
			}
		}
	}
}

sub show_dsp_sources {
	prt( "\nList of $dsp_cnt DSP projects, and their SOURCES ...\n" );
	my $msg = '';
	my ($mk);
	foreach my $key (keys %dspfiles) {
		my $pfs = $dspfiles{$key};
		my @pf = split(/\*/, $pfs);
		my $cnt = scalar @pf;
		prt( "\nDSP Project: $key, has $cnt sources ... root = $root_dir\n" );
		foreach my $fl (sort @pf) {
			##$msg = "$fl ";
			$msg = " ".sub_root($fl)." ";
			if ($proj_cnt) {
				# we have SLN/VCPROJ sources - COMPARE
				if (in_sln_srcs($key, $fl)) {
					$msg .= "(in SLN)";
				} else {
					push(@sln_missed, [$key, $fl]);
					$msg .= "MISSING in SLN";
				}
			}
			$mk = mark_all_files($fl);
			while (length($msg) < $min_len) {
				$msg .= ' ';
			}
			if ($mk) {
				$msg .= ' ok';
			} else {
				$msg .= ' MISSED!';
			}
			prt( "$msg\n" );
		}
	}
}

sub get_dsw_projects {
	my ($inf) = shift;	# the $dsw_file
	if (open INF, "<$inf") {
		my @lns = <INF>;
		close INF;
		prt( "DSP_COMPARE: Processing [$inf], got ".scalar @lns." lines ...\n" );
		my ($nm, $dir, $ext) = fileparse( $inf, qr/\.[^.]*/ );
		my $dcnt = 0;
		foreach my $ln (@lns) {
			# seeking Project: "gennmtab"=".\gennmtab.dsp" - Package Owner=<4>
			if ($ln =~ /^Project:\s+"(\w+)"="*([\w\.\\]+)"*\s+/) {
				my $pn = $1;
				my $pf = $2;
				my $ff = fix_rel($dir . $pf);
				$dcnt++;
				prt( "Got Project: $pn, $ff ...\n" ) if ($dbg5);
				if (defined $dswprojs{$pn} ) {
					prt( "WARNING: Duplicate PROJECT [%pn] ... $pf versus ".$dswprojs{$pn}."\n" );
				} else {
					$dswprojs{$pn} = $ff;	# keep project DSP file
				}
			}
		}
		prt( "DSP_COMPARE: Got $dcnt DSP files ...\n" );
	} else {
		prt( "WARNING: Unable to OPEN $inf ... $! ...\n" );
	}
}

sub process_sources {
	if ($AM_COMPARE) {
		prt("\nGetting folder list from C/C++ source files ...\n");
		foreach my $fl (@csrc_array) {
			my $dir = file_dirname($fl);
			if (!in_dir_array($dir)) {
				prt("Adding folder [$dir] to \$proj_dirs list ...\n") if ($dbg_on3);
				push(@proj_dirs, $dir);
			}
		}
		prt("Got ".scalar @proj_dirs." folders to check ...\n" );
		$prev_srcs = 0;
		$prev_hdrs = 0;
		$prev_othe = 0;
		foreach my $line2 (@proj_dirs) {
			get_dir_sources($line2); # and process any AM file found ...
			$prev_srcs = scalar @cdir_array;
			$prev_hdrs = scalar @hdir_array;
			$prev_othe = scalar @odir_array;
		}

		cmp_c_sources();
		if (@am_sources) {
			prt("Also got ".scalar @am_sources." SOURCE files from AM files ...\n");
			cmp_am_sources();
		}
	} else {
		prt( "No AM  compare, since \$AM_COMPARE is OFF ($AM_COMPARE)\n" );
	}
}

sub in_dir_array {
	my ($d1) = shift;
	foreach my $d2 (@proj_dirs) {
		if ($d1 eq $d2) {
			return 1;
		}
	}
	return 0;
}

# get_xml_projects
# parse the MS solution file, and extract the VCPROJ files
# contined there in ...
sub get_xml_projects {
	my ($in_file) = shift;
	my $in_fd = file_dirname($in_file);
	prt( "Loading [$in_file] in directory [$in_fd] ...\n" ) if ($dbg_on1);
	if (open FH, "<$in_file") {
		@lines = <FH>; # slurp the whole file
		close( FH );
	} else {
		prt( "ERROR: Can not open [$in_file] ... \n" );
		return;
	}
	prt( "Processing ".scalar @lines." lines from $in_file ...\n" );
	my $hadver = 0;
	foreach $line (@lines) {
		chomp $line;
		if ($hadver) {
			if ($line =~ /^Project\s*\(/) {
				##prt( "Got project [$line] ...\n" );
				my @arr = split( /\"/, $line );
				foreach my $par (@arr) {
					if (is_vcproj($par)) {
						my $ff = $in_fd.$par;
						prt( "Got PROJECT file [$par] " ) if ($dbg1);
						if ( -f $ff) {
							prt( "ok" ) if ($dbg1);
						} else {
							prt( "FAILED" ) if ($dbg1);
						}
						prt("\n") if ($dbg1);
						push(@proj_files, $ff);
					}
				}
			}
		} else {
			# seeking 'Microsoft Visual Studio Solution File, Format Version 9.00'
			#if ($line =~ /^Microsoft\s+.(\d+\.\d+)/) {
			if ($line =~ /^Microsoft\s+/) {
				if ($line =~ /.(\d+\.\d+)/) {
					my $ver = $1;
					prt( "Got solution file version [$ver] ...\n" );
					$hadver = 1;
				}
			}
		}
	}
}

# get_dir_sources - part of $AM_COMPARE
# Process the relative folders from the project file,
# and collect ALL the files in those folders ...
# An extension would be to parse the makefile.am, if present,
# and check WHAT sources actually SHOULD be included
# Some sources belong to other test executable items, or
# perhaps are just not used unless certain 'switches' are on ...
# And this does NOT include other possible folders, not already
# apparent from the VCPROJ files ...
sub get_dir_sources {	# part of $AM_COMPARE
	my ($in) = shift;
	prt( "\nProcessing directory [$in] ...\n" ) if ($dbg_on4);
	if ( !opendir(DIR, $in) ) {
		prt( "ERROR: Unable to open directory [$in] ...\n" );
		return;
	};
	@files = readdir(DIR);
	closedir DIR;
	$cnt = 0;
	foreach $file (@files) {
		if (($file eq '.') || ($file eq '..')) {
			next;
		}
		$cnt++;
		###$ff = $in_dir . '\\' . $file;
		my $ff = $in . $file;
		# prt( "$cnt $file ($ff)\n" );
		if (is_c_source($file)) {
			prt( "src $cnt $file ($ff)\n" ) if ($dbg_on2);
			push(@cdir_array,$ff);
		} elsif (is_h_source($file)) {	# if .h, .hpp, .hxx
			prt( "hdr $cnt $file ($ff)\n" ) if ($dbg_on2);
			push(@hdir_array,$ff);
		} else {
			prt( "other $cnt $file ($ff)\n" ) if ($dbg_on2);
			push(@odir_array,$ff);
			# seek .am files, and get sources IFF $AM_COMPARE
			if ($AM_COMPARE && ($file =~ /\.am$/i)) {
				prt( "\nProcessing AM file [$ff] ...\n" ) if ($dbg_on2);
				initialize_per_input();
				my @arr = read_am_file($ff);
				foreach my $s (@arr) {
					my $s2 = trim_line($s);
					if (length($s2)) {
						if (is_c_source($s2)) {
							my $ff2 = $in.$s2;
							push(@am_sources, $ff2);
						} elsif (is_h_source($s2)) {
							# quietly FORGET these ... for now ...
						} else {
							prt( "CHECK AM Discarded [$s2] ...\n" );
						}
					}
				}
				prt( "Done AM file [$ff] ...got ".scalar @arr." sources ...\n" ) if ($dbg_on2);
			}
		}
	}
	my $new_srcs = scalar @cdir_array - $prev_srcs;
	my $new_hdrs = scalar @hdir_array - $prev_hdrs;
	my $new_othe = scalar @odir_array - $prev_othe;
	prt( "Got new $new_srcs C/C++ files, $new_hdrs header files, and others $new_othe\n" ) if ($dbg_on4);
	prt( "Got ".scalar @cdir_array." C/C++ files, ".scalar @hdir_array. " header files" ) if ($dbg_on4);
	if (@odir_array) {
		prt( " and ".scalar @odir_array." other files" ) if ($dbg_on4);
	}
	prt("\n") if ($dbg_on4);
}

sub mark_all_files {
	my ($f) = shift;
	my $lcf = lc($f);
	#                   0    1    2  3
	# push(@all_files, [$df, $ff, 0, $typ]) if ($typ);
	my $ac = scalar @all_files;
	for (my $i = 0; $i < $ac; $i++) {
		my $tf = lc($all_files[$i][1]);
		if ($tf eq $lcf) {
			my $ct = $all_files[$i][2];
			$ct++;
			$all_files[$i][2] = $ct;
			return 1;
		}
	}
	return 0;
}

sub show_all_sources {
	my $ac = scalar @all_files;
	# push(@all_files, [$df, $ff, 0, $typ]) if ($typ);
	my $mc = 0;
	my $i = 0;
	for ($i = 0; $i < $ac; $i++) {
		if ($all_files[$i][3] == $TYPE_C) {
			if ($all_files[$i][2] == 0) {
				$mc++;
			}
		}
	}
	if ($mc) {
		prt( "\nSources found, but MISSED - $mc ... root = $top_dir\n" );
		for ($i = 0; $i < $ac; $i++) {
			if ($all_files[$i][3] == $TYPE_C) {
				if ($all_files[$i][2] == 0) {
					prt( "$all_files[$i][1]\n" );
				}
			}
		}
		prt( "Above $mc Sources NOT INCLUDED in DSW nor SLN ...\n\n" );
	} else {
		prt( "Appears NO sources MISSED from root scan = $root_dir\n" );
	}
	if ($SHOW_HDRS_MISSED) {
		$mc = 0;
		for ($i = 0; $i < $ac; $i++) {
			if ($all_files[$i][3] == $TYPE_H) {
				if ($all_files[$i][2] == 0) {
					$mc++;
				}
			}
		}
		if ($mc) {
			prt( "\nHeaders MISSED - $mc ... root = $top_dir\n" );
			for ($i = 0; $i < $ac; $i++) {
				if ($all_files[$i][3] == $TYPE_H) {
					if ($all_files[$i][2] == 0) {
						prt( "$all_files[$i][1]\n" );
					}
				}
			}
			prt( "Above $mc Headers NOT INCLUDED in DSW nor SLN ...\n\n" );
		}
	}
}

sub get_top_files {
	my ($td, $dep) = @_;
	my @dirs = ();
	prt( "Moment ... collecting files from [$td] ...\n" ) if ($dep == 0);
	$td = unix_2_dos($td);
	$td .= "\\" if (substr($td,length($td)-1) ne "\\");
	if (opendir(DIR, $td)) {
		my @dfiles = readdir(DIR);
		close DIR;
		foreach my $df (@dfiles) {
			if (($df eq '.') || ($df eq '..')) {
				next;
			}
			my $ff = $td.$df;
			if (-f $ff) {
				my $typ = is_my_type($df);
				push(@all_files, [$df, $ff, 0, $typ]) if ($typ);
			} elsif (-d $ff) {
				push(@dirs,$ff);
			} else {
				prt( "WARNING: What is THIS [$ff] ???\n" );
			}
		}
	} else {
		prt( "WARNING: Unable to OPEN directory $td ...\n" );
	}
	foreach my $de (@dirs) {
		get_top_files($de, ($dep + 1) );
	}
}

sub trimall($) {
	my ($ln) = shift;
	chomp $ln;			# remove CR (\n)
	$ln =~ s/\r$//;		# remove LF (\r)
	$ln =~ s/\t/ /g;	# TAB(s) to a SPACE
	while ($ln =~ /\s\s/) {
		$ln =~ s/\s\s/ /g;	# all double space to SINGLE
	}
	while ($ln =~ /^\s/) {
		$ln = substr($ln,1); # remove all LEADING space
	}
	while ($ln =~ /\s$/) {
		$ln = substr($ln,0, length($ln) - 1); # remove all TRAILING space
	}
	return $ln;
}

# get_xml_source
# process the XML project file (*.vcproj) and
# extract the SOURCE file list
sub get_xml_sources {
	my ($in) = shift;
	my $in_fd = file_dirname($in); # this could be the TOP, if no relative sources
	my ($src, $ff, $rff, $ll, $td);
	my $stf = '<File\\s+RelativePath=\\"([\\.\\\\\\w-]+)+\\"+(.)+';

	prt( "Loading [$in] file in directory [$in_fd] ...\n" ) if ($dbg2);
	if (open FH, "<$in") {
		@lines = <FH>; # slurp the whole file
		close( FH );
	} else {
		prt( "ERROR: Can not open [$in] ...\n" );
	}

	my $fline = '';
	my $version = '';
	my $projname = '';
	my @p_files = ();
	my @p2_files = ();	# as is, from VCPROJ
	my @h_files = ();
	prt( "Processing ".scalar @lines." lines in $in file...\n" ) if ($dbg3);
	my $hadver = 0;
	# get PROJECT NAME - seek -
	# <VisualStudioProject
	#     ProjectType="Visual C++"
	#     Version="8.00"
	#     Name="cpptest"
	#     ProjectGUID="{B5BF7E93-54ED-4353-8D18-8F9BC11E1EDE}"
	#     >
	foreach $line (@lines) {
		$line = trimall($line);
		$ll = length($line);
		if ($ll) {
			$fline .= ' ' if length($fline);
			$fline .= $line;
		}
		if ($fline =~ />/) {
			if ($fline =~ /<VisualStudioProject\s+/) {
				if ($fline =~ /.+Version="(\d+\.{1}\d+)+".+/ ) {
					$version = $1;
				}
				if ($fline =~ /.+Name="(\w+)".+/) {
					$projname = $1;
				}
				##prt( "$fline\n" );
				prt( "Project=$projname, v=$version\n" ) if ($dbg7);
			}
			# <File RelativePath="src\FDM\SP\ACMS.cxx" >
			if ($fline =~ /$stf/) {
				$src = $1;				# actual VCPROJ source
				$rff = $in_fd . $src;	# source, relative to .vcproj folder
				$ff = fix_rel($rff);	# remove relative, if any
				if ($rff =~ /\\\.\.\\/) {
					$td = get_comm_dir( $ff, $rff );
					if (length($td)) {
						if (length($top_dir)) {
							if ((lc($top_dir) ne lc($td)) &&
								( length($td) < length($top_dir)) ) {
								$top_dir = $td;
								prt( "CHANGED TOP DIRECTORY to [$top_dir] ...\n" );
							}
						} else {
							$top_dir = $td;
							prt( "Set TOP DIRECTORY to [$top_dir] ...\n" );
						}
					}
				}
				if (is_c_source($src)) {
					prt("SOURCE=[$src]\n") if ($dbg_src1);
					push(@csrc_array,$ff);
					push(@p_files, $ff);
					push(@p2_files, $src);
				} elsif (is_h_source($src)) {	#if .h, .hpp or .hxx
					prt("HEADER=[$src]\n") if ($dbg_src2);
					push(@hsrc_array,$ff);
					push(@h_files, $ff);	# save HEADER
				} elsif (is_h_special($src)) {	# files with NO extension!!!
					prt("HEADER=[$src]\n") if ($dbg_src2);
					push(@hsrc_array,$ff);
				} else {
					prt("OTHER=[$src]\n") if ($dbg_src3);
					push(@osrc_array,$ff);
				}
			} else {
				#prt( "$fline\n" );
			}
			$fline = '';
		}
	}
	if (@p_files) {
		if (length($projname)) {
			if (defined $projfiles{$projname}) {
				prt( "\nWARNING: DUPLICATE PROJECT NAME $projname in $in ...\n\n" );
			} else {
				$projfiles{$projname} = join('*', @p_files);
				$projhdrs{$projname} = join('*', @h_files);
				$projfilesasis{$projname} = join('*',@p2_files);	# and a list AS IS
				$projvcproj{$projname} = $in;	# and KEEP the project VCPROJ name
				###write2file( join("\n",@h_files)."\n", "temphdrs.txt" );
			}
		} else {
			prt( "\nWARNING: FAILED TO FIND PROJECT NAME in $in ...\n\n" );
		}
	} else {
		prt( "\nWARNING: Got NO C sources from $in ...\n\n" );
	}

	my $new_srcs = scalar @csrc_array - $prev_srcs;
	my $new_hdrs = scalar @hsrc_array - $prev_hdrs;
	my $new_othe = scalar @osrc_array - $prev_othe;
	prt( "Got new $new_srcs C/C++ files, $new_hdrs header files, and others $new_othe\n" ) if ($dbg4);
	prt( "Got ".scalar @csrc_array." C/C++ files, ".scalar @hsrc_array. " header files" ) if ($dbg4);
	if (@osrc_array) {
		prt( " and ".scalar @osrc_array." other files" ) if ($dbg4);
	}
	prt("\n") if ($dbg4);
}


sub cmp_c_sources {
	my ($f1, $f2);
	my $fnd = 0;
	my $ft = '';
	prt( "\nComparing C/C++ sources ...\n" );
	prt( "\nFinding ".scalar @csrc_array." from \@csrc_array, in ".scalar @cdir_array." of \@cdir_array...\n");
	$cnt = 0;
	foreach $f1 (@csrc_array) {
		$fnd = 0;
		foreach $f2 (@cdir_array) {
			if ( lc(file_name($f1)) eq lc(file_name($f2)) ) {
				$fnd = 1;
				last;
			}
		}
		if ($fnd == 0) {
			$ft = file_name($f1);
			prt( "NOT FOUND $ft [$f1]\n" );
			push(@not_found, $f1);
			$cnt++;
		}
	}
	if ($cnt) {
		prt( "MISSED $cnt - It appears these need to be DELETED from the PROJECT file ...\n" );
	}

	prt( "\nFinding ".scalar @cdir_array." from \@cdir_array, in ".scalar @csrc_array." of \@csrc_array...\n");
	$cnt = 0;
	foreach $f1 (@cdir_array) {
		$fnd = 0;
		foreach $f2 (@csrc_array) {
			if ( lc(file_name($f1)) eq lc(file_name($f2)) ) {
				$fnd = 1;
				last;
			}
		}
		if ($fnd == 0) {
			$ft = file_name($f1);
			prt( "NOT FOUND $ft [$f1]\n" );
			push(@not_found, $f1);
			$cnt++;
		}
	}
	if ($cnt) {
		prt( "MISSED $cnt - It appears these need to be ADDED to the PROJECT file ...\n" );
	}

	if (@not_found) {
		prt( "\nCHECK this list of ".scalar @not_found." files carefully ...\n" );
	}
}

sub cmp_am_sources {
	my ($f1, $f2);
	my $fnd = 0;
	my $ft = '';
	prt( "\nComparing C/C++ sources from AM files ...\n" );
	prt( "\nFinding ".scalar @csrc_array." from \@csrc_array, in ".scalar @am_sources." of \@am_sources...\n");
	$cnt = 0;
	foreach $f1 (@csrc_array) {
		$fnd = 0;
		foreach $f2 (@am_sources) {
			if ( lc(file_name($f1)) eq lc(file_name($f2)) ) {
				$fnd = 1;
				last;
			}
		}
		if ($fnd == 0) {
			$ft = file_name($f1);
			prt( "NOT FOUND $ft [$f1] DELETE?\n" );
			push(@not_found2, $f1);
			$cnt++;
		}
	}
	if ($cnt) {
		prt( "MISSED $cnt - It appears these need to be DELETED from the PROJECT file ...\n" );
	}

	prt( "\nFinding ".scalar @am_sources." from \@am_sources, in ".scalar @csrc_array." of \@csrc_array...\n");
	$cnt = 0;
	foreach $f1 (@am_sources) {
		$fnd = 0;
		foreach $f2 (@csrc_array) {
			if ( lc(file_name($f1)) eq lc(file_name($f2)) ) {
				$fnd = 1;
				last;
			}
		}
		if ($fnd == 0) {
			$ft = file_name($f1);
			prt( "NOT FOUND $ft [$f1] ADD?\n" );
			push(@not_found2, $f1);
			$cnt++;
		}
	}
	if ($cnt) {
		prt( "MISSED $cnt - It appears these need to be ADDED to the PROJECT file ...\n" );
	}

	if (@not_found2) {
		prt( "\nCHECK this list of ".scalar @not_found2." files carefully ...\n" );
	}
}

### utitlity subs
sub is_c_source {
	my ($f) = shift;
	if ( ($f =~ /\.c$/i) || ($f =~ /\.cpp$/i) || ($f =~ /\.cxx$/i) ||
		 ($f =~ /\.inl$/i) || ($f =~ /\.cc$/i) ) {
		return 1;
	}
	return 0;
}

sub is_h_special {
	my ($f) = shift;
	if (($f =~ /osg/i)||($f =~ /OpenThreads/i)||($f =~ /Producer/i)) {
		return 1;
	}
	return 0;
}

sub is_h_source {
	my ($f) = shift;
	if ( ($f =~ /\.h$/i) || ($f =~ /\.hpp$/i) || ($f =~ /\.hxx$/i) ) {
		return 1;
	}
	return 0;
}

sub is_dsw_file {
	my ($f) = shift;
	if ( ($f =~ /\.dsw$/i) || ($f =~ /\.dsp$/i) ) {
		return 1;
	}
	return 0;
}

sub is_sln_file {
	my ($f) = shift;
	if ( ($f =~ /\.sln$/i) || ($f =~ /\.vcproj$/i) ) {
		return 1;
	}
	return 0;
}

sub is_ch_source {
	my ($f) = shift;
	if (is_c_source($f) || is_h_source($f)) {
		return 1;
	}
	return 0;
}

sub is_my_type {
	my ($f) = shift;
	if (is_c_source($f)) {
		return $TYPE_C;
	} elsif (is_h_source($f)) {
		return $TYPE_H;
	} elsif (is_dsw_file($f)) {
		return $TYPE_DSW;
	} elsif (is_sln_file($f)) {
		return $TYPE_SLN;
	}
	return 0;
}

sub is_vcproj {
	my $fil = shift;
	if ($fil =~ /\.vcproj$/i) {
		return 1;
	}
	return 0;
}

sub is_solution {
	my $fil = shift;
	if ($fil =~ /\.sln$/i) {
		return 1;
	}
	return 0;
}

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

# fix relative directory - fix relative path - path fix
# Remove any DOT or DOUBLE DOT from the PATH
sub fix_rel {
	my ($path) = shift;
	$path = unix_2_dos($path);	# ensure DOS separator
	my @a = split(/\\/, $path);	# split on DOS separator
	my $npath = '';
	my $wmsg = '';
	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 {
				$wmsg = "WARNING: Got relative .. without previous!!! [$path]";
				prt( "$wmsg\n" );
				push(@warnings,$wmsg);
			}
		} else {
			push(@na,$p);
		}
	}
	foreach my $pt (@na) {
		$npath .= "\\" if length($npath);
		$npath .= $pt;
	}
	return $npath;
}

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

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);
	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 {
		prt( "WARNING: Did NOT split! [$ife]\n" );
	}
	return "UDETERMINED";
}

sub known_ext {
	my ($fil) = shift;
	if ($fil =~ /\.def$/i) {
		return 1;
	} elsif ($fil =~ /\.rc$/i) {
		return 2;
	} elsif ($fil =~ /\.bmp$/i) {
		return 3;
	} elsif ($fil =~ /\.ico$/i) {
		return 4;
	} elsif ($fil =~ /\.cur$/i) {
		return 5;
	} elsif ($fil =~ /\.txt$/i) {
		return 6;
	} elsif ($fil =~ /\.inp$/i) {
		return 7;
	} elsif ($fil =~ /\.cnt$/i) {
		return 8;
	} elsif ($fil =~ /\.rtf$/i) {
		return 9;
	} elsif ($fil =~ /\.dll$/i) {
		return 10;
	} elsif ($fil =~ /\.hpj$/i) {
		return 11;
	}
	return 0;
}

# load a DSP file sources
sub load_dsp {
	my ($prj, $f) = @_;
	my @dlns = ();
	my $lncnt = 0;
	my @dsrcs = ();
	my @dhdrs = ();
	my @dothers = ();
	my @rarr = ();
	if (open FH, "<$f") {
		@dlns = <FH>;
		close FH;
		$lncnt = scalar @dlns;
		prt( "File $f contains $lncnt lines ...\n" ) if ($dbg11);
	} else {
		prt( "WARNING: FAILED to OPEN [$f] ... $! ...\n" );
	}
	my $intarg = 0;
	my @arr = ();
	my $intrue = 0;
	my $inanif = 0;
	my $msg = '';
	my $package = '';
	my ($dsp_name, $dsp_dir) = fileparse( $f );
	%macros = ();	# clear the DSP macro set
	foreach my $line (@dlns) {
		chomp $line;
		$line = trim_tail($line);
		# # TARGTYPE "Win32 (x86) Console Application" 0x0103
	    if ( $line =~ /$COMMENT_PATTERN/ ) {
			# starts with '#'
			$line = substr($line,1);
			if ($line =~ /^\s+TARGTYPE\s+"(.*)"\s+/) {
				prt( "$package TARGET: $1\n" ) if ($dbg11);
			} 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"
				prt( "Begin Group: $1\n" ) if ($dbg10);
			} elsif ($line =~ /\s+Microsoft\s+Developer\s+Studio\s+Project\s+File\s-\sName="(\w+)"\s+/ ) {
				$package = $1;
			}
        } elsif ($line =~ /^!/ ) {
			# starts with '!'
			$line = substr($line,1);
			if ($line =~ /^IF\s+(.*)/ ) {
				$msg = "Entered IF [$1] ";
				$msg .= do_if_split($1);
				$inanif++;
				prt( "$msg $inanif\n" ) if ($dbg8);
			} elsif ($line =~ /^ELSEIF\s+(.*)/ ) {
				$msg = "Entered ELSEIF [$1] ";
				$msg .= do_if_split($1);
				prt( "$msg $inanif\n" ) if ($dbg8);
			} elsif ($line =~ /^ELSE\s*/ ) {
				prt( "Entered ELSE [$line]\n" ) if ($dbg8);
			} elsif ($line =~ /^ENDIF\s*/ ) {
				prt( "Out IF with ENDIF\n" ) if ($dbg8);
				$inanif = 0;
			} elsif ($line =~ /^MESSAGE\s*/ ) {
				#prt( "MESSAGE LINE ...\n" );
			} else {
				prt( "WARNING: What is THIS [$line]???\n" );
			}
		} 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);
				} elsif ( ($line =~ /\.hxx$/i) || ($line =~ /\.h$/i) || ($line =~ /\.hpp$/i) ) {
					push(@dhdrs, $ff);
				} elsif ( known_ext( $line ) ) {
					push(@dothers, $ff);
				} else {
					prt( "CHECK DSP Discarded $line\n" );
				}
			}
		} else {
			# NOT in Begin Target yet
			if ($line =~ /$MACRO_PATTERN2/) {
				if (defined $macros{$1}) {
					if ($macros{$1} ne $2) {
						prt( "WARNING: Duplicated MACRO $1, now $2, was $macros{$1} ...\n" );
					}
				} else {
					$macros{$1} = $2;
					prt( "SET: MACRO $1, to $2 ...\n" ) if ($dbg9);
				}
			}
		}
	}
	$lncnt = scalar @dsrcs;
	prt( "File $f contains $lncnt SOURCES ...\n" ) if ($dbg11);
	push(@rarr, [join('*',@dsrcs), join('*',@dhdrs), join('*',@dothers)]);
	return @rarr;
}

# given say - 
# absolute path = C:\FG\FGCOM\xmlrpc-c\lib\abyss\src\file.c, and
# relative path = C:\FG\FGCOM\xmlrpc-c\Windows\..\lib\abyss\src\file.c
sub get_comm_dir {
	my ($ap, $rp) = @_;
	my $i = 0;
	$ap = unix_2_dos($ap);
	$rp = unix_2_dos($rp);
	my $max = length($ap);
	my $lrp = length($rp);
	$max = $lrp if ($lrp < $max);
	while( lc(substr($ap,$i,1)) eq lc(substr($rp,$i,1)) ) {
		$i++;
	}
	### NO, keep trailing '\'$i-- if ($i);	# back up one
	return substr($ap,0,$i);
}

# exclude the ROOT FOLDER,
# if there is a $root_dir,
# and this file BEGINS with that root!
sub sub_root {
	my ($fil) = shift;
	my $lr = length($root_dir);
	my $lf = length($fil);
	if ($lr && ($lr < $lf)) {
		my $off = 0;
		my $dfil = unix_2_dos($fil);
		my $droot = unix_2_dos($root_dir);
		while ( substr($dfil,$off,1) eq substr($droot,$off,1) ) {
			$off++;
		}
		$fil = substr($fil,$off);
	}
	return $fil;
}

# eof - vc8srcs03.pl

