Reputation: 395
Hi I want to use getopt
in my program. It works so far for a correct input, but when I use the synopsis the wrong way it prints: option requires an argument -- 's'
after that I get a segmentation fault:
while((args = getopt(argc, argv, "ehs:")) != -1){
switch (args) {
case 'e':
if (options.opts[0] == 1)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[0] = 1;
break;
case 'h':
if (options.opts[1] == 1)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[1] = 1;
break;
case 's':
if (options.opts[2] == 2)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[2] = 2;
char *saveptr;
if((options.word = strtok_r(optarg, ":", &saveptr)) == NULL)
error_exit(USAGE_ERROR, "WORD MISSING");
if((options.tag = strtok_r(NULL, ":", &saveptr)) == NULL)
error_exit(USAGE_ERROR, "TAG MISSING");
char *temp = NULL;
if((temp = strtok_r(NULL, ":", &saveptr)))
error_exit(USAGE_ERROR, "WORD and TAG already set!");
break;
case '?': //Never enters this case or default. WHY???
default:
error_exit(USAGE_ERROR, "");
break;
}
}
I hope you can help me with this problem.
Here is my Opt struct if needed:
typedef struct Opt
{
int opts[3]; // e h s
char *word,
*tag;
} Opt;
Upvotes: 1
Views: 1194
Reputation: 753990
Please learn how to create an MCVE (How to create a Minimal, Complete, and Verifiable Example?) or SSCCE (Short, Self-Contained, Correct Example) — two names and links for the same basic idea.
Like user3629249 in his answer, I created a test program based on what you showed, and cannot find the problem.
goc.c
)#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct Opt
{
int opts[3]; // e h s
char *word,
*tag;
} Opt;
enum E_Numbers { USAGE_ERROR };
static void error_exit(enum E_Numbers e, const char *tag)
{
fprintf(stderr, "%d: %s\n", e, tag);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
Opt options = { { 0 }, 0, 0 };
int args;
while((args = getopt(argc, argv, "ehs:")) != -1){
switch (args) {
case 'e':
if (options.opts[0] == 1)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[0] = 1;
break;
case 'h':
if (options.opts[1] == 1)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[1] = 1;
break;
case 's':
if (options.opts[2] == 2)
error_exit(USAGE_ERROR, "option multiple times");
else
options.opts[2] = 2;
char *saveptr;
if((options.word = strtok_r(optarg, ":", &saveptr)) == NULL)
error_exit(USAGE_ERROR, "WORD MISSING");
if((options.tag = strtok_r(NULL, ":", &saveptr)) == NULL)
error_exit(USAGE_ERROR, "TAG MISSING");
char *temp = NULL;
if((temp = strtok_r(NULL, ":", &saveptr)))
error_exit(USAGE_ERROR, "WORD and TAG already set!");
break;
case '?': //Never enters this case or default. WHY???
default:
error_exit(USAGE_ERROR, "Unexpected option");
break;
}
}
printf("e = %d, h = %d, s = %d\n", options.opts[0], options.opts[1], options.opts[2]);
printf("word = <<%s>>\n", options.word ? options.word : "NULL");
printf("tag = <<%s>>\n", options.tag ? options.tag : "NULL");
return 0;
}
The only change made in the body of the loop was to add the message "Unexpected option"
to the call to error_exit()
in the default:
(and case '?':
) code.
$ ./goc
e = 0, h = 0, s = 0
word = <<NULL>>
tag = <<NULL>>
$ ./goc -h -e
e = 1, h = 1, s = 0
word = <<NULL>>
tag = <<NULL>>
$ ./goc -h -e -s abc:def
e = 1, h = 1, s = 2
word = <<abc>>
tag = <<def>>
$ ./goc -h -e -s abc
0: TAG MISSING
$ ./goc -h -e -s abc:def:
e = 1, h = 1, s = 2
word = <<abc>>
tag = <<def>>
$ ./goc -h -e -s abc:def:ghi
0: WORD and TAG already set!
$ ./goc -h -f -s abc:def:ghi
$ ./goc: illegal option -- f
0: Unexpected option
$ ./goc -a -b -c -d -e -f -h -e
./goc: illegal option -- a
0: Unexpected option
$ ./goc -s
./goc: option requires an argument -- s
0: Unexpected option
$ ./goc -s --
0: TAG MISSING
$ ./goc -s --:--
e = 0, h = 0, s = 2
word = <<-->>
tag = <<-->>
$ ./goc -s -- # Note this one!
0: TAG MISSING
$ ./goc -s --:
0: TAG MISSING
$ ./goc -s --:--
e = 0, h = 0, s = 2
word = <<-->>
tag = <<-->>
$ ./goc -s --::--
e = 0, h = 0, s = 2
word = <<-->>
tag = <<-->>
$ ./goc -s --::--::
e = 0, h = 0, s = 2
word = <<-->>
tag = <<-->>
$ ./goc -s --::--::--
0: WORD and TAG already set!
$
Because the error_exit()
function actually exits, I don't get multiple errors reported from a single run; the first one is fatal.
No crashes; no unexpected behaviour. I think your trouble is elsewhere.
For the record, testing was performed on Mac OS X 10.10.3 with GCC 5.1.0 and the standard getopt()
provided by Apple on the platform. However, I would expect the same behaviour on Linux too, and all the Unix platforms that I've worked on.
Upvotes: 3
Reputation: 16540
here is the code I used for testing:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
int main( int argc, char* argv[] )
{
int options[6] = {0};
int args;
while((args = getopt(argc, argv, "ehs:")) != -1)
{
switch (args)
{
case 'e':
if (options[0] == 1)
printf( "option 'e' can only be included once \n" );
else
options[0] = 1;
break;
case 'h':
if (options[1] == 1)
printf( "option 'h' can only be included once\n" );
else
options[1] = 1;
break;
case 's':
if (options[2] == 2)
printf( "option 's' can only be included once\n" );
else
options[2] = 2;
break;
case '?': //Never enters this case or default. WHY???
default:
printf( "unknown option encountered\n");
break;
}
}
}
here is the command line I used: (untitled is the name of the test function)
./untitled -a -b -c -d -e -f -h -e
here is the result of the run.
./untitled: invalid option -- 'a'
unknown option encountered
./untitled: invalid option -- 'b'
unknown option encountered
./untitled: invalid option -- 'c'
unknown option encountered
./untitled: invalid option -- 'd'
unknown option encountered
./untitled: invalid option -- 'f'
unknown option encountered
option 'e' can only be included once
Since the test worked perfectly, I suspect the problem is elsewhere in your code.
Upvotes: 2
Reputation: 126243
A single :
in the option string means that the option requires an agument, which is either the rest of the argument after the character, or the next argument, if the option character is alone. In this latter case optarg
is set to the next pointer from the argument list, which will be NULL if there is no next argument.
So after seeing a bogus -s
option, getopt
will return s
, and then your code will dereference optarg
, which is a NULL pointer, causing a segfault (actually it passes it to strtok_r
, which then proceeds to deref the uninitialized save
, causing the same problem). Check optarg
to make sure it is not NULL in your s
option code.
Upvotes: 1