/// <summary>Read the database for the specified terminal from the specified directory.</summary>
/// <param name="term">The identifier for the terminal.</param>
/// <param name="directoryPath">The path to the directory containing terminfo database files.</param>
/// <returns>The database, or null if it could not be found.</returns>
private static Database?ReadDatabase(string?term, string?directoryPath)
{
if (string.IsNullOrEmpty(term) || string.IsNullOrEmpty(directoryPath))
{
return(null);
}
Span <char> stackBuffer = stackalloc char[256];
SafeFileHandle?fd;
if (!TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{term[0]}/{term}"), out fd) && // /directory/termFirstLetter/term (Linux)
!TryOpen(string.Create(null, stackBuffer, $"{directoryPath}/{(int)term[0]:X}/{term}"), out fd)) // /directory/termFirstLetterAsHex/term (Mac)
{
return(null);
}
using (fd)
{
// Read in all of the terminfo data
long termInfoLength = Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_END)); // jump to the end to get the file length
Interop.CheckIo(Interop.Sys.LSeek(fd, 0, Interop.Sys.SeekWhence.SEEK_SET)); // reset back to beginning
const int MaxTermInfoLength = 4096; // according to the term and tic man pages, 4096 is the terminfo file size max
const int HeaderLength = 12;
if (termInfoLength <= HeaderLength || termInfoLength > MaxTermInfoLength)
{
throw new InvalidOperationException(SR.IO_TermInfoInvalid);
}
byte[] data = new byte[(int)termInfoLength];
if (ConsolePal.Read(fd, data) != data.Length)
{
throw new InvalidOperationException(SR.IO_TermInfoInvalid);
}
// Create the database from the data
return(new Database(term, data));
}
}