simple getopts c problems - c

So I have a pretty simple program but for some reason I can't get the options right.
If the -h option is present I just want to print the usage statement and exit.
If no options are present I want it to just run normally.
If any other options are present I want it to print the usage and EXIT_FAILURE
For some reason I just can't get these results. I know its a simple fix I just can't seem to find the answer.
Right now this is what I have.
int main(int argc, char* argv[]){
int c;
while(( c = getopt( argc, argv, "h")) != -1){
switch( c ){
case 'h':
usage(); return EXIT_SUCCESS;
case '*':
usage(); return EXIT_FAILURE;
default:
break;
}
}
mainProgram();
return EXIT_SUCCESS;
}

If you read the getopt(3) man page:
If getopt() does not recognize an option character, it prints an error message to stderr,
stores the character in optopt, and returns '?'. The calling program may prevent the error
message by setting opterr to 0.
So, getopt() will return ? if someone passes in an unrecognized option. You're looking for *, which you will never receive. In C, * does not act as a wildcard so this does not mean "any character".
Using default here isn't the correct solution (although it will work), because this would also trigger on valid options for which you had not implemented a handler.

Related

getopt usage with/without option

I'm writing a simple code making use of *argv[] parameter. I'd like to know whether I can use getopt() function for the following intent.
./myprogram -a PATH
./myprogram PATH
The program can either take merely PATH (e.g. /usr/tmp) or take -a option in addition to PATH. Can getopt() be used for this state? If can, how?
The program can either take merely PATH (e.g. /usr/tmp) or take option in addition to PATH. Can getopt() be used for this state? If can, how?
Certainly. I'm not sure where you even see a potential issue, unless its that you don't appreciate POSIX's and getopt()'s distinction between options and arguments. They are related, but not at all the same thing.
getopt() is fine with the case that no options are in fact specified, and it gives you access to the non-option arguments, such as PATH appears to be for you, regardless of how many options are specified. The usual usage model is to call getopt() in a loop until it returns -1 to indicate that no more options are available from the command line. At each step, the global variable optind variable provides the index of the next argv element to process, and after getopt() (first) returns -1, optind provides the index of the first non-option argument. In your case, that would be where you expect to find PATH.
int main(int argc, char *argv[]) {
const char options[] = "a";
_Bool have_a = 0;
char *the_path;
int opt;
do {
switch(opt = getopt(argc, argv, options)) {
case -1:
the_path = argv[optind];
// NOTE: the_path will now be null if no path was specified,
// and you can recognize the presence of additional,
// unexpected arguments by comparing optind to argc
break;
case 'a':
have_a = 1;
break;
case '?':
// handle invalid option ...
break;
default:
// should not happen ...
assert(0);
break;
}
} while (opt >= 0);
}
Using an optstring of "a" allows an argument of -a to act as a flag.
optind helps detect that only one additional argument is present.
The program can be executed as ./program -a path or ./program path
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char op = ' ';//default value
int opt;
while ((opt = getopt(argc, argv, "a")) != -1)//optstring allows for -a argument
{
switch (opt)
{
case 'a':
op = 'a';//found option, set op
break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if ( optind + 1 != argc)//one argument allowed besides optstring values
{
fprintf(stderr, "Usage: %s [-a] PATH\n", argv[0]);
return 1;
}
printf("%s %c\n", argv[optind], op);
return 0;
}

getopt with repeated and optional arguments

For a simple C project of a filesystem in a file, I have to make a command for writing the partitions table. It just contains the number of partitions and the size of them, pretty simple.
It should work like mk_part -s size [-s size ...] [name].
[name] is the filename of the disk, it's optionnal because there is a default one provided.
I don't know much getopt_long (and getopt) but all I read is that I while get options in a while so the two way of processing for me would be :
store all the sizes in an array and then write them in the table.
write size directly during parsing
For the first choice the difficulty is that I don't know the number of partitions. But I still could majorate this number by argc or better by (argc-1)/2 and it would work.
For the second choice I don't know which file to write.
So what is the best alternative to get all those arguments and how can I get this optionnal name ?
getopt can handle both repeated and optional args just fine. For repeated args each invocation of getopt will give you the next arg. getopt doesn't care that it is repeated. For the arg at the end, just need to check for its presence once all the options are parsed. Below is code modified from the example in the getopt man page to handle your scenario:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int
main(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "s:")) != -1) {
switch (opt) {
case 's':
printf("size=%d\n", atoi(optarg));
break;
default: /* '?' */
exit(EXIT_FAILURE);
}
}
if (optind < argc) {
printf("name=%s\n", argv[optind]);
} else {
printf("optional name arg not present\n");
}
exit(EXIT_SUCCESS);
}
And here is some sample runs of the program showing it handling repeated options and the arg at the end.
$ ./a.out -s 10 -s 20 -s 30
size=10
size=20
size=30
optional name arg not present
$ ./a.out -s 1 my_name
size=1
name=my_name
I think you're overthinking this. I know it's always tempting to try to avoid malloc's, but the efficiency of option parsing is never (*) important. You only parse options once, and in the cost of initializing a new process, finding the executable, linking and loading it, and all the rest of the process of starting up a command, the time it takes to parse options is probably not even noise.
So just do it in the simplest way possible. Here's one possible outline:
int main(int argc, char* argv) {
/* These variables describe the options */
int nparts = 0; // Number of partitions
unsigned long* parts = NULL; // Array of partitions (of size nparts)
const char* diskname="/the/default/name"; // Disk's filename
for (;;) {
switch (getopt(argc, argv, "s:")) {
case '?':
/* Print usage message */
exit(1);
case 's':
/* Some error checking missing */
parts = realloc(parts, ++nparts * sizeof *parts);
parts[nparts - 1] = strtoul(optarg, NULL, 0);
continue;
case -1:
break;
}
break;
}
if (optind < argc) diskname = argv[optind++];
if (optind != argc) {
/* print error message */
exit(1);
}
return do_partitions(diskname, parts, nparts);
}
The above code is missing a lot of error checking and other niceties, but it's short and to the point. It simply reallocs the partition array every time a new size is found. (That's probably not as awful as you think it is, because realloc itself is probably clever enough to increase the allocation size exponentially. But even if it were awful, it's not going to happen often enough to even notice.)
The trick with continue and break is a common way of nesting a switch inside a for. In the switch, continue will continue the for loop, while break will break out of the switch; since all the switch actions which do not terminate the for loop continue, whatever follows the switch block is only executed for a switch action which explicitly breaks. So the break following the switch block breaks the for loop in precisely those cases where the switch action did a break.
You might want to check that there was at least one partition size defined before call the function which does the repartitioning.

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.

getopt giving -1 as return value

I am trying to see how getopts work. I wrote the code below but not sure what I am doing wrong. Please point me :
#include<unistd.h>
..
..
int main( int argc, char *argv[])
{
int ch=0;
while((ch=getopt(argc, argv, "ltR:")!=-1) // 0 here was by mistake. Changed to -1
{
printf("%d",ch); //This prints -1
switch(ch)
{
case 'l':
printf("l");
break;
case 't':
printf("t");
break;
case 'R':
printf("R");
break;
}
}
return 0;
}
$ ./a.out -ltR
$ -1
$ ./a.out -l
$ -1
May be I am doing a really mistake or missing out on some aspect of my understanding of getopt.
Made changes but still gives the same result:(
Thanks,
Faizan
I think your while expression should be
while((ch=getopt(argc, argv, "ltR")!=-1)
{..}
and the option should end with ':'
while((ch=getopt(argc, argv, "ltR:")!=-1)
{..}
or two ':' if switch is optional.
From the man page -1 indicates that all the arguments have been done, rather than 0 as you appear to be checking for.
man 3 getopt
If an option was successfully found, then getopt() returns the option character. If all command-line options have been parsed, then getopt() returns -1.
The code in your question doesn't parse out of the box so it doesn't appear to be a proper copy paste which makes spotting the error a lot harder. The brackets on the while loop might be the cause of your issue but it's hard to tell. If I copy paste your example and correct the bracket issue the code works as you expect it to.

Where does getopt_long store an unrecognized option?

When getopt or getopt_long encounters an illegal option, it stores the offending option character in optopt. When the illegal option is a long option, where can I find out what the option was? And does anything meaningful get stored in optopt then?
I've set opterr = 0 to suppress the automatically printed error message. I want to create my own message that I can print or log where I'd like, but I want to include the name of the unrecognized option.
The closest I can find is that if you get a BADCH return the argv item that caused it is in argv[optind-1]. Seems like there should be a better way to find the problem argument.
You're quite right that the man page glosses right over these details, but enough hints can be gleaned from the source code, e.g., glibc's implementation in glibc-x.y.z/posix/getopt.c's _getopt_internal_r. (Perhaps that's the only interesting implementation of this GNU extension function?)
That code sets optopt to 0 when it encounters an erroneous long option, which I guess is useful to distinguish this case from an erroneous short option, when optopt will surely be non-NUL.
The error messages produced when opterr != 0 mostly print out the erroneous long option as argv[optind], and later code (always or -- conservatively -- at least mostly) later increments optind before returning.
Hence consider this program:
#include <getopt.h>
#include <stdio.h>
int main(int argc, char **argv) {
struct option longopts[] = {
{ "foo", no_argument, NULL, 'F' },
{ NULL, 0, NULL, 0 }
};
int c;
do {
int curind = optind;
c = getopt_long(argc, argv, "f", longopts, NULL);
switch (c) {
case 'f': printf("-f\n"); break;
case 'F': printf("--foo\n"); break;
case '?':
if (optopt) printf("bad short opt '%c'\n", optopt);
else printf("bad long opt \"%s\"\n", argv[curind]);
break;
case -1:
break;
default:
printf("returned %d\n", c);
break;
}
} while (c != -1);
return 0;
}
$ ./longopt -f -x --bar --foo
-f
./longopt: invalid option -- 'x'
bad short opt 'x'
./longopt: unrecognized option '--bar'
bad long opt "--bar"
--foo
Thus in these cases, by caching the pre-getopt_long value of optind, we're easily able to print out the same bad options as the opterr messages.
This may not be quite right in all cases, as the glibc implementation's use of its own __nextchar rather than argv[optind] (in the "unrecognized option" case) deserves study, but it should be enough to get you started.
If you think carefully about the relationship between optind and the repeated invocations of getopt_long, I think printing out argv[cached_optind] is going to be pretty safe. optopt exists because for short options you need to know just which character within the word is the problem, but for long options the problem is the whole current word (modulo stripping off option arguments of the form =param). And the current word is the one that getopt_long is looking at with the (incoming) optind value.
In the absence of a guarantee written in the documentation, I would be somewhat less sanguine about taking advantage of the optopt = 0 behaviour though.

Resources