how to determine order and count of arguments in getopt - c

I use getopt to get arguments from terminal, but it depends me on the order of arguemts. there's a difference between
./my -A -B -C
and
./my -B -A -C
is there any way to determine the order?
secondly, I'd like to determine count of the args (if there are some optional ones)
ps sorry for my english, i hope you understand what i mean

you have argv[] as a parameter passed to main() it contains an array of pointers to character strings. Those strings are in the exact order they were on the command line. The way to determine if some argument was listed before another.
is to step through the argv[] strings.

You record the order in which getopt() returns them. It returns them in left-to-right order, so there's no mystery. In the first invocation, -A is reported before -B and -C is reported last. In the second, -B is reported before -A and -C is still last. You can decide to do the processing when the arguments are read (so the actions occur while you're reading the options), or you can store the options in the sequence they appear and do the processing after the options are completely read. Both are feasible. If an error occurs with the first technique, though, the early processing will have been done. If that's unacceptable, process all the options first.
You can count the number of option arguments in your loop easily enough:
int optcnt = 0;
int opt;
while ((opt = getopt(argc, argv, "ABC")) != -1)
optcnt++;
You'd have a loop within braces with a switch as well as the simple increment, probably.
Note that you can have repeated options, possibly with a different option value on each time:
./my -D name1 -D name2 -A -B -D name3 -C
You can keep a record of which options and which names appear in sequence.
int optcnt = 0;
int opt;
int o_idx = 0;
int d_idx = 0;
char *d_opts[20];
char o_opts[20];
while ((opt = getopt(argc, argv, "ABCD:")) == -1)
{
optcnt++;
switch (opt)
{
case 'A':
case 'B':
case 'C':
o_opts[o_idx++] = opt;
break;
case 'D':
o_opts[o_idx++] = opt;
d_opts[d_idx++] = optarg;
break;
default:
…report error and exit…
break;
}
}
/* Parsing complete - do final checks */
/* Processing loop */
int d = 0;
int i;
for (i = 0; i < o_idx; i++)
{
switch (o_opts[i])
{
case 'A':
process_A();
break;
case 'B':
process_B();
break;
case 'C':
process_C();
break;
case 'D':
process_D(d_opts[d++]);
break;
}
}
assert(d == d_idx && i == o_idx);
Resetting getopt() back to the beginning is an undocumented art; try not to need to reparse your command line.

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);

Dealing with multiple program arguments, but only one is allowed in C

I have a problem figuring out how to reasonably consider which of the available arguments was given in a situation where one can be used, but there is also an optional argument after which one of the mandatory ones can be given. It is specifically about getopt in C. Example: I have a program with the argument -a -b -c and an optional -d. Only one of the first three may be used. Normally I would do something like that:
while ((opt = getopt(argc, argv, OPTSTR)) != EOF)
{
switch (opt)
{
case 'd':
optional_flag = 1;
break;
case 'a':
if (!mandatory_flag)
mandatory_flag = 1;
else
usage_prompt(argv[0]);
break;
case 'b':
if (!mandatory_flag)
mandatory_flag = 1;
else
usage_prompt(argv[0]);
break;
case 'c':
if (!mandatory_flag)
mandatory_flag = 1;
else
usage_prompt(argv[0]);
break;
The problem now is that I'm just checking to see if an optional argument was given and that if many mandatory arguments were not given. I could use a separate flag for each option, but it would be tiresome for many parameters. Is there any better solution to keep the logic but also know which parameter was given? I am thinking about another switch where I will be able to run appropriate functions.

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;
}

Switch case not affecting variable C

int main (int argc, char **argv) {
//Initialise variable for switch option
int option = 0;
//Set time to default to epoch
char *argtime = "1970-01-01 00:00:00";
//Use getopt to parse switches t and h (neither requires an argument, as epoch is defaulted to if non is supplied)
while ((option = getopt(argc, argv, "th")) != -1) {
//Switch statements for supplied switches
switch (option) {
//If -t
case 't':
//If arg supplied
if(optarg != NULL) {
//Set arg to given arg
argtime = optarg;
}
printf("-t is selected.\n");
break;
//If -h
case 'h':
printf("Help is selected\n");
break;
//If anything else
default:
printf("Option invalid\n");
return 1;
}
}
printf("The set time is %s\n", argtime);
return 0;
}
Here's some code I wrote to use getopt() to parse a command line switch, and argument. I want it argtime to default to "1970-01-01 00:00:00" if no argument is supplied, which it does, but when I use the -t switch and supply a different argument, argtime remains unaffected. So basically argtime = optarg; is not doing what it should, but I don't know why.
You need to tell getopt() about options that require an argument. As #xing described in comments, you do that by putting a colon in the option string, after the option letter that is affected. This allows getopt() to correctly handle grouped options and to recognize non-option arguments. Only if you do this can you expect option arguments to be communicated to you via optarg.
You claim in comments to want an optional option argument. POSIX-standard getopt() does not provide for that. In your particular case, it's not clear why you even want it, for what would it mean to specify a -t option without an argument? That the program should use a default? But that's what it will do if -t is omitted altogether.
Nevertheless, the way to approach the situation with POSIX getopt() is to provide two separate options, one that takes an argument and one that doesn't. For example, you might use option -T to signify whatever you wanted -t without an option to mean (getopt(argc, argv, "t:Th")). Alternatively, if you are willing to rely on GNU extensions then you can signify an optional option argument via a double colon (getopt(argc, argv, "t::h")), but this is less portable, and has slightly different semantics.
You might use optind after the while to see if there is another argument and if so, use that argument instead of the default.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv) {
//Initialise variable for switch option
int option = 0;
//Set time to default to epoch
char *argtime = "1970-01-01 00:00:00";
//Use getopt to parse switches t and h (neither requires an argument, as epoch is defaulted to if non is supplied)
while ((option = getopt(argc, argv, "th")) != -1) {
//Switch statements for supplied switches
switch (option) {
//If -t
case 't':
//If arg supplied
printf("-t is selected.\n");
break;
//If -h
case 'h':
printf("Help is selected\n");
break;
//If anything else
default:
printf("Option invalid\n");
return 1;
}
}
if ( optind < argc) {//more arguments available
//Set arg to given arg
argtime = argv[optind];
}
printf("The set time is %s\n", argtime);
return 0;
}

C, How is getopt() updating

I'm trying to make sense of a section of skeleton code for a class. The intended usage would be:
./a.out -d -n Foo -i Bar
The skeleton code works fine, but I have never used getopt() and can't understand why it works correctly (understanding it has nothing to do with the assignment, I just want to make sense of it). How is it that it updates / exits the while loop? I don't see a pointer increment or the arguments passed to it in the loop change at all.
char *optString = "-d-n:-i:";
int opt = getopt(argc, argv, optString);
while (opt != -1) {
switch(opt) {
case 'd':
debug = 1;
break;
case 'n':
nameserver_flag = 1;
nameserver = optarg;
break;
case 'i':
hostname = optarg;
break;
case '?':
usage();
exit(1);
default:
usage();
exit(1);
}
opt = getopt(argc, argv, optString);
}
getopt uses global variables to store the argument index, the next character to parse and some other information. Each time you call getopt, the function checks these variables to know where it last was (or where you told it it was) and updates the variables for the next call.
Most importantly, optind stores the index in argv of the next element to be scanned.
Each call to getopt processes one more of the arguments in argv, returning the result in opt, etc. etc. What else is there to understand?

Resources