Sunday, March 22, 2020

Command Line Processing with Popt

General practice for writing flexible applications lend toward the ability to specify command-line arguments to tailor the behavior of your application. For instance, a common practice of specifying -v to enable a verbose mode where debugging information is echoed as your application runs. Specifying a unique port for networking applications, or enablement/disablement of an application gui are two other examples that are common. Since command line arguments can take the flavor of integer, floats, doubles, strings...the code for parsing these arguments can be far from simple. Tailoring flexible parsers every time you develop an application is labor-intensive and a waste of time if a general mechanism were available.

Lucky for us, such a mechanism does exist; namely the Popt library.

Start by installing the Popt package:
$ sudo apt-get install libpopt-dev

A short example of an application which uses the library follows:

 1  #include <popt.h>
 2  #include <stdio.h>
 3  #include <stdlib.h>
 4
 5  int main(int argc, char **argv) {
 6          /* option parsing variables */
 7          char ch;
 8          poptContext opt_con;   /* context for parsing command-line options */
 9          char *extra_arg;
10          static int i=0;
11          static char *s="";
12          static float f=0.0;
13          static double d=0.0;
14          int verbose=0;
15
16          static struct poptOption options_table[] = {
17                  { "integer", 'i', POPT_ARG_INT, &i, 'i', "grab an integer", "INT" },
18                  { "string", 's', POPT_ARG_STRING, &s, 's', "grab a string", "STRING" },
19                  { "float", 'f', POPT_ARG_FLOAT, &f, 'f', "grab a float", "FLOAT" },
20                  { "double", 'd', POPT_ARG_DOUBLE, &d, 'd', "grab a double", "DOUBLE" },
21                  { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "enable verbose", "" },
22                  POPT_AUTOHELP
23                  { NULL, 0, 0, NULL, 0 } /* end-of-list terminator */
24          };
25
26          opt_con = poptGetContext(NULL, argc, (const char **)argv, options_table, 0);
27
28          /* Now do options processing */
29          while ((ch = poptGetNextOpt(opt_con)) >= 0) {
30                  printf("between while & switch: ch = %c\n", ch);
31                  switch (ch) {
32                          case 'i':
33                                  printf("handling 'i' option.\n");
34                                  break;
35                          case 's':
36                                  printf("handling 's' option.\n");
37                                  break;
38                          case 'f':
39                                  printf("handling 'f' option.\n");
40                                  break;
41                          case 'd':
42                                  printf("handling 'd' option.\n");
43                                  break;
44                          case 'v':
45                                  printf("handling 'v' option.\n");
46                                  verbose = 1;
47                                  break;
48                  }
49          }
50
51          if (ch < -1) {   

52                  // the user specified an invalid option, tell them   
53                  poptPrintHelp(opt_con, stderr, 0);       
54          }       
55       
56          /* non-option args */       
57          while (extra_arg = (char *)poptGetArg(opt_con)) {       
58                  printf("extra arg: %s\n", extra_arg);       
59                  exit(1);       
60          }       
61       
62       
63          /* cleanup */       
64          poptFreeContext(opt_con);       
65       
66  printf("(%s:%d) i = %d\n",__FILE__,__LINE__,i);       
67  printf("(%s:%d) s = '%s'\n",__FILE__,__LINE__,s);       
68  printf("(%s:%d) f = %f\n",__FILE__,__LINE__,f);       
69  printf("(%s:%d) d = %lf\n",__FILE__,__LINE__,d);       
70  printf("(%s:%d) v = %d\n",__FILE__,__LINE__,verbose);       
71       
72       
73    return EXIT_SUCCESS;       
74  }


You compile and link the application by:
g++ -Wall   main.o  -lpopt -o main


An added bonus of using this application is the automatic introduced interfaces for a brief and more verbose help descriptions.


~/Desktop/sourceCode/C/popt$ ./main --help
Usage: main [OPTION...]
-i, --integer=INT       grab an integer
-s, --string=STRING     grab a string
-f, --float=FLOAT       grab a float
-d, --double=DOUBLE     grab a double
-v, --verbose           enable verbose

Help options:
-?, --help              Show this help message
--usage                 Display brief usage message



~/Desktop/sourceCode/C/popt$ ./main --usage
Usage: main [-v?] [-i|--integer INT] [-s|--string STRING] [-f|--float FLOAT]
      [-d|--double DOUBLE] [-v|--verbose] [-?|--help] [--usage]



The heart of the library lies with proper initialization of the popt structure which is defined as follows:

struct poptOption {
  const char * longName;  
  char shortName;        
  int argInfo;
  void * arg;            
  int val;                
  const char * descrip;  
  const char * argDescrip;
};

The ability to specify either a long or short argument name is common practice; -h or --help is a common example of this form.

The long and short forms are specified as the 1st and 2nd element in the popt structure. The 3rd and 4th specify of what type the following argument consists of as well as the address in which the value will be stored. The 6th and 7th arguments specify the argument description and argument field name which is displayed in the help and brief. The 5th field is a bit of a mystery at this time.

That's all for now.

No comments:

Post a Comment