Issue with options order in getopt - c

I am having trouble with getopt. In my program, I wish to run the program with the following options: -l -w -n -b. n and b are optional, however the program must have either l or w, not both. Also, if w is invoked, it must be followed by an argument. Also, the order should be l or w first, followed by n or b, whose order does not matter.
Here are examples of valid arguments:
./prog -l -n -b
./prog -w argument -b -n
./prog -l -n
./prog -l -b
These examples are not valid:
./prog -l -w argument
./prog -n -l
./prog -b -l
Based on these requirements, I having having trouble using getopt. Of course I can use a myriad of if else statements, but that would be ugly and unnecessarily complicated.
Specifically, I am having trouble with the order of the arguments and making -l and -w options an either or relationship.
Here is what I have so far, although it is not much because of these uncertainties.
while ((option = getopt(argc, argv,"lw:nb:")) != -1) {
switch(option) {
case'l': ... // I haven't wrote anything here yet
case'w': ...
case'n': ...
case'b': ...
case '?': ...
default: ...
}
}

First, getopt makes sure that w has an argument. That's the point of the
: in your getopt string. If -b does not need an argument, then you should remove the : after it.
Second, I don't think verifying argument conditions is as bad as you're thinking
it's going to be. You should already have variables to keep track of your
options, like this:
int opt_l = 0;
int opt_w = 0;
char *arg_w = 0;
int opt_n = 0;
int opt_b = 0;
You should also have a function that prints out the usage and a helpful error
message to the user, like this:
int exit_usage(const char *err)
{
if( err ) { printf("Bad arguments: %s\n", err); }
printf("Usage: ...\n");
exit(1);
}
Then it's a simple one-line check to verify your preconditions. If a
precondition fails, then print an error and show the usage info to your user.
You don't have to cram the precondition check into one line, but it tends to
be easier to read a list of preconditions when they're all one line each.
Like this:
while ((option = getopt(argc, argv, "lw:nb")) != -1)
{
switch(option)
{
case 'l':
if( opt_w != 0 ) { exit_usage("Cannot combine -l with -w"); }
++opt_l;
break;
case 'w':
if( opt_l != 0 ) { exit_usage("Cannot combine -l with -w"); }
arg_w = optarg;
++opt_w;
break;
case 'n':
if( opt_l == 0 && opt_w == 0 ) { exit_usage("-n requires -l or -w first"); }
++opt_n;
break;
case 'b':
if( opt_l == 0 && opt_w == 0 ) { exit_usage("-n requires -l or -w first"); }
++opt_b;
break;
case '?':
exit_usage(NULL);
break;
default:
break;
}
}

Related

Wrong output when printing one of the elements in argv

I am writing a program that recurses through directories but came across a strange problem with my code. I compile and run the code with the following:
./sorter -c food -d thisdir -o thatdir
The following gives me a total argument count of 7. However when I try to check my inputs, I get unexpected results:
int main(int argc, char ** argv){
int checkInputs = checkFlags(argc,argv);
if(checkInputs == 1){
fileSearch(".");
}
else if(checkInputs == 2){
printf("arg counts %d\n",argc);
printf("%s\n",argv[4]);
fileSearch(argv[4]);
}
else if(checkInputs == 3){
printf("arg counts %d\n",argc);
printf("%s\n",argv[4]);
fileSearch(argv[4]);
}
return 0;
}
It returns the expected output when checkInputs is 2 but when it is 3 I get the following when I print argv[4]:
-d
The following is my checkInputs function, which may be causing this:
int checkFlags(int argc, char * argv[]) {
int output;
if(argc < 3){
printf("Not enough arguments\n");
output = 0;
return output;
}
else{
int options;
char * input = NULL;
while((options = getopt(argc,argv,"c:d::o::")) != -1){
switch(options) {
case 'c':
input = optarg;
//printf("Mandatory flag: %s\n",optarg);
output = 1;
break;
case 'd':
//printf("We have an optional %s\n",argv[4]);
output = 2;
break;
case 'o':
//printf("We have two optionals: -d: %s & -o: %s\n",argv[4],argv[6]);
output = 3;
break;
/* Probably don't need this case statement*/
case '?':
if(optopt == 'c'){
printf("Unknown option, -%c not present\n",optopt);
exit(0);
}
break;
}
}
}
return output;
}
Everything works fine however if the program is only compiled with the first 2 flags.
By debugging your code, I actually get a segmentation fault, when checkInputs == 2 and ./program -c hello -d. You should fix this, by adding a IF and check if a path is provided, for the option -d. Also keep in mind, that the sequence of the parameters can differ. You can save the extra options in a variable to access them out of the checkFlags function.
Executing ./program -c food -d thisdir -o thatdir outputs:
Mandatory flag: food
We have an optional thisdir
We have two optionals: -d: thisdir & -o: thatdir
arg counts 7
-o
It prints the expected.

