diff --git a/Aaru.Helpers/Aaru.Helpers.csproj b/Aaru.Helpers/Aaru.Helpers.csproj index 0568a7d69..088937ffa 100644 --- a/Aaru.Helpers/Aaru.Helpers.csproj +++ b/Aaru.Helpers/Aaru.Helpers.csproj @@ -41,6 +41,8 @@ ResXFileCodeGenerator Localization.Designer.cs + + diff --git a/Aaru.Helpers/AnsiColorParser.cs b/Aaru.Helpers/AnsiColorParser.cs new file mode 100644 index 000000000..0a0931d2d --- /dev/null +++ b/Aaru.Helpers/AnsiColorParser.cs @@ -0,0 +1,159 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ 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 . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2025 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Drawing; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Aaru.Helpers; + +public static partial class AnsiColorParser +{ + // Standard ANSI 16-color palette (indexes 0–15) + private static readonly Color[] _basicPalette = + [ + Color.FromArgb(0, 0, 0), // 0: black + Color.FromArgb(128, 0, 0), // 1: red + Color.FromArgb(0, 128, 0), // 2: green + Color.FromArgb(128, 128, 0), // 3: yellow + Color.FromArgb(0, 0, 128), // 4: blue + Color.FromArgb(128, 0, 128), // 5: magenta + Color.FromArgb(0, 128, 128), // 6: cyan + Color.FromArgb(192, 192, 192), // 7: white + Color.FromArgb(128, 128, 128), // 8: bright black (gray) + Color.FromArgb(255, 0, 0), // 9: bright red + Color.FromArgb(0, 255, 0), // 10: bright green + Color.FromArgb(255, 255, 0), // 11: bright yellow + Color.FromArgb(0, 0, 255), // 12: bright blue + Color.FromArgb(255, 0, 255), // 13: bright magenta + Color.FromArgb(0, 255, 255), // 14: bright cyan + Color.FromArgb(255, 255, 255) // 15: bright white + ]; + + // Matches ESC [ params m + private static readonly Regex _sequenceRegex = AnsiRegex(); + + public static Color Parse(string input) + { + Match m = _sequenceRegex.Match(input); + + if(!m.Success) throw new ArgumentException("No ANSI SGR sequence found.", nameof(input)); + + int[] parts = m.Groups["params"] + .Value.Split([ + ';' + ], + StringSplitOptions.RemoveEmptyEntries) + .Select(int.Parse) + .ToArray(); + + bool isBold = parts.Contains(1); + bool isDim = parts.Contains(2); + + // True-color: ESC[38;2;;;m + int idx38 = Array.IndexOf(parts, 38); + + switch(idx38) + { + case >= 0 when parts.Length > idx38 + 4 && parts[idx38 + 1] == 2: + { + int r = parts[idx38 + 2], g = parts[idx38 + 3], b = parts[idx38 + 4]; + + return Color.FromArgb(r, g, b); + } + + // 256-color: ESC[38;5;m + case >= 0 when parts.Length > idx38 + 2 && parts[idx38 + 1] == 5: + return ColorFrom256(parts[idx38 + 2]); + } + + // 30–37 and 90–97 color codes + foreach(int code in parts) + { + switch(code) + { + // 30–37 => palette[0–7] + case >= 30 and <= 37: + { + int baseIndex = code - 30; + + // Bold takes precedence + if(isBold) return _basicPalette[baseIndex + 8]; + + return isDim ? DimColor(_basicPalette[baseIndex]) : _basicPalette[baseIndex]; + } + + // 90–97 => palette[8–15] + case >= 90 and <= 97: + { + int brightIndex = code - 90 + 8; + + return isDim ? DimColor(_basicPalette[brightIndex]) : _basicPalette[brightIndex]; + } + } + } + + // Fallback + return Color.White; + } + + private static Color ColorFrom256(int index) + { + switch(index) + { + case < 16: + return _basicPalette[index]; + case <= 231: + { + // 6×6×6 color cube + int ci = index - 16; + int r = ci / 36, g = ci / 6 % 6, b = ci % 6; + + int[] levels = + [ + 0, 95, 135, 175, 215, 255 + ]; + + return Color.FromArgb(levels[r], levels[g], levels[b]); + } + default: + { + // Grayscale 232–255 + int gray = 8 + (index - 232) * 10; + + return Color.FromArgb(gray, gray, gray); + } + } + } + + private static Color DimColor(Color c) => + + // Halve each channel to simulate faint intensity + Color.FromArgb(c.R / 2, c.G / 2, c.B / 2); + + [GeneratedRegex(@"\e\[(?[0-9;]*)m", RegexOptions.Compiled)] + private static partial Regex AnsiRegex(); +} \ No newline at end of file diff --git a/Aaru.Helpers/DirColorsParser.cs b/Aaru.Helpers/DirColorsParser.cs new file mode 100644 index 000000000..f3f251d6c --- /dev/null +++ b/Aaru.Helpers/DirColorsParser.cs @@ -0,0 +1,166 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Author(s) : Natalia Portillo +// +// --[ 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 . +// +// ---------------------------------------------------------------------------- +// 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; + +/// +/// 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. +/// +public sealed class DirColorsParser +{ + private static readonly Lazy _instance = new(() => new DirColorsParser()); + + private DirColorsParser() + { + var map = new Dictionary(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 + string 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; + + /// + /// The hex color (e.g. "#RRGGBB") used for directories ("DIR" pattern). + /// Null if no directory color was defined. + /// + public string? DirectoryColor { get; } + + /// + /// The hex color (e.g. "#RRGGBB") used for normal files ("NORM" pattern). + /// Null if no directory color was defined. + /// + public string NormalColor => "white"; + + /// + /// Maps file extensions (including the leading '.') to hex color strings. + /// + public IReadOnlyDictionary ExtensionColors { get; } + + private static IEnumerable 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()!; + } +} \ No newline at end of file diff --git a/Aaru.Helpers/dir_colors b/Aaru.Helpers/dir_colors new file mode 100644 index 000000000..7ecd64be0 --- /dev/null +++ b/Aaru.Helpers/dir_colors @@ -0,0 +1,455 @@ +# /*************************************************************************** +# Aaru Data Preservation Suite +# ---------------------------------------------------------------------------- +# +# Filename : dir_colors +# Author(s) : Natalia Portillo +# +# Component : Commands. +# +# --[ Description ] ---------------------------------------------------------- +# +# Implements the 'ls' command. +# +# --[ 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 . +# +# ---------------------------------------------------------------------------- +# Copyright © 2011-2025 Natalia Portillo +# ****************************************************************************/ + +#NORMAL 00 # no color code at all +DIR 00;34 # directory + +# List any file extensions like '.gz' or '.tar' that you would like ls +# to colorize below. Put the extension, a space, and the color init string. +# (and any comments you want to add after a '#') + +# If you use DOS-style suffixes, you may want to uncomment the following: +# Or if you want to colorize scripts even if they do not have the +.app 01;32 +.bat 01;32 +.btm 01;32 +.cmd 01;32 # executables (bright green) +.com 01;32 +.exe 01;32 +.prg 01;32 +# executable bit actually set. +#.sh 01;32 +#.csh 01;32 + +# archives or compressed (bright red) +.7z 01;31 +.Z 01;31 +.ace 01;31 +.arj 01;31 +.bz 01;31 +.bz2 01;31 +.bz3 01;31 +.cab 01;31 +.cpio 01;31 +.crx 01;31 +.deb 01;31 +.dz 01;31 +.ear 01;31 +.gem 01;31 +.gz 01;31 +.hqx 01;31 +.jar 01;31 +.lrz 01;31 +.lz 01;31 +.lzfse 01;31 +.lzh 01;31 +.lzma 01;31 +.msi 01;31 +.pkg 01;31 +.rar 01;31 +.rpm 01;31 +.rz 01;31 +.s7z 01;31 +.sar 01;31 +.sit 01;31 +.sitx 01;31 +.sz 01;31 +.tar 01;31 +.taz 01;31 +.tbz 01;31 +.tbz2 01;31 +.tgz 01;31 +.tlz 01;31 +.tx 01;31 +.txz 01;31 +.tz 01;31 +.war 01;31 +.xpi 01;31 +.xz 01;31 +.z 01;31 +.zip 01;31 +.zipx 01;31 +.zoo 01;31 +.zst 01;31 +.zstd 01;31 + +# disk image formats (yellow) +.2mg 00;33 +.86f 00;33 +.aaruf 00;33 +.aif 00;33 +.b5i 00;33 +.b5t 00;33 +.b6i 00;33 +.b6t 00;33 +.bwi 00;33 +.bwt 00;33 +.cdi 00;33 +.cptp 00;33 +.cqm 00;33 +.cue 00;33 +.d77 00;33 +.d81 00;33 +.d88 00;33 +.dart 00;33 +.dasd 00;33 +.dc42 00;33 +.dcf 00;33 +.dcp 00;33 +.dicf 00;33 +.dko 00;33 +.dmg 00;33 +.fdd 00;33 +.hdd 00;33 +.hdi 00;33 +.hdk 00;33 +.hdv 00;33 +.imd 00;33 +.img 00;33 +.iso 00;33 +.mdf 00;33 +.mds 00;33 +.mdx 00;33 +.nrg 00;33 +.qcow 00;33 +.scp 00;33 +.sub 00;33 +.td0 00;33 +.toast 00;33 +.vdi 00;33 +.vhd 00;33 +.vhdx 00;33 +.vmdk 00;33 +.wim 00;33 +.xdf 00;33 +.xdi 00;33 + +# video formats +.3g2 00;35 +.3gp 00;35 +.MOV 00;35 +.MP4 00;35 +.anx 00;35 +.asf 00;35 +.ass 00;35 +.avi 00;35 +.axv 00;35 +.flc 00;35 +.fli 00;35 +.flv 00;35 +.m2v 00;35 +.m4v 00;35 +.mkv 00;35 +.mng 00;35 +.mov 00;35 +.mp4 00;35 +.mp4v 00;35 +.mpeg 00;35 +.mpg 00;35 +.nuv 00;35 +.ogm 00;35 +.ogv 00;35 +.ogv 00;35 +.ogx 00;35 +.ogx 00;35 +.qt 00;35 +.rm 00;35 +.rmvb 00;35 +.srt 00;35 +.vob 00;35 +.vtt 00;35 +.webm 00;35 +.wmv 00;35 +.yuv 00;35 + +# image formats +.ai 01;35 +.bmp 01;35 +.cdr 01;35 +.cgm 01;35 +.dl 01;35 +.emf 01;35 +.eps 01;35 +.flif 01;35 +.gif 01;35 +.gl 01;35 +.ico 01;35 +.jp2 01;35 +.jpeg 01;35 +.jpg 01;35 +.jxl 01;35 +.pbm 01;35 +.pcx 01;35 +.pgm 01;35 +.png 01;35 +.ppm 01;35 +.pps 01;35 +.ppsx 01;35 +.psd 01;35 +.qpi 01;35 +.svg 01;35 +.svgz 01;35 +.swf 01;35 +.tga 01;35 +.tif 01;35 +.tiff 01;35 +.wmf 01;35 +.xbm 01;35 +.xcf 01;35 +.xpm 01;35 +.xspf 01;35 +.xwd 01;35 + +# Document files +.diff 00;31 +.djvu 00;31 +.doc 00;31 +.docx 00;31 +.dot 00;31 +.dotx 00;31 +.epub 00;31 +.fla 00;31 +.kfx 00;31 +.mobi 00;31 +.odp 00;31 +.ods 00;31 +.odt 00;31 +.otp 00;31 +.ots 00;31 +.ott 00;31 +.patch 00;31 +.pdf 00;31 +.pdf 00;31 +.ppt 00;31 +.pptx 00;31 +.prc 00;31 +.ps 00;31 +.rtf 00;31 +.tex 00;31 +.txt 00;31 +.vcf 00;31 +.wps 00;31 +.xls 00;31 +.xlsx 00;31 + +# audio formats +.aac 00;36 +.au 00;36 +.axa 00;36 +.flac 00;36 +.m4a 00;36 +.mid 00;36 +.midi 00;36 +.mka 00;36 +.mp3 00;36 +.mpa 00;36 +.mpc 00;36 +.oga 00;36 +.ogg 00;36 +.opus 00;36 +.ra 00;36 +.spx 00;36 +.wav 00;36 +.wma 00;36 +.xspf 00;36 + +# Documentation +*AUTHORS 01;34 +*CHANGELOG 01;34 +*CHANGELOG.md 01;34 +*CHANGES 01;34 +*CHANGES.md 01;34 +*CODE_OF_CONDUCT.md 01;34 +*CONTRIBUTING 01;34 +*CONTRIBUTING.md 01;34 +*CONTRIBUTORS 01;34 +*COPYING 01;34 +*COPYRIGHT 01;34 +*ChangeLog 01;34 +*Changelog 01;34 +*HACKING 01;34 +*HISTORY 01;34 +*INSTALL 01;34 +*INSTALL.md 01;34 +*LICENSE 01;34 +*LICENSE.MD 01;34 +*LICENSE.md 01;34 +*NEWS 01;34 +*NOTICE 01;34 +*PATENTS 01;34 +*README 01;34 +*README.MD 01;34 +*README.md 01;34 +*README.rst 01;34 +*THANKS 01;34 +*TODO 01;34 +*VERSION 01;34 +*code-of-conduct.md 01;34 +*contributing.md 01;34 +*readme.md 01;34 +.MD 01;34 +.info 01;34 +.log 01;34 +.markdown 01;34 +.md 01;34 +.mkd 01;34 +.org 01;34 +.rst 01;34 + +# Programming +*Makefile 00;32 +.C 00;32 +.ac 00;32 +.am 38;5;246 +.asm 00;32 +.aspx 00;32 +.bash 00;32 +.c 00;32 +.cc 00;32 +.cl 00;32 +.class 00;32 +.coffee 00;32 +.cpp 00;32 +.cs 00;32 +.csh 00;32 +.css 00;32 +.csv 00;32 +.cxx 00;32 +.db 00;32 +.editorconfig 38;5;246 +.el 00;32 +.env 38;5;246 +.erb 00;32 +.example 38;5;246 +.f90 00;32 +.fish 00;32 +.gitattributes 38;5;246 +.gitignore 38;5;246 +.gitmodules 38;5;246 +.go 00;32 +.h 00;32 +.haml 00;32 +.hpp 00;32 +.hs 00;32 +.htm 00;32 +.html 00;32 +.in 38;5;246 +.java 00;32 +.js 00;32 +.json 00;32 +.l 00;32 +.less 00;32 +.lisp 00;32 +.lock 00;32 +.lua 00;32 +.m 00;32 +.m4 38;5;246 +.man 00;32 +.n 00;32 +.p 00;32 +.php 00;32 +.pl 00;32 +.pm 00;32 +.pod 00;32 +.py 00;32 +.r 00;32 +.razor 00;32 +.rb 00;32 +.rdf 00;32 +.rs 00;32 +.rss 00;32 +.sass 00;32 +.scss 00;32 +.sh 00;32 +.shtml 00;32 +.sql 00;32 +.sqlite 00;32 +.sv 00;32 +.svh 00;32 +.tex 00;32 +.toml 00;32 +.ts 00;32 +.v 00;32 +.vh 00;32 +.vhd 00;32 +.vim 00;32 +.xml 00;32 +.yaml 00;32 +.yml 00;32 +.zsh 00;32 + +# Unix +.3des 34 +.aes 34 +.asc 34 +.cer 34 +.cfg 34 +.conf 34 +.csr 34 +.desktop 34 +.enc 34 +.gpg 34 +.ini 34 +.otf 34 +.pgp 34 +.pid 34 +.ttf 34 + +# Miscellaneous +.crdownload 37 +.torrent 34 + +# Backup, Temporary, etc. +*# 1;34 +*lockfile 38;5;246 +*~ 1;34 +.BAK 38;5;246 +.DAT 38;5;246 +.DIST 38;5;246 +.OFF 38;5;246 +.OLD 38;5;246 +.ORIG 38;5;246 +.bak 38;5;246 +.dat 38;5;246 +.dist 38;5;246 +.dll 38;5;246 +.kate-swp 38;5;246 +.o 38;5;246 +.off 38;5;246 +.old 38;5;246 +.org_archive 38;5;246 +.orig 38;5;246 +.rlib 38;5;246 +.sassc 38;5;246 +.swo 38;5;246 +.swp 38;5;246 +.tmp 38;5;246 diff --git a/Aaru/Commands/Archive/List.cs b/Aaru/Commands/Archive/List.cs index 9b62e46f4..11a9e54c8 100644 --- a/Aaru/Commands/Archive/List.cs +++ b/Aaru/Commands/Archive/List.cs @@ -32,16 +32,19 @@ using System; using System.ComponentModel; +using System.IO; using System.Text; using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Core; +using Aaru.Helpers; using Aaru.Localization; using Aaru.Logging; using Spectre.Console; using Spectre.Console.Cli; +using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; namespace Aaru.Commands.Archive; @@ -312,6 +315,15 @@ sealed class ArchiveListCommand : Command continue; } + string color = DirColorsParser.Instance.NormalColor; + + if(stat.Attributes.HasFlag(FileAttributes.Directory)) + color = DirColorsParser.Instance.DirectoryColor; + else if(!DirColorsParser.Instance.ExtensionColors + .TryGetValue(Path.GetExtension(fileName) ?? "", + out color)) + color = DirColorsParser.Instance.NormalColor; + if(archive.ArchiveFeatures.HasFlag(ArchiveSupportedFeature.SupportsCompression)) { table.AddRow($"[blue]{stat.CreationTime?.ToShortDateString() ?? ""}[/]", @@ -319,7 +331,7 @@ sealed class ArchiveListCommand : Command $"[gold3]{new string(attr)}[/]", $"[lime]{uncompressedSize}[/]", $"[teal]{compressedSize}[/]", - $"[green]{Markup.Escape(fileName)}[/]"); + $"[{color}]{Markup.Escape(fileName)}[/]"); AaruLogging.Information($"Date: {stat.CreationTime?.ToShortDateString() ?? ""} " + $"Time: ({stat.CreationTime?.ToLongTimeString() ?? ""}), " + @@ -334,7 +346,7 @@ sealed class ArchiveListCommand : Command $"[dodgerblue1]{stat.CreationTime?.ToLongTimeString() ?? ""}[/]", $"[gold3]{new string(attr)}[/]", $"[lime]{uncompressedSize}[/]", - $"[green]{Markup.Escape(fileName)}[/]"); + $"[{color}]{Markup.Escape(fileName)}[/]"); AaruLogging.Information($"Date: {stat.CreationTime?.ToShortDateString() ?? ""} " + $"Time: ({stat.CreationTime?.ToLongTimeString() ?? ""}), " + diff --git a/Aaru/Commands/Filesystem/Ls.cs b/Aaru/Commands/Filesystem/Ls.cs index 321b4eabd..e9dda3224 100644 --- a/Aaru/Commands/Filesystem/Ls.cs +++ b/Aaru/Commands/Filesystem/Ls.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Linq; using System.Text; using Aaru.CommonTypes; @@ -40,11 +41,13 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Core; +using Aaru.Helpers; using Aaru.Localization; using Aaru.Logging; using JetBrains.Annotations; using Spectre.Console; using Spectre.Console.Cli; +using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; namespace Aaru.Commands.Filesystem; @@ -379,17 +382,20 @@ sealed class LsCommand : Command table.AddRow($"[gold3]{entry.Value.Attributes.ToAttributeChars()}[/]", "", "", - $"[green]{Markup.Escape(entry.Key)}[/]"); + $"[{DirColorsParser.Instance.DirectoryColor}]{Markup.Escape(entry.Key)}[/]"); AaruLogging.Information($"{entry.Value.Attributes.ToAttributeChars()} {entry.Key}"); } - else { + if(!DirColorsParser.Instance.ExtensionColors.TryGetValue(Path.GetExtension(entry.Key) ?? "", + out string color)) + color = DirColorsParser.Instance.NormalColor; + table.AddRow($"[gold3]{entry.Value.Attributes.ToAttributeChars()}[/]", $"[lime]{entry.Value.Length}[/]", $"[dodgerblue1]{entry.Value.LastWriteTimeUtc:s}[/]", - $"[green]{Markup.Escape(entry.Key)}[/]"); + $"[{color}]{Markup.Escape(entry.Key)}[/]"); AaruLogging .Information($"{entry.Value.Attributes.ToAttributeChars()} {entry.Value.Length} {entry.Value.LastWriteTimeUtc:s} {entry.Key}");