Cs50 Caesar Check50 fail - c

I am doing Caesar exercise from CS50 course, but it fail.
Here is the code:
# include <stdio.h>
# include <cs50.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
int main(int argc, string argv[])
{
if (argc == 2)
{
string key = argv[1];
int l = strlen(key);
for (int i = 0; i < l; i++)
{
// if key[i] is an alphabet character
if (isalpha(key[i]) != 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
//change charater to number
int k = atoi(argv[1]);
//print Plaintext
string plaintext = get_string("Plaintext: ");
int n = strlen(plaintext);
char ciphertext[n];
//declare plaintext in ASCII
int nplaintext[n];
//Change plaintext to ASCII
for (int i = 0; i < n; i++)
{
nplaintext[i] = (int)plaintext[i];
}
//Declare ASCII for ciphertext which we name "plusplaintext"
int plusnplaintext[n];
for (int i = 0; i < n; i++)
{
//if Capital
if ((nplaintext[i] < 91) && (nplaintext[i] > 64))
{
plusnplaintext[i] = 65 + ((nplaintext[i] + k) - 65) % 26 ;
}
//if Lowercase
else if ((nplaintext[i] < 123) && (nplaintext[i] > 96))
{
plusnplaintext[i] = 97 + ((nplaintext[i] + k) - 97) % 26 ;
}
//if not character a -> z and A -> Z
else
{
plusnplaintext[i] = nplaintext[i];
}
}
for (int i = 0; i < n; i++)
{
ciphertext[i] = (char)plusnplaintext[i];
}
printf("ciphertext: %s\n", ciphertext);
}
else
{
printf("Usage: ./caesar key\n");
return 1;
}
}
Here is the output from Check50:
:) caesar.c exists.
:) caesar.c compiles.
**:( encrypts "a" as "b" using 1 as key, output not valid ASCII text**
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
:) encrypts "barfoo" as "onesbb" using 65 as key
**:( encrypts "world, say hello!" as "iadxp, emk tqxxa!" using 12 as key, output not valid ASCII text**
:) handles lack of argv[1]
When I test the code, sometimes it gives right result, sometimes it give the wrong result with some more characters at the end...
How can I correct my code?

The problem is with the logic here
//if Capital
plusnplaintext[i] = 65 + ((nplaintext[i] + k) - 65) % 26 ;
and
//if Lowercase
plusnplaintext[i] = 97 + ((nplaintext[i] + k) - 97) % 26 ;
use this logic instead
//if Capital
((((nplaintext[i] - 90) + 25) + key) % 26) + 65
//if Lowercase
((((nplaintext[i] - 122) + 25) + key) % 26) + 97
and it is also not necessary to assign the letters to a string, instead you can directly print it
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int argc, string argv[])
{
// Error Checking on the command line argument
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
else if (argc == 2)
{
for (int i = 0; i < strlen(argv[1]); i++)
{
if (isdigit(argv[1][i]) == 0)
{
printf("Usage: ./caesar key\n");
return 1;
}
}
}
int key = atoi(argv[1]);
string plain_text = get_string("plaintext: ");
printf("ciphertext: ");
for (int i = 0; i < strlen(plain_text); i++)
{
if (isalpha(plain_text[i]) != 0)
{
// printf("%c", plain_text[i] + key);
if (isupper(plain_text[i]))
{
printf("%c", ((((plain_text[i] - 90) + 25) + key) % 26) + 65);
}
else
{
printf("%c", ((((plain_text[i] - 122) + 25) + key) % 26) + 97);
}
}
else
{
printf("%c", plain_text[i]);
}
}
printf("\n");
}

Instead of using the ascii value of a letter in your code, it could be helpful to keep them as characters for the sake of clarity. I took a snippet of your code and changed the values to demonstrate.
//if Capital
if ((nplaintext[i] <= 'Z') && (nplaintext[i] >= 'A'))
{
plusnplaintext[i] = 'A' + ((nplaintext[i] + k) - 'A') % 26 ;
}
This makes it clear what those values represent, and you don't have to reference an ascii chart. More comments would also help a user, or yourself later on, understand what the purpose is for each part of your code.
You could also create some functions outside of your main function. For example, to check for all digits in command line input, or change the letters by the amount given in the key. If you start working on long programs of code, it could be helpful to have functions defined that you can use as many times as you need. It also cleans up your main for clarity.
This function below takes the character of each letter in the original text (char p), then "moves" the character by k spaces (int k, given by the user as the key in the command line). It works when called in a for loop in main that iterates over each letter in the original given string (text[i]), with i being increased for each time the loop executes.
char rotate(char p, int k)
{
// declare variable for the rotated letter to be stored
char c;
// check if it is a letter
if (isalpha(p))
{
// check for lowercase letter
if (islower(p))
{
// subtract ascii value from p to initialize to 0 - 25 for computations
p -= 'a'; // if p is a, it is now initialized to 0, if b to 1, if c to 3, etc
c = (p + k) % 26; // use Caesar's algorithm to rotate letters, 'wrapping' by using % 26
c += 'a'; // add ascii value back to result to get the rotated letter
}
// the only other option is uppercase since we checked for only letters, do the same for uppercase letters as for lowercase
else
{
p -= 'A';
c = (p + k) % 26;
c += 'A';
}
}
// if it is the nul character '\0' return 0, do not print
else if (p == '\0')
{
return 0;
}
// if not a letter or nul character, return the character as is
else
{
c = p;
}
// print the rotated letter which is now stored in c
printf("%c", c);
// return the value of c
return c;
}

Related

Caesar cipher code is not cleared by check50, cannot figure out why

