#!/Perl -w
# adjrt02.pl - geoff mclane
# AIM: Given a SOLUTION (SLN) file, extract all projects
# Check and report RUNTIME setting - differentiate Debug/Release / static or not
# IFF $write_changes set, rename the file to .BAK, and write new vcproj file,
# else, just REPORT what would be changed if it was ON.
# OTHER USER OPTIONS
# IFF $UseMD is set, will adjust to /MD[d], else /MT[d]
# IFF $FixIgnore is set, will remove any IGNORE libraries
# IFF $FixStatic is set, will ALSO adjust those configuration that contain the word STATIC
# IFF $FixPrep1 is set, will ENSURE _CRT_SECURE_NO_DEPRECATE is in Preprocessor defines
#
# Contents of sample VCPROJ file
#	<Configurations>
#		<Configuration
#			Name="Debug|Win32"
# ...
#			ATLMinimizesCRunTimeLibraryUsage="false"
#			>
# ...
# 			<Tool
#				Name="VCCLCompilerTool"
#				Optimization="0"
#				AdditionalIncludeDirectories="..\vc2005;..\zlib;..\png;..\jpeg;.."
#				PreprocessorDefinitions="_CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_WINDOWS;WIN32_LEAN_AND_MEAN;VC_EXTRA_LEAN;WIN32_EXTRA_LEAN"
#				RuntimeLibrary="3"
# ...
#			/>
#		</Configuration>
#		<Configuration
#			Name="Release|Win32"
# ...
require 'logfile.pl' or die "Unable to load logfile.pl ...\n";

my $def_dir = 'C:\FG0910-5\OpenSceneGraph\VisualStudio\OpenSceneGraph.sln';
###my $def_dir = 'C:\FG\FG0910-7\OpenSceneGraph\VisualStudio\OpenSceneGraph.sln';
###my $def_dir = 'C:\FG0910-6\FlightGear\projects\VC8\FlightGear.sln';
my $write_change = 0; # set to 1 to WRITE changes ...
my $UseMD = 1;	# set 1 to use /MD and /MDd ...
my $FixIgnore = 1; # set to 1 to remove IGNORE libraries
my $FixPrep1 = 1;  # set to 1 to ensure $prep1 (_CRT_SECURE_NO_DEPRECATE) is in preprocessor defines
my $FixStatic = 1;	# set to ALSO change STATIC RUNTIME

my %runtimes = ( 'MT' => 0, 'MTd' => 1, 'MD' => 2, 'MDd' => 3 );
# desired (default) RUNTIME
my $RelRT = $runtimes{'MT'};	# '0';
my $DebRT = $runtimes{'MTd'};	# '1';
if ($UseMD) {
	$RelRT = $runtimes{'MD'};	# '2';
	$DebRT = $runtimes{'MDd'};	# '3';
}
my $prep1 = '_CRT_SECURE_NO_DEPRECATE';

my $dbg1 = 0;
my $dbg2 = 0;
my $dbg3 = 0;
my $dbg4 = 0;		# show RUNTIME change
my $dbgrt = 0;		# show NO RUNTIME change
my $dbg5 = 0;
my $dbgvers = 0;	# show configuration name="..."
my $dbgprep = 0;	# show FULL PREPROCESSOR define ...
my $dbgprep1 = 0;	# show NO CHANGE in PREPROCESSOR define ...
my $dbgstatic = 1;	# show NO RT FIX due $FixStatic is OFF
my $dbgign = 1;		# show change in IGNORE libraries
my $got_file = 1;
my $got_sln = 1;
my @sol_set = ();
my $sln_file = '';
my @proj_files = (); # hold the VCPROJ files to process
my $prj_cnt = 0;
my @warnings = ();
my $proc_cnt = 0;
my @new_files = ();
my $wouldchg1 = 0;	# would change except $FixStatic OPTION is OFF
my $wouldchg2 = 0;	# would change except $FixPrep1 OPTION is OFF
my $wouldchg3 = 0;	# would change except $FixIgnore OPTION is OFF
my $wouldwrite = 0;	# would write except $write_change OPTION is OFF

my $in_dir = '';
if (@ARGV) {
	$in_dir = $ARGV[0];
} else {
	$in_dir = $def_dir;
}
my $root_dir = '';

# log file stuff
my ($LF);
my $outfile = 'temp.'.$0.'.txt';
open_log($outfile);
prt( "$0 ... Hello, World ...\n" );

