diff --git a/Hasher/Features/HashFeature.cs b/Hasher/Features/HashFeature.cs deleted file mode 100644 index 4bbce40..0000000 --- a/Hasher/Features/HashFeature.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using SabreTools.CommandLine; -using SabreTools.CommandLine.Inputs; -using SabreTools.Hashing; - -namespace Hasher.Features -{ - /// - /// Set of options for the test executable - /// - /// TODO: Add file output - internal sealed class HashFeature : Feature - { - #region Feature Definition - - public const string DisplayName = "options"; - - private static readonly string[] _flags = ["hash"]; - - private const string _description = "Hash all input paths (default)"; - - #endregion - - #region Cached Inputs - - /// - /// Enable debug output for relevant operations - /// - private bool _debug = false; - - /// - /// List of all hash types to process - /// - private List _hashTypes = []; - - #endregion - - #region Constructors - - public HashFeature() - : base(DisplayName, _flags, _description) - { - RequiresInputs = true; - - Add(new FlagInput("debug", ["-d", "--debug"], "Enable debug mode")); - Add(new StringListInput("type", ["-t", "--type"], "Select included hashes")); - } - - #endregion - - /// - public override bool ProcessArgs(string[] args, int index) - { - // Process the arguments normally first - if (!base.ProcessArgs(args, index)) - return false; - - // Set the debug flag for easier access - _debug = GetBoolean("debug"); - - // Get hash types list - var hashTypes = GetStringList("type"); - if (hashTypes.Count == 0) - { - _hashTypes.Add(HashType.CRC32); - _hashTypes.Add(HashType.MD5); - _hashTypes.Add(HashType.SHA1); - _hashTypes.Add(HashType.SHA256); - } - else if (hashTypes.Contains("all")) - { - _hashTypes = [.. (HashType[])Enum.GetValues(typeof(HashType))]; - } - else - { - foreach (string typeString in hashTypes) - { - HashType? hashType = typeString.GetHashType(); - if (hashType != null && !_hashTypes.Contains(hashType.Value)) - _hashTypes.Add(item: hashType.Value); - } - } - - return true; - } - - /// - public override bool VerifyInputs() => Inputs.Count > 0; - - /// - public override bool Execute() - { - foreach (string inputPath in Inputs) - { - PrintPathHashes(inputPath); - } - - return true; - } - - /// - /// Wrapper to print hashes for a single path - /// - /// File or directory path - private void PrintPathHashes(string path) - { - Console.WriteLine($"Checking possible path: {path}"); - - // Check if the file or directory exists - if (File.Exists(path)) - { - PrintFileHashes(path); - } - else if (Directory.Exists(path)) - { - foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) - { - PrintFileHashes(file); - } - } - else - { - Console.WriteLine($"{path} does not exist, skipping..."); - } - } - - /// - /// Print information for a single file, if possible - /// - /// File path - private void PrintFileHashes(string file) - { - Console.WriteLine($"Attempting to hash {file}, this may take a while..."); - Console.WriteLine(); - - // If the file doesn't exist - if (!File.Exists(file)) - { - Console.WriteLine($"{file} does not exist, skipping..."); - return; - } - - try - { - // Get all file hashes for flexibility - var hashes = HashTool.GetFileHashes(file); - if (hashes == null) - { - if (_debug) Console.WriteLine($"Hashes for {file} could not be retrieved"); - return; - } - - // Output subset of available hashes - var builder = new StringBuilder(); - foreach (HashType hashType in _hashTypes) - { - // TODO: Make helper to pretty-print hash type names - if (hashes.TryGetValue(hashType, out string? hash) && hash != null) - builder.AppendLine($"{hashType}: {hash}"); - } - - // Create and print the output data - string hashData = builder.ToString(); - Console.WriteLine(hashData); - } - catch (Exception ex) - { - Console.WriteLine(_debug ? ex : "[Exception opening file, please try again]"); - return; - } - } - } -} diff --git a/Hasher/Program.cs b/Hasher/Program.cs index 8ad66eb..f640cfb 100644 --- a/Hasher/Program.cs +++ b/Hasher/Program.cs @@ -1,13 +1,24 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text; using Hasher.Features; using SabreTools.CommandLine; using SabreTools.CommandLine.Features; +using SabreTools.CommandLine.Inputs; +using SabreTools.Hashing; namespace Hasher { public static class Program { + #region Constants + + private const string _debugName = "debug"; + private const string _typeName = "type"; + + #endregion + public static void Main(string[] args) { // Create the command set @@ -22,47 +33,40 @@ namespace Hasher // Cache the first argument and starting index string featureName = args[0]; - int index = 1; - // Get the associated feature + // Try processing the standalone arguments var topLevel = commandSet.GetTopLevel(featureName); - if (topLevel == null || topLevel is not Feature feature) + switch (topLevel) { - index = 0; - feature = commandSet.GetFeature(HashFeature.DisplayName)!; + case Help help: help.ProcessArgs(args, 0, commandSet); return; + case ListFeature lf: lf.Execute(); return; } - // Handle default help functionality - if (topLevel is Help helpFeature) + // Loop through and process the options + int firstFileIndex = 0; + for (; firstFileIndex < args.Length; firstFileIndex++) { - helpFeature.ProcessArgs(args, 0, commandSet); - return; + string arg = args[firstFileIndex]; + + var input = commandSet.GetTopLevel(arg); + if (input == null) + break; + + input.ProcessInput(args, ref firstFileIndex); } - // Now verify that all other flags are valid - if (!feature.ProcessArgs(args, index)) - { - commandSet.OutputAllHelp(); - return; - } + // Get the required variables + bool debug = commandSet.GetBoolean(_debugName); + List hashTypes = GetHashTypes(commandSet.GetStringList(_typeName)); - // If there are no valid inputs - if (feature.RequiresInputs && !feature.VerifyInputs()) + // Loop through all of the input files + for (int i = firstFileIndex; i < args.Length; i++) { - Console.WriteLine("At least one path is required!"); - commandSet.OutputAllHelp(); - return; - } - - // Now execute the current feature - if (!feature.Execute()) - { - Console.Error.WriteLine("An error occurred during processing!"); - commandSet.OutputFeatureHelp(feature.Name); + string arg = args[i]; + PrintPathHashes(arg, hashTypes, debug); } } - /// /// Create the command set for the program /// @@ -85,12 +89,119 @@ namespace Hasher var commandSet = new CommandSet(header, footer); + // Standalone Options commandSet.Add(new Help()); commandSet.Add(new ListFeature()); - commandSet.Add(new HashFeature()); + + // Hasher Options + commandSet.Add(new FlagInput(_debugName, ["-d", "--debug"], "Enable debug mode")); + commandSet.Add(new StringListInput(_typeName, ["-t", "--type"], "Select included hashes")); return commandSet; } + /// + private static List GetHashTypes(List types) + { + List hashTypes = []; + if (types.Count == 0) + { + hashTypes.Add(HashType.CRC32); + hashTypes.Add(HashType.MD5); + hashTypes.Add(HashType.SHA1); + hashTypes.Add(HashType.SHA256); + } + else if (types.Contains("all")) + { + hashTypes = [.. (HashType[])Enum.GetValues(typeof(HashType))]; + } + else + { + foreach (string typeString in types) + { + HashType? hashType = typeString.GetHashType(); + if (hashType != null && !hashTypes.Contains(hashType.Value)) + hashTypes.Add(item: hashType.Value); + } + } + + return hashTypes; + } + + /// + /// Wrapper to print hashes for a single path + /// + /// File or directory path + /// Set of hashes to retrieve + /// Enable debug output + private static void PrintPathHashes(string path, List hashTypes, bool debug) + { + Console.WriteLine($"Checking possible path: {path}"); + + // Check if the file or directory exists + if (File.Exists(path)) + { + PrintFileHashes(path, hashTypes, debug); + } + else if (Directory.Exists(path)) + { + foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories)) + { + PrintFileHashes(file, hashTypes, debug); + } + } + else + { + Console.WriteLine($"{path} does not exist, skipping..."); + } + } + + /// + /// Print information for a single file, if possible + /// + /// File path + /// Set of hashes to retrieve + /// Enable debug output + private static void PrintFileHashes(string file, List hashTypes, bool debug) + { + Console.WriteLine($"Attempting to hash {file}, this may take a while..."); + Console.WriteLine(); + + // If the file doesn't exist + if (!File.Exists(file)) + { + Console.WriteLine($"{file} does not exist, skipping..."); + return; + } + + try + { + // Get all file hashes for flexibility + var hashes = HashTool.GetFileHashes(file); + if (hashes == null) + { + if (debug) Console.WriteLine($"Hashes for {file} could not be retrieved"); + return; + } + + // Output subset of available hashes + var builder = new StringBuilder(); + foreach (HashType hashType in hashTypes) + { + // TODO: Make helper to pretty-print hash type names + if (hashes.TryGetValue(hashType, out string? hash) && hash != null) + builder.AppendLine($"{hashType}: {hash}"); + } + + // Create and print the output data + string hashData = builder.ToString(); + Console.WriteLine(hashData); + } + catch (Exception ex) + { + Console.WriteLine(debug ? ex : "[Exception opening file, please try again]"); + return; + } + } } } diff --git a/README.MD b/README.MD index ec9441b..dbc2b21 100644 --- a/README.MD +++ b/README.MD @@ -22,14 +22,11 @@ Hasher file|directory ... Available options: ?, h, help Show this help list List all available hashes and quit - hash Hash all input paths (default) - -d, --debug Enable debug mode - -t=, --type= Select included hashes + -d, --debug Enable debug mode + -t=, --type= Select included hashes If no hash types are provided, this tool will default to outputting CRC-32, MD5, SHA-1, and SHA-256. -Optionally, all supported hashes can be output by -specifying a value of 'all'. ``` ## Internal Implementations