CI

Section: C Library Functions (3)
Updated: 5/20/83
Index Return to Main Contents
 

NAME

ci - command interpreter  

SYNOPSIS

#include <stdio.h>
#include <ci.h>

ci (prompt,file,depth,list,helppath,cmdfpath);
char *prompt,*helppath,*cmdfpath;
FILE *file;
int depth;
CIENTRY *list;  

DESCRIPTION

Ci allows the user to type instructions which execute commands and which display and assign values to variables. The commands which are executed correspond to procedures supplied by the user's program; the variables correspond to variables in the program. The most basic facilities of ci are easily learned and used, and it provides other, more sophisticated, features which make it suitable for complex command-driven programs.

Ci has a basic loop which consists of printing the string prompt, reading a line from its current input file, and executing the specified instructions. The format and meaning of these instructions is as follows:

command [ arg ... ]
Execute the procedure which corresponds to the named command. The list of arguments will be passed to this procedure. See details below.
variable [ param ... ]
Display the value of the named variable. Some variables may require parameters; see details below.
variable = [ param ... ] value
Assign the given value to the named variable. The equals sign (=) may appear anywhere in the line.
command*
List the names of commands which begin with "command".
variable*=
List the names and values of variables which begin with "variable".
<commandfile
Recursively call ci, with the new procedure reading its commands from "commandfile".
?topic

help topic

Print help message related to topic; or, if topic matches more than one help message name, list the names of matching topics. If no topic is specified, a standard help message is displayed which describes how to use ci. If the topic is "*", a list of help topics is produced.
instruction >filename

Execute the given instruction,placing the output in the specified file rather than on the terminal. If more than one instruction appears on the input line, >filename must appear at the end of the input line and applies to the entire line.
!

!shellcommand

Fork a shell; if a command is specified, execute it and have the shell exit. The shell invoked is that specified by the environment variable SHELL. If SHELL is not defined the standard shell is used.
:comment
Ignore this instruction.
^instruction ("^" character followed by instruction)
Exit from this instance of ci, then execute the given instruction. Used when one ci is running as a subroutine of another -- the child exits, and the parent executes the given instruction.
@instruction ("@" character followed by instruction)

Exit from this instance of ci, execute the given instruction, then return to this instance of ci. Used when one ci is running as a subroutine of another -- the child exits, the parent executes the given instruction, and the child is re-entered.
^D (control-D)
Exit from ci.

Several instructions may appear on the same line, separated by semicolons. In addition, ci allows abbreviation of command names, variable names, and help topics to any unique prefix.  

PARAMETERS TO CI

Prompt is the string which is printed to tell the user to type a command line to ci. During execution of command files, the prompt will be echoed along with each line read from the file; both will be indented to indicate that this command was read from a command file.

File is the file to be used for input. Normally, the standard input (stdin) is used; if you specify 0, ci will assume you are using the standard input.

Depth is used for indenting the prompt; this should usually be 0 when you execute ci, but, when it calls itself recursively for command files, this will be non-zero.

List is an array of the entry descriptors for commands and variables; you declare this to be of type CIENTRY (see below), and use the macros described below to describe the entries.

Helppath is a list of directories containing help files, separated by colons. If you specify a single directory, that's fine; ci will assume all help files lie in that directory. When the user asks for help on some topic, ci looks for a file in one of the help directories which has a matching name. The contents of the file will simply be typed out to the user. If you specify 0 for helppath, ci will assume that no help files exist.

Cmdfpath is a list of directories containing command files, separated by colons. This is useful for libraries of prepared command files. The current directory should be included in the list; this is best done by indicating a null directory name (i.e. a colon as the first character of the path, or two consecutive colons within the path). If you specify 0 (which you will probably do most of the time), ci will assume that all command file names are evaluated with respect to the current directory only. Absolute pathnames, of course, are always valid.  

THE ENTRY LIST

The parameter list is a list of objects of type CIENTRY. These objects, defined by a set of macros, consist of a name which is a character string, a pointer to a value, and a type. You declare the list in this manner:
        CIENTRY list[] = {
                CIINT(...),
                CIDOUBLE(...),
                CICMD(...),
                ...
                CIEND
        };

The macros which define entries are described below.  

COMMANDS

