Section 26.3. Using Callbacks


26.3. Using Callbacks

We have shown two ways of handling options with popt: having an option returned by poptGetNextOpt() and having variables changed automatically when options are present. Unfortunately, neither of these are satisfactory for nested tables. Returning options defined by a nested table for the application to handle is obviously unworkable as nested tables are designed to prevent the application from having to know about options provided by a library. Setting variables does not work very well either as it is not clear what variables would get set. Using global variables is often a bad idea, and there are no local variables available for the library to use as the parsing happens from the main application, not from a library. To provide flexible option handling in nested applications, popt provides a callback facility.

Each table can define its own callback function, which overrides normal processing for the options defined in that table. Instead, the callback function gets called for each option that is found. Options defined in other option tables (including tables nested inside of a table defining a callback) get handled using the normal rules, unless those other tables define their own callbacks.

Callbacks can be defined only in the first entry in an option table. When that entry defines a callback, the argInfo member is POPT_ARG_CALLBACK and arg points to the callback function. The descrip member can be any pointer value, and it is passed to the callback each time it is invoked, allowing any arbitrary data to be accessed by the callback. All of the other members of struct poptOption should be zero or NULL.

Callbacks can be called at three points during option processing: before processing starts, when an option in the table for that callback is found, and when processing is complete. This gives libraries the opportunity to initialize any structures they need to (including the data defined by the descrip member) and to perform any housekeeping that may need to occur when processing is completed (perhaps cleaning up dynamic memory allocated for the descrip member). They are always called when options are found, but the option table needs to specify that they should be called in the other two places. To do this, POPT_CBFLAG_PRE or POPT_CBFLAG_POST (or both) should be logically OR'ed with the POPT_ARG_CALLBACK value set in the arg member of the structure specifying the callback.

Here is the prototype that should be used to define the callback function:

 void callback(poptContext con, enum poptCallbackReason reason,     const struct poptOption * opt, const char * arg,     const void * data); 

The first option is the context that is being parsed when the callback gets invoked. The next option is POPT_CALLBACK_REASON_PRE if option processing has not yet begun, POPT_CALLBACK_REASON_POST if option processing is finished, or POPT_CALLBACK_REASON_OPTION if an option in the table for this callback has been found. If it is the latter, the opt argument points to the option table entry for the option that was found, and the arg argument points to the string that defines the argument for the option. If a nonstring argument is expected, the callback is responsible for checking the type and converting the argument. The last parameter to the callback, data, is the value of the descrip field in the option table entry that sets up the callback.

Here is an example of a library that uses a nested popt table and callbacks to parse some command-line options. The data structure is initialized before the command-line parsing begins, and the final values are displayed afterward.

  1: /* popt-lib.c */  2:  3: #include <popt.h>  4: #include <stdlib.h>  5:  6: struct params {  7:     int height, width;  8:     char * fg, * bg;  9: }; 10: 11: static void callback(poptContext con, 12:                      enum poptCallbackReason reason, 13:                      const struct poptOption * opt, 14:                      const char * arg, 15:                      const void * data); 16: 17: /* Store the parsed variables here. A global is not normally 18:    preferred, but it is simpler. */ 19: struct params ourParam; 20: 21: struct poptOption libTable[] = { 22:     { NULL, '\0', 23:       POPT_ARG_CALLBACK | POPT_CBFLAG_PRE | POPT_CBFLAG_POST, 24:       callback, '\0', (void *) &ourParam, NULL }, 25:     { "height", 'h', POPT_ARG_STRING, NULL, '\0', NULL, NULL }, 26:     { "width", 'w', POPT_ARG_STRING, NULL, '\0', NULL, NULL }, 27:     { "fg", 'f', POPT_ARG_STRING, NULL, '\0', NULL, NULL }, 28:     { "bg", 'b', POPT_ARG_STRING, NULL, '\0', NULL, NULL }, 29:     { NULL, '\0', POPT_ARG_NONE, NULL, '\0', NULL, NULL } 30: }; 31: 32: static void callback(poptContext con, 33:                      enum poptCallbackReason reason, 34:                      const struct poptOption * opt, 35:                      const char * arg, 36:                      const void * data) { 37:     struct params * p = (void *) data; 38:     char * chptr = NULL; 39: 40:     if (reason == POPT_CALLBACK_REASON_PRE) { 41:         p->height = 640; 42:         p->width = 480; 43:         p->fg = "white"; 44:         p->bg = "black"; 45:     } else if (reason == POPT_CALLBACK_REASON_POST) { 46:         printf("using height %d width %d fg %s bg %s\n", 47:                p->height, p->width, p->fg, p->bg); 48: 49:     } else { 50:         switch (opt->shortName) { 51:             case 'h': p->height = strtol(arg, &chptr, 10); break; 52:             case 'w': p->width = strtol(arg, &chptr, 10); break; 53:             case 'f': p->fg = (char *) arg; break; 54:             case 'b': p->bg = (char *) arg; break; 55:         } 56: 57:         if (chptr && *chptr) { 58:             fprintf(stderr, "numeric argument expected for %s\n", 59:                     opt->longName); 60:             exit(1); 61:         } 62:     } 63: } 64: 

Programs that want to provide these command-line arguments need to include a single extra line in their popt table. Normally, this line would be a macro provided by a header file (like POPT_AUTOHELP is implemented), but for simplicity we just explicitly listed the line for this example.

  1: /* popt-nest.c */  2:  3: #include <popt.h>  4:  5: /* this would normally be declared in a header file */  6: extern struct poptOption libTable[];  7:  8: int main(int argc, const char * argv[]) {  9:     poptContext optCon; 10:     int rc; 11:     struct poptOption options[] = { 12:         { "app1", '\0', POPT_ARG_NONE, NULL, '\0' }, 13:         { NULL, '\0', POPT_ARG_INCLUDE_TABLE, libTable, 14:           '\0', "Nested:", }, 15:         POPT_AUTOHELP 16:         { NULL, '\0', POPT_ARG_NONE, NULL, '\0' } 17:     }; 18: 19:     optCon = poptGetContext("popt-nest", argc, argv, options, 0); 20: 21:     if ((rc = poptGetNextOpt(optCon)) < -1) { 22:         fprintf(stderr, "%s: %s\n", 23:                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 24:                 poptStrerror(rc)); 25:         return 1; 26:     } 27: 28:     return 0; 29: } 


    Linux Application Development
    Linux Application Development (paperback) (2nd Edition)
    ISBN: 0321563220
    EAN: 2147483647
    Year: 2003
    Pages: 168 © 2008-2017.
    If you may any questions please contact us: