OUI: Options, Usage and Initializations

Contents

    0.0: Invocation
    1.0: Introducation
    2.0: Packages
    3.0: Options
    4.0: Initializations
    5.0: Usage
    6.0: Handling Default Initializations
    7.0: See Also

0.0: Invocation

oui [options] file [file ...]
    -k Keep incomplete output file on error
    -o <filename> Specify Output Filename
    -q Print usage message
    -u Don't sort the "sort" strings
    -v Increasing level of verbosity
    -w Treat warnings as errors

1.0: Introducation

oui is a little language for processing Options, Usage and Initializations for C programs.

When invoking a program from a shell command line, "options" are characters which may be added to the program name, possibly with arguments, which request a change in the behaviour of the program. For example, the -l option to the command ls causes a long-format output.

"Usage" is text which describes the options available from the command line as well as other useful information about running the program. Under QNX, usage information can be read by invoking the use utility, (e.g. use ls.)

"Initializations" are C-language commands which must be executed before the features of a particular package can be used. This includes processing command-line options as well as any other startup operations such as clearing the screen or establishing a connection with a windowing system.

oui reads in definition files for one or more "packages" and produces C source code for a function called oui_init_options() and usage information compatible with the QNX usemsg utility. oui is particularly helpful as an adjunct to function libraries. Many library "packages" require initialization code to be run and may offer standard options to users from the command line.

2.0: Packages

Each oui input file defines one or more "packages". The first line of the file should be the package definition:
        <package> sample
This statement defines the name of the current package to be "sample". oui guarantees that each file is read only once and that each package is defined only once. If an attempt is made to read a file more than once, the subsequent attempts are simply ignored. If a package statement is encountered for a package which is already defined, all of the statements in the new definition are ignored. (This mechanism can be used for overriding default definitions.)

It is often the case that one package is only useful in combination with another package or packages. This can be indicated by way of the "include" statement:

        <include> msg
This statement indicates that the current package requires the "msg" package. As it happens, there is a package named msg in the ARP oui library which defines the options, usage and initializations for programs accessing the msg() function. Any number of packages may be listed on one include statement.

When combining multiple packages, it is often the case that the initializations for one package must come before or after the initializations for another package. This type of dependency can be expressed through the <preceed> and <follow> statements.

        <follow> msg msghdr dg
This statement requests that the initializations for the current package be sequenced after any initializations for the msg, msghdr and dg packages. The preceed and follow statements do not cause the specified packages to be included. They simply specify the ordering if they are included by some other mechanism.

3.0: Options

oui supports the POSIX-style command-line options as accepted by the getopt() function. This style of options consists of single-letter options which are preceeded on the command line by a hyphen (e.g. ls -l). An option letter may take an argument (e.g. sin -n 1). Multiple options may be combined by concatenation (e.g. ls -lt).

If your package is going to support one or more command-line options, you must specify which option letters it will handle in an <opts> statement:

        <opts> "lt:q"
This statement indicates that the program will accept option letters l, t and q, and that the t option requires an argument. The syntax of this statement is important. There should be no spaces or tabs after the right angle bracket or between or after the option letters.

When packages are combined, the options required by each package are checked against the options for every other package to guarantee that there are no conflicts. In certain special cases, two related packages may wish to "share" an option letter. This can be accomplished by creating a third package which they both include. This is the case with the msg package and the client package which share the -h option. If they each specified h as an option, oui would complain that there was a conflict.

Specifying the option letters only means that the application will quietly accept those options at runtime. We have not as yet told oui what to do when those options are specified.

4.0: Initializations

The <init> statement specifies code to be executed during initialization. This code does not necessarily have anything to do with options:
        <init> printf("Starting Up...");
Within the ARP libraries, we use the convention of naming functions which handle command-line options using the suffix "_init_options()". The DG package contains the lines:
        <init>
          DG_init_options(argc, argv);
(You should recognize argc and argv as the standard C method of accessing the command line in the main program.) Your package may include any number of lines after the <init> statement.

If your initialization references a function or data object, that object should most likely be defined prior to its invocation. This is usually done in C via the #include statement. In oui, this is replaced with the <include> statement:

        <include> "dg.h"
        <include> <stdlib.h>
Note that this is the same keyword which is used for including additional oui packages. The difference is that C header filenames are enclosed in quotes or angle brackets.

You should include any header files required by your initializations without regard to whether other packages might also be including those headers. oui will guarantee that only one #include statement will be generated for each required header.

Sometimes you will need to make global or static definitions within the C file which is generated by oui. The <defs> keyword is used for this purpose. Each line following the <defs> command is copied into the output file outside of the initialization function:

        <defs>
          int file_count;
          #define MAX_FILES 5
