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