MKCMD(5L)

NAME
mkcmd - command line option packages for common facilities

SYNOPSIS
std_control.m std_help.m std_macro.m std_noargs.m std_targets.m std_version.m cmd.m cmd_cd.m cmd_echo.m cmd_exec.m cmd_help.m cmd_macro.m cmd_merge.m cmd_parse.m cmd_shell.m cmd_source.m cmd_umask.m cmd_version.m

DESCRIPTION

Mkcmd builds a C application's command line option parser from descriptions of the acceptable options. To provide some common user support features (like on-line help) in a consistent manner, mkcmd supplies some prefabricated option packages. This manual page describes the option packages supplied with mkcmd 7.x. It also provides some additional clues for constructing your own packages.

A package provides some options and control points which provide an independent service to the user. Packages may provide C functions (enclosed in %c...%%), header information (enclosed in %h...%%), includes and externs (enclosed in %i...%%), or just tune parameters in mkcmd to produce custom parsers.

C functions made available to the implementor may be called as described below -- any other use of these functions may break in subsequent releases.

Services provided may not be directly related to the function of the application. For example, std_help.m (standard help dot-m) provides the command line option `-h' (the online help facility) and some error traps. These are not really part of the function of an application, but they are handy to provide.

Other services provided are meant to take the place of common command line processing. I invite any implementor to submit new standard option descriptions to be included in the next release of mkcmd.

OPTION TEMPLATES
The distributed packages contain the following options:
std_help.m | -h | print this help message

    Provides handling of missing parameters, and unknown options. No implementor support is required. Every user-level shell utility should include std_help.m in the construction of its command line option parser, unless -h is used for something else (and cannot be renamed).

std_macro.m | -o macro[=value] | turn macro on, or assign it a value
 | +o [macro] | reset macro, or list all macros

    Support a simple list of macro names and text values. The -o option sets macro to the value provided. If no value is given the third column in the aMC table (below) is consulted. The +o escape binds macro to the fourth column in the aMC table. void DoMacro(char *)

    The guts of the -o facility. Not a good entry point to call yourself. void UndoMacro(char *)

    The guts of the +o facility. void ListMacros(FILE *)

    This C function outputs each macro with its current value -- +o with no macro after it activates the facility. #define ENDMACRO This C preprocessor macro must be the last line in the definition of aMC. char *Macro(char *) This function returns the value bound to a macro or the unique character array u_acFail if the macro is not in the aMC table. The implementor must provide (visible from main):
    	%c
    	MACRO aMC[] = {
    		/* name    no -o,+o      -o              +o */
    		{"macro", "init-value", "set-value", "reset-value"},
    		...
    		ENDMACRO	/* must always be last in table */
    	};
    	%%

    In practice these options are used to implement less often used switches in the guts of a very complex application. For example ksh's ``set -o vi'' feature. The macro's value is read by the application with `pcValue = Macro("name");'.
std_noargs.m | list

    When additional words are left on the command line this facility issues a usage message and aborts. Consumes the list control point.
