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