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.
Related
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);
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){
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.
void display(char * str){
printf("%s: Missing file\n", str);
}
int main(int argc, char **argv)
{
int longIndex, opt = 0;
const char *optString = "h?";
static const struct option longOpts[] = {
{ "help", no_argument, NULL, 'h' },
{ NULL, no_argument, NULL, 0 }
};
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
while( opt != -1 ) {
switch( opt ) {
case 'h':
case '?':
display(argv[0]);
break;
default:
break;
}
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
}
return 0;
}
This code compiles fine but when I run it like:
./a.out ?
it does not call display. What am I missing?
The question mark '?' is returned by getopt when it finds an argument that's not in the optstring or if it detects a missing option argument, so you shouldn't use '?' in optstring because it's sort of reserved for that, instead you should use the more conventional 'h' for help.
See the man page
Edit:
This is an example:
switch (opt) {
case 'n':
some_flag = 1;
break;
case 'h': /* help */
default: /* '?' unknown opt or missing arg*/
fprintf(stderr, "Usage: %s [-n nsecs] \n", argv[0]);
exit(EXIT_FAILURE);
}
If you include -? with --help on your help message, leave the question mark out of your call to getopt, leave it out of the case option, and make sure that --help is the first case option in your list, the question mark works as you'd want it to.
const char *optString = "h?";
Above line replace with following line
const char *optString = "h\?";
this is the literal of question mark in C language
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.