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;
}
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);
If optarg (the argument after the flag -s, from Getop library) isn't a digit, I want an error message to be printed and the program terminated, and if it is a digit, size needs to be set to optarg. The problem I have is while a command like the -s r will hit the error message, -s 2 will also, meaning it's interpreting the 2 as a string.
Debugging with for -s 2
#1 printf("%d",atoi(optarg));
#2 printf("%d",isdigit(atoi(optarg)));
I get the int value 2 for line #1,
and an int value of 0 for line #2.
So I was wondering why isdigit(atoi(optarg))) gives me a 0, when atoi(optarg) gives me an int. Is there a better way to check if optarg is an int?
int main (int argc, char *argv[]){
int size;
char option;
size = 0;
const char *optstring;
optstring = "rs:pih";
while ((option = getopt(argc, argv, optstring)) != EOF) {
switch (option) {
case 'r':
type_set = 1;
break;
**case 's':
capacity_set = 1;
if(isdigit(atoi(optarg))==0){
fprintf(stderr,"Argument after -s needs to be an int\n");
return 0;
}**
else{
size = atoi(optarg);
}
break;
default{
return 0;
}
isdigit takes a character and tells you if it is a digit. atoi takes a string (char *) and returns the number that the string represents. So when you call isdigit(atoi(... you're taking a number and treating it as a character. Since the charater codes for digits are 48..57, any number other than one of those will return false.
You probably want isdigit(*optarg) -- this will tell you if the first character of the argument (string) is a digit character. Of course, this only looks at the first character, so you might want isdigit(optarg[0]) && optarg[1] == 0 instead.
If you want to accept a number rather than a digit (and only a number), strtol works much better than atoi as it allows you to check for failures. Something like:
char *end;
errno = 0;
size = strtol(optarg, &end, 10);
while (isspace(*end)) ++end;
if (errno || *end) {
// an error occurred on conversion, or there is extra cruft
// after a number in the argument.
fprintf(stderr,"Argument after -s needs to be an int\n");
return 0; }
Chris Dodd has a great answer. Another, possibly simpler method is as follows ( assuming you just want to know if optarg is a SINGLE digit:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Assuming ASCII. "string" must be one character long and it
// must be a digit
int isSingleDigit( char *str )
{
return( (strlen(str) == 1) && ( *str >= '0' && *str <= '9' ) );
}
int main (void)
{
char *str = "a";
char *str1 = "9";
char *str2 = "10";
char *str3 = "1a";
printf( "%s %d\n", str, isSingleDigit(str));
printf( "%s %d\n", str1, isSingleDigit(str1));
printf( "%s %d\n", str2, isSingleDigit(str2));
printf( "%s %d\n", str3, isSingleDigit(str3));
return 0;
}
Output:
a 0
9 1
10 0
1a 0
Consider a file ArgumentFile.txt
int a=100;
int b[3] = { 5, 2, 5 };
double c = 0.0014;
And the main code code.c
int main(int argc, char *argv[])
{
if (argc > 1) FILE *f = fopen(argv[1], "r");
ParseFile(f); // Set the parameters based on file
DoStuff(a,b,c); // Run the process based on the parsed arguments
}
A user could then pass arguments by doing
./CodeExecutable ArgumentFile.txt
Is there a standard solution to parse arguments from file? It would be an equivalent of getopt which parse arguments from command line?
You do not need an equivalent to getopt() you can use exactly getopt(). The getopt() function does not specifically process command line arguments; it will process any array of pointers to strings in the style of command line arguments.
#define MAX_ARGS 256
#define MAX_FILE_LEN 4096
int main(int argc, char *argv[])
{
if( argc > 1 )
{
FILE *f = fopen(argv[1], "r");
if( f != 0 )
{
char fargs[MAX_FILE_LEN] = "" ;
fread( fargs, 1, MAX_FILE_LEN, f ) ;
// Build fargv from file content
char* fargv[MAX_ARGS] ;
int fargc = 0 ;
fargv[fargc] = strtok( fargs, " \n\r" ) ;
while( fargc < MAX_ARGS && fargv[fargc] != 0 )
{
fargc++ ;
fargv[fargc] = strtok( 0, "\n\r" ) ;
}
// Process fargv using getopt()
while( (char c = getopt( fargc, fargv, "a:b:c:")) != -1 )
{
switch( c )
{
...
}
}
}
}
...
return 0 ;
}
It is probably better to dynamically allocate fargs using teh actual file length, but the above is illustrative only.
Your input file might then look like:
-a 100
-b 5,2,5
-c 0.0014
The getopt() loop will then have to process the arguments as necessary - using sscanf() for example.
switch( c )
{
case 'a' : sscanf( optarg, "%i", a ) ; break ;
case 'b' : sscanf( optarg, "%i,%i,%i", b[0], b[1], b[2] ) ; break ;
case 'c' : sscanf( optarg, "%f", c ) ; break ;
}
DoStuff( a, b, c ) ;
I use getopt(). Here is an example that allows for some more flexibility. This example demonstrates how th handle for optional optarg and multiple optargs.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void usage(void)
{
printf("usage: \n"
"This example demonstrates how to add flexibility to the traditional linux getopt()\n"
"This help text is printed if the program is executed without arguments\n"
"or with an invalid argument configuration.\n"
"to view the help file run without arguments or with -h\n"
"Oterwise the program accepts two options: -d, -u\n"
"-d: can come with 0 or one option argument\n"
"-u: can come with one or more option arguments\n"
"try this to see the output:\n"
"./test -d aaa -u ccc 4 \"quoted multi token string\" -d -u\n");
}
int main(int argc, char **argv)
{
char data[101];
int opt;
memset(data, 0, 101);
while ((opt = getopt(argc, argv, "hd:u:t:")) != -1) {
switch (opt) {
case 'h':
usage();
return 0;
case 'd': // can accept 0 or 1 parameters
if (optarg[0] == '-') { //not an optarg of ours...
optind--;
printf("option: -d. no tokens (another option follows)\n");
break;
}
strncpy(data, optarg, 100);
printf("option: -d. tokens: %s\n", data);
break;
case 'u': //can accept one or more parameters ...
strncpy(data, optarg, 100);
printf("option: -u. tokens: %s", data);
//do we have more arguments for 'u'?
while( optind <= argc && argv[optind][0] != '-') {
strncpy(data, argv[optind], 100);
printf(", %s", data);
optind++;
}
printf(".\n");
break;
case ':': //this happens if we got an option which expects an arg without any optarg.
if(optopt == 'd') {//lets allow a '-d' without its optarg
printf("option: -d. no tokens\n");
break;
}
//otherwise fall through to the default handler
default: //covers ':' '?' for missing value, '-h' for help, etc.
printf("on error you get: opt=%c. optopt=%c opterr=%d\n", opt, optopt, opterr);
return 0;
}
}
return 0;
}
You can't do that using plain C code. You'll have to write platform specific assembly language code to handle that.
Your best option is to use the C proprocessor.
int main(int argc, char *argv[])
{
#include "myfile.txt"
// Do Stuff
}
Having said that, I don't know what you would gain by that instead of putting the contents of myfile.txt in main() directly.
Hi I am working on a Program in a book. The Program is working almost as it is supposed to, except for one mistake. Every time I try to use the "-l" case I get a Segmentation Fault. Any Ideas?
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char *lieferung = "";
int knusprig = 0;
int zahl = 0;
char ch;
while ((ch = getopt(argc, argv, "l : k")) != EOF){
switch (ch) {
case 'l':
lieferung = optarg;
break;
case 'k':
knusprig = 1;
break;
default:
fprintf(stderr, "Unbekannte Option: '%s'\n", optarg);
return 1;
}
}
argc -= optind;
argv += optind;
if (knusprig)
puts("Knuspriger Rand.");
if (lieferung[0])
printf("Zu liefern: %s.\n", lieferung);
puts("Zutaten:");
for (zahl = 0; zahl < argc; zahl++)
puts(argv[zahl]);
return 0;
}
Thanks in advance.
The third argument get getopt shouldn't contain any spaces. Because there are spaces, it reads this argument as "-l takes no argument, -(space) takes an argument, -(space) takes no argument, and -k takes no argument.
Since getopt doesn't expect -l to pass an argument, optarg is set to NULL, which you then subsequently assign to lieferung. You then dereference that variable, resulting in the segfault.
Git rid of the spaces in the format string:
while ((ch = getopt(argc, argv, "l:k")) != EOF){
I think the format is incorrect. Replace "l : k" with "l:k".
I wrote (copied and pasted from Google and simplified) a C program to use getopt to print out the values of the arguments passed in from the Unix command line.
From Unix command line:
./myprog -a 0 -b 1 -c 2
My C code is:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
while ((i = getopt(argc, argv, "abc")) != -1) {
switch (i) {
case 'a':
printf("A = %s\n", optarg);
break;
case 'b':
printf("B = %s\n", optarg);
break;
case 'c':
printf("C = %s\n", optarg);
break;
default:
break;
}
}
return 0;
}
I want to program to print out each of the values passed e.g.
A = 0
B = 1
C = 2
However it is not printing out anything at all.
You forget about ":" after any option with argument. If you will change one line
while ((i = getopt(argc, argv, "a:b:c:")) != -1) {
you will get working variant.
Read properly man 3 getopt, it said about third argument of getopt that
… optstring is a string containing the legitimate option characters. If such a character is followed by a colon, 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. Two colons mean an option
takes an optional arg; if there is text in the current argv-element (i.e., in the same word as the option name itself, for example, "-oarg"), then it is
returned in optarg, otherwise optarg is set to zero. …