fix scheme name resolution, and schema load on WSL (#5327)

This PR fixes the scheme resolution bug outlined in #5326

The approach is as follows:

* In [SchemeManager.cs], find the first scheme parser that actually
  successfully parses the scheme, as opposed to the existing code, which
  finds the first scheme parser which _says it can parse the scheme_, as
  that logic spuriously returns `true` currently. 
* In [XmlSchemeParser.cs] and [JsonParser.cs], ensure that the contents
  of the file are read and the contents passed to XmlDocument.LoadXXX,
  as this fails with an UriException on WSL otherwise.
* Remove `CanParse` as it is superfluous. The check for a valid scheme
  parser should not just check an extension but also if the file exists
  - this is best done by the `ParseScheme` function as it already
  returns null on failure.
* Add `FileExtension` to the interface because we need it lifted now.

Closes #5326
This commit is contained in:
John Azariah 2020-07-01 13:15:09 -07:00 committed by GitHub
parent 44e80d40b6
commit 436fac6afa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 487 additions and 492 deletions

View file

@ -91,9 +91,8 @@ namespace ColorTool
public static ColorScheme GetScheme(string schemeName, bool reportErrors = false)
{
return GetParsers()
.Where(parser => parser.CanParse(schemeName))
.Select(parser => parser.ParseScheme(schemeName, reportErrors))
.FirstOrDefault();
.FirstOrDefault(x => x != null);
}
public static IEnumerable<ISchemeParser> GetParsers()

View file

@ -8,7 +8,7 @@ namespace ColorTool.SchemeParsers
interface ISchemeParser
{
string Name { get; }
bool CanParse(string schemeName);
string FileExtension { get; }
ColorScheme ParseScheme(string schemeName, bool reportErrors = false);
}
}

View file

