context: using System;
  using System.Collections;
  using System.IO;
  using Premake.Tests.Framework;
  
  namespace Premake.Tests.Vs2005
  {
  	public class Vs2005Parser : Parser
  	{
  		private int _numAspNet = 0;
  
  		#region Parser Methods
  		public override string TargetName
  		{
  			get { return "vs2005"; }
  		}
  		#endregion
  
  		#region Solution Parsing
  		public override void Parse(Project project, string filename)
  		{
  			/* File header */
  			Begin(filename + ".sln");
  			Match("Microsoft Visual Studio Solution File, Format Version 9.00");
  			Match("# Visual Studio 2005");
  
  			/* Package entries - VS "projects" */
  			string[] matches;
  			Hashtable packageDependencies = new Hashtable();
  			while (!Match("Global", true))
  			{
  				matches = Regex("Project\\(\"{([0-9A-F-]+)}\"\\) = \"(.+)\", \"(.+)\", \"{([0-9A-F-]+)}\"");
  				Package package = new Package();
  				project.Package.Add(package);
  
  				package.Name = matches[1];
  				package.ID = matches[3];
  				package.Path = Path.GetDirectoryName(matches[2]);
  				package.ScriptName = Path.GetFileName(matches[2]);
  
  				switch (matches[0])
  				{
  				case "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942":
  					package.Language = "c++";
  					break;
  
  				case "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC":
  					package.Language = "c#";
  					break;
  
  				case "E24C65DC-7377-472B-9ABA-BC803B73C61A":
  					package.Kind = "aspnet";
  					break;
  				}
  
  				if (package.Kind == "aspnet")
  				{
  					Match("\tProjectSection(WebsiteProperties) = preProject");
  
  					ArrayList deps = new ArrayList();
  					matches = Regex("\t\tProjectReferences = \"{([0-9A-F-]+)}|(.+).dll;\"", true);
  					while (matches != null)
  					{
  						deps.Add(matches[0]);
  						matches = Regex("\t\tProjectReferences = \"{([0-9A-F-]+)}|(.+).dll;\"", true);
  					}
  					packageDependencies[package.ID] = deps;
  
  					matches = Regex("\t\t(.+).AspNetCompiler.VirtualPath = \"/" + package.Name + "\"", true);
  					while (matches != null)
  					{
  						string cfg = matches[0];
  						Match("\t\t" + cfg + ".AspNetCompiler.PhysicalPath = \"" + package.Path + "\\\"");
  						Match("\t\t" + cfg + ".AspNetCompiler.TargetPath = \"PrecompiledWeb\\" + package.Name + "\\\"");
  						Match("\t\t" + cfg + ".AspNetCompiler.Updateable = \"true\"");
  						Match("\t\t" + cfg + ".AspNetCompiler.ForceOverwrite = \"true\"");
  						Match("\t\t" + cfg + ".AspNetCompiler.FixedNames = \"false\"");
  						matches = Regex("\t\t" + cfg + ".AspNetCompiler.Debug = \"(True|False)\"");
  
  						matches = Regex("\t\t(.+).AspNetCompiler.VirtualPath = \"/" + package.Name + "\"", true);
  					}
  
  					int port;
  					if (_numAspNet == 0)
  						port = 1106;
  					else if (_numAspNet == 1)
  						port = 1231;
  					else
  						port = 1251 + 2 * (_numAspNet - 2);
  					Match("\t\tVWDPort = \"" + port + "\"");
  					_numAspNet++;
  
  					matches = Regex("\t\tDefaultWebSiteLanguage = \"(.+)\"");
  					if (matches == null)
  						package.Language = "vbnet";
  					else if (matches[0] == "Visual C#")
  						package.Language = "c#";
  
  					Match("\tEndProjectSection");
  				}
  				else
  				{
  					/* Collect package dependencies, I'll sort them out after
  					 * I finish parsing the scripts below */
  					Match("\tProjectSection(ProjectDependencies) = postProject");
  					ArrayList deps = new ArrayList();
  					while (!Match("\tEndProjectSection", true))
  					{
  						matches = Regex("\t\t{([0-9A-F-]+)} = {([0-9A-F-]+)}");
  						if (matches[0] != matches[1])
  							throw new FormatException("GUID mismatch in ProjectDependencies block, should be the same");
  						deps.Add(matches[0]);
  					}
  					packageDependencies[package.ID] = deps;
  				}
  
  				Match("EndProject");
  			}
  
  			/* Read the list of configurations...just cache for now, test later */
  			ArrayList possibleConfigs = new ArrayList();
  			Match("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
  			while (!Match("\tEndGlobalSection", true))
  			{
  				matches = Regex("\t\t(.+)[ ]=[ ](.+)", true);
  
  				string cfg = matches[0];
  				if (possibleConfigs.Contains(cfg))
  					throw new FormatException("Duplicate configuration '" + cfg + "'");
  				possibleConfigs.Add(cfg);
  
  				/* Build up my list of scripted package configurations */
  				matches = cfg.Split('|');
  				if (!project.Configuration.Contains(matches[0]))
  					project.Configuration.Add(matches[0]);
  			}
  
  			foreach (Package package in project.Package)
  				package.Config.Add(project);
  
  			/* Read the list of package configurations...cache and test later */
  			ArrayList packageConfigs = new ArrayList();
  			Match("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
  			while (!Match("\tEndGlobalSection", true))
  			{
  				string cfg = GetLine();
  				if (packageConfigs.Contains(cfg))
  					throw new FormatException("Duplicate configuration '" + cfg + "'");
  				packageConfigs.Add(cfg);
  			}
  
  			Match("\tGlobalSection(SolutionProperties) = preSolution");
  			Match("\t\tHideSolutionNode = FALSE");
  			Match("\tEndGlobalSection");
  			Match("EndGlobal");
  
  			foreach (Package package in project.Package)
  			{
  				if (package.Kind != "aspnet")
  				{
  					filename = Path.Combine(Path.Combine(project.Path, package.Path), package.ScriptName);
  					switch (package.Language)
  					{
  					case "c++":
  						ParseCpp(project, package, filename);
  						break;
  					case "c#":
  						ParseCs(project, package, filename);
  						break;
  					default:
  						throw new NotImplementedException("Loading of " + package.Language + " packages not implemented");
  					}
  				}
  				else
  				{
  					foreach (Configuration config in package.Config)
  						config.Kind = "aspnet";
  				}
  			}
  
  			/* Now sort out the inter-package dependencies */
  			foreach (Package package in project.Package)
  			{
  				ArrayList deps = (ArrayList)packageDependencies[package.ID];
  				string[] deplist = new string[deps.Count];
  
  				for (int i = 0; i < deps.Count; ++i)
  				{
  					foreach (Package p2 in project.Package)
  					{
  						if (p2.ID == (string)deps[i])
  							deplist[i] = p2.Name;
  					}
  				}
  
  				foreach (Configuration config in package.Config)
  					config.Dependencies = deplist;
  			}
  
  			/* Now test configurations */
  			bool hasDotNet = false;
  			bool hasCpp = false;
  			foreach (Package package in project.Package)
  			{
  				if (package.Language == "c" || package.Language == "c++")
  					hasCpp = true;
  				else
  					hasDotNet = true;
  			}
  
  			ArrayList platforms = new ArrayList();
  			if (hasDotNet) platforms.Add("Any CPU");
  			if (hasDotNet && hasCpp) platforms.Add("Mixed Platforms");
  			if (hasCpp) platforms.Add("Win32");
  
  			int c = 0;
  			foreach (Package package in project.Package)
  			{
  				string arch;
  				if (package.Language == "c" || package.Language == "c++")
  					arch = "Win32";
  				else
  					arch = "Any CPU";
  
  				foreach (Configuration config in package.Config)
  				{
  					foreach (string platform in platforms)
  					{
  						string configPlatform = config.Name + "|" + platform;
  						if (!possibleConfigs.Contains(configPlatform))
  							throw new FormatException(configPlatform + " is not listed in possible configuration");
  
  						string root = "\t\t{" + package.ID + "}." + configPlatform + ".";
  
  						string match = root + "ActiveCfg = " + config.Name + "|" + arch;
  						if (((string)packageConfigs[c]) != match)
  							throw new FormatException("Expected:\n  " + match + "\nBut I got:\n  " + packageConfigs[c]);
  						c++;
  
  						if (platform == "MixedPlatforms" || platform == arch)
  						{
  							match = root + "Build.0 = " + config.Name + "|" + arch;
  							if (((string)packageConfigs[c]) != match)
  								throw new FormatException("Expected:\n  " + match + "\nBut I got:\n  " + packageConfigs[c]);
  							c++;
  						}
  					}
  				}
  			}
  		}
  		#endregion
  
  		#region C++ Parsing
  		private void ParseCpp(Project project, Package package, string filename)
  		{
  			Begin(filename);
  
  			Match("<?xml version=\"1.0\" encoding=\"Windows-1252\"?>");
  			Match("<VisualStudioProject");
  			Match("\tProjectType=\"Visual C++\"");
  			Match("\tVersion=\"8.00\"");
  			Match("\tName=\"" + package.Name + "\"");
  
  			string[] matches = Regex("\tProjectGUID=\"{([A-F0-9-]+)}\"");
  			if (matches[0] != package.ID)
  				throw new FormatException("Solution (" + package.ID + ") and project (" + matches[0] + ") GUIDs don't match");
  
  			Match("\tRootNamespace=\"" + package.Name + "\"");
  			Match("\tKeyword=\"Win32Proj\"");
  			Match("\t>");
  
  			Match("\t<Platforms>");
  			Match("\t\t<Platform");
  			Match("\t\t\tName=\"Win32\"");
  			Match("\t\t/>");
  			Match("\t</Platforms>");
  
  			Match("\t<ToolFiles>");
  			Match("\t</ToolFiles>");
  
  			Match("\t<Configurations>");
  
  			foreach (Configuration config in package.Config)
  			{
  				ArrayList buildFlags = new ArrayList();
  
  				Match("\t\t<Configuration");
  				Match("\t\t\tName=\"" + config.Name + "|Win32\"");
  
  				matches = Regex("\t\t\tOutputDirectory=\"(.+)\"");
  				config.OutDir = matches[0];
  
  				matches = Regex("\t\t\tIntermediateDirectory=\"(.+)\"");
  				config.ObjDir = matches[0];
  
  				matches = Regex("\t\t\tConfigurationType=\"([0-9])\"");
  				switch (int.Parse(matches[0]))
  				{
  				case 1: config.Kind = "exe"; break;
  				case 2: config.Kind = "dll"; break;
  				case 4: config.Kind = "lib"; break;
  				default:
  					throw new FormatException("Unrecognized value: ConfigurationType=\"" + matches[0] + "\"");
  				}
  
  				if (config.Kind == "lib")
  					config.LibDir = config.OutDir;
  				else
  					config.BinDir = config.OutDir;
  
  				matches = Regex("\t\t\tCharacterSet=\"([1-2])\"");
  				if (matches[0] == "1")
  					buildFlags.Add("unicode");
  				Match("\t\t\t>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCPreBuildEventTool\"");
  				Match("\t\t\t/>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCCustomBuildTool\"");
  				Match("\t\t\t/>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCXMLDataGeneratorTool\"");
  				Match("\t\t\t/>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCWebServiceProxyGeneratorTool\"");
  				Match("\t\t\t/>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCMIDLTool\"");
  				Match("\t\t\t/>");
  
  				Match("\t\t\t<Tool");
  				Match("\t\t\t\tName=\"VCCLCompilerTool\"");
  
  				matches = Regex("\t\t\t\tAdditionalOptions=\"(.+)\"", true);
  				config.BuildOptions = (matches != null) ? matches[0] : String.Empty;
  
  				matches = Regex("\t\t\t\tOptimization=\"([0-9])\"");
  				int optimization = int.Parse(matches[0]);
  				switch (optimization)
  				{
  				case 0: break;
  				case 1: buildFlags.Add("optimize-size"); break;
  				case 2: buildFlags.Add("optimize-speed"); break;
  				case 3: buildFlags.Add("optimize"); break;
  				default:
  					throw new FormatException("Unrecognized value: Optimization=\"" + matches[0] + "\"");
  				}
  
  				if (Match("\t\t\t\tOmitFramePointers=\"true\"", true))
  					buildFlags.Add("no-frame-pointer");
  
  				matches = Regex("\t\t\t\tAdditionalIncludeDirectories=\"(.+)\"", true);
  				config.IncludePaths = (matches != null) ? matches[0].Split(';') : new string[] { };
  
  				matches = Regex("\t\t\t\tPreprocessorDefinitions=\"(.+)\"", true);
  				config.Defines = (matches != null) ? matches[0].Split(';') : new string[] { };
  
  				if (optimization == 0)
  					Match("\t\t\t\tMinimalRebuild=\"true\"");
  
  				if (Match("\t\t\t\tExceptionHandling=\"false\"", true))
  					buildFlags.Add("no-exceptions");
  
  				if (Match("\t\t\t\tExceptionHandling=\"2\"", true))
  					buildFlags.Add("seh-exceptions");
  
  				if (optimization > 0)
  					Match("\t\t\t\tStringPooling=\"true\"", true);
  
  				if (optimization == 0)
  					Match("\t\t\t\tBasicRuntimeChecks=\"3\"", true);
  
  				if (optimization == 0)
  					matches = Regex("\t\t\t\tRuntimeLibrary=\"(1|3)\"");
  				else
  					matches = Regex("\t\t\t\tRuntimeLibrary=\"(0|2)\"");
  
  				if (matches[0] == "0" || matches[0] == "1")
  					config.LinkFlags = new string[] { "static-runtime" };
  
  				Match("\t\t\t\tEnableFunctionLevelLinking=\"true\"");
  
  				if (Match("\t\t\t\tRuntimeTypeInfo=\"false\"", true))
  					buildFlags.Add("no-rtti");
  
  				matches = Regex("\t\t\t\tTreatWChar_tAsBuiltInType=\"(true|false)\"", true);
  				if (matches != null)
  					buildFlags.Add(matches[0] == "true" ? "native-wchar" : "no-native-wchar");
  
  				config.Pch = "off";
  				matches = Regex("\t\t\t\tUsePrecompiledHeader=\"([0-9])\"");
  				if (matches[0] == "2")
  				{
  					config.Pch = "on";
  					matches = Regex("\t\t\t\tPrecompiledHeaderThrough=\"(.+?)\"");
  					config.PchHeader = matches[0];
  				}
  				else if (matches[0] != "0")
  				{
  					throw new FormatException("Expected UsePrecompiledHeader to be 2, got " + matches[0]);
  				}
  
  				matches = Regex("\t\t\t\tWarningLevel=\"([3-4])\"");
  				if (matches[0] == "4")
  					buildFlags.Add("extra-warnings");
  
  				if (Match("\t\t\t\tWarnAsError=\"true\"", true))
  					buildFlags.Add("fatal-warnings");
  
  				matches = Regex("\t\t\t\tDetect64BitPortabilityProblems=\"(true|false)\"");
  				if (matches[0] == "false")
  					buildFlags.Add("no-64bit-checks");
  
  				matches = Regex("\t\t\t\tDebugInformationFormat=\"([0-9])\"");
  
gt: 				if (matches[0] == "0")