1    	//  =========================================================================
2    	//  
3    	//                             INTEL CONFIDENTIAL
4    	//                            Copyright 2005 - 2015
5    	//                    Intel Corporation All Rights Reserved. 
6    	//  
7    	//  =========================================================================
8    	//  The source code contained or described herein and all documents 
9    	//  related to the source code ("Material") are owned by Intel Corporation 
10   	//  or its suppliers or licensors. Title to the Material remains with 
11   	//  Intel Corporation or its suppliers and licensors. The Material contains 
12   	//  trade secrets and proprietary and confidential information of Intel or 
13   	//  its suppliers and licensors. The Material is protected by worldwide 
14   	//  copyright and trade secret laws and treaty provisions. No part of the 
15   	//  Material may be used, copied, reproduced, modified, published, uploaded, 
16   	//  posted, transmitted, distributed, or disclosed in any way without Intel’s 
17   	//  prior express written permission.
18   	//  
19   	//  No license under any patent, copyright, trade secret or other intellectual 
20   	//  property right is granted to or conferred upon you by disclosure or 
21   	//  delivery of the Materials, either expressly, by implication, inducement, 
22   	//  estoppel or otherwise. Any license under such intellectual property rights 
23   	//  must be express and approved by Intel in writing.
24   	//  ==========================================================================
25   	
26   	using System;
27   	using System.Collections.Generic;
28   	using System.IO;
29   	using System.Linq;
30   	using System.Text.RegularExpressions;
31   	using System.Xml;
32   	using System.Xml.Linq;
33   	using System.Xml.XPath;
34   	using Microsoft.Build.Framework;
35   	
36   	namespace Intel.Tools.MSBuildTasks.CreateMup
37   	{
38   		/// <summary>
39   		/// An MSBuild Task for creating a Dell MUP Xml file.
40   		/// </summary>
41   		public class CreateMup : Task
42   		{
43   			#region Constructor
44   			/// <summary>
45   			/// Creates a new instance of <see cref="CreateMup"/>.
46   			/// </summary>
47   			public CreateMup()
48   			{
49   				this.MupTemplate = new ITaskItem[0];
50   				this.OutputFile = new ITaskItem[0];
51   				this.InfFiles = new ITaskItem[0];
52   				this.Directories = new ITaskItem[0];
53   				this.Recursive = true;
54   				this.Version = string.Empty;
55   				this.MsiUpgradeCode = null;
56   				this.MsiProductCodex86 = null;
57   				this.MsiProductCodex64 = null;
58   			}
59   			#endregion
60   	
61   			#region Fields
62   			private const string _LogPrefix = @" ** CreateMup ** ";
63   			private const string MupRegKeyForProductVersion = @"HKEY_LOCAL_MACHINE\SOFTWARE\Dell\ManageableUpdatePackage\Intel\";
64   			#endregion
65   	
66   			#region Properties
67   			/// <summary>
68   			/// Gets a prefix for log file entries.
69   			/// </summary>
70   			protected override string LogPrefix
71   			{
72   				get { return CreateMup._LogPrefix; }
73   			}
74   	        /// <summary>
75   	        /// MupTemplate file
76   	        /// </summary>
77   			[Required]
78   			public ITaskItem[] MupTemplate { get; set; }
79   	        /// <summary>
80   	        /// Output mup.xml file
81   	        /// </summary>
82   			[Required]
83   			public ITaskItem[] OutputFile { get; set; }
84   	        /// <summary>
85   	        /// Input InfFiles to get information from
86   	        /// </summary>
87   			public ITaskItem[] InfFiles { get; set; }
88   	        /// <summary>
89   	        /// Directories that contains input Inf files
90   	        /// </summary>
91   			public ITaskItem[] Directories { get; set; }
92   	        /// <summary>
93   	        /// Is recursive
94   	        /// </summary>
95   			public bool Recursive { get; set; }
96   	        /// <summary>
97   	        /// Version
98   	        /// </summary>
99   			[Required]
100  			public string Version { get; set; }
101  	        /// <summary>
102  	        /// MSI bundle upgrade code
103  	        /// </summary>
104  			public string MsiUpgradeCode { get; set; }
105  	        /// <summary>
106  	        /// Upgrade code for x86 MSI package
107  	        /// </summary>
108  			public string MsiProductCodex86 { get; set; }
109  	        /// <summary>
110  	        /// Upgrade code for x64 MSI package
111  	        /// </summary>
112  			public string MsiProductCodex64 { get; set; }
113  			#endregion
114  	
115  			#region Methods
116  			/// <summary>
117  			/// When overridden in a derived class, executes the Task
118  			/// with logging initialized and error handling implemented.
119  			/// </summary>
120  			/// <returns>A boolean value indicating whether the Task completed successfully.</returns>
121  			protected override bool ExecuteSafe()
122  			{
123  				string mupTemplateFile;
124  				string outputFile;
125  				Version version;
126  				Guid msiUpgradeCode = Guid.Empty, msiProductCodex86 = Guid.Empty, msiProductCodex64 = Guid.Empty;
127  				var infFiles = new List<string>();
128  	
129  				//Get the mup file template.
130  				mupTemplateFile = base.VerifyFile(this.MupTemplate, true, "MupTemplate");
131  	
132  				//Get the output file.
133  				outputFile = base.VerifyFile(this.OutputFile, name: "OutputFile");
134  	
135  				//Get the product's version.
136  				if (string.IsNullOrWhiteSpace(this.Version) || !System.Version.TryParse(this.Version, out version))
137  				{
138  					throw new ArgumentException("Must provide a valid version.", "Version");
139  				}
140  	
141  				//Get the msi product codes and upgrade code.
142  				//TODO: Support x86-only or x64-only.
143  				if (!string.IsNullOrWhiteSpace(this.MsiUpgradeCode) && !string.IsNullOrWhiteSpace(this.MsiProductCodex86) && !string.IsNullOrWhiteSpace(this.MsiProductCodex64))
144  				{
145  					if (!Guid.TryParse(this.MsiUpgradeCode, out msiUpgradeCode)) throw new ArgumentException("Incorrectly formatted MSI Upgrade Code GUID: " + this.MsiUpgradeCode);
146  					if (!Guid.TryParse(this.MsiProductCodex86, out msiProductCodex86)) throw new ArgumentException("Incorrectly formatted x86 MSI Product Code GUID: " + this.MsiProductCodex86);
147  					if (!Guid.TryParse(this.MsiProductCodex64, out msiProductCodex64)) throw new ArgumentException("Incorrectly formatted x64 MSI Product Code GUID: " + this.MsiProductCodex64);
148  				}
149  				else if (!string.IsNullOrWhiteSpace(this.MsiUpgradeCode) || !string.IsNullOrWhiteSpace(this.MsiProductCodex86) || !string.IsNullOrWhiteSpace(this.MsiProductCodex64))
150  				{
151  					throw new ArgumentException("Must provide all 3 or none: MsiUpgradeCode, MsiProductCodex86, and MsiProductCodex64");
152  				}
153  	
154  				//Get all the inf files in the given directories.
155  				if (this.Directories != null)
156  				{
157  					infFiles.AddRange(base.VerifyFiles(this.Directories, "*.inf", this.Recursive, 0, "Directories"));
158  				}
159  	
160  				//Get all the inf files from the given files.
161  				if (this.InfFiles != null)
162  				{
163  					infFiles.AddRange(base.VerifyFiles(this.InfFiles, 0, true, "InfFiles"));
164  				}
165  	
166  				//Warn if there are no inf files.
167  				if (infFiles.Count == 0)
168  				{
169  					base.Log.LogWarning("No inf files will be included in the mup file.");
170  				}
171  	
172  				CreateMup.Execute(mupTemplateFile, infFiles, version, outputFile, msiUpgradeCode, msiProductCodex86, msiProductCodex64);
173  	
174  				return true;
175  			}
176  	
177  			internal static void Execute(string mupTemplateFile, List<string> infFiles, Version version, string outputFile, Guid msiUpgradeCode, Guid msiProductCodex86, Guid msiProductCodex64)
178  			{
179  				XmlNamespaceManager manager;
180  	
181  				//Load the mup template xml file.
182  				var mupFile = XDocument.Load(mupTemplateFile);
183  	
184  				//Get the namespace manager.
185  				using (var reader = mupFile.Root.CreateReader())
186  				{
187  					manager = new XmlNamespaceManager(reader.NameTable);
188  					manager.AddNamespace("mup", "http://schemas.dell.com/openmanage/cm/2/0/mupdefinition.xsd");
189  				}
190  	
191  	            //get component ID from template xml file
192  	            XElement componentIDElement = mupFile.Root.XPathSelectElement("//mup:msi[@componentID]", manager);
193  	            if (componentIDElement == null) throw new Exception("Could not find '//mup:msi[@componentID]' atrribute.");
194  	            componentID = componentIDElement.Attribute("componentID");
195  	
196  				//Set the product version entries.
197  				SetProductVersion(mupFile, manager, version, infFiles);
198  	
199  							//Set the msi upgrade code and product codes.
200  				if (msiUpgradeCode != Guid.Empty)
201  				{
202  					//If one guid is non-empty, then they must all be non-empty. We already made sure of this.
203  					SetMsiCodes(mupFile, manager, version, msiUpgradeCode, msiProductCodex86, msiProductCodex64);
204  				}
205  	
206  				//Ensure that the output directory exists.
207  				Directory.GetParent(outputFile).Create();
208  	
209  				//Save the output file.
210  				mupFile.Save(outputFile);
211  			}
212  	
213  			//TODO: Simplify this code.
214  			private static void SetMsiCodes(XDocument mupFile, XmlNamespaceManager manager, Version version, Guid msiUpgradeCode, Guid msiProductCodex86, Guid msiProductCodex64)
215  			{
216  				string xpath = string.Format("./mup:{0}/mup:{1}/mup:{2}/mup:{3}", "inventorymetadata", "fullpackageidentifier", "msis", "msi");
217  				List<XElement> parentElements = mupFile.Root.XPathSelectElements(xpath, manager).ToList();
218  	
219  				XNamespace ns = manager.LookupNamespace("mup");
220  	
221  				if (parentElements != null && parentElements.Count == 1)
222  				{
223  					foreach (var parentElement in parentElements)
224  					{
225  						//Set the MSI Product version.
226  						AssignContent("version", version, parentElement, manager, ns);
227  						//Set the MSI Upgrade Code.
228  						AssignContentGuid("upgradecode", msiUpgradeCode, parentElement, manager, ns);
229  					}
230  	
231  					//Set the MSI Product Codes.
232  					AssignContentGuid("identifyingnumber", msiProductCodex86, parentElements[0], manager, ns);
233  				}
234  				else throw new Exception("Could not find 'MUPDefinition/inventorymetadata/fullpackageidentifier/msis/msi' elements.");
235  			}
236  	
237  	        private static XAttribute componentID = null;
238  	
239  			private static void AssignContentGuid(string elementName, Guid guid, XElement parentElement, XmlNamespaceManager manager, XNamespace ns)
240  			{
241  				AssignContent(elementName, guid.ToString("B").ToUpperInvariant(), parentElement, manager, ns);
242  			}
243  	
244  			private static void AssignContent(string elementName, object content, XElement parentElement, XmlNamespaceManager manager, XNamespace ns)
245  			{
246  				var element = parentElement.XPathSelectElement("./mup:"+ elementName, manager);
247  	
248  				if (element == null)
249  				{
250  					//TODO IMPORTANT: Fix these NullReferenceExceptions.
251  					parentElement.Add(new XElement(ns + elementName, content));
252  				}
253  				else
254  				{
255  					element.ReplaceAll(content);
256  				}
257  			}
258  	        //add <Content> to <packageinformation>
259  	        private static void CreatePackageContent(XElement packageInformation, XmlNamespaceManager manager, List<string> infFiles)
260  	        {
261  	            XNamespace ns = manager.LookupNamespace("mup");
262  	            XElement contentRoot = new XElement(ns + "content");
263  	            var elements = GetPnPDevices(infFiles, ns, true);
264  	            contentRoot.Add(elements);
265  	            packageInformation.Add(contentRoot);
266  	        }
267  	
268  			private static void SetProductVersion(XDocument mupFile, XmlNamespaceManager manager, Version version, List<string> infFiles)
269  			{
270  				string xpath;
271  				XElement versionElement, versionElementParent;
272  	
273  				//Set the version of the product version entry.
274  				versionElementParent = mupFile.Root.XPathSelectElement("./mup:packageinformation", manager);
(1) Event cond_true: Condition "versionElementParent != null", taking true branch.
275  				if (versionElementParent != null)
276  				{
277  					versionElement = versionElementParent.XPathSelectElement("./mup:version", manager);
278  	
(2) Event cond_true: Condition "versionElement == null", taking true branch.
(3) Event var_compare_op: Comparing "versionElement" to null implies that "versionElement" might be null.
Also see events: [null_method_call]
279  					if (versionElement == null)
280  					{
(4) Event null_method_call: Calling a method on null object "versionElement".
Also see events: [var_compare_op]
281  						versionElement.Add(version);
282  					}
283  					else
284  					{
285  						versionElement.ReplaceAll(version);
286  					}
287  	                CreatePackageContent(versionElementParent, manager, infFiles);
288  				}
289  				else throw new Exception("Could not find 'MUPDefinition/packageinformation' element.");
290  	
291  	
292  				//Set the version of the registry key entry.
293  				xpath = string.Format("./mup:inventorymetadata/mup:fullpackageidentifier/mup:registrykeys/mup:registrykey/mup:name[starts-with(.,'{0}')]/..", MupRegKeyForProductVersion);
294  				versionElementParent = mupFile.Root.XPathSelectElement(xpath, manager);
295  	
296  				if (versionElementParent != null)
297  				{
298  					versionElement = versionElementParent.XPathSelectElement("./mup:value", manager);
299  	
300  					if (versionElement == null)
301  					{
302  						versionElement.Add(version);
303  					}
304  					else
305  					{
306  						versionElement.ReplaceAll(version);
307  					}
308  				}
309  			}
310  	
311  			private static void SetDrivers(XDocument mupFile, XmlNamespaceManager manager, List<string> infFiles)
312  			{
313  				string xpath;
314  				XElement parentElement;
315  				xpath = string.Format("./mup:{0}/mup:{1}/mup:{2}", "inventorymetadata", "extractdriversidentifier", "pnpids");
316  				parentElement = mupFile.Root.XPathSelectElement(xpath, manager);
317  	
318  				if (parentElement != null)
319  				{
320  					XNamespace ns = manager.LookupNamespace("mup");
321  	
322  					var elements = GetPnPDevices(infFiles, ns);
323  					if (elements.Count > 0)
324  					{
325  						parentElement.ReplaceAll(elements);
326  					}
327  				}
328  				else throw new Exception("Could not find 'MUPDefinition/inventorymetadata/extractdriversidentifier/pnpids' element.");
329  	
330  			}
331  	
332  			private static List<XElement> GetPnPDevices(List<string> infFiles, XNamespace ns, bool isPackageContent = false)
333  			{
334  				string line;
335  				List<string> lines;
336  				string version;
337  				List<XElement> hwIDelements;
338  				var pnpDeviceElements = new List<XElement>();
339  	
340  	            var MupMapping = MupHelper.GetMupMapping(infFiles.ToArray());
341  	
342  				foreach (var infFile in infFiles)
343  				{
344  					lines = new List<string>();
345  	
346  					using (var reader = new StreamReader(infFile))
347  					{
348  						while (!reader.EndOfStream && null != (line = reader.ReadLine()))
349  						{
350  							lines.Add(line);
351  						}
352  						reader.Close();
353  					}
354  	
355  					if (null == (version = GetDriverVersion(lines)))
356  					{
357  						throw new Exception("Driver version not found: " + infFile);
358  					}
359  					if ((hwIDelements = GetDriverDevices(lines, ns, isPackageContent)).Count == 0)
360  					{
361  						throw new Exception("Hardware IDs not found: " + infFile);
362  					}
363  	
364  	                if (isPackageContent)
365  	                {
366  	                    List<XElement> pnpHwID = new List<XElement>();
367  	                    List<XElement> pciHwID = new List<XElement>();
368  	                    foreach(XElement hwIDelement in hwIDelements)
369  	                    {
370  	                        if (hwIDelement.Name.LocalName == "PnPInfo")
371  	                            pnpHwID.Add(hwIDelement);
372  	                        else if (hwIDelement.Name.LocalName == "PCIInfo")
373  	                            pciHwID.Add(hwIDelement);
374  	                    }
375  	
376  	                    
377  	                    foreach (KeyValuePair<string,string> infElement in MupMapping.Where(element => element.Key.EndsWith(infFile)))
378  	                    {
379  	                        if(!infElement.Key.EndsWith(".inf"))
380  	                        {// not a inf file, can ignore
381  	                            continue;
382  	                        }
383  	                        string MupFolder = Path.GetDirectoryName(infElement.Value);
384  	                        string InfFile = Path.GetFileName(infElement.Key);
385  	
386  	                        if (pnpHwID.Count > 0)
387  	                            pnpDeviceElements.Add(
388  	                                new XElement(
389  	                                    ns + "Device",
390  	                                    componentID,
391  	                                    new XComment(string.Format(" Source: {0} ", Path.Combine(MupFolder, InfFile))),
392  	                                    pnpHwID,
393  	                                    GetContentImageInformation(ns, version, infElement.Key, MupFolder))
394  	                                );
395  	                        if (pciHwID.Count > 0)
396  	                            pnpDeviceElements.Add(
397  	                                new XElement(
398  	                                    ns + "Device",
399  	                                    componentID,
400  	                                    new XComment(string.Format(" Source: {0} ", Path.Combine(MupFolder, InfFile))),
401  	                                    pciHwID,
402  	                                    GetContentImageInformation(ns, version, infElement.Key, MupFolder))
403  	                                );
404  	                    }
405  	                } else
406  	                {
407  	                    pnpDeviceElements.Add(
408  	                        new XElement(
409  	                            ns + "pnpdevice",
410  	                            componentID,
411  	                            new XComment(string.Format(" Source: {0} ", Path.GetFileName(infFile))),
412  	                            new XElement(ns + "driverversion", version),
413  	                            hwIDelements )
414  	                        );
415  	                }
416  				}
417  	
418  				return pnpDeviceElements;
419  			}
420  	
421  	        private static XElement GetContentImageInformation(XNamespace ns, string version, string infFile, string folder)
422  	        {
423  	            if(!File.Exists(infFile)) throw new Exception ("File :"+infFile+" not found");
424  	            string catFile = null;
425  	            foreach (string Infline in File.ReadAllLines(infFile))
426  	            {
427  	                if (Infline.Trim().StartsWith(";"))
428  	                {//ignore comments
429  	                    continue;
430  	                }
431  	                string[] EqualsCharSplit = Infline.Split(new char[] { '=' });
432  	                if (EqualsCharSplit.Length != 2) continue;
433  	                if (EqualsCharSplit[0].Trim().Equals("CatalogFile", StringComparison.InvariantCultureIgnoreCase))
434  	                {//will search for "CatalogFile = some.cat"
435  	                    catFile = EqualsCharSplit[1].Trim();
436  	                }
437  	            }
438  	
439  	            return new XElement(ns + "Image",
440  	                new XAttribute("type","DRVR"),
441  	                new XAttribute("version", version),
442  	                new XElement(ns + "file", Path.Combine(folder, Path.GetFileName(infFile))),
443  	                catFile == null ? null : new XElement(ns + "file", Path.Combine(folder, catFile))
444  	                );
445  	        }
446  	
447  			private static string GetDriverVersion(List<string> lines)
448  			{
449  				//TODO: Test this with many different types of versions.
450  				string pattern = @"^DriverVer=(\d+/\d+/\d+)?,(\d+(.\d+){0,3})$";
451  	
452  				for (int i = 0; i < lines.Count; i++)
453  				{
454  					//Remove the whitespace so that our regex pattern is more accurate and also easier to read.
455  					var line = Regex.Replace(lines[i], @"\s+", string.Empty);
456  	
457  					if (Regex.IsMatch(line, pattern))
458  					{
459  						try
460  						{
461  							return Regex.Match(line, pattern).Groups[2].Value;
462  						}
463  						catch { }
464  					}
465  				}
466  	
467  				return null;
468  			}
469  	
470  			private static List<XElement> GetDriverDevices(List<string> lines, XNamespace ns, bool isPackageContent = false)
471  			{
472  				var allHardwareIDs = new List<string>();
473  				var elements = new List<XElement>();
474  				XElement element;
475  				GroupCollection groups;
476  	
477  				//%DeviceDesc%=Driver,,<hardwareID>
478  				string linePattern = @"^\%.+\%=.+,,?(.+)$";
479  				//Hardware ID pattern
480  				string hwIDPattern1 = @"^(\w+)\\(VEN|VID)_([0-9A-Z]{3,4})\&(DEV|PID)_([0-9A-F]{4})(\&SUBSYS_([0-9A-F]{4})([0-9A-F]{4}))?(\&.+)?$";
481  				//ACPI\INTFFFF
482  				string hwIDPattern2 = @"^(\w+)\\(.{3,4})([0-9A-F]{4})$";
483  				//*INTFFFF
484  				string hwIDPattern3 = @"^\*?(.{3,4})([0-9A-F]{4})$";
485  	
486  				var pnpIdStrings = new List<XElement>();
487  	
488  	
489  				allHardwareIDs = GetValidDriverSections(lines)
490  									.SelectMany(sectionName => GetLinesFromSection(lines, sectionName))
491  									.Select(line => line.ToTrimmedInfLine().ToUpperInvariant())
492  									.Where(line => Regex.IsMatch(line, linePattern))
493  									.Select(line => Regex.Match(line, linePattern).Groups[1].Value)
494  									.Distinct()
495  									.ToList();
496  	
497  	            string pnpstring = (isPackageContent ? "PnPInfo" : "pnpidstring");
498  	            string pcistring = (isPackageContent ? "PCIInfo" : "pciinfo");
499  	
500  				foreach (var hwID in allHardwareIDs)
501  				{
502  					element = null;
503  	
504  					if (Regex.IsMatch(hwID, hwIDPattern1))
505  					{
506  						groups = Regex.Match(hwID, hwIDPattern1).Groups;
507  	
508  						if (groups[1].Value == "PCI")
509  						{
510  	                        element = new XElement(ns + pcistring,
511  											new XAttribute("vendorID", groups[3].Value),
512  											new XAttribute("deviceID", groups[5].Value));
513  						}
514  						else if (groups[3].Value.Length == 4)
515  						{
516  	                        element = new XElement(ns + pnpstring,
517  											new XElement(ns + "ACPIID",       groups[3].Value),
518  											new XElement(ns + "PnPProductID", groups[5].Value));
519  						}
520  						else if (groups[3].Value.Length == 3)
521  						{
522  	                        element = new XElement(ns + pnpstring,
523  											new XElement(ns + "PNPID",        groups[3].Value),
524  											new XElement(ns + "PnPProductID", groups[5].Value));
525  						}
526  					}
527  					else if (Regex.IsMatch(hwID, hwIDPattern2))
528  					{
529  						groups = Regex.Match(hwID, hwIDPattern2).Groups;
530  	
531  						if (groups[2].Value.Length == 4)
532  						{
533  	                        element = new XElement(ns + pnpstring,
534  											new XElement(ns + "ACPIID",       groups[2].Value),
535  											new XElement(ns + "PnPProductID", groups[3].Value));
536  						}
537  						else if (groups[2].Value.Length == 3)
538  						{
539  	                        element = new XElement(ns + pnpstring,
540  											new XElement(ns + "PNPID",        groups[2].Value),
541  											new XElement(ns + "PnPProductID", groups[3].Value));
542  						}
543  					}
544  					else if (Regex.IsMatch(hwID, hwIDPattern3))
545  					{
546  						groups = Regex.Match(hwID, hwIDPattern3).Groups;
547  	
548  	                    element = new XElement(ns + pnpstring,
549  											new XElement(ns + "PNPID",        groups[1].Value),
550  											new XElement(ns + "PnPProductID", groups[2].Value));
551  					}
552  					
553  					if (element != null)
554  					{
555  						elements.Add(element);
556  					}
557  					else throw new Exception("Unknown hardware ID format: " + hwID);
558  				}
559  	
560  				return elements;
561  			}
562  	
563  			private static List<string> GetValidDriverSections(List<string> lines)
564  			{
565  				var allSections = GetAllSections(lines);
566  				var possibleDriverSections = GetPossibleDriverSections(lines);
567  	
568  				return allSections.Intersect(possibleDriverSections, new IgnoreCaseEqualityComparer()).ToList();
569  			}
570  			
571  			private static List<string> GetAllSections(List<string> lines)
572  			{
573  				string sectionPattern = @"^\[(.+)\]$";
574  				return lines
575  						.Select(line => line.ToTrimmedInfLine())
576  						.Where(line => IsSectionTag(line))
577  						.Select(line => Regex.Match(line, sectionPattern).Groups[1].Value)
578  						.ToList();
579  				//string line;
580  				//string sectionPattern = @"^\[(.+)\]$";
581  				//var sections = new List<string>();
582  	
583  				//for (int i = 0; i < lines.Count; i++)
584  				//{
585  				//	line = Regex.Replace(lines[i].TrimComments(), @"\s", string.Empty);
586  	
587  				//	if (Regex.IsMatch(line, sectionPattern))
588  				//	{
589  				//		sections.Add(Regex.Match(line, sectionPattern).Groups[1].Value);
590  				//	}
591  				//}
592  			}
593  	
594  			private static List<string> GetPossibleDriverSections(List<string> lines)
595  			{
596  				var sections = new List<string>();
597  				string pnpSectionPattern = @"^.+=(.+(,.+)*)$";
598  	
599  				var mfgLine = GetLinesFromSection(lines, "Manufacturer")
600  								.Select(line => line.ToTrimmedInfLine())
601  								.ToList()
602  								.Find(line => Regex.IsMatch(line, pnpSectionPattern));
603  	
604  				if (!string.IsNullOrWhiteSpace(mfgLine))
605  				{
606  					var allSections =
607  						Regex.Match(mfgLine, pnpSectionPattern)
608  							.Groups[1]
609  							.Value
610  							.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
611  	
612  					for (int i = 0; i < allSections.Length; i++)
613  					{
614  						sections.Add(allSections[0] + (i == 0 ? string.Empty : "." + allSections[i]));
615  					}
616  	
617  					return sections;
618  				}
619  				else throw new Exception("PnP sections not found.");
620  			}
621  	
622  			private static List<string> GetLinesFromSection(List<string> lines, string sectionName)
623  			{
624  				var sectionLines = new List<string>();
625  	
626  				int startLine =
627  					lines
628  						.FindIndex(line => IsSectionTag(line, sectionName)) + 1;
629  	
630  				if (startLine == 0) throw new Exception("Could not find section: " + sectionName);
631  	
632  	
633  				int endLine = 
634  					lines
635  						.Skip(startLine)
636  						.ToList()
637  						.FindIndex(line => IsSectionTag(line)) - 1;
638  	
639  				if (endLine == -2) endLine = lines.Count - 1;
640  				else
641  				{
642  					endLine += startLine;
643  				}
644  	
645  				for (int i = startLine; i <= endLine; i++)
646  				{
647  					if (!string.IsNullOrWhiteSpace(lines[i].ToTrimmedInfLine()))
648  					{
649  						sectionLines.Add(lines[i]);
650  					}
651  				}
652  	
653  				return sectionLines;
654  			}
655  	
656  			private static bool IsSectionTag(string line, string sectionName = null)
657  			{
658  				string sectionPattern = @"^\[(.+)\]$";
659  				line = line.ToTrimmedInfLine();
660  	
661  				if (string.IsNullOrWhiteSpace(sectionName))
662  				{
663  					return Regex.IsMatch(line, sectionPattern);
664  				}
665  				else
666  				{
667  					return
668  						Regex.IsMatch(line, sectionPattern) &&
669  						sectionName.Equals(Regex.Match(line, sectionPattern).Groups[1].Value, StringComparison.InvariantCultureIgnoreCase);
670  				}
671  			}
672  			#endregion
673  		}
674  	}
675