I think you have too many loops [levels].
If the key length runs out before the word is finished, you restart encrypting the word from the beginning of the word (i.e. a bug).
The main thrust is to loop through all word chars. A single loop that increments i
works, providing it also increments l
[modulo key length].
Here's a cleaned up version [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// NOTE: I don't have GetString on my system
char *FakeGetString(void);
int
main(int argc, char *argv[])
{
// key
// prompts if no key is entered
if (argc < 2) {
printf("Please enter your word key");
return 1;
}
char *key = (argv[1]);
int klen = strlen(key);
if (argc >= 2) {
printf("plaintext:");
#if 0
char *word = GetString();
#else
char *word = FakeGetString();
#endif
int wlen = strlen(word);
printf("ciphertext:");
// current key index
int l = 0;
// starts loop to repeat following for loop
// iterates through word entered by user as plaintext
// advance to next key char [with wrap to beginning if we're short]
for (int i = 0; i < wlen; ++i, l = (l + 1) % klen) {
int num = key[l];
int cipher;
// if original characters are uppercase
if (isupper(word[i])) {
cipher = (word[i] + num - 65) % 26 + 65;
}
// if original characters are lowercase
else if (islower(word[i])) {
cipher = (word[i] + num - 97) % 26 + 97;
}
// all other types of characters
else {
cipher = word[i];
}
printf("%c", cipher);
}
printf("\n");
}
return 0;
}
// NOTE: I don't have GetString on my system
char *
FakeGetString(void)
{
static char buf[1000];
char *cp;
fgets(buf,sizeof(buf),stdin);
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
return buf;
}
UPDATE:
The above code I wrote was pretty close but wasn't Vigenere because your original equation was off. The key value has to be an offset/row number, so it needs to have 'A'
subtracted from it (i.e. keywords can only be uppercase).
So, here is the corrected version [with some additional cleanups]:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// NOTE: I don't have GetString on my system
char *FakeGetString(void);
int
baseof(int chr)
{
int base;
// if original character is uppercase
if (isupper(chr)) {
base = 'A';
}
// if original character is lowercase
else if (islower(chr)) {
base = 'a';
}
// anything else
else
base = 0;
return base;
}
int
main(int argc, char *argv[])
{
int kval;
int base;
int i;
// key
// prompts if no key is entered
if (argc < 2) {
printf("Please enter your word key\n");
return 1;
}
char *key = argv[1];
int klen = strlen(key);
// key must be uppercase and we only want row numbers
for (i = 0; i < klen; ++i) {
kval = key[i];
base = baseof(kval);
if (base) {
key[i] = kval - base;
continue;
}
printf("Key value must be only A-Z\n");
return 1;
}
if (argc >= 2) {
printf("plaintext:");
#if 0
char *word = GetString();
#else
char *word = FakeGetString();
#endif
int wlen = strlen(word);
printf("ciphertext:");
// starts loop to repeat following for loop
// iterates through word entered by user as plaintext
// advance to next key char [with wrap to beginning if we're short]
for (i = 0; i < wlen; ++i) {
int wval = word[i];
int cipher;
base = baseof(wval);
// uppercase or lowercase
if (base) {
kval = key[i % klen];
cipher = ((wval - base) + kval) % 26 + base;
}
// all other types of characters
else {
cipher = wval;
}
printf("%c",cipher);
}
printf("\n");
}
return 0;
}
// NOTE: I don't have GetString on my system
char *
FakeGetString(void)
{
static char buf[1000];
char *cp;
fgets(buf,sizeof(buf),stdin);
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
return buf;
}
UPDATE #2:
Your code passed all check50 checks except this one: :( encrypts "world, say hello!" as "xoqmd, rby gflkp!" using "baz" as keyword \ expected output, but not "ciphertext:xoqmd, szz gflkp!\n". It didn't encipher just the word 'say' properly, which is weird.
I tested it using the test data/example from the Wikipedia page on Vigenere, but it only had one ready test example [without spaces or punctuation].
This was the only check which contained a space (before the word 'say'). The spaces have to be directly copied. Perhaps that's why.
The spaces were directly copied, so that was okay. But ...
The correct way is that when a non-alpha char is copied, the key index must not be incremented.
My version used i
to index the phrase and i % klen
to index the key, so the key index would always be incremented [effectively]. That is the bug.
Ironically, I had wondered about this, but didn't have the expanded test data at the time.
So, the solution is to separate the index variables [again :-)].
Here's the corrected version. When I was fixing it I renamed the i
variable to something more descriptive (e.g. widx
) and (re)created the index variable for the key (e.g. kidx
).
Notice that, now, kidx
is only incremented when actually encrypting a character. The "pass through" case does not
#include <stdio.h>
#include <string.h>
#include <ctype.h>
// NOTE: I don't have GetString on my system
char *FakeGetString(void);
int
baseof(int chr)
{
int base;
// if original character is uppercase
if (isupper(chr)) {
base = 'A';
}
// if original character is lowercase
else if (islower(chr)) {
base = 'a';
}
// anything else
else
base = 0;
return base;
}
int
main(int argc, char *argv[])
{
int kval;
int base;
int widx;
int kidx;
// key
// prompts if no key is entered
if (argc < 2) {
printf("Please enter your word key\n");
return 1;
}
char *key = argv[1];
int klen = strlen(key);
// key must be uppercase and we only want row numbers
for (kidx = 0; kidx < klen; ++kidx) {
kval = key[kidx];
base = baseof(kval);
if (base) {
key[kidx] = kval - base;
continue;
}
printf("Key value must be only A-Z\n");
return 1;
}
if (argc < 2)
return 1;
printf("plaintext:");
#if 0
char *word = GetString();
#else
char *word = FakeGetString();
#endif
int wlen = strlen(word);
printf("ciphertext:");
kidx = 0;
// starts loop to repeat following for loop
// iterates through word entered by user as plaintext
// advance to next key char [with wrap to beginning if we're short]
for (widx = 0; widx < wlen; ++widx) {
int wval = word[widx];
int cipher;
base = baseof(wval);
// uppercase or lowercase
if (base) {
kval = key[kidx];
cipher = ((wval - base) + kval) % 26 + base;
kidx = (kidx + 1) % klen;
}
// all other types of characters
else {
cipher = wval;
}
printf("%c",cipher);
}
printf("\n");
return 0;
}
// NOTE: I don't have GetString on my system
char *
FakeGetString(void)
{
static char buf[1000];
char *cp;
fgets(buf,sizeof(buf),stdin);
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
return buf;
}