1

`

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

// function prototypes

int get_key(int argc, char *argk[]);

int main(int argc, char *argv[]){
    char *argv1 = argv[1];
    get_key(argc, argv1);        
    
    return 0;
}

// function defination.

int get_key(int argc, char *argk){
    // ensure single cmd line arg
    if(argc < 2 || argc > 2){
        printf("usage: ./caesar key(int) !\n");
        return 1;
    } 
    char str1[] = "2";
    char str2[] = "23";
    char str3[] = "a23";
    char str4[] = "23b";
    char str5[] = "a23b";
    
    int len = strlen(str3);
    int number;
    
    for(int i = 0; i < len; i++){ 
        if(isdigit(str3[i])){ // since isdigit() accept only char, not char*.
            number = atoi(str3); // meaning if str3 did contain digits 0-9 from ascii chart.
        }
    }
    return number;
    
}   

`

hello guys, please i want to convert argv[1] to integer, without trusting the user. since isdigit() accept only char, not char*, thats why i am iterating through the str3 and atoi accepts char*. it works fine with str1 and str2. but for other string variables, it returns digits within them, for which cant be converted using atoi() or any other calculations. i want to report error messsage if any of argv[1] elements contains non digits 0-9 from ascii chart. thanks.

10
  • 2
    Use strol. It will populate endptr which you can use to tell whether the whole string is a number. man 3 strtol has examples. Commented Jan 18, 2022 at 21:16
  • 1
    First - don't use atoi(). It will always return a value that could have been a valid input. Commented Jan 18, 2022 at 21:17
  • since isdigit() accept only char, not char*. If you want to do it that way then check the whole string first before calling atoi. Otherwise use strtol as suggested already.' Commented Jan 18, 2022 at 21:22
  • thanks, but from the man page: $ ./a.out 123abc , results: strtol() returned 123. Commented Jan 18, 2022 at 21:28
  • 2
    "from the man page". You can't just blindly copy the example. The example code does not do the check you need. It checks whether the string has any digits at the start. But strtol can definetely be used to do the case you need. So you need to read the manual, understand the paramters and modify the example to do the extra checks to determine whether the string contains any non-digit characters. Commented Jan 18, 2022 at 21:45

1 Answer 1

1

Since you are stating that the user input cannot be trusted, I don't recommend using the function atoi, for two reasons:

  1. The behavior is undefined if the converted number is not representable as an int (i.e. if the number is out of range).

  2. The function will return 0 when the conversion failed. However, when this happens, you will not know whether the user actually entered 0 or whether you got this value due to conversion failure.

Both of these problems can be solved if you use the functon strtol instead of atoi.

In your question, you state that you want to reject the input if it contains any non-digit characters. One problem with the function strtol is that it will skip any leading whitespace characters (e.g. space and tab characters) before attempting to convert a number. If you instead want to reject the input if it contains any leading whitespace characters, then you will have to compare each character with isdigit, before using the function strtol. Doing this will also solve the problem of invalid characters after the number, so that input such as 123abc will get rejected.

Here is a program which will attempt to convert argv[1] to an integer, while performing full input validation:

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

int main( int argc, char *argv[] )
{
    long num;
    char *p;

    //verify that argv[1] is valid
    if ( argc < 2 )
    {
        printf( "not enough parameters!\n" );
        exit( EXIT_FAILURE );
    }

    //verify that argv[1] consists only of digits
    for ( p = argv[1]; *p != '\0'; p++ )
    {
        if ( !isdigit( (unsigned char)*p ) )
        {
            printf( "first program argument must consist only of digits!\n" );
            exit( EXIT_FAILURE );
        }
    }

    //attempt to convert argv[1] to integer
    errno = 0;
    num = strtol( argv[1], &p, 10 );

    //verify that conversion was successful
    if ( p == argv[1] )
    {
        printf( "unable to convert to integer\n" );
        exit( EXIT_FAILURE );
    }

    //verify that no range error occurred
    if ( errno == ERANGE )
    {
        printf( "input is out of range\n" );
        exit( EXIT_FAILURE );
    }

    //everything is ok, so print the result
    printf( "The result is: %ld\n", num );

    return 0;
}
Sign up to request clarification or add additional context in comments.

13 Comments

I like to check to see if *p is 0 after strtol() to see if the entire string was used for the conversion.
And the ERANGE check is only valid if it returns LONG_MAX or LONG_MIN, iirc.
@Shawn: Actually, what I normally do is check all remaining characters of the string, to verify that they are whitespace characters. If they are not all whitespace characters, I reject the input. I normally do this for the following reason: Since strtol accepts and skips leading whitespace characters, it makes sense to also accept trailing whitespace characters. It would not make sense to accept leading, but reject trailing whitespace.
@Shawn: However, in this case, OP stated in the question that the input should only be allowed to consist of digits, so I have verified before the call to strtol that the entire string consists of only digits and nothing else (not even whitespace characters). For this reason, I don't see any need to check how much of the line was actually converted. Actually, I don't see any way how the function strtol could possibly fail, except for a range error. Therefore, the line if ( p == argv[1] ) is probably redundant, too.
@Shawn: According to §7.22.1.4 ¶8 of the ISO C11 standard, if the converted value is not representable as a long, then LONG_MIN or LONG_MAX is returned and errno is set to ERANGE. This does not make checking for ERANGE only valid when one of these values is returned.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.