@ -1,193 +1,190 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class IniSchemeParser : SchemeParserBase
{
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
protected override string FileExtension { get; } = ".ini";
// These are in Windows Color table order - BRG, not RGB.
internal static readonly IReadOnlyList<string> ColorNames = new[]
{
"DARK_BLACK",
"DARK_BLUE",
"DARK_GREEN",
"DARK_CYAN",
"DARK_RED",
"DARK_MAGENTA",
"DARK_YELLOW",
"DARK_WHITE",
"BRIGHT_BLACK",
"BRIGHT_BLUE",
"BRIGHT_GREEN",
"BRIGHT_CYAN",
"BRIGHT_RED",
"BRIGHT_MAGENTA",
"BRIGHT_YELLOW",
"BRIGHT_WHITE"
};
public override string Name { get; } = "INI File Parser";
public override bool CanParse(string schemeName) =>
string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase);
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
bool success = true;
string filename = FindIniScheme(schemeName);
if (filename == null) return null;
string[] tableStrings = new string[ColorTableSize];
uint[] colorTable = null;
uint? foregroundColor = null;
uint? backgroundColor = null;
uint? popupForegroundColor = null;
uint? popupBackgroundColor = null;
for (int i = 0; i < ColorTableSize; i++)
{
string name = ColorNames[i];
StringBuilder buffer = new StringBuilder(512);
GetPrivateProfileString("table", name, null, buffer, 512, filename);
tableStrings[i] = buffer.ToString();
if (tableStrings[i].Length <= 0)
{
success = false;
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniParseError, filename, name, tableStrings[i]));
}
break;
}
}
if (success)
{
try
{
colorTable = new uint[ColorTableSize];
for (int i = 0; i < ColorTableSize; i++)
{
colorTable[i] = ParseColor(tableStrings[i]);
}
if (ReadAttributes("popup", out var foreground, out var background))
{
var foregroundIndex = (ColorNames as IList<string>).IndexOf(foreground);
var backgroundIndex = (ColorNames as IList<string>).IndexOf(background);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
popupForegroundColor = colorTable[foregroundIndex];
popupBackgroundColor = colorTable[backgroundIndex];
}
}
if (ReadAttributes("screen", out foreground, out background))
{
var foregroundIndex = (ColorNames as IList<string>).IndexOf(foreground);
var backgroundIndex = (ColorNames as IList<string>).IndexOf(background);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
foregroundColor = colorTable[foregroundIndex];
backgroundColor = colorTable[backgroundIndex];
}
}
}
catch (Exception /*e*/)
{
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniLoadError, filename));
}
colorTable = null;
}
}
if (colorTable != null)
{
var consoleAttributes = new ConsoleAttributes(backgroundColor, foregroundColor, popupBackgroundColor, popupForegroundColor);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
else
{
return null;
}
bool ReadAttributes(string section, out string foreground, out string background)
{
foreground = null;
background = null;
StringBuilder buffer = new StringBuilder(512);
GetPrivateProfileString(section, "FOREGROUND", null, buffer, 512, filename);
foreground = buffer.ToString();
if (!ColorNames.Contains(foreground))
return false;
buffer = new StringBuilder(512);
GetPrivateProfileString(section, "BACKGROUND", null, buffer, 512, filename);
background = buffer.ToString();
if (!ColorNames.Contains(background))
return false;
return true;
}
}
private static uint ParseHex(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
return RGB(col.R, col.G, col.B);
}
private static uint ParseRgb(string arg)
{
int[] components = { 0, 0, 0 };
string[] args = arg.Split(',');
if (args.Length != components.Length) throw new Exception("Invalid color format \"" + arg + "\"");
if (args.Length != 3) throw new Exception("Invalid color format \"" + arg + "\"");
for (int i = 0; i < args.Length; i++)
{
components[i] = Int32.Parse(args[i]);
}
return RGB(components[0], components[1], components[2]);
}
private static uint ParseColor(string arg)
{
if (arg[0] == '#')
{
return ParseHex(arg.Substring(1));
}
else
{
return ParseRgb(arg);
}
}
private string FindIniScheme(string schemeName)
{
return SchemeManager.GetSearchPaths(schemeName, FileExtension).FirstOrDefault(File.Exists);
}
}
}
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections.Generic;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class IniSchemeParser : SchemeParserBase
{
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);
public override string FileExtension { get; } = ".ini";
// These are in Windows Color table order - BRG, not RGB.
internal static readonly IReadOnlyList<string> ColorNames = new[]
{
"DARK_BLACK",
"DARK_BLUE",
"DARK_GREEN",
"DARK_CYAN",
"DARK_RED",
"DARK_MAGENTA",
"DARK_YELLOW",
"DARK_WHITE",
"BRIGHT_BLACK",
"BRIGHT_BLUE",
"BRIGHT_GREEN",
"BRIGHT_CYAN",
"BRIGHT_RED",
"BRIGHT_MAGENTA",
"BRIGHT_YELLOW",
"BRIGHT_WHITE"
};
public override string Name { get; } = "INI File Parser";
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
bool success = true;
string filename = FindIniScheme(schemeName);
if (filename == null) return null;
string[] tableStrings = new string[ColorTableSize];
uint[] colorTable = null;
uint? foregroundColor = null;
uint? backgroundColor = null;
uint? popupForegroundColor = null;
uint? popupBackgroundColor = null;
for (int i = 0; i < ColorTableSize; i++)
{
string name = ColorNames[i];
StringBuilder buffer = new StringBuilder(512);
GetPrivateProfileString("table", name, null, buffer, 512, filename);
tableStrings[i] = buffer.ToString();
if (tableStrings[i].Length <= 0)
{
success = false;
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniParseError, filename, name, tableStrings[i]));
}
break;
}
}
if (success)
{
try
{
colorTable = new uint[ColorTableSize];
for (int i = 0; i < ColorTableSize; i++)
{
colorTable[i] = ParseColor(tableStrings[i]);
}
if (ReadAttributes("popup", out var foreground, out var background))
{
var foregroundIndex = (ColorNames as IList<string>).IndexOf(foreground);
var backgroundIndex = (ColorNames as IList<string>).IndexOf(background);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
popupForegroundColor = colorTable[foregroundIndex];
popupBackgroundColor = colorTable[backgroundIndex];
}
}
if (ReadAttributes("screen", out foreground, out background))
{
var foregroundIndex = (ColorNames as IList<string>).IndexOf(foreground);
var backgroundIndex = (ColorNames as IList<string>).IndexOf(background);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
foregroundColor = colorTable[foregroundIndex];
backgroundColor = colorTable[backgroundIndex];
}
}
}
catch (Exception /*e*/)
{
if (reportErrors)
{
Console.WriteLine(string.Format(Resources.IniLoadError, filename));
}
colorTable = null;
}
}
if (colorTable != null)
{
var consoleAttributes = new ConsoleAttributes(backgroundColor, foregroundColor, popupBackgroundColor, popupForegroundColor);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
else
{
return null;
}
bool ReadAttributes(string section, out string foreground, out string background)
{
foreground = null;
background = null;
StringBuilder buffer = new StringBuilder(512);
GetPrivateProfileString(section, "FOREGROUND", null, buffer, 512, filename);
foreground = buffer.ToString();
if (!ColorNames.Contains(foreground))
return false;
buffer = new StringBuilder(512);
GetPrivateProfileString(section, "BACKGROUND", null, buffer, 512, filename);
background = buffer.ToString();
if (!ColorNames.Contains(background))
return false;
return true;
}
}
private static uint ParseHex(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
return RGB(col.R, col.G, col.B);
}
private static uint ParseRgb(string arg)
{
int[] components = { 0, 0, 0 };
string[] args = arg.Split(',');
if (args.Length != components.Length) throw new Exception("Invalid color format \"" + arg + "\"");
if (args.Length != 3) throw new Exception("Invalid color format \"" + arg + "\"");
for (int i = 0; i < args.Length; i++)
{
components[i] = Int32.Parse(args[i]);
}
return RGB(components[0], components[1], components[2]);
}
private static uint ParseColor(string arg)
{
if (arg[0] == '#')
{
return ParseHex(arg.Substring(1));
}
else
{
return ParseRgb(arg);
}
}
private string FindIniScheme(string schemeName)
{
return SchemeManager.GetSearchPaths(schemeName, FileExtension).FirstOrDefault(File.Exists);
}
}
}