if ( -d $in_dir) {
	prt( "Using in directory of [$in_dir] ...\n" );
	$root_dir = $in_dir;
	$got_file = 0;
	$got_sln = 0;
} elsif ( -f $in_dir) {
	if (is_solution($in_dir)) {
		$got_sln = 1;
		prt( "Using SOLUTION in file of\n[$in_dir] ...\n" );
		$sln_file = $in_dir;
	} elsif (is_vcproj($in_dir)) {
		prt( "Using VCPROJ in file of\n[$in_dir] ...\n" );
		$got_sln = 0;
	} else {
		mydie( "ERROR: File is NOT solution or vcproj file - \n[$in_dir] ...\n" );
	}
} else {
	mydie( "ERROR: Can not locate file or folder \n[$in_dir] ...\n" );
}

if ($got_file) {

} else {
	# got a folder
	@sol_set = find_solution( $in_dir );
	my $sol_cnt = scalar @sol_set; 
	if ($sol_cnt == 1) {
		$sln_file = pop @sol_set;
		prt( "Got solution file [$sln_file] ...\n" );
		$got_file = 1;
		$got_sln = 1;
	} else {
		prt( "Got $sol_cnt solutions files ... Choose and enter one name only from ...\n" );
		$sol_cnt = 0;
		foreach $sln_file (@sol_set) {
			$sol_cnt++;
			prt( "$sol_cnt $sln_file ...\n" );
		}
		mydir( "Enter one of above file names ...\n" );
	}
}

if ($got_sln && ( -f $sln_file )) {
	get_proj_files($sln_file);
	$prj_cnt = scalar @proj_files;
	if ($prj_cnt) {
		prt( "Found $prj_cnt project files ...\n" );
	} else {
		mydir( "WARNING: Found NO project files ...\n" );
	}
}
if ($prj_cnt) {
	prt( "Processing $prj_cnt project files ...\n" );
	foreach my $file (@proj_files) {
		$proc_cnt++;
		process_proj_file($file);
	}
}

prt( "\nAll done - summary ...\n" );
my $cnt = scalar @new_files;
if ($wouldchg1) {
	prt( "Note: Would change $wouldchg1 lines, except \$FixStatic OPTION is OFF!\n" );
}
if ($wouldchg2) {
	prt( "Note: Would change $wouldchg2 lines, except \$FixPrep1 OPTION is OFF!\n" );
}
if ($wouldchg3) {
	prt( "Note: Would change $wouldchg3 lines, except \$FixIgnore OPTION is OFF!\n" );
}
if ($cnt > 0) {
	prt("Written $cnt new files ...\n");
	prt( join("\n", @new_files ) );
	prt( "\n" );
} else {
	if ($wouldwrite) {
		prt( "Note: Would write $wouldwrite, except \$write_change OPTION is OFF!\n" );
	} else {
		prt("Written NO new files ... None to write with present options ...\n");
	}
}

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

