How to parse unordered flags in arguments via getopt in C - c

int res = 0;
while ((res = getopt(argc, argv, "ivlcne")) != -1) {
switch (res)
{
case 'i':
pCurrentFlags->i = 1;
break;
case 'v':
pCurrentFlags->v = 1;
break;
default:
break;
}
}
Using such a code, I encounter a problem. If I write ./a.out -i filename -v in the terminal, the -v flag will not work.

UPDATE:
Commonly used practice to parse unordered flags is to parse all the arguments first and only after that handle them.
There is also a GNU implementation of the getopt() which supports that behavior as #Jonathan Leffler mentioned in the comments.
But from your copy, it is unclear what you are trying to do.
The v option in the code you provide needs an argument, but in the command line ./a.out -i filename -v it is not taking it.
If you need v as a non-parameter option, the easiest way to make it work is to change "ivlcne" with "i:v1:l:c:n:e" and with this way getopt() will no longer wait for an argument for v.

Related

How to get the arguments from getopt to work together

I want to be able to use every argument (-S, -s, -f) and them be able to be used together. -S prints the files in the folder and their size... -s prints the files if they are >= the file size provided by the argument -f finds all the files with the given substring.
How would I get these to work together? Right now, my code does all of this separately.
while((c = getopt(argc, argv, "Ss:f:")) != -1){
switch(c){
case 'S':
// No clue how to make them work together.
printf("Case: S\n");
printf("option -%c with argument '%s'\n", c, argv[optind]);
printDIR(cwd, case_S);
break;
case 's':
printf("Case: s\n");
printf("option -%c with argument '%s'\n", c, optarg);
printDIR(cwd, case_s);
break;
case 'f':
printf("Case: f\n");
printf("option -%c with argument '%s'\n", c, optarg);
printDIR(cwd, case_f);
break;
default:
printf("...");
}
}
printDIR is a pointer function which is why I have cwd(which is the directory) and case_S and so on.
I want to be able to say... './search -S -s 1024 -f tar'. This should recursively search the current directory and print the size of the file if it is >= 1024 and if the file has the substring 'tar' in it. But I also want it to work even if I don't provide all arguments.
This is my first time trying anything like this so I'm new to trying to make UNIX commands and using getopt args.
Converting parts of some of my comments into an answer.
You should process the options without doing any actions. Only when you've finished processing the options, with no errors, do you think about doing anything like calling printDIR(). You'll probably need more arguments to the function, or use global variables.
You'd have a flag such as:
int recursive = 0;
which you would set to 1 if the search was to be recursive. And int minimum_size = 0; and modify it with -s 1024. And const char *filter = ""; and then modify that with -s tar. Etc. Often, these are global variables — but if you can avoid that by passing them to the function, that is better.
Your function might then become:
int printDIR(const char *cwd, int recursive, int minimum, const char *filter);
and you'd call it with the appropriately set local variables. Note that you should check the conversion from string to integer before calling printDIR().
If there are non-option arguments, you'd process them after the option handling with:
for (int i = optind; i < argc; i++)
printDIR(argv[i], recursive, minimum_size, filter);

How can the `-V` flag be both `--verbose` and `--version`?

