1

I just started learning computer science.

I'm studying through CS50 taught at Harvard online. Well, I'm working on this one problem where I need to get the key from the user in command line, then a plaintext, and then shift that text for key amount of numbers in ASCII to make a ciphertext.

here is what I've got so far.

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

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

    {
        string plaintext = get_string("plaintext:  ");

        int key = atoi(argv[1]);
        int n = strlen(plaintext);
        char chr[n];

        printf("ciphertext: ");
        for (int i = 0; i < n; i++)
        {
            chr[i] = plaintext[i];
            if (isalpha(chr[i]))
            {
                if (isupper(chr[i]))
                {
                    chr[i] = (chr[i] - 65 + key) % 26 + 65;
                    printf("%c",chr[i]);    
                }
                else if (islower(chr[i]))
                {
                    chr[i] = (chr[i] - 97 + key) % 26 + 97;
                    printf("%c",chr[i]);    
                }            
            }
            else
            {
                printf("%c",chr[i]);        
            }
        }
    printf("\n");
    }
}

well I know this seems very floppy but man it's my second week into programming while working full time.

anyways, I'm trying to have the user to run this program by using ./caesar "any number for the key".

if the user puts in any other things, then I'd like to print "Usage: ./caesar key\n"

so far the only way i can think of is making the if statement with argc != 2 so that I can at least make sure the user puts in just one command on top of the program's name.

but the problem is that if the user puts in other things such as ./caesar HELLO ./caesar YolO

the program still runs.

I'm trying to figure out what I can do in order to prevent that from happening.

Really appreciate your time for reading this and help.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1005hoon
  • 21
  • 2
  • 1
    Looks like you're missing an `else` after the initial `if` ? – RamblinRose Jan 06 '20 at 14:03
  • 2
    I suggest using `'A'` instead of `65` and `'a'` instead of `97`. – Fiddling Bits Jan 06 '20 at 14:03
  • 1
    I'm quite certain your main should be `int main(int argc, char* argv[])` see [this SO Q/A](https://stackoverflow.com/questions/12072600/why-main-in-c-is-not-overloaded-to-use-stdstring) - unless I'm way behind on C++.... – RamblinRose Jan 06 '20 at 14:28
  • 1
    There is no `string` type in C. – RobertS supports Monica Cellio Jan 06 '20 at 14:29
  • @1005hoon RobertS - Reinstate Monica makes a good point, is this supposed to be C or C++ ? – RamblinRose Jan 06 '20 at 14:31
  • @RobertS-ReinstateMonica CS50-stupidity – J. Doe Jan 06 '20 at 14:32
  • This program will not compile neither as C nor C++. – Lxer Lx Jan 06 '20 at 14:39
  • @RamblinRose OP could make a typedef, like `typedef char string;` but I strictly do *not* recommend that style, because it may confuses and let people think, who do not know or see the typedef, that there would be a type of `string` in C. – RobertS supports Monica Cellio Jan 06 '20 at 14:39
  • @RobertS - Reinstate Monica ha, indeed...but since `#include ` I assumed that was not happening. – RamblinRose Jan 06 '20 at 14:44
  • @RobertS-ReinstateMonica Do you mean `typedef char* string;` ? – Lxer Lx Jan 06 '20 at 15:32
  • @RobertS-ReinstateMonica Well, I'm studying with this online course CS50 taught at Harvard and they made their own IDE with which students can use. In their CS50 Library, they made features as get_int, or get_string and even string data type so that students beginning to learn programming can benefit from them. I didn't know that there is no string type in C until now haha . Now i'm into lecture 3, the lecturer now tells us that the string is just an array of characters stored back to back, not an actual data type in C. – 1005hoon Jan 07 '20 at 04:14
  • @J.Doe haha yea bud . Well I'm thinking of getting the basic idea of programming through this course because I thought it was the best oriented course compared to the others. well but I don't think it's the best course to teach a programming language tho. so I'm thinking of just taking lectures up to 4 (which teaches about Memory and Data Structure) and study languages with other sources – 1005hoon Jan 07 '20 at 04:19
  • Don't forget to use the `` header to provide the definitions of types such as `string` (which are not a part of standard C, and routinely raise the question "is this meant to be C++" where the `` header provides a type `std::string`). – Jonathan Leffler Jan 17 '20 at 04:37

