From 7b56b93edd6b1410ba810575229a75f4e978cb57 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 10:44:51 -0500 Subject: [PATCH 1/9] Fixed typo with project directory - Renamed IsFSActive to isFSEnabled --- src/.editorconfig | 12 +++- src/TOMAS.sln | 10 +-- src/Tomas.Common/SysMeta.cs | 5 +- src/Tomas.Kernal/Kernel.cs | 51 -------------- .../GlobalUsing.cs | 0 src/Tomas.Kernel/Kernel.cs | 70 +++++++++++++++++++ .../Programs/About.cs | 0 src/{Tomas.Kernal => Tomas.Kernel}/Shell.cs | 0 src/{Tomas.Kernal => Tomas.Kernel}/SysFS.cs | 41 ++++++++--- .../Tomas.Kernel.csproj | 1 + 10 files changed, 120 insertions(+), 70 deletions(-) delete mode 100644 src/Tomas.Kernal/Kernel.cs rename src/{Tomas.Kernal => Tomas.Kernel}/GlobalUsing.cs (100%) create mode 100644 src/Tomas.Kernel/Kernel.cs rename src/{Tomas.Kernal => Tomas.Kernel}/Programs/About.cs (100%) rename src/{Tomas.Kernal => Tomas.Kernel}/Shell.cs (100%) rename src/{Tomas.Kernal => Tomas.Kernel}/SysFS.cs (73%) rename src/{Tomas.Kernal => Tomas.Kernel}/Tomas.Kernel.csproj (97%) diff --git a/src/.editorconfig b/src/.editorconfig index 7868c26..ebdac3a 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -13,6 +13,8 @@ indent_style = space indent_size = 4 insert_final_newline = false trim_trailing_whitespace = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 # C# files [*.cs] @@ -50,7 +52,7 @@ dotnet_style_predefined_type_for_member_access = true:suggestion # Use camel_case for private or internal constant fields dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constant_fields.applicable_kinds = field dotnet_naming_symbols.constant_fields.required_modifiers = const @@ -62,7 +64,7 @@ dotnet_naming_style.pascal_case_style.capitalization = pascal_case # internal and private fields should be _camel_case dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields -dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style dotnet_naming_symbols.private_internal_fields.applicable_kinds = field dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal @@ -122,3 +124,9 @@ csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = when_multiline:silent +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent diff --git a/src/TOMAS.sln b/src/TOMAS.sln index 5fff92d..5a713b9 100644 --- a/src/TOMAS.sln +++ b/src/TOMAS.sln @@ -19,7 +19,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Interface", "Tomas.In EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Terminal", "Tomas.Terminal\Tomas.Terminal.csproj", "{49E67E55-F9D2-419A-8097-38F39E98A95E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Kernel", "Tomas.Kernal\Tomas.Kernel.csproj", "{20750C95-A3C7-4958-BA9F-56E4C3BD0293}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Kernel", "Tomas.Kernel\Tomas.Kernel.csproj", "{B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,10 +39,10 @@ Global {49E67E55-F9D2-419A-8097-38F39E98A95E}.Debug|Any CPU.Build.0 = Debug|Any CPU {49E67E55-F9D2-419A-8097-38F39E98A95E}.Release|Any CPU.ActiveCfg = Release|Any CPU {49E67E55-F9D2-419A-8097-38F39E98A95E}.Release|Any CPU.Build.0 = Release|Any CPU - {20750C95-A3C7-4958-BA9F-56E4C3BD0293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {20750C95-A3C7-4958-BA9F-56E4C3BD0293}.Debug|Any CPU.Build.0 = Debug|Any CPU - {20750C95-A3C7-4958-BA9F-56E4C3BD0293}.Release|Any CPU.ActiveCfg = Release|Any CPU - {20750C95-A3C7-4958-BA9F-56E4C3BD0293}.Release|Any CPU.Build.0 = Release|Any CPU + {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Tomas.Common/SysMeta.cs b/src/Tomas.Common/SysMeta.cs index 79ffd67..80c9505 100644 --- a/src/Tomas.Common/SysMeta.cs +++ b/src/Tomas.Common/SysMeta.cs @@ -26,13 +26,12 @@ public struct SysMeta /// The build number is a 6-digit number, with the first 3 digits being the first 3 digits of the commit hash /// converted to a uint, and the last 3 digits being the last 3 digits of the commit hash converted to a uint. /// - [SuppressMessage("Usage", "CA2211:Non-constant fields should not be visible")] public static string BuildNumber = $"Build {BuildNumFromCommit}"; /// - /// Let's the system know that the file system is activated. + /// Let's the kernel know that the file system is activated. /// - public static bool IsFSActive { get; set; } = false; + public static bool IsFSEnabled { get; set; } = false; /// /// Generates the build number from the commit hash. diff --git a/src/Tomas.Kernal/Kernel.cs b/src/Tomas.Kernal/Kernel.cs deleted file mode 100644 index e5f4150..0000000 --- a/src/Tomas.Kernal/Kernel.cs +++ /dev/null @@ -1,51 +0,0 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - -namespace Tomas.Kernel; - -public class Kernel : Os.Kernel -{ - - protected override void BeforeRun() - { - SysFS.Initialize(); - - if (!SysMeta.IsFSActive) - Console.WriteLine($"{SysMeta.NAME} booted with errors."); - else - Console.WriteLine($"{SysMeta.NAME} booted successfully."); - } - - protected override void Run() - { - while (true) - { - var shell = new Shell(); - var command = shell.ReadLine; - var programs = shell.Programs; - - if (!programs.TryGetValue(command, out var program)) - { - Console.WriteLine("Command Not Found."); - continue; - } - - try - { - var start = program.Run(shell); - switch (start) - { - case true: - continue; - case false: - Console.WriteLine("Program closed unexpectedly."); - continue; - } - } - catch (Exception err) - { - Console.WriteLine(err.Message); - } - } - } -} diff --git a/src/Tomas.Kernal/GlobalUsing.cs b/src/Tomas.Kernel/GlobalUsing.cs similarity index 100% rename from src/Tomas.Kernal/GlobalUsing.cs rename to src/Tomas.Kernel/GlobalUsing.cs diff --git a/src/Tomas.Kernel/Kernel.cs b/src/Tomas.Kernel/Kernel.cs new file mode 100644 index 0000000..9ce08b6 --- /dev/null +++ b/src/Tomas.Kernel/Kernel.cs @@ -0,0 +1,70 @@ +// I license this project under the BSD 3-Clause license. +// See the LICENSE file in the project root for more information. + +namespace Tomas.Kernel; + +public class Kernel : Os.Kernel +{ + // This method is called before the Run method + protected override void BeforeRun() + { + // Initialize the file system + SysFS.Initialize(); + + // If the file system is not enabled, print an error message indicating that the system booted with errors + if (!SysMeta.IsFSEnabled) + Console.WriteLine($"{SysMeta.NAME} booted with errors."); + // If the file system is enabled, print a message indicating that the system booted successfully + else + Console.WriteLine($"{SysMeta.NAME} booted successfully."); + } + + // This method is the main loop of the kernel, which handles input and runs programs + protected override void Run() + { + // Run the loop indefinitely + while (true) + { + // Create a new instance of the Shell class + var shell = new Shell(); + + // Read a line of input from the user + var command = shell.ReadLine; + + // Get the dictionary of programs from the shell + var programs = shell.Programs; + + // If the command is not a key in the dictionary of programs, print an error message + // and continue to the next iteration of the loop + if (!programs.TryGetValue(command, out var program)) + { + Console.WriteLine("Command Not Found."); + continue; + } + + // Try to run the program and handle any exceptions that may be thrown + try + { + // Run the program and store the returned value in the 'start' variable + var start = program.Run(shell); + + // Check the value of 'start' and take the appropriate action + switch (start) + { + case true: + // If 'start' is true, continue to the next iteration of the loop + continue; + case false: + // If 'start' is false, print an error message and continue to the next iteration of the loop + Console.WriteLine("Program closed unexpectedly."); + continue; + } + } + catch (Exception err) + { + // If an exception is caught, print the error message and continue to the next iteration of the loop + Console.WriteLine(err.Message); + } + } + } +} diff --git a/src/Tomas.Kernal/Programs/About.cs b/src/Tomas.Kernel/Programs/About.cs similarity index 100% rename from src/Tomas.Kernal/Programs/About.cs rename to src/Tomas.Kernel/Programs/About.cs diff --git a/src/Tomas.Kernal/Shell.cs b/src/Tomas.Kernel/Shell.cs similarity index 100% rename from src/Tomas.Kernal/Shell.cs rename to src/Tomas.Kernel/Shell.cs diff --git a/src/Tomas.Kernal/SysFS.cs b/src/Tomas.Kernel/SysFS.cs similarity index 73% rename from src/Tomas.Kernal/SysFS.cs rename to src/Tomas.Kernel/SysFS.cs index 56f4d58..73118b9 100644 --- a/src/Tomas.Kernal/SysFS.cs +++ b/src/Tomas.Kernel/SysFS.cs @@ -12,8 +12,12 @@ static class SysFS static string LOG_FILE = $"{SYSTEM_DIR}system.log"; - // An instance of the CosmosVFS class, used for accessing the virtual file system - static readonly CosmosVFS _fs = new(); + const string FS_ERROR = "File system disabled."; + + /// + /// An instance of the CosmosVFS class, used for accessing the virtual file system + /// + static CosmosVFS fileSystem = new(); /// /// Initializes the file system by creating the system directory and sysinfo.txt file @@ -26,20 +30,25 @@ static class SysFS var createSysFiles = "Creating system files."; var setSysPref = "Writing system info."; var fsSuccess = "File system succesfully initialized."; + var sysInfoFile = "sysinfo.txt"; // Register the CosmosVFS instance as the virtual file system - VFSManager.RegisterVFS(_fs); + VFSManager.RegisterVFS(fileSystem); // Create the system directory - _fs.CreateDirectory(SYSTEM_DIR); + if (!Directory.Exists(SYSTEM_DIR)) + fileSystem.CreateDirectory(SYSTEM_DIR); - _fs.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); + // Create the system.log file in the system directory + if (!File.Exists($"{SYSTEM_DIR}{LOG_FILE}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); Console.WriteLine(createSysFiles); File.AppendAllText(LOG_FILE, createSysFiles); // Create the sysinfo.txt file in the system directory - _fs.CreateFile($"{SYSTEM_DIR}sysinfo.txt"); + if (!File.Exists($"{SYSTEM_DIR}{sysInfoFile}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{sysInfoFile}"); Console.WriteLine(setSysPref); @@ -52,7 +61,7 @@ static class SysFS File.AppendAllText(LOG_FILE, fsSuccess); // Set the IsFSActive property of the SysMeta class to true - SysMeta.IsFSActive = true; + SysMeta.IsFSEnabled = true; // Read the contents of the sysinfo.txt file and print it to the console var systemInfo = File.ReadAllText($"{SYSTEM_DIR}sysinfo.txt"); @@ -74,8 +83,13 @@ static class SysFS { try { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); + // Create the directory using the CosmosVFS instance - _fs.CreateDirectory($"{ROOT_DIR}\\{directory}"); + if (!Directory.Exists($"{ROOT_DIR}\\{directory}")) + fileSystem.CreateDirectory($"{ROOT_DIR}\\{directory}"); } catch (IOException err) { @@ -94,8 +108,13 @@ static class SysFS { try { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); + // Create the file using the CosmosVFS instance - _fs.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); + if (!File.Exists($"{ROOT_DIR}\\{path}\\{file}")) + fileSystem.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); } catch (IOException err) { @@ -114,6 +133,10 @@ static class SysFS { try { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); + // Get the directories in the specified path using the Directory.GetDirectories method var dirs = Directory.GetDirectories(path); diff --git a/src/Tomas.Kernal/Tomas.Kernel.csproj b/src/Tomas.Kernel/Tomas.Kernel.csproj similarity index 97% rename from src/Tomas.Kernal/Tomas.Kernel.csproj rename to src/Tomas.Kernel/Tomas.Kernel.csproj index f747da9..eeb902b 100644 --- a/src/Tomas.Kernal/Tomas.Kernel.csproj +++ b/src/Tomas.Kernel/Tomas.Kernel.csproj @@ -5,6 +5,7 @@ cosmos false + enable True enable From dc2176b26dd7fc424debceb612e535b9fc19c92b Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 11:22:38 -0500 Subject: [PATCH 2/9] Renamed Tomas.Common to Tomas.Core - Imported CSTNet parser to kernal under Globalization namespace - Added System.Diagnostics to global usings - Split version systems between kernal and terminal emulator (see changelog) --- Changelog.md | 6 +- src/TOMAS.sln | 2 +- .../GlobalUsing.cs | 1 + .../Programs/Clear.cs | 0 .../Programs/Commands.cs | 0 .../Programs/FenSay.cs | 0 .../Tomas.Core.csproj} | 7 -- .../GitInfo.txt | 0 src/Tomas.Kernel/GlobalUsing.cs | 2 + src/Tomas.Kernel/Globalization/CST.cs | 98 +++++++++++++++++ src/Tomas.Kernel/Globalization/IUIText.cs | 20 ++++ src/Tomas.Kernel/Globalization/UIText.cs | 101 ++++++++++++++++++ src/Tomas.Kernel/SysFS.cs | 4 +- src/{Tomas.Common => Tomas.Kernel}/SysMeta.cs | 9 +- src/Tomas.Kernel/Tomas.Kernel.csproj | 6 +- src/Tomas.Terminal/GitInfo.txt | 1 + src/Tomas.Terminal/GlobalUsing.cs | 2 + src/Tomas.Terminal/Programs/About.cs | 11 +- src/Tomas.Terminal/TermMeta.cs | 48 +++++++++ src/Tomas.Terminal/Tomas.Terminal.csproj | 10 +- 20 files changed, 300 insertions(+), 28 deletions(-) rename src/{Tomas.Common => Tomas.Core}/GlobalUsing.cs (69%) rename src/{Tomas.Common => Tomas.Core}/Programs/Clear.cs (100%) rename src/{Tomas.Common => Tomas.Core}/Programs/Commands.cs (100%) rename src/{Tomas.Common => Tomas.Core}/Programs/FenSay.cs (100%) rename src/{Tomas.Common/Tomas.Common.csproj => Tomas.Core/Tomas.Core.csproj} (51%) rename src/{Tomas.Common => Tomas.Kernel}/GitInfo.txt (100%) create mode 100644 src/Tomas.Kernel/Globalization/CST.cs create mode 100644 src/Tomas.Kernel/Globalization/IUIText.cs create mode 100644 src/Tomas.Kernel/Globalization/UIText.cs rename src/{Tomas.Common => Tomas.Kernel}/SysMeta.cs (87%) create mode 100644 src/Tomas.Terminal/GitInfo.txt create mode 100644 src/Tomas.Terminal/TermMeta.cs diff --git a/Changelog.md b/Changelog.md index 9453408..038027e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,10 +2,10 @@ ## v23.0 -- Calendar versioning, `YY.MINOR.MICRO` - +- Split versioning systems between kernal and terminal + - Calendar versioning, `YY.MINOR.MICRO`, for kernal + - Semantic versioning for terminal - If the file system is activate, system activity will be logged - - Build number based on commit hash Due to the huge time skip and architectural changes, I've (retroactively) switched to calendar versioning with ``v0.1`` now known as ``v20.1`` as well. diff --git a/src/TOMAS.sln b/src/TOMAS.sln index 5a713b9..1c437ae 100644 --- a/src/TOMAS.sln +++ b/src/TOMAS.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Common", "Tomas.Common\Tomas.Common.csproj", "{C50F3A6F-CFF4-4725-A1A5-21C5A2BC3321}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Core", "Tomas.Core\Tomas.Core.csproj", "{C50F3A6F-CFF4-4725-A1A5-21C5A2BC3321}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{59C9B3FC-B1EE-4C23-9BD9-D33074BF1334}" ProjectSection(SolutionItems) = preProject diff --git a/src/Tomas.Common/GlobalUsing.cs b/src/Tomas.Core/GlobalUsing.cs similarity index 69% rename from src/Tomas.Common/GlobalUsing.cs rename to src/Tomas.Core/GlobalUsing.cs index c66f29a..2e2aee3 100644 --- a/src/Tomas.Common/GlobalUsing.cs +++ b/src/Tomas.Core/GlobalUsing.cs @@ -1,2 +1,3 @@ global using System.Diagnostics.CodeAnalysis; +global using System.Diagnostics; global using Tomas.Interface; diff --git a/src/Tomas.Common/Programs/Clear.cs b/src/Tomas.Core/Programs/Clear.cs similarity index 100% rename from src/Tomas.Common/Programs/Clear.cs rename to src/Tomas.Core/Programs/Clear.cs diff --git a/src/Tomas.Common/Programs/Commands.cs b/src/Tomas.Core/Programs/Commands.cs similarity index 100% rename from src/Tomas.Common/Programs/Commands.cs rename to src/Tomas.Core/Programs/Commands.cs diff --git a/src/Tomas.Common/Programs/FenSay.cs b/src/Tomas.Core/Programs/FenSay.cs similarity index 100% rename from src/Tomas.Common/Programs/FenSay.cs rename to src/Tomas.Core/Programs/FenSay.cs diff --git a/src/Tomas.Common/Tomas.Common.csproj b/src/Tomas.Core/Tomas.Core.csproj similarity index 51% rename from src/Tomas.Common/Tomas.Common.csproj rename to src/Tomas.Core/Tomas.Core.csproj index 6a91aed..665370e 100644 --- a/src/Tomas.Common/Tomas.Common.csproj +++ b/src/Tomas.Core/Tomas.Core.csproj @@ -9,11 +9,4 @@ - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - diff --git a/src/Tomas.Common/GitInfo.txt b/src/Tomas.Kernel/GitInfo.txt similarity index 100% rename from src/Tomas.Common/GitInfo.txt rename to src/Tomas.Kernel/GitInfo.txt diff --git a/src/Tomas.Kernel/GlobalUsing.cs b/src/Tomas.Kernel/GlobalUsing.cs index b847d34..25fcb20 100644 --- a/src/Tomas.Kernel/GlobalUsing.cs +++ b/src/Tomas.Kernel/GlobalUsing.cs @@ -1,3 +1,5 @@ +global using System.Diagnostics.CodeAnalysis; +global using System.Diagnostics; global using Tomas.Common.Programs; global using Tomas.Interface; global using Tomas.Kernel.Programs; diff --git a/src/Tomas.Kernel/Globalization/CST.cs b/src/Tomas.Kernel/Globalization/CST.cs new file mode 100644 index 0000000..23dc7e3 --- /dev/null +++ b/src/Tomas.Kernel/Globalization/CST.cs @@ -0,0 +1,98 @@ +// This project is licensed under the BSD 3-Clause license. +// See the LICENSE file in the project root for more information. + +namespace Tomas.Kernel.Globalization; + +public class CST +{ + const char CARET = '^'; + const string LF = "\u000A"; + const string CR = "\u000D"; + const string CRLF = "\u000D\u000A"; + const string LS = "\u2028"; + + /// + /// Gets the value from the digit-based key. + /// + /// Returns the entry + public static string Parse(string content, int key) => Parse(content, key.ToString()); + + /// + /// Gets the value from the string-based key. + /// + /// Returns the entry + public static string Parse(string content, string key) + { + var entries = NormalizeEntries(content); + return GetEntry(entries, key); + } + + /// + /// Replaces the document's line endings with the native system line endings. + /// + /// This stage ensures there are no crashes during parsing. + /// The content of the document. + /// The document's content with native system line endings. + static IEnumerable NormalizeEntries(string content) + { + // Check if the document already uses native system line endings. + if (!content.Contains(Environment.NewLine)) + { + // If not, check for and replace other line ending types. + if (content.Contains(LF)) + content = content.Replace(LF, Environment.NewLine); + + if (content.Contains(CR)) + content = content.Replace(CR, Environment.NewLine); + + if (content.Contains(CRLF)) + content = content.Replace(CRLF, Environment.NewLine); + + if (content.Contains(LS)) + content = content.Replace(LS, Environment.NewLine); + } + + // Split the content by the caret and newline characters. + var lines = content.Split(new[] { $"{CARET}{Environment.NewLine}" }, + StringSplitOptions.RemoveEmptyEntries); + + // Filter out any lines that start with "//", "#", "/*", or end with "*/". + return lines.Where(line => + !line.StartsWith("//") && + !line.StartsWith("#") && + !line.StartsWith("/*") && + !line.EndsWith("*/")) + .AsEnumerable(); + } + + /// + /// Retrieves the value for the specified key from the given entries. + /// + /// The entries to search through. + /// The key to search for. + /// The value for the specified key, or a default string if not found. + static string GetEntry(IEnumerable entries, string key) + { + // Iterate through the entries. + foreach (var entry in entries) + { + // If the line doesn't start with the key, keep searching. + if (!entry.StartsWith(key)) + continue; + + // Locate the index of the caret character. + var startIndex = entry.IndexOf(CARET); + // Get the line from the caret character to the end of the string. + var line = entry[startIndex..]; + + // Return the line with the caret characters trimmed. + return line.TrimStart(CARET).TrimEnd(CARET); + } + + // If no entry is found, return a default string. + return "***MISSING***"; + } + +} + + diff --git a/src/Tomas.Kernel/Globalization/IUIText.cs b/src/Tomas.Kernel/Globalization/IUIText.cs new file mode 100644 index 0000000..e4ddb37 --- /dev/null +++ b/src/Tomas.Kernel/Globalization/IUIText.cs @@ -0,0 +1,20 @@ +// This project is licensed under the BSD 3-Clause license. +// See the LICENSE file in the project root for more information. + +namespace Tomas.Kernel.Globalization; + +public interface IUIText +{ + /// + /// The base directory for the language files. + /// + string[] BasePath { get; set; } + + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + string GetText(int id, int key); +} diff --git a/src/Tomas.Kernel/Globalization/UIText.cs b/src/Tomas.Kernel/Globalization/UIText.cs new file mode 100644 index 0000000..cf2bd26 --- /dev/null +++ b/src/Tomas.Kernel/Globalization/UIText.cs @@ -0,0 +1,101 @@ +// This project is licensed under the BSD 3-Clause license. +// See the LICENSE file in the project root for more information. + +namespace Tomas.Kernel.Globalization; + +public class UIText : IUIText +{ + /// + /// The language of the text. + /// + string Language { get; set; } = "english"; + + /// + /// The base directory for the language files. + /// + public string[] BasePath { get; set; } = { AppContext.BaseDirectory, "uitext" }; + + /// + /// Constructor for the UIText class. + /// + public UIText() { } + + /// + /// Constructor for the UIText class. + /// Loads the language file for the specified language. + /// + /// Language to load + public UIText(string language) + { + Language = language; + } + + /// + /// Constructor for the UIText class. + /// Loads the language file for the specified language and base directory. + /// + /// Language to load + /// Base directory for the language files. + public UIText(string language, params string[] baseBath) + { + Language = language; + BasePath = baseBath; + } + + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + public string GetText(int id, int key) => GetText(id, key.ToString()); + + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + public string GetText(int id, string key) + { + // Combine the base path and language path to get the full path of the language file. + var basePath = Path.Combine(BasePath); + var langPath = Path.Combine(basePath, $"{Language}.dir"); + + // Get all the files in the language directory. + var files = Directory.GetFiles(langPath); + + // Iterate through the files in the language directory. + foreach (var file in files) + { + // Skip files that do not have the ".cst" extension. + if (!file.Contains(".cst")) + continue; + + // Get the id of the current file. + var ids = Path.GetFileName(file); + var second = ids.IndexOf("_", 1, StringComparison.InvariantCultureIgnoreCase); + + if (second == -1) + continue; + + ids = ids.Substring(1, second - 1); + + // If the id of the current file does not match the id passed to the function, + // skip to the next file. + if (ids != id.ToString()) + continue; + + // Read the content of the current file. + var content = File.ReadAllText(file); + + // Return the text for the specified key. + return CST.Parse(content, key); + } + + // If no text is found, return a default string. + return "***MISSING***"; + } + +} + diff --git a/src/Tomas.Kernel/SysFS.cs b/src/Tomas.Kernel/SysFS.cs index 73118b9..ae905f5 100644 --- a/src/Tomas.Kernel/SysFS.cs +++ b/src/Tomas.Kernel/SysFS.cs @@ -6,13 +6,13 @@ namespace Tomas.Kernel; static class SysFS { // The root directory of the file system - const string ROOT_DIR = "0:\\"; + public const string ROOT_DIR = "0:\\"; // The system directory, located in the root directory static string SYSTEM_DIR = $"{ROOT_DIR}\\SYSTEM\\"; static string LOG_FILE = $"{SYSTEM_DIR}system.log"; - const string FS_ERROR = "File system disabled."; + public const string FS_ERROR = "File system disabled."; /// /// An instance of the CosmosVFS class, used for accessing the virtual file system diff --git a/src/Tomas.Common/SysMeta.cs b/src/Tomas.Kernel/SysMeta.cs similarity index 87% rename from src/Tomas.Common/SysMeta.cs rename to src/Tomas.Kernel/SysMeta.cs index 80c9505..86d4b83 100644 --- a/src/Tomas.Common/SysMeta.cs +++ b/src/Tomas.Kernel/SysMeta.cs @@ -1,13 +1,8 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. using System.Text; -namespace Tomas.Common; +namespace Tomas.Kernel; -/// -/// System metdata, such as name, version and build number. -/// -public struct SysMeta +internal struct SysMeta { /// /// The name of the operating system. diff --git a/src/Tomas.Kernel/Tomas.Kernel.csproj b/src/Tomas.Kernel/Tomas.Kernel.csproj index eeb902b..f15c019 100644 --- a/src/Tomas.Kernel/Tomas.Kernel.csproj +++ b/src/Tomas.Kernel/Tomas.Kernel.csproj @@ -25,10 +25,14 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - + diff --git a/src/Tomas.Terminal/GitInfo.txt b/src/Tomas.Terminal/GitInfo.txt new file mode 100644 index 0000000..ceab6e1 --- /dev/null +++ b/src/Tomas.Terminal/GitInfo.txt @@ -0,0 +1 @@ +0.1 \ No newline at end of file diff --git a/src/Tomas.Terminal/GlobalUsing.cs b/src/Tomas.Terminal/GlobalUsing.cs index 6169845..a3f2539 100644 --- a/src/Tomas.Terminal/GlobalUsing.cs +++ b/src/Tomas.Terminal/GlobalUsing.cs @@ -1,2 +1,4 @@ +global using System.Diagnostics.CodeAnalysis; +global using System.Diagnostics; global using Tomas.Common.Programs; global using Tomas.Interface; \ No newline at end of file diff --git a/src/Tomas.Terminal/Programs/About.cs b/src/Tomas.Terminal/Programs/About.cs index bf3e15f..6c716fa 100644 --- a/src/Tomas.Terminal/Programs/About.cs +++ b/src/Tomas.Terminal/Programs/About.cs @@ -6,10 +6,9 @@ namespace Tomas.Terminal.Programs; public class About : IProgram { -public bool Run(IShell shell) -{ -Console.WriteLine($"{SysMeta.NAME} Terminal Emulator v{SysMeta.BuildNumber}{Environment.NewLine}" - + "TOMAS (Tony's Managed Operating System) is a operating system written in C# using the COSMOS framework."); -return true; -} + public bool Run(IShell shell) + { + Console.WriteLine($"{TermMeta.NAME} Terminal Emulator v{TermMeta.VERSION}"); + return true; + } } \ No newline at end of file diff --git a/src/Tomas.Terminal/TermMeta.cs b/src/Tomas.Terminal/TermMeta.cs new file mode 100644 index 0000000..7eb564e --- /dev/null +++ b/src/Tomas.Terminal/TermMeta.cs @@ -0,0 +1,48 @@ +// I license this project under the BSD 3-Clause license. +// See the LICENSE file in the project root for more information. +using System.Text; + +namespace Tomas.Common; + +/// +/// System metdata, such as name, version and build number. +/// +public struct TermMeta +{ + /// + /// The name of the operating system. + /// + public const string NAME = "TOMAS Emulator"; + + /// + /// The version of the operating system, in the Calendar Versioning format: "yy.minor.patch". + /// The year, minor, and patch version numbers are automatically extracted from the Git repository + /// using the ThisAssembly.Git.SemVer object. + /// + public const string VERSION = $"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Minor}.{ThisAssembly.Git.SemVer.Patch}"; + + /// + /// The build number of the operating system, generated from the commit hash. + /// The build number is a 6-digit number, with the first 3 digits being the first 3 digits of the commit hash + /// converted to a uint, and the last 3 digits being the last 3 digits of the commit hash converted to a uint. + /// + public static string BuildNumber = $"Build {BuildNumFromCommit}"; + + /// + /// Generates the build number from the commit hash. + /// + /// The build number as a uint. + static uint BuildNumFromCommit + { + get + { + // Get the bytes of the commit hash as a UTF-8 encoded string + var commit = Encoding.UTF8.GetBytes(ThisAssembly.Git.Commit); + + // Convert the first 4 bytes of the commit hash to a uint and return it modulo 1000000 + // (this will give us a 6-digit number with the first 3 digits being the first 3 digits of the commit hash + // and the last 3 digits being the last 3 digits of the commit hash) + return BitConverter.ToUInt32(commit, 0) % 1000000; + } + } +} diff --git a/src/Tomas.Terminal/Tomas.Terminal.csproj b/src/Tomas.Terminal/Tomas.Terminal.csproj index f466519..91627f1 100644 --- a/src/Tomas.Terminal/Tomas.Terminal.csproj +++ b/src/Tomas.Terminal/Tomas.Terminal.csproj @@ -8,7 +8,15 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + From 2794aa17f49a293c39a7980500dabb6e7ae3d55d Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 11:44:50 -0500 Subject: [PATCH 3/9] Copyright waiver under the Unlicense with BSD 3-Clause fallback. --- README.md | 6 +- UNLICENSE | 24 ++ src/TOMAS.sln | 1 + src/TOMAS.sln.licenseheader | 8 +- src/Tomas.Core/GlobalUsing.cs | 6 + src/Tomas.Core/Programs/Clear.cs | 21 +- src/Tomas.Core/Programs/Commands.cs | 11 +- src/Tomas.Core/Programs/FenSay.cs | 37 ++-- src/Tomas.Interface/IProgram.cs | 22 +- src/Tomas.Interface/IShell.cs | 13 +- src/Tomas.Kernel/GlobalUsing.cs | 6 + src/Tomas.Kernel/Globalization/CST.cs | 159 +++++++------- src/Tomas.Kernel/Globalization/IUIText.cs | 31 +-- src/Tomas.Kernel/Globalization/UIText.cs | 161 +++++++------- src/Tomas.Kernel/Kernel.cs | 9 +- src/Tomas.Kernel/Programs/About.cs | 25 ++- src/Tomas.Kernel/Shell.cs | 45 ++-- src/Tomas.Kernel/SysFS.cs | 255 +++++++++++----------- src/Tomas.Kernel/SysMeta.cs | 6 + src/Tomas.Terminal/GlobalUsing.cs | 6 + src/Tomas.Terminal/Program.cs | 69 +++--- src/Tomas.Terminal/Programs/About.cs | 20 +- src/Tomas.Terminal/Shell.cs | 30 +-- src/Tomas.Terminal/TermMeta.cs | 74 ++++--- 24 files changed, 572 insertions(+), 473 deletions(-) create mode 100644 UNLICENSE diff --git a/README.md b/README.md index 41a0ebc..ef1e671 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,11 @@ TOMAS (**To**ny's **Ma**naged Operating **S**ystem) is a hobby operating system ### Prerequisites - Windows 10 or later - - Visual Studio 2022 - - .NET 6 or later - - COSMOS User Kit v2022 or later - - VMWare Workstation Player ## License -This project is licensed under the BSD 3-Clause license - see the [LICENSE](LICENSE) file for details. +In jurisdictions that recognize copyright waivers, I've [waived all copyright](UNLICENSE) and related or neighboring rights for to this project. In areas where these waivers are not recognized, [BSD-3-Clause](LICENSE) is enforced. diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/src/TOMAS.sln b/src/TOMAS.sln index 1c437ae..44fc703 100644 --- a/src/TOMAS.sln +++ b/src/TOMAS.sln @@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\LICENSE = ..\LICENSE ..\README.md = ..\README.md TOMAS.sln.licenseheader = TOMAS.sln.licenseheader + ..\UNLICENSE = ..\UNLICENSE EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Interface", "Tomas.Interface\Tomas.Interface.csproj", "{DAA9EDF4-83C7-4271-9805-FD6CE29E1796}" diff --git a/src/TOMAS.sln.licenseheader b/src/TOMAS.sln.licenseheader index df33244..81ee8df 100644 --- a/src/TOMAS.sln.licenseheader +++ b/src/TOMAS.sln.licenseheader @@ -1,4 +1,8 @@ extensions: designer.cs generated.cs extensions: .cs .cpp .h .fs -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. \ No newline at end of file +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ \ No newline at end of file diff --git a/src/Tomas.Core/GlobalUsing.cs b/src/Tomas.Core/GlobalUsing.cs index 2e2aee3..511e2b8 100644 --- a/src/Tomas.Core/GlobalUsing.cs +++ b/src/Tomas.Core/GlobalUsing.cs @@ -1,3 +1,9 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ global using System.Diagnostics.CodeAnalysis; global using System.Diagnostics; global using Tomas.Interface; diff --git a/src/Tomas.Core/Programs/Clear.cs b/src/Tomas.Core/Programs/Clear.cs index afeec90..080ae07 100644 --- a/src/Tomas.Core/Programs/Clear.cs +++ b/src/Tomas.Core/Programs/Clear.cs @@ -1,13 +1,16 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - -namespace Tomas.Common.Programs; +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +namespace Tomas.Core.Programs; public class Clear : IProgram { - public bool Run(IShell shell) - { - Console.Clear(); - return true; - } + public bool Run(IShell shell) + { + Console.Clear(); + return true; + } } \ No newline at end of file diff --git a/src/Tomas.Core/Programs/Commands.cs b/src/Tomas.Core/Programs/Commands.cs index c2590c2..70e0348 100644 --- a/src/Tomas.Core/Programs/Commands.cs +++ b/src/Tomas.Core/Programs/Commands.cs @@ -1,7 +1,10 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - -namespace Tomas.Common.Programs; +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +namespace Tomas.Core.Programs; public class Commands : IProgram { diff --git a/src/Tomas.Core/Programs/FenSay.cs b/src/Tomas.Core/Programs/FenSay.cs index 0dd73a4..9b227cf 100644 --- a/src/Tomas.Core/Programs/FenSay.cs +++ b/src/Tomas.Core/Programs/FenSay.cs @@ -1,15 +1,18 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - -namespace Tomas.Common.Programs; +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +namespace Tomas.Core.Programs; public class FenSay : IProgram { - /// - /// Fennec art by Todd Vargo - /// - const string _fennec = @" \/ + /// + /// Fennec art by Todd Vargo + /// + const string _fennec = @" \/ /\ /\ //\\_//\\ ____ \_ _/ / / @@ -20,19 +23,19 @@ public class FenSay : IProgram [ [ / \/ _/ _[ [ \ /_/"; - readonly string[] _phrases = - { + readonly string[] _phrases = + { "[SCREAMS IN FENNEC]", "Some people call me a coffee fox.", "Drink Soda. It makes you see faster.", "10/10, Wouldn't Recommend." }; - public bool Run(IShell shell) - { - var rng = new Random(); - var phrases = _phrases[rng.Next(_phrases.Length)]; - Console.WriteLine($"{phrases}{Environment.NewLine}{_fennec}"); - return true; - } + public bool Run(IShell shell) + { + var rng = new Random(); + var phrases = _phrases[rng.Next(_phrases.Length)]; + Console.WriteLine($"{phrases}{Environment.NewLine}{_fennec}"); + return true; + } } \ No newline at end of file diff --git a/src/Tomas.Interface/IProgram.cs b/src/Tomas.Interface/IProgram.cs index ccdfb70..dd7cf90 100644 --- a/src/Tomas.Interface/IProgram.cs +++ b/src/Tomas.Interface/IProgram.cs @@ -1,14 +1,18 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Interface; public interface IProgram { - /// - /// The program's main entry point. Boolean behaves as an exit point. - /// True and False are the equivalent to C's 0 and 1, i.e. "Success" and "Failure," respectfully. - /// - /// Allows the program to interact with the shell. - /// Exit back to shell. - bool Run(IShell shell); + /// + /// The program's main entry point. Boolean behaves as an exit point. + /// True and False are the equivalent to C's 0 and 1, i.e. "Success" and "Failure," respectfully. + /// + /// Allows the program to interact with the shell. + /// Exit back to shell. + bool Run(IShell shell); } \ No newline at end of file diff --git a/src/Tomas.Interface/IShell.cs b/src/Tomas.Interface/IShell.cs index fce577d..398eb91 100644 --- a/src/Tomas.Interface/IShell.cs +++ b/src/Tomas.Interface/IShell.cs @@ -1,11 +1,14 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Interface; public interface IShell { - string ReadLine { get; } + string ReadLine { get; } - Dictionary Programs { get; } + Dictionary Programs { get; } } \ No newline at end of file diff --git a/src/Tomas.Kernel/GlobalUsing.cs b/src/Tomas.Kernel/GlobalUsing.cs index 25fcb20..6380375 100644 --- a/src/Tomas.Kernel/GlobalUsing.cs +++ b/src/Tomas.Kernel/GlobalUsing.cs @@ -1,3 +1,9 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ global using System.Diagnostics.CodeAnalysis; global using System.Diagnostics; global using Tomas.Common.Programs; diff --git a/src/Tomas.Kernel/Globalization/CST.cs b/src/Tomas.Kernel/Globalization/CST.cs index 23dc7e3..1063563 100644 --- a/src/Tomas.Kernel/Globalization/CST.cs +++ b/src/Tomas.Kernel/Globalization/CST.cs @@ -1,97 +1,100 @@ -// This project is licensed under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel.Globalization; public class CST { - const char CARET = '^'; - const string LF = "\u000A"; - const string CR = "\u000D"; - const string CRLF = "\u000D\u000A"; - const string LS = "\u2028"; + const char CARET = '^'; + const string LF = "\u000A"; + const string CR = "\u000D"; + const string CRLF = "\u000D\u000A"; + const string LS = "\u2028"; - /// - /// Gets the value from the digit-based key. - /// - /// Returns the entry - public static string Parse(string content, int key) => Parse(content, key.ToString()); + /// + /// Gets the value from the digit-based key. + /// + /// Returns the entry + public static string Parse(string content, int key) => Parse(content, key.ToString()); - /// - /// Gets the value from the string-based key. - /// - /// Returns the entry - public static string Parse(string content, string key) - { - var entries = NormalizeEntries(content); - return GetEntry(entries, key); - } + /// + /// Gets the value from the string-based key. + /// + /// Returns the entry + public static string Parse(string content, string key) + { + var entries = NormalizeEntries(content); + return GetEntry(entries, key); + } - /// - /// Replaces the document's line endings with the native system line endings. - /// - /// This stage ensures there are no crashes during parsing. - /// The content of the document. - /// The document's content with native system line endings. - static IEnumerable NormalizeEntries(string content) - { - // Check if the document already uses native system line endings. - if (!content.Contains(Environment.NewLine)) - { - // If not, check for and replace other line ending types. - if (content.Contains(LF)) - content = content.Replace(LF, Environment.NewLine); + /// + /// Replaces the document's line endings with the native system line endings. + /// + /// This stage ensures there are no crashes during parsing. + /// The content of the document. + /// The document's content with native system line endings. + static IEnumerable NormalizeEntries(string content) + { + // Check if the document already uses native system line endings. + if (!content.Contains(Environment.NewLine)) + { + // If not, check for and replace other line ending types. + if (content.Contains(LF)) + content = content.Replace(LF, Environment.NewLine); - if (content.Contains(CR)) - content = content.Replace(CR, Environment.NewLine); + if (content.Contains(CR)) + content = content.Replace(CR, Environment.NewLine); - if (content.Contains(CRLF)) - content = content.Replace(CRLF, Environment.NewLine); + if (content.Contains(CRLF)) + content = content.Replace(CRLF, Environment.NewLine); - if (content.Contains(LS)) - content = content.Replace(LS, Environment.NewLine); - } + if (content.Contains(LS)) + content = content.Replace(LS, Environment.NewLine); + } - // Split the content by the caret and newline characters. - var lines = content.Split(new[] { $"{CARET}{Environment.NewLine}" }, - StringSplitOptions.RemoveEmptyEntries); + // Split the content by the caret and newline characters. + var lines = content.Split(new[] { $"{CARET}{Environment.NewLine}" }, + StringSplitOptions.RemoveEmptyEntries); - // Filter out any lines that start with "//", "#", "/*", or end with "*/". - return lines.Where(line => - !line.StartsWith("//") && - !line.StartsWith("#") && - !line.StartsWith("/*") && - !line.EndsWith("*/")) - .AsEnumerable(); - } + // Filter out any lines that start with "//", "#", "/*", or end with "*/". + return lines.Where(line => + !line.StartsWith("//") && + !line.StartsWith("#") && + !line.StartsWith("/*") && + !line.EndsWith("*/")) + .AsEnumerable(); + } - /// - /// Retrieves the value for the specified key from the given entries. - /// - /// The entries to search through. - /// The key to search for. - /// The value for the specified key, or a default string if not found. - static string GetEntry(IEnumerable entries, string key) - { - // Iterate through the entries. - foreach (var entry in entries) - { - // If the line doesn't start with the key, keep searching. - if (!entry.StartsWith(key)) - continue; + /// + /// Retrieves the value for the specified key from the given entries. + /// + /// The entries to search through. + /// The key to search for. + /// The value for the specified key, or a default string if not found. + static string GetEntry(IEnumerable entries, string key) + { + // Iterate through the entries. + foreach (var entry in entries) + { + // If the line doesn't start with the key, keep searching. + if (!entry.StartsWith(key)) + continue; - // Locate the index of the caret character. - var startIndex = entry.IndexOf(CARET); - // Get the line from the caret character to the end of the string. - var line = entry[startIndex..]; + // Locate the index of the caret character. + var startIndex = entry.IndexOf(CARET); + // Get the line from the caret character to the end of the string. + var line = entry[startIndex..]; - // Return the line with the caret characters trimmed. - return line.TrimStart(CARET).TrimEnd(CARET); - } + // Return the line with the caret characters trimmed. + return line.TrimStart(CARET).TrimEnd(CARET); + } - // If no entry is found, return a default string. - return "***MISSING***"; - } + // If no entry is found, return a default string. + return "***MISSING***"; + } } diff --git a/src/Tomas.Kernel/Globalization/IUIText.cs b/src/Tomas.Kernel/Globalization/IUIText.cs index e4ddb37..b570366 100644 --- a/src/Tomas.Kernel/Globalization/IUIText.cs +++ b/src/Tomas.Kernel/Globalization/IUIText.cs @@ -1,20 +1,23 @@ -// This project is licensed under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel.Globalization; public interface IUIText { - /// - /// The base directory for the language files. - /// - string[] BasePath { get; set; } + /// + /// The base directory for the language files. + /// + string[] BasePath { get; set; } - /// - /// Get the text for the given id and key. - /// - /// The id of the text. - /// The key of the text. - /// The text for the given id and key. - string GetText(int id, int key); + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + string GetText(int id, int key); } diff --git a/src/Tomas.Kernel/Globalization/UIText.cs b/src/Tomas.Kernel/Globalization/UIText.cs index cf2bd26..4c9c3fe 100644 --- a/src/Tomas.Kernel/Globalization/UIText.cs +++ b/src/Tomas.Kernel/Globalization/UIText.cs @@ -1,101 +1,104 @@ -// This project is licensed under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel.Globalization; public class UIText : IUIText { - /// - /// The language of the text. - /// - string Language { get; set; } = "english"; + /// + /// The language of the text. + /// + string Language { get; set; } = "english"; - /// - /// The base directory for the language files. - /// - public string[] BasePath { get; set; } = { AppContext.BaseDirectory, "uitext" }; + /// + /// The base directory for the language files. + /// + public string[] BasePath { get; set; } = { AppContext.BaseDirectory, "uitext" }; - /// - /// Constructor for the UIText class. - /// - public UIText() { } + /// + /// Constructor for the UIText class. + /// + public UIText() { } - /// - /// Constructor for the UIText class. - /// Loads the language file for the specified language. - /// - /// Language to load - public UIText(string language) - { - Language = language; - } + /// + /// Constructor for the UIText class. + /// Loads the language file for the specified language. + /// + /// Language to load + public UIText(string language) + { + Language = language; + } - /// - /// Constructor for the UIText class. - /// Loads the language file for the specified language and base directory. - /// - /// Language to load - /// Base directory for the language files. - public UIText(string language, params string[] baseBath) - { - Language = language; - BasePath = baseBath; - } + /// + /// Constructor for the UIText class. + /// Loads the language file for the specified language and base directory. + /// + /// Language to load + /// Base directory for the language files. + public UIText(string language, params string[] baseBath) + { + Language = language; + BasePath = baseBath; + } - /// - /// Get the text for the given id and key. - /// - /// The id of the text. - /// The key of the text. - /// The text for the given id and key. - public string GetText(int id, int key) => GetText(id, key.ToString()); + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + public string GetText(int id, int key) => GetText(id, key.ToString()); - /// - /// Get the text for the given id and key. - /// - /// The id of the text. - /// The key of the text. - /// The text for the given id and key. - public string GetText(int id, string key) - { - // Combine the base path and language path to get the full path of the language file. - var basePath = Path.Combine(BasePath); - var langPath = Path.Combine(basePath, $"{Language}.dir"); + /// + /// Get the text for the given id and key. + /// + /// The id of the text. + /// The key of the text. + /// The text for the given id and key. + public string GetText(int id, string key) + { + // Combine the base path and language path to get the full path of the language file. + var basePath = Path.Combine(BasePath); + var langPath = Path.Combine(basePath, $"{Language}.dir"); - // Get all the files in the language directory. - var files = Directory.GetFiles(langPath); + // Get all the files in the language directory. + var files = Directory.GetFiles(langPath); - // Iterate through the files in the language directory. - foreach (var file in files) - { - // Skip files that do not have the ".cst" extension. - if (!file.Contains(".cst")) - continue; + // Iterate through the files in the language directory. + foreach (var file in files) + { + // Skip files that do not have the ".cst" extension. + if (!file.Contains(".cst")) + continue; - // Get the id of the current file. - var ids = Path.GetFileName(file); - var second = ids.IndexOf("_", 1, StringComparison.InvariantCultureIgnoreCase); + // Get the id of the current file. + var ids = Path.GetFileName(file); + var second = ids.IndexOf("_", 1, StringComparison.InvariantCultureIgnoreCase); - if (second == -1) - continue; + if (second == -1) + continue; - ids = ids.Substring(1, second - 1); + ids = ids.Substring(1, second - 1); - // If the id of the current file does not match the id passed to the function, - // skip to the next file. - if (ids != id.ToString()) - continue; + // If the id of the current file does not match the id passed to the function, + // skip to the next file. + if (ids != id.ToString()) + continue; - // Read the content of the current file. - var content = File.ReadAllText(file); + // Read the content of the current file. + var content = File.ReadAllText(file); - // Return the text for the specified key. - return CST.Parse(content, key); - } + // Return the text for the specified key. + return CST.Parse(content, key); + } - // If no text is found, return a default string. - return "***MISSING***"; - } + // If no text is found, return a default string. + return "***MISSING***"; + } } diff --git a/src/Tomas.Kernel/Kernel.cs b/src/Tomas.Kernel/Kernel.cs index 9ce08b6..91048a7 100644 --- a/src/Tomas.Kernel/Kernel.cs +++ b/src/Tomas.Kernel/Kernel.cs @@ -1,6 +1,9 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel; public class Kernel : Os.Kernel diff --git a/src/Tomas.Kernel/Programs/About.cs b/src/Tomas.Kernel/Programs/About.cs index 365c563..83fcb68 100644 --- a/src/Tomas.Kernel/Programs/About.cs +++ b/src/Tomas.Kernel/Programs/About.cs @@ -1,17 +1,20 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel.Programs; public class About : IProgram { - public bool Run(IShell shell) - { - Console.WriteLine($"TOMAS v{SysMeta.VERSION} ({SysMeta.BuildNumber}) is a hobby operating system written in C# using the COSMOS framework.{Environment.NewLine}Commands:"); - var progs = shell.Programs; - foreach (var commands in progs.Keys) - Console.WriteLine(commands); + public bool Run(IShell shell) + { + Console.WriteLine($"TOMAS v{SysMeta.VERSION} ({SysMeta.BuildNumber}) is a hobby operating system written in C# using the COSMOS framework.{Environment.NewLine}Commands:"); + var progs = shell.Programs; + foreach (var commands in progs.Keys) + Console.WriteLine(commands); - return true; - } + return true; + } } \ No newline at end of file diff --git a/src/Tomas.Kernel/Shell.cs b/src/Tomas.Kernel/Shell.cs index 1b49aa5..3a829a4 100644 --- a/src/Tomas.Kernel/Shell.cs +++ b/src/Tomas.Kernel/Shell.cs @@ -1,16 +1,19 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel; public class Shell : IShell { - // The symbol to display before the cursor when the shell is waiting for user input - const char SYMBOL = '$'; + // The symbol to display before the cursor when the shell is waiting for user input + const char SYMBOL = '$'; - // A dictionary containing the programs available to the shell, with the keys being the program names - // and the values being the program objects - public Dictionary Programs => new() + // A dictionary containing the programs available to the shell, with the keys being the program names + // and the values being the program objects + public Dictionary Programs => new() { {"about", new About() }, {"fensay", new FenSay() }, @@ -18,20 +21,20 @@ public class Shell : IShell {"commands", new Commands() } }; - // A property that allows the shell to read a line of input from the user - public string ReadLine - { - get - { - // Write the SYMBOL character to the console - Console.Write(SYMBOL); + // A property that allows the shell to read a line of input from the user + public string ReadLine + { + get + { + // Write the SYMBOL character to the console + Console.Write(SYMBOL); - // Read a line of input from the user - var readl = Console.ReadLine(); + // Read a line of input from the user + var readl = Console.ReadLine(); - // Return the line of input - return readl; - } - } + // Return the line of input + return readl; + } + } } diff --git a/src/Tomas.Kernel/SysFS.cs b/src/Tomas.Kernel/SysFS.cs index ae905f5..5b5865a 100644 --- a/src/Tomas.Kernel/SysFS.cs +++ b/src/Tomas.Kernel/SysFS.cs @@ -1,156 +1,159 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Kernel; static class SysFS { - // The root directory of the file system - public const string ROOT_DIR = "0:\\"; - // The system directory, located in the root directory - static string SYSTEM_DIR = $"{ROOT_DIR}\\SYSTEM\\"; + // The root directory of the file system + public const string ROOT_DIR = "0:\\"; + // The system directory, located in the root directory + static string SYSTEM_DIR = $"{ROOT_DIR}\\SYSTEM\\"; - static string LOG_FILE = $"{SYSTEM_DIR}system.log"; + static string LOG_FILE = $"{SYSTEM_DIR}system.log"; - public const string FS_ERROR = "File system disabled."; + public const string FS_ERROR = "File system disabled."; - /// - /// An instance of the CosmosVFS class, used for accessing the virtual file system - /// - static CosmosVFS fileSystem = new(); + /// + /// An instance of the CosmosVFS class, used for accessing the virtual file system + /// + static CosmosVFS fileSystem = new(); - /// - /// Initializes the file system by creating the system directory and sysinfo.txt file - /// and setting the IsFSActive property of the SysMeta class to true - /// - public static void Initialize() - { - try - { - var createSysFiles = "Creating system files."; - var setSysPref = "Writing system info."; - var fsSuccess = "File system succesfully initialized."; - var sysInfoFile = "sysinfo.txt"; + /// + /// Initializes the file system by creating the system directory and sysinfo.txt file + /// and setting the IsFSActive property of the SysMeta class to true + /// + public static void Initialize() + { + try + { + var createSysFiles = "Creating system files."; + var setSysPref = "Writing system info."; + var fsSuccess = "File system succesfully initialized."; + var sysInfoFile = "sysinfo.txt"; - // Register the CosmosVFS instance as the virtual file system - VFSManager.RegisterVFS(fileSystem); + // Register the CosmosVFS instance as the virtual file system + VFSManager.RegisterVFS(fileSystem); - // Create the system directory - if (!Directory.Exists(SYSTEM_DIR)) - fileSystem.CreateDirectory(SYSTEM_DIR); + // Create the system directory + if (!Directory.Exists(SYSTEM_DIR)) + fileSystem.CreateDirectory(SYSTEM_DIR); - // Create the system.log file in the system directory - if (!File.Exists($"{SYSTEM_DIR}{LOG_FILE}")) - fileSystem.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); + // Create the system.log file in the system directory + if (!File.Exists($"{SYSTEM_DIR}{LOG_FILE}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); - Console.WriteLine(createSysFiles); - File.AppendAllText(LOG_FILE, createSysFiles); + Console.WriteLine(createSysFiles); + File.AppendAllText(LOG_FILE, createSysFiles); - // Create the sysinfo.txt file in the system directory - if (!File.Exists($"{SYSTEM_DIR}{sysInfoFile}")) - fileSystem.CreateFile($"{SYSTEM_DIR}{sysInfoFile}"); + // Create the sysinfo.txt file in the system directory + if (!File.Exists($"{SYSTEM_DIR}{sysInfoFile}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{sysInfoFile}"); - Console.WriteLine(setSysPref); + Console.WriteLine(setSysPref); - File.AppendAllText(LOG_FILE, setSysPref); + File.AppendAllText(LOG_FILE, setSysPref); - // Write the system name, version, and build number to the sysinfo.txt file - File.WriteAllText($"{SYSTEM_DIR}sysinfo.txt", $"{SysMeta.NAME} v{SysMeta.VERSION} ({SysMeta.BuildNumber})"); + // Write the system name, version, and build number to the sysinfo.txt file + File.WriteAllText($"{SYSTEM_DIR}sysinfo.txt", $"{SysMeta.NAME} v{SysMeta.VERSION} ({SysMeta.BuildNumber})"); - Console.WriteLine(fsSuccess); - File.AppendAllText(LOG_FILE, fsSuccess); + Console.WriteLine(fsSuccess); + File.AppendAllText(LOG_FILE, fsSuccess); - // Set the IsFSActive property of the SysMeta class to true - SysMeta.IsFSEnabled = true; + // Set the IsFSActive property of the SysMeta class to true + SysMeta.IsFSEnabled = true; - // Read the contents of the sysinfo.txt file and print it to the console - var systemInfo = File.ReadAllText($"{SYSTEM_DIR}sysinfo.txt"); + // Read the contents of the sysinfo.txt file and print it to the console + var systemInfo = File.ReadAllText($"{SYSTEM_DIR}sysinfo.txt"); - Console.WriteLine(systemInfo); - } - catch (Exception err) - { - // If an exception is caught, print an error message indicating that the file system failed to load - Console.WriteLine($"{err.Message}{Environment.NewLine}Warning: Error messages will not logged."); - } - } + Console.WriteLine(systemInfo); + } + catch (Exception err) + { + // If an exception is caught, print an error message indicating that the file system failed to load + Console.WriteLine($"{err.Message}{Environment.NewLine}Warning: Error messages will not logged."); + } + } - /// - /// Creates a new directory at the specified path - /// - /// directory - public static void CreateDirectory(string directory) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); + /// + /// Creates a new directory at the specified path + /// + /// directory + public static void CreateDirectory(string directory) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - // Create the directory using the CosmosVFS instance - if (!Directory.Exists($"{ROOT_DIR}\\{directory}")) - fileSystem.CreateDirectory($"{ROOT_DIR}\\{directory}"); - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); - } - } + // Create the directory using the CosmosVFS instance + if (!Directory.Exists($"{ROOT_DIR}\\{directory}")) + fileSystem.CreateDirectory($"{ROOT_DIR}\\{directory}"); + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); + } + } - /// - /// Creates a new file at the specified path - /// - /// file path - /// file - public static void CreateFile(string path, string file) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); + /// + /// Creates a new file at the specified path + /// + /// file path + /// file + public static void CreateFile(string path, string file) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - // Create the file using the CosmosVFS instance - if (!File.Exists($"{ROOT_DIR}\\{path}\\{file}")) - fileSystem.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); - } - } + // Create the file using the CosmosVFS instance + if (!File.Exists($"{ROOT_DIR}\\{path}\\{file}")) + fileSystem.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); + } + } - /// - /// Lists all directories in the specified path - /// - /// path to directory - /// returns a list of directories - public static string[] ListDirectories(string path) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); + /// + /// Lists all directories in the specified path + /// + /// path to directory + /// returns a list of directories + public static string[] ListDirectories(string path) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - // Get the directories in the specified path using the Directory.GetDirectories method - var dirs = Directory.GetDirectories(path); + // Get the directories in the specified path using the Directory.GetDirectories method + var dirs = Directory.GetDirectories(path); - // Return the directories - return dirs; - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); + // Return the directories + return dirs; + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); - throw; - } - } + throw; + } + } } diff --git a/src/Tomas.Kernel/SysMeta.cs b/src/Tomas.Kernel/SysMeta.cs index 86d4b83..0153a0e 100644 --- a/src/Tomas.Kernel/SysMeta.cs +++ b/src/Tomas.Kernel/SysMeta.cs @@ -1,3 +1,9 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ using System.Text; namespace Tomas.Kernel; diff --git a/src/Tomas.Terminal/GlobalUsing.cs b/src/Tomas.Terminal/GlobalUsing.cs index a3f2539..5232722 100644 --- a/src/Tomas.Terminal/GlobalUsing.cs +++ b/src/Tomas.Terminal/GlobalUsing.cs @@ -1,3 +1,9 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ global using System.Diagnostics.CodeAnalysis; global using System.Diagnostics; global using Tomas.Common.Programs; diff --git a/src/Tomas.Terminal/Program.cs b/src/Tomas.Terminal/Program.cs index 80f945a..f95cf7d 100644 --- a/src/Tomas.Terminal/Program.cs +++ b/src/Tomas.Terminal/Program.cs @@ -1,40 +1,43 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Terminal; class Program { - static void Main() + static void Main() + { + while (true) + { + var shell = new Shell(); + var command = shell.ReadLine; + var programs = shell.Programs; + + if (!programs.TryGetValue(command, out var program)) + { + Console.WriteLine("Command Not Found."); + continue; + } + + try + { + var start = program.Run(shell); + switch (start) { - while (true) - { - var shell = new Shell(); - var command = shell.ReadLine; - var programs = shell.Programs; - - if (!programs.TryGetValue(command, out var program)) - { - Console.WriteLine("Command Not Found."); - continue; - } - - try - { - var start = program.Run(shell); - switch (start) - { - case true: - continue; - case false: - Console.WriteLine("Program closed unexpectedly."); - continue; - } - } - catch (Exception err) - { - Console.WriteLine(err.Message); - } - } + case true: + continue; + case false: + Console.WriteLine("Program closed unexpectedly."); + continue; } + } + catch (Exception err) + { + Console.WriteLine(err.Message); + } + } + } } \ No newline at end of file diff --git a/src/Tomas.Terminal/Programs/About.cs b/src/Tomas.Terminal/Programs/About.cs index 6c716fa..b6cb3b7 100644 --- a/src/Tomas.Terminal/Programs/About.cs +++ b/src/Tomas.Terminal/Programs/About.cs @@ -1,14 +1,16 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. -using Tomas.Common; - +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ namespace Tomas.Terminal.Programs; public class About : IProgram { - public bool Run(IShell shell) - { - Console.WriteLine($"{TermMeta.NAME} Terminal Emulator v{TermMeta.VERSION}"); - return true; - } + public bool Run(IShell shell) + { + Console.WriteLine($"{TermMeta.NAME} Terminal Emulator v{TermMeta.VERSION}"); + return true; + } } \ No newline at end of file diff --git a/src/Tomas.Terminal/Shell.cs b/src/Tomas.Terminal/Shell.cs index 097c79f..f9a3989 100644 --- a/src/Tomas.Terminal/Shell.cs +++ b/src/Tomas.Terminal/Shell.cs @@ -1,14 +1,18 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ using Tomas.Terminal.Programs; namespace Tomas.Terminal; public class Shell : IShell { - const char SYMBOL = '$'; + const char SYMBOL = '$'; - public Dictionary Programs => new() + public Dictionary Programs => new() { {"about", new About()}, {"fensay", new FenSay()}, @@ -16,13 +20,13 @@ public class Shell : IShell {"commands", new Commands()} }; - public string ReadLine - { - get - { - Console.Write(SYMBOL); - var readl = Console.ReadLine(); - return readl; - } - } + public string ReadLine + { + get + { + Console.Write(SYMBOL); + var readl = Console.ReadLine(); + return readl; + } + } } \ No newline at end of file diff --git a/src/Tomas.Terminal/TermMeta.cs b/src/Tomas.Terminal/TermMeta.cs index 7eb564e..8451cdb 100644 --- a/src/Tomas.Terminal/TermMeta.cs +++ b/src/Tomas.Terminal/TermMeta.cs @@ -1,48 +1,52 @@ -// I license this project under the BSD 3-Clause license. -// See the LICENSE file in the project root for more information. +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ using System.Text; -namespace Tomas.Common; +namespace Tomas.Terminal; /// /// System metdata, such as name, version and build number. /// public struct TermMeta { - /// - /// The name of the operating system. - /// - public const string NAME = "TOMAS Emulator"; + /// + /// The name of the operating system. + /// + public const string NAME = "TOMAS Emulator"; - /// - /// The version of the operating system, in the Calendar Versioning format: "yy.minor.patch". - /// The year, minor, and patch version numbers are automatically extracted from the Git repository - /// using the ThisAssembly.Git.SemVer object. - /// - public const string VERSION = $"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Minor}.{ThisAssembly.Git.SemVer.Patch}"; + /// + /// The version of the operating system, in the Calendar Versioning format: "yy.minor.patch". + /// The year, minor, and patch version numbers are automatically extracted from the Git repository + /// using the ThisAssembly.Git.SemVer object. + /// + public const string VERSION = $"{ThisAssembly.Git.SemVer.Major}.{ThisAssembly.Git.SemVer.Minor}.{ThisAssembly.Git.SemVer.Patch}"; - /// - /// The build number of the operating system, generated from the commit hash. - /// The build number is a 6-digit number, with the first 3 digits being the first 3 digits of the commit hash - /// converted to a uint, and the last 3 digits being the last 3 digits of the commit hash converted to a uint. - /// - public static string BuildNumber = $"Build {BuildNumFromCommit}"; + /// + /// The build number of the operating system, generated from the commit hash. + /// The build number is a 6-digit number, with the first 3 digits being the first 3 digits of the commit hash + /// converted to a uint, and the last 3 digits being the last 3 digits of the commit hash converted to a uint. + /// + public static string BuildNumber = $"Build {BuildNumFromCommit}"; - /// - /// Generates the build number from the commit hash. - /// - /// The build number as a uint. - static uint BuildNumFromCommit - { - get - { - // Get the bytes of the commit hash as a UTF-8 encoded string - var commit = Encoding.UTF8.GetBytes(ThisAssembly.Git.Commit); + /// + /// Generates the build number from the commit hash. + /// + /// The build number as a uint. + static uint BuildNumFromCommit + { + get + { + // Get the bytes of the commit hash as a UTF-8 encoded string + var commit = Encoding.UTF8.GetBytes(ThisAssembly.Git.Commit); - // Convert the first 4 bytes of the commit hash to a uint and return it modulo 1000000 - // (this will give us a 6-digit number with the first 3 digits being the first 3 digits of the commit hash - // and the last 3 digits being the last 3 digits of the commit hash) - return BitConverter.ToUInt32(commit, 0) % 1000000; - } - } + // Convert the first 4 bytes of the commit hash to a uint and return it modulo 1000000 + // (this will give us a 6-digit number with the first 3 digits being the first 3 digits of the commit hash + // and the last 3 digits being the last 3 digits of the commit hash) + return BitConverter.ToUInt32(commit, 0) % 1000000; + } + } } From ac28f2b9a445a8c64918766efa0a46310d771a63 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 11:48:46 -0500 Subject: [PATCH 4/9] Global usings now reference Tomas.Core instead of Tomas.Common --- src/Tomas.Kernel/GlobalUsing.cs | 4 ++-- src/Tomas.Terminal/GlobalUsing.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tomas.Kernel/GlobalUsing.cs b/src/Tomas.Kernel/GlobalUsing.cs index 6380375..b1bfac8 100644 --- a/src/Tomas.Kernel/GlobalUsing.cs +++ b/src/Tomas.Kernel/GlobalUsing.cs @@ -6,10 +6,10 @@ See the (UN)LICENSE file in the project root for more information. */ global using System.Diagnostics.CodeAnalysis; global using System.Diagnostics; -global using Tomas.Common.Programs; +global using Tomas.Core.Programs; global using Tomas.Interface; global using Tomas.Kernel.Programs; global using Cosmos.System.FileSystem; global using Cosmos.System.FileSystem.VFS; -global using Tomas.Common; +global using Tomas.Core; global using Os = Cosmos.System; \ No newline at end of file diff --git a/src/Tomas.Terminal/GlobalUsing.cs b/src/Tomas.Terminal/GlobalUsing.cs index 5232722..bbcdb99 100644 --- a/src/Tomas.Terminal/GlobalUsing.cs +++ b/src/Tomas.Terminal/GlobalUsing.cs @@ -6,5 +6,5 @@ See the (UN)LICENSE file in the project root for more information. */ global using System.Diagnostics.CodeAnalysis; global using System.Diagnostics; -global using Tomas.Common.Programs; +global using Tomas.Core.Programs; global using Tomas.Interface; \ No newline at end of file From f006ab41bf96cbee6a83f17dbdd2339ce44e72f5 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 12:39:07 -0500 Subject: [PATCH 5/9] Issue and pull request templates - Added code of conduct - Code climate badges --- .github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md | 17 +++ .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++ .github/PULL_REQUEST_TEMPLATE.md | 7 ++ .github/workflows/dotnet.yml | 4 +- README.md | 13 ++- code_of_conduct.md | 132 ++++++++++++++++++++++ src/Tomas.Kernel/Shell.cs | 36 +++--- 8 files changed, 246 insertions(+), 21 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 code_of_conduct.md diff --git a/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..65667de --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +## Expected Behavior + + +## Actual Behavior + + +## Steps to Reproduce the Problem + + 1. + 1. + 1. + +## Specifications + + - Version: + - Platform: + - Operating System: \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..d5e3ee4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +# Fixes + +## Proposed Changes + + - + - + - \ No newline at end of file diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 7282312..21f7542 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -4,9 +4,9 @@ on: schedule: - cron: "0 0 * * 0" push: - branches: [main, develop, "feature/**"] + branches: [main, develop, "feature/**", "release/**"] pull_request: - branches: [main, develop, "feature/**"] + branches: [main, develop, "feature/**", "release/**"] jobs: build: diff --git a/README.md b/README.md index ef1e671..d0e8613 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,17 @@ # TOMAS - +