I coded the assignment and followed the best that I could. I can pass all of the Check50 arguments except one! Help?? The validation is correct, but when I run the debugger, it begins to give me problems around the ciphering section. Thank you!
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main(int arg, string argv[])
{
//KEY VALIDATION
if (arg != 2) //checks to make sure that arg is 2 (for # of arguments)
{
printf("Usage: ./caesar key\n");
return 1;
}
else if (arg == 2)
{ // checks key for validity
for(int i = 0, len = strlen(argv[1]); i < len; i++)
{
if (!isdigit(argv[1][i]))
{
printf("Usage: ./caesar key\n");
return 1;
}
}
//int key = atoi(argv[1]);
}
//ciphering
int key = atoi(argv[1]);
string plain = get_string("plaintext: ");
int len_plain = strlen(plain);
string cipher = plain;
for (int x = 0; x < len_plain; x++)
{
if (plain[x] >= 'a' && plain[x] <= 'z')
{
cipher[x] = ((plain[x] + key)%122);
if (cipher[x]<97)
{
cipher[x] = cipher[x] + 96;
}
}
else if (plain[x] >= 'A' && plain[x] <= 'Z')
{
cipher[x] = ((plain[x] + key)%90);
if (cipher[x] < 65)
{
cipher[x] = cipher[x] + 64;
}
}
else
{
cipher[x] = plain[x];
}
}
printf("ciphertext: %s\n", cipher);
}
the check50 results I keep getting are,
:) caesar.c exists.
:) caesar.c compiles.
:) encrypts "a" as "b" using 1 as key
:) encrypts "barfoo" as "yxocll" using 23 as key
:) encrypts "BARFOO" as "EDUIRR" using 3 as key
:) encrypts "BaRFoo" as "FeVJss" using 4 as key
***:( encrypts "barfoo" as "onesbb" using 65 as key
output not valid ASCII text
printf("ciphertext: ");
for (int x = 0, y = len_plain; x < y; x++)
{
//Use islower and isupper instead of typing so much words
if islower(code[i])
{
printf("%c", (((code[i] + k) - 97) % 26) + 97);
}
else if isupper(code[i])
{
printf("%c", (((code[i] + k) - 65) % 26) + 65);
}
//If neither then just print it
else
{
printf("%c", code[i]);
}
}
printf("\n")

C: Reprompting to enter value into argv[] - without closing by error - Caesar Cipher

I am new to programming and I am undertaking CS50 harvard course on eDX. We had assignement for caesar cipher, I am quite satisfied with my code, even though it is not perfect. But I am not sure how to make program no to close by error when not providing any arguments.
According to the course assignement, it should be possible and the output should be "Usage: ./caesar key" - it is working if I use string instead of integer values or if there is multiple values inserted. But with empty values (./caesar) I am getting error in the terminal. Is there any way how to avoid this and just to close program like with entering string for instance? Have checked multiple topics already and being not able to find the was how to do it.
Thanks
PS: have already submited the assignement, but in my mind I am still returning to this topic and want to know if it is possible and how :D
#include <cs50.h>
#include <stdio.h>
#include <string.h>
int main(int argc, string argv[])
{
int k = atoi(argv[1]);
if (argc != 2 || argv[1] == NULL || k <= 0)
{
printf("Usage: ./caesar key\n");
return(1);
}
string p = get_string("Plaintext: ");
printf("Ciphertext: ");
//incrementing each letter
for (int i = 0, n = strlen(p); i < n; i++)
{
//for capital letters
if (p[i] >= 65 && p[i] <= 90)
{
//refreshing alphabet char counting to 65
if (p[i] + k > 90)
{
int m = ((p[i] + k) - 90);
char c = 64 + m;
printf("%c", c);
}
else
{
printf("%c", p[i] + k);
}
}
//for non-capital letters
if (p[i] >= 97 && p[i] <= 122)
{
//refreshing alphabet char counting to 97
if (p[i] + k > 122)
{
int m = ((p[i] + k) - 122);
char c = 96 + m;
printf("%c", c);
}
else
{
printf("%c", p[i] + k);
}
}
//for non-letter characters
if (p[i] < 65 || p[i] > 122)
{
printf("%c", p[i]);
}
}
printf("\n");
}
You need to check the value of argc before you start accessing argv. So rather than setting k and then doing those checks, do most of the checks, then assign k and then check k is a legal value.
int k;
if (argc != 2 || argv[1] == NULL)
{
printf("Usage: ./caesar key\n");
return(1);
}
k = atoi(argv[1]);
if (k <= 0)
{
printf("Usage: ./caesar key\n");
return(1);
}

Vigenere Cipher - Formula Explanation

First of all there is no one that I can ask this kind of question so please pardon me
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[]) {
string key = argv[1];
int l = strlen(argv[1]);
if (argc != 2) {
return 0;
}
for (int i = 0, n = strlen(key); i < n; i++) {
if (!isalpha(key[i])) {
return 0;
}
key[i] = tolower(key[i]);
key[i] = key[i] - 97;
}
string txt = GetString();
for (int k = 0, p = strlen(txt); k < p; k++) {
if (isalpha(txt[k])) {
if (isupper(txt[k])) {
printf("%c", (((txt[k] - 65) + (key[k % l])) % 26 + 65));
}
if (islower(txt[k])) {
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));
}
} else
if (!isalpha(txt[k])) {
printf("%c", txt[k]);
}
}
printf("\n");
return 0;
}
I can't quite get these 2 lines of code
key[i] = key[i] - 97;
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97));
Is there an easy explanation of why did we use the first and how the second one works?
The key used for the Vigenere cypher is supposed to be all letters. The first expression converts the string into an array of offsets, 0 for a, 1 for b, etc. 97 is the ASCII code for 'a'. It would be more readable to write:
for (int i = 0, n = strlen(key); i < n; i++) {
if (!isalpha((unsigned char)key[i])) {
printf("key '%s' must contain only letters\n", key);
return 1;
}
key[i] = tolower((unsigned char)key[i]);
key[i] = key[i] - 'a';
}
For the second expression, if the character txt[k] is a lower case letter, printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97)); computes and prints the transposed letter by adding the shift value (each character in key is used as a shift value one after the other, shifting by 0 for a, 1 for b etc.). Here are the steps:
The program computes the letter index txt[k] - 97, 97 being the ASCII code for 'a',
it then adds the shift value key[k % l], cycling the values in key in a circular fashion,
it takes the modulo 26 to get a letter index between 0 and 25.
it finally adds 97, the ASCII value of 'a' to convert the index back into a lowercase letter.
It would be less redundant and more readable to write it this way:
for (int i = 0, j = 0; txt[i] != '\0'; i++) {
int c = (unsigned char)txt[i];
if (isupper(c)) {
c = (c - 'A' + key[j++ % l]) % 26 + 'A';
} else
if (islower(c)) {
c = (c - 'a' + key[j++ % l]) % 26 + 'a';
}
putchar(c);
}
Also note that argv[1] should not be passed to strlen() before checking that enough arguments have been passed on the command line.
Here is a modified version of the program:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[]) {
if (argc != 2) {
printf("missing key argument\n");
return 1;
}
string key = argv[1];
int klen = strlen(key);
if (klen == 0) {
printf("key cannot be empty\n");
return 1;
}
for (int i = 0; i < klen; i++) {
if (!isalpha((unsigned char)key[i])) {
printf("key '%s' must contain only letters\n", key);
return 1;
}
key[i] = tolower((unsigned char)key[i]) - 'a';
}
string txt = GetString();
for (int i = 0, j = 0; txt[i] != '\0'; i++) {
int c = (unsigned char)txt[i];
if (isupper(c)) {
c = (c - 'A' + key[j++ % klen]) % 26 + 'A';
} else
if (islower(c)) {
c = (c - 'a' + key[j++ % klen]) % 26 + 'a';
}
putchar(c);
}
putchar('\n');
return 0;
}
key[i] = key[i] - 97;
That line's use is to give key[i], which value represents the value of a caracter in ascii it's index in our alphabet. Then, 'a' will be given the value 0, 'b' the value 1 .... , and 'z' the value 25.
As for the second line,
printf("%c", (((txt[k] - 97) + (key[k % l])) % 26 + 97))
it prints the caracter which's ascii value is
(((txt[k] - 97) + (key[k % l])) % 26 + 97))
The substraction of 97 has the same purpose as explained above.
The % 26 is the modulus, ie the remainder of ((txt[k] - 97) + (key[k % l])) when divided per 26 (integer division). Then, 97 is added again to convert the order, or index of the result into a corresponding ascii value.
This page might give you some more insight about the character representation in C.
As for the meaning of k,i and l, I let you grasp the inner functionning of the cypher by yourself, but the whole encryption happens in the second line you wished an explanation for.
PS : The parts with '65' are just the same, but with uppercase letters, since 'A' value in ascii is 65.

