CMD Function Library Reference Manual functions for terminal command interaction October 1997 by Stephen McConnel Copyright (C) 2000 SIL International Published by: Language Software Development SIL International 7500 W. Camp Wisdom Road Dallas, TX 75236 U.S.A. Permission is granted to make and distribute verbatim copies of this file provided the copyright notice and this permission notice are preserved in all copies. The author may be reached at the address above or via email as `steve@acadcomp.sil.org'. Introduction to the CMD function library **************************************** This document describes a library of data structures and functions developed over the years for programs in the Occasional Publications in Academic Computing series published by SIL International. (For SIL International, "academic" refers to linguistics, literacy, anthropology, translation, and related fields.) It is hoped that this documentation will make future maintenance of these programs easier. This particular function library is used by the PC-Kimmo and PC-PATR programs. It is quite possible that no other programs will be developed using this style of user interface, since the whole world has been convinced that hand-eye coordination is the most important talent for using a computer, that is, pointing and clicking with a mouse is universally considered better than typing commands. Variable and function naming conventions **************************************** The basic goal behind choosing names in the CMD function library is for the name to convey information about what it represents. This is achieved in two ways: striving for a descriptive name rather than a short cryptic abbreviated name, and following a different pattern of capitalization for each type of name. Preprocessor macro names ======================== Preprocessor macro names are written entirely in capital letters. If the name requires more than one word for an adequate description, the words are joined together with intervening underscore (`_') characters. Data structure names ==================== Data structure names consist of one or more capitalized words. If the name requires more than one word for an adequate description, the words are joined together without underscores, depending on the capitalization pattern to make them readable as separate words. Variable names ============== Variable names in the CMD function library follow a modified form of the Hungarian naming convention described by Steve McConnell in his book `Code Complete' on pages 202-206. Variable names have three parts: a lowercase type prefix, a descriptive name, and a scope suffix. Type prefix ----------- The type prefix has the following basic possibilities: `b' a Boolean, usually encoded as a `char', `short', or `int' `c' a character, usually a `char' but sometimes a `short' or `int' `d' a double precision floating point number, that is, a `double' `e' an enumeration, encoded as an `enum' or as a `char', `short', or `int' `i' an integer, that is, an `int', `short', `long', or (rarely) `char' `s' a data structure defined by a `struct' statement `sz' a NUL (that is, zero) terminated character string `pf' a pointer to a function In addition, the basic types may be prefixed by these qualifiers: `u' indicates that an integer or a character is unsigned `a' indicates an array of the basic type `p' indicates a pointer to the type, possibly a pointer to an array or to a pointer Descriptive name ---------------- The descriptive name portion of a variable name consists of one or more capitalized words concatenated together. There are no underscores (`_') separating these words from each other, or from the type prefix. For the CMD function library, the descriptive name for global variables begins with a reference to the relevant CMD data structure. Scope suffix ------------ The scope suffix has these possibilities: `_g' indicates a global variable accessible throughout the program `_m' indicates a module (semiglobal) variable accessible throughout the file (declared `static') `_in' indicates a function argument used for input `_out' indicates a function argument used for output (must be a pointer) `_io' indicates a function argument used for both input and output (must be a pointer) `_s' indicates a function variable that retains its value between calls (declared `static') The lack of a scope suffix indicates that a variable is declared within a function and exists on the stack for the duration of the current call. Function names ============== Global function names in the CMD function library have two parts: a verb that is all lowercase followed by a noun phrase containing one or more capitalized words. These pieces are concatanated without any intervening underscores (`_'). For the CMD library functions, the noun phrase section includes a reference to the relevant CMD data structure. Examples ======== Given the discussion above, it is easy to discern at a glance what type of item each of the following names refers to. `SAMPLE_NAME' is a preprocessor macro. `SampleName' is a data structure. `pSampleName' is a local pointer variable. `writeSampleName' is a function (that may apply to a data structure named `SampleName'). CMD function library data structures ************************************ This chapter describes the data structure defined for the CMD function library. The CMD function library also makes heavy use of the `NumberedMessage' data structure from the OPAC function library. See section `NumberedMessage' in `OPAC Function Library Reference Manual'. CmdKeyword ========== Definition ---------- #include "cmd.h" typedef struct { char * pszKeyword; int iValue; int iFlags; } CmdKeyword; Description ----------- `CmdKeyword' is a data structure for building command keyword tables (arrays) for programs that use an interactive user interface based on typing commands. The fields of the `CmdKeyword' data structure are as follows: `pszKeyword' points to a `NUL'-terminated keyword string. `iValue' is the value associated with the keyword (a positive integer). `iFlags' contains special bit flags associated with the keyword: `iFlags & CMD_INVISIBLE' the keyword is not shown in help messages `iFlags & CMD_DISABLED' the keyword is currently disabled Source File ----------- `cmd.h' The CMD function library global variables ***************************************** This chapter gives the proper usage information about each of the global variables found in the CMD function library. For each global variable that the library provides, this information includes which CMD library functions use it. bCmdShowWarnings_g ================== Syntax ------ #include "cmd.h" extern int bCmdShowWarnings_g; Description ----------- The CMD function library uses `displayNumberedMessage' (see section `NumberedMessage' in `OPAC Function Library Reference Manual') to display error and warning messages. `bCmdShowWarnings_g' determines what to do with messages of type `WARNING_MSG'. If `bCmdShowWarnings_g' is `FALSE' (zero), then messages are totally ignored unless they are of type `ERROR_MSG'. This includes both screen display and writing the message to a log file. The default value of `bCmdShowWarnings_g' is `TRUE' (`1'). Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdmessg.c' Used By ------- `doCmdChdir' `doCmdClose' `doCmdEdit' `doCmdLog' `doCmdTake' bCmdSilentMessages_g ==================== Syntax ------ #include "cmd.h" extern int bCmdSilentMessages_g; Description ----------- The CMD function library uses `displayNumberedMessage' (see section `displayNumberedMessage' in `OPAC Function Library Reference Manual') to display error and warning messages. If `bCmdSilentMessages_g' is `TRUE' (nonzero), then messages are not displayed on the screen; they may, however, still be written to a log file. If `bCmdSilentMessages_g' is `FALSE' (zero), then messages are displayed on the screen, and may also be written to a log file. The default value of `bCmdSilentMessages_g' is `FALSE' (`0'). Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdmessg.c' Used By ------- `doCmdChdir' `doCmdClose' `doCmdEdit' `doCmdLog' `doCmdTake' pCmdLogFP_g =========== Syntax ------ #include "cmd.h" extern FILE * pCmdLogFP_g; Description ----------- The CMD function library uses `displayNumberedMessage' (see section `displayNumberedMessage' in `OPAC Function Library Reference Manual') to display error and warning messages. `pCmdLogFP_g' points to an optional output log file used to record the messages displayed by `displayNumberedMessage'. The application program can use it to record other information as well. The default value of `pCmdLogFP_g' is `NULL' (no log file). Example ------- See below for section `sCmdClosingLog_g' (Example), section `sCmdNoLogFile_g' (Example), and chapter 5 (`Sample Program'). Source File ----------- `cmddata.c' Used By ------- `doCmdTake' `doCmdChdir' `doCmdEdit' `doCmdLog' `doCmdClose' pCmdOutputFP_g ============== Syntax ------ #include "cmd.h" extern FILE * pCmdOutputFP_g; Description ----------- `pCmdOutputFP_g' is used by `handleCmdSigint', as a means to safely close an output file when processing is interrupted by the user. It may also be used by the application program to refer to the output file. The default value of `pCmdOutputFP_g' is `NULL' (no output file to close). Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmddata.c' Used By ------- `handleCmdSigint' pszCmdLogFile_g =============== Syntax ------ #include "cmd.h" extern char * pszCmdLogFile_g; Description ----------- `pszCmdLogFile_g' records the name of the output log file opened for `pCmdLogFP_g'. The default value for `pszCmdLogFile_g' is `NULL' (no log file). Example ------- See below for section `sCmdClosingLog_g' (Example), section `sCmdNoLogFile_g' (Example), and chapter 5 (`Sample Program'). Source File ----------- `cmddata.c' Used By ------- `doCmdClose' `doCmdLog' pszCmdProgramName_g =================== Syntax ------ #include "cmd.h" extern char * pszCmdProgramName_g; Description ----------- `pszCmdProgramName_g' points to a form of the application program's name that is suitable for serving as the basis of a filename. The default value of `pszCmdProgramName_g' is `NULL' (no program name). Example ------- See below for section `sCmdClosingLog_g' (Example) and chapter 5 (`Sample Program'). Source File ----------- `cmddata.c' Used By ------- `doCmdLog' `doCmdSystem' `doCmdTake' sCmdAmbiguousKeyword_g ====================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdAmbiguousKeyword_g; Description ----------- `sCmdAmbiguousKeyword_g' is defined as follows: NumberedMessage sCmdAmbiguousKeyword_g = { ERROR_MSG, 105, "Ambiguous keyword in %s command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdAmbiguous_g =============== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdAmbiguous_g; Description ----------- `sCmdAmbiguous_g' is defined as follows: NumberedMessage sCmdAmbiguous_g = { ERROR_MSG, 101, "Ambiguous command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdBadArgument_g ================= Syntax ------ #include "cmd.h" extern NumberedMessage sCmdBadArgument_g; Description ----------- `sCmdBadArgument_g' is defined as follows: NumberedMessage sCmdBadArgument_g = { ERROR_MSG, 107, "Invalid argument in %s command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdBadInputFile_g ================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdBadInputFile_g; Description ----------- `sCmdBadInputFile_g' is defined as follows: NumberedMessage sCmdBadInputFile_g = { ERROR_MSG, 109, "Cannot open input file %s in %s command" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- `doCmdTake' sCmdBadKeyword_g ================ Syntax ------ #include "cmd.h" extern NumberedMessage sCmdBadKeyword_g; Description ----------- `sCmdBadKeyword_g' is defined as follows: NumberedMessage sCmdBadKeyword_g = { ERROR_MSG, 106, "Invalid keyword in %s command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdBadOutputFile_g =================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdBadOutputFile_g; Description ----------- `sCmdBadOutputFile_g' is defined as follows: NumberedMessage sCmdBadOutputFile_g = { ERROR_MSG, 110, "Cannot open output file %s in %s command" }; Example ------- See below for section `sCmdClosingLog_g' (Example) and chapter 5 (`Sample Program'). Source File ----------- `messages.c' Used By ------- `doCmdLog' sCmdClosingLog_g ================ Syntax ------ #include "cmd.h" extern NumberedMessage sCmdClosingLog_g; Description ----------- `sCmdClosingLog_g' is defined as follows: NumberedMessage sCmdClosingLog_g = { WARNING_MSG, 114, "Closing the existing log file %s" }; This example is the actual library function source code (at least when this documentation was written). Example ------- #include #include "allocmem.h" #include "cmd.h" ... void doCmdLog(pszFilename_in) const char * pszFilename_in; { char buffer[80]; char * pszFilename; sprintf(buffer, "LOG [] (default is %s.log)", pszCmdProgramName_g ? pszCmdProgramName_g : "log"); if (wantCmdHelp(pszFilename_in, buffer)) return; if (pCmdLogFP_g != NULL) fclose(pCmdLogFP_g); if (pszCmdLogFile_g != NULL) { displayNumberedMessage(&sCmdClosingLog_g, NULL, NULL, pszCmdLogFile_g); freeMemory(pszCmdLogFile_g); pszCmdLogFile_g = NULL; } sprintf(buffer, "%s.log", pszCmdProgramName_g ? pszCmdProgramName_g : "log"); pszFilename = setCmdFilename(pszFilename_in, buffer, ".log"); pCmdLogFP_g = fopen(pszFilename,"w"); if (pCmdLogFP_g == NULL) { displayNumberedMessage(&sCmdBadOutputFile_g, NULL, NULL, pszFilename, "LOG"); freeMemory(pszFilename); } else pszCmdLogFile_g = pszFilename; } Source File ----------- `messages.c' Used By ------- `doCmdLog' sCmdErrorInTake_g ================= Syntax ------ #include "cmd.h" extern NumberedMessage sCmdErrorInTake_g; Description ----------- `sCmdErrorInTake_g' is defined as follows: NumberedMessage sCmdErrorInTake_g = { ERROR_MSG, 112, "TAKE file aborted due to invalid command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdInvalidDirectory_g ====================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdInvalidDirectory_g; Description ----------- `sCmdInvalidDirectory_g' is defined as follows: NumberedMessage sCmdInvalidDirectory_g = { ERROR_MSG, 117, "Bad pathname for CD command" }; Example ------- This example is simpler than the actual library function source code. #include #include "cmd.h" ... void doCmdChdir(pszPathname_in) const char * pszPathname_in; { char szPathname[1026]; char * pszHomeDir; const char * pszDirectory; if ((pszPathname_in == NULL) || (*pszPathname_in == NUL)) { /* default to home directory */ pszHomeDir = getenv("HOME"); if (pszHomeDir != NULL) pszDirectory = pszHomeDir; else { displayNumberedMessage(&sCmdMissingDirectory_g, NULL, NULL); return; } } else pszDirectory = pszPathname_in; if (wantCmdHelp(pszPathname_in, "CD (no default)\n")) return; if (getcwd(szPathname, 1026) == NULL) { perror("CD"); return; } if (chdir(pszDirectory) != 0) { chdir(szPathname); displayNumberedMessage(&sCmdInvalidDirectory_g, NULL, pszPathname_in); return; } if (getcwd(szPathname, 1026) != NULL) fprintf(stderr, "%s\n", szPathname); else perror("CD"); } Source File ----------- `messages.c' Used By ------- `doCmdChdir' sCmdInvalid_g ============= Syntax ------ #include "cmd.h" extern NumberedMessage sCmdInvalid_g; Description ----------- `sCmdInvalid_g' is defined as follows: NumberedMessage sCmdInvalid_g = { ERROR_MSG, 102, "Invalid command: %s" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdJmpBuf_g ============ Syntax ------ #include #include "cmd.h" extern jmp_buf sCmdJmpBuf_g; Description ----------- `sCmdJmpBuf_g' is used by `handleCmdSigint' to jump out of an undesired situation back to the beginning of the command loop. It must be filled by `setjmp' before calling `signal' with `handleCmdSigint'. Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdsig.c' Used By ------- `handleCmdSigint' sCmdMissingArgument_g ===================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingArgument_g; Description ----------- `sCmdMissingArgument_g' is defined as follows: NumberedMessage sCmdMissingArgument_g = { ERROR_MSG, 104, "Missing argument in %s command" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdMissingDirectory_g ====================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingDirectory_g; Description ----------- `sCmdMissingDirectory_g' is defined as follows: NumberedMessage sCmdMissingDirectory_g = { ERROR_MSG, 116, "Missing pathname for CD command" }; Example ------- *Note Example: sCmdInvalidDirectory_g. Source File ----------- `messages.c' Used By ------- `doCmdChdir' sCmdMissingEditFile_g ===================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingEditFile_g; Description ----------- `sCmdMissingEditFile_g' is defined as follows: NumberedMessage sCmdMissingEditFile_g = { ERROR_MSG, 115, "Missing filename for EDIT command" }; Example ------- This example is simpler than the actual library function source code. #include #include "cmd.h" ... void doCmdEdit(pszFilename_in) const char * pszFilename_in; { char szEditCommand[200]; char * pszEditor; if (pszFilename_in == (char *)NULL) { displayNumberedMessage(&sCmdMissingEditFile_g, NULL, NULL); return; } if (wantCmdHelp(pszFilename_in, "EDIT (no default filename or type)")) return; pszEditor = getenv("EDITOR"); if (pszEditor == NULL) pszEditor = "emacs"; sprintf(szEditCommand, "%s %s", pszEditor, pszFilename_in); doCmdSystem(szEditCommand); } Source File ----------- `messages.c' Used By ------- `doCmdEdit' sCmdMissingInputFile_g ====================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingInputFile_g; Description ----------- `sCmdMissingInputFile_g' is defined as follows: NumberedMessage sCmdMissingInputFile_g = { ERROR_MSG, 108, "Missing input file argument in %s command" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdMissingKeyword_g ==================== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingKeyword_g; Description ----------- `sCmdMissingKeyword_g' is defined as follows: NumberedMessage sCmdMissingKeyword_g = { ERROR_MSG, 103, "Missing keyword in %s command" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdMissingOutputFile_g ======================= Syntax ------ #include "cmd.h" extern NumberedMessage sCmdMissingOutputFile_g; Description ----------- `sCmdMissingOutputFile_g' is defined as follows: NumberedMessage sCmdMissingOutputFile_g = { ERROR_MSG, 118, "Missing output file argument in %s command" }; Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `messages.c' Used By ------- sCmdNoLogFile_g =============== Syntax ------ #include "cmd.h" extern NumberedMessage sCmdNoLogFile_g; Description ----------- `sCmdNoLogFile_g' is defined as follows: NumberedMessage sCmdNoLogFile_g = { ERROR_MSG, 113, "No log file was open" }; Example ------- This example is the actual library function source code (at least when this documentation was written). void doCmdClose() { if ((pCmdLogFP_g == NULL) && (pszCmdLogFile_g == NULL)) { displayNumberedMessage(&sCmdNoLogFile_g, NULL, NULL); return; } if (pCmdLogFP_g != (FILE *)NULL) { fclose(pCmdLogFP_g); pCmdLogFP_g = (FILE *)NULL; } if (pszCmdLogFile_g != (char *)NULL) { freeMemory(pszCmdLogFile_g); pszCmdLogFile_g = (char *)NULL; } } Source File ----------- `messages.c' Used By ------- `doCmdClose' sCmdTooDeepTake_g ================= Syntax ------ #include "cmd.h" extern NumberedMessage sCmdTooDeepTake_g; Description ----------- `sCmdTooDeepTake_g' is defined as follows: NumberedMessage sCmdTooDeepTake_g = { ERROR_MSG, 111, "TAKE files nested too deeply" }; Example ------- This example is the actual library function source code (at least when this documentation was written). #include #include "opaclib.h" #include "cmd.h" ... typedef struct { FILE * pInputFP; char * pszFilename; unsigned uiLineNumber; } CmdTakeFile; #define CMD_TAKE_LEVELS 3 static CmdTakeFile asTake_m[CMD_TAKE_LEVELS]; static int iTakeLevel_m = -1; static char szTakeUsage_m[] = "TAKE [] (default name is %s.tak, default type is .tak)"; ... void doCmdTake(char * pszFilename_in) { char * pszFile; char * p; char szBuffer[128]; if (iTakeLevel_m >= CMD_TAKE_LEVELS-1) { displayNumberedMessage(&sCmdTooDeepTake_g, NULL, NULL); return; } if ((pszCmdProgramName_g != NULL) && (*pszCmdProgramName_g != NUL)) sprintf(szBuffer, szTakeUsage_m, pszCmdProgramName_g); else sprintf(szBuffer, szTakeUsage_m, "cmd"); if (wantCmdHelp(pszFilename_in, szBuffer)) return; if ((pszCmdProgramName_g != NULL) && (*pszCmdProgramName_g != NUL)) sprintf(szBuffer, "%s.tak", pszCmdProgramName_g); else strcpy(szBuffer, "cmd.tak"); pszFile = setCmdFilename(pszFilename_in, szBuffer, ".tak"); if (iTakeLevel_m > -1) { p = buildAdjustedFilename(pszFile, asTake_m[iTakeLevel_m].pszFilename, NULL); freeMemory( pszFile ); pszFile = p; } ++iTakeLevel_m; asTake_m[iTakeLevel_m].pInputFP = fopen(pszFile, "r"); if (asTake_m[iTakeLevel_m].pInputFP == NULL) { displayNumberedMessage(&sCmdBadInputFile_g, NULL, NULL, pszFile, "TAKE"); --iTakeLevel_m; freeMemory(pszFile); } else { asTake_m[iTakeLevel_m].uiLineNumber = 1; asTake_m[iTakeLevel_m].pszFilename = pszFile; } } Source File ----------- `messages.c' Used By ------- `doCmdTake' The CMD functions ***************** This chapter gives the proper usage information about each of the functions found in the CMD function library. The prototypes and type definitions relevent to the use of these functions are all found in the `cmd.h' header file. closeCmdTake ============ Syntax ------ #include "cmd.h" int closeCmdTake(void); Description ----------- `closeCmdTake' closes the current "take" file, if any are open. If there were two or more "take" files open, future calls to `getCmdString' will read from the parent "take" file. Otherwise, this causes `getCmdString' to read from the keyboard. `closeCmdTake' does not have any arguments. Return Value ------------ the number of (nested) "take" files open when `closeCmdTake' finishes Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' doCmdChdir ========== Syntax ------ #include "cmd.h" void doCmdChdir(const char * pszDirectory_in); Description ----------- `doCmdChdir' executes a "change directory" command. If read from a "take" file, it is relative to the location of the take file unless an absolute path is given. `doCmdChdir' has one argument: `pszDirectory_in' points to a pathname for a directory that becomes the current directory. Return Value ------------ none Example ------- See below for section `sCmdInvalidDirectory_g' (Example) and chapter 5 (`Sample Program'). Source File ----------- `cmdcd.c' doCmdClose ========== Syntax ------ #include "cmd.h" void doCmdClose(void); Description ----------- `doCmdClose' closes the currently open log file, clearing the global variables `pCmdLogFP_g' and `pszCmdLogFile_g'. It is designed to perform the operation of an interactive `close' command. `doCmdClose' does not have any arguments. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdlog.c' doCmdDirectory ============== Syntax ------ #include "cmd.h" void doCmdDirectory(const char * pszArgument_in); Description ----------- `doCmdDirectory' lists the contents of a directory on the screen. It is designed to to perform an interactive `directory ' command. `doCmdDirectory' works by running the operating system's standard directory listing program as a subprocess. It does not work on the Macintosh. `doCmdDirectory' has one argument: `pszArgument_in' points to an argument string suitable for the standard directory listing program, or is `NULL'. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmddir.c' doCmdEdit ========= Syntax ------ #include "cmd.h" void doCmdEdit(const char * pszFilename_in); Description ----------- `doCmdEdit' edits a file. It is designed to to perform an interactive `edit ' command. `doCmdEdit' works by running a text editor program as a subprocess. Either the program specified by the `EDITOR' environment variable or a standard editor is used. For MS-DOS, this is `edit'; for Unix, this is `emacs'; for VMS, this is `edt'. `doCmdEdit' does not work on the Macintosh. `doCmdEdit' has one argument: `pszFilename_in' points to the the pathname of the file to edit. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdedit.c' doCmdLog ======== Syntax ------ #include "cmd.h" void doCmdLog(const char * pszFilename_in); Description ----------- `doCmdLog' opens a log file, setting the global variables `pCmdLogFP_g' and `pszCmdLogFile_g' after automatically closing any previously open log file. It is designed to perform the operation of an interactive `log ' command. `doCmdLog' has one argument: `pszFilename_in' points to the pathname of the log file to open. Return Value ------------ none Example ------- See below for section `sCmdClosingLog_g' (Example) and chapter 5 (`Sample Program'). Source File ----------- `cmdlog.c' doCmdSystem =========== Syntax ------ #include "cmd.h" void doCmdSystem(const char * pszCommand_in); Description ----------- `doCmdSystem' executes a system command in a subshell. It is designed to perform the operation of an interactive `system ' command. `doCmdSystem' works by executing the command as a subprocess using the standard shell. For MS-DOS, the standard shell is given by either the `SHELL' or the `COMSPEC' environment variable (or defaults to `command.com'). For Unix, the standard shell is given by the `SHELL' environment variable (or defaults to `/bin/sh'). For VMS, the standard shell is `DCL'. `doCmdSystem' does not work on the Macintosh. `doCmdSystem' has one argument: `pszCommand_in' points to the command string to execute in a subshell, or is `NULL'. If it is `NULL' or an empty string, a subshell is spawned. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdsys.c' doCmdTake ========= Syntax ------ #include "cmd.h" void doCmdTake(const char * pszFilename_in); Description ----------- `doCmdTake' opens a "take" file, unless 3 levels of "take" files are already open. If successful, this causes future calls to `getCmdString' to read from the "take" file. `doCmdTake' has one argument: `pszFilename_in' points to the pathname of the "take" file. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' getCmdString ============ Syntax ------ #include "cmd.h" char * getCmdString(const char * pszCommand_in, const char * pszPrompt_in, int cComment_in); Description ----------- `getCmdString' gets a command from the appropriate place, in this order of priority: 1. the command argument 2. a "take" established by a previous call to `doCmdTake' 3. the keyboard (standard input) "Take" files are closed, and the current "take" level decremented, if the end of the current "take" file is detected. The arguments to `getCmdString' are as follows: `pszCommand_in' points to a command string, or is `NULL' (the usual case). `pszPrompt_in' points to a prompt string. `cComment_in' is the character that starts a comment in a command line, or is `NUL' (`'\0''). Return Value ------------ a pointer to the command string (which may be overwritten by next call to `getCmdString') Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' getCmdTakeFile ============== Syntax ------ #include "cmd.h" char * getCmdTakeFile(void); Description ----------- `getCmdTakeFile' gets the name of the current "take" file, if any are open. `getCmdTakeFile' does not have any arguments. Return Value ------------ the pathname of the current "take" file, or `NULL' if none are open Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' getCmdTakeLevel =============== Syntax ------ #include "cmd.h" int getCmdTakeLevel(void); Description ----------- `getCmdTakeLevel' gets the number of open "take" files. `getCmdTakeLevel' does not have any arguments. Return Value ------------ the number of (nested) "take" files open Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' handleCmdSigint =============== Syntax ------ #include "cmd.h" RETSIGTYPE handleCmdSigint(int iSignal_in); Description ----------- `handleCmdSigint' handles `SIGINT' signals by closing the current output file and jumping to a predefined spot. It is invoked indirectly after being established by signal( SIGINT, handleCmdSigint ); `handleCmdSigint' uses the global variables `pCmdOutputFP_g' and `sCmdJmpBuf_g', setting the former to `NULL' after closing the file. `handleCmdSigint' has one argument: `iSignal_in' is the type of signal (should always be `SIGINT'). Return Value ------------ none (it does not return) Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdsig.c' lookupCmdKeyword ================ Syntax ------ #include "cmd.h" int lookupCmdKeyword( char * pszCommand_in, const CmdKeyword * pKeywordTable_in, int iTableSize_in, char * pszHelp_in); Description ----------- `lookupCmdKeyword' searches the given table of command keywords for the given command. The keyword table must be arranged in ascending alphabetical order, and all letters must be lowercase. A match is successful if the target matches a keyword exactly, or if the target is a prefix of exactly one keyword. It is ambiguous if the target matches two or more keywords from the table unless it matches one exactly. The arguments to `lookupCmdKeyword' are as follows: `pszCommand_in' points to a command keyword (that may be modified by `strlwr'). `pKeywordTable_in' points to an array of `CmdKeyword' data structures. `iTableSize_in' is the size of the array. `pszHelp_in' points to a help message string for this set of commands (if `NULL', no help at all is displayed), displayed following the list of keywords. Return Value ------------ the keyword's associated value (`iValue') if found, or `CMD_NULL' if the input command is empty or `NULL' `CMD_AMBIGUOUS' if the input command is ambiguous with respect to the input keyword table `CMD_INVALID' if the input command is not found in the input keyword table `CMD_HELP' if `?' is found in the input command (the keyword list is displayed automatically) Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmd.c' setCmdFilename ============== Syntax ------ #include "cmd.h" char * setCmdFilename(const char * pszFilename_in, const char * pszDefaultName_in, const char * pszExtension_in); Description ----------- `setCmdFilename' sets a filename using one provided by the user, appending the default extension if the provided filename lacks an extension, or using a default filename. The arguments to `setCmdFilename' are as follows: `pszFilename_in' points to the filename given by the user, or is `NULL'. `pszDefaultName_in' points to the default full filename, or is `NULL'. `pszExtension_in' points to the default filename extension, including the leading period (`.'), or is `NULL'. Return Value ------------ a dynamically allocated filename string, or `NULL' if `pszFilename_in' and `pszDefaultName_in' are both `NULL' Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `setcmdfi.c' showCmdTiming ============= Syntax ------ #include "cmd.h" void showCmdTiming(void ); Description ----------- `showCmdTiming' displays the elapsed time for an operation, using the internal values set by `startCmdTiming' and `stopCmdTiming'. `showCmdTiming' does not have any arguments. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdtime.c' startCmdTiming ============== Syntax ------ #include "cmd.h" void startCmdTiming(void); Description ----------- `startCmdTiming' sets an internal variable to the starting time of an operation. It is useful only in conjunction with `stopCmdTiming' and `showCmdTiming'. `startCmdTiming' does not have any arguments. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdtime.c' stopCmdTiming ============= Syntax ------ #include "cmd.h" void stopCmdTiming(void ); Description ----------- `stopCmdTiming' sets an internal variable to the ending time of an operation. It is useful only in conjunction with `startCmdTiming' and `showCmdTiming'. `stopCmdTiming' does not have any arguments. Return Value ------------ none Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `cmdtime.c' wantCmdHelp =========== Syntax ------ #include "cmd.h" int wantCmdHelp(const char * pszArgument_in, const char * pszHelpMessage_in); Description ----------- `wantCmdHelp' displays the help message if the first argument is equal to a single question mark (`"?"'). The arguments to `wantCmdHelp' are as follows: `pszArgument_in' points to a command argument which may or may not be equal to `"?"', or is `NULL'. `pszHelpMessage_in' points to a message string that provides helpful information about a command. Return Value ------------ `TRUE' (nonzero) if help provided, otherwise `FALSE' (zero) Example ------- See chapter 5 (`Sample Program') below. Source File ----------- `wanthelp.c' Sample Program ************** Rather than try to create small code fragments to illustrate each of the functions and global variables in the CMD function library, a complete (but rather useless) sample program is provided in this chapter. It has been compiled correctly on Unix systems (actually Linux and SunOS). /* SAMPLE.C - sample program for the CMD function library ******************************************************************* * Copyright 1997 by SIL International. */ #include #include #include #include #include #include "cmd.h" #include "opaclib.h" #define KW_CHDIR 1 #define KW_CLOSE 2 #define KW_DIRECTORY 3 #define KW_EDIT 4 #define KW_HELP 5 #define KW_LOAD 6 #define KW_LOG 7 #define KW_PROCESS 8 #define KW_QUIT 9 #define KW_SAVE 10 #define KW_SET 11 #define KW_SHOW 12 #define KW_SYSTEM 13 #define KW_TAKE 14 static CmdKeyword asCommandTable_m[] = { { "cd", KW_CHDIR, CMD_INVISIBLE }, { "chdir", KW_CHDIR, 0 }, { "close", KW_CLOSE, 0 }, { "directory", KW_DIRECTORY, 0 }, { "edit", KW_EDIT, 0 }, { "exit", KW_QUIT, CMD_INVISIBLE }, { "help", KW_HELP, 0 }, { "load", KW_LOAD, 0 }, { "log", KW_LOG, 0 }, { "ls", KW_DIRECTORY, CMD_INVISIBLE }, { "process", KW_PROCESS, 0 }, { "quit", KW_QUIT, 0 }, { "save", KW_SAVE, 0 }, { "set", KW_SET, 0 }, { "show", KW_SHOW, 0 }, { "system", KW_SYSTEM, 0 }, { "take", KW_TAKE, 0 } }; static int iCommandTableSize_m = (sizeof(asCommandTable_m) / sizeof(CmdKeyword)); static char szProcessUsage_m[] = "PROCESS (no default filenames or types)"; #define KW_CODE 11 #define KW_COMMENT 12 #define KW_SILENT 13 #define KW_TIMING 14 #define KW_WARNING 15 static CmdKeyword asSetTable_m[] = { { "code", KW_CODE, 0 }, { "comment", KW_COMMENT, 0 }, { "silent", KW_SILENT, 0 }, { "timing", KW_TIMING, 0 }, { "warnings", KW_WARNING, 0 } }; static int iSetTableSize_m = (sizeof(asSetTable_m) / sizeof(CmdKeyword)); #define KW_OFF 16 #define KW_ON 17 static CmdKeyword asOnOffTable_m[] = { { "false", KW_OFF, CMD_INVISIBLE }, { "off", KW_OFF, 0 }, { "on", KW_ON, 0 }, { "true", KW_ON, CMD_INVISIBLE } }; static int iOnOffTableSize_m = (sizeof(asOnOffTable_m) / sizeof(CmdKeyword)); static int bTiming_m = FALSE; static int cCommentMarker_m = ';'; static unsigned char uiCode_m = 0xFF; static char szWhitespace_m[7] = " \t\r\n\f\v"; static char szPrompt_m[] = "Command>"; /******************************************************************* * NAME * do_help_set * DESCRIPTION * execute a HELP SET command * RETURN VALUE * none */ static void do_help_set(char * pszKeyword_in) { static char szGenericSet_s[] = "Type HELP SET for more help\n"; switch (lookupCmdKeyword(pszKeyword_in, asSetTable_m, iSetTableSize_m, "")) { case CMD_NULL: fputs("\n\ set code sets the byte code value for the PROCESS command\n\ set comment sets the comment characters for TAKE files\n\ set silent enables or disables error/warning messages\n\ set timing enables or disables timing the PROCESS command\n\ set warnings enables or disables warning messages\n\ \n", stderr); fputs(szGenericSet_s, stderr); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "HELP SET", pszKeyword_in); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "HELP SET", pszKeyword_in); break; case CMD_HELP: fputs(szGenericSet_s, stderr); break; case KW_CODE: fputs("\ SET CODE sets the code byte for the PROCESS command to\n\ the indicated value. The default code byte is 0xFF (255).\n", stderr); break; case KW_COMMENT: fputs("\ SET COMMENT sets the comment character to the indicated\n\ value. If is missing (or equal to the current comment\n\ character), then comment handling is disabled. The default\n\ comment character is ';' (the semicolon).\n", stderr); break; case KW_SILENT: fputs("\ SET SILENT ON disables all error and warning messages.\n\ SET SILENT OFF enables all error and warning messages.\n\ The default is OFF.\n", stderr); break; case KW_TIMING: fputs("\ SET TIMING ON turns on timing mode, and SET TIMING OFF turns it\n\ off. If timing mode is ON, then the elapsed time required to\n\ process a command is displayed when the command finishes. If\n\ timing mode is OFF, then the elapsed time is not shown. The\n\ default is OFF.\n", stderr); break; case KW_WARNING: fputs("\ SET WARNING ON turns on warning mode. SET WARNING OFF turns off\n\ warning mode. If warning mode is ON, then warning messages are\n\ displayed on the output. If warning mode is OFF, then no warning\n\ messages are displayed. The default is ON.\n", stderr); break; } } /******************************************************************* * NAME * do_help_show * DESCRIPTION * execute a HELP SHOW command * RETURN VALUE * none */ static void do_help_show(char * pszKeyword_in) { static char szGenericShow_s[] = "Type HELP SHOW for more help\n"; switch (lookupCmdKeyword(pszKeyword_in, asSetTable_m, iSetTableSize_m, "")) { case CMD_NULL: fputs("\n\ show code shows the byte code value for the PROCESS command\n\ show comment shows the comment characters for TAKE files\n\ show silent shows whether error/warning messages are disabled\n\ show timing shows whether timing of PROCESS command is enabled\n\ show warnings shows whether warning messages are enabled\n\ \n\ show shows all of the above values\n\ \n", stderr); fputs(szGenericShow_s, stderr); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "HELP SHOW", pszKeyword_in); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "HELP SHOW", pszKeyword_in); break; case CMD_HELP: fputs(szGenericShow_s, stderr); break; case KW_CODE: fputs("\ SHOW CODE displays the code byte for the PROCESS command.\n\ ", stderr); break; case KW_COMMENT: fputs("\ SHOW COMMENT displays the comment character.\n", stderr); break; case KW_SILENT: fputs("\ SHOW SILENT tells whether error and warning messages are disabled\n\ (not displayed on the screen).\n", stderr); break; case KW_TIMING: fputs("\ SHOW TIMING tells whether the elapsed time required to process a\n\ command is displayed when the command finishes.\n", stderr); break; case KW_WARNING: fputs("\ SHOW WARNING tells whether warning messages are displayed on the\n\ screen.\n", stderr); break; } } /******************************************************************* * NAME * do_help * DESCRIPTION * execute the HELP command * RETURN VALUE * none */ static void do_help(char * pszKeyword_in) { static char szGeneric_s[] = "Type HELP for more help on a particular command\n"; char * pszProg; char * pszFile; if ((pszCmdProgramName_g == NULL) || (*pszCmdProgramName_g == NUL)) { pszProg = "the program"; pszFile = "cmd"; } else { pszProg = pszCmdProgramName_g; pszFile = pszCmdProgramName_g; } switch (lookupCmdKeyword(pszKeyword_in, asCommandTable_m, iCommandTableSize_m, "")) { case CMD_NULL: fputs("\n\ chdir (or cd) changes the current directory\n\ close closes the log file\n\ directory (or ls) lists the files in the current directory\n\ edit edits a file with your favorite editor\n\ help provides nice messages like this one\n\ load ...\n\ log opens a log file\n\ process processes one file to create another\n\ quit (or exit) exits the program\n\ save ...\n\ set sets a program parameter\n\ show shows the settings of one or more parameters\n\ system (or !) executes a command in a subshell\n\ take (or @) reads (\"takes\") commands from a file\n\ \n", stderr); fputs(szGeneric_s, stderr); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "HELP", pszKeyword_in); break; case CMD_INVALID: if (strcmp(pszKeyword_in, "!") == 0) goto help_system; if (strcmp(pszKeyword_in, "@") == 0) goto help_take; displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "HELP", pszKeyword_in); break; case CMD_HELP: /* ? - list of commands displayed */ fputs(szGeneric_s, stderr); break; case KW_CHDIR: fputs("\ CHDIR changes the current directory. You can give a\n\ full path starting with a / (for example,\n\ \"/u/evan/pckimmo/englex/new\"); a path starting with ../ which\n\ indicates the directory above the current one; and so on.\n\ Directories are separated by the / character.\n", stderr); break; case KW_CLOSE: fputs("\ CLOSE closes the log file opened by a LOG command.\n", stderr); break; case KW_EDIT: fputs("\ EDIT attempts to edit a file using the program indicated\n\ by the environment variable EDITOR. If this environment variable\n\ is not defined, then emacs is used to edit the file.\n", stderr); break; case KW_DIRECTORY: fputs("\ DIRECTORY lists the contents of the current directory.\n", stderr); break; case KW_HELP: fprintf(stderr, "\ HELP [] displays a description of a command. If\n\ HELP is typed by itself, %s displays a list of commands with\n\ short descriptions of each command.\n", pszProg); break; case KW_LOAD: fputs("\ LOAD ... (has not been written)\n", stderr); break; case KW_LOG: fprintf(stderr, "\ LOG [] opens a log file. If a filename is given on the\n\ same line as the LOG command, then that file is used for the log\n\ file. Any previously existing file with the same name will be\n\ overwritten. If no filename is provided, then the file %s.log\n\ in the current directory is used for the log file.\n\ \n\ Use CLOSE to stop recording in a log file. If a LOG command is\n\ given when a log file is already open, then the earlier log file\n\ is closed before the new log file is opened.\n", pszFile); break; case KW_PROCESS: fputs("\ PROCESS processes the input file to create the\n\ output file.\n", stderr); break; case KW_QUIT: fputs("\ Either EXIT or QUIT terminates the program.\n", stderr); break; case KW_SAVE: fputs("\ SAVE ... (has not been written)\n", stderr); break; case KW_SET: do_help_set( strtok(NULL, szWhitespace_m) ); break; case KW_SHOW: do_help_show( strtok(NULL, szWhitespace_m) ); break; case KW_SYSTEM: help_system: fprintf(stderr, "\ SYSTEM [] allows the user to execute an operating system\n\ command (such as listing the files in the current directory) from\n\ within %s. If no system-level command is given on the\n\ line with the SYSTEM command, then %s is pushed into the\n\ background and a new system command processor (shell) is started.\n\ Control is usually returned to %s in this case by typing\n\ EXIT as the operating system command.\n\ \n\ ! (exclamation point) is a synonym for SYSTEM.\n", pszProg, pszProg, pszProg); break; case KW_TAKE: help_take: fprintf(stderr, "\ TAKE [] redirects command input to the specified file.\n\ \n\ The default filetype extension for TAKE is \".tak\", and the\n\ default filename is \"%s.tak\" (without the quotation marks,\n\ of course).\n\ \n\ TAKE files can be nested three deep. That is, the user types\n\ TAKE file1, file1 contains the command TAKE file2, and file2 has\n\ the command TAKE file3.\n\ It would be an error for file3 to contain a TAKE command. This\n\ should not prove to be a serious limitation.\n", pszFile); break; } } /******************************************************************* * NAME * do_set * DESCRIPTION * execute the SET command * RETURN VALUE * none */ static void do_set(char * pszKeyword_in) { char * pszArg; char * p; unsigned uiVal; switch (lookupCmdKeyword(pszKeyword_in, asSetTable_m, iSetTableSize_m, "")) { case CMD_NULL: displayNumberedMessage(&sCmdMissingKeyword_g, NULL, NULL, "SET"); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "SET", pszKeyword_in); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "SET", pszKeyword_in); break; case CMD_HELP: /* ? */ fprintf(stderr, "Type HELP SET for more help\n"); break; case KW_CODE: pszArg = strtok(NULL, szWhitespace_m); if ((pszArg == NULL) || (*pszArg == NUL)) displayNumberedMessage(&sCmdMissingArgument_g, NULL, NULL, "SET CODE"); else { uiVal = strtoul(pszArg, &p, 0); if ((*p != NUL) || (uiVal > 255)) { displayNumberedMessage(&sCmdBadArgument_g, NULL, NULL, "SET CODE", pszArg); } else uiCode_m = uiVal; } break; case KW_COMMENT: pszArg = strtok(NULL, szWhitespace_m); if ((pszArg == NULL) || (*pszArg == NUL)) displayNumberedMessage(&sCmdMissingArgument_g, NULL, NULL, "SET COMMENT"); else cCommentMarker_m = *pszArg; break; case KW_SILENT: pszArg = strtok(NULL, szWhitespace_m); switch (lookupCmdKeyword(pszArg, asOnOffTable_m, iOnOffTableSize_m, "")) { case CMD_NULL: displayNumberedMessage(&sCmdMissingKeyword_g, NULL, NULL, "SET SILENT"); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "SET SILENT", pszArg); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "SET SILENT", pszArg); break; case CMD_HELP: break; case KW_ON: bCmdSilentMessages_g = TRUE; break; case KW_OFF: bCmdSilentMessages_g = FALSE; break; } break; case KW_TIMING: pszArg = strtok(NULL, szWhitespace_m); switch (lookupCmdKeyword(pszArg, asOnOffTable_m, iOnOffTableSize_m, "")) { case CMD_NULL: displayNumberedMessage(&sCmdMissingKeyword_g, NULL, NULL, "SET TIMING"); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "SET TIMING", pszArg); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "SET TIMING", pszArg); break; case CMD_HELP: break; case KW_ON: bTiming_m = TRUE; break; case KW_OFF: bTiming_m = FALSE; break; } break; case KW_WARNING: pszArg = strtok(NULL, szWhitespace_m); switch (lookupCmdKeyword(pszArg, asOnOffTable_m, iOnOffTableSize_m, "")) { case CMD_NULL: displayNumberedMessage(&sCmdMissingKeyword_g, NULL, NULL, "SET SILENT"); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "SET SILENT", pszArg); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "SET SILENT", pszArg); break; case CMD_HELP: break; case KW_ON: bCmdShowWarnings_g = TRUE; break; case KW_OFF: bCmdShowWarnings_g = FALSE; break; } break; } } /******************************************************************* * NAME * do_show * DESCRIPTION * execute the SHOW command * RETURN VALUE * none */ static void do_show(char * pszKeyword_in) { switch (lookupCmdKeyword(pszKeyword_in, asSetTable_m, iSetTableSize_m, "")) { case CMD_NULL: fprintf(stderr, "CODE is %x\n", uiCode_m); if (cCommentMarker_m) fprintf(stderr, "COMMENT is %c\n", cCommentMarker_m); else fprintf(stderr, "COMMENT is not set\n"); fprintf(stderr, "SILENT is %s\n", bCmdSilentMessages_g ? "ON" : "OFF"); fprintf(stderr, "TIMING is %s\n", bCmdSilentMessages_g ? "ON" : "OFF"); fprintf(stderr, "WARNING is %s\n", bCmdShowWarnings_g ? "ON" : "OFF"); break; case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguousKeyword_g, NULL, NULL, "SHOW", pszKeyword_in); break; case CMD_INVALID: displayNumberedMessage(&sCmdBadKeyword_g, NULL, NULL, "SHOW", pszKeyword_in); break; case CMD_HELP: fprintf(stderr, "Type HELP SHOW for more help\n"); break; case KW_CODE: fprintf(stderr, "CODE is %x\n", uiCode_m); break; case KW_COMMENT: if (cCommentMarker_m) fprintf(stderr, "COMMENT is %c\n", cCommentMarker_m); else fprintf(stderr, "COMMENT is not set\n"); break; case KW_SILENT: fprintf(stderr, "SILENT is %s\n", bCmdSilentMessages_g ? "ON" : "OFF"); break; case KW_TIMING: fprintf(stderr, "TIMING is %s\n", bCmdSilentMessages_g ? "ON" : "OFF"); break; case KW_WARNING: fprintf(stderr, "WARNING is %s\n", bCmdShowWarnings_g ? "ON" : "OFF"); break; } } /******************************************************************* * NAME * do_process * DESCRIPTION * process the input file to create the output file * RETURN VALUE * none */ static void do_process(char * pszInput_in, char * pszOutput_in) { time_t iTime = time(NULL); int c; FILE * pInputFP; char * pszInputFile; char * pszOutputFile; if (pszInput_in == NULL) { displayNumberedMessage(&sCmdMissingInputFile_g, NULL, NULL, "PROCESS"); return; } if (pszOutput_in == NULL) { displayNumberedMessage(&sCmdMissingOutputFile_g, NULL, NULL, "PROCESS"); return; } if ( wantCmdHelp(pszInput_in, szProcessUsage_m) || wantCmdHelp(pszOutput_in, szProcessUsage_m) ) return; if (getCmdTakeLevel() != 0) { pszInputFile = buildAdjustedFilename(pszInput_in, getCmdTakeFile(), NULL); pszOutputFile = buildAdjustedFilename(pszOutput_in, getCmdTakeFile(), NULL); } else { pszInputFile = duplicateString(pszInput_in); pszOutputFile = duplicateString(pszOutput_in); } pInputFP = fopen( pszInputFile, "rb"); if (pInputFP == NULL) { displayNumberedMessage(&sCmdBadInputFile_g, NULL, NULL, pszInputFile, "PROCESS"); } else { pCmdOutputFP_g = fopen( pszOutputFile, "wb" ); if (pCmdOutputFP_g == NULL) { displayNumberedMessage(&sCmdBadOutputFile_g, NULL, NULL, pszOutputFile, "PROCESS"); } else { if (bTiming_m) startCmdTiming(); if (pCmdLogFP_g != NULL) { fprintf(pCmdLogFP_g, "Processing %s to create %s (log = %s) at %s", pszInput_in, pszOutput_in, pszCmdLogFile_g ? pszCmdLogFile_g : "?", ctime(&iTime)); } while ((c = fgetc(pInputFP)) != EOF) { c ^= uiCode_m; fputc(c, pCmdOutputFP_g); } fclose(pCmdOutputFP_g); pCmdOutputFP_g = NULL; if (bTiming_m) { stopCmdTiming(); showCmdTiming(); } } fclose(pInputFP); } freeMemory( pszInputFile ); freeMemory( pszOutputFile ); } /******************************************************************* * NAME * do_load * DESCRIPTION * execute a LOAD command * RETURN VALUE * none */ static void do_load(char * pszFilename_in) { if ((pszFilename_in == NULL) || (*pszFilename_in == NUL)) { displayNumberedMessage(&sCmdMissingInputFile_g, NULL, NULL, "LOAD"); return; } /* left as an exercise for the reader :-) */ fprintf(stderr, "load %s is not implemented\n", pszFilename_in); } /******************************************************************* * NAME * do_save * DESCRIPTION * execute a SAVE command * RETURN VALUE * none */ static void do_save(char * pszFilename_in) { if ((pszFilename_in == NULL) || (*pszFilename_in == NUL)) { displayNumberedMessage(&sCmdMissingOutputFile_g, NULL, NULL, "SAVE"); return; } /* left as an exercise for the reader :-) */ fprintf(stderr, "save %s is not implemented\n", pszFilename_in); } /******************************************************************* * NAME * do_command * DESCRIPTION * read and execute a command * RETURN VALUE * TRUE (1) to continue, FALSE (0) to stop the program */ static int do_command() { char * pszCommand; char * pszSaved; char * pszArg; char * p; pszCommand = getCmdString(NULL, szPrompt_m, cCommentMarker_m); if ((pszCommand == NULL) || (*pszCommand == NUL)) return 1; /* ignore empty commands */ /* * save a copy of the command line */ pszSaved = duplicateString(pszCommand + strspn(pszCommand, szWhitespace_m)); /* * parse the first keyword of the command */ pszArg = strtok(pszCommand, szWhitespace_m); switch (lookupCmdKeyword(pszArg, asCommandTable_m, iCommandTableSize_m, "")) { case CMD_NULL: break; /* ignore empty commands */ case CMD_AMBIGUOUS: displayNumberedMessage(&sCmdAmbiguous_g, NULL, NULL, pszCommand); break; case CMD_INVALID: if (*pszArg == '!') { pszArg = pszSaved + 1; pszArg += strspn(pszArg, szWhitespace_m); doCmdSystem( pszArg ); } else if (*pszArg == '@') { if (pszArg[1] == NUL) pszArg = strtok(NULL, szWhitespace_m); else ++pszArg; doCmdTake( pszArg ); } else if (getCmdTakeLevel() != 0) { displayNumberedMessage(&sCmdErrorInTake_g, NULL, NULL, pszArg); closeCmdTake(); } else displayNumberedMessage(&sCmdInvalid_g, NULL, NULL, pszArg); break; case CMD_HELP: /* ? - list of commands displayed */ fprintf(stderr, "Type HELP for more help\n"); break; case KW_CHDIR: pszArg = strtok(NULL, szWhitespace_m); doCmdChdir( pszArg ); break; case KW_CLOSE: doCmdClose(); break; case KW_EDIT: pszArg = strtok(NULL, szWhitespace_m); if (getCmdTakeLevel() != 0) pszArg = buildAdjustedFilename(pszArg, getCmdTakeFile(), NULL); doCmdEdit( pszArg ); freeMemory( pszArg ); break; case KW_DIRECTORY: pszArg = pszSaved; pszArg += strcspn(pszArg, szWhitespace_m); pszArg += strspn(pszArg, szWhitespace_m); doCmdDirectory( pszArg ); break; case KW_HELP: do_help( strtok(NULL, szWhitespace_m) ); break; case KW_LOAD: pszArg = strtok(NULL, szWhitespace_m); pszArg = setCmdFilename( pszArg, "settings.ini", ".ini"); if (getCmdTakeLevel() != 0) { p = buildAdjustedFilename(pszArg, getCmdTakeFile(), NULL); freeMemory( pszArg ); pszArg = p; } do_load( pszArg ); freeMemory( pszArg ); break; case KW_LOG: doCmdLog( strtok(NULL, szWhitespace_m) ); break; case KW_PROCESS: pszArg = strtok(NULL,szWhitespace_m); do_process(pszArg, strtok(NULL, szWhitespace_m)); break; case KW_QUIT: /* QUIT or EXIT */ while (closeCmdTake()) ; freeMemory(pszSaved); return 0; case KW_SAVE: pszArg = strtok(NULL, szWhitespace_m); pszArg = setCmdFilename( pszArg, "settings.ini", ".ini"); if (getCmdTakeLevel() != 0) { p = buildAdjustedFilename(pszArg, getCmdTakeFile(), NULL); freeMemory( pszArg ); pszArg = p; } do_save( pszArg ); freeMemory( pszArg ); break; case KW_SET: do_set( strtok(NULL, szWhitespace_m) ); break; case KW_SHOW: do_show( strtok(NULL, szWhitespace_m) ); break; case KW_SYSTEM: pszArg = pszSaved; pszArg += strcspn(pszArg, szWhitespace_m); pszArg += strspn(pszArg, szWhitespace_m); doCmdSystem( pszArg ); break; case KW_TAKE: pszArg = strtok(NULL, szWhitespace_m); if (getCmdTakeLevel() != 0) pszArg = buildAdjustedFilename(pszArg, getCmdTakeFile(), NULL); doCmdTake( pszArg ); freeMemory( pszArg ); break; } freeMemory(pszSaved); return 1; } /******************************************************************* * NAME * main * DESCRIPTION * the main procedure for this program * RETURN VALUE * 1 to indicate successful execution of the program */ int main(int argc, char ** argv) { char * p; /* * establish the program basename */ pszCmdProgramName_g = strrchr(argv[0], '/'); if (pszCmdProgramName_g == NULL) pszCmdProgramName_g = argv[0]; else ++pszCmdProgramName_g; if ((pszCmdProgramName_g == NULL) || (*pszCmdProgramName_g == NUL)) pszCmdProgramName_g = "unknown"; pszCmdProgramName_g = duplicateString(pszCmdProgramName_g); if ((p = strrchr(pszCmdProgramName_g, '.')) != NULL) *p = NUL; /* * set up for error or SIGINT trapping */ if (setjmp(sCmdJmpBuf_g) != 0) { fputs("\nRETURNING TO PC-KIMMO COMMAND PROCESSING\n\n", stderr); } else { #ifdef USE_SIGNAL #ifdef SIGINT signal( SIGINT, handleCmdSigint ); #endif #endif } /* * read and execute commands until QUIT or EXIT */ fprintf(stderr, "%s - test program for the CMD function library\n", argv[0]); while (do_command()) ; return 1; }