C Getopts: Managing all received options and optional optargs - c

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

Related

Using getopt_long() second pass does not work

I wanted to use getopt_long() twice in a routine so that a verbosity flag can be set before parsing everything else but somehow the second call of the function does not process the arguments as expected. Here is a simplified code that demonstrates the issue. Anyone has any clues?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
static void processInput(int argc, const char **argv) {
int k;
int verbose = 0;
struct option long_options[] = {
{"help" , no_argument, NULL, 'h'},
{"verbose", no_argument, NULL, 'v'},
{0, 0, 0, 0}
};
// Construct short_options from long_options
char str[1024] = "";
for (k = 0; k < sizeof(long_options) / sizeof(struct option); k++) {
struct option *o = &long_options[k];
snprintf(str + strlen(str), 1023, "%c%s", o->val, o->has_arg == required_argument ? ":" : (o->has_arg == optional_argument ? "::" : ""));
}
// First pass: just check for verbosity level
int opt, long_index = 0;
while ((opt = getopt_long(argc, (char * const *)argv, str, long_options, &long_index)) != -1) {
printf("Processing %c\n", opt);
switch (opt) {
case 'v':
verbose++;
break;
default:
break;
}
}
//
// Add verbose flag here to show something while setting up
//
// Second pass: now we go through all of them.
long_index = 0;
while ((opt = getopt_long(argc, (char * const *)argv, str, long_options, &long_index)) != -1) {
printf("Processing %c\n", opt);
switch (opt) {
case 'h':
printf("Help text here\n");
break;
case 'v':
// Add a dummy line here so that '-v' does not trigger the default task
break;
default:
fprintf(stderr, "I don't understand: -%c optarg = %s\n", opt, optarg);
exit(EXIT_FAILURE);
break;
}
}
}
int main(int argc, const char **argv) {
processInput(argc, argv);
return EXIT_SUCCESS;
}
The global variable optind is increased to argc by the end of getopt_long() routine so the second pass simply would not go further. In order to have the function reprocess everything from the beginning again, the global variable optind needed to be reset. So, add
// Second pass: now we go through all of them.
optind = 1;
long_index = 0;
As stated is the man page of getopt_long :
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 you have to set optind to 1 before the second pass.
// Second pass: now we go through all of them.
optind = 1;
long_index = 0;
while ((opt = getopt_long(argc, (char * const *)argv, str, long_options, &long_index)) != -1) {
printf("Processing %c\n", opt);
switch (opt) {
case 'h':
printf("Help text here\n");
break;
case 'v':
// Add a dummy line here so that '-v' does not trigger the default task
break;
default:
fprintf(stderr, "I don't understand: -%c optarg = %s\n", opt, optarg);
exit(EXIT_FAILURE);
break;
}
}
Little warning : Further on the man, there is this note :
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.)
So be carefull and choose what it most suit your situation.

Multiple integer arguments with getopt?

