How to detect apostrophe in a char* argument? - c

I have a c file such that I pass arguments to as such:
./cfile [-size number]
e.g.
./cfile -size 22
Here is an example code for such a file:
int main(int argc, char** argv) {
if (argv[1] != "-size") {
fprintf(stderr, "Wrong format");
return 1;
}
// get_number is some function that returns int value of number if number, NULL otherwise
if (get_number(argv[2]) == NULL) {
fprintf(stderr, "Wrong format");
return 1;
}
return 0;
}
However, when I write
./cfile '-size' '22'
I cannot find a way of making C determine that the apostrophes should not be there.
I want to throw an error due to the apostrophes on each argument, but c seems to treat them as if they are not there.
Is there any way of doing this?

The quotes are interpreted by the shell in order to separate the arguments. They are removed before your program even sees them.
So your program will only see -size for argv[1], not '-size'.
Also, when comparing strings, you need to use strcmp. Using != or == to compare strings is actually comparing pointer values which is not what you want.

Related

Possible to sanitise command line arguments in argv? [duplicate]

This question already has answers here:
How to check if a string is a number?
(16 answers)
Closed 2 years ago.
I'm currently learning C and I'm messing around with command line inputs from the user.
I understand that argc is the count, i.e. the amount of entered commands, and argv is an array of what was entered.
I know how to check to see if the user entered a value after the called program with argc > 1 etc, and I know that argv takes string type variables which I can convert to real integers using atoi(). However I'm not sure how to implicitly only accepts integers. I've tried using isdigit etc around argv but I usually end up with a segment error. Is there a way around this?
int main(int argc, string argv[])
{
if (argc == 2 && //code to ensure argv[1] is int)
{
//code
}
}
"...not sure how to implicitly only accepts integers."
This is not implicitly done, you have to explicitly test each string for its contents.
Using functions in the string test category, for example isdigit, walk through each of the argv string arguments, i.e from argv[1] through argv[argc - 1] and test the character type to ensure it is a numeric value, from 0 to 9 and -. (Note: other valid numeric char symbols can included, eg: e, x,et.al. but are not in this limited example.)
A simple example:
given command line: prog.exe 23 45 1f4 -57
int main(int argc, char *argv[])
{
for(int i=1;i<argc;i++)
{
if(!test_argv(argv[i]))
{
//notify user of wrong input, and exit
printf("argv[%d] contains non numeric value. Exiting\n", i);
}
}
//continue with program
return 0;
}
bool test_argv(const char *buf)
{
while(*buf)
{
if((!isdigit(*buf)) && (*buf != '-'))
{
return false;
}
buf++;
}
return true;
}
Results in:

How can I reject an alphanumeric command-line argument (i.e. ./main 20x), but not a solely numeric one (i.e. ./main 20)? (in C)

I have figured out how to reject a purely alphabetical argument. I cannot figure out how to reject an alphanumeric user input while passing numeric inputs.
Here is my relevant code:
#include <cs50.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, string argv[])
{
if (argc != 2 || isalpha(*argv[1]))
{
printf("Usage: ./caesar key\n");
return 1;
}
else...
and my code goes on to successfully carry out the function of my program (save for this one bug).
Any help would be much appreciated! This is for an edX cs50 homework assignment. I finished the whole assignment except for this bug. I searched for an answer for over 3 hours, to no avail. Help me, stack overflownobi. You're my only hope.
You're almost there: your code checks if *argv[1] (i.e. the first character of that argument) is an alphabetic character. Instead, let's use a loop to check the entire argv[1] string. We're also going to use isdigit instead, so that we can reject strings such as 41!#4 which wouldn't get detected using isalpha.
argv is an array of char*, or pointers to characters, meaning that argv[1] is a pointer to the first character of that argument. Given a pointer to the first character of a string, we need to find the string's length using strlen, after which we can write a loop. Let's break this out into a function:
bool string_is_numeric(char* string) {
size_t length = strlen(string);
for(size_t i = 0; i < length; i++) {
if(!isdigit(string[i])) { return false; }
}
return true;
}
You can call this as follows:
if (argc != 2 || !string_is_numeric(*argv[1]))
{
printf("Usage: ./caesar key\n");
return 1;
}
Note that the code I gave has a few limitations:
It doesn't check whether the number is too large, e.g. to fit into an int.
It doesn't handle decimal values (i.e. those that we would parse into double).
It doesn't allow negative numbers.
Alternative using strtol:
The library function strtol allows you to convert your string to a long int, while also providing you a pointer to the very first character that couldn't be converted.
You can check that pointer: if it points to a null character (i.e. *endptr == '\0'), then strtol reached the end of the string successfully meaning that it was all valid digits.
You'll need to declare a long and a char* to hold the results:
if (argc != 2)
{
printf("Usage: ./caesar key\n");
return 1;
}
char* endptr;
long key = strtol(argv[1], &endptr, 10); // 10 meaning decimal
if(*endptr != '\0') {
printf("Key must be numeric and fit into a long\n");
return 1;
}

Command line arguments that has options [duplicate]

This question already has answers here:
How to properly compare command-line arguments?
(4 answers)
Closed 4 years ago.
So I have to write a program that contains two possible options depending on what the user chooses by entering either -i or -w. I'm very new to command line arguments in general and I have no idea how to do this. So far I have:
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc == -'i') {
puts("Test 1");
}
else if(argc == -'w') {
puts("Test 2");
}
return 0;
}
It doesn't print anything...
Any explanation is greatly appreciated. I'm just trying to understand the logic behind this.
First of all, you are comparing oranges with appels. argc stores the number of
arguments. Second, even if you used argv[1], the comparison would be still
wrong, because it would be comparing pointers, not the contents. Note that in C
a string is a sequence of characters that terminates to '\0'-terminating byte.
A char* variable only points to the start of that sequence. The == operator
checks for equality of values, in case of pointers (and string literals), it
compares whether both pointers point to the same location.
When you want to compare strings, you have to compare the strings themselves,
that means you have to compare the contents where the pointers are pointing to.
For strings you have to use the strcmp function like this:
#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
fprintf(stderr, "usage: %s option\n", argv[0]);
return 1;
}
if(strcmp(argv[1], "-i") == 0)
puts("Test 1");
else if(strcmp(argv[1], "-w") == 0)
puts("Test 2");
else {
fprintf(stderr, "Invalid option '%s'\n", argv[1]);
return 1;
}
return 0;
}
Note that it is important to check first that you have enough command line
arguments (the first if of my code). If the user didn't pass any argument,
argc will be 1 and argv[1] will point to NULL, which would yield undefined
behaviour if it is passed to strcmp. That's why I only do strcmp when I'm
100% sure that argv[1] is not NULL.
Also if you are coding for a POSIX system (linux, mac, etc), then I recommend using
getopt for parsing of the command line arguments.
You have to check argv[i] where i is the array number of the command line argument being put in, argv[0] would be the file name called upon and after that argv[1] would be the first statement, argv[2] the next and so on
argc means "argument count". meaning the number of arguments
argv is a 2-dimensional array. Strings are 1-dimensional character arrays in C. The second dimension comes from you having multiple String.
So if you want the first String argument, you would access it as follows:
argv[0]
You are also attempting to compare strings, which are more than 1 character long. You should use strcmp to compare strings in C. See How do I properly compare strings?
and if you want to compare equality, you would not use ==, == is for basic data types such as int or char.
argc represents the number of parameters that were passed in at the command line including the program name itself.
In c, a character, e.g., 'i' is an 8-bit number representing the ASCII code of the letter i. So your conditional statement if(argc == -'i') is actually checking whether -105 (105 is the ascii value of the letter i) is the number of arguments that was passed to your program.
To check whether the arguments passed in are "-i" or "-w" you need to perform string comparison, using the c library functions or your own algorithm, and you need to be comparing those strings to argv[i] (where i is the position of the parameter you're checking in the program invocation)
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("The name of the program is %s\n", argv[0]);
if( strcmp(argv[1], "-i") == 0 ) { puts("Test 1"); }
if( strcmp(argv[1], "-w") == 0 ) { puts("Test 2"); }
return 0;
}

