Command Interpreter API reference

The command interpreter engine API itself is language neutral, and is usable by any language that can support the "System" calling convention and pointers. C++ language bindings to the API are provided in a header file CMDAPI.H. The import library for linking to the API DLLs is CMDAPI.LIB.

API function index

Instances of the interpreter

Many functions in the API require a pointer to an "instance" object. This object represents one "instance" of the interpreter, with its own flags and settings. It has an opaque type.

An application must have created an instance of the interpreter before calling most API functions. To create an instance of the interpreter call CMDInitialise(). The resources used by that instance of the interpreter must be properly released by calling CMDTerminate().

Two of the uses to which multiple instances of the interpreter may be put are executing multiple interpreters in parallel in multiple threads or having multiple separate instances of the interpreter in separate windows. (In both cases, it is up to the user or the application to properly synchronize any commands that manipulate process-wide settings such as the process' environment or working directory.)

An instance of the interpreter has an error level, an exit flag, an echo flag, an ANSI prompt banner flag, and an interactive flag.

The basic structure of a front end

A front end usually crates an instance of the command interpreter, and sets or clears the instance's interactive flag when parsing its own command line, by calling CMDSetInteractiveFlag(). In the standard front ends, this flag is set or cleared in response to the /K and /C options respectively.

After parsing its arguments, a front end queries the interactive flag using CMDQueryInteractiveFlag(), and if the flag is set, enters its main interactive loop where it prompts for user input and executes any command lines that are entered.

Note: The main loop need not necessarily be coded as a real loop. Conceptually, it is a loop, however.

The main loop executes whilst the exit flag is false. The exit flag may be queried by CMDQueryExitFlag(). The exit flag is set either by the EXIT command, if it has been enabled as an internal command, or by the front end itself calling CMDSetExitFlag() in response to a front-end specific mechanism for exiting, such as a close button on a PM window, being triggered.

The interpreter polls the exit flag in between successive commands and when reading command scripts, and if it is set returns all the way back through all of the nested command scripts to the calling application.

The first thing that the main loop does after checking the exit flag is to display the prompt. The front end queries the echo flag with CMDQueryEchoFlag(), and if it is set displays the prompt with CMDShowPrompt(). The initial state of the echo flag is not defined, and may vary from release to release of the API. In the standard front ends the echo flag is initially set, explicitly, by calling CMDSetEchoFlag() and is cleared in response to the /Q option when the front end parses its arguments.

The interpreter also uses the echo flag when executing command scripts to determine whether to echo the command lines to standard output. The state of the echo flag may be modified or queried by the ECHO command if it has been enabled as an internal command.

CMDShowPrompt() queries the current value of the PROMPT environment variable. and displays the prompt accordingly. If the application enables the pre-supplied PROMPT command or SET command as internal commands, the user can use them to change the PROMPT environment variable. To change whether or not the $i sequence in the prompt string prints an ANSI banner message, call CMDSetANSIPromptBannerFlag() .

The front end then retrieves the command line in some unspecified manner. It executes this command line by calling CMDProcessCommandLine(), and then loops around to the top of the main loop once more.

When a front end terminates, it queries the error level of the instance of the interpreter with CMDQueryErrorLevel() before destroying it, and uses that value for its own exit status.

Messages

To print the volume label and volume serial number of a drive, call the CMDPrintLabel() function. The volume label and serial number are printed using the message text from the OS/2 system message file, and will thus be in the appropriate language.

This function is used by the VOL command, the FREE command, and the DIR command.

Applications may also print arbitrary messages from the system message file or the command interpreter's own message file (CMD.MSG) to either standard error or standard output. Each function has several variants, allowing for differing numbers of parameters to be substituted (which varies of course according to the message being printed).

Aliases

An application that intends to be compatible with the 16-bit CMD supplied with IBM OS/2 and present the "standard" set of commands to the user must pre-define the "ECHO." alias at startup, before it parses its command line (if it supports executing commands from its command line). To do this, call the CMDPredefineStandardAliases() function.

The alias list can also be modified directly. To query an alias, call CMDGetAlias(). To create or to modify an individual alias, call CMDSetAlias(). To remove an alias call CMDUnsetAlias(). To clear the entire alias list call CMDClearAlias().

Displaying and setting the version string

The version string is printed by calling CMDPrintVersionString(). The name and version of the command interpreter engine are determined by the engine itself. The operating system name is "OS/2"; and the operating system major version number, minor version number, and release number are exactly those that are returned by the DosQuerySysInfo() system API function.

Note: The version information obtained from the operating system is not modified in any way. OS/2 Warp 4.0.x reports itself as version 2.40.0, so that is exactly what is printed.

The command interpreter name and version string must be specified beforehand by calling CMDSetNameAndVersionStrings() . A front end registers its name and version strings when it starts up, before it parses its command line.

Manipulating the process' environment

The command interpreter directly uses and modifies the environment area maintained in the Process Information Block (PIB), a pointer to which which may be obtained via the DosQueryInfoBlocks() system API function.

Note: Unfortunately, many (but not all) C/C++ implementations for OS/2 maintain their own private copies of the environment area internally rather than using the environment area pointed to by the PIB. As a result, the setenv() and putenv() functions in such implementations will not affect the PIB environment area, and the getenv() function will only "see" environment variables within the library's private environment table.

It is the C/C++ implementations that are at fault here. This practice means that an application with multiple instances of the C/C++ runtime library linked into it (one in a main EXE and one in a DLL, for example) will have multiple private environment areas, one for each runtime library; and calls to setenv() and getenv() in the different runtime libraries will not be consistent. This design is a legacy of DOS implementations of the same C/C++ runtime libraries, combined with an attempt to mimic a UNIX programming practice (the `environ' variable) that has, ironically, long since been deprecated by the POSIX standard. The correct way of operating on 32-bit OS/2 is to access the environment area pointed to by the PIB, and this is what the command interpreter engine (and indeed other subsystems such as the REXX interpreter engine's VALUE() function) do.

Do not use the getenv(), setenv(), or putenv() functions on such C/C++ implementations.

The environment can be queried with the CMDGetEnv() function. To create or to modify an individual environment variable, call CMDSetEnv(). To remove an environment variable call CMDUnsetEnv(). To clear the entire environment call CMDClearEnv().

Built in commands

An application that intends to be compatible with the 16-bit CMD supplied with IBM OS/2 and present the "standard" set of commands to the user must call CMDPredefineStandardCommands() at startup, before it parses its command line (if it supports executing commands from its command line). This will add the "standard" commands to the internal list of built in commands.

Applications may also provide their own built in commands by calling CMDAddBuiltInCommand(). This associates a command name with a callback function. Whenever the command is invoked, the callback function will be called.

The callback function resembles the `main' function of a C/C++ program. It is passed an argument count and an argument string array. If the argument count is greater than one, the argument string array's second element, element 1, is the command tail that was given to the built-in command. The return value of the function is taken to be the result of the command, as recorded in the error level.

The callback function is not expected to perform any redirection or environment variable expansion of its own. The command interpreter engine will have handled all of that. It is, however, expected to parse the command tail that it is passed into distinct words, handling quotation marks, backslashes, and whitespace, if it requires it. A function for a built in command that expects an argument array in the style passed to programs by UNIX shells must use a "wrapper" function as the callback function, that parses the command tail into individual "words" and then calls the underlying function.

Caveat: Callback functions that use the C/C++ runtime library must be aware that, unlike the `main' function of a standalone C/C++ program, the standard library cleanup routines will not be called when the callback function returns. (The command interpreter engine knows nothing about the C/C++ runtime library used by client applications, and does not even assume that callback functions are written in C/C++.) Callback functions must, if they use C/C++ runtime facilities such as "stdout" or "cout", ensure that those facilities are properly flushed or cleaned up before the function returns. Again, the easiest way to do this is with a "wrapper" function that flushes the library's output buffers before returning the result of the "wrapped" function.

If this is not done, users may experience strange behaviour when the built in command's output is redirected, such as redirected output becoming "stuck" or being sent to the wrong place.

If an application wishes custom built in commands to be available within command pipelines, it must modify the COMSPEC environment variable to point to a suitable executable that can be invoked with the /C option and a command line. If this is not done, the commands on the left hand side of a command pipeline will be passed to an instance of the ordinary command interpreter (which is the usual value of COMSPEC), which will naturally not have any custom built-in commands available.

Miscellaneous functions

CMDSetHandleInheritance() is a simple wrapper around the DosSetFHState() system API call that modifies the inheritiability of an open operating system file handle by setting or resetting the OPEN_FLAGS_NOINHERIT bit. It can be used by applications to prevent the inheritance of any file handles that they do not want to be inherited by any external commands that may be invoked. (Applications can also set the bit directly in calls to DosOpen(), of course.) The command interpreter engine uses this call internally to modify the inheritability of the unnamed pipe handles when it is executing a command pipeline, and when it is performing redirection, so that child processes do not inherit file handles that they should not.

Several commands parse text strings for attribute masks or colour specifications. The CMDParseAttributeRule() and CMDParseColour() functions provide a single uniform mechanism for doing so.

Commands that behave differently depending from whether their argument is a file or a directory should call CMDIsDir().

Commands that need to execute their command tails as command lines in their own right can call CMDProcessSingleCommand().


The 32-bit Command Interpreter is © Copyright Jonathan de Boyne Pollard. "Moral" rights are asserted.