Standard C strings, consisting of character arrays, have some weakness. It is necessary to keep track of the maximum and current lengths of a C string in separate variables or constants. Also, a byte with a null value in the middle of a string will cause library routines to treat that as the end of the string. Further, failure to terminate a C string with a null byte will cause library routines to march off the end of a string, to the detriment of the program as a whole.
Key invocations make heavy use of strings, which are expected to be able to include any binary value from 0-255 as a byte. Key invocation statements need to know the current length of a string when passing it, and the maximum length of a string when receiving into it. For these reasons a PL/I-like string definition (type STRING) is provided by Key Logic. The STRING type is converted to standard C language declarations by the KCPP preprocessor. A library of functions is provided to manipulate STRING type variables and constants.
Every C program must include the header file "KEYKOS.H" in order to use the KeyKOS C language extensions. The following statement must be used in the program before any other #include statement:
#include <keykos.h>
#include <lli.h>
This header contains the following declaration:
typedef struct {unsigned long hi, low;} LLI;
KeyKOS library functions for operating on LLIs are described elsewhere in this manual. You should use only those functions for operating on LLIs, and not rely on the structure representation. It is non-portable to assume that an unsigned long has 4 characters and/or 32 bits. You may, however, initialize an LLI as follows:
LLI mylli; unsigned long value; mylli.hi = 0; mylli.low = value;
You may also test whether an LLI is less than or equal to the maximum unsigned long, and extract its value, as follows
if (mylli.hi == 0) value = mylli.low;
STRING <name> [ ( <maxlen> ) [ = <init> ] ]
where <name> is an alphanumeric identifier, <maxlen> is a positive integer specifying the length of the string, and <init> is an optional initial string value. The optional initial string value may only be specified if <maxlen> is also provided. Only a single identifier may be declared per STRING statement.
A STRING variable is implemented as a structure that keeps track of the maximum possible string size it can store (<maxlen>), the length of the string it is currently storing, and the string itself. The KCPP preprocessor expands a STRING declaration into a static structure declaration and initializes the maximum and actual lengths of the string. For example, the declaration
STRING name(20) = "KeyKOS";
is expanded to
static struct { int maxlen, actlen; char s(:20:); } _name = {20, 6, "KeyKOS"}; char *name = &_name;
Every STRING declaration with a <maxlen> attribute results in creating a unique structure. Whenever the STRING variable is passed as an argument to a function, an address of the structure is passed. But a STRING declaration without <maxlen> does not create any structure. Instead it is expanded by the KCPP preprocessor into a declaration for a pointer to a STRING type. For example, the following declarations are expanded from
STRING name; extern STRING new();
to
char *name; extern char *new();
A STRING variable declared without the <maxlen> attribute may be used for defining a function's formal argument that is a STRING variable, or for defining a function that returns a STRING value. For example:
int doit(mystring) STRING mystring; { ... } STRING this(arg1) { ... }
Notes:
KEY <name> [ = <slot number> ]
The <name> can be thought of as the KEY name, but you must realize that this really declares a name for a slot number, either in the key registers or the key cache (see below). The initial value <slot number> must be an integer in the range 0 to 15 without key cache, and in the range -1 to 32767 with key cache. If an initial positive value <slot number> is specified, then the key is defined as a constant. If the key is not initialized, or is initialized to -1, then it is defined as a variable. Any combination of KEY variables and constants may be declared in the same statement. For instance, in
KEY x,y=6,z=1,v;
the constants y and z are declared in the same statement with the variables x and v.
Since all keys must be defined as type KEY, you cannot write a statement like:
KC(1,1) RCTO(rc); /* invalid */
Instead, use:
KEY KEY1 = 1; KC(KEY1,1) RCTO(rc); /* good example */
The value of a KEY constant must not be changed from the slot number assigned to it by the declaration statement. Any attempt to reassign a constant KEY will lead to an unpredictable result.
KEY variables which are initialized to -1 are intended for use only with the KeyKOS key cache mechanism (see "Compilation" in Chapter 2 for details on specifying key cache use). The first time a key is received into such a KEY variable, the key cache mechanism allocates a slot in the key cache super node, and then saves that slot number in the KEY variable. Thereafter, the value of that KEY variable must not be changed. Any attempt to reassign a key cache KEY variable will lead to an unpredictable result. Any attempt to initialize a KEY variable to -1 without key cache usage will be flagged as an error by the KCPP preprocessor.
Uninitialized KEY variables are intended for use as global or local KEY variables. Their values can be reassigned.
There are special conditions on how a KEY name can be used: