CMD Function Library Reference Manual

functions for terminal command interaction

October 1997

by Stephen McConnel


Table of Contents


1. 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.

2. 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.

2.1 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.

2.2 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.

2.3 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.

2.3.1 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

2.3.2 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.

2.3.3 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.

2.4 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.

2.5 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).

3. 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.

3.1 CmdKeyword

3.1.1 Definition

#include "cmd.h"

typedef struct {
    char *      pszKeyword;
    int         iValue;
    int         iFlags;
    } CmdKeyword;

3.1.2 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

3.1.3 Source File

`cmd.h'

4. 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.

4.1 bCmdShowWarnings_g

4.1.1 Syntax

#include "cmd.h"

extern int bCmdShowWarnings_g;

4.1.2 Description

The CMD function library uses displayNumberedMessage (see section `displayNumberedMessage' 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).

4.1.3 Example See section 6. Sample Program.

4.1.4 Source File `cmdmessg.c'

4.1.5 Used By

doCmdChdir
doCmdClose
doCmdEdit
doCmdLog
doCmdTake

4.2 bCmdSilentMessages_g

4.2.1 Syntax

#include "cmd.h"

extern int bCmdSilentMessages_g;

4.2.2 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).

4.2.3 Example See section 6. Sample Program.

4.2.4 Source File `cmdmessg.c'

4.2.5 Used By

doCmdChdir
doCmdClose
doCmdEdit
doCmdLog
doCmdTake

4.3 pCmdLogFP_g

4.3.1 Syntax

#include "cmd.h"

extern FILE * pCmdLogFP_g;

4.3.2 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).

4.3.3 Example See section 6. Sample Program, section 4.13 sCmdClosingLog_g, and section 4.24 sCmdNoLogFile_g.

4.3.4 Source File `cmddata.c'

4.3.5 Used By

doCmdTake
doCmdChdir
doCmdEdit
doCmdLog
doCmdClose

4.4 pCmdOutputFP_g

4.4.1 Syntax

#include "cmd.h"

extern FILE * pCmdOutputFP_g;

4.4.2 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).

4.4.3 Example See section 6. Sample Program.

4.4.4 Source File `cmddata.c'

4.4.5 Used By

handleCmdSigint

4.5 pszCmdLogFile_g

4.5.1 Syntax

#include "cmd.h"

extern char * pszCmdLogFile_g;

4.5.2 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).

4.5.3 Example See section 6. Sample Program, section 4.13 sCmdClosingLog_g, and section 4.24 sCmdNoLogFile_g.

4.5.4 Source File `cmddata.c'

4.5.5 Used By

doCmdClose
doCmdLog

4.6 pszCmdProgramName_g

4.6.1 Syntax

#include "cmd.h"

extern char * pszCmdProgramName_g;

4.6.2 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).

4.6.3 Example See section 6. Sample Program, and section 4.13 sCmdClosingLog_g.

4.6.4 Source File `cmddata.c'

4.6.5 Used By

doCmdLog
doCmdSystem
doCmdTake

4.7 sCmdAmbiguousKeyword_g

4.7.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdAmbiguousKeyword_g;

4.7.2 Description

sCmdAmbiguousKeyword_g is defined as follows:

NumberedMessage sCmdAmbiguousKeyword_g = {
    ERROR_MSG,
    105,
    "Ambiguous keyword in %s command: %s"
    };

4.7.3 Example

See section 6. Sample Program.

4.7.4 Source File `messages.c'

4.7.5 Used By

4.8 sCmdAmbiguous_g

4.8.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdAmbiguous_g;

4.8.2 Description

sCmdAmbiguous_g is defined as follows:

NumberedMessage sCmdAmbiguous_g = {
    ERROR_MSG,
    101,
    "Ambiguous command: %s"
    };

4.8.3 Example

See section 6. Sample Program.

4.8.4 Source File `messages.c'

4.8.5 Used By

4.9 sCmdBadArgument_g

4.9.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdBadArgument_g;

4.9.2 Description

sCmdBadArgument_g is defined as follows:

NumberedMessage sCmdBadArgument_g = {
    ERROR_MSG,
    107,
    "Invalid argument in %s command: %s"
    };

4.9.3 Example

See section 6. Sample Program.

4.9.4 Source File `messages.c'

4.9.5 Used By

4.10 sCmdBadInputFile_g

4.10.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdBadInputFile_g;

4.10.2 Description

sCmdBadInputFile_g is defined as follows:

NumberedMessage sCmdBadInputFile_g = {
    ERROR_MSG,
    109,
    "Cannot open input file %s in %s command"
    };

4.10.3 Example

See section 6. Sample Program.

4.10.4 Source File `messages.c'

4.10.5 Used By

doCmdTake

4.11 sCmdBadKeyword_g

4.11.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdBadKeyword_g;

4.11.2 Description

sCmdBadKeyword_g is defined as follows:

NumberedMessage sCmdBadKeyword_g = {
    ERROR_MSG,
    106,
    "Invalid keyword in %s command: %s"
    };

4.11.3 Example

See section 6. Sample Program.

4.11.4 Source File `messages.c'

4.11.5 Used By

4.12 sCmdBadOutputFile_g

4.12.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdBadOutputFile_g;

4.12.2 Description

sCmdBadOutputFile_g is defined as follows:

NumberedMessage sCmdBadOutputFile_g = {
    ERROR_MSG,
    110,
    "Cannot open output file %s in %s command"
    };

4.12.3 Example

See section 6. Sample Program, and section 4.13 sCmdClosingLog_g.

4.12.4 Source File `messages.c'

4.12.5 Used By

doCmdLog

4.13 sCmdClosingLog_g

4.13.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdClosingLog_g;

4.13.2 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).

4.13.3 Example

#include <stdio.h>
#include "allocmem.h"
#include "cmd.h"
...
void doCmdLog(pszFilename_in)
const char *    pszFilename_in;
{
char buffer[80];
char *  pszFilename;

sprintf(buffer, "LOG [<file>] (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;
}

4.13.4 Source File

`messages.c'

4.13.5 Used By

doCmdLog

4.14 sCmdErrorInTake_g

4.14.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdErrorInTake_g;

4.14.2 Description

sCmdErrorInTake_g is defined as follows:

NumberedMessage sCmdErrorInTake_g = {
    ERROR_MSG,
    112,
    "TAKE file aborted due to invalid command: %s"
    };

4.14.3 Example

See section 6. Sample Program.

4.14.4 Source File `messages.c'

4.14.5 Used By

4.15 sCmdInvalidDirectory_g

4.15.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdInvalidDirectory_g;

4.15.2 Description

sCmdInvalidDirectory_g is defined as follows:

NumberedMessage sCmdInvalidDirectory_g = {
    ERROR_MSG,
    117,
    "Bad pathname for CD command"
    };

4.15.3 Example

This example is simpler than the actual library function source code.

#include <stdlib.h>
#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 <pathname>   (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");
}

4.15.4 Source File

`messages.c'

4.15.5 Used By

doCmdChdir

4.16 sCmdInvalid_g

4.16.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdInvalid_g;

4.16.2 Description

sCmdInvalid_g is defined as follows:

NumberedMessage sCmdInvalid_g = {
    ERROR_MSG,
    102,
    "Invalid command: %s"
    };

4.16.3 Example

See section 6. Sample Program.

4.16.4 Source File `messages.c'

4.16.5 Used By

4.17 sCmdJmpBuf_g

4.17.1 Syntax

#include <setjmp.h>
#include "cmd.h"

extern jmp_buf sCmdJmpBuf_g;

4.17.2 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.

4.17.3 Example See section 6. Sample Program.

4.17.4 Source File `cmdsig.c'

4.17.5 Used By

handleCmdSigint

4.18 sCmdMissingArgument_g

4.18.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingArgument_g;

4.18.2 Description

sCmdMissingArgument_g is defined as follows:

NumberedMessage sCmdMissingArgument_g = {
    ERROR_MSG,
    104,
    "Missing argument in %s command"
    };

4.18.3 Example

See section 6. Sample Program.

4.18.4 Source File `messages.c'

4.18.5 Used By

4.19 sCmdMissingDirectory_g

4.19.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingDirectory_g;

4.19.2 Description

sCmdMissingDirectory_g is defined as follows:

NumberedMessage sCmdMissingDirectory_g = {
    ERROR_MSG,
    116,
    "Missing pathname for CD command"
    };

4.19.3 Example

See section 4.15 sCmdInvalidDirectory_g.

4.19.4 Source File `messages.c'

4.19.5 Used By

doCmdChdir

4.20 sCmdMissingEditFile_g

4.20.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingEditFile_g;

4.20.2 Description

sCmdMissingEditFile_g is defined as follows:

NumberedMessage sCmdMissingEditFile_g = {
    ERROR_MSG,
    115,
    "Missing filename for EDIT command"
    };

4.20.3 Example

This example is simpler than the actual library function source code.

#include <stdlib.h>
#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 <file> (no default filename or type)"))
    return;

pszEditor = getenv("EDITOR");
if (pszEditor == NULL)
    pszEditor = "emacs";
sprintf(szEditCommand, "%s %s", pszEditor, pszFilename_in);
doCmdSystem(szEditCommand);
}

4.20.4 Source File

`messages.c'

4.20.5 Used By

doCmdEdit

4.21 sCmdMissingInputFile_g

4.21.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingInputFile_g;

4.21.2 Description

sCmdMissingInputFile_g is defined as follows:

NumberedMessage sCmdMissingInputFile_g = {
    ERROR_MSG,
    108,
    "Missing input file argument in %s command"
    };

4.21.3 Example

See section 6. Sample Program.

4.21.4 Source File `messages.c'

4.21.5 Used By

4.22 sCmdMissingKeyword_g

4.22.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingKeyword_g;

4.22.2 Description

sCmdMissingKeyword_g is defined as follows:

NumberedMessage sCmdMissingKeyword_g = {
    ERROR_MSG,
    103,
    "Missing keyword in %s command"
    };

4.22.3 Example

See section 6. Sample Program.

4.22.4 Source File `messages.c'

4.22.5 Used By

4.23 sCmdMissingOutputFile_g

4.23.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdMissingOutputFile_g;

4.23.2 Description

sCmdMissingOutputFile_g is defined as follows:

NumberedMessage sCmdMissingOutputFile_g = {
    ERROR_MSG,
    118,
    "Missing output file argument in %s command"
    };

4.23.3 Example

See section 6. Sample Program.

4.23.4 Source File `messages.c'

4.23.5 Used By

4.24 sCmdNoLogFile_g

4.24.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdNoLogFile_g;

4.24.2 Description

sCmdNoLogFile_g is defined as follows:

NumberedMessage sCmdNoLogFile_g = {
    ERROR_MSG,
    113,
    "No log file was open"
    };

4.24.3 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;
    }
}

4.24.4 Source File

`messages.c'

4.24.5 Used By

doCmdClose

4.25 sCmdTooDeepTake_g

4.25.1 Syntax

#include "cmd.h"

extern NumberedMessage sCmdTooDeepTake_g;

4.25.2 Description

sCmdTooDeepTake_g is defined as follows:

NumberedMessage sCmdTooDeepTake_g = {
    ERROR_MSG,
    111,
    "TAKE files nested too deeply"
    };

4.25.3 Example

This example is the actual library function source code (at least when this documentation was written).

#include <stdio.h>
#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 [<file>] (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;
    }
}

4.25.4 Source File

`messages.c'

4.25.5 Used By

doCmdTake

5. 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.

5.1 closeCmdTake

5.1.1 Syntax

#include "cmd.h"

int closeCmdTake(void);

5.1.2 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.

5.1.3 Return Value the number of (nested) "take" files open when closeCmdTake finishes

5.1.4 Example See section 6. Sample Program.

5.1.5 Source File `cmd.c'

5.2 doCmdChdir

5.2.1 Syntax

#include "cmd.h"

void doCmdChdir(const char * pszDirectory_in);

5.2.2 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.

5.2.3 Return Value

none

5.2.4 Example See section 6. Sample Program, and section 4.15 sCmdInvalidDirectory_g.

5.2.5 Source File `cmdcd.c'

5.3 doCmdClose

5.3.1 Syntax

#include "cmd.h"

void doCmdClose(void);

5.3.2 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.

5.3.3 Return Value none

5.3.4 Example See section 6. Sample Program.

5.3.5 Source File `cmdlog.c'

5.4 doCmdDirectory

5.4.1 Syntax

#include "cmd.h"

void doCmdDirectory(const char * pszArgument_in);

5.4.2 Description

doCmdDirectory lists the contents of a directory on the screen. It is designed to to perform an interactive `directory <options>' 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.

5.4.3 Return Value

none

5.4.4 Example See section 6. Sample Program.

5.4.5 Source File `cmddir.c'

5.5 doCmdEdit

5.5.1 Syntax

#include "cmd.h"

void doCmdEdit(const char * pszFilename_in);

5.5.2 Description

doCmdEdit edits a file. It is designed to to perform an interactive `edit <filename>' 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.

5.5.3 Return Value

none

5.5.4 Example See section 6. Sample Program.

5.5.5 Source File `cmdedit.c'

5.6 doCmdLog

5.6.1 Syntax

#include "cmd.h"

void doCmdLog(const char * pszFilename_in);

5.6.2 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 <filename>' command.

doCmdLog has one argument:

pszFilename_in
points to the pathname of the log file to open.

5.6.3 Return Value

none

5.6.4 Example See section 6. Sample Program, and section 4.13 sCmdClosingLog_g.

5.6.5 Source File `cmdlog.c'

5.7 doCmdSystem

5.7.1 Syntax

#include "cmd.h"

void doCmdSystem(const char * pszCommand_in);

5.7.2 Description

doCmdSystem executes a system command in a subshell. It is designed to perform the operation of an interactive `system <command line>' 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.

5.7.3 Return Value

none