View file

@ -1,141 +1,142 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Xml;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class JsonParser : SchemeParserBase
{
protected override string FileExtension { get; } = ".json";
private static readonly IReadOnlyList<string> ConcfgColorNames = new[]
{
"black", // Dark Black
"dark_blue", // Dark Blue
"dark_green", // Dark Green
"dark_cyan", // Dark Cyan
"dark_red", // Dark Red
"dark_magenta", // Dark Magenta
"dark_yellow", // Dark Yellow
"gray", // Dark White
"dark_gray", // Bright Black
"blue", // Bright Blue
"green", // Bright Green
"cyan", // Bright Cyan
"red", // Bright Red
"magenta", // Bright Magenta
"yellow", // Bright Yellow
"white" // Bright White
};
public override string Name { get; } = "concfg Parser";
public override bool CanParse(string schemeName) =>
string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase);
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
XmlDocument xmlDoc = LoadJsonFile(schemeName);
if (xmlDoc == null) return null;
try
{
XmlNode root = xmlDoc.DocumentElement;
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[ColorTableSize]; ;
for (int i = 0; i < ColorTableSize; i++)
{
string name = ConcfgColorNames[i];
var node = children.OfType<XmlNode>().Where(n => n.Name == name).Single();
colorTable[i] = ParseColor(node.InnerText);
}
uint? popupForeground = null;
uint? popupBackground = null;
var popupNode = children.OfType<XmlNode>().Where(n => n.Name == "popup_colors").SingleOrDefault();
if (popupNode != null)
{
var parts = popupNode.InnerText.Split(',');
if (parts.Length == 2)
{
var foregroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[0]);
var backgroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[1]);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
popupForeground = colorTable[foregroundIndex];
popupBackground = colorTable[backgroundIndex];
}
}
}
uint? screenForeground = null;
uint? screenBackground = null;
var screenNode = children.OfType<XmlNode>().Where(n => n.Name == "screen_colors").SingleOrDefault();
if (screenNode != null)
{
var parts = screenNode.InnerText.Split(',');
if (parts.Length == 2)
{
var foregroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[0]);
var backgroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[1]);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
screenForeground = colorTable[foregroundIndex];
screenBackground = colorTable[backgroundIndex];
}
}
}
var consoleAttributes = new ConsoleAttributes(screenBackground, screenForeground, popupBackground, popupForeground);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
catch (Exception /*e*/)
{
if (reportErrors)
{
Console.WriteLine("failed to load json scheme");
}
return null;
}
}
private static uint ParseColor(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
return RGB(col.R, col.G, col.B);
}
private XmlDocument LoadJsonFile(string schemeName)
{
XmlDocument xmlDoc = new XmlDocument();
foreach (string path in SchemeManager.GetSearchPaths(schemeName, FileExtension)
.Where(File.Exists))
{
try
{
var data = File.ReadAllBytes(path);
var reader = JsonReaderWriterFactory.CreateJsonReader(data, System.Xml.XmlDictionaryReaderQuotas.Max);
xmlDoc.Load(reader);
return xmlDoc;
}
catch (XmlException /*e*/) { /* failed to parse */ }
catch (IOException /*e*/) { /* failed to find */ }
catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ }
}
return null;
}
}
}
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Json;
using System.Xml;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class JsonParser : SchemeParserBase
{
public override string FileExtension { get; } = ".json";
private static readonly IReadOnlyList<string> ConcfgColorNames = new[]
{
"black", // Dark Black
"dark_blue", // Dark Blue
"dark_green", // Dark Green
"dark_cyan", // Dark Cyan
"dark_red", // Dark Red
"dark_magenta", // Dark Magenta
"dark_yellow", // Dark Yellow
"gray", // Dark White
"dark_gray", // Bright Black
"blue", // Bright Blue
"green", // Bright Green
"cyan", // Bright Cyan
"red", // Bright Red
"magenta", // Bright Magenta
"yellow", // Bright Yellow
"white" // Bright White
};
public override string Name { get; } = "concfg Parser";
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
XmlDocument xmlDoc = LoadJsonFile(schemeName);
if (xmlDoc == null) return null;
try
{
XmlNode root = xmlDoc.DocumentElement;
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[ColorTableSize]; ;
for (int i = 0; i < ColorTableSize; i++)
{
string name = ConcfgColorNames[i];
var node = children.OfType<XmlNode>().Where(n => n.Name == name).Single();
colorTable[i] = ParseColor(node.InnerText);
}
uint? popupForeground = null;
uint? popupBackground = null;
var popupNode = children.OfType<XmlNode>().Where(n => n.Name == "popup_colors").SingleOrDefault();
if (popupNode != null)
{
var parts = popupNode.InnerText.Split(',');
if (parts.Length == 2)
{
var foregroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[0]);
var backgroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[1]);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
popupForeground = colorTable[foregroundIndex];
popupBackground = colorTable[backgroundIndex];
}
}
}
uint? screenForeground = null;
uint? screenBackground = null;
var screenNode = children.OfType<XmlNode>().Where(n => n.Name == "screen_colors").SingleOrDefault();
if (screenNode != null)
{
var parts = screenNode.InnerText.Split(',');
if (parts.Length == 2)
{
var foregroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[0]);
var backgroundIndex = (ConcfgColorNames as IList<string>).IndexOf(parts[1]);
if (foregroundIndex != -1 && backgroundIndex != -1)
{
screenForeground = colorTable[foregroundIndex];
screenBackground = colorTable[backgroundIndex];
}
}
}
var consoleAttributes = new ConsoleAttributes(screenBackground, screenForeground, popupBackground, popupForeground);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
catch (Exception /*e*/)
{
if (reportErrors)
{
Console.WriteLine("failed to load json scheme");
}
return null;
}
}
private static uint ParseColor(string arg)
{
System.Drawing.Color col = System.Drawing.ColorTranslator.FromHtml(arg);
return RGB(col.R, col.G, col.B);
}
private XmlDocument LoadJsonFile(string schemeName) =>
SchemeManager
.GetSearchPaths(schemeName, FileExtension)
.Select(path =>
{
try
{
var data = File.ReadAllBytes(path);
var reader = JsonReaderWriterFactory.CreateJsonReader(data, XmlDictionaryReaderQuotas.Max);
var xmlDoc = new XmlDocument();
xmlDoc.Load(reader);
return xmlDoc;
}
catch (XmlException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
catch (Exception e)
{
Console.WriteLine($"Unexpected Exception: {e}.\nBailing...");
throw;
}
return null;
})
.FirstOrDefault(x => x != null);
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -12,17 +13,13 @@ namespace ColorTool.SchemeParsers
public abstract string Name { get; }
public abstract bool CanParse(string schemeName);
public abstract ColorScheme ParseScheme(string schemeName, bool reportErrors = false);
// Common elements and helpers
protected abstract string FileExtension { get; }
public abstract string FileExtension { get; }
protected string ExtractSchemeName(string schemeFileName)
{
return schemeFileName.Substring(0, schemeFileName.Length - FileExtension.Length);
}
protected string ExtractSchemeName(string schemeFileName) =>
Path.ChangeExtension(schemeFileName, null);
}
}

