Login Register


Tutorial CLI parsing in C, part 1 filter_list
Author
Message
CLI parsing in C, part 1 #1
In any language, parsing CLI options (the arguments prefixed with - or --) is a chore. Thankfully, the getopt library exists for C and C++. Being C, it won't do things like generate usage strings or help messages for you as other languages like Python might, but it mitigates many of the annoyances that come with this task.

Basic structure
To use the basic getopt (which only supports short flags, i.e. "-" followed by one character), we need to declare an int (in our case, "c") and assign it the return value of the getopt function, which takes an int, argc; a char**, argv; and a char*, options which I'll explain later (for any nitpickers, yes, I'm simplifying) until it returns -1.
Code:
#include <getopt.h> // getopt library header int main(int argc,char **argv){    int c;    while((c=getopt(argc,argv,"ab:c::"))!=-1){        ...    } }
Unless you want to get into the mess of pointers involved in moving around arrays of C strings, it's best to call this in the main function (check out main.c, cliopts.h, and cliopts.c here for a possible strategy of breaking up the code).

Options
Next, the options argument that I said I would explain. Each option is made up of a non-colon character (representing the option's identifier, e.g. "x" in options would match "-x" when the program is executed); followed by nothing, one colon to take an argument (e.g. "x:"), or two colons for an optional argument (e.g. "x::"; this is a GNU extension, there's nothing in POSIX RFC that requires it. It actually breaks the standard that optional arguments shouldn't exist).

To match options, it's common practice to switch on the return value of getopt within the while loop. Storing option results needs to be done manually. If you're going to be passing the options to other functions, a struct works nicely.
Code:
#include <getopt.h> struct opt_results{    int a,fc;    char *b,*sc; } opt_results; int main(int argc,char **argv){    struct opt_results res;    int c;    while((c=getopt(argc,argv,"ab:c::"))!=-1){        switch(c){            case 'a':                res.a=1;                break;            case 'b':                res.b=optarg; // argument passed to -b                break;            case 'c':                res.sc=optarg; // argument passed to -c (or null)                res.fc=1;                break;        }    } }

Getopt can also return '?' for unknown options or erroneous arguments. If the "opterr" variable is set to 0 (default), getopt will print these messages automatically, but it's a good example nonetheless.
Code:
#include <getopt.h> #include <stdlib.h> // needed for abort #include <stdio.h> // needed for fprintf int main(int argc,char **argv){    int c;    opterr=1; // disable automatic error reporting    while((c=getopt(argc,argv,"ab:c::"))!=-1){        switch(c){            ...            case '?':                if(optopt=='c') // optopt is the erroneous option                    fprintf(stderr,"Option -%c requires an argument.\n",optopt);                else if(isprint(optopt)) // optopt is printable                    fprintf(stderr,"Unknown option `-%c'.\n",optopt);                else // optopt is a non-printable byte                    fprintf(stderr,"Unknown option character `\\x%x'.\n",optopt);                return 1;            default: // something went horribly wrong                abort();        }    } }

Non-option arguments
The last new thing we'll be exploring in this part is non-option arguments. If your program takes extra arguments after the options (e.g. a list of files), you can use the "optind" variable set by getopt to get the remaining arguments.
Code:
int main(int argc,char **argv){    int c;    while((c=getopt(argc,argv,"ab:c::"))!=-1){        ...    }    printf("files:");    for(int i=optind;i<argc;i++)        printf(" %s",argv[i]);    printf("\n"); }

Putting it all together
At the end of it all, you'll have a working parser! I made the functional one below using the examples throughout this tutorial.
Code:
#include <getopt.h> #include <stdlib.h> #include <stdio.h> struct opts_results{    int a,fc;    char *b,*sc; } opts_results; int main(int argc,char **argv){    struct opts_results res;    int c;    opterr=1;    while((c=getopt(argc,argv,"ab:c::"))!=-1){        switch(c){            case 'a':                res.a=1;                break;            case 'b':                res.b=optarg; // argument passed to -b                break;            case 'c':                res.sc=optarg; // argument passed to -c (or null)                res.fc=1;                break;            case '?':                if(optopt=='c') // optopt is the erroneous option                    fprintf(stderr,"Option -%c requires an argument.\n",optopt);                else if(isprint(optopt)) // optopt is printable                    fprintf(stderr,"Unknown option `-%c'.\n",optopt);                else // optopt is a non-printable byte                    fprintf(stderr,"Unknown option character `\\x%x'.\n",optopt);                return 1;            default: // something went horribly wrong                abort();        }    }    printf("a: %d, b: %s, fc: %d, sc: %s\n",res.a,res.b,res.fc,res.sc);    printf("files:");    for(int i=optind;i<argc;i++)        printf(" %s",argv[i]);    printf("\n"); }

A quick end note: this is part 1 of 2. The getopt_long function (part of the same library) is more complicated, so I thought I'd lay the foundation here first.
(This post was last modified: 07-31-2017, 01:24 PM by Inori.)
It's often the outcasts, the iconoclasts ... those who have the least to lose because they
don't have much in the first place, who feel the new currents and ride them the farthest.

Reply

RE: CLI parsing in C, part 1 #2
LOL, just boost that stuff, way easier.

Reply

RE: CLI parsing in C, part 1 #3
(07-16-2017, 08:48 PM)BORW3 Wrote: LOL, just boost that stuff, way easier.

Boost is a terrible solution if you're trying to make anything lightweight. Plus, how do you learn C if you use a library that does everything for you?
It's often the outcasts, the iconoclasts ... those who have the least to lose because they
don't have much in the first place, who feel the new currents and ride them the farthest.

Reply







Users browsing this thread: