True and false in C in if statement with getopt() - c

I have a hard time in understatanding how true and false works with "if statement" when I am using argv & getopt.
This is the simple code:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
int opt;
while ((opt = getopt (argc, argv, "i:l:")) != -1)
switch (opt) {
case 'i':
printf("This is option i");
break;
case 'l':
printf("This is option l");
break;
default:
fprintf(stderr,"Usage: %s here goes usage\n",argv[0]);
}
if (argc == 1) {
printf("Without options");
}
if ((argc == 2) && (argv[1] != "-l") || (argv[1] != "-i")) {
printf("Without option -l or -i but with other argument \n");
printf("argv[1] is %s\n", argv[1]);
}
Usage:
./a.out foo
Output:
Without option -l or -i but with other argument
argv[1] is foo
It's good so far. Now let me check if it works when argv[1] is "-l":
Usage:
./a.out -l
Output:
./a.out: option requires an argument -- 'l'
Usage: ./a.out here goes usage
Without option -l or -i but with other argument
argv[1] is -l
Getopt works fine, but second information occurs even if argv[1] is -l and I set in "if" statement that (argv[1] != "-l"). Why it works like that? I have no clue.
Thanks for any answer.
B.

Do not use:
argv[1] != "-l"
to compare strings in C(a), this compares the addresses of two arrays of char(b) rather than the contents of those two strings. Instaed, you should be using:
strcmp(argv[1], "-l") == 0
(a) This is one of the things fixed in C++ strings, the equality operators there compare the content of the strings.
(b) This may sometimes seem to work with something like "xyzzy" == "xyzzy" but that's only because the compiler is allowed (but not required) to fold identical string constants together (so they take less room). It generally is not possible when one of the strings is not a constant (such as when it's passed on the command line).

You re comparing two char *s using != this compares the addresses.
(argv[1] != "-l")
The way to compare strings is using strcmp. Mind hat the strcmp function is "trinary", and returns either a negative or positive value if the strings are different (to provide order) or 0 if equal.
(strcmp(argv[1], "-l") == 0)

AND (&&) has precedence over OR (||), so I think your code inside the if statement is interpreted like this: if (((argc == 2) && (argv[1] != "-l")) || (argv[1] != "-i")) So the logic inside of it is always true, because even if your option IS -l and it's one and only (number of option != 2) it's also true that the option you typed is NOT -i.

Related

getopt to take only one argument

I have a question about getopt in C. I have multiple option flags and am trying to get it to take one argument for everything. The command line will look something like command -a fileName or command -b fileName or command -ab fileName. Basically every command takes in one fileName and if they want to combine commands they should only have to type in one fileName. In getopt the string looks like a:b: and a variable is set to argv[argc -1]. This is fine if it's just one option but fails if there are multiple options (ie command -ab fileName) since : forces users to input an option but :: will make the singe options not force the user to type in an option. Any suggestions?
The optind global variable lets you know how many of the argv strings were used. So one way to approach this problem is to just drop the colons, and use a string like "ab".
Here's an example of what the code looks like (adapted from the example in the man page):
int main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "ab")) != -1)
{
if (ch == 'a')
printf("Got A\n");
else if (ch == 'b')
printf("Got B\n");
else
printf("Got confused\n");
}
if (optind != argc-1)
printf("You forgot to enter the filename\n");
else
printf("File: %s\n", argv[optind]);
}
If you run this with a command line like
./test -ab hello
the output is
Got A
Got B
File: hello

Why is my if/else statement not ever going to else?