What is the best way to handle multiple integer arguments while using getopt? I have to be able to enter something like this on the command line: calc -a 100 50 and it will add up 100 and 50.
while ((opt = getopt(argc, argv, "adms")) != -1)
{
value = atoi(argv[optind + 1]);
printf("value : %d\n", value);
switch (opt)
{
case 'a': aFlag = 1; num1 = atoi(argv[optind]);
sum = num1 + value;
printf("%d + %d = %d\n" , num1, value, sum);
break;
case 'd': dFlag = 1;
break;
case 'm': break;
case 's': break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if (aFlag == 1)
{
num1 = atoi(argv[optind]);
sum = num1 + value;
printf("%d + %d = %d\n" , num1, value, sum);
}
if (dFlag == 1)
{
num2 = atoi(argv[optind]);
sub = num2 / value;
printf("%d / %d = %d\n" , num2, value, sum);
}
I think that the interface you're after is probably:
./program -a 100 50
which accepts a single option -a (alternatives -m, -s and -d), and then processes the other arguments as a list of numbers, rather than as values attached to the option. This works fine unless you planned to be able to use:
./program -a 100 50 -m 20 30
to do addition and multiplication.
What do you want to do if someone can't make their mind up and tries:
./program -am 100 50
Should it be an error, or should -a or -m win? You can argue for any of those with some degree of coherency. I'd go with an error, though.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static int op_add(int a, int b) { return a + b; }
static int op_sub(int a, int b) { return a - b; }
static int op_mul(int a, int b) { return a * b; }
static int op_div(int a, int b) { return a / b; }
int main(int argc, char **argv)
{
int (*fun)(int, int) = 0;
char op;
int opt;
while ((opt = getopt(argc, argv, "adms")) != -1)
{
if (fun != 0)
{
fprintf(stderr, "%s: you can't use two operations\n", argv[0]);
return 1;
}
switch (opt)
{
case 'a': fun = op_add; op = '+'; break;
case 'd': fun = op_div; op = '/'; break;
case 'm': fun = op_mul; op = '*'; break;
case 's': fun = op_sub; op = '-'; break;
default:
fprintf(stderr, "%s: unknown option %c\n", argv[0], optopt);
return 1;
}
}
if (fun == 0 || optind + 2 != argc)
{
fprintf(stderr, "Usage: %s [-adms] num1 num2\n", argv[0]);
return 1;
}
int res = fun(atoi(argv[optind]), atoi(argv[optind+1]));
printf("%s %c %s = %d\n", argv[optind], op, argv[optind+1], res);
return 0;
}
The error checking on the values in the 'numeric' arguments is reprehensibly sloppy (meaning, non-existent). There's no division by zero check, either. But that at least produces plausible answers. It would probably be better to have an array of structures saving the function pointer and the operation character, but that sort of refinement is for you to deal with.
If you need a negative number, use -- to mark the end of the options:
$ ./program -m -- -39 -75
-39 * -75 = 2925
$
Just in case you're not sure about it, int (*fun)(int, int) is a pointer to a function that takes two int arguments and returns an int result. It allows the code to select which function to execute at runtime.
With getopt(), an option can take at most one argument. If you want your program to handle its command line with getopt(), and it must accept arguments such as you describe
calc -a 100 50
, and you must not repeat options, then the only viable solution is to make one or both of the integers non-option arguments instead of arguments to the -a option. I'd suggest making both (or all) non-option arguments. Thus, you might have something like this:
int result = getopt(argc, argv, "asmd");
switch (result) {
case 'a':
// careful: getopt() will be confused by negative numbers
if (getopt(argc, argv, "asmd") != -1) {
fprintf(stderr, "usage: %s -a|-s|-m|-d <operands>\n", argv[0]);
exit(1);
}
do_add(argv + optind);
break;
case 's':
// ...
}
After getopt() returns -1, optind is the index in argv one past that of the last option (+/- option argument) processed.
This excerpt from the man page for getopt() clearly shows that each of the answers (and the original question) are not showing an understanding of getopt:
optstring is a string containing the legitimate option characters.
If such a character is followed by a colon,
[then] the option requires an argument,
so getopt() places a pointer
to the following text in the same argv-element,
or the text of the following argv-element,
in optarg.
your code should also allow for the following:
If getopt() does not recognize an option character,
it prints an error message to stderr,
stores the character in optopt,
and returns '?'.
and finally this (which will allow for the - to be in any position on the command line:
By default, getopt() permutes the contents of argv as it scans, so that
eventually all the nonoptions are at the end.

Issue with options order in getopt

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

Why program doesn't enter into the if statement when it should

I'm trying to implement an 'ls' command that lists file and directories. I have set the incoming argument array to the following:
argv[0] = "./a.out"
argv[1] = "-l"
argv[2] = "test.c"
Here is my code (assume that the main function passes argc and argv to the function I_AM_LS):
#include "ls.h"
int I_AM_LS(int argc, char ** argv)
{
// 'INCLUDING_HIDDEN_FILE' indicates program performs ls including hidden files
// 'EXCLUDING_HIDDEN_FILE' indicates program performs ls excluding.
int hidden_flag = EXCLUDING_HIDDEN_FILE;
int detail_flag = SIMPLY; // default option in ls.
// 'IN_DETAIL' indicates program performs ls with additional information.
// 'SIMPLY' indicates program performs ls without.
char option;
int i;
DIR * dp;
while ((option = getopt(argc, argv, "al")) != -1)
{
switch (option)
{
case 'a':
hidden_flag = INCLUDING_HIDDEN_FILE;
break;
case 'l':
detail_flag = IN_DETAIL;
break;
default: /* '?' */
printf("invaild option.\n");
return -1;
}
}
if( argv[optind] != NULL && argv[optind + 1] != NULL) // multiple argument
{
; // I have not finished the corresponding code yet.
}
else
{
if( argv[optind] == NULL) // case 1
I_REALLY_CALL_ls("./", hidden_flag, detail_flag);
else
I_REALLY_CALL_ls(argv[optind], hidden_flag, detail_flag);
}
printf("optind %d %d\n", optind, argv[optind]);
return 0;
}
}
int main(int argc, const char * argv[])
{
I_AM_LS(argc, argv);
return 0;
}
After the initial parsing loop, the program doesn't enter into the if statement 'argv[optind] != NULL'. We know that optind is 2 and argv[optind] points to "test.c", not NULL, the same behaviour is seem in debug mode.
Are there any problems with passing argv and argc to the function I_AM_LS? What should I do?
Note : I'm working on Xcode on OS X.
if( argv[optind] == NULL) // case 1
I_REALLY_CALL_ls("./", hidden_flag, detail_flag);
else if( argv[optind] != NULL && argv[optind] != NULL)
{
;
}
The condition in this else if is argv[optind] != NULL, evaluated twice for no good reason. So if the first condition doesn't hold, this one does, you do nothing (;), and
else if( argv[optind] != NULL)
{
// single non-option arguemnt.
I_REALLY_CALL_ls(argv[optind], hidden_flag, detail_flag);
}
is unreachable.

options using getopt in c

I want the put a sub-option in a string so that I can use it as a file name for reading a file:
char *nvalue = NULL;
char *dvalue = NULL;
char *input = NULL;
char inputfilename[] = "\"";
int ar;
int main(int argc, char *argv[])
{
while ((ar = getopt(argc, argv, "hn:d:i:")) != -1)
switch (ar)
{
case 'h':
printf("something");
break; /* added */
case 'n':
nvalue = optarg;
if (isdigit(nvalue))
stop = atoi(nvalue);
else
printf("something\n");
break; /* added */
case 'd':
dvalue = optarg;
if (!strcmp(dvalue, "FCFS") || !strcmp(dvalue, "SSTF") ||
!strcmp(dvalue, "C-SCAN") || !strcmp(dvalue, "LOOK"))
;
else
printf("Invalid type of disk scheduling policy entered.\n");
break; /* added */
case 'i':
input = optarg;
strcpy(inputfilename, optarg);
printf("Filename :%s\n", inputfilename);
break;
}
/* ... */
}
So on the command line if I input:
./foobar -i hello
then I should be able to read the file with:
FILE *file = fopen(inputfilename, "r" );
any suggestions? answers?
thanks!
There are a number of problems with your code. I'm ignoring the absence of header files (assuming your code uses the correct ones so all functions have a prototype in scope before use). I'm also reformatting your code ruthlessly but without further comment.
char *nvalue = NULL;
char *dvalue = NULL;
char *input = NULL;
char inputfilename[] = "\"";
This allocated an array of two bytes as inputfilename. I hate to think what's going to happen when you use it.
int ar;
There is every reason why this variable should be local to the main function and no reason visible for it to be a global variable. Unless you have a header declaring them, the other variables should also be static - assuming that you need to access their values outside of main() without a convenient way to pass them as locals. Avoid global variables whenever possible.
int main(int argc, char *argv[])
{
while ((ar = getopt(argc, argv, "hn:d:i:")) != -1)
{
switch (ar)
{
case 'h':
printf("something");
Oops; no break, so the code drops through to the case 'n': code. C is not Pascal.
case 'n':
nvalue = optarg;
if (isdigit(nvalue))
stop = atoi(nvalue);
You haven't shown a declaration for stop. Unless you really need the string, you can do without nvalue, avoiding a global variable, which is always desirable.
else
printf("something\n");
Another missing break; I'm not going to point it out again.
case 'd':
dvalue = optarg;
if (strcmp(dvalue, "FCFS") == 0 ||
strcmp(dvalue, "SSTF") == 0 ||
strcmp(dvalue, "C-SCAN") == 0 ||
strcmp(dvalue, "LOOK" == 0)
{
I'd suggest a comment such as /* Nothing - dvalue is OK */. Or inverting the condition using De Morgan's theorem:
if (strcmp(dvalue, "FCFS") != 0 &&
strcmp(dvalue, "SSTF") != 0 &&
strcmp(dvalue, "C-SCAN") != 0 &&
strcmp(dvalue, "LOOK" != 0)
You might even decide to encapsulate that test into a function which tests the value against each element of an array of codes.
}
else
printf("Invalid type of disk scheduling policy entered.\n");
It would be courteous to provide a list of the acceptable values - which suddenly becomes another reason for having an array of valid values which you can use to generate the list. Very often, error messages should be reported on stderr (using fprintf()) rather than stdout.
case 'i':
input = optarg;
This assignment is sufficient.
strcpy(inputfilename, optarg);
Unless the user types a single-character file name, you've just overflowed the inputfilename array. You really didn't need to make a copy of the argument unless you're going to modify the name (for example, to add or change the extension on the name).
//strcat(inputfilename,"\"");
printf("Filename :%s\n", inputfilename);
}
You didn't include a default clause. Since ar will be assigned the value ? when the user-provided option is not recognized, this is normally your cue to provide a simple usage message and exit.

Resources