-3

I'm trying to create a program that accepts cmd line arguments to encipher a plaintext! The program must accept one cmd line argument after its name when making it and this would be the key which by the plaintext (only) alphabetical characters are rotated by this key (e.g. it's number is added to the real alphabet ASCII number resulting in another alphabet to be printed!

it is supposed to print an error message when one argument is present (e.g. here:/make encipher) instead of here:/make encipher 12 <-- 12 = key!

I am getting a segmentation fault when running the program without the key argument, why?

This is the full code. I'm posting it because I need to learn where is my fault's exact location and why is it triggered?!


#include <cs50.h>
#include <stdio.h>
#include <stdlib.h> //  To use atoi (converting a string to an int)
#include <ctype.h>
#include <string.h>

bool key_is_numb(string argv[]);
void encipher(string txt, int key);

int main(int argc, string argv[])
{
    if (key_is_numb(argv) == false)
        {
            printf("Usage: ./caesar key\n");
            return 1;
        }

    else
    {
    int key = atoi(argv[1]);
    string plaintext;

            if (argc == 2 && key > 0)
                {
                    plaintext = get_string("plaintext: ");
                    encipher(plaintext, key);  //  A function that prints the ciphered text
                    return 0;   //  returns Zero as main return value which means "All good"!
                }

            else if (argc == 1 || argc > 2 || key <= 0)
                {
                    printf("Usage: ./caesar key\n");
                    return 1;
                }

    }   //  End else.

}   // End main()å func.



bool key_is_numb(string argv[])
{
    int n = strlen(argv[1]);

    for (int i = 0; i < n; i++) //  checking element by element in the second string of the argv[] array of strings
    {
        if (isdigit(argv[1][i]) == 0)   //  if the entered string "key" contains chars other than digits.
        {
            return false;   //  break out of the if statement & the entire function key_is_numb()
                            //  and return false as soon as a letter is encountered.
        }

        else
        {
            continue;   //  go up & start the next iteration for the for loop.
        }
    //  if only digits encountered then this for loop will come to an end and exist from here.
    }   //  End for loop

    return true;   //  function exits and return boolean true from here.

}   // End key_is_numb() func.


void encipher(string txt, int key)
{
    printf("ciphertext: ");
    for (int i = 0, n = strlen(txt); i <= n; i++)    // strlen counts the number of elements in a string excluding '\0'
    {
        char c = txt[i];

        if (isalpha(c))
        {
            if (isupper(c))
            {
                char m = 'A';   //  This is a modifyer character equals to 'A' = 65 so that it is indexed @ ZERO!
                printf("%c", (c - m + key) % 26 + m );
                //c = ((((int)txt[i] - 65) + key) % 26) + 65; //  char c = 65 <-- 65 is an ASCII code equals 'A'
            }
            else if (islower(c))
            {
                char m = 'a';   //  This is a modifying character 'a' = 97
                printf("%c", (c - m + key) % 26 + m );
            }
        }//  End if(alpha).

        else
        {
            printf("%c", c);
        }

    }   //  End for().
        printf("\n");

}   //  End encipher() func.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
The_M_Code
  • 127
  • 2
  • 10

3 Answers3

3
int n = strlen(argv[1]);

in key_is_numb() and

int key = atoi(argv[1]);

in main().

If you didn't enter a key argument, argv[1] as equal as argv[argc] is a null pointer as stated in C17, §5.1.2.2.1/2.

Any attempt to access its data is undefined behavior and probably caused the segmentation fault.

2

Well you are assuming that argv[1] is defined in key_is_numb. However, in C and C++, the second parameter of the main function contains command line arguments. Which, in your case will be the name of the binary as the first element, then any other arguments. This is why when you are running the program without arguments, it will segfault, as there are no argument to put in argv, and no default value either.

You should always check the size of argv, by using the number stored in argc, before trying to read anything in argv.

Your segmentation fault comes from this line int n = strlen(argv[1]);, but I'd highly suggest you to learn to use debugger software like valgrind, which if the program has been compiled with debug flag will tell you the exact line.

Other debugger are really useful too, so you should learn to use them, as they usually report this kind of errors.

Phantomas
  • 206
  • 1
  • 6
2

Your code asumes there is always an argv[1]. You should check argc which tells the number of arguments. For example:

int main(int argc, string argv[])
{
    if (argc < 2) {
        printf("Key required\n");
        exit (1);
    }
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41