Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does "optind" get assigned in C?

Tags:

c

posix

getopt

I am creating this question because there is not much about how this optind gets assigned for each loop.

Man page says :

The variable optind is the index of the next element to be processed in argv. The system initializes this value to 1.

Below, I have a simple code I got from Head First C and in the code we subtract "optind" from "argc" and we get the number of leftover arguments, which will we use then to print leftover arguments as "Ingredients".

#include <unistd.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
char* delivery = "";
int thick = 0 ;
int count = 0;

char ch;,

for(int i = 0; i < argc;i++){
//This is , to show the whole array and their indexes.
    printf("Argv[%i] = %s\n", i, argv[i]);
}
while((ch = getopt(argc, argv, "d:t")) != -1 ){
    switch(ch) {
        case 'd':
            printf("Optind in case 'd' : %i\n",optind);
            delivery = optarg;
            break;
        case 't':
            printf("Optind in case 't' : %i\n",optind);
            thick = 1;
            break;
        default:
            fprintf(stderr,"Unknown option: '%s'\n", optarg); // optional argument.
            return 1;
    }
}
    argc -= optind;
    argv += optind;
    printf("Optind : %i and Argc after the subctraction : %i\n",optind,argc);
    if(thick)
        puts("Thick crust");
    if(delivery[0]){
        printf("To be delivered %s\n", delivery);
    }

    puts("Ingredients:");
    for(count = 0; count < argc ; count ++){
        puts(argv[count]);
    }
    return 0;
}

So at the beginning of the code the for loop writes all the array and its indexes to see the difference.

Then I run the code with :

./pizzaCode -d now Anchovies Pineapple -t //-t is intentionally at the end

I was told that if the flag was at the end it wouldn't get in the 't' case but somehow it works on my ubuntu. That is another thing I wonder but not the main question.

So the output is as follows :

Argv[0] = ./pizzaCode
Argv[1] = -d
Argv[2] = now
Argv[3] = Anchovies
Argv[4] = Pineapple
Argv[5] = -t
Optind in case 'd' : 3
Optind in case 't' : 6
Optind : 4 and Argc after the subctraction : 2
Thick crust
To be delivered now
Ingredients:
Anchovies
Pineapple

1- Everything is fine so far, the problem is how come argv[0] and argv1 became Anchovies and Pineapple ?

2- And another question is how did optind become 3 in case 'd'? Since 'd's index is 1 and the next index is 2.

3- How did optind become 4 after the loop ? It was 6 in the case 't'.

I hope my question is clear for you all, I am just trying to understand the logic instead of having to memorize it. Thank you in advance!

like image 934
Burak Kaymakci Avatar asked Oct 08 '17 22:10

Burak Kaymakci


People also ask

How does Optind work in C?

The optind variable is the index value of the next argument that should be handled by the getopt() function. opterr will let you control if the getopt() function should print errors to the console.

What is Optind in getopt?

The variable optind is the index of the next element of argv to be processed. It is initialized to 1, and getopt() updates it as it processes each element of argv[]. The getopt() function returns the next option character (if one is found) from argv that matches a character in optstring, if any.

What is the purpose of Optind and Optopt?

optarg indicates an optional parameter to a command line option. opterr can be set to 0 to prevent getopt() from printing error messages. optind is the index of the next element of the argument list to be process, and optopt is the command line option last matched.

What is argv [] in C?

The argv argument is a vector of C strings; its elements are the individual command line argument strings. The file name of the program being run is also included in the vector as the first element; the value of argc counts this element.


1 Answers

The manpage for Gnu getopt documents this non-standard implementation:

By default, getopt() permutes the contents of argv as it scans, so that eventually all the nonoptions are at the end.

This is actually not quite true; the permutation occurs after the last option is scanned, as you have seen in your example. But the effect is the same; argv is permuted so that nonoptions are at the end, and optind is modified to index the first nonoption.

If you want to avoid the permutation, so that getopt behaves as per Posix:

If the first character of optstring is '+' or the environment variable POSIXLY_CORRECT is set, then option processing stops as soon as a nonoption argument is encountered.

In this case, no permuting is done and optind's value is preserved.

Setting POSIXLY_CORRECT has other consequences, documented here and there in the manpages for various Gnu utilities. My habit is to use + as the first character of the option string (unless I actually want non-Posix behaviour), but arguably setting the environment variable is more portable.


For your specific questions:

  1. Why are the non-option arguments at argv[0] and argv[1]?

    Because you modified argv: argv += optind;

  2. Why is optind 3 in the loop processing option -d?

    Because that option takes an argument. So the next argument is the one following the now argument, which has already been processed (by placing a pointer to it into optarg).

  3. How did optind become 4?

    As above, it was modified after the argv vector was permuted, in order for optind to be the index of the first "unprocessed" non-option argument.

like image 139
rici Avatar answered Sep 25 '22 00:09

rici