#!/perl -w
# NAME: cmp2dsps.pl
# AIM: Comparison of TWO DSP files - show different SOURCE files only ...
# 13/08/2008 geoff mclane http://geoffair.net/mperl
# Only compares SOURCE lists. Still to compare config parameters ...
use strict;
use warnings;
use File::Basename;
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";
# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
	my @tmpsp = split(/\\/,$pgmname);
	$pgmname = $tmpsp[-1];
}
my $outfile = "temp.$pgmname.txt";
open_log($outfile);
prt( "$0 ... Hello, World ...\n" );

my $minfi1 = '..\include\xmlrpc-c\server_w32httpsys.h ';
my $minfi2 = 'C:\FG\FGCOMXML\xmlrpc-c\include\xmlrpc-c\server_w32httpsys.h ';
my $minlen1 = length($minfi1);
my $minlen2 = length($minfi2);

my $compconfig = 0; # not completed - compare config stuff

my $dbg_dsp = 0;
my $dbg_ds1 = 0;
my $dbg_ds2 = 0;
my $dbg_ds3 = 0;
my $dbg_ds4 = 0;	# show CPP and LINK config items - %v6_conf
my $dbg_ds5 = 0;	# show Defined items - %v6_defs
my $dbg_ds6 = 0;	# show !IF, !ELSEIF switching
my $dbg_ds7 = 0;	# show %v6_conf listing
my $big_dbg = 0;
my $dbg_ds8 = 0;	# show VC6 Filter and Group Name
my $dbg_ds9 = 0;	# show EACH VC6 source pushed

my @v6_srcs = ();  # relnm full group filter
my %v6_defs = ();
my %v6_conf = ();

# file 1 stuff
my @v6_srcs1 = ();  # relnm full group filter
my %v6_defs1 = ();
my %v6_conf1 = ();

# file 2 stuff
my @v6_srcs2 = ();  # relnm full group filter
my %v6_defs2 = ();
my %v6_conf2 = ();

my $in_file1 = 'temp.#Win32.libtar.dsp';
my $in_file2 = 'C:\Projects\tar120\Win32\libtar.dsp';
my $root_dir = "C:\\Projects\\tar120\\Win32\\";
my $projname = 'libtar';

###my $in_file1 = 'temp.xmlrpc.dsp';
###my $in_file2 = 'C:\FG\FGCOMXML\xmlrpc-c\Windows\xmlrpc.dsp';
###my $root_dir = "C:\\FG\\FGCOMXML\\xmlrpc-c\\Windows\\";
###my $projname = 'xmlrpc';

my @lines1 = ();
my @lines2 = ();
my $lncnt1 = 0;
my $lncnt2 = 0;
if (open INF1, "<$in_file1") {
	@lines1 = <INF1>;
	close INF1;
	$lncnt1 = scalar @lines1;
}
if (open INF2, "<$in_file2") {
	@lines2 = <INF2>;
	close INF2;
	$lncnt2 = scalar @lines2;
}
if ($lncnt1 && $lncnt2) {
	prt( "Comparing $lncnt1, with $lncnt2 ...\n" );
}

# load FILE 1
clear_load();
process_DSP( $projname, $in_file1 );
@v6_srcs1 = @v6_srcs;  # relnm full group filter
%v6_defs1 = %v6_defs;
%v6_conf1 = %v6_conf;

# load FILE 2
clear_load();
process_DSP( $projname, $in_file2 );
@v6_srcs2 = @v6_srcs;  # relnm full group filter
%v6_defs2 = %v6_defs;
%v6_conf2 = %v6_conf;

# compare SOURCES
compare_dsp_with_dsp($in_file1, $in_file2);
prt("\n");

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

sub clear_load {
	@v6_srcs = ();  # relnm full group filter
	%v6_defs = ();
	%v6_conf = ();
}