sub process_proj_file {
	my ($file) = shift;
	if ( ! -f $file) {
		push(@warnings, "WARNING: Unable to open \n[$file] ...\n");
		return;
	}
	my ($in_cfgs, $in_cfg, $in_tool, $aline, $ln, $pl2, $had_nm, $vers, $in_cfg2, $rt, $tn);
	my ($chged, $pline);
	open IF, "<$file";
	my @plines = <IF>;
	close(IF);
	prt( "\n$proc_cnt: Processing ".scalar @plines. " lines from\n[$file]...\n" );
	$in_cfgs = 0;
	$in_cfg = 0;
	$in_cfg2 = 0;
	$in_tool = 0;
	$aline = '';
	$ln = 0;
    ##my $dbg = 'Name="Debug\\|Win32"';
    ##my $rel = 'Name="Release\\|Win32"';
	##my $dr = 'Name="(\\w)+\\|Win32"';
	##my $dr = 'Name="(\\w+)\\|Win32"';
	my $dr = 'Name="(.+)\\|Win32"';
	my $fulldr = '';
	my $tl = 'Name="(\\w+)"';
	my $rtl = 'RuntimeLibrary="(\\d{1})"';
	my $igl = 'IgnoreDefaultLibraryNames="(\\w+)"';
	my $prepro = 'PreprocessorDefinitions="(.+)"';
	my $pp = '';
	my $ign = '';
	my $isstatic = 0;
	my $dortfix = 1;
	my $change = '';
	$had_nm = 0;
	$vers = '';
	$rt = '';
	$tn = '';
	$chged = 0;

	foreach $pline (@plines) {
		$ln++;
		$pl2 = $pline;
		chomp $pl2;
		$pl2 =~ s/\r$//;
		while ($pl2 =~ /\t/) {
			$pl2 =~ s/\t/ /g;
		}
		while ($pl2 =~ /  /) {
			$pl2 =~ s/  / /g;
		}
		if ($in_cfgs) {
			if ($pline =~ m|</Configurations>|) {
				prt( "$ln End configurations ... [$pl2]\n") if ($dbg1);
			}
		} else {
			if ($pline =~ m|<Configurations>|) {
				prt( "$ln In Configurations ... [$pl2]\n") if ($dbg1);
				$in_cfgs = 1;
			}
		}
		if ($in_cfgs) {
			if ($pline =~ m/<Configuration[\s|\W]+/) {
				$in_cfg = 1;
				$in_cfg2 = 1;
				prt( "$ln In configuration ... [$pl2]\n") if ($dbg1);
				if ($pline =~ />/) {
					$in_cfg = 0;
					prt( "$ln Out configuration ... [$pl2]\n") if ($dbg1);
				}
			}
			if ($in_cfg) {
				if ($pline =~ />/) {
					$in_cfg = 0;
					prt( "$ln Out configuration ... [$pl2]\n") if ($dbg1);
				}
			}
			if ($in_cfg) {
				###prt( "In cfg with [$pl2]\n" );
				if ($pline =~ /$dr/) {
					### $vers = $1;
					$fulldr = $1;	# get the FULL configuration NAME
					if ($fulldr =~ /Debug/i) {
						$vers = 'Debug';
					} elsif ($fulldr =~ /Release/i) {
						$vers = 'Release';
					} else {
						$vers = 'Unknown';
					}
					if ($fulldr =~ /Static/i) {	# if STATIC in config NAME
						$isstatic = 1;
					} else {
						$isstatic = 0;
					}
					prt( "$ln *** Got [$fulldr]=[$vers] ***\n" ) if ($dbgvers);
				}
			}
			if ($in_cfg2) {
				if ($pline =~ m|</Configuration>|) {
					prt( "$ln End configuration ... [$pl2]\n") if ($dbg1);
					$in_cfg2 = 0;
				}
			}
			if ($in_cfg2) {
				if ($pline =~ /<Tool[\s|\W]/) {
					$in_tool = 1;
					prt( "$ln In Tool ... [$pl2]\n" ) if ($dbg1);
				}
				if ($in_tool) {
					if ($pline =~ m|/>| ) {
						prt( "$ln End Tool ... [$pl2]\n") if ($dbg1);
						$in_tool = 0;
					}
				}
				if ($in_tool) {
					prt( "$ln [$pl2] tool=[$tn]\n" ) if ($dbg3);
					if ($pline =~ /$tl/) {
						$tn = $1;
						prt( "$ln *** Got Name=$tn ... [$pl2]***\n" ) if ($dbg1);
					}
					if ($tn eq 'VCCLCompilerTool') {
						if ($pline =~ /$rtl/) {
							$rt = $1;
							$dortfix = 1; # assume DO RT FIX
							if ($isstatic && !$FixStatic) {
								$dortfix = 0; # inhibit RT fix
							}
							prt( "$ln *** Runtime=\"$rt\" ... [$pl2]***\n" ) if ($dbg4);
							if ($vers eq 'Debug') {
								if ($rt ne $DebRT) {
									if ($dortfix) {
										$pline =~ s/$rt/$DebRT/;
										prt("$ln CHANGED [$fulldr] RT [$rt] to [".trimall($pline)."]\n" );
										$chged++;
										$plines[$ln - 1] = $pline;
										$change .= "[$fulldr][$DebRT] ";
									} else {
										$wouldchg1++;	# bump the WOULD CHANGE IFF
										prt("$ln CFGOPTION: NO [$fulldr] RT CHANGE due \$FixStatic OFF\n") if ($dbgstatic);
									}
								} else {
									prt("$ln NO [$fulldr] RT CHANGE\n") if ($dbgrt);
								}
							} elsif ($vers eq 'Release') {
								if ($rt ne $RelRT) {
									if ($dortfix) {
										$pline =~ s/$rt/$RelRT/;
										prt("$ln CHANGED [$fulldr] RT [$rt] to [".trimall($pline)."]\n" );
										$chged++;
										$plines[$ln - 1] = $pline;
										$change .= "[$fulldr][$RelRT] ";
									} else {
										$wouldchg1++;	# bump the WOULD CHANGE IFF
										prt("$ln CFGOPTION: NO [$fulldr] RT CHANGE due \$FixStatic OFF\n") if ($dbgstatic);
									}
								} else {
									prt("$ln NO [$fulldr] RT CHANGE\n") if ($dbgrt);
								}
							} else {
								prt( "WARNING: [$vers][$fulldr] neither RELEASE NOR DEBUG!\n" );
							}
						} elsif ($pline =~ /$prepro/) {
							$pp = $1;
							prt( "$ln PREPROCESSOR ($fulldr=$vers) is [$pp] ...\n" ) if ($dbgprep);
							if ($pp =~ /$prep1/) {
								prt( "$ln NO CHANGE IN [$fulldr] PREPROCESSOR\n" ) if ($dbgprep1);
							} else {
								if ($FixPrep1) {
									my $npp = $pp;
									if (substr($npp,-1) ne ';') {
										$npp .= ';'
									}
									$npp .= $prep1;
									$pline =~ s/$pp/$npp/;
									prt("$ln CHANGED [$fulldr] PREPRO [$pp] to [".trimall($pline)."]\n" );
									$chged++;
									$plines[$ln - 1] = $pline;
									$change .= "[$fulldr][$prep1] ";
								} else {
									$wouldchg2++;
									prt( "$ln CFGOPTION: PREPROCESSOR not CHANGED due \$FixPrep1 is OFF\n" );
								}
							}
						}
					} elsif ($tn =~ /VCLinkerTool/i) {
						if ($pline =~ /$igl/i) {
							$ign = $1;
							prt( "$ln *** IGNORE DEFAULT is [".trimall($pline)."] ***\n" ) if ($dbg5);
							if ($FixIgnore) {
								# IgnoreDefaultLibraryNames="libcmt" or "libc"
								if (length($ign)) {
									$pline =~ s/$ign//i;
									prt("$ln CHANGED IGNORE [$ign] to [".trimall($pline)."]\n" ) if ($dbgign);
									$chged++;
									$plines[$ln - 1] = $pline;
									$change .= "[$fulldr]IGN[$ign] ";
								}
							} else {
								$wouldchg3++;
								prt("$ln CFGOPTION: [$fulldr] NO IGNORE CHANGED since \$FixIgnore is OFF\n" ) if ($dbg5);
							}
						} # runtime or ignore ...
					} # got compiler tool
				} # in Tool
			} # in Configuration
		}
	}
	if ($chged > 0) {
		if ($write_change > 0) {
			prt("Outputing the $chged changes ...\n");
			my $bak = $file . '.bak';
			###my $bakn = file_name($bak);
			if ( -f $bak) {
				prt("Removing old backup [$bak] ...\n");
				unlink $bak;
			}
			prt( "Renaming $file to $bak ...\n" );
			rename $file, $bak;
			prt( "Creating new file ...\n" );
			open OF, ">$file";
			foreach $pline (@plines) {
				print OF $pline;
			}
			close(OF);
			prt( "Done $file ... $change ...\n" );
			push(@new_files, $file);
		} else {
			$wouldwrite++;
			prt( "Got $chged CHANGES, but NO WRITE [$change]\n" );
		}
	} else {
		prt( "No changes in ".scalar @plines. " lines ...\n" );
	}
}

