3

I am running a C program like this:

int main(int argc, char **argv)
{
    int opt;

    // Normal command line parsing
    while ((opt = getopt(argc, argv, "s")) != -1)
        printf("app opt %c\n", opt);

    // "Simulate" the command line compiling the argument list manually
    char optarr0[100];
    char optarr1[100];
    strcpy(optarr0, "./myapp");
    strcpy(optarr1, "-s");
    char *man_argv[2];
    int man_argc = 2;
    man_argv[0] = optarr0;
    man_argv[1] = optarr1;  
    while ((opt = getopt(man_argc, man_argv, "s")) != -1)
        printf("man opt %c\n", opt);

    printf("done\n");

    return 0;
}

Where I am trying to supply arguments to POSIX getopt manually instead of getting them from the main parameters argc and argv, from the actual command line.

The runtime system is uCLinux for Blackfin ADSP-BF518 processor. Linux version is:

Linux blackfin 2.6.35.13 #204 Tue Oct 31 13:34:19 CET 2017 blackfin GNU/Linux

and I am cross-compiling the application in a Lubuntu 12.04 system with gcc 4.6.3.

The problem is that the output of the program is:

root:/home> ./myapp -s
app opt s
done

instead of the expected one:

root:/home> ./myapp -s
app opt s
man opt s
done

The application even crashes when called with an additional invalid parameter:

root:/home> ./myapp -s -c

What could be the problem?

2
  • 4
    You need to reset the value of optind as described in getopt(3) — Linux manual page Commented May 29 at 13:23
  • 4
    The array of argument pointers is supposed to be null terminated, i.e. man_argv[man_argc] should be a null pointer. Commented May 29 at 13:46

2 Answers 2

2

The getopt function uses global variables to keep track of the current parsing state. The only variables documented by POSIX for this purpose are:

extern char *optarg;
extern int opterr, optind, optopt;

If you reset optind to 1 before calling getopt on a different set of arguments, you might get the expected behavior, but as is documented in the linux man page:

When an element of argv[] contains multiple option characters, it is unspecified how getopt() determines which options have already been processed.

This means that there is no standard way to guarantee that setting optind to 1 ensures a proper reset of the getopt parser. If this variable had an initial value of 0, the parser could assume that this is meant to specify an initial condition, but this behavior is not specified so it cannot be relied upon.

More recent versions of BSD unix (OpenBSD, FreeBSD, macOS) mention another global variable int optreset that can be set to 1 to ensure a proper restart, in addition to optind being set to 1 before the call to getopt. This behavior is documented in the OpenBSD man page:

In order to use getopt() to evaluate multiple sets of arguments, or to evaluate a single set of arguments multiple times, the variable optreset must be set to 1 before the second and each additional set of calls to getopt(), and the variable optind must be reinitialized.

Sign up to request clarification or add additional context in comments.

Comments

1

The getopt function uses a global variable called optind, initialized to 1, to keep track of the current option it is processing on successive calls. When you call getopt again on your rebuilt list of arguments, the value of optind is 3 so that's where it starts in your set of arguments.

You need to reset this to 1 before calling getopt on your argument list. Also, the argument list needs to be NULL terminated:

char *man_argv[3] = { optarr0, optarr1, NULL };
int man_argc = 2;
optind = 1;
while ((opt = getopt(man_argc, man_argv, "s")) != -1)
    printf("man opt %c\n", opt);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.