sub process_DSP {
	my ($proj, $fil) = @_;
	my ($line, $prjname, $grpname, $filter, $ff, $src, $chr, $tmp, $conf, $ffnr);
	my ($itm1, $itm2, $key, $targtype);
	my $scnt = 0;
	my @dsp_lines = ();
	prt( "\nProcess DSP file $fil ... " );
	if ( -f $fil ) {
		prt( "already exists ...\n" );
		my ($name,$dir,$suffix) = fileparse($fil);
		$dir = trim_all($dir);
		$dir = $root_dir if ((length($dir) == 0)||($dir eq ".\\"));
		if ( open IF, "<$fil" ) {
			@dsp_lines = <IF>;
			close IF;
			prt( "Got ".scalar @dsp_lines." lines to process ...\n" );
			my $ins = 0;
			foreach $line (@dsp_lines) {
				$line = trim_all($line);
				next if (length($line) == 0);
				$chr = substr($line, 0, 1);	# get FIRST char of LINE
				if( $chr eq '#' ) {
					# line begins with SHARP
					if ($line =~ /\s+Microsoft Developer Studio Project File - Name=\"([\.\w\s]+)+/) {
						$prjname = $1;
						prt( "Project NAME = $prjname ...\n" ) if ($dbg_dsp);
					} elsif ($line =~ /\s+Microsoft Developer Studio Generated Build File, Format Version ([\d\.]+)/) {
						prt( "MSVC Version $1 ...\n" ) if ($dbg_dsp);
					} elsif ($line =~ /\s*TARGTYPE\s+\"(.+)\"\s+/) { # Win32 (x86) Console Application" 0x0103
						$targtype = $1;
						prt( "TARGTYPE: $targtype, name=$prjname ($proj)\n" );
					} elsif ($line =~ /Begin Group \"([\s\w]+)+/ ) {
						$grpname = $1;
						prt( "Begin group ... $grpname\n" ) if ($dbg_dsp);
					} elsif ($line =~ /PROP\s+Default_Filter\s+\"([\w;]+)+\"/ ) {
						$filter = $1;
						prt( "Begin Filter group $grpname, filter $filter\n" ) if ($dbg_ds8);
					} elsif ($line =~ /Begin Source File/) {
						$ins = 1;
						prt( "Begin source ... $ins ...\n" ) if ($dbg_dsp);
					} elsif ($line =~ /End Source File/) {
						$ins = 0;
						prt( "End source ... $ins ...\n" ) if ($dbg_dsp);
					} elsif ( $line =~ /ADD BASE CPP (.+)/ )  {
						# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c
						$key = 'CPP '.$conf;
						prt( "ADD BASE $key [$1]\n" ) if ($dbg_ds4);
						$v6_conf{$key} = $1;
					} elsif ( $line =~ /ADD CPP (.+)/ )  {
						# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /FD /c
						$key = 'CPP '.$conf;
						prt( "ADD $key [$1]\n" ) if ($dbg_ds4);
						$v6_conf{$key} = $1;
					} elsif ( $line =~ /ADD BASE LINK32 (.+)/ )  {
						# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
						$key = 'LINK '.$conf;
						prt( "ADD BASE $key [$1]\n" ) if ($dbg_ds4);
						$v6_conf{$key} = $1;
					} elsif ( $line =~ /ADD LINK32 (.+)/ )  {
						$key = 'LINK '.$conf;
						# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
						prt( "ADD $key [$1]\n" ) if ($dbg_ds4);
						$v6_conf{$key} = $1;
					}
				} elsif ( $chr eq '!' ) {
					if ($line =~ /^!IF\s+(.+)/i ) {
						prt( "Entering IF $1 ...\n" ) if ($dbg_ds6);
						if ($line =~ /!IF\s+(.+) == (.+)/i) {
							$itm1 = $1;
							$itm2 = $2;
							$itm1 =~ s/\"//g;
							$itm2 =~ s/\"//g;
							prt( "Got [$itm1] [$itm2]...\n" ) if ($dbg_ds6);
							if ($itm1 =~ /^\$\((.+)\)/ ) {
								if (defined $v6_defs{$1}) {
									if ($v6_defs{$1} eq $itm2) {
										$tmp = "TRUE";
									} else {
										$tmp = "FALSE";
									}
									if( $1 eq 'CFG' ) {
										$conf = $itm2;
									}
									prt( "and [$1] defined as \"$v6_defs{$1}\" ... $tmp $conf\n" ) if ($dbg_ds6);
								} else {
									prt( "NOT DEFINED [$1] ...\n" );
								}
							}
						} else {
							prt( "FAILED IF == [$line]\n" );
						}
					} elsif ( $line =~ /^!ELSE\s+/i ) {
						prt( "Entering ELSE ...\n" ) if ($dbg_ds6);
					} elsif ( $line =~ /^!ELSEIF\s+(.+)/i ) {
						prt( "Entering ELSEIF $1 ...\n" ) if ($dbg_ds6);
						if ($line =~ /!ELSEIF\s+(.+) == (.+)/i) {
							$itm1 = $1;
							$itm2 = $2;
							$itm1 =~ s/\"//g;
							$itm2 =~ s/\"//g;
							prt( "Got [$itm1] [$itm2]...\n" ) if ($dbg_ds6);
							if ($itm1 =~ /^\$\((.+)\)/ ) {
								if (defined $v6_defs{$1}) {
									if ($v6_defs{$1} eq $itm2) {
										$tmp = "TRUE";
									} else {
										$tmp = "FALSE";
									}
									if( $1 eq 'CFG' ) {
										$conf = $itm2;
									}
									prt( "and [$1] defined as \"$v6_defs{$1}\" ... $tmp $conf\n" ) if ($dbg_ds6);
								} else {
									prt( "NOT DEFINED [$1] ...\n" );
								}
							}
						} else {
							prt( "FAILED ELSEIF == [$line]\n" );
						}
					} elsif ( $line =~ /^!ENDIF\s*/i ) {
						prt( "Out ENDIF ...\n" ) if ($dbg_ds6);
					}
				} else {
					if ($line =~ /SOURCE=([\.\\\w-]+)+/ ) {
						$src = $1;
						$ff = $dir.$src;
						$ffnr = fix_rel_path($ff);
						###prt( "fixed=$ffnr, src=[$src], dir=[$dir]\n" );
						if ($ins) {
							prt( "SOURCE = $src ($ff)... $ins \n" ) if ($dbg_dsp);
						} else {
							prt( "source = $src ($ff) OUTSIDE 'in source' ... $ins\n" );
						}
						if (is_c_source($src)) {
							prt("SOURCE=[$src]\n") if ($dbg_ds1);
						} elsif (is_h_source($src)) {
							prt("HEADER=[$src]\n") if ($dbg_ds2);
						} elsif (is_h_special($src)) {
							prt("HEADER=[$src]\n") if ($dbg_ds2);
						} else {
							prt("OTHER=[$src]\n") if ($dbg_ds3);
						}
						#                0     1      2          3          4  5  6
						#push(@v8_srcs, [$src, $ffnr, $filtname, $filttype, 0, 0, $projname] );
						push(@v6_srcs, [$src, $ffnr, $grpname, $filter, 0, 0, $proj] );
						$scnt++;
						prt( "$scnt v6_srcs: $src, $ffnr, $grpname, $filter, 0, 0, $proj\n" ) if ($dbg_ds9);
					} elsif ( $line =~ /(.*)=(.*)/ ) {
						$itm1 = $1;
						$itm2 = $2;
						$v6_defs{$itm1} = $itm2;
						prt( "Defined [$itm1] = [$itm2] ...\n" ) if ($dbg_ds5);
					} else {
						prt( "skipped [$line]\n" );
					}
				}
			}
			prt( "End DSP - got $scnt source files ...\n" );
			if ($dbg_ds7) {
				foreach $key (keys %v6_conf) {
					prt( "Config $key = [$v6_conf{$key}]\n" );
				}
			}
		} else {
			prt( "ERROR: FAILED TO OPEN $fil FILE!\n" );
		}
	} else {
		prt( "*** DOES NOT EXIST ***\n" );
	}
}

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

sub fix_rel_path {
	my ($path) = shift;
	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!!! path=$path\n" );
			}
		} else {
			push(@na,$p);
		}
	}
	foreach my $pt (@na) {
		$npath .= "\\" if length($npath);
		$npath .= $pt;
	}
	return $npath;
}
### utitlity subs
sub is_c_source {
	my $f = shift;
	if ( ($f =~ /\.c$/i) || ($f =~ /\.cpp$/i) || ($f =~ /\.cxx$/i) ) {
		return 1;
	}
	##if (!is_h_source($f)) {
	##	prt( "Item [$f] IS NOT C/C++ SOURCE!\n" );
	##}
	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;
	}
	##if (!is_c_source($f)) {
	##	prt( "Item [$f] IS NOT C/C++ SOURCE!\n" );
	##}
	return 0;
}

sub compare_dsp_with_dsp {
	my ($file1,$file2) = @_;
	my ($i1, $i2, $fnd, $matched);
	my ($src1, $ff1, $fnm1, $typ1, $flg1);
	my ($src2, $ff2, $fnm2, $typ2, $flg2);
	my ($nm1, $pth1, $suf1);
	my ($nm2, $pth2, $suf2);
	my ($added, $subed, $miss1, $miss2, $min);
	my $msg = '';
    # compare SOURCES
    ###############################
	my $v6_tot1 = scalar @v6_srcs1;
	my $v6_tot2 = scalar @v6_srcs2;
	prt( "\nCompare of sources MSVC6 totals $v6_tot1, with $v6_tot2 ...\n" ); 
	$fnd = 0;
	$matched = 0;
	for ($i1 = 0; $i1 < $v6_tot1; $i1++) {
		$v6_srcs1[$i1][4] = 0;  # clear matched
	}
	for ($i2 = 0; $i2 < $v6_tot2; $i2++) {
		$v6_srcs2[$i2][4] = 0;  # clear matched
	}

    # for EACH in file 1
	for ($i1 = 0; $i1 < $v6_tot1; $i1++) {
		$src1 = $v6_srcs1[$i1][0];
		$ff1  = $v6_srcs1[$i1][1];
		$fnm1 = $v6_srcs1[$i1][2];
		$typ1 = $v6_srcs1[$i1][3];
		$flg1 = $v6_srcs1[$i1][4];
		$fnd = 0;
        # find in file2
		for ($i2 = 0; $i2 < $v6_tot2; $i2++) {
			$src2 = $v6_srcs2[$i2][0];
			$ff2  = $v6_srcs2[$i2][1];
			$fnm2 = $v6_srcs2[$i2][2];
			$typ2 = $v6_srcs2[$i2][3];
			$flg2 = $v6_srcs2[$i2][4];
			if (is_same_file($ff1,$ff2)) {
				$v6_srcs2[$i1][4] = $i1 + 1;
				$v6_srcs1[$i1][4] = $i2 + 1;
				$fnd = 1;
				last;
			}
		}
		if ($fnd) {
			$matched++;
		} else {
			#prt( "NO MATCH\n" ) if ($big_dbg);
		}
	}
	$added = $v6_tot1 - $matched;
	$subed = $v6_tot2 - $matched;
	prt( "In the compare of $file1, with $file2 ...\n" );
	prt( "Matched $matched of $v6_tot1, with $matched of $v6_tot2 ...\n" );

    # get sources NOT matched in file1
	$miss1 = 0;
	for ($i1 = 0; $i1 < $v6_tot1; $i1++) {
		if ($v6_srcs1[$i1][4] == 0) {
			$miss1++;
		}
	}
	if ($miss1) {
		prt( "\n$file1 contains $miss1, NOT in $file2 ...\n" );
		for ($i1 = 0; $i1 < $v6_tot1; $i1++) {
			if ($v6_srcs1[$i1][4] == 0) {
				$src1 = $v6_srcs1[$i1][0];
				$ff1  = $v6_srcs1[$i1][1];
				$msg = "$src1 ";
				$min = $minlen1;
				$msg .= ' ' while (length($msg) < $min);
				$msg .= "($ff1) ";
				$min += 3 + $minlen2;
				$msg .= ' ' while (length($msg) < $min);
				if (-f $ff1) {
					$msg .= "ok";
				} else {
					$msg .= "MISSING?";
				}
				prt( "$msg\n" );
			}
		}
	}

    # get sources NOT matched in file2
	$miss2 = 0;
	for ($i2 = 0; $i2 < $v6_tot2; $i2++) {
		if ($v6_srcs2[$i2][4] == 0) {
			$miss2++;
		}
	}
	if ($miss2) {
		prt( "\n$file2 contains $miss2, NOT in $file1 ...\n" );
		for ($i2 = 0; $i2 < $v6_tot2; $i2++) {
			if ($v6_srcs2[$i2][4] == 0) {
				$src2 = $v6_srcs2[$i2][0];
				$ff2  = $v6_srcs2[$i2][1];
				$msg = "$src2 ";
				$min = $minlen1;
				$msg .= ' ' while (length($msg) < $min);
				$msg .= "($ff2) ";
				$min += 3 + $minlen2;
				$msg .= ' ' while (length($msg) < $min);
				if (-f $ff2) {
					$msg .= "ok";
				} else {
					$msg .= "MISSING?";
				}
				prt( "$msg\n" );
			}
		}
	}
	prt("\n");
    if ($compconfig) {
        # also should COMPARE
        # %v6_defs1 with %v6_defs2, and
        # %v6_conf1 with %v6_conf2
        foreach $src1 (keys %v6_defs1) {
            $msg = $v6_defs1{$src1};
            prt( "$src1 = $msg\n" );
        }
        foreach $src1 (keys %v6_defs2) {
            $msg = $v6_defs2{$src1};
            prt( "$src1 = $msg\n" );
        }

        foreach $src1 (keys %v6_conf1) {
            $msg = $v6_conf1{$src1};
            prt( "$src1 = $msg\n" );
        }
        foreach $src1 (keys %v6_conf2) {
            $msg = $v6_conf2{$src1};
            prt( "$src1 = $msg\n" );
        }
    }
	return ($added + $subed);	# return CHANGE counter
}


sub is_same_file {
	my ($f1, $f2) = @_;
	my $len = length($f1);
    my $len2 = length($f2);
	if ($len != $len2) {
        ###prt( "NOT SAME LENGTH [$f1]($len) vs [$f2]($len2)\n" );
		return 0;	# not the SAME
	}
	$f1 =~ s/\//\\/g;
	$f2 =~ s/\//\\/g;
	my $lcf1 = lc($f1);
	my $lcf2 = lc($f2);
	my $i = 0;
    my $ch1 = '';
    my $ch2 = '';
	while ($i < $len) {
        $ch1 = substr($lcf1,$i,1);
        $ch2 = substr($lcf2,$i,1);
		if ($ch1 ne $ch2) {
            ###prt( "FAILED at char ".($i + 1)." $ch1 vs $ch2 [$f1]($len) & [$f2]($len2)\n" );
			return 0;
		}
		$i++;
	}
	return 1;   # they ARE the SAME
}

# extracted from utils.pl
sub unix_2_dos {
	my ($f) = shift;
	$f =~ s/\//\\/g;
	return $f;
}

sub sub_common_folder {
	my ($f1, $f2) = @_;
	my $off = 0;
	my $df1 = lc(unix_2_dos($f1));
	my $df2 = lc(unix_2_dos($f2));
	while ( substr($df1,$off,1) && substr($df2,$off,1) &&
			( substr($df1,$off,1) eq substr($df2,$off,1) ) ) {
		$off++;
	}
	return substr($f1,$off);
}

# eof - cmp2dsps.pl
