main.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. QUICKCYPHER - quickly encode messages using a substitution cypher
  3. The purpose of this program is to emulate the workings of a simple substitution
  4. cypher. The cypher does not distinguish between "encrypted" or "decrypted"
  5. states; rather it simply translates between the characters contained in the
  6. cypher and allows plain passthrough of any other remaining characters.
  7. Here's an example of a valid, albeit weak, cypher structure:
  8. TENIS
  9. -----
  10. POLAR
  11. When in action, this cypher will take a letter T and substitute it by P, E by
  12. O, N by L... etc. The opposite is also done: P becomes T, O becomes E... And
  13. finally, if no match within the cypher domain is found, the letter is passed
  14. on with no translation whatsoever (H would remain H, U remains U...)
  15. This cypher is valid because it contains two rows that are of the same length
  16. and contain distinct characters - no two equal characters appear in any row.
  17. Your cyphers may contain any character (alphanumeric, non-alphanumeric,
  18. accented, extended Unicode...) depending on the complexity of the cypher you
  19. wish to have provided that character collisions do not happen.
  20. Copyright (C) 2015 Klaus Zimmermann
  21. This program is free software: you can redistribute it and/or modify
  22. it under the terms of the GNU General Public License as published by
  23. the Free Software Foundation, either version 3 of the License, or
  24. (at your option) any later version.
  25. This program is distributed in the hope that it will be useful,
  26. but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. GNU General Public License for more details.
  29. You should have received a copy of the GNU General Public License
  30. along with this program. If not, see <http://www.gnu.org/licenses/>.
  31. To report bugs, request features or other inquiries, please contact me
  32. through my GNUSocial account @kzimmermann or visit my code repository at
  33. https://notabug.org/kzimmermann
  34. */
  35. #include <iostream>
  36. #include <string>
  37. #include <vector>
  38. #include <unistd.h>
  39. #include "cypher.h"
  40. bool validate_cypher(std::string row1, std::string row2)
  41. {
  42. bool statuscode = true;
  43. for (auto letter1 : row1) {
  44. for (auto letter2 : row2) {
  45. if (letter1 == letter2) {
  46. std::cout << "Warning: cypher collision detected at '" << letter1 << "'!\n";
  47. statuscode = false;
  48. }
  49. }
  50. }
  51. return statuscode;
  52. }
  53. // Begin translation. Note that it's symmetrical, as in we don't exactly have
  54. // a "state" saying encypted or decrypted. The cypher takes a letter, sees if
  55. // it has it available and rotates it. That's it.
  56. std::string translate(std::string input, std::string r1, std::string r2) {
  57. std::string output = ""; // this will become the final string.
  58. bool match = false;
  59. int row(0);
  60. int index(0);
  61. for (auto letter : input) {
  62. match = false;
  63. for (auto letter1 = r1.begin();
  64. letter1 != r1.end(); ++letter1) {
  65. if (letter == *letter1) {
  66. // we got a match!
  67. match = true;
  68. row = 1;
  69. index = letter1 - r1.begin();
  70. break;
  71. }
  72. }
  73. if (match == false) {
  74. for (auto letter2 = r2.begin();
  75. letter2 != r2.end(); ++letter2) {
  76. if (letter == *letter2) {
  77. // we got a match!
  78. match = true;
  79. row = 2;
  80. index = letter2 - r2.begin();
  81. break;
  82. }
  83. }
  84. }
  85. // uncomment the following block for debugging!
  86. /*
  87. std::cout << "Now manipulating letter " << letter << ", match is "
  88. << match << " row is " << row << " index is " << index << std::endl;
  89. */
  90. if (match) {
  91. std::string newletter;
  92. // get the letter in the opposite row at the same index:
  93. if (row == 1) {
  94. newletter = r2.at(index);
  95. }
  96. else if (row == 2) {
  97. newletter = r1.at(index);
  98. }
  99. // compose the new string by appending it to the string:
  100. output.append(newletter);
  101. }
  102. else {
  103. // push the character unchanged:
  104. std::string unchanged;
  105. unchanged = std::string(1, (char)letter);
  106. output.append(unchanged);
  107. }
  108. }
  109. return output;
  110. }
  111. // Instructions for use:
  112. void helper()
  113. {
  114. std::cout << "Translates text using cypher substitution." << std::endl;
  115. std::cout << "If no arguments are given, will enter 'interactive mode'\n";
  116. std::cout << "Otherwise pass a stream of data as it's input for translation.\n";
  117. }
  118. int main(int argc, char * argv[])
  119. {
  120. std::string row1 = cypher::row1;
  121. std::string row2 = cypher::row2;
  122. if (!validate_cypher(row1, row2)) {
  123. std::cout << "You have a corrupted cypher.\n";
  124. std::cout << "Please fix it before running this program.\n";
  125. return 1;
  126. }
  127. // finalstring will be composed by appending samplestring to it:
  128. std::string samplestring;
  129. std::string finalstring;
  130. // decide if we're running as a script or in "interactive mode":
  131. if (isatty(STDIN_FILENO)) {
  132. // we're running in interactive mode.
  133. if (argc > 1 && (std::string(argv[1]) == "--help"
  134. || std::string(argv[1]) == "-h")) {
  135. helper();
  136. return 0;
  137. }
  138. std::cout << "Enter the text to be translated (terminate with EOF):\n>_ ";
  139. while (std::getline(std::cin, samplestring)) {
  140. finalstring.append(translate(samplestring, row1, row2));
  141. // newlines aren't appended, so we must add them manually:
  142. finalstring.append(std::string(1, (char)'\n'));
  143. }
  144. std::cout << finalstring << std::endl;
  145. }
  146. else {
  147. // read the content from stdin:
  148. while (std::getline(std::cin, samplestring)) {
  149. finalstring.append(translate(samplestring, row1, row2));
  150. // newlines aren't appended, so we must add them manually:
  151. finalstring.append(std::string(1, (char)'\n'));
  152. }
  153. std::cout << finalstring << std::endl;
  154. }
  155. return 0;
  156. }