Files
CipherStream/helperFunctions.hpp

515 lines
14 KiB
C++

//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 <http://www.gnu.org/licenses/>.
*/
#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 <iostream>
#include <fstream>
#include <string>
#include <vector>
//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<std::string> flagsToStrings(int argc, char** argv, std::string& inputFileName, std::string& outputFileName){
std::vector<std::string> 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<std::string> 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