getopt_long `--' options does not work for switch statement - c

I have this c program which use getopt_long to parse - and -- options, but when i parse -- options the optarg does not work for the program. how can i assing the optarg value for the varibale if -- is parsed for the program.
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "file_handle.c"
//#include "songs.c"
#include "getoptions.c"
//Mix_Music *music = NULL;
int main(int argc, char *argv[])
{
//init();
int f = 0;
int d = 0;
char *file = NULL;
char *directory = NULL;
const struct option long_options[] = {
/*
{"verbose",no_argument,&verbose_flag,1}
*/
{"help", no_argument, 0, 'h'},
{"file", required_argument, &f, 'f'},
{"directory", required_argument, &d, 'd'},
{0, 0, 0, 0}
};
int opt;
if (argc <= 1)
{
fprintf(stderr, "No arguments provided!\nTry 'cplay --help' for more information.\n");
}
else
{
while ((opt = getopt_long(argc, argv, "d:f:h", long_options, NULL)) != -1)
{
printf("optarg %s\n",optarg);
switch(opt){
case 'h':
//print_help();
break;
case 'f':
puts("test f");
file = optarg;
break;
case 'd':
directory = optarg;
break;
default:
break;
}
}
}
if(f && d){
printf("OPTION -f, --file and -d, --directory are given\n");
printf("%s\n",directory);
int valid = 0;
char response;
while(!valid)
{
printf("Enter one option [f\\d]: ");
scanf(" %c",&response);
if(response == 'd' || response == 'f'){
valid = 1;
}
else
printf("Wrong Input given press f for single file and d for directory option\n");
}
}
printf("%d\n %d",f,d);
}

Re: "the switch statement is only checking for the -h or -d or -f options."
From the GNU manual:
When getopt_long encounters a long option, it takes actions based on
the flag and val fields of the definition of that option. The option
name may be abbreviated as long as the abbreviation is unique.
If flag is a null pointer, then getopt_long returns the contents of
val to indicate which option it found.
If flag (The third member of the struct option) is NULL, then getopt_long returns the contents of val (the fourth member of struct option) to indicate which option it found.
So if the long option was --help, and the corresponding val member was assigned h, then getopt_long would return h.
Some remarks:
Typically, this condition:
if(f && d)
shouldn't be reached. Keep flags and check if the f flag is already given before assigning true to the d flag.
bool f_flag = false;
bool d_flag = false;
Then check for the f_flag:
...
case 'd':
if (f_flag) {
/* Both file and directory flags are
* present. Print usage message and exit.
*/
} else {
strcpy (directory, optarg);
break;
}
Given two pointers p and q, the statement:
p = q;
doesn't copy the contents of the memory pointed to by q to the contents of the memory pointed to by p. It copies the pointer values, such that both p and q now point to the same memory, and any change to the memory via p is reflected when q is used.
So, given:
file = optarg;
and
directory = optarg;
these statements copies the pointer values, not the pointed to memory. This could be a problem if a subsequent operation modified argv, because it would change the memory pointed to by optarg and file / directory too.
Instead, copy the memory to the pointers with strcpy():
strcpy (file, optarg);
strcpy (directory, optarg);

Related

Are optarg values persistent across successive calls to getopt?

By experiment, it appears that I can can capture successive values of optarg while iterating int getopt(int argc, char * const argv[], const char *optstring) and reference them later, as in the following sample program:
// main.c
#include <stdio.h>
#include <unistd.h>
int main( int argc, char* argv[] )
{
int opt;
char o;
char* a = NULL;
char* b = NULL;
while ( -1 != ( opt = getopt( argc, argv, "abcd:e:" ) ) )
{
char o = opt & 0xFF;
switch ( o )
{
case 'a':
{
printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
break;
}
case 'b':
{
printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
break;
}
case 'c':
{
printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
break;
}
case 'd':
{
printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
a = optarg;
break;
}
case 'e':
{
printf( "%c (%p): [%s]\n", o, optarg, (NULL == optarg ? "" : optarg ) );
b = optarg;
break;
}
}
}
printf( "(%p): [%s]\n", a, (NULL == a ? "" : a ) );
printf( "(%p): [%s]\n", b, (NULL == b ? "" : b ) );
return 0;
}
Compilation & example execution:
> gcc -g main.c && ./a.out -dabc -e def -a
d (0x7fffe8d1d2b2): [abc]
e (0x7fffe8d1d2b9): [def]
a ((nil)): []
(0x7fffe8d1d2b2): [abc]
(0x7fffe8d1d2b9): [def]
Question: Is this valid? I.e. are successive non-NULL optarg values valid after successive iterations of getopt() and/or its final iteration (when it returns -1)? I.e. is it safe to capture successive values and reference them later (i.e. without strduping them)? I don't want to assume my experimental code is generally correct.
The man page states there is an extern char* optarg but doesn't specify whether it may be reused by successive invocations of getopt().
(Since the arguments to getopt are argc and argv, that implies that optarg is set to offsets of argv in which case I imagine it is safe to capture its successive values, but I'd like to learn if this is a correct surmise).
According to the POSIX specification of getopt:
The getopt() function shall return the next option character (if one is found) from argv that matches a character in optstring, if there is one that matches. If the option takes an argument, getopt() shall set the variable optarg to point to the option-argument as follows:
If the option was the last character in the string pointed to by an element of argv, then optarg shall contain the next element of argv, and optind shall be incremented by 2. If the resulting value of optind is greater than argc, this indicates a missing option-argument, and getopt() shall return an error indication.
Otherwise, optarg shall point to the string following the option character in that element of argv, and optind shall be incremented by 1.
(Emphasis mine.)
This says optarg always points into an element of argv. It never makes a copy.
Since the elements of argv are valid for as long as your program runs, your code is valid (no copying required).
NB. The POSIX page also shows an example of command line options with arguments that is essentially equivalent to your version:
char *ifile;
...
while ((c = getopt(argc, argv, ":abf:o:")) != -1) {
switch(c) {
...
case 'f':
ifile = optarg;
break;
...
The man page for getopt states:
optstring is a string containing the legitimate option characters. If
such a character is followed by a colon, the option requires an argu‐
ment, so getopt() places a pointer to the following text in the same
argv-element, or the text of the following argv-element, in optarg
And also:
By default, getopt() permutes the contents of argv as it scans, so that
eventually all the nonoptions are at the end.
This means, it does not allocate any memory and does not store text in static buffers, but instead works directly on the argv pointer array you provided and just gives you the address within it.
You can verify this behavior in code:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv) {
int opt, cur;
while (-1 != (opt = getopt(argc, argv, "a:b:c:"))) {
cur = optind - 1;
printf("current elemnt = %d argv[%d] = %p optarg = %p delta = %d\n",
cur, cur, argv[cur], optarg, (int)(optarg - argv[cur]));
}
return 0;
}

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.

Command Line Argument C

I need help to display name to command line like this (I don't know how to explain) in C
$:Enter your name: Test
$:Test>
But when you continue press enter it still showing Test>
$:Test>
$:Test>
So how do we get argv[0] and do something like this (Sorry that I cannot explain probably)
Thank you
command line arguments are stored in char **argv, and there are argc of them.
int main(int argc, char **argv)
{
int i=0;
for(i=0; i< argc; i++)
printf("argument number %d = %s\n", i, argv[i]);
return 0;
}
argv[0] is the name of the program being executed, so argc is always at least == 1 ( or more)
If you had rather shell-like program in mind, maybe the following couldbe of use:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define BUFSIZE 64
int main() {
char prompt[BUFSIZE];
char command[BUFSIZE];
char *prefix = "$:";
char *suffix = ">";
printf("%s%s%s", prefix, "Enter your name:", suffix);
fgets(prompt, BUFSIZE, stdin);
prompt[strlen(prompt)-1] = '\0'; // get rid of the \n
while (true) {
printf("%s%s%s", prefix, prompt, suffix);
fgets(command, BUFSIZE, stdin);
if (strncmp(command,"Quit",4) == 0)
break;
}
return 0;
}
Whenever possible, you should use getopt() so that the order of your parameters doesn't matter. For example, suppose you wanted to take an integer parameter for the size, an integer for the mode of execution, and a toggle to indicate whether to run in "quiet mode" or not. Further suppose that "-h" should print help and exit. Code like this will do the trick. The "s:m:hq" string indicates that "-s" and "-m" provide parameters, but the other flags don't.
int main() {
// parse the command-line options
int opt;
int size = DEFAULT_SIZE, mode = DEFAULT_MODE, quiet = 0;
while ((opt = getopt(argc, argv, "s:m:hq")) != -1) {
switch (opt) {
case 's': size = atoi(optarg); break;
case 'm': mode = atoi(optarg); break;
case 'q': quiet = 1; break;
case 'h': usage(); return 0;
}
}
// rest of code goes here
}
Of course, you should add error checking in case optarg is null.
Also, if you're using C++, "string(optarg)" is an appropriate way for your case statement to set a std::string to hold a value that is stored as a char* in argv.

C : Parsing options the right way

I'm trying to parse two options in a C program.
The program is called like this:
./a.out [OPTIONS] [DIRECTORY 1] [DIRECTORY 2]
The program syncs two directories and has two options. (-r) for recursive syncing (folder inside folder), and (-n) to copy file from local to remote in case it doesn't exist in remote.
Options are:
-r : recursive
-n : copy file if it doesn't exist in remote folder
So calling:
./a.out -r D1 D2
would recursively sync all files and directories from D1 to D2. Files presents in D1 and not present in D2 are ignored.
And calling:
./a.cout -rn D1 D2
would do the same thing but files present in D1 and not present in D2 are copied to D2.
The problem is that calling ./a.out -rn is not the same as calling ./a.out -nr and ./a.out -r -n is not working neither because (-n) is not D1.
Here is how I implement the main.
int main(int argc, char** argv) {
int next_option = 0;
const char* const short_options = "hrn:";
const struct option long_options[] = {
{ "help", 0, NULL, 'h' },
{ "recursive", 1, NULL, 'r' },
{ "new", 1, NULL, 'n' },
{ NULL, 0, NULL, 0 }
};
int recursive = 0;
int new = 0;
do {
next_option = getopt_long(argc, argv, short_options, long_options, NULL);
switch(next_option) {
case 'r':
recursive = 1;
break;
case 'n':
new = 1;
break;
case 'h':
print_help();
return 0;
case -1:
break;
default:
print_help();
return -1;
}
} while(next_option != -1);
sync(argv[2], argv[3], recursive, new);
return EXIT_SUCCESS;
}
You have two (potential) problems here:
You have a stray : in your short option string. This makes -n take an option that swallows any following r. You also have the long options set to take mandatory arguments.
You've hard coded the argument numbers in an inflexible way, and you don't test that they exist.
Try this:
int main(int argc, char** argv) {
int next_option = 0;
const char* const short_options = "hrn";
extern int optind;
const struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "recursive", no_argument, NULL, 'r' },
{ "new", no_argument, NULL, 'n' },
{ NULL, no_argument, NULL, 0 }
};
int recursive = 0;
int new = 0;
do {
next_option = getopt_long(argc, argv, short_options, long_options, NULL);
switch(next_option) {
case 'r':
recursive = 1;
break;
case 'n':
new = 1;
break;
case 'h':
print_help();
return 0;
case -1:
break;
default:
print_help();
return -1;
}
} while(next_option != -1);
if (optind + 1 >= argc)
return -1;
sync(argv[optind], argv[optind+1], recursive, new);
return EXIT_SUCCESS;
}
The problem with using a command line such as -r -n is because you have hard-coded the indexes in the call to sync. You should not do that.
If you read the getopt_long manual page (always do when you have problems with a function!) then you would notice this line:
If there are no more option characters, getopt() returns -1. Then optind is the index in argv of the first argv-element that is not an option.
Read the second sentence carefully.

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