+ GitHub license + GitHub Workflow Status + GitHub commit activity +
+ Contributor Covenant + Code Climate maintainability + Code Climate technical debt +

+ + + TOMAS (**To**ny's **Ma**naged Operating **S**ystem) is a hobby operating system based on the [COSMOS](https://github.com/CosmosOS/Cosmos) framework that comes with a respective terminal emulator. diff --git a/code_of_conduct.md b/code_of_conduct.md new file mode 100644 index 0000000..1b9bfcd --- /dev/null +++ b/code_of_conduct.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/src/Tomas.Kernel/Shell.cs b/src/Tomas.Kernel/Shell.cs index 3a829a4..85b109a 100644 --- a/src/Tomas.Kernel/Shell.cs +++ b/src/Tomas.Kernel/Shell.cs @@ -8,12 +8,12 @@ namespace Tomas.Kernel; public class Shell : IShell { - // The symbol to display before the cursor when the shell is waiting for user input - const char SYMBOL = '$'; + // The symbol to display before the cursor when the shell is waiting for user input + const char SYMBOL = '$'; - // A dictionary containing the programs available to the shell, with the keys being the program names - // and the values being the program objects - public Dictionary Programs => new() + // A dictionary containing the programs available to the shell, with the keys being the program names + // and the values being the program objects + public Dictionary Programs => new() { {"about", new About() }, {"fensay", new FenSay() }, @@ -21,20 +21,20 @@ public class Shell : IShell {"commands", new Commands() } }; - // A property that allows the shell to read a line of input from the user - public string ReadLine - { - get - { - // Write the SYMBOL character to the console - Console.Write(SYMBOL); + // A property that allows the shell to read a line of input from the user + public string ReadLine + { + get + { + // Write the SYMBOL character to the console + Console.Write(SYMBOL); - // Read a line of input from the user - var readl = Console.ReadLine(); + // Read a line of input from the user + var readl = Console.ReadLine(); - // Return the line of input - return readl; - } - } + // Return the line of input + return readl; + } + } } From 6fb029b76ed3d81c3879b44dae38be17289c068e Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 13:09:52 -0500 Subject: [PATCH 6/9] With the help of DALL-E 2, I now have a cute logo that I extracted the vector from with another AI tool --- README.md | 7 +- assets/logo.png | Bin 0 -> 25392 bytes assets/logo.svg | 246 ++++++++++++++++++++++++ screenshot.png => assets/screenshot.png | Bin 4 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 assets/logo.png create mode 100644 assets/logo.svg rename screenshot.png => assets/screenshot.png (100%) diff --git a/README.md b/README.md index d0e8613..05975a1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # TOMAS

+ +
+ +


GitHub license GitHub Workflow Status GitHub commit activity @@ -10,9 +14,6 @@ Code Climate technical debt

- - - TOMAS (**To**ny's **Ma**naged Operating **S**ystem) is a hobby operating system based on the [COSMOS](https://github.com/CosmosOS/Cosmos) framework that comes with a respective terminal emulator. ## Requirements diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..012253881ebc9ce0d70fb030c46546d2c5e5da9f GIT binary patch literal 25392 zcmeFY^K&NO7cKn6PM+A#C$>2;C$`OrZQJ(5nAo=MN%F*;*mm;fTW{U};Qn~K>Qq;s zR-Lo=UcL6(ry~{RC6VCq-~j*tlC+eVG5`Rk|Gx$c{oON7=Z^QigL9D5as~j92mjZ= zM(xW?zdLbV#5G-1?9E-=jhxH??(Xi4R(96TCPofsjP_0z*_Zs^cai|4#e`KoexK*K z`(T)Tj`q%`RIl1_!jY0f!ZO@fL`1+x6o6tY{v1*(dGpOCJazFnSA@%IWE8Z~BKbT z9uXn|!a}~}qv?Tx2MQhA19$)XKF%Z^`1#$>DjJghecm(-mMKt(WDF5i|NEK*T(T}m z@IVqaF%LLMaAEU^YcyEMpsD|Nku9VHBEynmoVc@$aHqMF0%30x?Vf(f4Db?C zWknMrk3SCrVLwoLE?-RBwv**O4$#(xC~_Iyh4a| zbU(?9g}{9zFb{F+lE(g%TM=bW90vOl{nq_Efm=V47`{I;I#MK~jQO#M;t}QHt>@ux zbL*&!Kg5v-HY9~S=BuOkjWXqJcKhSpJrCQ!N6GadRfARdF&Hp8I?I%D2Bc}w>GBf; zxwnIS4kl8y*vzLgisvmU*$~-c3d?i>H~@flNW$q#501$O1OQnHV;xG^a^JIO{=`LH z<9nOU73iF7%+v7P<;N^p{;)mkhNusLvl78T+UeiYgy`Ayd)^JlJ^JlDqgk!4Gt^*1 zDy}3-6QxWZ(f|+&1WpuC|DqPL1Om8OMlktj({K2DVNA{;0Z_Ad;pWp>4}U*D-iA#^ zQx}5tpMG^+>S={?1su!F=xA$G9vmD9i2uER0h(E$#5lH6M>Q8xtHT8;+Hz*Q*JwBY z0wHm27(Sfuv7t24H(%OkyAwtL(3Ru3BW?Q*Dk>_BLPJB3%2X@Ztg>=rfOudm9-`Qg zMoUb+?@oU=+MPS|uLPK~pC>50NTV)q%sMxF6sy&H0XG+VR1t-myDq%F)|QrMtB|bGwe$n{VaH_Lv>mPGOQ*e~-Nrv)gX#t^+-mDrZTKc9C*5aq_*&76 z{ZkavA#C|x!B7oFl2FAQ$We91ZP#1vnW$-Kcyx;9r_68;=|dF#AeUlcl`wu9c%78+ zL}N3#32l4xU+&?%?;>fq(9zOL#1itk!2rrg`VC{wc;en_>g#WMizH$z0_ODP23!?9 z(j~<*5`cTuEwFYp_>fwI@+H&<<#-&){x950KG!ZCrXpY*JXySw(US*WDkaJSkN~m} zlAdzA*0#X%H%ov$J+(C&CCLR}vUHg(w0c(XNERzaOLKc;1iC8)Nlir;Lb&5@`` zMkb*9h^3S30Ank^@NL6+_5_PbUpF4OFw(57q_{@LIrT~fD^H{=mKrmEc7EQ)PepZ9)TTx)0ZRt!|CYM_a<5GbB2^>@)Zs?H ze~E>ks65{u<`o=xg9W{XC6{6&6?_3D?B^A$)=egZY1;gsGNsHkAtQLgOC0Xr#B_zjkMS?z@Yj3{i zO_UF*aybHy%avvIsm`tN7QL%J$;NeoPLY3pyC!C+V3cC=DM54}EnYeY)U?(s92rWV zU;rdFw!OeVvsj&%dDF5MWsBnvn`>)c7x}`$JrG|5_|7+}Djp?6>9<)bK_utjVJW6d zi(!$LdTZ9|D_LGepyp(gj1YM_D?V8O+O6J4a_-lbj|89_%OFBM9pAoOSqwbW)e2X= zY75;X0sxo{%Yxo<9{;vhr=&4tx|M9t-QN70v@Zl4L|lBKaQa3M5>me1zS+AAVP~P_ z=flr5daEc;GgC;DKBljMug4+njs)~M!&!6S%hHP5KQ#}NIZ6_6*@Qq?NUpj97mH~e z8Loe`8OrZqzy&Zs9fqCXDSd(C4S~4?Eqo1(5(dfwy8xzqgx!W7awOW8ei|b{vD5u3 zm&NsTG0#{(6S5DkkKwhydq1z2#Y9YqXH>9nk8vtfKQzAf6Kf6R@fJAv07JYfHIz;w z98rkhmWT-G&p%<;?LGuAUfc$Y$lZTI1moQ1R$FKkvqzK4hwu^M{O%6R1N-<+2jvs;$>O=56}hwLWt z6+gEm>%0>G2UEsEQ;@Cy%%R>d?KMbCF2VB4>h-rV&5;gUeSHPkI2909St{I=>+Ci= zaPJPME5QI@B28Y&G9IEXY>GW9Iy&om5HQHPcz-7-;Bq)OoHtqhzD|SBY33>s*g1`7 zK3S^_?on1A;;0Cw^8hd|g20=L?T`d>j<%f{KYa)`?5zNdtT(`%;uXXRhVg?t2y6=1 zuM3I1x~#msy`#9;l!E|MJO26g)yL$}E@6-fJ?UYx!A4s^xO;r5u!{I=DED#y!KXS1 zt^0k28lR?Vw!nFN3+k!G0N|^~tPFzBf8r462rMo-Pk(;dEWvB+PWM*WuT|}XNE0mh zOS~G6v8K^2jt$nbu*`pphda?-K%?h%nPj zN__cmN^>85jJa~|!pf8nzEFb1c1y#u-uu4|M?>)YhftUfPcPx2R)64*Fn-idu1DWC zh!;h#$p&B9ybDTFg_elQU@(1&9{kQ4yRp#{{`mQ~^tl7>PaWCp!%Nl+?^61WD_V>w zV_2JW5A+8CEQM&U6v5$}$kLO#-}jm>2&EVP8)j=y=lX$XZekj0FLTrhk08EPwolJQ zZxpDuN>Dfk8w{?5O$m`>4`MpQEVR{4s+ZciYjxy9_7)^i*Z(WF=c~v&dsZkOK0A@} z3nstprW+-sI)2K{-qtTBlb!5OD-IRDCWe^G#hbTWW%#Aic~(~K5vS~E>84NHsFBt( z;B$NIW9w()mIcehO&HGfj=+3w`?#qWIUAd^aG6ZKbA+9+^w zN>|ISo{Jmlx*yS}S^_q`<$Iw{iLLF0l}JFoE2)8jzq$w@ZUyrV-{7GQF9Hb>V!kBz+#4 zpW@<1Gf~IsJC;k;I7ad)FN#|cmGEuyZ&`r*fpvGWo1w_j}{h4mb z(WzAqIfs1S&;t9h$@*dq^ZAXtPGi% zq-;PpRzATYzeR2>f6tULTR($FI-`bf@ke2^R#^T$tujb6c;IxQRIYk72p0`9IeC@f zmg;s4^R@r4p*8X|Z z%i$>bd`wW*!4^Db^9ONdBJ2LwoY}TbU?=iSKo3e?f^Y8)AB0F1oQlhvyoR4qK9OU0 zXcL6SGy3Md*2d-GuvBtP;pBRjoL+YA)_CnSGk%AD21e8|IKXfy?CVrPNgH-{FZNVF zD)ajF<|-)9R+;L*5EY_fOH3TmF?koveOhMjp4_2J9E-XtMsCA$uM2=7X*s=682I77 zb&AEi3%S3dG+g-Gc|aMQltEXV*VqmG?>A*iNIS#?Cs80sGzw@Ok@3Yf@y5$2MqaxT zYBfAf;wVVgmu4}0=%yx6oRL;3o_WI?FI7jpy0p=3cgWb}Sw}m1^CAr|F$MR@z@#Xa zb7FaYh5XP;#NwpW^$7_US$K^2WC$){;x{c%1xvm`_ zT`gKonxQHK`-8#VDr}Gr7Ob^IX!%-L?I8I<1MGNMM=gF%ix=LatvYvlE)+a`Q z1>rD)wSvn&$Wt=Ik8 zT6{J%vzter@(6l;NBPU zNqmY^<(AhpIz~p5FFV#ys36e=^dLcyx6ak})68k43V8}PjcOFkxLVqp*C8Br(i&_uc}ke^%TF?`BEt0P5Tx0B(Y|P z?$qc_8HYJHaL%jig&-)Rb|YkG_Cb*0vD@lbu-^4UdWbk9mP&mf3b@ZO4`$cZ`dcKclpD7o?aN4&_R563;vM7Z4rghFfjOmqbVrotV$QOu`tHLHz|` zv(unG`P}bx|Bzvfan!A5(Gjw2C5~Go1_%dLv6FkEf?tF>~KTXT_wWZ5#=+ zmD8ceqS=jI_f4>je&Qx1B)sT#6L=0tX=rqVPePd}HUw-cO;+cFj6M`3HDU7HmzcY~GY9kTk)s1Lc-NMPu;~)1ta!lRMwhAB3VHQ0PYFrk!n)w;(X{!AzD1 z21f2k*ysQA+vY}7s|oeiW)kTjr2`~v+ucHkc2-;(xfQD?wvjH`#(q`0S1q(!=xpW4 zs%SoXwK0k9L$Ri}qh4OP$@X1=J}C&-T0hh!-iNWrWxHla-@1Ibq^I8CFO}v$$d&Q8z_0Tn_=9(m7X_qH zSj)<0aLgcBtwwjB+eD@D8VH?L- z2>mimK_`A$w>0H_TBn(3NjRY1=CV3Tr!$_Dd0V-SHtGmtK>OhW9Wt z#~Bf|`-?I&#LRs78)5jM64a+SC*xi=^>+~Ac5dq9Uj{ab*5UwtU5$gYT(Qhm^%NIh z&M-?NqkNwCcpu8K5nJCHeNdInx)Gy0?819~Y$xDt#t~7p35}aty?P78fA*m+^NoEL zL|n2wiHw35PkR%4A;qoXV>)pWctng({8a+`TQmY8X<^mrswS_(_%zrgrqfO&hS=4z z6H~`$s#(4Kt9D691fLBx?JOu%)u?e*7yU9YRSO-u!{bouSuF|$7A)sX_s4L9*N$-O zK|Twr>+`2VR;F{*dj}y<*{l~aS{4NyG2wIGlgUMH9b2DTh5v%5zqdCeb`$O-uJ`603=FJKcOhjtV<+FR zjPwkrnGgu9G1c3t=CiM+QMw0*CuLO^?op3c_K}n^)V*B5QZ=rkMuY6IQ9W+~ALcU> z0^bf5;BzybYO(%73CR9i!^;XB44g-VN*gZ;z=Msz3t(9Q4`mM!MisH*k64KUazX^? zVT9WC`S8K&A~?)&GPiOLNK$$7UhM2|pLP?$1?nQ^n!$f|`Gd2@_(1^K$XCErf5-_Muwry`@=$m&5^rG61h1kv+-6?LO9r6X?uG+;(I49xNC95FOMe=gxV|O zwWr1(wGFPWuANa)B6xpmuqRNT%!D5lP}P*>f#AIBpC>7o9DGk_N0)fg^|%f9<$1be zh_AQ5G!8H)G4O=IZX3wV;qlm=@%{4Oq&>{;djWA$?S#M(DtTpNNQb*EkvU9RfcOYx zRKci8J{6!)0z!ysvP40+fS1&=R*=N?JZ05Ha+Uz{I>Qp0ewl<`JvvAc%I=KFa=QWj z$6NQG_3|U#|0+oXd&9@!=-l@RX-~T}h(@Yj15KLYe1zyTIJams^a;o^h#F7_X$e0{sR!`z;-GN2H!aMBob zTJQ(lf(_4ghw;=Lr|xjAZGJy8^LfXsC-TU&@Cvpsv{;{*U$Dc9_lWtS+im($`EYlF zz3YH~ehPKR```T0QuLV_&!6DNB|S9FKTZZqsQSDF_5+>)XfHX1>&;+Zzx=|UO1U0C z`6*tbE;o_b5^Ihb{9l~kh=kZ{J0k^IEs8~2d^*5ECapDuOJy1^X=7^UO&QNYv>%=f zqOvR!a-ek z{Cvaf{`b9pLTO&@hYR;)0nroF{*-p80NELf()~XshS{t-igVJ~j*gC=+3D$S?02pz zPcaaiG#)V=wmv}m25FIbVRR1)&{0&h{{jX9txIUPx;(hOY__PI1-be-I5=X3TsdPm zRg${eVCsw}280$otNP_$0WQw_>@(gC#%0rm{zLE&YH^7BL@a#9-MdxF0yDA7If2lk zhm;Rv0(Ssk;*LHkZ_MQG-3xw!Z>=iIFuKI(%N5?-zq4%!8Am(r(Dx4?$<@uqrH1ZP zzSuWj!#DOSnWUQD0p86b^#bJYEY!k$A0b)RpRwI{KH>jQXOgAE1~24y?6-oQ7;f4#|0ZLC_kR{Xt`iPe zH0xSmi^*v1wglkh+C5b=3K29?R@}yDI|Nd5uGdwiIOd@M%=L}G$WZkF0nOGRyWl>S z&ZJiiX|0|y+nZPI?j*mhlAr0wdJU%IBT^GwcD?27>$k!(?C{UVXklt+vjWWUK!#{b zTi%_Dyt^MdfC%`f_(_NnWFhL^!EfCXJmT_UW&VzkoA(hy1fztG7o`!`9SojHZhuVn zE%!8>Lj{;L%COITX)oC;SD=cR$IhcL0O0#`X`^u_Xhdue^5PiuGg$KCt;`zT49KWKk8 zUTY99fWJ04aLTouo97$YmzFRShbMm_xKKKZnd-$S2k5SFl;45qWDaPr<~`TSoe>!L z{?W#hAVGCG&WdPLr62QlOz@UnBr7j1Kmh={?=jmoOZ{cl)tivMzAeh=LNOZ%TSK}Y zgC5|J{PVZBw@8W0fke&hM*DKZLApn`<0E<~GVvu?FzVa6EDQ=p>r@(^ki^Vx)op24 zcsv1#;*g$_olt&!;t@x{vzw8_W%g6aZ49L$n30ukmy?C*o!)Mm%kGtLtqc0}Ww)~% z1l4ww>+R+d>q?B3Iao_%<$8u9al1(Yh8t5Wd%X1}6G{-u!e02qsElO9lJ26l^-Was zeyu-sI?`$ljMRFj|Q0pjTd z2dHsQkA{P*wjF=XgDlCe@pVsE9%RKW?$g*D{7i=I$2x z>-n$6+S8;G78r@fZke`cwojq;hL4#Qqgq-Ls^C@|d^!%8QsOV{|D`D!q7Gc5%&nrT z-@yPKv;Cm=^)Fe0Q>d6Kj(URUdTS=aI*WoCVWjHq%YJ3b=@Lz7um+czoF+fx!qxi4qp$zVqyc|ej7_Xn z&(MJ+%HA8S`3P~IzR%6DdiHb+--4H^oR@hOTVxni6b9zx(HfWBX&)K35_GA<%ZvPG z3c*NPLL%EMOvcwQFbjgL=`mKD_(|RMavqmiL#ZXkb$y57T20`vSe4H7^z_zpjbN+D zTpYSmTpPI#cV_QJzdRZU1o{G6^+%{T#0kf@enZmBxM`<^TP>eB7K^FX(0PvnO9Hg} zL@x^kd8-^}`iEbkS22N^(L94gwnc^}^dBD!?8bG)KTPf9g>8B$lj%(9X!=?ooq%z+ ziMs9R>ox1z>LDD2L@d*c=OONF9dGKrEK1bXG8dmC`=>E9b9Gb0M)i+*Te4W&dNb89 zvn#>cg-K9G%gKNf;d-*u%TJ2F`;ae*5)HRb->ahdA8UaAP%gV3Jrt*SA=Jg%GrsPp zEpNSfF)lzMb-_P9EhmyO;g*xw`nWiT26nt$0d+Rxae4$Rn`l&-bDLGSJoT0c@X1}w z2cGw=l&3RrfC~aSU6VMrSZZ=xPmdoIB#6*{kFv)ReGB%ZD+#-}rG?Yj9ReV9cx^3_ z`4UvI<4q}u0j|E`$;g!6`^1EO`X}f2dLz|yZio;GJul}5e4#Ld!Yq5Dd)tXG0tPkG zdq)r~;{y#{q0CM&3zokB#{)5GTPVK!7h^!zuBr06NLH@LUL*(3ewn`K>WRUb7-B<& zTGu?SjcA9T9*K@ZqXE__6%O^!YTbhp^4aNT0`3w`4$YcduNK{QgCYm42{nEGlkv6= zkyH-OisBc_h zZO8CoTThOmy&hHcm3BXg+w4aL0bXb4KWe7UjKp=l*D;>gI&@dbsP2n&%HT79^nbKW zx!fBQoP#k>ML*I%-k$ssjBvaYCVuF{C2wq_Qyz=2wAijUSe_*doK4=8XVhxIhXnMO zq#Fg`{7JtXOt9hfUQMmB1JI{FXYasptl<0i{3LeVu%du1N#{kCsR*bRm0;6Y*fsQ$y5@BT`KC;a>0 z&ob?3K4M@psjn=R(AGz4$f?!BzBtUS+3)(Cg0M#E6uV zfSlK}*6+I#i+0szF`KJUaAlafbq4T-;<{Y6FVsT<7`5R80JWO+d;6Lg6?8pLrs;~U!|N$K7RGnr(=zh#<{ zhSVSQvxVCX9_(Q<4;Nyfi{p9gBfL_=HPwJsi>mT~rlVRu$4pDLyCVrKMtEn0zHtFw zRF*e$acRldh8=Gcn3-LWZa%y)4M=DcJ61kZ#_k8zsXY-ya4pX1Wx^57 z;U&f{8U5v#w>u$OloW(QoWcjJN{&qh<9%sjAYq;*dAmK zGRBXK{=^V}Bai35#eBA6O3L7WK2_9}g4Tc#GISRuzNs#^b|L;{cNHPtU*(oRBXDU= zCm*bDJCT;ocEEVozHOmEukQXwZZt(oShLRdy^$Xc z+^;CDq{orNH2dDKPjB=P%8rH(T!1OY7WF1lH>0M7^~JV<-&tda=91jEOYdE7NYV+9 zAP}Q8!M_2&;juv{zn5_lQ)y+B!M%Na$mCDf=_p2-DKUS;pW6pKDDq+#*v!owM{euO zma=_U0SEp=d(X~AP%D1i@yOEmyy;qz3f$%I>GZn$4wvGM`lH}`npDxI2t2e$=2XzB z^f(8+^+h+tsP4)I=Fazi@afPMKL^t(hn2~u`nOZ_k?Oge3^J9tpm5B{J=8TmnJUI` zn&-&o>YLH&bnX>>06UJo5{AFu!(Rg(&_hL_=<~$rZ?l=#d%TJ@3IA{A(>MxsTFUBX zsl63xn2gDLwoAvtA~yRMbE$ZW6u07v?dO5rMZQ?dMav>S+bTH?pF>GR{{`Nc99Jbi zE?aaCcHMKon>YFSR|8B?bPbHwCL(|p!9M0&FzZdIbm#bJ>6$|QtAyJyyT)6eO`O({ zh)mC5zN!Nvk{4S&yIP1>kBIZ*5POfd(8`WNloi2iq35vY5RNMK9l{Y)`CRz5nGkJ) z7kjgz(9*En(V#ZR<;I1)641F}&B7TJv;Y8gTM zZP>O|%hPt#4k*AU90aiQ4q z40~++#>-8MjN5@{P2%U9)D~8gk6g1pEy!!8wjN$^w7WL)&g<8V%Rqwl&!JXODEj2* z$GA>s)37b&7RIF4LX>Rk<;KXKouB1IJ0bgV#Ci4EmXE*A3?yMg$uPHjl3|QsM+}>p zwWVBP#SW{Qdjn$Hw_c(hvvxMwpy0)D*d9q}ckbm~c^t;XbRTC9d$^5c`7$#3?Ir|R zwM@#`;tk|b0K?~lOo{oe=946pa~NYiR57~MDrf7T9m7c}Q7USzK5;v&Yv^h{LBdlL z_^1$?E>soBH~*&IYq~Y^QZf~-L8~2{2ND6Czpzrw^Inanj9t66+BTH!c-k&RcdGb9 zF1Mj-+p8#|uc7Z+A)EYeF5S02t3|mSe|FOy#>zw(TMf3S?(-T+(8ltGaXZ_Jymt}F zg?&Zcu*9%SnvRrR^&Z?T`y$b7!VFL_5JL9~L z`eVPKcizoN5QcA|5^lESv7)g_nj|<;-}(Emd*Zh*cNUAjnpd%?S^*;M7N5++8QmTR zw1`7FY@B*9TW@grXCtE?>E}3XxT79POVhFIJitC*{(wk(1XQP##;%8 zM&j*_=Jxo8&$oNi3E)9zq(#tu5*UxSBlBHC&ISxjs9D;E*YVCYrGepay#*ZnQ3 z!y1^4qXl7%1p>8JmrG4o<9>-yBZg{dOr#8yxI*3ZTNq-X#V(r?1Hq^ zGzG3>+QK^Z9tzCj2gjTHxHLYYKYcrTg3d_`7zabXm%rrNd{*fjxDQ4mbUw6Gn*Mq} z2XTHZVjje`xdkYiV&StMx+7kw>S|_%kZVO${2p(QtO?=z24$pD=JZ8-xX(7T!us1q zV^`tAvt6WYf29HAW(XsU(My$S>q#}!ksACu3~M?<*X^Z~(wz>quYesS%l@sxc=GUO zgaloz+C{kVQzvf5vAtJudkCy54W^*T`=xyUz2bX z{mxqrV+$|BT>tir4|q1Ua?aluOvlcwK~u3>%PpEt>nCB3Dh|)K;>5OvCHX*nAS3l_h%ymYv`b{sNAcU^R3nGZU<7d^+7x#F(gz+^f zDezq+H0{IHL+|=x#=}U2uLCn;h%4Tv&Yyx8sd?26E(Rqy)vQlbYAOWDezshlcWBE? zI3_!6lph>cok@0@862!?Xea!xZRz*d&`p_7={9uQf_UYQFT3j_culw`T zzybVS>Uz5KBjavG*3h}?8J>P`mMZgtmFK@Jv}&9LT$CCgD-rjh$Mo#VOe?76XekzO z3t80|ySe#13vnnEPbH7=I5Z}~Mfstr!!MbsDAPe>=@xcR_ zx4wq6fzKCQRmkX0!KO_$5J8Hy#3<6TDMMl~S#uwf`cSTyp1&rR7VmMgO%zpbUj^!^ z@t(eX^KP$Ays8IS*tdpxHYU^$y4)a!Pm?JN`-$(qjOahgdHC#vALfOOT3>d81Hw%k zr3K?~v3RyRympKT*U_KAIK@$QB@UZtKgPcpj9rHt;=GKUq8@`vE*YuRu>wEF;-8zl z!~rBB%ur6E1lWQ+-(TNx>l<(}>j!YdF1MSz0H{h)Rpi{yif;KKR>1?HxS2wmebS?k zwbD~Qh<5sNqwQhO>2?a$<&=mM^Bc)_-qsQK+4`0fj=iLA_OzDMRDs_*z8DS8w3h43 z4WjKokB&+eS8*EuU%_loRR&a(3>{qo!_rLa$h`Fpuz={c^N&^%2X6&solbx>MHi|Y zd78|Nn=!3_Ed+772Rb0XKaFKK>dD(}^nv0%ka~joa;rY`h#1!_udaMtYMR#hZVat* zP{(5s?3X%H(F9(0)2EaH2BNXXCM2REi_s4m8p`8XZ{iFKoaAG#5QyrjWy@JB+Hvo);RJ*mJv$xS*+q__ZMjQQk-uGCuoc1o`@jh+b2UC*110sc=37*owweh+;6`p|s zgiLBGJy}X-CQZ@OE?KmvT!}%2Yi|j0C-9x48Q&p*rMdw7;xF{d;*RtOGb|*WLjSAY z3N)JfdNQgm-u04+V6?9Svl~Je2xIjWRGZzelK>WR{YXjFqu*wA#8GDWnhXG79D1J; z^)a)DPbVG4hybDsyFWnY7L!k#g^mJsmlCPqvb<@BF6;w%E;eHhF~Sbl6>d}!jXq8XLs{DcodfiUwBrd|M+gQ2akf9 z=b`!;^z3)#c&)KXBJn^$K$(!>UIJt?Kt!9_I%o@~s`->M=U!%ZD+4)2IwAv`aXgn> z)htEe8;%Q2KB`l)BJ7;5^QzM(eiR}A#x|ajH1Riq+7O++mSM%DG@)E0kqhzi+aE@B zu-$yhDQ*^WxHrE`7H0m_nDp{Dn-s_tccNPG^ifyXY$`bA=b5CCy6ac^=^j%1U2nsB z^8HSjuIN!^9o{1B$*T1NC@}Q}Asf6t!c#mcXC}z4@8GwCIeFPeQS^Ta>TZv_j(%al z22#(lW!BERH%+K*ap%;(e=$zQodEYk#-7(ZazD6!YIXnsJFNEMatZ`NJDb5DZV6JH zN$MU&5oZt94^fzTz6_4^$Ed6laekP)SC87sWG7Pohc#>uCR8V#d%04QfjJl1m(u6g z=OINKFXHYe!GadCsTg{IK0-5t0RKT+3~MecpH8ChM}7dKmd>_8oqo~~ZMI3l>hOK$ z1|70||LQR}8z`1YpDq3lR4KuqN0?;EO)>SZU5qWy!Nsdw%o-v-C;7Wyy;x)#!9V{c zqrdM%X{`>OyS<>SRsf|JV{LIidx>wT-BoXcqZ%8m*k!o9o^6(p{UGuZNywDtsD3@K zc>Zspy`|WD8!FjlaL>_3#Inufoajm9sR#2RtUT~hG%d|OzjFdA5Pwkw)fga!{B8X4 zP!LMLKGnNcx1`{Do5z1UeQ*n0?l`3w0wxtKYoTqin-Z{7B`Pk1votXu>!U);x_7&- ztJ3K!U4ru)gV|A^#zISm|-K(D*aYOQ^aF``DtDVlRm`*xjFN#j=ZQ3cH9;6eR= z?ehBAUEw4at8ms=f`9^@wjGQ0d6cA~?^poag-Yl${LFPAwow>f;*G76G*IHR@E?cew|E7M3hZl?C^c|O4d<+67_xIm!`@aHO|5428 z`_;USX}(Iky0m-G;;d-3?B@QH8u`0j=S;?R6q`GdGjcvxD~L7q+?_ld5GTM1+v{Ct zft>5fs?`agF}`^v#W1LC>I`gz9b3dmS~@e5Og9?n`%^7vCKnfnvy=xUBi2tBpb6(B zj<|ortK+JDcYbWsT#f|dUD>l}9PwYA!vFmK<3LNNH#|-NIiiMzp?qjLI|X%%M4_s# z2!F_z*z5NrsmVykUip{eDU&Bi^K?hd{n1448AQzFQyacxOFzHr3#sJHLRopSP8M+< zPcaK-@c6mNY}){=yZGP3r;M3Z(QlfjkboGluZKUEQB<72pl^YjV^eLePy5$-Ck6pu z_wmX-o5i>a3>eB<9$@cW4W!phT~tHvr_-LtUH)I-u;S_I7zi_eZ)KN=KUhqp9BT%E zOZBiJ82f{^rEA`Ph8S2?gm@4Ab(W)fxz4-hW*;G@e3(JD>Itk<#kwh?jz z`Uh&$4V{nNhLcA9~nIfed8 ztEr9GsOU!mB!RvoQ)w3pwmfkf1Uj?VB!L!~`#`2{JRE8s(EKOy%llXQZ#lXeK5@Yy z7oGo#!Oddh9$|W#TdM5G@gix|v1c0+UgkRYvIq5pdPL4pcT?mkoi_%66f3IpMjx4=u1STT zG10WXcY_^;D7i5dI$BVK@Vj|@$!7_Y-Pw%1UVVh7=caL2Bi5IiFR9p%AcqOf@0h0V z0w@jNKVsKR=nos-}iV&6V)EF zgb9@YvpMI1;|lHtvTmb>2w*V*Q1v|{SRLiAp2p5{3I9Ghi96I z_bSFFGgNSf7$`DTEl~fwru7-T zIO1Dy`UnBBp$u=%>G=F)oRvuTZ^tBQ7eh9hPpxg@)Z>?Mpy$`i5wuI7tR5st)7`O5}Kb7165*cHtldlSp+@QAuMpafwEJu^aJWPkOD)WEG?o? zgfn@5_N;}+i?}q!PB;-{a-m&gCadxMiJQYnfQ1W!(eK+^4t^ZWyK2GruQJ03KgSg{ zeFB9=54J(~W;t_(fUL;=!(Za3NOi^j4@W+tw%1lUhnjkpmy@GP*faTmdA>|e?{?M9 zb}#|b-KC+>s zKX$ee8hbGF0y4xLGDJX+yBJ!~bhuyO4q>j4F)l@#N{~x~2Uwvimux!+@yBJ3+~~3j z4n3`y#i$SxNH1mgP~t$zLzhTdmY{epX4(RYB7~vY^1@MPBCEZ|7K!m}hBC4CvUpX! zlk9KYl4n5KKPP-Kz*fUO!OFFmz&$z_gb*GqlhB+*i^B#IqSwkJ5_8tg?a~i0!x$PM zJ$>7SOq9E&R_>%}y-p?f8t?A{V0}^nGax{obO85WVr|0~pn2Q^7l%w|&$TM<9^cMaB&N~==J}sO zV#fb(hAYZ6v%B&jL=-PDJGxPmN$&eKSJg*1xOl}cG(XR{mpa}ge*O5Z zB<`BhJkpy+BlLGn1i zDo*!JiZRtV&`0Hdl#Kjp4in@yOSq>8kUT>@GU7eUvfA3w2w zm*xO!{Eo~4Qslcxe>Zo!H(CRy&1mCI)RK`WHVjp3-;0oiVze@}MiXc1ljV7##342p+Otd@~9l7x6G5
NV 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 PAdsq(hGNAF(~$oG5%SvB literal 0 HcmV?d00001 diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..57131e1 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/screenshot.png b/assets/screenshot.png similarity index 100% rename from screenshot.png rename to assets/screenshot.png From 19f7483fbd10ddbe427c4001aad697ea4ab6c188 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 13:20:31 -0500 Subject: [PATCH 7/9] Social preview - Possibly fixed formatting error with README --- README.md | 2 ++ assets/social-preview.png | Bin 0 -> 45666 bytes assets/social-preview.xcf | Bin 0 -> 63157 bytes 3 files changed, 2 insertions(+) create mode 100644 assets/social-preview.png create mode 100644 assets/social-preview.xcf diff --git a/README.md b/README.md index 05975a1..0a66f0d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@

+

+

GitHub license GitHub Workflow Status GitHub commit activity diff --git a/assets/social-preview.png b/assets/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..72e8c2e5e39a8a2dd41ac15b2e0e84b6ddc52baa GIT binary patch literal 45666 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ℜ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 Date: Sat, 7 Jan 2023 14:31:09 -0500 Subject: [PATCH 8/9] Unit tests - Renamed GlobalUsings.cs to Usings.cs - Refractored SysFS - Seperated building of the terminal and unit tests - To save on resources, the unit tests job on runs on Ubuntu --- .github/workflows/dotnet.yml | 19 ++ src/TOMAS.sln | 6 + src/Tomas.Core/{GlobalUsing.cs => Usings.cs} | 0 src/Tomas.Interface/Usings.cs | 0 src/Tomas.Kernel/SysFS.cs | 240 +++++++++--------- .../{GlobalUsing.cs => Usings.cs} | 0 src/Tomas.Tests/Shell/MockProgram.cs | 16 ++ src/Tomas.Tests/Shell/MockShell.cs | 17 ++ src/Tomas.Tests/ShellTests.cs | 26 ++ src/Tomas.Tests/Tomas.Tests.csproj | 29 +++ src/Tomas.Tests/Usings.cs | 11 + 11 files changed, 237 insertions(+), 127 deletions(-) rename src/Tomas.Core/{GlobalUsing.cs => Usings.cs} (100%) create mode 100644 src/Tomas.Interface/Usings.cs rename src/Tomas.Kernel/{GlobalUsing.cs => Usings.cs} (100%) create mode 100644 src/Tomas.Tests/Shell/MockProgram.cs create mode 100644 src/Tomas.Tests/Shell/MockShell.cs create mode 100644 src/Tomas.Tests/ShellTests.cs create mode 100644 src/Tomas.Tests/Tomas.Tests.csproj create mode 100644 src/Tomas.Tests/Usings.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 21f7542..bc48a7a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -28,3 +28,22 @@ jobs: run: dotnet restore src/Tomas.Terminal - name: Build run: dotnet build src/Tomas.Terminal -c Release --no-restore + + test: + timeout-minutes: 15 + continue-on-error: true + runs-on: ubuntu-latest + strategy: + matrix: + dotnet: ["6.0.x"] + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ matrix.dotnet }} + - name: Install test dependencies + run: dotnet restore src/Tomas.Tests + - name: Test + run: dotnet test src --no-restore --debug \ No newline at end of file diff --git a/src/TOMAS.sln b/src/TOMAS.sln index 44fc703..d18435f 100644 --- a/src/TOMAS.sln +++ b/src/TOMAS.sln @@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Terminal", "Tomas.Ter EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tomas.Kernel", "Tomas.Kernel\Tomas.Kernel.csproj", "{B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tomas.Tests", "Tomas.Tests\Tomas.Tests.csproj", "{76AD2140-2975-43DA-89A9-0BEC70B2ECDD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,6 +46,10 @@ Global {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Debug|Any CPU.Build.0 = Debug|Any CPU {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {B70BDFD5-64BA-4FCE-A00F-DDD209C2C0FB}.Release|Any CPU.Build.0 = Release|Any CPU + {76AD2140-2975-43DA-89A9-0BEC70B2ECDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76AD2140-2975-43DA-89A9-0BEC70B2ECDD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76AD2140-2975-43DA-89A9-0BEC70B2ECDD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76AD2140-2975-43DA-89A9-0BEC70B2ECDD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Tomas.Core/GlobalUsing.cs b/src/Tomas.Core/Usings.cs similarity index 100% rename from src/Tomas.Core/GlobalUsing.cs rename to src/Tomas.Core/Usings.cs diff --git a/src/Tomas.Interface/Usings.cs b/src/Tomas.Interface/Usings.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/Tomas.Kernel/SysFS.cs b/src/Tomas.Kernel/SysFS.cs index 5b5865a..6ad6324 100644 --- a/src/Tomas.Kernel/SysFS.cs +++ b/src/Tomas.Kernel/SysFS.cs @@ -8,152 +8,138 @@ namespace Tomas.Kernel; static class SysFS { - // The root directory of the file system - public const string ROOT_DIR = "0:\\"; - // The system directory, located in the root directory - static string SYSTEM_DIR = $"{ROOT_DIR}\\SYSTEM\\"; + // The root directory of the file system + public const string ROOT_DIR = "0:\\"; + // The system directory, located in the root directory + static string SYSTEM_DIR = $"{ROOT_DIR}\\SYSTEM\\"; - static string LOG_FILE = $"{SYSTEM_DIR}system.log"; + static string LOG_FILE = $"{SYSTEM_DIR}system.log"; - public const string FS_ERROR = "File system disabled."; + public const string FS_ERROR = "File system disabled."; - ///

- /// An instance of the CosmosVFS class, used for accessing the virtual file system - /// - static CosmosVFS fileSystem = new(); + /// + /// An instance of the CosmosVFS class, used for accessing the virtual file system + /// + static CosmosVFS fileSystem = new(); - /// - /// Initializes the file system by creating the system directory and sysinfo.txt file - /// and setting the IsFSActive property of the SysMeta class to true - /// - public static void Initialize() - { - try - { - var createSysFiles = "Creating system files."; - var setSysPref = "Writing system info."; - var fsSuccess = "File system succesfully initialized."; - var sysInfoFile = "sysinfo.txt"; + /// + /// Initializes the file system by creating the system directory and sysinfo.txt file + /// and setting the IsFSActive property of the SysMeta class to true + /// + public static void Initialize() + { + try + { + // File to store system information + const string sysInfoFile = "sysinfo.txt"; - // Register the CosmosVFS instance as the virtual file system - VFSManager.RegisterVFS(fileSystem); + // Create system directory if it doesn't exist + if (!Directory.Exists(SYSTEM_DIR)) + fileSystem.CreateDirectory(SYSTEM_DIR); - // Create the system directory - if (!Directory.Exists(SYSTEM_DIR)) - fileSystem.CreateDirectory(SYSTEM_DIR); + // Create system log file if it doesn't exist + if (!File.Exists($"{SYSTEM_DIR}{LOG_FILE}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); - // Create the system.log file in the system directory - if (!File.Exists($"{SYSTEM_DIR}{LOG_FILE}")) - fileSystem.CreateFile($"{SYSTEM_DIR}{LOG_FILE}"); + // Create sysinfo.txt file if it doesn't exist + if (!File.Exists($"{SYSTEM_DIR}{sysInfoFile}")) + fileSystem.CreateFile($"{SYSTEM_DIR}{sysInfoFile}"); - Console.WriteLine(createSysFiles); - File.AppendAllText(LOG_FILE, createSysFiles); + // Write system name, version, and build number to sysinfo.txt file + File.WriteAllText($"{SYSTEM_DIR}sysinfo.txt", $"{SysMeta.NAME} v{SysMeta.VERSION} ({SysMeta.BuildNumber})"); - // Create the sysinfo.txt file in the system directory - if (!File.Exists($"{SYSTEM_DIR}{sysInfoFile}")) - fileSystem.CreateFile($"{SYSTEM_DIR}{sysInfoFile}"); + // Set IsFSEnabled property of SysMeta class to true + SysMeta.IsFSEnabled = true; - Console.WriteLine(setSysPref); + // Read contents of sysinfo.txt file and print to console + var systemInfo = File.ReadAllText($"{SYSTEM_DIR}sysinfo.txt"); + Console.WriteLine(systemInfo); + } + catch (Exception err) + { + // Print error message if an exception is caught + Console.WriteLine($"{err.Message}{Environment.NewLine}Warning: Error messages will not logged."); + } + } - File.AppendAllText(LOG_FILE, setSysPref); - // Write the system name, version, and build number to the sysinfo.txt file - File.WriteAllText($"{SYSTEM_DIR}sysinfo.txt", $"{SysMeta.NAME} v{SysMeta.VERSION} ({SysMeta.BuildNumber})"); - Console.WriteLine(fsSuccess); - File.AppendAllText(LOG_FILE, fsSuccess); + /// + /// Creates a new directory at the specified path + /// + /// directory + public static void CreateDirectory(string directory) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - // Set the IsFSActive property of the SysMeta class to true - SysMeta.IsFSEnabled = true; + // Create the directory using the CosmosVFS instance + if (!Directory.Exists($"{ROOT_DIR}\\{directory}")) + fileSystem.CreateDirectory($"{ROOT_DIR}\\{directory}"); + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); + } + } - // Read the contents of the sysinfo.txt file and print it to the console - var systemInfo = File.ReadAllText($"{SYSTEM_DIR}sysinfo.txt"); + /// + /// Creates a new file at the specified path + /// + /// file path + /// file + public static void CreateFile(string path, string file) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - Console.WriteLine(systemInfo); - } - catch (Exception err) - { - // If an exception is caught, print an error message indicating that the file system failed to load - Console.WriteLine($"{err.Message}{Environment.NewLine}Warning: Error messages will not logged."); - } - } + // Create the file using the CosmosVFS instance + if (!File.Exists($"{ROOT_DIR}\\{path}\\{file}")) + fileSystem.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); + } + } - /// - /// Creates a new directory at the specified path - /// - /// directory - public static void CreateDirectory(string directory) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); + /// + /// Lists all directories in the specified path + /// + /// path to directory + /// returns a list of directories + public static string[] ListDirectories(string path) + { + try + { + // If file system isn't enabeld, throw exception + if (!SysMeta.IsFSEnabled) + throw new IOException(FS_ERROR); - // Create the directory using the CosmosVFS instance - if (!Directory.Exists($"{ROOT_DIR}\\{directory}")) - fileSystem.CreateDirectory($"{ROOT_DIR}\\{directory}"); - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); - } - } + // Get the directories in the specified path using the Directory.GetDirectories method + var dirs = Directory.GetDirectories(path); - /// - /// Creates a new file at the specified path - /// - /// file path - /// file - public static void CreateFile(string path, string file) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); + // Return the directories + return dirs; + } + catch (IOException err) + { + // If an exception is caught, print an error message indicating the error + Console.WriteLine(err.Message); + File.AppendAllText(LOG_FILE, err.Message); - // Create the file using the CosmosVFS instance - if (!File.Exists($"{ROOT_DIR}\\{path}\\{file}")) - fileSystem.CreateFile($"{ROOT_DIR}\\{path}\\{file}"); - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); - } - } - - /// - /// Lists all directories in the specified path - /// - /// path to directory - /// returns a list of directories - public static string[] ListDirectories(string path) - { - try - { - // If file system isn't enabeld, throw exception - if (!SysMeta.IsFSEnabled) - throw new IOException(FS_ERROR); - - // Get the directories in the specified path using the Directory.GetDirectories method - var dirs = Directory.GetDirectories(path); - - // Return the directories - return dirs; - } - catch (IOException err) - { - // If an exception is caught, print an error message indicating the error - Console.WriteLine(err.Message); - File.AppendAllText(LOG_FILE, err.Message); - - throw; - } - } + throw; + } + } } diff --git a/src/Tomas.Kernel/GlobalUsing.cs b/src/Tomas.Kernel/Usings.cs similarity index 100% rename from src/Tomas.Kernel/GlobalUsing.cs rename to src/Tomas.Kernel/Usings.cs diff --git a/src/Tomas.Tests/Shell/MockProgram.cs b/src/Tomas.Tests/Shell/MockProgram.cs new file mode 100644 index 0000000..3077976 --- /dev/null +++ b/src/Tomas.Tests/Shell/MockProgram.cs @@ -0,0 +1,16 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +namespace Tomas.Tests.Shell; + +internal class MockProgram : IProgram +{ + public bool Run(IShell shell) + { + Debug.WriteLine("Test Program."); + return true; + } +} diff --git a/src/Tomas.Tests/Shell/MockShell.cs b/src/Tomas.Tests/Shell/MockShell.cs new file mode 100644 index 0000000..0ea450a --- /dev/null +++ b/src/Tomas.Tests/Shell/MockShell.cs @@ -0,0 +1,17 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +namespace Tomas.Tests.Shell; + +internal class MockShell : IShell +{ + public string? ReadLine { get; } + + public Dictionary Programs => new() + { + { "test", new MockProgram() }, + }; +} diff --git a/src/Tomas.Tests/ShellTests.cs b/src/Tomas.Tests/ShellTests.cs new file mode 100644 index 0000000..e837252 --- /dev/null +++ b/src/Tomas.Tests/ShellTests.cs @@ -0,0 +1,26 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +using Tomas.Tests.Shell; + +namespace Tomas.Tests; + +public class ShellTests +{ + // Create a new instance of the mock shell + readonly MockShell _mockShell = new(); + + [Fact] + public void ProgramTest() + { + // Create a mock program + var program = new MockProgram(); + + // Assert that the Run method of the program and returns true when passed the shell object. + Assert.True(program.Run(_mockShell)); + } + +} \ No newline at end of file diff --git a/src/Tomas.Tests/Tomas.Tests.csproj b/src/Tomas.Tests/Tomas.Tests.csproj new file mode 100644 index 0000000..5c6309a --- /dev/null +++ b/src/Tomas.Tests/Tomas.Tests.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + enable + enable + enable + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + diff --git a/src/Tomas.Tests/Usings.cs b/src/Tomas.Tests/Usings.cs new file mode 100644 index 0000000..d97cace --- /dev/null +++ b/src/Tomas.Tests/Usings.cs @@ -0,0 +1,11 @@ +/* +In jurisdictions that recognize copyright waivers, I've waived all copyright +and related or neighboring rights for to this project. In areas where these +waivers are not recognized, BSD-3-Clause is enforced. +See the (UN)LICENSE file in the project root for more information. +*/ +global using Xunit; +global using System.Diagnostics.CodeAnalysis; +global using System.Diagnostics; +global using Tomas.Core; +global using Tomas.Interface; \ No newline at end of file From 5888771e201d6ac63e3dc7cf0169cb7fce204d02 Mon Sep 17 00:00:00 2001 From: Tony Bark Date: Sat, 7 Jan 2023 14:47:52 -0500 Subject: [PATCH 9/9] Removed nonexisting flag in workflow - Moved shell instance as a global field --- .github/workflows/dotnet.yml | 6 +++--- src/Tomas.Terminal/{GlobalUsing.cs => Usings.cs} | 0 src/Tomas.Tests/ShellTests.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/Tomas.Terminal/{GlobalUsing.cs => Usings.cs} (100%) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index bc48a7a..aca121d 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -27,7 +27,7 @@ jobs: - name: Install dependencies run: dotnet restore src/Tomas.Terminal - name: Build - run: dotnet build src/Tomas.Terminal -c Release --no-restore + run: dotnet build src/Tomas.Terminal -c Release --no-restore --nologo test: timeout-minutes: 15 @@ -44,6 +44,6 @@ jobs: with: dotnet-version: ${{ matrix.dotnet }} - name: Install test dependencies - run: dotnet restore src/Tomas.Tests + run: dotnet restore src/Tomas.Tests - name: Test - run: dotnet test src --no-restore --debug \ No newline at end of file + run: dotnet test src --no-restore --nologo \ No newline at end of file diff --git a/src/Tomas.Terminal/GlobalUsing.cs b/src/Tomas.Terminal/Usings.cs similarity index 100% rename from src/Tomas.Terminal/GlobalUsing.cs rename to src/Tomas.Terminal/Usings.cs diff --git a/src/Tomas.Tests/ShellTests.cs b/src/Tomas.Tests/ShellTests.cs index e837252..0f7c04f 100644 --- a/src/Tomas.Tests/ShellTests.cs +++ b/src/Tomas.Tests/ShellTests.cs @@ -16,7 +16,7 @@ public class ShellTests [Fact] public void ProgramTest() { - // Create a mock program + // Create a mock program instance var program = new MockProgram(); // Assert that the Run method of the program and returns true when passed the shell object.