/// <summary> /// Parse the command-line arguments passed into the program and extract out any /// options and the file paths to process. /// </summary> /// <param name="args">A <see cref="String"/> array containing the command-line /// arguments passed in to the program's Main() method</param> public static bool PerformHashes(CmdLineAppArgs args) { // This is a convenience version of the same method above, only this one does // not let us override the user's preferred hash. No sense reinventing the // wheel, so... return(PerformHashes(args, false, Hashes.SHA1)); }
/// <summary> /// Perform the actual hash operation /// </summary> /// <param name="args">A <see cref="CmdLineAppArgs"/> object containing the parsed /// input parameters from the command line</param> /// <param name="overrideHash">If true, override the hash algorithm specified in /// args.Hash with the hash specified by hashToOverrideWith</param> /// <param name="hashToOverrideWith">If overrideHash is true, override the hash /// algorithm specified in args.Hash with this hash algorithm</param> /// <returns></returns> public static bool PerformHashes(CmdLineAppArgs args, bool overrideHash, Hashes hashToOverrideWith) { // If the arguments are null, return false to notify the caller that there's // nothing to do: if (args == null) { return(false); } // If we're being told to override the hash, check to make sure that the hash // specified in the arguments matches the hash we're supposed to override it // with. If they don't match, print a warning to the user and override the // hash. Otherwise, we'll let this quietly slip by. if (overrideHash && args.Hash != hashToOverrideWith) { Console.Error.WriteLine("WARNING: Can't override hash algorithm, " + GetHashSwitchString(args.Hash) + " switch ignored."); args.Hash = hashToOverrideWith; } // If the file list is empty and the input file parameter was not set, then // there's really not much for us to do. Complain and exit out with a false // result. if ((args.Files == null || args.Files.Length == 0) && String.IsNullOrEmpty(args.InputFile)) { Console.Error.WriteLine("ERROR: No file list or input file specified. Nothing to do!"); return(false); } // If the input file was specified but we also got a list of files on the // command line, we'll claim the input file overrides the list of files from // the arguments and null those out: if (!String.IsNullOrEmpty(args.InputFile) && args.Files != null && args.Files.Length > 0) { Console.Error.WriteLine("WARNING: Input file specified. File list on command line will be ignored."); args.Files = null; } // Now that it looks like we're going to do something, put on our asbestos // underpants: try { // If an input file was specified, try to read in its contents: if (!String.IsNullOrEmpty(args.InputFile)) { // Declare a list of strings to hold the intermediate values read // from the file: List <string> fileList = new List <string>(); // Now try to open up the file for reading: StreamReader reader = new StreamReader(args.InputFile); // Loop through the lines in the file and examine each one: string line = null; while ((line = reader.ReadLine()) != null) { // For the sake of data integrity, we'll ignore any empty lines // or lines that consist entirely of white space. As a convenience // to the user, we'll also allow they to put in comments by starting // a line with a hash/pound sign, which we'll also ignore. if (line.Length == 0 || Regex.IsMatch(line, @"^\s*$") || Regex.IsMatch(line, @"^#")) { continue; } // If we pass that test, add the contents of the line to the list. // Note that we'll strip any leading or trailing white space while // we're at it. fileList.Add(line.Trim()); } // Close the reader: reader.Close(); // If we got anything from the file, convert the list into a string // array and put that into the arguments: if (fileList.Count > 0) { args.Files = fileList.ToArray(); } } // We need to test again whether or not we have anything to do, because // reading the input file may have populated the file list where it was // empty before. If no files are listed, bail: if (args.Files == null || args.Files.Length == 0) { Console.Error.WriteLine("ERROR: No file list specified or input file contained nothing useful. Nothing to do!"); return(false); } // Now declare our output stream writer. If an output file was specified, // try to open that file, using the append flag as well. If no output file // was specified, the writer object will remain null, which will be our flag // below on where to send our output. StreamWriter writer = null; if (!String.IsNullOrEmpty(args.OutputFile)) { writer = new StreamWriter(args.OutputFile, args.AppendOutput); } // Now to get to work. First, we need to know what mode we're working in. // We'll handle compare mode first: if (args.CompareMode) { // Compare mode only makes sense if we've got more than one file to // compare. If not, we need to complain: if (args.Files.Length == 1) { Console.Error.WriteLine("ERROR: Cannot compare files unless two or more files are specified."); return(false); } // Print out the initial status message like above: ConsoleStatusUpdater status = null; if (writer == null) { status = new ConsoleStatusUpdater(); Console.WriteLine(); Console.Write("Comparing " + GetHashString(args.Hash) + " of " + args.Files.Length + " files... 0%"); } // Compute the hashes and compare the result. Note that the // displayed status might be less than 100% if the comparisons // fail. bool isMatch = HashEngine.CompareHashes(args.Hash, args.Files, status); // Print the result to the appropriate place: string result = null; if (isMatch) { result = "Congratulations! All " + args.Files.Length + " files match!"; } else { result = "WARNING! One or more of these " + args.Files.Length + " files do not match!"; } if (writer == null) { Console.WriteLine(); Console.WriteLine(result); } else { writer.WriteLine(result); } } // If we're not in compare mode, we must be hashing each file individually: else { // Loop through the file list: foreach (string file in args.Files) { // We've already warned the user if they tried to use a redirection // command, so ignore those here: if (IsRedirectionCommand(file)) { continue; } // Try to hash the file. Note that our actual result here is to // concatenate the result with the file name, giving us a similar // output to md5sum, shasum, and their friends. string result = HashEngine.HashFile(args.Hash, file, args.OutputType, null) + " " + file; // Write the result to the appropriate output: if (writer != null) { writer.WriteLine(result); } else { Console.WriteLine(result); } } } // Once we're finished, close the file if necessary: if (writer != null) { writer.Close(); } return(true); } // This needs to be prettied up, but for now just output the message from any // exception caught and return false to the caller to indicate an error: catch (Exception e) { Console.Error.WriteLine("ERROR: " + e.Message); return(false); } }
/// <summary> /// Parse the command-line arguments passed into the program and extract out any /// options and the file paths to process. /// </summary> /// <param name="args">A <see cref="String"/> array containing the command-line /// arguments passed in to the program's Main() method</param> /// <param name="defaultHash">A <see cref="Hashes"/> enum value representing the /// default hash algorithm to use if no other hash is specified.</param> /// <returns>A <see cref="CmdLineAppArgs"/> object containing the parsed /// arguments</returns> public static CmdLineAppArgs ParseCmdLineArgs(string[] args, Hashes defaultHash) { // If no arguments were passed in, there's nothing for us to do. Return // null to signal the caller to print an error or, more likely, the usage // statement. if (args == null || args.Length == 0) { return(null); } // Asbestos underpants: try { // Create a new CmdLineAppArgs object with the default values (SHA-1, // lower-case hex output, no compare mode, and no input or output files): CmdLineAppArgs parsedArgs = new CmdLineAppArgs(); parsedArgs.Hash = defaultHash; // Run through the array and look for option commands. Note that even // though we tested for null above, we need to test for it again because // the pop-and-discard step could give us back a null array. while (args != null && args.Length > 0 && args[0].StartsWith("-")) { // We'll allow upper-case or mixed-case options by forcing the // flag to lower-case: switch (args[0].ToLower()) { // Most of these determine which hash to use. Overwrite the // default when one of these flags are encountered. Note that // if the program forces a specific hash (like md5 forces MD5), // this could get overridden. case "-md5": parsedArgs.Hash = Hashes.MD5; break; case "-sha1": parsedArgs.Hash = Hashes.SHA1; break; case "-sha256": parsedArgs.Hash = Hashes.SHA256; break; case "-sha384": parsedArgs.Hash = Hashes.SHA384; break; case "-sha512": parsedArgs.Hash = Hashes.SHA512; break; case "-ripemd160": parsedArgs.Hash = Hashes.RIPEMD160; break; case "-whirlpool": parsedArgs.Hash = Hashes.Whirlpool; break; case "-tiger": parsedArgs.Hash = Hashes.Tiger; break; // But this one puts us in Base64 mode: case "-base64": parsedArgs.OutputType = OutputType.Base64; break; // And this outputs hex with capital letters: case "-hexcaps": parsedArgs.OutputType = OutputType.CapHex; break; // And this outputs Bubble Babble: case "-bubbab": parsedArgs.OutputType = OutputType.BubbleBabble; break; // If we encounter the compare switch, put us in compare mode rather // that hashing each file individually: case "-compare": parsedArgs.CompareMode = true; break; // If the output flag is found, we'll assume the next item on // the list is the name of the file where we'll write our // output. Note that if no file name is found, this flag // essentially does nothing. case "-out": args = CmdLineAppUtils.PopAndDiscardArray(args); if (args.Length > 0) { parsedArgs.OutputFile = args[0]; } break; // Similarly, if the input switch is encountered, the next item // is assumed to be the input file listing the files to hash: case "-in": args = CmdLineAppUtils.PopAndDiscardArray(args); if (args.Length > 0) { parsedArgs.InputFile = args[0]; } break; // If this flag is set and an output file has been specified, this // will make sure we append to the existing file rather than overwrite // it. If no output file was specified, this will get silently // ignored. case "-append": parsedArgs.AppendOutput = true; break; // If we didn't get a valid switch, complain: default: Console.Error.WriteLine("ERROR: Invalid switch \"" + args[0] + "\""); break; } // Now shift the array down to the next argument: args = CmdLineAppUtils.PopAndDiscardArray(args); } // Now that we've processed the switches, make sure there files on the // command line to process. Note that if the "-in" switch was used, // it would be valid to have nothing left in this list. if (args != null && args.Length > 0) { // Loop through the remaining items: for (int i = 0; i < args.Length; i++) { // We're going to officially state that we don't support the // standard redirection symbols (">", ">>", "<", etc.), simply // because they're a pain to work with. We'll ignore these when // we run through the list of files, but we'll print an error // here if we encounter them. if (IsRedirectionCommand(args[0])) { Console.Error.WriteLine("ERROR: Redirection not supported. Use -input and -output instead."); } } // Now add the array as the file list to the return value: parsedArgs.Files = args; } // Return the parsed arguments: return(parsedArgs); } // If anything blew up, return a null value: catch { return(null); } }