C Getopts: Managing all received options and optional optargs

I am writing on a program that should get up to 3 options while they can be combined as well. The part look like in following:
char flag = 0; // to mark which options were set
int aopt = INT_MAX; // default to INT_MAX if no args delivered
char bemerkung[100];
memset(bemerkung, '\0', 100); // string to receive via '-b'
char option;
while((option = getopt (argc, argv, "m:a:b:")) != EOF){
switch( option ){
case 'm': {
flag = flag | 1;
break;
}
case 'a': {
flag = flag | 2;
char * arg = optarg;
aopt = atoi(arg);
if(aopt == 0 && strcmp("0", arg) != 0) // if no valid optarg was entered
aopt = INT_MAX; // get back the default value
break;
}
case 'b': {
flag = flag | 4;
strcpy(bemerkung, optarg);
break;
}
default: {
fprintf(stderr, "Unknown option entered.\n");
return -1;
}
}
}
// some code here, not touching `flag`
if(flag == 1){
// deal with `-m`, no optargs
}
if(flag == 2){
// `-a aopt` called
}
if(flag == 3){
// `-m -a aopt` called
}
The basic idea is to set the bits in flag such that I can handle the options and their combinations later. But the trouble I face right now is that -m requires arguments (should be optional for -m only) and ./main -m -a 0 results into flag = 1 instead of flag = 3.
How do I make arguments for -m optional and get the OR to be done correctly for the flag?
If you want -m to not take any arguments, then omit the : that follows it in the options string. The : means that the preceding option expects an argument:
while((option = getopt (argc, argv, "ma:b:")) != EOF){

C getopt does not read all the parameters

I have an assignment in C that requires options to be read in for different forms of a program. Before I start on that, though, I want to make sure that the getopt portion is working fine. However, the program keeps dropping the last parameter and I don't know why. Whenever I enter the last char, the program goes to the default value that kills the program. Any help is appreciated!
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
int main(int argc, char **argv)
{
int sFlag = 0;
int lFlag = 0;
int dFlag = 0;
int rFlag = 0;
int c;
opterr = 0;
while ((c = getopt (argc, argv, "slr:")) != -1)
{
switch(c)
{
case 's':
sFlag = 1;
break;
case 'l':
lFlag = 1;
break;
case 'r':
rFlag = 1;
break;
default:
printf("unknown parameter introduced");
exit(-1);
break;
}
}
printf("s = %i, l = %i, d = %i, r = %i", sFlag, lFlag, dFlag, rFlag);
return 1;
}
The colon symbol after r in "slr:" tells getopt() to wait for a mandatory argument which follows -r.
Examples:
getopt(argc, argv, "slr:") can parse ./project -s -l -r r_arg (or ./project -r r_arg -s etc.)
getopt(argc, argv, "s:lr:") can parse ./project -s s_arg -l -r r_arg
getopt(argc, argv, "s:lr:") can also parse ./project -s -l -r r_arg with no error, but the program works differently from user's expectation. This is because getopt() expects -s to be followed by its argument, however it looks like, so the next argument -l is consumed and will not hit your switch(c).

Getopt optional arguments?

I have a program where you enter an option
-d
and then whether or not you supply a non-optional argument after the option, do something.
Heres my code:
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#define OPT_LIST "d::"
int main (int argc, char *argv[])
{
int c;
char string[] = "blah";
while ((c = getopt (argc, argv, OPT_LIST)) != -1)
{
switch (c)
{
case 'd':
printf("%s\n", optarg);
break;
case '?':
fprintf(stderr, "invalid option\n");
exit(EXIT_FAILURE);
}
}
}
So if you enter a non-optional argument after the option, it prints the argument. But I want it to print out the char "string" if the user doesn't supply a non-optional argument (this is why I put the double colon in the
OPT_LIST). But I'm not sure how to do this so any help would be greatly appreciated.
Heres what happens when I run the program:
user:desktop shaun$ ./arg -d hello
hello
user:desktop shaun$ ./arg -d
./arg: option requires an argument -- d
invalid option
I'm running a Mac with OS X using C language.
The "optional value of an option" feature is only a GNU libc extension, not required by POSIX, and is probably simply unimplemented by the libc shipped with Mac OS X.
The options argument is a string that specifies the option characters that are valid for this program. 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.
https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html
In fact, POSIX.1-2008, section 12.2, "Utility Syntax Guidelines", explicitly forbids this feature:
Guideline 7: Option-arguments should not be optional.
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02
According to the getopt documentation, it will return : if an option with an argument does not have one. It also sets optopt with the matching argument.
Therefore, use:
int main (int argc, char *argv[])
{
int c;
while ((c = getopt (argc, argv, ":d:f:")) != -1)
{
switch (c)
{
case 'd':
case 'f':
printf("option -%c with argument '%s'\n", c, optarg);
break;
case ':':
switch (optopt)
{
case 'd':
printf("option -%c with default argument value\n", optopt);
break;
default:
fprintf(stderr, "option -%c is missing a required argument\n", optopt);
return EXIT_FAILURE;
}
break;
case '?':
fprintf(stderr, "invalid option: -%c\n", optopt);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
Maybe I'm misunderstanding the question. I'm reading it as though the user wants to over ride getopt's default error handling. If that is the case, shouldn't there be a : in the beginning of their OPT_LIST? I think the code above is good, however, I think it will still print
./arg: option requires an argument -- d
To surpress that, don't we need a : at the beginning of the OPT_LIST? For example, changing this:
while ((c = getopt (argc, argv, "d:f:")) != -1)
to this:
while ((c = getopt (argc, argv, ":d:f:")) != -1)
Correct me if I'm wrong.
Try this solution. It works for me. Consider option 'z' as option with optional argument.
int c;
opterr = 0; //if (opterr != 0) (which it is by default), getopt() prints its own error messages for invalid options and for missing option arguments.
while ((c = getopt (argc, argv, "x:y:z:")) != -1)
switch (c)
{
case 'x':
case 'y':
case 'z':
printf("OK ... option -%c with argument '%s'\n", c, optarg);
break;
case '?':
if (optopt == 'x' || optopt == 'y')
fprintf (stderr, "ERR ... Option -%c requires an argument.\n", optopt);
else if(optopt == 'z' && isprint(optopt))
{
printf("OK ... option '-z' without argument \n");
break;
}
else if (isprint (optopt))
fprintf (stderr, "ERR ... Unknown option `-%c'.\n", optopt);
else
fprintf (stderr, "ERR ... Unknown option character `\\x%x'.\n", optopt);
return -1;
default: ;
}
Here are some examples:
./prog -x
ERR ... Option -x requires an argument.
./prog -y
ERR ... Option -x requires an argument.
./prog -z
OK ... option '-z' without argument
./prog -x aaa
OK ... option -x with argument 'aaa'
./prog -y bbb -x aaa
OK ... option -y with argument 'bbb'
OK ... option -x with argument 'aaa'
./prog -x aaa -y bbb -z
OK ... option -x with argument 'aaa'
OK ... option -y with argument 'bbb'
OK ... option '-z' without argument
./prog -x aaa -y bbb -z ccc
OK ... option -x with argument 'aaa'
OK ... option -y with argument 'bbb'
OK ... option -z with argument 'ccc'

C using getOpt to parse multiple arguments

I need to use getopt to parse the following command:
./center -n name –cp Anumber –i Anumber –t Anumber –s Anumber -fc nameOfaFile
All of them can be given out in any order.
So its clear i have to use getOpt.
So i have been investigation and this is what i have so far
void check_parameters (int argc, char** argv) {
int opt;
while((opt = getopt(argc, argv, "n:cp:i:c:fc:")) != -1) {
printf("give me opt %c \n", opt)
swicth(opt){
case 'n' :
//do something
case 'cp' :
//do something
case 'i' :
//do something
}
}
}
I do know for a fact that the 3 parameter: the OptString is where i tell getOpt what are the char of the options it should expect
Also in optarg is where the argument is
its not working!
when i printf( "give me opt %c", opt ) its giving me the numbers, not the char!
for this reason it does not work. it does not check the parameters correctly
Edit:
After doing man 3 getopt
the return value of getopt is: If an option was successfully found, then getopt() returns the option character. If all command-line options have been parsed, then getopt() returns -1.
it returns the option character? it always return integer 1. always! i don't understand how getopt works. how does the switch compare to characters?!
First of all, this:
case 'cp':
...makes no sense. You can't represent two characters with a char or int. Besides, if running your program with -cp 101, getopt will treat it as -c p 101, for a completely different result. If you want to use long options, use getopt_long instead and the appropriate calling syntax, per example: ./a.out -n 42 --cp 101.
Now, here's a working example of getopt() in action:
#include <stdio.h>
#include <getopt.h>
int main(int argc, char* argv[]) {
int opt;
while((opt = getopt(argc, argv, "n:cp:i:c:fc:")) != -1) {
switch (opt){
case 'n':
printf("n was %s\n", optarg);
break;
case 'i':
printf("i was %s\n", optarg);
break;
}
}
return 0;
}
getopt handles only single-character option names, not things like -cp or -fc (which it interprets as multiple option flags in a single string: -c -p, -f -c). If you want to stick to the syntax you defined, then it's easier to just roll your own:
// first check whether argc is odd, then:
for (i = 1; i < argc; i += 2) {
if (strcmp(argv[i], "n") == 0) {
name = argv[i+1];
} else if (strcmp(argv[i], "cp") == 0) {
// etc. etc.
}
}

Resources