3 of 6
added 1279 characters in body
red_eight
  • 672
  • 3
  • 13

Caesar Cipher that takes an arbitrary offset

This Caesar cipher can take any integer and it will wrap around the correct number of times and still perform the encryption/decryption.

Let's say we have a text file named dog.

~$ cat dog
The quick brown fox jumps over the lazy dog...


If the user enters a normal offset (1-26), it will perform like any other Caesar cipher.

~$ ./caesar dog 1
~$ cat dog
Uif rvjdl cspxo gpy kvnqt pwfs uif mbaz eph...


Negative numbers can be used to shift backwards.

~$ ./caesar dog -1
~$ cat dog
The quick brown fox jumps over the lazy dog...


Now, here's the beauty of it. Choosing an offset of 1 is no different then choosing an offset of, say, 53 or -51. This algorithm will correctly wrap around.

~$ ./caesar dog -51
~$ cat dog
Uif rvjdl cspxo gpy kvnqt pwfs uif mbaz eph...
~$ ./caesar dog 51
~$ cat dog
The quick brown fox jumps over the lazy dog...

I wrote this a long time ago and I posted it on some other programming site back in the day. I haven't really written much pure c code since. I'm guessing there's plenty of room for improvement.

Compiles with:
gcc -ansi -pedantic -Wall -Werror -O3 -o caesar caesar.c

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

/* #define offset (-1) */ /* My chosen default offset */
#define lstart (0x61) /* Start of lowercase ASCII alphabet */
#define ustart (0x41) /* Start of uppercase ASCII alphabet */
#define nalpha 26 /* Number of letters in my alphabet */

int main(int argc, char **argv) {
    int ec, wrap, offset;
    FILE *fp;
    
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <filename> <offset>\n", argv[0]);
        return 1;
    }

    fp = fopen(argv [1], "r+b");

    offset = atoi (argv[2]);
    
    if(!fp) {
        fprintf(stderr, "Error: could not open file!\n"); 
        return 1; 
    }
    
    while((ec = fgetc(fp)) != EOF) {        
        if(isalpha(ec)) { 
            if(islower(ec)) {
                wrap = (ec + offset - lstart) % nalpha;
                wrap = (wrap < 0) ? nalpha + wrap : wrap; 
    
            } else {
                wrap = (ec + offset - ustart) % nalpha;
                wrap = (wrap < 0) ? nalpha + wrap : wrap; 
            }
                            
            ec = islower(ec) ? wrap + lstart : wrap + ustart;
             
            fseek(fp, -1L, SEEK_CUR);
            fputc(ec, fp);
        }
    }
    
    fclose(fp);
    return 0;
}

Update: I will leave the code I originally posted above. Any modifications that I make will be posted below.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define LSTART (0x61) /* Start of lowercase ASCII alphabet */
#define USTART (0x41) /* Start of uppercase ASCII alphabet */
#define NALPHA 26 /* Number of letters in my alphabet */

int main(int argc, char **argv) {
    int ec, wrap, offset;
    FILE *fp;
    
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <filename> <offset>\n", argv[0]);
        return 1;
    }

    fp = fopen(argv [1], "r+b");

    offset = atoi (argv[2]);
    
    if(!fp) {
        fprintf(stderr, "Error: could not open file!\n"); 
        return 1; 
    }
    
    while((ec = fgetc(fp)) != EOF) {        
        if(isalpha(ec)) { 
            int ascii_offset = islower (ec) ? LSTART : USTART;
            
            wrap = (ec + offset - ascii_offset) % NALPHA;
            wrap = (wrap < 0) ? NALPHA + wrap : wrap;
            ec = islower(ec) ? wrap + LSTART : wrap + USTART;
            
            fputc(ec, stdout);
        }
    }

    fputc('\n', stdout);
    fclose(fp);
    return 0;
}
red_eight
  • 672
  • 3
  • 13