Vigenere cipher C program I implemented a Vigenere cipher that preserves case and can also decrypt if you pass the -d argument. Usage is ./vigenere &lt;plaintext|ciphertext&gt; key [-d] and it works fine but I think there are probably some things I could do better. So, just looking for critique.
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;ctype.h&gt;
#include &lt;string.h&gt;

#define ENCRYPT 1
#define DECRYPT -1

int mod(int x, int n)
{
    return (x % n + n) % n;
}

char *vigenere(char *input, char *keystream, int mode)
{
    if (strlen(keystream) != strlen(input))
        return NULL;
    
    int output_len = strlen(input), offset = 'a', wasupper = 0; 
    char *output = malloc(output_len);
    
    for (int i = 0; i &lt; output_len; ++i)
    {
        if (isupper(input[i]))
        {
            input[i] = tolower(input[i]);
            wasupper = 1;
        }
        else
            wasupper = 0;
            
        output[i] = mod((input[i] - offset) + (keystream[i] - offset) * mode, 26) + offset;
        
        if (wasupper)
            output[i] = toupper(output[i]);
    }

    return output;
}

char *gen_keystream(char *key, int keystream_len)
{
    int key_len = strlen(key), j = 0;
    char *keystream = malloc(keystream_len);
    
    for (int i = 0; i &lt; keystream_len; j = ++i % key_len)
        keystream[i] = key[j];
        
    return keystream;
}

int main(int argc, char **argv)
{
    if (argc &lt; 3)
        return EXIT_FAILURE;
        
    int mode = ENCRYPT;
        
    if (argc &gt; 3 &amp;&amp; strcmp(argv[3], &quot;-d&quot;) == 0)
        mode = DECRYPT;
        
    int keystream_len = strlen(argv[1]);
    char *keystream = gen_keystream(argv[2], keystream_len);
    char *result = vigenere(argv[1], keystream, mode);
    
    printf(&quot;%s\n&quot;, result);

    free(keystream);
    free(result);
    
    return EXIT_SUCCESS;
}
Bug: no null character
printf(&quot;%s\n&quot;, result); fails as result does not point to a string.  That data lacks a null character and is short, by 1 of allocated memory needed.
// char *output = malloc(output_len);
char *output = malloc(output_len +1);
output[output_len] = '\0';

Similar problem with decryptor.
No protection against non-A-Z characters
I'd expect only encrypting/decrypting when the input[] is an A-Z.
I suspect code may generate output[i] == 0 for select inputs, rendering the string short.

Pedantic bug: Negative char
When input[i] &lt; 0, isupper(input[i]) is UB.  Better to access the string via an unsigned char * for is...().
A negative char value also messes-up mod().  When x &lt; 0, x % n code does not perform a Euclidean mod.