View file

@ -1,148 +1,149 @@
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class XmlSchemeParser : SchemeParserBase
{
// In Windows Color Table order
private static readonly string[] PListColorNames =
{
"Ansi 0 Color", // Dark Black
"Ansi 4 Color", // Dark Blue
"Ansi 2 Color", // Dark Green
"Ansi 6 Color", // Dark Cyan
"Ansi 1 Color", // Dark Red
"Ansi 5 Color", // Dark Magenta
"Ansi 3 Color", // Dark Yellow
"Ansi 7 Color", // Dark White
"Ansi 8 Color", // Bright Black
"Ansi 12 Color", // Bright Blue
"Ansi 10 Color", // Bright Green
"Ansi 14 Color", // Bright Cyan
"Ansi 9 Color", // Bright Red
"Ansi 13 Color", // Bright Magenta
"Ansi 11 Color", // Bright Yellow
"Ansi 15 Color" // Bright White
};
private const string ForegroundKey = "Foreground Color";
private const string BackgroundKey = "Background Color";
private const string RedKey = "Red Component";
private const string GreenKey = "Green Component";
private const string BlueKey = "Blue Component";
protected override string FileExtension { get; } = ".itermcolors";
public override string Name { get; } = "iTerm Parser";
public override bool CanParse(string schemeName) =>
string.Equals(Path.GetExtension(schemeName), FileExtension, StringComparison.OrdinalIgnoreCase);
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
XmlDocument xmlDoc = LoadXmlScheme(schemeName); // Create an XML document object
if (xmlDoc == null) return null;
XmlNode root = xmlDoc.GetElementsByTagName("dict")[0];
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[ColorTableSize];
uint? fgColor = null, bgColor = null;
int colorsFound = 0;
bool success = false;
foreach (var tableEntry in children.OfType<XmlNode>().Where(_ => _.Name == "key"))
{
uint rgb = 0;
int index = -1;
XmlNode components = tableEntry.NextSibling;
success = ParseRgbFromXml(components, ref rgb);
if (!success) { break; }
else if (tableEntry.InnerText == ForegroundKey) { fgColor = rgb; }
else if (tableEntry.InnerText == BackgroundKey) { bgColor = rgb; }
else if (-1 != (index = Array.IndexOf(PListColorNames, tableEntry.InnerText)))
{ colorTable[index] = rgb; colorsFound++; }
}
if (colorsFound < ColorTableSize)
{
if (reportErrors)
{
Console.WriteLine(Resources.InvalidNumberOfColors);
}
success = false;
}
if (!success)
{
return null;
}
var consoleAttributes = new ConsoleAttributes(bgColor, fgColor, null, null);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
private static bool ParseRgbFromXml(XmlNode components, ref uint rgb)
{
int r = -1;
int g = -1;
int b = -1;
foreach (XmlNode c in components.ChildNodes)
{
if (c.Name == "key")
{
if (c.InnerText == RedKey)
{
r = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else if (c.InnerText == GreenKey)
{
g = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else if (c.InnerText == BlueKey)
{
b = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else
{
continue;
}
}
}
if (r < 0 || g < 0 || b < 0)
{
Console.WriteLine(Resources.InvalidColor);
return false;
}
rgb = RGB(r, g, b);
return true;
}
private XmlDocument LoadXmlScheme(string schemeName)
{
XmlDocument xmlDoc = new XmlDocument(); // Create an XML document object
foreach (string path in SchemeManager.GetSearchPaths(schemeName, FileExtension)
.Where(File.Exists))
{
try
{
xmlDoc.Load(path);
return xmlDoc;
}
catch (XmlException /*e*/) { /* failed to parse */ }
catch (IOException /*e*/) { /* failed to find */ }
catch (UnauthorizedAccessException /*e*/) { /* unauthorized */ }
}
return null;
}
}
}
//
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the terms described in the LICENSE file in the root of this project.
//
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using static ColorTool.ConsoleAPI;
namespace ColorTool.SchemeParsers
{
class XmlSchemeParser : SchemeParserBase
{
// In Windows Color Table order
private static readonly string[] PListColorNames =
{
"Ansi 0 Color", // Dark Black
"Ansi 4 Color", // Dark Blue
"Ansi 2 Color", // Dark Green
"Ansi 6 Color", // Dark Cyan
"Ansi 1 Color", // Dark Red
"Ansi 5 Color", // Dark Magenta
"Ansi 3 Color", // Dark Yellow
"Ansi 7 Color", // Dark White
"Ansi 8 Color", // Bright Black
"Ansi 12 Color", // Bright Blue
"Ansi 10 Color", // Bright Green
"Ansi 14 Color", // Bright Cyan
"Ansi 9 Color", // Bright Red
"Ansi 13 Color", // Bright Magenta
"Ansi 11 Color", // Bright Yellow
"Ansi 15 Color" // Bright White
};
private const string ForegroundKey = "Foreground Color";
private const string BackgroundKey = "Background Color";
private const string RedKey = "Red Component";
private const string GreenKey = "Green Component";
private const string BlueKey = "Blue Component";
public override string FileExtension { get; } = ".itermcolors";
public override string Name { get; } = "iTerm Parser";
public override ColorScheme ParseScheme(string schemeName, bool reportErrors = false)
{
XmlDocument xmlDoc = LoadXmlScheme(schemeName); // Create an XML document object
if (xmlDoc == null) return null;
XmlNode root = xmlDoc.GetElementsByTagName("dict")[0];
XmlNodeList children = root.ChildNodes;
uint[] colorTable = new uint[ColorTableSize];
uint? fgColor = null, bgColor = null;
int colorsFound = 0;
bool success = false;
foreach (var tableEntry in children.OfType<XmlNode>().Where(_ => _.Name == "key"))
{
uint rgb = 0;
int index = -1;
XmlNode components = tableEntry.NextSibling;
success = ParseRgbFromXml(components, ref rgb);
if (!success) { break; }
else if (tableEntry.InnerText == ForegroundKey) { fgColor = rgb; }
else if (tableEntry.InnerText == BackgroundKey) { bgColor = rgb; }
else if (-1 != (index = Array.IndexOf(PListColorNames, tableEntry.InnerText)))
{ colorTable[index] = rgb; colorsFound++; }
}
if (colorsFound < ColorTableSize)
{
if (reportErrors)
{
Console.WriteLine(Resources.InvalidNumberOfColors);
}
success = false;
}
if (!success)
{
return null;
}
var consoleAttributes = new ConsoleAttributes(bgColor, fgColor, null, null);
return new ColorScheme(ExtractSchemeName(schemeName), colorTable, consoleAttributes);
}
private static bool ParseRgbFromXml(XmlNode components, ref uint rgb)
{
int r = -1;
int g = -1;
int b = -1;
foreach (XmlNode c in components.ChildNodes)
{
if (c.Name == "key")
{
if (c.InnerText == RedKey)
{
r = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else if (c.InnerText == GreenKey)
{
g = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else if (c.InnerText == BlueKey)
{
b = (int)(255 * Convert.ToDouble(c.NextSibling.InnerText, CultureInfo.InvariantCulture));
}
else
{
continue;
}
}
}
if (r < 0 || g < 0 || b < 0)
{
Console.WriteLine(Resources.InvalidColor);
return false;
}
rgb = RGB(r, g, b);
return true;
}
private XmlDocument LoadXmlScheme(string schemeName) =>
SchemeManager
.GetSearchPaths(schemeName, FileExtension)
.Select(path =>
{
try
{
var text = File.ReadAllText(path);
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(text);
return xmlDoc;
}
catch (XmlException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
catch (Exception e)
{
Console.WriteLine($"Unexpected Exception: {e}.\nBailing...");
throw;
}
return null;
})
.FirstOrDefault(x => x != null);
}
}