mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 11:14:25 +00:00
166 lines
5.8 KiB
C#
166 lines
5.8 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2011-2025 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace Aaru.Helpers;
|
|
|
|
/// <summary>
|
|
/// Singleton that parses ~/.dir_colors or falls back to an embedded "dir_colors" resource.
|
|
/// Maps file extensions (".txt") → Spectre.Console hex color strings ("#RRGGBB"),
|
|
/// and provides a separate property for the directory color.
|
|
/// Uses AnsiColorParser.Parse to convert ANSI SGR codes into System.Drawing.Color.
|
|
/// </summary>
|
|
public sealed class DirColorsParser
|
|
{
|
|
private static readonly Lazy<DirColorsParser> _instance = new(static () => new DirColorsParser());
|
|
|
|
private DirColorsParser()
|
|
{
|
|
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
string? directoryHex = null;
|
|
|
|
// Choose ~/.dir_colors or embedded fallback
|
|
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
string path = Path.Combine(home, ".dir_colors");
|
|
string[] rawLines = File.Exists(path) ? File.ReadAllLines(path) : LoadResourceLines("dir_colors").ToArray();
|
|
|
|
foreach(string raw in rawLines)
|
|
{
|
|
string? line = raw?.Trim();
|
|
|
|
if(string.IsNullOrEmpty(line) || line is ['#', ..]) continue;
|
|
|
|
// Remove inline comments
|
|
int hashIdx = line.IndexOf('#');
|
|
if(hashIdx >= 0) line = line[..hashIdx].Trim();
|
|
|
|
if(string.IsNullOrEmpty(line)) continue;
|
|
|
|
// Split on '=' or whitespace
|
|
string pattern, sgr;
|
|
|
|
if(line.Contains('='))
|
|
{
|
|
string[] parts = line.Split('=', 2);
|
|
pattern = parts[0].Trim();
|
|
sgr = parts[1].Trim();
|
|
}
|
|
else
|
|
{
|
|
string[] parts = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
if(parts.Length < 2) continue;
|
|
pattern = parts[0];
|
|
sgr = parts[1];
|
|
}
|
|
|
|
if(!pattern.StartsWith("DIR", StringComparison.OrdinalIgnoreCase) &&
|
|
pattern is not ['.', ..] &&
|
|
!pattern.StartsWith("NORM", StringComparison.OrdinalIgnoreCase))
|
|
continue; // ( DIR, FILE, LINK, EXECUTABLE, or SUID)
|
|
|
|
// Build ANSI escape sequence
|
|
var ansi = $"\e[{sgr}m";
|
|
string hex;
|
|
|
|
try
|
|
{
|
|
Color color = AnsiColorParser.Parse(ansi);
|
|
hex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
|
}
|
|
catch
|
|
{
|
|
#pragma warning disable ERP022
|
|
continue;
|
|
#pragma warning restore ERP022
|
|
}
|
|
|
|
// Directory color pattern
|
|
if(pattern.Equals("DIR", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
directoryHex = hex;
|
|
|
|
continue;
|
|
}
|
|
|
|
// Directory color pattern
|
|
if(pattern.Equals("NORM", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
directoryHex = hex;
|
|
|
|
continue;
|
|
}
|
|
|
|
if(pattern is ['.', ..]) map[pattern] = hex;
|
|
}
|
|
|
|
DirectoryColor = directoryHex;
|
|
ExtensionColors = map;
|
|
}
|
|
|
|
public static DirColorsParser Instance => _instance.Value;
|
|
|
|
/// <summary>
|
|
/// The hex color (e.g. "#RRGGBB") used for directories ("DIR" pattern).
|
|
/// Null if no directory color was defined.
|
|
/// </summary>
|
|
public string? DirectoryColor { get; }
|
|
|
|
/// <summary>
|
|
/// The hex color (e.g. "#RRGGBB") used for normal files ("NORM" pattern).
|
|
/// Null if no directory color was defined.
|
|
/// </summary>
|
|
public string NormalColor => "white";
|
|
|
|
/// <summary>
|
|
/// Maps file extensions (including the leading '.') to hex color strings.
|
|
/// </summary>
|
|
public IReadOnlyDictionary<string, string> ExtensionColors { get; }
|
|
|
|
private static IEnumerable<string> LoadResourceLines(string resourceFileName)
|
|
{
|
|
var asm = Assembly.GetExecutingAssembly();
|
|
|
|
string? resource = asm.GetManifestResourceNames()
|
|
.FirstOrDefault(n => n.EndsWith(resourceFileName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
if(resource == null) yield break;
|
|
|
|
using Stream? stream = asm.GetManifestResourceStream(resource);
|
|
|
|
if(stream == null) yield break;
|
|
|
|
using var reader = new StreamReader(stream);
|
|
|
|
while(!reader.EndOfStream) yield return reader.ReadLine()!;
|
|
}
|
|
} |