CS50 Vigenere - Output Incorrect

Man I thought I had it! I've been working on the Vigenere problem and have gotten close but keep getting this error when I check. It looks like there's problem when the key has to loop back around. Thoughts?
Here is the error:
:) vigenere.c exists
:) vigenere.c compiles
:) encrypts "a" as "a" using "a" as keyword :( encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword \ expected output, but not "xoqmj, yfz gflkp!\n"
:( encrypts "BaRFoo" as "CaQGon" using "BaZ" as keyword \ expected output, but not "CaQAun\n"
:( encrypts "BARFOO" as "CAQGON" using "BAZ" as keyword \ expected output, but not "CAQAON\n"
:) handles lack of argv[1]
:) handles argc > 2
:) rejects "Hax0r2" as keyword
and here's my code:
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
int main(int argc, string argv[])
{
string key = argv[1];
if (argc!= 2)
{
printf("please provide only one perameter \n");
// stop the program
return 1;
}
// iterate over the key to make sure its all alpha
int i,j;
for (i = 0, j = strlen(key); i < j; i++)
{
if (!isalpha(key[i]))
{
printf("please use only alphanumeric values \n");
return 1;
}
}
// now we have a key, "key" from the ONE perameter that is all alpha
string message = GetString();
int k = 0;
int keyindex;
for (i = 0, j = strlen(message); i < j; i++, k++)
{
if (isalpha(message[i]))
{
keyindex = k % strlen(argv[1]);
// covering the upper case letters
if (isupper(message[i]))
{
// covering the upper case letters with upper case key letters
if isupper(key[i])
{
// print cipher according to two upper case
int cipher = ((message[i] - 65 + key[keyindex] - 65) % 26)
+ 65;
printf("%c", cipher);
}
else
{
// print according to upper case message lower case key
int cipher = ((message[i] - 65 + key[keyindex] - 97) % 26)
+ 65;
printf("%c", cipher);
}
}
// this is for the non upper case letters
if (islower(message[i]))
{
if isupper(key[i])
{
// print cipher according to lower case message and
// upper case key letter
int cipher = ((message[i] - 97 + key[keyindex] - 65) % 26)
+ 97;
printf("%c", cipher);
}
else
{
// print according to lower case message and lower case key
int cipher = ((message[i] - 97 + key[keyindex] - 97) % 26)
+ 97;
printf("%c", cipher);
}
}
}
// non alpha symbols
else
{
printf("%c", message[i]);
}
}
// end program after iterating
printf("\n");
}
Problems with your program:
1) Sytax error that should keep it from compiling:
if isupper(key[i]) -> if (isupper(key[i]))
There are two of these so make sure to fix them both.
2) Incrementing k
int k = 0;
...
for (i = 0, j = strlen(message); i < j; i++, k++)
{
if (isalpha(message[i]))
{
keyindex = k % strlen(argv[1]);
There two was to approach this, either increment k on ever character or increment k on every letter. The designer of this problem chose letter so instead we need to do:
int k = 0;
...
for (i = 0, j = strlen(message); i < j; i++)
{
if (isalpha(message[i]))
{
keyindex = k++ % strlen(argv[1]);
3) Use keyindex now that we've defined it:
if isupper(key[i]) -> if isupper(key[keyindex])
There are two of these so make sure to fix them both.
Applying these changes and a little bit of style cleanup, we get:
#include <stdio.h>
#include <cs50.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
if (argc != 2)
{
printf("please provide only one parameter \n");
return 1; // stop the program
}
string key = argv[1];
// iterate over the key to make sure it's all alpha
for (int i = 0, j = strlen(key); i < j; i++)
{
if (!isalpha(key[i]))
{
printf("please use only alphanumeric values \n");
return 1;
}
}
// now we have a key, "key" from the ONE parameter that is all alpha
string message = GetString();
for (int i = 0, k = 0, j = strlen(message); i < j; i++)
{
if (isalpha(message[i]))
{
int keyindex = k++ % strlen(key);
if (isupper(message[i])) // covering the upper case letters
{
if (isupper(key[keyindex]))
{
// print cipher according to both upper case
int cipher = ((message[i] - 'A' + key[keyindex] - 'A') % 26) + 'A';
printf("%c", cipher);
}
else
{
// print cipher according to upper case message and lower case key
int cipher = ((message[i] - 'A' + key[keyindex] - 'a') % 26) + 'A';
printf("%c", cipher);
}
}
else // this is for the non upper case letters
{
if (isupper(key[keyindex]))
{
// print cipher according to lower case message and upper case key letter
int cipher = ((message[i] - 'a' + key[keyindex] - 'A') % 26) + 'a';
printf("%c", cipher);
}
else
{
// print cipher according to both lower case
int cipher = ((message[i] - 'a' + key[keyindex] - 'a') % 26) + 'a';
printf("%c", cipher);
}
}
}
else // non alpha symbols
{
printf("%c", message[i]);
}
}
printf("\n"); // end program after iterating
}
Your code duplicates a lot of logic that you could combine with minor changes -- duplicated logic is one way that hard to find errors creep into code.

Why is my vigenere.c not working?

I keep making changes to the looping part of this code and my check50 always fails. I don't know what's going on. Below is my code:
#include <stdio.h>
#include <ctype.h>
#include <cs50.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, string argv[])
{
// declare variables
int cipherText;
if (argc != 2)
{
printf("Usage: ./vigenere keyword");
printf("\n");
return 1;
}
// keyword is the second command line argument
string key = argv[1];
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if ((key[i] >= '0') && (key[i] <= '9'))
{
printf("Keyword must consist only of letters.");
return 1;
}
}
// get the plaintext
string plainText = GetString();
// encypher - iterate over the characters in string, print each one encrypted
for (int i = 0, j = 0, n = strlen(plainText); i < n; i++, j++)
{
// start the key again if key shorter than plainText
if (j >= strlen(key))
{
j = 0;
}
// skip key[j] if plainText[i] is not an alpha character
if (!isalpha(plainText[i]))
{
j = (j-1);
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j%keylen])%26) + 'A';
printf("%c", cipherText);
}
else if (islower(plainText[i]))
{
cipherText = (plainText[i] - 'a');
cipherText = ((cipherText + key[j%keylen])%26 + 'a');
printf("%c", cipherText);
}
else
{
printf("%c", plainText[i]);
}
}
printf("\n");
return 0;
}
Some answered this: "The first for loop has a problem. The condition is checking for i > keylen when it should be checking for i < keylen".
Also when computing the next output value, the steps should be
(p[i]-65) results in a number between 0 and 25
adding (key[i % keylen]) results in a number between 0 and 50
apply modulo 26 so the number is between 0 and 25 (this is the missing step)
then add 65 to get the output"
and it's what I tried to do.
Given this code:
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0, n = strlen(argv[1]); i < n; i++)
{
if ((key[i] >= '0') && (key[i] <= '9'))
{
printf("Keyword must consist only of letters.");
return 1;
}
}
Your test inside the loop identifies digits as 'not a letter' (which is valid), but ignores punctuation, spaces and so on. You should probably be using if (!isalpha(key[i])) for the test (and it is courteous to print the erroneous character in the error message, which should be printed on standard error, not standard output, and should end with a newline:
fprintf(stderr, "Keyword must consist only of letters (%c found at %d)\n",
key[i], i+1);
You could refine that so it doesn't try printing non-printable characters with %c, but this is a huge step in the right direction.
You really don't need to set n in the loop; you just set keylen before the loop, so you could have written:
for (int i = 0; i < keylen; i++)
However, that is mostly cosmetic. Your real problem lies here:
// start the key again if key shorter than plainText
if (j >= strlen(key))
{
j = 0;
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
You modify the key string on each iteration through the key. Unfortunately, though, if any of the letters in the key is a or A, you've converted that to '\0', which means that strlen(key) returns a different answer from before. So, you should use keylen in place of strlen(). AFAICS, if there isn't an a or A, that part of the code is OK.
Later, you have:
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j%keylen])%26) + 'A';
printf("%c", cipherText);
}
The j % keylen is superfluous; j is already limited to 0 .. keylen-1. Similarly with the code for lower-case text.
Putting these changes together, and dummying up a GetString() function using fgets(), I get:
#include <stdio.h>
#include <ctype.h>
// #include <cs50.h>
#include <stdlib.h>
#include <string.h>
typedef char *string;
static char *GetString(void)
{
static char buffer[4096];
if (fgets(buffer, sizeof(buffer), stdin) == 0)
{
fprintf(stderr, "EOF detected in GetString()\n");
exit(EXIT_SUCCESS);
}
buffer[strlen(buffer) - 1] = '\0';
return buffer;
}
int main(int argc, string argv[])
{
// declare variables
int cipherText;
if (argc != 2)
{
printf("Usage: ./vigenere keyword");
printf("\n");
return 1;
}
// keyword is the second command line argument
string key = argv[1];
int keylen = strlen(argv[1]);
// iterate through keyword to check if alphabetical
for (int i = 0; i < keylen; i++)
{
if (!isalpha(key[i]))
{
printf("Keyword must consist only of letters (%c at %d)\n",
key[i], i+1);
return 1;
}
}
// get the plaintext
string plainText = GetString();
// encypher - iterate over the characters in string, print each one encrypted
for (int i = 0, j = 0, n = strlen(plainText); i < n; i++, j++)
{
// start the key again if key shorter than plainText
if (j >= keylen)
{
j = 0;
}
// skip key[j] if plainText[i] is not an alpha character
if (!isalpha(plainText[i]))
{
j = (j - 1);
}
// makes Aa = 0, Zz = 25 for the uppercase letters
if (isupper(key[j]))
{
key[j] = (key[j] - 'A');
}
// makes Aa = 0, Zz = 25 for lowercase letters
else if (islower(key[j]))
{
key[j] = (key[j] - 'a');
}
if (isupper(plainText[i]))
{
cipherText = (plainText[i] - 'A');
cipherText = ((cipherText + key[j]) % 26) + 'A';
printf("%c", cipherText);
}
else if (islower(plainText[i]))
{
cipherText = (plainText[i] - 'a');
cipherText = ((cipherText + key[j]) % 26 + 'a');
printf("%c", cipherText);
}
else
{
printf("%c", plainText[i]);
}
}
printf("\n");
return 0;
}
Sample run:
$ ./vigenere bakedalaska
What a wonderful world! The news is good, and the Vigenere cipher is solved.
Xhkx d wznvorguv arrwd! Lre oegw ls rogn, aod dlh Vtgwxese mmshpr ac splfig.
$

Resources