2 Answers2

5

That is the reason way the old atoi function has been superseded by strtol. The former only try to convert an initial numeric part from the given string, while the latter also tells what remains after the converted part.

So to make sure that the user has given a number as sole argument, you could do:

int main(int argc, char *argv[]) {
    long key;
    int reject = 0;

    if (argc != 2) {                         // ensure one argument
        reject = 1;
    }
    else {
        char *end;
        key = strtol(argv[1], &end, 10);    // ensure not empty and numeric
        if ((*end != 0) || (end == argv[1])) reject = 1;
     }
    if (reject) {
        printf("Usage: %s key\n", argv[0]);
    return 1;
}

sscanf could be used too. It even allows blank characters after the number, which should not happen as the argv array is blank separated:

int main(int argc, char *argv[]) {
    int key;
    char empty[2];

    if ((argc != 2) || (sscanf(argv[1], "%d%1s", &key, empty) != 1)) {
        printf("Usage: %s key\n", argv[0]);
        return 1;
    }
    ...
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • The resulting value can be out of range when using `strtol`. `errno` should be checked against `ERANGE`. – Lxer Lx Jan 06 '20 at 14:48
  • @LxerLx: if the value is out of range, LONG_MAX (or LONG_LMIN for negative values) is consistently returned. So it may not be the given value but can still be used as a key. Anyway using keys greater than 26 for a caesar cipher does not really make sense... – Serge Ballesta Jan 06 '20 at 15:06
  • **If** using keys greater than 26 does not really make sense, then the values outside of that range, that is, negative values and values grater than 26 should also be rejected. And, "_So it may not be the given value..._" is a big problem in general. – Lxer Lx Jan 06 '20 at 15:22
  • @LxerLx: I must acknowledge that I was nitpicking... Of course you are right and out of range values should always be tested :-). My comment was just to insist on `strtol` returning sensible values even on the out of range case. – Serge Ballesta Jan 06 '20 at 15:27
-1

To rephrase your question: You want to perform validation on user input, where the only legal input values are numeric strings: 123 and 4534 and 000 could all be considered valid, but hello world and f00 and one hundred thirty two are not valid.

In most programming languages, including C, strings are more or less just arrays of individual char elements (in other words, char is a primitive data type, but string is not.... C doesn't have a string data type at all. hint: take a look at the way you've declared main). So how can you use that to your advantage to validate the input to this program? Easy enough: every character in the input string must come from the domain [0123456789] (plus the null terminator \0 at the end...).

So to solve this problem, you just need to implement a check that verifies that the inputted value has that property. There are a number of ways to do that, maybe the easiest of which is:

int num;
int y = scanf("%d", &num);

then check the value of y. It will be set to the number of valid inputs that scanf read (so it will be 1 if a number was given).

Note:
as mentioned in the comments, this will also accept things like 123abc (it will strip off the numbers and ignore the characters at the end). If you absolutely must have only numbers for input, scanf might not be the right function. In that case, using fgets to read the input as a string and then looping over each input to check that it is from the valid input domain might be a better approach.

Z4-tier
  • 7,287
  • 3
  • 26
  • 42
  • 1
    But if the input is, for instance, "123abc" then `scanf` will still return 1 on invalid input. And no, I am not who down voted your answer, – Lxer Lx Jan 06 '20 at 14:57
  • Feel free to use the `%1s` part from my answer to fix the problem raised by @LxerLx (not the downvoter either...) – Serge Ballesta Jan 06 '20 at 15:08
  • Good point. I added a note to explain the behavior of `scanf` in this case. I think this is still a good approach for OP's purpose as it minimizes complexity, as long as that caveat is understood, so I will leave it here for now regardless of downvoting naysayers :) – Z4-tier Jan 06 '20 at 15:18