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);
(1) Event cond_false: |
Condition "componentIDElement == null", taking false branch. |
(2) Event if_end: |
End of if statement. |
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.
(3) Event cond_true: |
Condition "msiUpgradeCode != System.Guid.Empty", taking true branch. |
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.
(4) Event returned_null: |
"GetParent" returns "null" (checked 1 out of 4 times). |
(6) Event null_method_call: |
Calling a method on null object "System.IO.Directory.GetParent(outputFile)". |
Also see events: |
[example_checked] |
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);
275 if (versionElementParent != null)
276 {
277 versionElement = versionElementParent.XPathSelectElement("./mup:version", manager);
278
279 if (versionElement == null)
280 {
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