std_version.m | -V | show version information

    Provides `-V' to output the version of the application. The implementor must provide a rcsid visible from main. For example:
    	%c
    	static char rcsid[] =
    		"$Id: ...$";
    	%%
    The applications writer may augment the -V action's user attribute to display more text on stdout.
std_control.m | -n | do not execute commands, trace only
 | -v | be verbose

    The implementor uses these to control the application of system calls which alter the system. The names of the control variables are fExec and fVerbose. If fExec is non-zero then the application should take the action. If fVerbose is non-zero that the application should output (on stdout) shell commands which closely approximate the actions the application is taking. Note that -n on the command line always forces -v. For example:
    	if (fVerbose) {
    		printf("%s: rm -f %s\n", progname, pcFile);
    	}
    	if (fExec && -1 == unlink(pcFile)) {
    		fprintf(stderr, "%s: unlink: %s: %s\n", progname, pcFile, ...
    		exit(1);
    	}
std_targets.m | none

    Provide no options. No implementor support required. This template inserts header comment with marked lines for the mk(1L) program.

INTERACTIVE TEMPLATES

Mkcmd has some support for interactive products like lpc which have micro-shell interpreters to control advanced features. The interpreter uses the same rules used to split options from an environment variables to break a command into an argument vector. The interpreter searches a table of commands for the first word of the subsequently formed command. A function pointer and a mask of special bits complete the execution of the command.

The application must provide 2 definitions and a call to cmd_init to use the interpreter. The first definition is a list of the commands to be included in the interpreter. Each command is described as a C structure:
    typedef | struct CPnode {
     | char *pccmd; | /* command name, "quit" */
     | char *pcdescr; | /* command help text, "terminate this" */
     | int (*pfi)(); | /* function to call */
     | int facts; | /* action to take after function call */
     | char *pcparams; | /* an optional usage message for params */
    } CMD;
The list of commands is an array of these structures. For example:
    CMD | CMList[] = {
     | {"page", "display a file", page_stuff, 0, "[files]"},
     | {"move", "move files", relocate, 0, "files to"},
     | CMD_DEF_QUIT,
     | ....
    };

Some of these lines are provided as C preprocessor macros in the
templates below.  They are all named like CMD_DEF_name.

The other definition you must have is a variable of type `CMDSET'.
Any name will do.  It must be in an appropriate scope, of course.
Something like:

    CMDSET CSUser;

We are going to pass the address of this structure (as the first parameter) to all the routines that implement the interpreter. To complete the activation of the generic interpreter we have to call cmd_init before we try to process any commands (see below).