Handling specific command line arguments

So I'm finishing up with a project and there is only one thing left for me to do; handle the command line arguments properly. I thought I had them handled but apparently I'm wrong...There can be two different sets of command line arguments that can come in, here are examples of what I'm talking about: ./hw34 -c 385 280 50 balloons.ascii.pgm balloonCircle.pgm and ./hw34 -e 100 balloons.ascii.pgm balloonEdge.pgm
This is what I had tried to handle the arguments, but this didn't seem to work:
if(argc==5) && isdigit(atoi(argv[2]))){ and else if(argc==7 && isdigit(atoi(argv[2])) && isdigit(atoi(argv[3])) && isdigit(atoi(argv[4]))){
What I'm stuck on is trying to figure out if argv[x] are numbers or not.
You should probably try to handle your command line differently.
Something like:
./hw34 -c "385,280,50" balloons.ascii.pgm balloonCircle.pgm
./hw34 -e 100 balloons.ascii.pgm balloonEdge.pgm
Then combined with getopt for command line handling and strtok for argument list's splitting, you should be able to achieve what you want.
The main advantage is that you won't have to worry about the number of arguments or their position in your command line anymore.
argv is an array of strings. There are no integers.
You can easily test if conversion was possible by using strtol, and then check if all characters were consumed:
char const * str = "123";
char * endptr;
long number = strtol(str, &endptr, 0); // do the conversion to long integer
if(&str[strlen(str)] == endptr) // endptr should point at the end of original string
printf("ok");
else
printf("error");
Canonical main function with arguments prototype is
int main( int argc, char* argv[] );
Where argc is the number of parameters, char* argv[] - an array of strings of these parameters. First element of the array - argv[0] - is the name of the program.
To check if the parameter is a number you can use strtol function which returns status of the conversion:
char* tail;
long n = strtol(argv[2], &tail, 10); // 10 is the base of the conversion
Then check the tail which points to the part of the string after a number (if any):
if (tail == argv[2]) { // not a number
// ...
} else if (*tail != 0) { // starts with a number but has additional chars
// ...
} else { // a number, OK
// ...
}

Segfault when using invalid input

I'm getting a segfault thrown when using invalid input or the -help flag in the command arguments. It is a re-creation of the Unix expand utility, and its supposed to handle errors in a similar fashion.
int main(int argc, char *argv[]){
char help1[]= "-help";
char help2[]= "--help";
int spaces; //number of spaces to replace tabs
if (argc==1){ //if only one argument in stack
//check if asking for help
if ( (strcmp(argv[1], help1)==0) || (strcmp(argv[1], help2)==0) )
printHelp();
else
printError(); //otherwise, print error message
//right number of tokens are provided, need to validate them
} else if (argc>=2){
spaces= atoi(argv[2]); //assign it to spaces
parse_file(spaces); //open the stream and pass on
}
return 0;
}
My printerror method:
void printError(){
fprintf(stderr, "\nInvalid Input.\n");
fprintf(stderr, "The proper format is myexpand -[OPTION] [NUMBER OF SPACES]\n");
exit(1);
}
When I try invalid input or the help flag, I get a segfault. Why is this, since I'm checking if the first flag is help?
If a single command-line parameter is passed to your program, argc == 2, so you need to replace
if (argc==1){ //if only one argument in stack
with
if (argc==2){
Note that in most systems argv[0] is the program name and in this case argc is at least 1. You can think of argc as the number of elements in argv. If you’re testing for argv[1], you’re expecting argv to have at least two elements (argv[0] and argv[1]), hence argc needs to be at least 2.
argv[0] counts too, so if argc==1 argv[1] is NULL
Your help message should be displayed if there are less than 2 parameter given, hence
if (argc<3)
printHelp();
else if(...)
Upon initialization, the arguments to main will meet the following requirements according to this.
argc is greater than zero.
argv[argc] is a null pointer.
argv[0] through to argv[argc-1] are pointers to strings whose meaning will be determined by the program.
argv[0] will be a string containing the program's name or a null string if that is not available. Remaining elements of argv represent the arguments supplied to the program. In cases where there is only support for single-case characters, the contents of these strings will be supplied to the program in lower-case.
As such, you are passing in argv[argc] (which is a null pointer) to strcmp.

Resources