In other cases, a local variable is required within the initialization function for holding intermediate values or default initializations. This is handled by the <vars> command. Each line following this command is placed in the output immediately after the declaration of the initialization function prior to any of the <init> statements. I will describe later how these variables are used to provide default initialization parameters.

Often you may find you want to add a single option to an existing program, for example passing a calibration constant to a data extraction program. Ordinarily, processing any options requires generation of a function which loops while calling getopt(). This is somewhat tedious when only a single option is required. The <switch> and <arg> commands will generate this loop for you automatically. Lines following the <switch> command are placed inside a switch statement in a position suitable for case statements. For example, the commands:

        <switch>
          case 'l':
          limit = atoi(optarg);
          break;
will process a -l option which takes an integer argument. When an argument is required (as specified by a ':' following the option letter in the <opts> statement), the <switch> code can reference the argument string via the global variable optarg as described in the getopt() documentation.

Warning: As currently implemented, the <switch> command does not respect the <follow> and <preceed> directives. A single loop is generated and is placed before all the other initializations. However it can still be useful in these cases: go ahead and use <switch>, then use the generated C code to create your own init_options() function. You can put the new init_options() function back into the .oui file under <defs>, remove the <switch> and replace it with an <init> referencing the new function.

The <args> command will place code in a loop which processes any arguments remaining on the command line after the options are handled. Again, the optarg variable is used to reference each argument in turn:

        <args>
          process(optarg);

Note that the same caveats apply to <args> as apply to <switch>. It is really only useful in very simple cases.

5.0: Usage

There are three commands which govern the generation of usage messages. The <synopsis> command may be used to override the default command-line synopsis, <sort> may be used to give one-line descriptions of the options supported by this package, and <unsort> may be used to supply more general usage information.

<synopsis> accepts exactly one line of text which becomes the first line of the programs usage message. There may be only one <synopsis> statement among all the packages referenced, since there can be only one synopsis. The default synopsis is:

        %C [options]
and is often sufficient if coupled with good descriptions of the options.

Usage for the options is provided with the <sort> command:

        <sort>
          -l <limit> Define a new upper limit
          -c Clear the server's error count
Each option line should begin with a tab character, which will line up the resulting output with the synopsis' "[options]". As the command implies, all the <sort> lines are sorted by option letter before being output. This behaviour can be disabled by specifying -u on the oui command line.

More general usage can be provided with the <unsort> command. This text will not be rearranged in the final output, so it is possible to provide paragraphs of information.

Oui always ignores blank lines and lines with only whitespace in the input, since whitespace often helps to make the input file readable. This can present a problem if you want a blank line to appear in the output. This is most likely only in the <unsort> text, but might arise elsewhere also. The <blank> command will introduce a blank line into the output of the appropriate section.

Finally, comments may be introduced to the input file by way of the <comment> command. All lines following this command up to the next keyword are treated as comments and ignored.

6.0: Handling Default Initializations

Some packages require more information during initialization than arrives with the command line. msg_init_options() , for example, requires a default message header which may be overridden by a command-line option. Often the package creator can predict a reasonable default behaviour, but a particular application may wish to provide a different default behaviour. For msg , a default header of "hdr" is OK in the general case, but collection programs should use a default of "col", soldrvs should use "sol" and so on.

I have come up with a mechanism to provide this sort of flexibility. It works like this:

1) The package in question defines a local variable to hold the initialization parameter. In our example, the package is msghdr and the initialization parameter is the message header:

        <package> msghdr
        ...
        <var>
          char *msg_hdr;
2) A second package is defined, usually in the same file as the first package, which provides the default initialization:
        <package> msghdr_init
        <init>
          msg_hdr = "hdr";
3) Note that the parameter initialization must preceed the use of that parameter in the final output. Hence a statement
        <follow> msghdr_init
must be placed in the package which uses the parameter.

4) An application (or another library package) wishing to override the initialization simply redefines the msghdr_init package, providing a different initialization. The first package by that name which is read by oui is the only one which is used, so controlling the order in which the packages is read controls which initialization takes effect.

This approach has proven effective in handling a wide range of message header defaults. The hooks have not been fully realized for other packages requiring additional parameters, such as the command control package, but I believe the same approach can be made to work there.

7.0: See Also

For more information, refer to the getopt() function in the "Watcom C Library Reference". There is also a compatible getopts function available in the shell (/bin/sh), which is documented under "sh" in the "QNX Utilities Reference-N to Z"


Return to Manuals Guide


last updated: Wed Nov 21 15:21:10 2007 webmaster@huarp.harvard.edu
Copyright 2007 by the President and Fellows of Harvard College