These parameters tune the behavior of the interpreter as a whole. Each command in the interpreter has a set of bits that control just that command. In the CMD definition these facts bits may be set (and or'd together):
    CMD_NULL | take no special action
    CMD_RET | return from the parser after this command
    CMD_OFF | this command is off (maybe privileged)
    CMD_HIDDEN | this command not in the `commands' or `help' list
    CMD_NO_FREE | do not free argument vector
     | (because command records a pointer into it)

Commands are interfaced to the interpreter as C functions that take a standard argc, argv pair and a pointer to a command interpreter data structure.
    int
    myfunction(argc, argv, pCS)
    int argc;
    char **argv;
    CMDSET *pCS;
    {
    	...
    }

The following text describes the facilities provided by each cmd_thing.m source file in the mkcmd standard library. Each templates below implements either part of the interpreter infrastructure or some standard functions.
cmd.m | quit | exit this command interpreter

    The C preprocessor macro CMD_DEF_QUIT provides an initializer for a CMD that implements a ``quit'' command.

    This is the main code for the interpreter. Include this file in the mkcmd command line before the file which contains the list of commands and the command set declaration. cmd_init(CMDSET *, CMD *, unsigned int, char *, int);

    The parameters to cmd_init are (in order):
        the address of a CMDSET structure
        the address of the first element of the list of commands
        the number of commands in that list
        a string prompt, or the NULL pointer (viz. (char *))
        the minimum number of character to accept for a shortened command name, or zero
    For example we might setup the new interperter ``CSUser'' with the list of commands in the array aCMList and a generic prompt, and a minimum command length of 3 characters as:
        cmd_init(& CSUser, aCMList, sizeof(CMList)/sizeof(CMD), "prompt> ", 3);

    Once this init routine has been called two other facilities are available to process commands. They are usually bound the -c and -f (see below). int cmd_from_file(CMDSET *, FILE *)

    This function reads and interprets commands from the given (FILE *). It might be used at the zero control to process commands from stdin, e.g.:
        zero {
            update ``cmd_from_file(& CSUser, stdin)''
        }
    int cmd_from_string(CMDSET *, char *, int *)

    This function interprets a single command from a string. If the third argument to cmd_from_string is the address of an int buffer a copy of the facts member from the active CMD entry for the executed command is stored here. These bits are used internally, most implementors will have little use for them: just pass `(int *)0' as the third argument to cmd_from_string. int (*cmd_unknown)();
    int (*cmd_ambiguous)();
    int (*cmd_not_available)();

    These function pointers are hooks to catch bad commands. For example cmd_unknown is used by the cmd_macro facility to trap unknown commands which might be macro names. If your facility cannot process or recover it should chain to the older function (so record the old value before you stomp on it). All of these functions take the standard 3 parameters (argc, argc, pCS). int _cmd_unknown(int, char **, CMDSET *);
    int _cmd_ambiguous(int, char **, CMDSET *);
    int _cmd_not_available(int, char **, CMDSET *);

    These are the default functions for the hooks above.
cmd_cd.m | cd | change directory
 | chdir | change directory

    This command emulates sh's builtin cd command to change the process's current working directory. The C preprocessor macro CMD_DEF_CD provides an initializer for the ``cd'' command. The C preprocessor macro CMD_DEF_CHDIR provides an initializer for a ``chdir'' alias. int cmd_cd(int, char **, CMDSET *) This routine knows about the tilde (``~'') expansion, but not file globbing. With no arguments, change to $HOME.
cmd_echo.m | echo | print text to stdout

    This is included mostly as an example of a simple command. The C preprocessor macro CMD_DEF_ECHO provides an initializer for the ``echo'' command. int cmd_echo(int, char **, CMDSET *) Any arguments are output on a single line to stdout.
cmd_exec.m | pwd | runs /bin/pwd

    This package uses the name of the command in an extended mode to run a command from the file sysem. After the name of the command put a literal NUL (\000) character then the path to the binary file. The macro CMD_DEF_PWD provides an initializer for the ``pwd'' command. int cmd_fs_exec(int, char **, CMDSET *)

    This C function looks beyond the end of the argv[0] string for a path to execute. Put the path in the CMD initializer as:
        { ``pwd\0/bin/pwd'', ``print working directory'', cmd_fs_exec, 0}
cmd_help.m | commands | output a terse list of commands
 | help | output a help message

    The C preprocessor macro CMD_DEF_COMMANDS provides an initializer for a CMD that implements a command that displays the names of all of the available commands. Some support code is also included.

    The C preprocessor macro CMD_DEF_HELP provides an initializer for a CMD that implements a command that displays the descriptions for all the available commands.
cmd_macro.m | macro=value | set a macro
 | -macro | activate macro
 | +macro | deactivate macro
 | ?macro | output macro value
 | ? | output all macro values

    This provides a command interface to the std_macro macros. Each macro in the aMC array becomes 4 commands (set, activate, deactivate, and print).

    The routine cmd_macro_install() was called to install this facility in older mkcmd libraries: that is no longer the case. Remove any references to this function.
cmd_merge.m

    This interface provides no commands unto itself. It provides a C routine (viz. cmd_merge) which adds new commands to an existing interpreter. This is used in conjunction with cmd_parse.m and cmd_source.m to add commands form a table at run-time. int cmd_merge(CMDSET *, CMD *, unsigned int, int (*)())

    Cmd_merge takes four parameters: the address of a CMDSET structure, a pointer to the list of CMD structures, a count of the number of CMDs provided and a function pointer to compare two CMD structures for order. A NULL function pointer (viz. (int (*)())0) may be supplied as the last parameter to activate an internal function which sorts as strcmp does.
cmd_parse.m

    This interface provides no commands unto itself. It provides a C routine (viz. cmd_parse) which reads command descriptions from a (FILE *). Commands are stored in a text table with lines that look like:
        path argv0 params -- helptext

    The params are optional as is argv0. If no argv0 is provided the tail of path is assumed.

    Two optional flags may prefix the path: exclamation point (!) declares that the new command is CMD_OFF by default, and commercial at (@) declares that the command is hidden. int cmd_parse(FILE *, CMD **, unsigned int *)

    The usage of this routine is more clear in an example (see below), basically we fopen(3) a file, pass this file handle and the address of two buffers (a CMD *, and an unsigned int), close the file. Now the (CMD *) should point to a malloc(3) area which holds all the data about the requested commands.

    Each command is setup for execution via cmd_fs_exec which must be included as cmd_exec.m on the mkcmd command line.
cmd_shell.m | shell | spawn a subshell

    The C preprocessor macro CMD_DEF_SHELL provides an initializer for a CMD that spawns a copy of the user's shell, or /bin/sh if $SHELL is not set. int cmd_shell(int, char **, CMDSET *)

    If no arguments are provided a ``-i'' is forced into the shell's command line.
cmd_source.m | source | interpret files of commands

    The C preprocessor macro CMD_DEF_SOURCE provides an initializer for a CMD that read commands from a list of files. int cmd_source(int, char **, CMDSET *) Each file provided on the command line is opened in turn and read as commands to the interpreter.
cmd_umask.m | umask | print/set file creation mask

    The C preprocessor macro CMD_DEF_UMASK provides an initializer for a CMD that prints (sets) the process's file creation mask. int cmd_umask(int, char **, CMDSET *)

    This C routine implements the umask command. In addition the POSIX -S option is emulated if NEED_SYMBOLIC_UMASK is defined to be non-zero (which is the default).
cmd_version.m | version | output a version string

    The C preprocessor macro CMD_DEF_VERSION provides an initializer for a CMD that outputs the rcsid as a version string. int cmd_version(int, char **, CMDSET *)

    This C routine outputs rcsid the version of the program. The implementor must provide this character array.

EXAMPLES

Mkcmd has a simple syntax and a declarative nature -- none of the examples below are mind bogglingly ``clever''.

To allow exactly two positional parameters (a file and an integer):

    integer variable "iSize" {
    	parameter "size"
    	help "maximum size to grow file"
    }
    fd ["O_CREAT|O_RDWR, 644"] variable "fdOutFile" "pcOutFile" {
    	parameter "file"
    	help "file to append message"
    }
    left "fdOutFile" "iSize" {
    }
To make -v and -s be opposites (verbose and silent):
    boolean 'v' {
    	named "fVerbose"
    	init "0"
    	help "be verbose"
    }
    action 's' {
    	update "%rvn = 0;"
    	help "be silent (turn off verbose)"
    }
To augment the standard version option to output additional stuff (note that the percent and the backslash must be quoted):
    augment action 'V' {
    	user 'printf("%%s: additional stuff\\n", %b);'
    }

To allow access to a cmd interpreter from the command line:
    function 'c' {
    	track named "cmd_from_string"
    	update '%n(& CSGlobal, %N, (int *)0);'
    	parameter "cmd"
    	help "provide an interactive command"
    }
If the user should be able to source a file with -f:
    file 'f' {
    	track named "fpSource" "pcSource"
    	user 'cmd_from_file(& CSGlobal, %n);'
    	help "interpret commands from file"
    }

If no -c or -f options are given we might use zero to drop into an interactive mode:
    zero {
    	named "cmd_from_file"
    	update 'if (!%rcU && !%rfU) {%n(& CSGlobal, stdin);}'
    }

To read file system commands from a map file (see cmd_parse.m, cmd_exec.m and cmd_merge.m):

    auto unsigned int ui;
    auto CMD *pCMMerge;
    ...
    if ((FILE *)0 != (fpCmds = fopen(acAddCmds, "r"))) {
    	if (0 == cmd_parse(fpCmds, & pCMMerge, & uiMerge) && 0 != uiMerge) {
    		cmd_merge(pCS, pCMMerge, uiMerge, (int (*)())0);
    	}
    	(void)fclose(fpCmds);
    }

BUGS

Some more attention should be given to the C code supplied.

The internal percent-based code generator is too cryptic for most programmers to use effectively.

FILES
/usr/local/lib/mkcmd | the default directory for template files

AUTHOR
Kevin S Braunsdorf
Federal Express
ksbrauns@fedex.com

SEE ALSO
sh(1), execve(2), mk(1L), umask(2), ch(2)