5.7.4 Example See section 6. Sample Program.

5.7.5 Source File `cmdsys.c'

5.8 doCmdTake

5.8.1 Syntax

#include "cmd.h"

void doCmdTake(const char * pszFilename_in);

5.8.2 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.

5.8.3 Return Value

none

5.8.4 Example See section 6. Sample Program.

5.8.5 Source File `cmd.c'

5.9 getCmdString

5.9.1 Syntax

#include "cmd.h"

char * getCmdString(const char *  pszCommand_in,
                    const char *  pszPrompt_in,
                    int           cComment_in);

5.9.2 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').

5.9.3 Return Value

a pointer to the command string (which may be overwritten by next call to getCmdString)

5.9.4 Example See section 6. Sample Program.

5.9.5 Source File `cmd.c'

5.10 getCmdTakeFile

5.10.1 Syntax

#include "cmd.h"

char * getCmdTakeFile(void);

5.10.2 Description

getCmdTakeFile gets the name of the current "take" file, if any are open.

getCmdTakeFile does not have any arguments.

5.10.3 Return Value the pathname of the current "take" file, or NULL if none are open

5.10.4 Example See section 6. Sample Program.

5.10.5 Source File `cmd.c'

5.11 getCmdTakeLevel

5.11.1 Syntax

#include "cmd.h"

int getCmdTakeLevel(void);

5.11.2 Description

getCmdTakeLevel gets the number of open "take" files.

getCmdTakeLevel does not have any arguments.

5.11.3 Return Value the number of (nested) "take" files open

5.11.4 Example See section 6. Sample Program.

5.11.5 Source File `cmd.c'

5.12 handleCmdSigint

5.12.1 Syntax

#include "cmd.h"

RETSIGTYPE handleCmdSigint(int iSignal_in);

5.12.2 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).

5.12.3 Return Value

none (it does not return)

5.12.4 Example See section 6. Sample Program.

5.12.5 Source File `cmdsig.c'

5.13 lookupCmdKeyword

5.13.1 Syntax

#include "cmd.h"

int lookupCmdKeyword(
    char *             pszCommand_in,
    const CmdKeyword * pKeywordTable_in,
    int                iTableSize_in,
    char *             pszHelp_in);

5.13.2 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.

5.13.3 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)

5.13.4 Example

See section 6. Sample Program.

5.13.5 Source File `cmd.c'

5.14 setCmdFilename

5.14.1 Syntax

#include "cmd.h"

char * setCmdFilename(const char * pszFilename_in,
                      const char * pszDefaultName_in,
                      const char * pszExtension_in);

5.14.2 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.

5.14.3 Return Value

a dynamically allocated filename string, or NULL if pszFilename_in and pszDefaultName_in are both NULL

5.14.4 Example See section 6. Sample Program.

5.14.5 Source File `setcmdfi.c'

5.15 showCmdTiming

5.15.1 Syntax

#include "cmd.h"

void showCmdTiming(void );

5.15.2 Description

showCmdTiming displays the elapsed time for an operation, using the internal values set by startCmdTiming and stopCmdTiming.

showCmdTiming does not have any arguments.

5.15.3 Return Value none

5.15.4 Example See section 6. Sample Program.

5.15.5 Source File `cmdtime.c'

5.16 startCmdTiming

5.16.1 Syntax

#include "cmd.h"

void startCmdTiming(void);

5.16.2 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.

5.16.3 Return Value none

5.16.4 Example See section 6. Sample Program.

5.16.5 Source File `cmdtime.c'

5.17 stopCmdTiming

5.17.1 Syntax

#include "cmd.h"

void stopCmdTiming(void );

5.17.2 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.

5.17.3 Return Value none

5.17.4 Example See section 6. Sample Program.

5.17.5 Source File `cmdtime.c'

5.18 wantCmdHelp

5.18.1 Syntax

#include "cmd.h"

int wantCmdHelp(const char * pszArgument_in,
                const char * pszHelpMessage_in);

5.18.2 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.

5.18.3 Return Value

TRUE (nonzero) if help provided, otherwise FALSE (zero)

5.18.4 Example See section 6. Sample Program.

5.18.5 Source File `wanthelp.c'

6. 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#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 <infile> <outfile> (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 <parameter> 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 <number> 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 <char> sets the comment character to the indicated\n\
value.  If <char> 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 <parameter> 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 <command> 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 <pathname> 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 <file> 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 [<command-name>] 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 <filename> ... (has not been written)\n", stderr);
        break;
    case KW_LOG:
        fprintf(stderr, "\
LOG [<file>] 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 <infile> <outfile> 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 <filename> ... (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 [<command>] 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 [<file>] 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;
}


This document was generated on 11 May 2000 using texi2html 1.55k.