From https://github.com/karelzak/util-linux/blob/master/disk-utils/mkfs.c#L94-L113
/* Check commandline options. */
opterr = 0;
while ((more == 0)
&& ((i = getopt_long(argc, argv, "Vt:h", longopts, NULL))
!= -1))
switch (i) {
case 'V':
verbose++;
break;
case 't':
fstype = optarg;
break;
case 'h':
usage();
case VERSION_OPTION:
print_version();
default:
optind--;
more = 1;
break; /* start of specific arguments */
The docs for mkfs say that -V is the short flag for both version and verbose. I'm having trouble understanding how this is possible, and am looking for clarity.
VERSION_OPTION is defined as enum { VERSION_OPTION = CHAR_MAX + 1 }; so I'm not sure what char that is.
Look up a few lines. Before calling getopt_long:
if (argc == 2 && !strcmp(argv[1], "-V"))
print_version();
The code has a special case, not handled by the normal option processing code, for a single argument with the value -V. If there's more than one argument, it will fall through that if and process -V as the verbose flag.
One effect of this is that
mkfs -V -V
prints an error message:
mkfs: no device specified
Try 'mkfs --help' for more information.
This is also noted in (some versions of) the man page:
-V, --version
Display version information and exit. (Option -V will display version information only when it is the only parameter, otherwise it will work as --verbose.)

getopt value stays null

I am passing my program inputs and I could see them in argv but getopt doesnt seem to have the argument that I expect.
This is how I run my prog: ./my_prog -X -f filename
<snip>
while ((opt = getopt(argc, argv, "Xf:eE:dD")) != EOF) {
switch (opt) {
case 'X':
case 'f':
if (optarg == NULL)
fput("no point of living", fp); << for debugging
</snip>
I always get optarg as null. WHY?
Your argument string does not have a : after the X (e.g. X:f) so optarg will always be null.
I'll also point out that generally in a switch statement you'll want a break after each case (generally, not always, but when parsing arguments usually), so:
switch ( ... ) {
case 'X': {
// do something
} break;
case 'f': {
// do something else
} break;
}
For who else get to this page:
From http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt:
An option character in this string can be followed by a colon (‘:’) to indicate that it takes a required argument. If an option character is followed by two colons (‘::’), its argument is optional; this is a GNU extension.
so in your argument you might use:
"X:f:e:E:d:D:"
Had the same problem.
I just dealt with this issue, and it appears this question was never fully answered.
You have to make sure you're setting the external libc variable opterr = 0 before you call getopt; if you don't reset it and getopt previously had an error in another application anywhere in your system that used it, it will fail for the argument. I'll also reiterate the existing point that because you don't have a break statement after case 'X': that's a sure sign of an issue since it will fall through.
getopt only processes one argument at a time, so falling through case X into case f is a bad thing to do. You should always have a break in each case statement of a switch unless you are absolutely certain it should fall through (which is very rare in my experience). As another bit of general good practice, you should always enclose blocks of code in { } (referring to your conditional) unless it's a return statement or break or something that causes the program flow to to drop out of the current or parent block scope or to enter a new scope through a function or method call.
I think your option string Xf:eE:dD is fine. This indicates that:
1) The following will simply be option flags that always have a null argument: XedD
2) The following options will require an argument: fE
If this is the functionality you're looking for, the given option string is fine. If you're using GNU libc, per the other above answer, you can use :: after an option in the option string to indicate that the option might have an argument, but doesn't have to.
So at the top of your file make sure you at least have:
extern int opterr;
Then right before you call getopt for the first time in your code, set opterr to 0.
e.g.
opterr = 0;
while ((opt = getopt(argc, argv, "Xf:eE:dD")) != EOF) {
switch (opt) {
case 'X':
case 'f':
if (optarg == NULL)
fput("no point of living", fp); << for debugging
This should at least partially resolve your issue. Here's a link to an example:
http://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
Cheers,
Jon
I know this is old but I recently noticed changed behaviour in the way I used to use getopt years ago. Maybe it was a different environment but I find using it today requires the optarg to be DIRECTLY after the flag (no space) otherwise optarg is null.
Using your example, replace ./my_prog -X -f filename with ./my_prog -X -ffilename
I find that works fine even though it feels wrong. Hope this helps someone else out later. Make sure to try it both ways.

C getopt multiple value

My argument is like this
./a.out -i file1 file2 file3
How can I utilize getopt() to get 3 (or more) input files?
I'm doing something like this:
while ((opt = getopt(argc, argv, "i:xyz.."))!= -1){
case 'i':
input = optarg;
break;
...
}
I get just the file1; how to get file2, file3?
I know this is quite old but I came across this in my search for a solution.
while((command = getopt(argc, argv, "a:")) != -1){
switch(command){
case 'a':
(...)
optind--;
for( ;optind < argc && *argv[optind] != '-'; optind++){
DoSomething( argv[optind] );
}
break;
}
I found that int optind (extern used by getopt() ) points to next position after the 'current argv' selected by getopt();
That's why I decrease it at the beginning.
First of all for loop checks if the value of current argument is within boundaries of argv (argc is the length of array so last position in array argv is argc-1).
Second part of && compares if the next argument's first char is '-'. If the first char is '-' then we run out of next values for current argument else argv[optind] is our next value. And so on until the argv is over or argument runs out of values.
At the end increment optind to check for the next argv.
Note that because we are checking 'optind < argc' first second part of condition will not be executed unless first part is true so no worries of reading outside of array boundaries.
PS I am a quite new C programmer if someone has an improvements or critique please share it.
If you must, you could start at argv[optind] and increment optind yourself. However, I would recommend against this since I consider that syntax to be poor form. (How would you know when you've reached the end of the list? What if someone has a file named with a - as the first character?)
I think that it would be better yet to change your syntax to either:
/a.out -i file1 -i file2 -i file3
Or to treat the list of files as positional parameters:
/a.out file1 file2 file3
Note that glibc's nonconformant argument permutation extension will break any attempt to use multiple arguments to -i in this manner. And on non-GNU systems, the "second argument to -i" will be interpreted as the first non-option argument, halting any further option parsing. With these issues in mind, I would drop getopt and write your own command line parser if you want to use this syntax, since it's not a syntax supported by getopt.
I looked and tried the code above, but I found my solution a little easier and worked better for me:
The handling I wanted was:
-m mux_i2c_group mux_i2c_out
(2 arguments required).
Here's how it panned out for me:
case 'm':
mux_i2c_group = strtol(optarg, &ch_p, 0);
if (optind < argc && *argv[optind] != '-'){
mux_i2c_out = strtol(argv[optind], NULL, 0);
optind++;
} else {
fprintf(stderr, "\n-m option require TWO arguments <mux_group> "
"<mux_out>\n\n");
usage();
}
use_mux_flag = 1;
break;
This grabbed the first value form me as normal and then just looked for the second, REQUIRED value.
The solution by GoTTimw has proven very useful to me. However, I would like to mention one more idea, that has not been suggested here yet.
Pass arguments as one string in this way.
./a.out -i "file1 file2 file3"
Then you get one string as a single argument and you only need to split it by space.

command line processing library - getopt

Can someone help me with the getopt function?
When I do the following in main:
char *argv1[] = {"testexec","-?"};
char *argv2[] = {"testexec","-m","arg1"};
int cOption;
/* test for -? */
setvbuf(stdout,(char*)NULL,_IONBF,0);
printf("\n argv1 ");
while (( cOption = getopt (2, argv1, "m:t:n:fs?")) != -1) {
switch(cOption){
case 'm':
printf("\n -m Arg : %s \n",optarg);
break;
case '?':
printf("\n -? Arg ");
break;
case 'n':
printf("\n -n Arg : %s \n",optarg);
break;
}
}
printf("\n argv2 ");
while (( cOption = getopt (3, argv2, "m:t:n:fs?")) != -1) {
switch(cOption){
case 'm':
printf("\n -m Arg : %s \n",optarg);
break;
case '?':
printf("\n -? Arg : %s \n",optarg);
break;
case 'n':
printf("\n -n Arg : %s \n",optarg);
break;
}
}
I'm running this code on rhel3 which uses old libc version. I don't know which one to be exact.
Now the problem is getopt doesn't work the second time with argv2.
But if I comment out the first getopt call with argv1 , it works.
Can someone tell me what am I doing wrong here?
argv1 and 2 must end in 0:
char* argv1[] = {"par1", "par2", 0};
Edit: OK, I read the getopt man page and I found this:
The variable optind is the index of the next element to be processed in argv. The system initializes this value
to 1. The caller can reset it to 1 to restart scanning of the same argv, or when scanning a new argument vector.
So, making optind=1 between the two calls at getopt makes it work as expected.
The getopt() function uses some global variables, like optind and optarg, to store state information between calls. After you finish processing one set of options, there is data left in those variables that is causing problems with the next set of options. You could potentially try to reset getopt's state between calls by clearing the variables, but I'm not sure that would work since the function might use other variables which aren't documented and you'd never know if you'd gotten them all; besides, it would be absolutely nonportable (i.e. if the implementation of getopt() changes, your code breaks). See the man page for details. Best not to use getopt() for more than one set of arguments in a given program if you can help it.
I'm not sure if there is an actual function to reset getopt's state (or perhaps a reentrant version of the function, which lets you store the state in your own variables)... I seem to remember seeing something like that once, but I can't find it now that I look :-/
As stated in the man page:
"A program that scans multiple argument vectors, or rescans the same vector more than once, and wants to make use of GNU extensions such as '+' and '-' at the start of optstring, or changes the value of POSIXLY_CORRECT between scans, must reinitialize getopt() by resetting optind to 0, rather than the traditional value of 1. (Resetting to 0 forces the invocation of an internal initialization routine that rechecks POSIXLY_CORRECT and checks for GNU extensions in optstring.)"
Is there any reason why you are not using getopt_long() instead? On most platforms, getopt() just calls _getopt_long() with a switch to disable long arguments. That's the case with almost every platform that I know of (still in use), including Linux, BSD and even emerging OS's like HelenOS -, I know, I was the one who ported getopt to its libc :)
It is much easier on ANYONE using your program to have long options at least until they get used to using it.
getopt_long() will allow you to use two (or more) option indexes that can stay 'live' after they are done processing arguments, only the internal (global, non-reentrant) one would have to be re-set which is no big deal.
This lets you easily compare the argument count to the number of options actually passed in both invocations with many other benefits .. please consider not using the antiquated interface.
Look at getopt.h, you'll see what I mean.

Resources