//Ciphers/helperFunctions.hpp //Matthew Ellison // Created: 04-29-18 //Modified: 03-07-19 //This file contains the driver function and the test functions for the Cipher program //This program will use some simple ciphers that are no longer widely used but still fun to play with /* Copyright (C) 2019 Matthew Ellison This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef HELPER_FUNCTIONS_HPP #define HELPER_FUNCTIONS_HPP #include "Headers/Caesar.hpp" #include "Headers/Playfair.hpp" #include "Headers/Vigenere.hpp" #include "Headers/Atbash.hpp" #include "Headers/Morse.hpp" #include "Headers/Autokey.hpp" #include #include #include #include //Error handling classes //Thrown if there is a file flag that has no corresponding file name class noFileName{ private: std::string type; public: noFileName(std::string type): type(type) {} std::string getType(){ return type; } }; //Thrown if there is an invalid flag given class invalidFlag{ private: std::string flag; public: invalidFlag(std::string flag): flag(flag) {} std::string getFlag(){ return flag; } }; //Thrown if there is an invalid combination of flags given class invalidCombo{ private: std::string type; public: invalidCombo(std::string type): type(type) {} std::string getType(){ return type; } }; //Holds the location of the possible cipher flags for both the string and boolean arrays enum CipherFlagLocation { CAESAR, PLAYFAIR, VIGENERE, ATBASH, MORSE, AUTOKEY, NUM_CIPHERS, ENCODE, DECODE, INPUT_FILE, OUTPUT_FILE, SIZE }; std::string flagStrings[SIZE]; //Error handling bool failFlag = false; //Set if something fails //Sets a typedef for the functions for the return typedef std::string (*Fn)(std::ifstream&, bool); /** * @brief Set the array of strings that controls what each flag should look * */ void setFlagStrings(){ flagStrings[CAESAR] = "c"; flagStrings[PLAYFAIR] = "p"; flagStrings[VIGENERE] = "v"; flagStrings[ATBASH] = "a"; flagStrings[MORSE] = "m"; flagStrings[AUTOKEY] = "u"; flagStrings[CipherFlagLocation::ENCODE] = "e"; flagStrings[CipherFlagLocation::DECODE] = "d"; flagStrings[INPUT_FILE] = "i"; flagStrings[OUTPUT_FILE] = "o"; } /** * @brief Takes the char** flags and turns them into a std::string to make it easier to process and sets the file names if necessary * * @param argc The number of arguments given * @param argv The arguments given * @param inputFileName The name of the input file if needed * @param outputFileName The name of the output file if needed */ std::vector flagsToStrings(int argc, char** argv, std::string& inputFileName, std::string& outputFileName){ std::vector args; bool getInputFile = false; bool getOutputFile = false; //Step through every element in argv adding it to the string (without the dashes) for(int cnt = 1;cnt < argc;++cnt){ std::string tempArg = argv[cnt]; //If you find a dash add the next character to the if(tempArg.at(0) == '-'){ //Step through every element in the rest of that string, adding all characters to the args string for(unsigned long argCnt = 1;argCnt < tempArg.size();++argCnt){ args.push_back(""); args.at(args.size() - 1) = tempArg.at(argCnt); //If one of the arguments is a file indicator set a flag to look for the file name if(args.at(args.size() - 1) == flagStrings[INPUT_FILE]){ getInputFile = true; } else if(args.at(args.size() - 1) == flagStrings[OUTPUT_FILE]){ getOutputFile = true; } } //Get the input file name if necessary if(getInputFile){ ++cnt; if(cnt < argc){ inputFileName = argv[cnt]; } else{ throw noFileName("Input"); } } //Get the output file name if necessary if(getOutputFile){ ++cnt; if(cnt < argc){ outputFileName = argv[cnt]; } else{ throw noFileName("Output"); } } } else{ //Throw an error } } //Return the vector of strings return args; } /** * @brief Takes an argument, searches the valid arguments that could be passed into the program, and sets the appropriate flags * * @param arg The argument that is currently being checked * @param cipherFlags The array of booleans that act as flags that the rest of the program checks against */ void setArgs(std::string arg, bool cipherFlags[]){ //Check against every flag that can currently be set for(int cnt = CipherFlagLocation::CAESAR;cnt < CipherFlagLocation::SIZE;++cnt){ //If the flag matches set the appropriate boolean and exit the function if(arg == flagStrings[cnt]){ cipherFlags[cnt] = true; return; } } throw invalidFlag(arg); } /** * @brief Checks that the flags given are valid * * @param flags All of the flags that the program can have * @return true If valid options were chosen * @return false If invalid options were chosen */ void checkFlags(bool flags[]){ int counter = 0; //Run through each flag and counter how many are true for(int cnt = CAESAR;cnt < NUM_CIPHERS;++cnt){ if(flags[cnt]){ ++counter; } } //Check if a valid number of ciphers were given. (Only valid answer is 1) if(counter > 1){ throw invalidCombo("too many ciphers are given"); } else if(counter < 1){ throw invalidCombo("no cipher is given"); } //Check if either encode or decode is set, but not both if(flags[CipherFlagLocation::ENCODE] && flags[CipherFlagLocation::DECODE]){ throw invalidCombo("both encode and decode are set"); } else if(!flags[CipherFlagLocation::ENCODE] && !flags[CipherFlagLocation::DECODE]){ throw invalidCombo("neither encode or decode is set"); } } /** * @brief Takes all of the arguments that were given to the program and sets boolean flags inside the program that will change behavior * * @param argc The number of arguments given * @param argv The arguments given * @param cipherFlags The array of booleans that represent the possible options passed in * @param inputFileString The name of the input file, if given * @param outputFileString The name of the output file if given */ void getFlags(int argc, char** argv, bool cipherFlags[], std::string& inputString, std::string& outputString){ //Set the strings that control each setFlagStrings(); //Turn the char** to string array std::vector args = flagsToStrings(argc, argv, inputString, outputString); //Cycle through every argument, checking it's validity and setting the correct flags for(unsigned long argCnt = 0;argCnt < args.size();++argCnt){ //Set all the arguments. If there is an invalid argument the function will throw an error setArgs(args.at(argCnt), cipherFlags); } checkFlags(cipherFlags); } /** * @brief Run the appropriate commands to use a Caesar cipher * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runCaesar(std::ifstream& infile, bool encode){ std::string inputString, cipherString; int offset; Caesar cipher; //Check if the input file is open if(infile.is_open()){ infile >> offset; infile.ignore(10000, '\n'); //Get rid of the \n that is right after the offset std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the cipher offset: "; std::cin >> offset; if(std::cin.fail()){ std::cin.ignore(10000, '\n'); std::cin.clear(); std::cout << "That is an invalid offset\nEnter the cipher offset: "; std::cin >> offset; } std::cout << "Enter the input string: "; std::cin.ignore(10000, '\n'); std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(offset, inputString); } else{ cipherString = cipher.decode(offset, inputString); } return cipherString; } /** * @brief Run the appropriate commands to use a Playfair cipher * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runPlayfair(std::ifstream& infile, bool encode){ std::string keyword, inputString, cipherString; Playfair cipher; //Check if the input file is open if(infile.is_open()){ std::getline(infile, keyword); std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the keyword: "; std::getline(std::cin, keyword); std::cout << "Enter the input string: "; std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(keyword, inputString); } else{ cipherString = cipher.decode(keyword, inputString); } return cipherString; } /** * @brief Run the appropriate commands to use a Vigenere cipher * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runVigenere(std::ifstream& infile, bool encode){ std::string keyword, inputString, cipherString; Vigenere cipher; //Check if the input file is open if(infile.is_open()){ std::getline(infile, keyword); std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the keyword: "; std::getline(std::cin, keyword); std::cout << "Enter the input string: "; std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(keyword, inputString); } else{ cipherString = cipher.decode(keyword, inputString); } return cipherString; } /** * @brief Run the appropriate commands to use an Atbash cipher * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runAtbash(std::ifstream& infile, bool encode){ std::string inputString, cipherString; Atbash cipher; //Check if the input file is open if(infile.is_open()){ std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the input string: "; std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(inputString); } else{ cipherString = cipher.decode(inputString); } return cipherString; } /** * @brief Run the appropriate commands to use Morse code * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runMorse(std::ifstream& infile, bool encode){ std::string inputString, cipherString; Morse cipher; //Check if the input file is open if(infile.is_open()){ std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the input string: "; std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(inputString); } else{ cipherString = cipher.decode(inputString); } return cipherString; } /** * @brief Run the appropriate commands to use an Autokey cipher * * @param infile The input file if one was provided * @param encode Whether the encoding flag was set. True if encoding, false if decoding * @return The new message */ std::string runAutokey(std::ifstream& infile, bool encode){ std::string keyword, inputString, cipherString; Autokey cipher; //Check if the input file is open if(infile.is_open()){ std::getline(infile, keyword); std::getline(infile, inputString); if(infile.fail()){ failFlag = true; cipherString = "Input file has an incorrect format\n"; } } //Otherwise prompt for the appropriate strings else{ std::cout << "Enter the keyword: "; std::getline(std::cin, keyword); std::cout << "Enter the input string: "; std::getline(std::cin, inputString); } //Check if there was an error reading the file if(failFlag){ return cipherString; } //Run the correct cipher if(encode){ cipherString = cipher.encode(keyword, inputString); } else{ cipherString = cipher.decode(keyword, inputString); } return cipherString; } /** * @brief Uses the flags to determine which cipher needs to be run * * @param cipherFlags The possible flags given to the program * @return The appropriate run___ function based on the flags */ //Returns the correct function for the flags that are set Fn getCipher(const bool cipherFlags[]){ if(cipherFlags[CAESAR]){ return runCaesar; } else if(cipherFlags[PLAYFAIR]){ return runPlayfair; } else if(cipherFlags[VIGENERE]){ return runVigenere; } else if(cipherFlags[ATBASH]){ return runAtbash; } else if(cipherFlags[MORSE]){ return runMorse; } else if(cipherFlags[AUTOKEY]){ return runAutokey; } //If it didn't trip one of the flags, there was an error before this else{ std::cout << "There was an error selecting the appropriate function"; exit(0); } } #endif //HELPER_FUNCTIONS_HPP