I've written a simple program that encrypt and decrypt a string using Caesar cipher.
There are some examples to show how it works:
$ cae encrypt "Hello World!" 5
Mjqqt Btwqi!
$ cae decrypt "Mjqqt Btwqi!" 5
Hello World!
$ cae encrypt "Hello world!"
Ebiil Tloia!
$ cae decrypt "Ebiil Tloia!"
Hello World!
If no key is given, it will encrypt or decrypt the string with three shifts.
The code is organized in a single main.c
file and a Makefile.
main.c
#include <stdio.h>
#include <getopt.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define LEFT_SHIFT 1
#define RIGHT_SHIFT 0
#define DEFAULT_KEY 3
/* It shifts a character by 'key' positions to right.
*
* Arguments:
* chr - the character (is must be between A-Z or a-z);
* key - the number of shifts (max 25).
*
* Return value:
* The shifted character.
*
* Note:
* If chr is not between A-Z or a-z or key is greater than 25, there is
* undefined behaviour.
*/
char __shift_chr(char chr, const unsigned int key)
{
const int letters = 26;
int diff = isupper(chr) ? 'A' : 'a';
return (chr - diff + key) % letters + diff;
}
/* It takes a string and shifts every character by 'key' positions to left or
* right.
*
* Arguments:
* chiper - the string to shift;
* key - the number of shifts for every character (between 1 and 25).
* mode - LEFT_SHIFT (to left) or RIGHT_SHIFT (to right).
*
* Return value:
* It returns zero on success, otherwise one on error if the key is not
* between 1 and 25 or chiper is a NULL pointer.
*/
int __shift_str(char *chiper, unsigned int key, int mode)
{
const short int letters = 26;
if (!chiper || (key < 1 && key > 25))
return 1;
/* Because chiper function works only with right shift. */
if (mode == LEFT_SHIFT)
key = letters - key;
while (*chiper != '\0') {
if (isalpha(*chiper))
*chiper = __shift_chr(*chiper, key);
chiper++;
}
return 0;
}
/* It encrypt a null-terminated byte string with `key` left shifts.
*
* Arguments:
* - *str - the null-terminated byte string;
* - key - number of shifts (between 1 and 25).
*
* Return value:
* Zero on success, otherwhise a positive value.
*/
int cae_encrypt(char *str, const short int key)
{
return __shift_str(str, key, LEFT_SHIFT);
}
/* It decrypt a null-terminated byte string.
*
* Arguments:
* - *str - the null-terminated byte string;
* - key - number of shifts (between 1 and 25).
*
* Return value:
* Zero on success, otherwhise a positive value.
*/
int cae_decrypt(char *str, const short int key)
{
return __shift_str(str, key, RIGHT_SHIFT);
}
int main(int argc, char *argv[])
{
if (argc != 4 && argc != 3) {
printf("usage: cae <encrypt|decrypt> <message> [key]\n\n");
return 1;
}
unsigned int key;
if (argc == 3)
key = DEFAULT_KEY;
else
key = strtoul(argv[3], NULL, 0);
if (key < 1 || key > 25) {
fprintf(stderr, "key must be between 1 and 25\n\n");
return 1;
}
char *str = argv[2];
char *command = argv[1];
if (!strcmp(command, "encrypt")) {
cae_encrypt(str, key);
} else if (!strcmp(command, "decrypt")) {
cae_decrypt(str, key);
} else {
fprintf(stderr, "invalid command\n\n");
return 1;
}
/* We don't check the return value because we checked the arguments
* before we passed them to the function, so it will always return 0.
*/
printf("%s\n", str);
return 0;
}
Makefile
.PHONY = clean all tests
SHELL = /bin/sh
CC ?= gcc
CFLAGS = -Wall -Wextra -g
EXEC = cae
OBJECTS = main.o
all : $(EXEC)
$(EXEC) : $(OBJECTS)
$(CC) -o $@ $^
clean :
$(RM) *.o $(EXEC)
I think that is not necessary to move functions __shift_chr
and __shift_str
into a new file, because they are small.
Also I forced the program to encrypt with left shifts (and decrypt with right shifts), but It could be possibile to do the same opposite.
$ cae decrypt "Hello World!"
(no key) produceHello World!
? I'm looking at the examples at the top of your post. \$\endgroup\$