sub trimall($) {
	my ($ln) = shift;
	chomp $ln;
	$ln =~ s/\r$//;
	$ln =~ s/\t/ /g;
	while ($ln =~ /\s\s/) {
		$ln =~ s/\s\s/ /g;
	}
	while ($ln =~ /^\s/) {
		$ln = substr($ln,1);
	}
	while ($ln =~ /\s$/) {
		$ln = substr($ln,0, length($ln) - 1);
	}
	return $ln;
}

sub find_solution {
	my ($dir) = shift;
	opendir THEDIR, $dir or mydie( "ERROR: Can NOT open directory $dir ...\n" );
	local @files = readdir(THEDIR);
	closedir(THEDIR);
	local @solution = ();
	foreach my $f (@files) {
		if (is_solution($f)) {
			my $ff = $dir."\\".$f;
			push(@solution, $ff);
		}
	}
	return @solution;
}

sub get_proj_files {
	my ($in_file) = shift;
	my ($cnt, $cnt2);
    open $IF, "<$in_file" or mydie( "ERROR: Can not OPEN $in_file!\n" );
    local @lines = <$IF>; # slurp whole file, to an array of lines
    close($IF);
	my $dir = file_dirname($in_file);
	prt("Processing ".scalar @lines. " lines ...\n");
	$cnt = 0;
	foreach my $line (@lines) {
		chomp $line;
		$cnt++;
		if ($line =~ /^Project/) {
			my @arr = split( /\"/, $line );
			## prt( "Got ". scalar @arr . " after split at inverted commas...\n" );
			$cnt2 = 0;
			foreach my $bt (@arr) {
				if (is_vcproj($bt)) {
					my $pf = $dir . $bt;
					##prt("$bt is vcproj ");
					if ( -f $pf) {
						##prt( "FOUND [$pf]!");
						push(@proj_files, $pf);
					} else {
						prt( "NO FIND [$pf]!\n" );
					}
					###prt("\n");
				}
				$cnt2++;
			}
		}
	}
}


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

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


# eof - adjrt01.pl