I'm trying to write a program that takes in an argument(file name) from the command line. If it's not one of the four files that I expect then I want to print out that the correct file isn't found then exit safely. Right now when I run my code no matter what the argument is it will always run the printf("\n\n %s \n\n", argv[1]); line. Why is this happening?
int main(int argc, char **argv)
{
if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" ||
"twolines.csv"){
printf("\n\n %s \n\n", argv[1]);
} else {
printf("etl ERROR: File %s not found", argv[1]);
return -1;
}
}
if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" || "twolines.csv")
This doesn't do what you think it does.
First it tests whether the address of the string argv[1] is equal to the address of the string "orlando.csv". Since one is a command-line parameter and the other is a string literal, this test will always be false.
(Two strings with the same address must also have the same contents, but two strings with different addresses might still have the same contents.)
Then it tests whether the address of the string "orlando5.csv" is not "null". String literals never have "null" addresses, so this test will always be true.
Once one entry in a chain of || tests has been found to be true, all further tests are skipped (this is called "short-circuit evaluation").
So the if-expression is always found to be true, and the if-branch is always executed, and the else-branch is never executed.
The if-expression you should have written is
if (!strcmp(argv[1], "orlando.csv") ||
!strcmp(argv[1], "orlando5.csv") ||
!strcmp(argv[1], "florida.csv") ||
!strcmp(argv[1], "twolines.csv"))
Yes, you have to repeat the "!strcmp(argv[1]," part every single time. Yes, you have to call a library function to compare strings in C. (You will need to add #include <string.h> to the top of your source file.)
Change:
if(argv[1] == "orlando.csv" || "orlando5.csv" || "florida.csv" || "twolines.csv")
To:
if( (strcmp(argv[1],"orlando.csv")==0) ||
(strcmp(argv[1],"orlando5.csv")==0) ||
(strcmp(argv[1],"florida.csv")==0) ||
(strcmp(argv[1],"twolines.csv")==0) )

clarification on argc and argv

I have some doubts on what are argc and argv, i cant seem to grasp the concept, for what should i use them and how should i use them?
like i have this program that receives from the command line two integers between -100000 and 100000 computes thir addition and prints the result, while performing all needed check about te number of paramenters and their correctness.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int a, b;
char ch;
if (argc != 4)
{
printf("ERROR - Wrong number of command line parameters.\n");
exit(1);
}
if (sscanf(argv[1], "%d", &a) != 1)
{
printf("ERROR - the first parameter (%s) is not a valid integer.\n",
argv[1]);
exit(2);
}
if (sscanf(argv[2], "%d", &b) != 1)
{
printf("ERROR - the second parameter (%s) is not a valid integer.\n",
argv[2]);
exit(2);
}
ch = argv[3][0];
if (ch == 'a')
printf("The sum result is %d\n", a + b);
else if (ch == 'b')
printf("The subtraction result is %d\n", a - b);
else if (ch == 'c')
printf("The multiplication result is %d\n", a * b);
else if (ch == 'd')
{
if (b != 0)
printf("The division result is %d\n", a / b);
else
printf("ERROR the second value shoulb be different than 0 \n");
}
else
printf("ERROR parameter (%c) does not correspond to a valid value.\n",
ch);
return 0;
}
but how does the program receive from the command line two arguments?? where do i input them?? i am using codeblocks.
argc is the number of parameters passed to your program when it's invoked from command line.
argv is the array of received parameters, and it is an array of strings.
Note that the name of the program is always passed automatically.
Assuming your program executable is test, when you invoke from terminal:
./text 145 643
argc will be 3: program name and the two numbers
argv will be the char* array {"./text","145","643"}
When you write a code, say hello.c, you can run it from the terminal, by going to that directory/folder from the terminal, and compile it using a compiler like gcc.
gcc hello.c -o hello
If you are using Windows, with a compiler like Turbo C or Visual Studio, then it would create a .exe file. This creates an executable file.
When you run the file from command line, you can give command-line arguments as a way of input to the program.
On terminal, you could ./hello arg1 arg2, where arg1 and arg2 are the command line arguments to it. To see how to do this in Windows using a compiler like Turbo C, see this link too.
So what are argc and argv[]?
Your main functions uses main(int argc, char *argv[]), to take the command line arguments.
argc is the number of command line arguments passed. In the above case, that is 3.
argv[] is an array of strings, which in this case are 3 strings. argv[1] will be equal to "arg1" and argv[2] will be equal to "arg2". "./hello" will be in argv[0].
So you give your command line arguments in the command line, be it Linux or Windows. The above explanation was more for Linux. See this and this for command line arguments in Turbo C (I do not recommend Turbo C), and this in case of Visual C.
To know more about command line arguments, you can read this.

Compare string literal command line parameters in C

I need my program to run this way ./src c 2345 or ./src s 345, whereby the first character hs to be either c or s and second an integer. The program should throw an usage error if there's any less parameters and also any charcter other than c or s. Here's my code
int main(int argc, char **argv) {
int num_of_connections = 0, client_sockfd;
int max_sockfd, master_socket;
fd_set socket_collection, read_collection;
// Check validity of the user input
if(argc < 3) {
if((strcmp(argv[2], "s") != 0) || (strcmp(argv[2], "c") != 0)){
fprintf(stderr, "Usage: ./src <s/c> <port>\n");
exit(EXIT_FAILURE);
}
}
When I enter one argument I get a segmentation fault. Also it doesnt identify the C or S parameter. Any help will be appreciated.
Notice that main has a very specific convention: the argv array has argc+1 members, with the last being NULL and the others being non-null distinct pointers to zero-terminated strings.
So if argc is 1 (e.g. if your run ./src alone) or 2, argv[2] is NULL and you cannot pass it to  strcmp
You can call strcmp(argv[2],"s") only when argc>=3
BTW, I would suggest to use getopt(3) or preferably (on Linux only) getopt_long and to accept the --help and --version arguments, per GNU conventions.
Also, compile with all warnings and debug info (gcc -Wall -g) and use the gdb debugger. It would be faster for you to use gdb than to ask here and wait for replies.
if(argc < 3) { does not make sense if you want exactly two parameters. In the inner if block you are confusion || (logical or) with && (logical and).
In your invocation example ./src s 345 the character is the first argument, so probably argv[2] should read argv[1].
if ((argc != 3) || ((strcmp(argv[1], "s") != 0) &&
(strcmp(argv[1], "c") != 0))) {
fprintf(…);
return EXIT_FAILURE;
}
Note: all parentheses in this if (…) condition are optional, because of C's operator precedence. I put them for readability.

Handle segmentation fault accessing non-existent command line argument

I'm making a program in C in linux environment. Now, program runs with arguments which I supply in the command line.
For example:
./programName -a 45 -b 64
I wanted to handle the case when my command line parameters are wrongly supplied. Say, only 'a' and 'b' are valid parameters and character other than that is wrong. I handled this case. But suppose if my command line parameter is like this:
./programName -a 45 -b
It gives segmentation fault(core dumped). I know why it gives because there is no arguments after b. But how can I handle this situation such that when this condition arrives, I can print an error message on screen and exit my program.
As per the main function wiki page:
The parameters argc, argument count, and argv, argument vector, respectively
So you can use your argc parameter to check whether or not you have the right number of arguments. If you don't have 4, handle it and proceed without segfault.
You can, and quite probably should, use getopt() or its GNU brethren getopt_long().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int b = 0;
int a = 0;
int opt;
while ((opt = getopt(argc, argv, "a:b:")) != -1)
{
switch (opt)
{
case 'a':
a = atoi(optarg);
break;
case 'b':
b = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: %s -a num -b num\n", argv[0]);
exit(1);
}
}
if (a == 0 || b == 0)
{
fprintf(stderr, "%s: you did not provide non-zero values for both -a and -b options\n", argv[0]);
exit(1);
}
printf("a = %d, b = %d, sum = %d\n", a, b, a + b);
return(0);
}
You can make the error detection more clever as you wish, not allowing repeats, spotting extra arguments, allowing zeros through, etc. But the key point is that getopt() will outlaw your problematic invocation.
We can't see what went wrong with your code because you didn't show it, but if you go accessing a non-existent argument (like argv[4] when you run ./programName -a 42 -b), then you get core dumps. There are those who write out option parsing code by hand; such code is more vulnerable to such problems than code using getopt() or an equivalent option parsing function.

Resources