123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // Sections of this code have been abridged from https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TermInfoReader.cs under the MIT license
- using System;
- using System.IO;
- using System.Linq;
- using System.Text;
- namespace BepInEx.Unix
- {
- internal class TtyInfo
- {
- public string TerminalType { get; set; } = "default";
- public int MaxColors { get; set; }
- public string[] ForegroundColorStrings { get; set; }
- public static TtyInfo Default { get; } = new TtyInfo
- {
- MaxColors = 0
- };
- public string GetAnsiCode(ConsoleColor color)
- {
- if (MaxColors <= 0 || ForegroundColorStrings == null)
- return string.Empty;
- int index = (int)color % MaxColors;
- return ForegroundColorStrings[index];
- }
- }
- internal static class TtyHandler
- {
- private static readonly string[] ncursesLocations = new[]
- {
- "/usr/share/terminfo",
- "/etc/terminfo",
- "/usr/lib/terminfo",
- "/lib/terminfo"
- };
- private static string TryTermInfoDir(string dir, string term)
- {
- string infoFilePath = $"{dir}/{(int)term[0]:x}/{term}";
- if (File.Exists(infoFilePath))
- return infoFilePath;
- infoFilePath = Utility.CombinePaths(dir, term.Substring(0, 1), term);
- if (File.Exists(infoFilePath))
- return infoFilePath;
- return null;
- }
- private static string FindTermInfoPath(string term)
- {
- if (string.IsNullOrEmpty(term))
- return null;
- string termInfoVar = Environment.GetEnvironmentVariable("TERMINFO");
- if (termInfoVar != null && Directory.Exists(termInfoVar))
- {
- string text = TryTermInfoDir(termInfoVar, term);
- if (text != null)
- {
- return text;
- }
- }
- foreach (string location in ncursesLocations)
- {
- if (Directory.Exists(location))
- {
- string text = TryTermInfoDir(location, term);
- if (text != null)
- return text;
- }
- }
- return null;
- }
- public static TtyInfo GetTtyInfo(string terminal = null)
- {
- terminal = terminal ?? Environment.GetEnvironmentVariable("TERM");
- var path = FindTermInfoPath(terminal);
- if (path == null)
- return TtyInfo.Default;
- byte[] buffer = File.ReadAllBytes(path);
- var info = TtyInfoParser.Parse(buffer);
- info.TerminalType = terminal;
- return info;
- }
- }
- internal static class TtyInfoParser
- {
- private static readonly int[] ansiColorMapping =
- {
- 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
- };
- public static TtyInfo Parse(byte[] buffer)
- {
- int intSize;
- int magic = GetInt16(buffer, 0);
- switch (magic)
- {
- case 0x11a:
- intSize = 2;
- break;
- case 0x21E:
- intSize = 4;
- break;
- default:
- // Unknown ttyinfo format
- return TtyInfo.Default;
- }
- int boolFieldLength = GetInt16(buffer, 4);
- int intFieldLength = GetInt16(buffer, 6);
- int strOffsetFieldLength = GetInt16(buffer, 8);
- // Normally i'd put a more complete implementation here, but I only need to parse this info to get the max color count
- // Feel free to implement the rest of this using these sources:
- // https://github.com/mono/mono/blob/master/mcs/class/corlib/System/TermInfoReader.cs
- // https://invisible-island.net/ncurses/man/term.5.html
- // https://invisible-island.net/ncurses/man/terminfo.5.html
- int baseOffset = 12 + GetString(buffer, 12).Length + 1; // Skip the terminal name
- baseOffset += boolFieldLength; // Length of bool field section
- baseOffset += baseOffset % 2; // Correct for boundary
- int colorOffset =
- baseOffset
- + (intSize * (int)TermInfoNumbers.MaxColors); // Finally the offset for the max color integer
- //int stringOffset = baseOffset + (intSize * intFieldLength);
- //int foregoundColorOffset =
- // stringOffset
- // + (2 * (int)TermInfoStrings.SetAForeground);
- //foregoundColorOffset = stringOffset
- // + (2 * strOffsetFieldLength)
- // + GetInt16(buffer, foregoundColorOffset);
- var info = new TtyInfo();
- info.MaxColors = GetInteger(intSize, buffer, colorOffset);
- //string setForegroundTemplate = GetString(buffer, foregoundColorOffset);
- //info.ForegroundColorStrings = ansiColorMapping.Select(x => setForegroundTemplate.Replace("%p1%", x.ToString())).ToArray();
- info.ForegroundColorStrings = ansiColorMapping.Select(x => $"\u001B[{(x > 7 ? 82 + x : 30 + x)}m").ToArray();
- return info;
- }
- private static int GetInt32(byte[] buffer, int offset)
- {
- return buffer[offset]
- | (buffer[offset + 1] << 8)
- | (buffer[offset + 2] << 16)
- | (buffer[offset + 3] << 24);
- }
- private static short GetInt16(byte[] buffer, int offset)
- {
- return (short)(buffer[offset]
- | (buffer[offset + 1] << 8));
- }
- private static int GetInteger(int intSize, byte[] buffer, int offset)
- {
- return intSize == 2
- ? GetInt16(buffer, offset)
- : GetInt32(buffer, offset);
- }
- private static string GetString(byte[] buffer, int offset)
- {
- int length = 0;
- while (buffer[offset + length] != 0x00)
- length++;
- return Encoding.ASCII.GetString(buffer, offset, length);
- }
- internal enum TermInfoNumbers
- {
- MaxColors = 13
- }
- internal enum TermInfoStrings
- {
- SetAForeground = 359
- }
- }
- }
|