A command is a procedure provided by your program, which can be executed by a user by typing its name and, optionally, a list of arguments. You specify a command by providing the procedure, which must take a character string as an argument, and by placing an entry into the CIENTRY list:
        mycommand (arglist)
        char *arglist;
        {
                char *p;        /* recommended for parsing */
                int arg1,arg2;
                p = arglist;
                arg1 = intarg (&p,0,...);       /* see intarg(3) */
                arg2 = intarg (&p,0,...);
                ...
        }
        ...
        CIENTRY list[] =
        {
                ...
                CICMD ("munch",mycommand),
                ...
                CIEND
        };

The user can then type "munch 3 4", and myproc will be executed with arglist equal to "3 4". The parsing sequence shown above (using intarg(3)), will assign 3 to arg1 and 4 to arg2. If the user were to type "munch" with no arguments, he would be prompted for arg1 and arg2 as described in intarg(3).  

SIMPLE VARIABLES

Ci knows how to manipulate several kinds of simple variables. To use these, you declare a variable of the appropriate type, and place an entry into the CIENTRY list. The types of variables known to ci correspond to the macros which you place into the list:
CIINT ("name", variable)
This specifies a variable of type int. "Name" is the name of the variable as it will appear to the user who is executing the program.
CILONG ("name", variable)

CISHORT ("name", variable)

These specify variables of type long (actually, long int), and short (actually, short int), respectively.
CIOCT ("name", variable)

CIHEX ("name", variable)

These specify unsigned int variables, whose values will be shown and interpreted as octal and hexadecimal integers, respectively. Thus, the value of an octal variable might be 07773; the value of a hexadecimal variable might be 0xabc.
CIDOUBLE ("name", variable)

CIFLOAT ("name", variable)

These indicate floating-point variables of types double and float, respectively.
CIBOOL ("name", variable)
This indicates a variable of type int, whose value will be either "yes" (i.e. non-zero), or "no" (i.e. zero).
CISTRING ("name", variable)
This indicates a variable of type "char *" or "char []", which will be treated as a character string. For ci to work properly, this should not contain garbage when you call ci.

Here is an example of two variables and how they might be used:

        int i;
        char s[100];
        ...
        CIENTRY list[] =
        {
                CIINT ("number",i),
                CISTRING ("string",s),
                ...
                CIEND
        };

Here is an excerpt of a dialogue with the program containing the above statements (lines typed by the user are indicated by italics):

        n=3
        number          3
        s=Hello, mom!
        string          Hello, mom!
        nu
        number          3
        num=4
        number          4
        *=
        number          4
        string          Hello, mom!
 

CLUSTER VARIABLES

In addition to the simple variables described above, ci can manipulate "clustered variables", which consist of a variable and some descriptive information about it. The descriptive information for a variable of type X (int, float, etc.) is exactly the information in the parameter list of the routine called "getX" (getint(3), getfloat(3), etc.). It typically includes some description of the legal values for the variable, and a prompt string printed to remind the user what this variable means.

To use a clustered variable involves two steps: you must declare the variable itself, together with its description; and you must insert the proper declaration into the CIENTRY list.

To declare a clustered "int" variable, use this macro:
       CINT (sname, vname, min, max, "prompt");

This macro appears just like any other declaration, but must be outside of any procedures (i.e. global). It will create an int variable called vname, which you may refer to in other parts of your program; it also declares a structure called sname which contains the description of vname. The description consists of three values: min, the minimum allowable value for vname; max, the maximum allowed value; and prompt, the prompt string for assigning a value to vname.

The corresponding entry of the CIENTRY list would be:
       CICINT ("name", sname)

where sname is the same as sname in the CINT macro.

A clustered variable differs from a simple variable in two ways. When a user tries to assign a value to a clustered variable, the new value is checked for legality. If it is legal, it is assigned; otherwise, a message is printed and the user can type another value. Also, the user may type "name=", omitting the value, and will be prompted for the value to be assigned.

Here are the clustered types known to ci:

CINT (sname, vname, min, max, "prompt");

CICINT ("name", sname)

Declares a clustered int variable. The legal range of values is [min..max]. The variable will be called vname. As indicated above, CINT is a declaration, and CICINT is the corresponding entry in the CIENTRY list.
CLONG (sname, vname, min, max, "prompt");

CICLONG ("name", sname)


CSHORT (sname, vname, min, max, "prompt");


CICSHORT ("name", sname)

These define long and short clustered variables, respectively. CLONG and CSHORT are the declaractions; CICLONG and CICSHORT are the entries for the CIENTRY list.
COCT (sname, vname, min, max, "prompt");

CICOCT ("name", sname)


CHEX (sname, vname, min, max, "prompt");


CICHEX ("name", sname)

These define unsigned int clustered variables whose values are interpreted as octal or hexadecimal numbers, respectively. COCT and CHEX are declarations; CICOCT and CICHEX are CIENTRYs.
CDOUBLE (sname, vname, min, max, "prompt");

CICDOUBLE ("name", sname)


CFLOAT (sname, vname, min, max, "prompt");


CICFLOAT ("name", sname)

These define floating-point variables (double and float, respectively). CDOUBLE and CFLOAT are declarations; CICDOUBLE and CICFLOAT are CIENTRYs.
CBOOL (sname, vname, "prompt");

CICBOOL ("name", sname)

Defines an int variable whose value is interpreted as "yes" (non-zero) or "no" (zero).
CCHR (sname, vname, "legals", "prompt");

CICCHR ("name", sname)

Defines an int variable whose value corresponds to a single character within the string legals. The value will be printed as the character indexed by the current value of the variable (i.e. legals[vname]), and, when assigning a value to it, the user types a character. The index of that character within legals will then be assigned to vname.
CSTRING (sname, vname, length, "prompt");

CICSTRING ("name", sname)

These define a variable which is a character array of length length. It will be treated as a character string.
CSTAB (sname, vname, table, "prompt");

CICSTAB ("name", sname)


CSEARCH (sname, vname, table, "prompt");


CICSEARCH ("name", sname)

These define a variable of type int, which corresponds to one of the strings in the string array table. The table is declared as for getstab(3) or getsearch(3), respectively, and the corresponding routine is actually used for assigning a value to the variable. The value is displayed as the string it indexes (e.g. table[vname]), and, to assign a value, the user types a string which matches an entry of the table.

Here is an example using two clustered variables:

        CINT (si, i, 1, 10, "What's your favorite number?");
        CSTRING (sname, name, 100, "What's your name?");
        ...
        CIENTRY list[] =
        {
                CICINT ("favorite",si),
                CICSTRING ("name",sname),
                ...
                CEND
        }

This might be part of a dialogue with the program containing the above declarations (lines typed by the user are indicated in italics):

        fav=7
        favorite        7
        name=Humpty Dumpty
        name            Humpty Dumpty
        fav=32
        32 out of range.  What's your favorite number?  (1 to 10)  [7] 4
        favorite        4
        f=
        What's your favorite number?  (1 to 10)  [4]  8
        favorite        8
        name=
        What's your name?  [Humpty Dumpty]  Minnie Mouse
        name            Minnie Mouse
        *=
        favorite        8
        name            Minnie Mouse

Most users, for most programs, will find clustered variables to be preferable to simple variables.  

PROCEDURE VARIABLES

Ci allows you to specify any type of variable you want -- an ordered pair, a character font, a buffer of a color TV display, a strange plotter, a robot arm, a file, the color of the pajama tops worn by three hippopotami in the CS lounge, absolutely anything at all!

There is, however, a catch. You have to write the procedure that manipulates the variable.

This type of variable is called a procedure variable. It consists of a procedure which you must provide, and an entry on the CIENTRY list which looks like this:

        CIPROC ("name", procname)
where procname is the name of your procedure.

Your procedure will be called with two parameters:

        proc (mode,arglist)
        CIMODE mode;
        char *arglist;
The first parameter, mode, indicates what ci is trying to do; the second, arglist is the list of parameters and values typed by the user.

The mode parameter may have one of three values:

CISET
ci is trying to assign a value to the variable; i.e. the user typed "name=" or "name=value" or something like that.
CISHOW
ci is trying to display the value of the variable; i.e. the user typed "name".
CIPEEK
ci is trying to do a one-line printed display of the variable in the format "name<TAB><TAB>value". This is normally performed when the user types "*=", and you should do this following a CISET.

Typically, the procedure will use a switch statement to deal with the three cases. If the value can be displayed by printing it on one line, the CISET and CIPEEK cases may be the same. This is true, for example, for an ordered pair of integers; it is not true, say, for a variable which represents a color picture (to display this may involve writing it onto a color TV monitor).

Here is an example of a procedure variable which represents an ordered pair:

        int x,y;
        ...
        xy (mode,arg)
        CIMODE mode;
        char *arg;
        {
                char *p;                /* for parsing */
                switch (mode) {
                case CISET:
                        p = arg;
                        x = intarg (&p,0,"X coordinate?",-100,100,x);
                        y = intarg (&p,0,"Y coordinate?",-100,100,y);
                        /* now, fall through to display the value */
                case CISHOW:
                case CIPEEK:
                        printf ("point\t\tx %d\ty %d\n",x,y);
                }
        }
        ...
        CIENTRY list[] =
        {
                CIPROC ("point",xy),
                ...
                CIEND
        }

Here is an example of dialogue with the program containing the above code (lines typed by the user are indicated by italics):

        point=3 5
        point           x 3     y 5
        p=
        X coordinate?  (-100 to 100)  [3]  72
        Y coordinate?  (-100 to 100)  [5]  39
        point           x 72    y 39
        p= 287
        287 out of range.  X coordinate?  (-100 to 100)  [72]  28
        Y coordinate?  (-100 to 100)  [39]  29
        point           x 28    y 29
        p
        point           x 28    y 29

Note that some kinds of variables may require parameters just to be displayed; you will receive a (possibly null) argument list every time your procedure variable is called, and may parse arguments for all three activities specified by mode.  

CLASS VARIABLES

On occasion, you may want to have several procedure variables which require the exact same code for their processing. For example, you may have sixteen different robot arms that you want the user to treat as variables; or have several windows on the color TV screen that you want to treat as variables. In such cases, it would be a shame to have to create several procedure variables, each with exactly the same code. To eliminate this duplication, ci provides a facility called the class.

A class is a collection of procedure variables which share the same code. Each variable, however, is distinguished by its own data. The entries on the CIENTRY list look like this:

        CICLASS ("name1",var1,classname),
        CICLASS ("name2",var2,classname),
and so on, one entry for each variable. Var1 and var2 are the names of the data areas for the variables; they might be declared like this:
        typedef struct { int field1; ... } DATAAREA;
        DATAAREA var1, var2;
Classname is the name of the procedure which is used by these variables for displaying and assigning a value.

The procedure will be called with four parameters. Continuing the above example, the procedure might begin like this:

        classname (mode,arglist,varptr,varname)
        CIMODE mode;
        char *arglist, *varptr, *varname;
        {
                DATAAREA *p;
                . . .
                p = (DATAAREA *) varptr;
                . . . p->field1 . . .
In this example, note that the first two parameters are just the same as the first two parameters for a procedure variable. They have exactly the same meaning. The third parameter is a pointer to the data area for the variable being displayed or assigned to. This value must be of type "char *" for C's type-checking to work properly, so you will want to coerce it by a type-cast to be a pointer to the proper type. Note also that var1 and var2 are DATAAREAs, not (DATAAREA *)s. In general, whatever type of object you declare in the CICLASS macro, the parameter passed to the procedure will be a pointer to that type of object. The fourth (last) parameter passed to the procedure will be the name of the variable being processed, in a character string.

Here is an example of two ordered pairs represented by two class variables:

        typedef struct {int x,y;} ORDPAIR;
        ORDPAIR startp,endp;
        ...
        ordproc (mode,arg,cdata,name)
        CIMODE mode;
        char *arg, *cdata, *name;
        {
                char *p;
                ORDPAIR *data;
                data = (ORDPAIR *) cdata;
                switch (mode) {
                case CISET:
                        p = arg;
                        data->x = intarg (&p,0,"X coordinate?",-100,100,data->x);
                        data->y = intarg (&p,0,"Y coordinate?",-100,100,data->y);
                case CISHOW:
                case CIPEEK:
                        printf ("%s\t\tx %d\ty %d\n",name,data->x,data->y);
                }
        }
        ...
        CIENTRY list[] =
        {
                CICLASS ("start",startp,ordproc),
                CICLASS ("end",endp,ordproc),
                ...
                CIEND
        }

Here is an example of dialogue with the program containing the above code (lines typed by the user are indicated by italics):

        
        start = 3 5
        start           x 3     y 5
        end = 6 10
        end             x 6     y 10
        start =
        X coordinate?  (-100 to 100)  [3]  72
        Y coordinate?  (-100 to 100)  [5]  39
        start           x 72    y 39
 

INTERRUPT HANDLING

If you use del(3) to trap interrupts, you will receive a bonus from ci. If you hit DEL during the execution of a command, that command may trap it (by DELRETURN, etc.); if the command ignores it, ci will deal with it when the command is finished executing.

If the interrupt occurred while ci was reading from the standard input, it will just print "Interrupt ignored".

If, however, the interrupt occurred during a command file, ci will print:

        INTERRUPT:  Abort or breakpoint?  [abort]
and wait for you to type something. If you type "a", or "abort", or just a carriage return, ci will abort the command file (i.e. pretend it just encountered the end of the file). If you type "b", or "breakpoint", or something like that, then ci will recursively call itself, with the new ci taking input from the standard input (e.g. terminal). The prompt will be "Breakpoint for prompt", where prompt is the prompt for the interrupted command file. When you exit from the new ci, the command file will be resumed as if nothing had happened.  

EXTERNAL VARIABLES

Ci uses six external variables (declared in the file <ci.h>) which you may also use in your program.
int cidepth;
This variable is the current depth of nesting of invocations of ci. It is automatically updated by ci to have the proper value.
FILE *ciinput;
This variable is the current input file for ci. You can read lines from this file within your commands and variables, if you want to read from the same place that ci is reading from.
char cinext[];
Normally the null string. If you place a ci instruction in this string, it will be executed before ci reads any new input lines.
char ciprev[];
Normally the null string. When an instance of ci (the "parent") contains a command which invokes a new ci with a different entry list (the "child"), the string ciprev must be used to enable the "@" instruction to function. In the parent, immediately after the ci() invocation for the child, place the statement: 'strcpy (ciprev,"childname");' where "childname" is the command the user would type to invoke the child instance of ci.
int ciexit;
If you put a non-zero value into this variable, ci will exit (i.e. return) when the current command (or procedure variable) is finished. This allows you to write a command which causes ci to exit.
int ciquiet;
This word contains several bits which govern the output produced by ci.

The bits for ciquiet are also declared in the macro file. If a bit is 0, the output will be produced; if it is 1, the output is suppressed:

CISHEXIT
print a message when ci resumes after a shell command (i.e. "!").
CISETPEEK
automatically display the new value of a variable when that variable has a new value assigned. This effectively performs a CIPEEK on a variable after a CISET. The automatic display is never performed for variable procedures or class variables. CISETPEEK is only used when the input to ci is coming from the terminal. For command files, see CICMDFPEEK below.
CICMDFECHO
echo on the terminal each line which is read from a command file.
CICMDFPROMPT
echo the prompt (indented) on the terminal before each line read from a command file.
CICMDFEXIT
print an end-of-file message on the terminal after executing a command file.
CICMDFPEEK
display new value of a variable after assigning; used when input is from command file. See CISETPEEK, above.
CICMDNOINDENT
indent commands to reflect nesting of ci invocations.

Certain bits in ciquiet control the processing of input rather than output. If these bits are 0, all special input symbols are processed; if 1, the corresponding special input character is treated just like ordinary data with no special meaning to ci:

CINOSEM
If 1, semicolons (;) are data characters; if 0, semicolons separate multiple instructions on a single input line.
CINOFILE
If 1, right angle-brackets (>) are data characters; if 0, ">filename" instructs ci to place output into the named file.
CIFIRSTEQUAL
If 1, an equals sign (=) is a data character unless it appears immediately after a variable name; if 0, any equals sign indicates that the instruction is assigning a value to a variable.

 

FILES

/usr/cs/lib/ci.help    default help file
 

ENVIRONMENT VARIABLES

SHELL
Name of shell to invoke for "!" instruction. If not defined, "sh" is used.
 

SEE ALSO

intarg(3), shortarg(3), longarg(3), octarg(3), hexarg(3), doublearg(3),
floatarg(3), chrarg(3), boolarg(3), strarg(3), stabarg(3), searcharg(3), del(3)  

BUGS

"Depth" argument is now a no-op, since its function is subsumed by the "cidepth" global variable. The argument has been retained for backward compatibility; its value is ignored.


 

Index

NAME
SYNOPSIS
DESCRIPTION
PARAMETERS TO CI
THE ENTRY LIST
COMMANDS
SIMPLE VARIABLES
CLUSTER VARIABLES
PROCEDURE VARIABLES
CLASS VARIABLES
INTERRUPT HANDLING
EXTERNAL VARIABLES
FILES
ENVIRONMENT VARIABLES
SEE ALSO
BUGS

This document was created by man2html, using the manual pages.
Time: 12:50:29 GMT, May 26, 2025