123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /*
-
- QUICKCYPHER - quickly encode messages using a substitution cypher
- The purpose of this program is to emulate the workings of a simple substitution
- cypher. The cypher does not distinguish between "encrypted" or "decrypted"
- states; rather it simply translates between the characters contained in the
- cypher and allows plain passthrough of any other remaining characters.
- Here's an example of a valid, albeit weak, cypher structure:
- TENIS
- -----
- POLAR
- When in action, this cypher will take a letter T and substitute it by P, E by
- O, N by L... etc. The opposite is also done: P becomes T, O becomes E... And
- finally, if no match within the cypher domain is found, the letter is passed
- on with no translation whatsoever (H would remain H, U remains U...)
- This cypher is valid because it contains two rows that are of the same length
- and contain distinct characters - no two equal characters appear in any row.
- Your cyphers may contain any character (alphanumeric, non-alphanumeric,
- accented, extended Unicode...) depending on the complexity of the cypher you
- wish to have provided that character collisions do not happen.
- Copyright (C) 2015 Klaus Zimmermann
- 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/>.
- To report bugs, request features or other inquiries, please contact me
- through my GNUSocial account @kzimmermann or visit my code repository at
- https://notabug.org/kzimmermann
- */
- #include <iostream>
- #include <string>
- #include <vector>
- #include <unistd.h>
- #include "cypher.h"
- bool validate_cypher(std::string row1, std::string row2)
- {
- bool statuscode = true;
- for (auto letter1 : row1) {
- for (auto letter2 : row2) {
- if (letter1 == letter2) {
- std::cout << "Warning: cypher collision detected at '" << letter1 << "'!\n";
- statuscode = false;
- }
- }
- }
- return statuscode;
- }
- // Begin translation. Note that it's symmetrical, as in we don't exactly have
- // a "state" saying encypted or decrypted. The cypher takes a letter, sees if
- // it has it available and rotates it. That's it.
- std::string translate(std::string input, std::string r1, std::string r2) {
- std::string output = ""; // this will become the final string.
- bool match = false;
- int row(0);
- int index(0);
- for (auto letter : input) {
- match = false;
- for (auto letter1 = r1.begin();
- letter1 != r1.end(); ++letter1) {
- if (letter == *letter1) {
- // we got a match!
- match = true;
- row = 1;
- index = letter1 - r1.begin();
- break;
- }
- }
- if (match == false) {
- for (auto letter2 = r2.begin();
- letter2 != r2.end(); ++letter2) {
- if (letter == *letter2) {
- // we got a match!
- match = true;
- row = 2;
- index = letter2 - r2.begin();
- break;
- }
- }
- }
- // uncomment the following block for debugging!
- /*
- std::cout << "Now manipulating letter " << letter << ", match is "
- << match << " row is " << row << " index is " << index << std::endl;
- */
- if (match) {
- std::string newletter;
- // get the letter in the opposite row at the same index:
- if (row == 1) {
- newletter = r2.at(index);
- }
- else if (row == 2) {
- newletter = r1.at(index);
- }
- // compose the new string by appending it to the string:
- output.append(newletter);
- }
- else {
- // push the character unchanged:
- std::string unchanged;
- unchanged = std::string(1, (char)letter);
- output.append(unchanged);
- }
- }
- return output;
- }
- // Instructions for use:
- void helper()
- {
- std::cout << "Translates text using cypher substitution." << std::endl;
- std::cout << "If no arguments are given, will enter 'interactive mode'\n";
- std::cout << "Otherwise pass a stream of data as it's input for translation.\n";
- }
- int main(int argc, char * argv[])
- {
- std::string row1 = cypher::row1;
- std::string row2 = cypher::row2;
- if (!validate_cypher(row1, row2)) {
- std::cout << "You have a corrupted cypher.\n";
- std::cout << "Please fix it before running this program.\n";
- return 1;
- }
- // finalstring will be composed by appending samplestring to it:
- std::string samplestring;
- std::string finalstring;
- // decide if we're running as a script or in "interactive mode":
- if (isatty(STDIN_FILENO)) {
- // we're running in interactive mode.
- if (argc > 1 && (std::string(argv[1]) == "--help"
- || std::string(argv[1]) == "-h")) {
- helper();
- return 0;
- }
- std::cout << "Enter the text to be translated (terminate with EOF):\n>_ ";
- while (std::getline(std::cin, samplestring)) {
- finalstring.append(translate(samplestring, row1, row2));
- // newlines aren't appended, so we must add them manually:
- finalstring.append(std::string(1, (char)'\n'));
- }
- std::cout << finalstring << std::endl;
- }
- else {
- // read the content from stdin:
- while (std::getline(std::cin, samplestring)) {
- finalstring.append(translate(samplestring, row1, row2));
- // newlines aren't appended, so we must add them manually:
- finalstring.append(std::string(1, (char)'\n'));
- }
- std::cout << finalstring << std::endl;
- }
- return 0;
- }
|