


ici()               UNIX Programmer's Manual                ici()



NAME
     ici - ici interpreter

SYNOPSIS
     ici file arguments...
     ici [options] file arguments...

DESCRIPTION
     Ici is an interpreter for the ici language defined in the
     ICI Technical Description.  It is designed for use in many
     environments, including embedded systems, as an adjunct to
     other programs, as a text-based interface to compiled
     libraries, and as a cross-platform scripting language with
     good string-handling capabilities.

     The ici language is reminiscent of C, with a very similar
     syntax, the same operator precedence, but a few powerful
     additions.  Automatic garbage collection, exception han-
     dling, dynamic collection data types like sets, associative
     arrays (`structs'), and support for object oriented program-
     ming are features of the language.  Types are assigned at
     run-time and can be examined via `typeof()'.  The only
     requirement is that types must make sense at run-time.
     Function argument lists are represented as an ici array so
     they can be manipulated just like any other first-class type
     - they can be examined, extended, and passed on to other
     functions.  Functions can be called with optional parame-
     ters, default values, and varying types.  Case-expressions
     in switch statements can be strings and other types.

     The most visible differences between ici and C are:

     +   Declarations take no type specifier.

     +   Function declarations require a storage class (e.g.
         static) to distinguish them from a function call.

     +   There is no `main' - execution starts with the first
         executable statement.

     +   Declarations are executable statements - but they are
         executed once only, at parse time.

     The interpreter may be invoked in two ways, the first using
     no command line switches and the second using switches forc-
     ing the interpreter to examine the command line arguments.
     The first form is useful when your program requires access
     to command line switches. The second form provides some use-
     ful command line options.  In both usages the file argument
     may be replaced with a - to indicate the standard input
     should be read. Also any remaining command line arguments
     are collected into an array and made available to the ici
     program as the global variables argc and argv (see the
     technical reference for further information).

     The interpreter parses the ici program from the named file
     and executes it. Ici programs are ordinary text files. Com-
     ments are started with the characters /* and continue until
     the next */.  Also, lines which start with a # character are
     considered comments.

     A program consists of a series of statements. Statements may
     be either declarations or expressions. A declaration defines
     a name and possibly associates a parse-time value with that
     name.  Expressions and other executable statements generate
     code which is executed.


     Command Line Arguments

     Versions of ici on systems that support passing parameters
     from the command line provide two predefined variables, argv
     and argc, for accessing these arguments.

     On Win32 platforms ici performs wildcard expansion in the
     traditional MS-DOS fashion.  Arguments containing wildcard
     meta-characters, `?' and `*', may be protected by enclosing
     them in single or double quotes.

     argv

     An array of strings containing the command line arguments.
     The first element is the name of the ici program and subse-
     quent elements are the arguments passed to that program.

     argc

     The count of the number of elements in argv.  Initially
     equal to nels(argv).



     Reserved Words

     The complete list of ici's reserved words is:

               NULL      auto      break
               case      continue  default
               do        else      extern
               for       forall    if
               in        onerror   return
               static    switch    try
               while



     Lexicon

     The first stage of the ici parser breaks the input streams
     into tokens, optionally separated by white space.  The next
     token is always formed from the longest meaningful sequence
     of characters.  These are the tokens that make up ici's set
     of operators:

         *   &   -   +   !   ~   ++ --  @   $
         /   %   >>  <<  <   >   <= >=  ==  !=
         ~   !~  ~~  ~~~ &   ^   |  &&  ||  :
         ?   =   +=  -=  *=  /=  %= >>= <<= &=
         ^=  |=  ~~= <=> ,   .   ->

     Other tokens are:

         [   ]   (   )   {   }   ;

     Still other tokens are literal regular expressions (they
     start and end with a `#', enclosing any sequence of charac-
     ters except newline), literal strings, literal characters,
     and literal numbers.

     White space consists of spaces, tabs, newlines, or comments.
     Comments are as in C (/* ... */), and also from a # at the
     start of a line to the end of the line.

     Literal strings and literal characters can include the fol-
     lowing escape sequences:


     \a      audible bell (ASCII 0x07)

     \b      backspace (ASCII 0x08)

     \cx     control-x (ASCII 0x01 .. 0x1A)

     \e      escape (ASCII 0x1B)

     \f      form feed (ASCII 0x0C)

     \n      newline (ASCII 0x0A)

     \r      carriage return (ASCII 0x0D)

     \t      tab (ASCII 0x09)

     \v      vertical tab (ASCII 0x0B)

     \"      double quote (ASCII 0x22)

     \'      single quote (ASCII 0x27)

     \?      question mark (ASCII 0x3F)

     \\      backslash (ASCII 0x5C)

     \xx..   the character with hex code x.. (1, or 2 hexadecimal
             digits).

     \xn...  the character with octal code n... (1, 2, or 3 octal
             digits).


     Adjacent string literals (separated by white space) are con-
     catenated to form a single string literal.  A sequence of
     upper or lower case letters, underscores and digits is
     interpreted as:

         An integer if possible,

         otherwise as a floating point number if possible,

         otherwise as an identifier.




     Syntax

     Ici's syntax is defined by the following grammar.

     statement executable-statement
                         declaration

     executable-statement          expression ;
               compound-statement
               if ( expression ) statement
               if ( expression ) statement else statement
               while  ( expression ) statement
               do statement while ( expression ) ;
               for ( [ expression ] ; [ expression ] ; [ expression ] ) statement
               forall ( expression [ , expression ] in expression ) statement
               switch ( expression ) compound-statement
               case parser-evaluated-expression :
               default ;
               break ;
               continue ;
               return [ expression ] ;
               try statement onerror statement
               ;

     factor    integer-number
               character-code
               floating-point-number
               string
               regular-expression
               identifier
               NULL
               ( expression )
               [ array expression-list  ]
               [ set expression-list ]
               [ struct [ : expression , ] assignment-list ]
               [ func function-body ]

     expression-list     empty
               expression [ , ]
               expression , expression-list

     assignment-list     empty
               assignment [ , ]
               assignment , assignment-list

     assignment          struct-key =  expression

     struct-key          identifier
               ( expression )

     function-body       ( identifier-list ) compound-statement

     identifier-list     empty
               identifier [  , ]
               identifier ,  identifier-list

     primary-expression  factor  primary-operation...

     primary-operation   [ expression ]
               . identifier
               . ( expression )
               -> identifier
               ->  ( expression )
               ( expression-list )


     term      [ prefix-operator...] primary-expression [ postfix-operator... ]

     prefix-operator     Any of:
               *  &  -  +  !  ~  ++  --  @  $

     postfix-operator    Any of:
               ++  --

     expression          term
               expression binary-operator expression

     binary-operator     Any of:

               @
               *  /  %
               +  -
               >>  <<
               <  >  <=  >=
               ==  !=  ~  !~  ~~  ~~~
               &

               ^
               |
               &&
               ||
               :
               ?
               =  +=  -=  *=  /=  %=  >>=  <<=  &=  ^=  |=  ~~=  <=>
               ,

     compound-statement
               { statement... }



     Unary Operators

     Prefix operators


     *       Indirection; applied to a pointer, gives target of
             the pointer.

     &       Address of; applied to any lvalue, gives a pointer
             to it.

     -       Negation; gives negative of any arithmetic value.

     +       Positive; no real effect.

     !       Logical not; applied to 0 or NULL, gives 1, else
             gives 0.

     ~       Bit-wise complement.

     ++      Pre-increment; increments an lvalue and gives new
             value.

     --      Pre-decrement; decrements an lvalue and gives new
             value.

     @       Atomic form; gives the (unique) read-only version of
             any value.

     $       Immediate evaluation.  This $, is only a pseudo-
             operator.  It actually has its effect entirely at
             parse time.  The $ operator causes its subject
             expression to be evaluated immediately by the parser
             and the result of that evaluation substituted in its
             place.  This is used to speed later execution, to
             protect against later scope or variable changes, and
             to construct constant values which are better made
             with running code than literal constants.

     Postfix operators


     ++      Post-increment; increments an lvalue and gives old
             value.

     --      Post-increment; decrements an lvalue and gives old
             value.


     Binary Operators


     @       Form a pointer.

     *       Multiplication, Set intersection.

     /       Division.

     %       Modulus.

     +       Addition, Set union.

     -       Subtraction, Set difference

     >>      Right shift (shift to lower significance)

     <<      Left shift (shift to higher significance)

     <       Logical test for less than, Proper subset

     >       Logical test for greater than, Proper superset

     <=      Logical test for less than or equal to, Subset

     >=      Logical test for greater than or equal to, Superset

     ==      Logical test for equality

     !=      Logical test for inequality

     ~       Logical test for regular expression match

     !~      Logical test for regular expression non-match

     ~~      Regular expression sub-string extraction

     ~~~     Regular expression multiple sub-string extraction

     &       Bit-wise and

     ^       Bit-wise exclusive or

     |       Bit-wise or

     &&      Logical and

     ||      Logical or

     :       Choice separator (must be right hand subject of ?
             operator)

     ?       Choice (right hand expression must use : operator)

     =       Assignment

     +=      Add to

     -=      Subtract from

     *=      Multiply by

     /=      Divide by

     %=      Modulus by

     >>=     Right shift by

     <<=     Left shift by

     &=      And by

     ^=      Exclusive or by

     |=      Or by

     ~~=     Replace by regular expression extraction

     <=>     Swap values

     ,       Multiple expression separator



     Pattern Matching


     Ici uses Henry Spencer's freely distributable regular
     expression package. Here is Henry's documentation on the
     syntax of regular expressions:

     A regular expression is zero or more branches, separated by
     `|'. It matches anything that matches one of the branches.

     A branch is zero or more pieces, concatenated. It matches a
     match for the first, followed by a match for the second,
     etc.

     A piece is a unit possibly followed by `*', `+', or `?'.  A
     unit followed by `*' matches a sequence of 0 or more matches
     of the unit.  A unit followed by `+' matches a sequence of 1
     or more matches of the unit.  A unit followed by `?' matches
     a match of the unit, or the null string.

     A unit is a regular expression in parentheses (matching a
     match for the regular expression), a range (see below), `.'
     (matching any single character), `^' (matching the null
     string at the beginning of the input string), `$' (matching
     the null string at the end of the input string), a `-' fol-
     lowed by a single character (matching that character), or a
     single character with no other significance (matching that
     character).

     A range is a sequence of characters enclosed in `[]'. It
     normally matches any single character from the sequence. If
     the sequence begins with `^', it matches any single charac-
     ter not from the rest of the sequence. If two characters in
     the sequence are separated by `-', this is shorthand for the
     full list of ASCII characters between them (e.g. `[0-9]'
     matches any decimal digit). To include a literal `]' in the
     sequence, make it the first character (following a possible
     `^'). To include a literal `-', make it the first or last
     character.

     Ambiguity

     If a regular expression could match two different parts of
     the input string, it will match the one which begins earli-
     est. If both begin in the same place but match different
     lengths, or match the same length in different ways, life
     gets messier, as follows.

     In general, the possibilities in a list of branches are con-
     sidered in left-to-right order, the possibilities for `*',
     `+', and `?' are considered longest-first, nested constructs
     are considered from the outermost in, and concatenated con-
     structs are considered leftmost-first. The match that will
     be chosen is the one that uses the earliest possibility in
     the first choice that has to be made. If there is more than
     one choice, the next will be made in the same manner (earli-
     est possibility) subject to the decision on the first
     choice. And so forth.

     For example, `(ab|a)b*c' could match `abc' in one of two
     ways. The first choice is between `ab' and `a'; since `ab'
     is earlier, and does lead to a successful overall match, it
     is chosen.  Since the `b' is already spoken for, the `b*'
     must match its last possibility the empty string since it
     must respect the earlier choice.

     In the particular case where no `|'s are present and there
     is only one `*', `+', or `?', the net effect is that the
     longest possible match will be chosen. So `ab*', presented
     with `xabbbby', will match `abbbb'. Note that if `ab*' is
     tried against `xabyabbbz', it will match `ab' just after
     `x', due to the begins-earliest rule. (In effect, the deci-
     sion on where to start the match is the first choice to be
     made, hence subsequent choices must respect it even if this
     leads them to less-preferred alternatives.)

     Legibility

     Since ici concatenates adjacent literal strings into a sin-
     gle string, since string variables can be concatenated with
     the + operator, and because the $ parse-time operator
     ensures that a function is only `compiled' into an internal
     form once, it is possible to rewrite complex #...# regular
     expressions into highly-legible equally-efficient forms.

     This means that regular expressions can be easily con-
     structed and maintained.  They are no longer ``write-only
     pieces of program''.  Here is a moderately complex example
     from an overly-simplistic C function header parser.
     (`my_text' contains something like "char * fred(float a,
     my_typ *b)".)

     static  parts;
     static  pat_ident  = "[A-Za-z_][A-Za-z0-9_]*";
     static  pat_white  = "[ \t]*";
     static  pat_wh_ptr = "[ \t*]*";
     static  reg;
     /*
      * parts       To hold the matched parts of the regular expression.
      * pat_ident   Pattern matching an identifier.
      * pat_white   Pattern matching any number of space or tab
      *             characters is white space.
      * pat_wh_ptr  Treat `*' as pseudo-space for simplicity!
      *             Note that the 1st * in with the white space
      *             is the literal C pointer indirection char.
      */

     /*
      * Find the function name...
      */
     func_name =
         my_text
         ~~
         regexp
         (
               "(" + pat_ident + ")"   /* The function name */
               + pat_white + "\\("     /* Anchored by the open-paren.  )*/
         );

     /*
      * Delete the function name, but leave the parenthesis as an anchor,
      * to find the return type.
      */
     my_text = gsub(my_text, func_name + pat_white + "\(", "(");
     reg =
         $regexp
         (
             "(" + pat_ident + ")"  /* Extract the return type */
             "(" + pat_wh_ptr + ")" /* White space + ptr-edness */
             + "\\("                /* Anchored by the open-paren.  )*/
         );
     parts = my_text ~~~ reg;

     returns_void = parts == NULL || parts[0] == "void";
     returns_ptr  = parts != NULL && parts[1] ~ #\*#;


     The if statement


     The if statement has two forms:

               if ( expression ) statement
               if ( expression ) statement else statement


     The parser converts both to an internal form.  Upon execu-
     tion, the expression is evaluated.  If the expression evalu-
     ates to anything other than 0 (integer zero) or NULL, the
     following statement is executed; otherwise it is not.  In
     the first form this is all that happens, in the second form,
     if the expression evaluated to 0 or NULL the statement fol-
     lowing the else is executed; otherwise it is not.


     The interpretation of both 0 and NULL as false, and anything
     else as true, is common to all logical operations in ici.
     There is no special boolean type.


     The ambiguity introduced by multiple if statements with a
     lesser number of else clauses is resolved by binding else
     clauses with their closest possible if.  Thus:


     if (a) if (b) do_x(); else do_y();



     is equivalent to:


     if (a)
     {
         if (b)
             do_x();
         else
             do_y();
     }


     The while statement


     The while statement has the form:


               while  ( expression ) statement


     The parser converts it to an internal form.  Upon execution
     a loop is established.  Within the loop the expression is
     evaluated, and if it is false (0 or NULL) the loop is ter-
     minated and flow of control continues after the while state-
     ment.  But if the expression evaluates to true (not 0 and
     not NULL) the statement is executed and then flow of control
     moves back to the start of the loop where the test is per-
     formed again (although other statements, as seen below, can
     be used to modify this natural flow of control).


     The do-while statement


     The do-while statement has the following form:


               do statement while ( expression ) ;


     The parser converts it to an internal form.  Upon execution
     a loop is established.  Within the loop the statement is
     executed.  Then the expression is evaluated and if it evalu-
     ates to true, flow of control resumes at the start of the
     loop.  Otherwise the loop is terminated and flow of control
     resumes after the do-while statement.


     The for statement


     The for statement has the form:


               for ( [ expression ]; [ expression ]; [ expression
     ] ) statement


     The parser converts it to an internal form.  Upon execution
     the first expression is evaluated (if present).  Then, a
     loop is established.  Within the loop: If the second expres-
     sion is present, it is evaluated and if it is false the loop
     is terminated.  Next the statement is executed.  Finally,
     the third expression is evaluated (if present) and flow of
     control resumes at the start of the loop.


     The forall statement


     The forall statement has the form:


               forall ( expression [ ,expression ] in expression
     ) statement


     The parser converts it to an internal form.  In doing so the
     first and second expressions are required to be lvalues
     (that is, capable of being assigned to).  Upon execution the
     first expression is evaluated and that storage location is
     noted.  If the second expression is present the same is done
     for it.  The third expression is then evaluated and the
     result noted; it must evaluate to an array, a set, a struct,
     a string, or NULL; we will call this the aggregate.  If this
     is NULL, the forall statement is finished and flow of con-
     trol continues after the statement; otherwise, a loop is
     established.


     Within the loop, an element is selected from the noted
     aggregate.  The value of that element is assigned to the
     location given by the first expression.  If the second
     expression was present, it is assigned the key used to
     access that element.  Then the statement is executed.
     Finally, flow of control resumes at the start of the loop.


     Each arrival at the start of the loop will select a dif-
     ferent element from the aggregate.  If no as yet unselected
     elements are left, the loop terminates.  The order of selec-
     tion is predictable for arrays and strings, namely first to
     last.  But for structs and sets it is unpredictable.  Also,
     while changing the values of the structure members is
     acceptable, adding or deleting keys, or adding or deleting
     set elements during the loop will have an unpredictable
     effect on the progress of the loop.


     Note in particular the interpretation of the value and key
     for a set.  For consistency with the access method and the
     behavior of structs and arrays, the values are all 1 and the
     elements are regarded as the keys. As a special case, when
     the second expression is omitted, the first is set to each
     "key" in turn, that is, the elements of the set.


     When a forall loop is applied to a string (which is not a
     true aggregate), the "sub-elements" will be successive one
     character sub-strings.


     Note that although the sequence of choice of elements from a
     set or struct is at first examination unpredictable, it will
     be the same in a second forall loop applied without the
     structure or set being modified in the interim.


     The switch, case, and default statements


     These statements have the form:

               switch ( expression ) compound-statement
               case expression :
               default :


     The parser converts the switch statement to an internal
     form.  As it is parsing the compound statement, it notes any
     case and default statements it finds at the top level of the
     compound statement.  When a case statement is parsed the
     expression is evaluated immediately by the parser.  As noted
     previously for parser evaluated expressions, it may perform
     arbitrary actions, but it is important to be aware that it
     is resolved to a particular value just once by the parser.
     As the case and default statements are seen their position
     and the associated expressions are noted in a table.


     Upon execution, the switch statement's expression is
     evaluated.  This value is looked up in the table created by
     the parser.  If a matching case statement is found, flow of
     control immediately moves to immediately after that case
     statement. On no match, if there is a default statement flow
     of control immediately moves to just after that.  If there
     is no matching case and no default statement, flow of con-
     trol continues just after the entire switch statement.


     The break and continue statements


     The break and continue statements have the form:

               break ;
               continue ;


     The parser converts these to an internal form.  Upon execu-
     tion of a break statement the execution engine will cause
     the nearest enclosing loop (a while, do, for or forall) or
     switch statement within the same scope to terminate.  Flow
     of control will resume immediately after the affected state-
     ment.  Note that a break statement without a surrounding
     loop or switch in the same function or module is illegal.


     Upon execution of a continue statement the execution engine
     will cause the nearest enclosing loop to move to the next
     iteration.  For while and do loops this means the test.  For
     for loops it means the step, then the test.  For forall
     loops it means the next element of the aggregate.


     The return statement


     The return statement has the form:


               return [ expression ] ;


     The parser converts this to an internal form.  Upon execu-
     tion, the execution engine evaluates the expression if it is
     present.  If it is not, the value NULL is substituted.  Then
     the current function terminates with that value as its
     apparent value in any expression it is embedded in.  It is
     an error for there to be no enclosing function.


     The try statement


     The try statement has the form:


               try  statement onerror statement


     The parser converts this to an internal form.  Upon execu-
     tion, the first statement is executed. If this statement
     executes normally flow continues after the try statement;
     the second statement is ignored.  But if an error occurs
     during the execution of the first statement control is
     passed immediately to the second statement.


     Note that "during the execution" applies to any depth of
     function calls, even to other modules or the parsing of
     sub-modules.  When an error occurs both the parser and exe-
     cution engine unwind as necessary until an error catcher
     (that is, a try statement) is found.


     Errors can occur almost anywhere and for a variety of rea-
     sons.  They can be explicitly generated with the fail func-
     tion (described below), they can be generated as a side-
     effect of execution (such as division by zero), and they can
     be generated by the parser due to syntax or semantic errors
     in the parsed source.  For whatever reason an error is gen-
     erated, a message (a string) is always associated with it.


     When any otherwise uncaught error occurs during the execu-
     tion of the first statement, two things are done:


     Firstly, the string associated with the failure is assigned
     to the variable error.  The assignment is made as if by a
     simple assignment statement within the scope of the try
     statement.


     Secondly, flow of control is passed to the statement follow-
     ing the onerror keyword.


     Once the second statement finishes execution, flow of con-
     trol continues as if the whole try statement had executed
     normally.


     The handling of errors which are not caught by any try
     statement is implementation dependent.  A typical action is
     to prepend the file and line number on which the error
     occurred to the error string, print this, and exit.


     The null statement


     The null statement has the form:

               ;

     The parser may convert this to an internal form. Upon execu-
     tion it will do nothing.


     Declaration statements


     There are two types of declaration statements:


     declaration
               storage-class declaration-list ;
               storage-class identifier function-body


     storage-class
               extern
               static
               auto


     declaration-list    identifier [ = expression ]
               declaration-list , identifier [ = expression ]


     That is, a comma separated list of identifiers, each with an
     optional initialisation, terminated by a semicolon.


     The storage class keyword establishes which scope the vari-
     ables in the list are established in. Note that declaring
     the same identifier at different scope levels is permissible
     and that they are different variables.


     A declaration with no initialisation first checks if the
     variable already exists at the given scope.  If it does, it
     is left unmodified.  In particular, any value it currently
     has is undisturbed.  If it does not exist it is established
     and is given the value NULL.


     A declaration with an initialisation establishes the vari-
     able in the given scope and gives it the given value even if
     it already exists and even if it has some other value.


     Note that initial values are parser evaluated expressions.
     That is they are evaluated immediately by the parser, but
     may take arbitrary actions apart from that.


     Abbreviated function declarations


     As seen above there are two forms of declaration.  The
     second:


               storage-class identifier function-body


     is the normal way to declare simple functions, and is a
     shorthand for:


               storage-class identifier = [ func function-body ]
     ;

     E.g.:
          static sum(a, b) { return a + b; }

     is a shorthand for:

          static sum = [func (a, b) { return a + b; }];

     Data types


     Ici supports a base set of standard data types.  Each is
     identified by a simple name.  In summary these are:


     array     An ordered sequence of other objects.

     file      An open file reference.

     float     A double precision floating point number.

     func      A function.

     int       A signed 32 bit integer.

     mem       References to raw machine memory.

     ptr       A reference to a storage location.

     regexp    A compiled regular expression.

     set       An unordered collection of other objects.

     string    An ordered sequence of 8 bit characters.

     struct    An unordered set of pairs of objects.

     socket    The communication socket type is also defined for
               ici on the Unix and Win32 platforms.

     window    The ASCII terminal-window type is also defined for
               ici on the Unix and Win32 platforms.


     Operators


     The following table is in precedence order.


     *ptr      Indirection: The result references the thing the
               pointer points to. The result is an lvalue.


     &any      Address of: The result is a pointer to any. If any
               is an lvalue the pointer references that storage
               location.  If any is not an lvalue but is a term
               other than a bracketed non-term, as described in
               the syntax above, a one element array containing
               any will be fabricated and a pointer to that
               storage location returned. For example:

                         p = &1;

               sets p to be a pointer to the first element of an
               un-named array, which currently contains the
               number 1.


     -num      Negation: Returns the negation of num. The result
               is the same type as the argument. The result is
               not an lvalue.


     +any      Has no effect except the result is not an lvalue.


     !any      Logical negation: If any is 0 (integer) or NULL, 1
               is returned, else 0 is returned.


     ~int      Bit-wise complement: The bit-wise complement of
               int is returned.


     ++any     Pre-increment: Equivalent to (any += 1). any must
               be an lvalue and obey the restrictions of the
               binary + operator.  See + below.


     --any     Pre-decrement: Equivalent to (any -= 1). any must
               be an lvalue and obey the restrictions of the
               binary - operator.  See - below.


     @any      Atomic form of: Returns the unique, read-only form
               of any.  If any is already atomic, it is returned
               immediately.  Otherwise an atomic form of any is
               found or generated and returned; this is of execu-
               tion time order equal to the number of elements in
               any.


     $any      Immediate evaluation: Recognised by the parser.
               The sub-expression any is immediately evaluated by
               invocation of the execution engine.  The result of
               the evaluation is substituted directly for this
               expression term by the parser.


     any++     Post-increment: Notes the value of any, then per-
               forms the equivalent of (any += 1), except any is
               only evaluated once, and finally returns the ori-
               ginal noted value.  any must be an lvalue and obey
               the restrictions of the binary + operator.  See +
               below.


     any--     Post-increment: Notes the value of any, then
               performs the equivalent of (any -= 1), except any
               is only evaluated once, and finally returns the
               original noted value.  any must be an lvalue and
               obey the restrictions of the binary - operator.
               See - below.


     any1 @ ideForm pointer: Returns a pointer object.  It is a
               pointer within the aggregate object any1.  It
               refers to the part of any1 keyed by the identif-
               ier.  E.g. [array 1, "fred", 3 ] @ "fred" yields a
               pointer to the 2nd element of the array.  Refer
               also to the section Method Calls.
               [Actually, it core dumps.]


     num1 * num2
               Multiplication: Returns the product of the two
               numbers, if both nums are ints, the result is int,
               else the result is float.


     set1 * set2
               Set intersection: Returns a set that contains all
               elements that appear in both set1 and set2.


     num1 / num2
               Division: Returns the result of dividing num1 by
               num2.  If both numbers are ints the result is int,
               else the result is float.  If num2 is zero the
               error division by 0 is generated, or division by
               0.0 if the result would have been a float.


     int1 % int2
               Modulus: Returns the remainder of dividing int1 by
               int2.  If int2 is zero the error modulus by 0 is
               generated.


     num1 + num2
               Addition: Returns the sum of num1 and num2.  If
               both numbers are ints the result is int, else the
               result is float.


     ptr + int Pointer addition: ptr must point to an element of
               an indexable object whose index is an int.
               Returns a new pointer which points to an element
               of the same aggregate which has the index which is
               the sum of ptr's index and int.  The arguments may
               be in any order.


     string1 + string2
               String concatenation: Returns the string which is
               the concatenation of the characters of string1
               then string2.  The execution time order is propor-
               tional to the total length of the result.


     array1 + array2
               Array concatenation: Returns a new array which is
               the concatenation of the elements from array1 then
               array2.  The execution time order is  proportional
               to the total length of the result. Note the
               difference between the following:

                         a += [array 1];
                         push(a, 1);

               In the first case a is replaced by a newly formed
               array which is the original array with one element
               added.  But in the second case the push function
               (see below) appends an element to the array a
               refers to, without making a new array. The second
               case is much faster, but modifies an existing
               array.


     struct1 + struct2
               Structure concatenation: Returns a new struct
               which is a copy of struct1, with all the elements
               of struct2 assigned into it.  Obeys the semantics
               of copying and assignment discussed in other sec-
               tions with regard to super structs.  The execution
               time order is proportional to the sum of the
               lengths of the two arguments.


     set1 + set2
               Set union: Returns a new set which contains all
               the elements from both sets.  The execution time
               order is  proportional to the sum of the lengths
               of the two arguments.


     num1 - num2
               Subtraction: Returns the result of subtracting
               num2 from num1.  If both numbers are ints the
               result is int, else the result is float.


     set1 - set2
               Set subtraction: Returns a new set which contains
               all the elements of set1, less the elements of
               set2. The execution time order is proportional to
               the sum of the lengths of the two arguments.


     ptr1 - ptr2
               Pointer subtraction: ptr1 and ptr2 must point to
               elements of indexable objects whose indexs are
               ints.  Returns an int which is the index of ptr1
               less the index of ptr2.


     int1 >> int2
               Right shift: Returns the result of right shifting
               int1 by int2.  Equivalent to division by 2**int2.
               int1 is interpreted as a signed quantity.


     int1 << int2
               Left shift: Returns the result of left shifting
               int1 by int2.  Equivalent to multiplication by
               2**int2.


     array << int
               Left shift array: Returns a new array which
               contains the elements of array from index int
               onwards.  Equivalent to the function call
               interval(array, int) (which is considered prefer-
               able, this operator may disappear in future
               releases).


     num1 < num2
               Numeric test for less than: Returns 1 if num1 is
               less than num2, else 0.


     set1 < set2
               Test for subset: Returns 1 if set1 contains only
               elements that are in set2, else 0.


     string1 < string2
               Lexical test for less than: Returns 1 if string1
               is lexically less than string2, else 0.


     ptr1 < ptr2
               Pointer test for less than:  ptr1 and ptr2 must
               point to elements of indexable objects whose
               indexes are ints.  Returns 1 if ptr1 points to an
               element with a lesser index than ptr2, else 0.

               The >, <= and >= operators work in the same
               fashion as <, above. For sets > tests for one set
               being a superset of the other.  The < and > opera-
               tors test for proper sub- or super- sets.  That
               is, one set can contain only those elements con-
               tained in the other set but cannot be equal to the
               other set.  The <= and >= operators test for sub-
               or super- sets.  That is one set can contain ele-
               ments contained in the other set and can equal the
               other set.


     any1 == any2
               Equality test: Returns 1 if any1 is equal to any2,
               else 0.  Two objects are equal when: they are the
               same object; or they are both arithmetic (int and
               float) and have equivalent numeric values; or they
               are aggregates of the same type and all the sub-
               elements are the same objects.


     any1 != any2
               Inequality test: Returns 1 if any1 is not equal to
               any2, else 0.  See above.


     string ~ regexp
               Logical test for regular expression match: Returns
               1 if string can be matched by regexp, else 0.  The
               arguments may be in any order.


     string !~ regexp
               Logical test for regular expression non-match:
               Returns 1 if string can not be matched by regexp,
               else 0.  The arguments may be in any order.


     string ~~ regexp
               Regular expression sub-string extraction: Returns
               the sub-string of string which is matched by the
               first bracket enclosed portion of regexp, or NULL
               if there is no match or regexp does not contain a
               (...) portion. The arguments may be in any order.
               For example, a "basename" operation can be
               performed with:

                         argv[0] ~~= #([^/]*)$#;


     string ~~~ regexp
               Regular expression multiple sub-string extraction:
               Returns an array of the the sub-strings of string
               which are matched by the (...) enclosed portions
               of regexp, or NULL if there is no match. The argu-
               ments may be in any order.


     int1 & int2
               Bit-wise and: Returns the bit-wise and of int1 and
               int2.


     int1 ^ int2
               Bit-exclusive or: Returns the bit-wise exclusive
               or of int1 and int2.


     int1 | int2
               Bit-wise or: Returns the bit-wise or of int1 and
               int2.


     any1 && any2
               Logical and: Evaluates the expression which deter-
               mines any1, if this evaluates to 0 or NULL (i.e.
               false), 0 is returned, else any2 is evaluated and
               returned. Note that if any1 does not evaluate to a
               true value, the expression which determines any2
               is never evaluated.


     any1 || any2
               Logical or: Evaluates the expression which deter-
               mines any1, if this evaluates to other than 0 or
               NULL (i.e. true), 1 is returned, else any2 is
               evaluated and returned. Note that if any1 does not
               evaluate to a false value, the expression which
               determines any2 is never evaluated.


     any1 ? any2 : any3
               Choice: If any1 is neither 0 or NULL (i.e. true),
               the expression which determines  any2 is evaluated
               and returned, else the expression which determines
               any3 is evaluated and returned.  Only one of any2
               and any3 are evaluated.  The result may be an
               lvalue if the returned expression is. Thus:

                         flag ? a : b = value

               is a legal expression and will assign value to
               either a or b depending on the state of flag.


     any1 = any2
               Assignment: Assigns any2 to any1.  any1 must be an
               lvalue. The behavior of assignment is a conse-
               quence of aggregate access as discussed in earlier
               sections.  In short, an lvalue (in this case any1)
               can always be resolved into an aggregate and an
               index into the aggregate.  Assignment sets the
               element of the aggregate identified by the index
               to any2.  The returned result of the whole assign-
               ment is any1, after the assignment has been per-
               formed.

               The result is an lvalue, thus:

                         ++(a = b)

               will assign b to a and then increment a by 1.

               Note that assignment operators (this and following
               ones) associate right to left, unlike all other
               binary operators, thus:

                         a = b += c -= d

               Will subtract d from c, then add the result to b,
               then assign the final value to a.


     +=  -=  *=  /=  %=  >>=  <<=  &=  ^=  |=  ~~=
               Compound assignments: All these operators are
               defined by the rewriting rule:

                         any1 op= any2

               is equivalent to:

                         any1 = any1 op any2

               except that any1 is not evaluated twice. Type res-
               trictions and the behaviour or op will follow the
               rules given with that binary operator above. The
               result will be an lvalue (as a consequence of =
               above).  There are no further restrictions.  Thus:

                         a = "Hello";
                         a += " world.\n";

               will result in the variable a referring to the
               string:

                         "Hello world.\n"


     any1 <=> any2
               Swap: Swaps the current values of any1 and any2.
               Both operands must be lvalues. The result is any1
               after the swap and is an lvalue, as in other
               assignment operators.  Also like other assignment
               operators, associativity is right to left, thus:

                         a <=> b <=> c <=> d

               rotates the values of a, b and c towards d and
               brings d's original value back to a.

     any1 , any2
               Sequential evaluation: Evaluates any1, then any2.
               The result is any2 and is an lvalue if any2 is.
               Note that in situations where comma has meaning at
               the top level of parsing an  expression (such as
               in function call arguments), expression parsing
               precedence starts at one level below the comma,
               and a comma will not be recognised as an operator.
               Surround the expression with brackets to avoid
               this if necessary.


     Method Calls


     Ici's support for object-oriented programming is very sim-
     ple.  Inheritance is already provided through the ability to
     extend one structure's scope by defining a super structure
     for it.  This can be done either with the notation:

         static  my_instance = [struct:my_parent x, y ];

     or with the functional notation

         static  my_instance = super(my_parent, x, y);

     although it's probably better to set up an atomic (`class')
     variable for the super structure, and use that:

         @my_parent

     to refer to an atomic (unchanging) version of your `class'
     struct.


     The method call is achieved by an extension of the normal
     function call operator "()".  When the "()" operand is a
     function identifier, the function is called in the usual way
     with the supplied parameter list.  But if the "()" operand
     is a pointer object keyed by a function identifier, the
     function is called with the object pointed-to passed as an
     implicit first parameter.

     The binary operator `@' forms such a pointer from an aggre-
     gate object (usually a struct) and an identifier (i.e. a
     member of the struct).  This is the same basic technique
     used for dynamic dispatch in languages like Smalltalk and
     Objective-C.


     Here is a little example showing a few ways this can be
     used.  Note the occasional use of literal functions.

     static
     showtype(any, label)
     {
         printf("%s is a %s\n", label, typeof(any));
     }

     /*
      * Class definition.
      */
     static Point =
     [struct
         p_x     = 0.0,
         p_y     = 0.0,
         p_swap  = [func (self) { self.p_x <=> self.p_y; } ],
         p_print = [func (s) { printf("Pt <%g %g>\n", s.p_x, s.p_y);} ],
         p_what  = showtype,
     ];

     static p1 = struct(@Point);  /* Set super struct to be atomic Point */

         p1.p_x = 1.2;
         p1.p_y = 3.4;

         p1@p_what("p1");
         p1@p_print();
         p1@p_swap();
         p1@p_print();


     Which produces this output when run:

     p1 is a struct
     Pt <1.2 3.4>
     Pt <3.4 1.2>


     In summary, method calls depend on:

     +       Ici's super linkage to provide the normal OO inheri-
             tance name search mechanism.

     +       The `@' binary operator to form a keyed pointer that
             will select the named member of the struct.

     +       The "()" (call) operator's special treatment of a
             function-keyed pointer.

     The syntax is the natural result of using ici's normal
     language facilities.


     Standard Functions

     The following list summarises the standard functions.  Fol-
     lowing this is a detailed description of each of them.  This
     is not the complete list of functions - system functions
     (like sleep(), access(), etc.), and socket functions are
     described in their own sections.


               float =   acos(number)
               mem =     alloc(int [, int])
               array =   array(any...)
               float =   asin(number)
               any =     assign(struct, any, any)
               float =   atan(number)
               float =   atan2(number, number)
               any =     call(func, array)
               any =     call(pointer, array)
               float =   ceil(number)
                         close(file)
               any =     copy(any)
               float =   cos(number)
               file =    currentfile()
                         del(struct, any)
               int =     eq(any, any)
               int =     eof(file)
                         exit([int|string|NULL])
               float =   exp(number)
               array =   explode(string)
                         fail(string)
               any =     fetch(struct, any)
               float =   float(any)
               float =   floor(number)
               int =     flush(file)
               float =   fmod(number, number)
               file =    fopen(string [, string])
                         flush([file])
               string =  getchar([file])
               string =  getfile([file])
               string =  getline([file])
               string =  getenv(string)
               string =  gettoken([file|string [,string]])
               array =   gettokens([file|string [,string
     [,string]]])
               string =  gsub(string, regexp, string)
               string =  implode(array)
               struct =  include(string [, struct])
               int =     int(any)
               string =  interval(string, int [, int])
               array =   interval(array, int [, int])
               int =     isatom(any)
               array =   keys(struct)
               float =   log(number)
               float =   log10(number)
               mem =     mem(int, int [,int])
               file =    mopen(string [, string])
               int =     nels(any)
               int|float =         num(string|int|float)
               struct =  parse(file|string [, struct])
               any =     pop(array)
               file =    popen(string [, string])
               float =   pow(number, number)
                         printf([file,] string [, any...])
               any =     push(array, any)
                         put(string)
                         putenv(string [, string])
               int =     rand([int])
                         reclaim()
               regexp =  regexp(string)
               regexp =  regexpi(string)
                         remove(string)
               struct =  scope([struct])
               int =     seek(file, int, int)
               set =     set(any...)
               float =   sin(number)
               int =     sizeof(any)
               array =   smash(string, string)
               file =    sopen(string [, string])
                         sort(array, func)
               string =  sprintf(string [, any...])
               float =   sqrt(number)
               string =  string(any)
               struct =  struct(any, any...)
               string =  sub(string, regexp, string)
               struct =  super(struct [, struct])
               int =     system(string)
               float =   tan(number)
               string =  tochar(int)
               int =     toint(string)
               any =     top(array [, int])
               int =     trace(string)
               string =  typeof(any)
               array =   vstack()
               file =    waitfor(file...)
               int =     waitfor(int...)
               float =   waitfor(float...)



     The following is an alphabetic listing of each of the stan-
     dard functions.

     angle = acos(x)

     Returns the arc cosine of x in the range 0 to pi.

     mem = alloc(nwords [, wordz])

     Returns a new mem object referring to nwords (an int) of
     newly allocated and cleared memory.  Each word is either 1,
     2, or 4 bytes as specified by wordz  (an int, default 1).
     Indexing of mem objects performs the obvious operations, and
     thus pointers work too.

     array = array(any...)

     Returns an array formed from all the arguments. For example:

     array()

     will return a new empty array; and

     array(1, 2, "a string")

     will return a new array with three elements, 1, 2, and "the
     string".

     This is the run-time equivalent of the array literal. Thus
     the following two expressions are equivalent:

     $array(1, 2, "a string")

     [array 1, 2, "a string"]

     float = asin(x)

     Returns the arc sine of x  in the range -pi/2 to pi/2.

     value = assign(struct, key, value)

     Sets the element of struct identified by key to value,
     ignoring any super struct.  Returns value.

     angle = atan(x)

     Returns the arc tangent of x  in the range -pi/2 to pi/2.

     angle = atan2(y, x)

     Returns the angle from the origin to the rectangular coordi-
     nates x, y (floats) in the range -pi to pi.

     return = call(func, args)
     return =call(pointer, args)

     In the first form, this calls the function func with argu-
     ments taken from the array args.  Returns the return value
     of the function.

     This is often used to pass on an unknown argument list.  For
     example:

     static
     db()
     {
         auto vargs;

         if (debug)
             return call(printf, vargs);
     }

     new = copy(old)

     Returns a copy of old.  If old is an intrinsically atomic
     type such as an int or string, the new will be the same
     object as the old.  But if old is an array, set, or struct,
     a copy will be returned.  The copy will be a new non-atomic
     object (even if old was atomic) which will contain exactly
     the same objects as old and will be equal to it (that is
     ==).  If old is a struct with a super struct, new will have
     the same super (exactly the same super, not a copy of it).

     x = cos(angle)

     Returns the cosine of angle (a float interpreted in radi-
     ans).

     file = currentfile()

     Returns the file associated with the innermost parsing con-
     text, or NULL if there is no module being parsed.

     This function can be used to include data in a program
     source file which is out-of-band with respect to the normal
     parse stream.  But to do this it is necessary to know up to
     what character in the file in question the parser has con-
     sumed.

     In general: after having parsed any simple statement the
     parser will have consumed up to and including the terminat-
     ing semicolon, and no more.  Also, after having parsed a
     compound statement the parser will have consumed up to and
     including the terminating close brace and no more.  For
     example:

     static help = gettokens(currentfile(), "", "!")[0]

     ;This is the text of the help message.
     It follows exactly after the ; because
     that is exactly up to where the parser
     will have consumed. We are using the
     gettokens() function (as described below)
     to read the text.
     !

     static otherVariable = "etc...";

     This function can also be used to parse the rest of a module
     within an error catcher.  For example:

     try
         parse(currentfile(), scope())
     onerror
         printf("That didn't work, but never mind.\n");

     static this = that;
     etc();

     The functions  parse and scope are described below.

     del(struct, key)

     Deletes the element of struct identified by key. Any super
     structs are ignored.  Returns NULL.  For example:

     static s = [struct a = 1, b = 2, c = 3];
     static v, k;

     forall (v, k in s)
         printf("%s=%d\n", k, v);
     del(s, "b");
     printf("\n");
     forall (v, k in s)
         printf("%s=%d\n", k, v);

     When run would produce (possibly in some other order):

     a=1
     c=3
     b=2

     a=1
     c=3

     int = eof([file])

     Returns non-zero if end of file has been read on file. If
     file is not given the current value of stdin in the current
     scope is used.

     eq(obj1, obj2)

     Returns 1 (one) if obj1 and obj2 are the same object, else 0
     (zero).

     exit([string|int|NULL])

     Causes the interpreter to finish execution and exit. If no
     parameter or the empty string or NULL is passed, the exit
     status is zero. If an integer is passed that is the exit
     status. If a non-empty string is passed then that string is
     printed to the interpreter's standard error output and an
     exit status of one used.  This is implementation dependent
     and may be replaced by a more general exception mechanism.
     Avoid.

     float = exp(x)

     Returns the exponential function of x.

     array = explode(string)

     Returns an array containing each of the integer character
     codes of the characters in string.

     fail(string)

     Causes an error to be raised with the message string associ-
     ated with it.  See the section of error handling in the try
     statement above.  For example:

     if (qf > 255)
         fail(sprintf("Q factor %d is too large", qf));

     value = fetch(struct, key)

     Returns the value from struct associated with key, ignoring
     any super structs. Returns NULL if the key is not an element
     of struct.

     value = float(x)

     Returns a floating point interpretation of x, or 0.0 if no
     reasonable interpretation exists. x should be an int, a
     float, or a string, else 0.0 will be returned.

     file = fopen(name [, mode])

     Opens the named file for reading or writing according to
     mode and returns a file object that may be used to perform
     I/O on the file. Mode is the same as in C and is passed
     directly to the C library fopen function. If mode is not
     specified "r" is assumed.

     fprintf(file, fmt, args...)

     Formats a string based on fmt and args as per sprintf
     (below) and outputs the result to file.  See sprintf.
     Changes to ici's printf have made fprintf redundant and it
     may be removed in future versions of the interpreter. Avoid.

     string = getchar([file])

     Reads a single character from file and returns it as a
     string. Returns NULL upon end of file. If file is not given
     the current value of stdin in the current scope is used.

     string = getfile([file])

     Reads all remaining data from file and returns it as a
     string. Returns an empty string upon end of file. If file is
     not given the current value of stdin in the current scope is
     used.

     string = getline([file])

     Reads a line of text from file and returns it as a string.
     Any end-of-line marker is removed.  Returns NULL upon end of
     file. If file is not given the current value of stdin in the
     current scope is used.

     string = gettoken([file [, seps]])

     Read a token (that is, a string) from file.

     Seps must be a string.  It is interpreted as a set of char-
     acters which do not from part of the token.  Any leading
     sequence of these characters is first skipped.  Then a
     sequence of characters not in seps is gathered until end of
     file or a character from seps is found.  This terminating
     character is not consumed.  The gathered string is returned,
     or NULL if end of file was encountered before any token was
     gathered.

     If file is not given the current value of stdin in the
     current scope is used.

     If seps is not given the string " \t\n" is assumed.

     array = gettokens([file [, seps [, terms]]])

     Read tokens (that is, strings) from file.  The tokens are
     character sequences separated by seps and terminated by
     terms.  Returns an array of strings, NULL on end of file.

     If seps is a string, it is interpreted as a set of charac-
     ters, any sequence of which will separate one token from the
     next.  In this case leading and trailing separators in the
     input stream are discarded.

     If seps is an integer it is interpreted as a character code.
     Tokens are taken to be sequences of characters separated by
     exactly one of that character.

     Terms must be a string.  It is interpreted as a set of char-
     acters, any one of which will terminate the gathering of
     tokens.  The character which terminated the gathering will
     be consumed.

     If file is not given the current value of stdin in the
     current scope will be used.

     If seps is not given the string " \t" is assumed.

     If terms is not given the string "\n" is assumed.

     For example:

     forall (token in gettokens(currentfile()))
         printf("<%s>", token)
     ;   This    is my line    of data.
     printf("\n");

     when run will print:

     <This><is><my><line><of><data.>

     Whereas:

     forall (token in gettokens(currentfile(), ':', "*"))
               printf("<%s>", token)
     ;:abc::def:ghi:*
     printf("\n");

     when run will print:

     <><abc><><def><ghi><>

     string = gsub(string, string|regexp, string)

     gsub performs text substitution using regular expressions.
     It takes the first parameter, matches it against the second
     parameter and then replaces the matched portion of the
     string with the third parameter. If the second parameter is
     a string it is converted to a regular expression as if the
     regexp function had been called. Gsub does the replacement
     multiple times to replace all occurrances of the pattern. It
     returns the new string formed by the replacement. If there
     is no match this is original string. The replacement string
     may contain the special sequence "\&" which is replaced by
     the string that matched the regular expression.
     Parenthesized portions of the regular expression may be
     matched by using \n where n is a decimal digit.

     string = implode(array)

     Returns a string formed from the concatenation of elements
     of array.  Integers in the array will be interpreted as
     character codes; strings in the array will be included in
     the concatenation directly.  Other types are ignored.

     struct = include(string [, scope])

     Parses the code contained in the file named by the string
     into the scope. If scope is not passed the current scope is
     used. Include always returns the scope into which the code
     was parsed. The file is opened by calling the current defin-
     ition of the ici fopen() function so path searching can be
     implemented by overriding that function.

     value = int(any)

     Returns an integer interpretation of x, or 0 if no reason-
     able interpretation exists. x should be an int, a float, or
     a string, else 0 will be returned.

     subpart = interval(str_or_array, start [, length])

     Returns a sub-interval of str_or_array, which may be either
     a string or an array.

     If start (an integer) is positive the sub-interval starts at
     that offset (offset 0 is the first element).  If start is
     negative the sub-interval starts that many elements from the
     end of the string (offset -1 is the last element, -2 the
     second last etc).

     If length is absent, all the elements from the start are
     included in the interval.  Otherwise that many elements are
     included (or till the end, whichever is smaller).

     For example, the last character in a string can be accessed
     with:

     last = interval(str, -1);

     And the first three elements of an array with:

     first3 = interval(ary, 0, 3);

     isatom(any)

     Return 1 (one) if any is an atomic (read-only) object, else
     0 (zero).  Note that integers, floats and strings are always
     atomic.

     array = keys(struct)

     Returns an array of all the keys from struct.  The order is
     not predictable, but is repeatable if no elements are added
     or deleted from the struct between calls and is the same
     order as taken by a forall loop.

     float = log(x)

     Returns the natural logarithm of x (a float).

     float = log10(x)

     Returns the log base 10 of x (a float).

     mem = mem(start, nwords [, wordz])

     Returns a memory object which refers to a particular area of
     memory in the ici interpreter's address space.  Note that
     this is a highly dangerous operation.  Many implementations
     will not include this function or restrict its use.  It is
     designed for diagnostics, embedded systems and controllers.
     See the alloc function above.

     file = mopen(mem [, mode])

     Returns a file, which when read will fetch successive bytes
     from the given memory object.  The memory object must have
     an access size of one (see alloc and mem above). The file is
     read-only and the mode, if passed, must be one of "r" or
     "rb".

     int = nels(any)

     Returns the number of elements in any.  The exact meaning
     depends on the type of any.  If any is an:


     array     the length of the array is returned; if it is a

     struct    the number of key/value pairs is returned; if it
               is a

     set       the number of elements is returned; if it is a

     string    the number of characters is returned; and if it is
               a

     mem       the number of words (either 1, 2 or 4 byte quanti-
               ties) is returned;

     and if it is anything else, one is returned.


     number = num(x)

     If x is an int or float, it is returned directly.  If x is a
     string it will be converted to an int or float depending on
     its appearance; applying octal and hex interpretations
     according to the normal ici source parsing conventions.
     (That is, if it starts with a 0x it will be interpreted as a
     hex number, else if it starts with a 0 it will be inter-
     preted as an octal number, else it will be interpreted as a
     decimal number.)

     If x can not be interpreted as a number the error %s is not
     a number is generated.

     scope = parse(source [, scope])

     Parses source in a new variable scope, or, if scope (a
     struct) is supplied, in that scope.  Source may either be a
     file or a string, and in either case it is the source of
     text for the parse.  If the parse is successful, the vari-
     ables scope structure of the sub-module is returned.  If an
     explicit scope was supplied this will be that structure.

     If scope is not supplied a new struct is created for the
     auto variables.  This structure in turn is given a new
     structure as its super struct for the static variables.
     Finally, this structure's super is set to the current static
     variables.  Thus the static variables of the current module
     form the externs of the sub-module.

     If scope is supplied it is used directly as the scope for
     the sub-module.  Thus the base structure will be the struct
     for autos, its super will be the struct for statics etc.

     For example:


     static x = 123;
     parse("static x = 456;", scope());
     printf("x = %d\n", x);

     When run will print:

     x = 456

     Whereas:

     static x = 123;
     parse("static x = 456;");
     printf("x = %d\n", x);

     When run will print:

     x = 123

     Note that while the following will work:

     parse(fopen("my-module.ici"));

     It is preferable in a large program to use:

     parse(file = fopen("my-module.ici"));
     close(file);

     In the first case the file will eventually be closed by gar-
     bage collection, but exactly when this will happen is
     unpredictable. The underlying system may only allow a lim-
     ited number of simultaneous open files.  Thus if the program
     continues to open files in this fashion a system limit may
     be reached before the unused files are garbage collected.

     any = pop(array)

     Returns the last element of array and reduces the length of
     array by one.  If the array was empty to start with, NULL is
     returned.

     file = popen(string, [flags])

     Executes a new process, specified as a shell command line as
     for the system function, and returns a file that either
     reads or writes to the standard input or output of the pro-
     cess according to mode. If mode is "r" the reading from the
     file reads from the standard output of the process. If mode
     is "w" writing to the file writes to the standard input of
     the process. If mode is not speicified it defaults to "r".

     float = pow(x, y)

     Returns x^y where both x and y are floats.

     printf([file,] fmt, args...)

     Formats a string based on fmt and args as per sprintf
     (below) and outputs the result to the file or to the current
     value of the stdout variable in the current scope if the
     first parameter is not a file.  The current stdout must be a
     file.  See sprintf.

     any = push(array, any)

     Appends any to array, increasing its length in the process.
     Returns any.

     put(string [, file])

     Outputs string to file. If file is not passed the current
     value of stdout in the current scope is used.

     int = rand([seed])

     Returns an pseudo random integer in the range 0..0x7FFF.  If
     seed (an int) is supplied the random number generator is
     first seeded with that number.  The sequence is predictable
     based on a given seed.

     reclaim()

     Force a garbage collection to occur.  (This should not be
     needed in normal operation.)

     re = regexp(string)

     Returns a compiled regular expression derived from string
     This is the method of generating regular expressions at
     run-time, as opposed to the direct lexical form. For exam-
     ple, the following three expressions are similar:

     str ~ #.*\.c#
     str ~ regexp(".*\\.c");
     str ~ $regexp(".*\\.c");

     except that the middle form computes the regular expression
     each time it is executed.  Note that when a regular expres-
     sion includes a # character the regexp function must be
     used, as the direct lexical form has no method of escaping a
     #.

     Note that regular expressions are intrinsically atomic.
     Also note that non-equal strings may sometimes compile to
     the same regular expression.

     re = regexpi(string)

     Returns a compiled regular expression derived from string
     that is case-insensitive. I.e., the regexp will match a
     string regardless of the case of alphabetic characters. Note
     that there is no literal form of regular expressions that
     has this property.

     remove(string)

     Deletes the file whose name is given in string.

     current = scope([replacement])

     Returns the current scope structure.  This is a struct whose
     base element holds the auto variables, the super of that
     hold the statics, the super of that holds the externs etc.
     Note that this is a real reference to the current scope
     structure.  Changing, adding and deleting elements of these
     structures will affect the values and presence of variables
     in the current scope.

     If a replacement is given, that struct  replaces the current
     scope structure, with the obvious implications.  This should
     clearly be used with caution.  Replacing the current scope
     with a structure which has no reference to the standard
     functions also has the obvious effect.

     int = seek(file, int, int)

     Set the input/output position for a file and returns the new
     I/O position or -1 if an error ocurred. The arguments are
     the same as for the C library's fseek function. If the file
     object does not support setting the I/O position or the seek
     operation fails an error is raised.

     set = set(any...)

     Returns a set formed from all the arguments. For example:

     set()

     will return a new empty set; and

     set(1, 2, "a string")

     will return a new set with three elements, 1, 2, and "the
     string".

     This is the run-time equivalent of the set literal. Thus the
     following two expressions are equivalent:

     $set(1, 2, "a string")

     [set 1, 2, "a string"]

     x = sin(angle)

     Returns the sine of angle (a float interpreted in radians).

     int = sizeof(any)

     Sizeof is the old name of the nels function (described
     above).

     file = sopen(string [, mode])

     Returns a file, which when read will fetch successive char-
     acters from the given string. The file is read-only and the
     mode, if passed, must be one of "r" or "rb".

     Files are, in general, system dependent.  This is the only
     standard routine which opens a file.  But on systems that
     support byte stream files, the function fopen will be set to
     the most appropriate method of opening a file for general
     use. The interpretation of mode is largely system dependent,
     but the strings "r", "w", and "rw" should be used for read,
     write, and read-write file access respectively.

     sort(array, func)

     Sort the content of the array using the heap sort algorithm
     with func as the comparison function. The comparison func-
     tion is called with two elements of the array as parameters,
     a and b. If a is equal to b the function should return zero.
     If a is less than b, -1, and if a is greater than b, 1.

     For example,

     static
     cmp(a, b)
     {
         if (a == b)
             return 0;
         if (a < b)
             return -1;
         return 1;
     }

     static a = array(1, 3, 2);

     sort(a, cmp);


     string = sprintf(fmt, args...)

     Return a formatted string based on fmt (a string) and args.
     Most of the usual % format escapes of ANSI C printf are sup-
     ported.  In particular; the integer format letters diouxXc
     are supported, but if a float is provided it will be con-
     verted to an int.  The floating point format letters feEgG
     are supported, but if the argument is an int it will be con-
     verted to a float.  The string format letter, s is supported
     and requires a string.  Finally the % format to get a single
     % works.

     The flags, precision, and field width options are supported.
     The indirect field width and precision options with * also
     work and the corresponding argument must be an int.

     For example:

     sprintf("%08X <%4s> <%-4s>", 123, "ab", "cd")

     will produce the string:

     0000007B <  ab> <cd  >

     and

     sprintf("%0*X", 4, 123)

     will produce the string:

     007B

     x = sqrt(float)

     Returns the square root of float.

     string = string(any)

     Returns a short textual representation of any. If any is an
     int or float it is converted as if by a %d or %g format.  If
     it is a string it is returned directly.  Any other type will
     returns its type name surrounded by angle brackets, as in
     <struct>.

     struct = struct([super,] key, value...)

     Returns a new structure.  This is the run-time equivalent of
     the struct literal.  If there are an odd number of arguments
     the first is used as the super of the new struct; it must be
     a struct.  The remaining pairs of arguments are treated as
     key and value pairs to initialise the structure with; they
     may be of any type.  For example:

     struct()

     returns a new empty struct;

     struct(anotherStruct)

     returns a new empty struct which has anotherStruct as its
     super;

     struct("a", 1, "b", 2)

     returns a new struct which has two entries a and b with the
     values 1 and 2; and

     struct(anotherStruct, "a", 1, "b", 2)

     returns a new struct which has two entries a and b with the
     values 1 and 2 and a super of anotherStruct.

     Note that the super of the new struct is set after the
     assignments of the new elements have been made. Thus the
     initial elements given as arguments will not affect values
     in any super struct.

     The following two expressions are equivalent:

     $struct(anotherStruct, "a", 1, "b", 2)

     [struct:anotherStruct, a = 1, b = 2]

     string = sub(string, string|regexp, string)

     sub() performs text substitution using regular expressions.
     It takes the first parameter, matches it against the second
     parameter and then replaces the matched portion of the
     string with the third parameter. If the second parameter is
     a string it is converted to a regular expression as if the
     regexp function had been called. Sub does the replacement
     once (unlike gsub). It returns the new string formed by the
     replacement. If there is no match this is original string.
     The replacement string may contain the special sequence "\&"
     which is replaced by the string that matched the regular
     expression. Parenthesized portions of the regular expression
     may be matched by using \n where n is a decimal digit.

     current = super(struct [, replacement])

     Returns the current super struct of struct, and, if replace-
     ment is supplied, sets it to a new value.  If replacement is
     NULL any current super struct reference is cleared (that is,
     after this struct will have no super).

     x = tan(angle)

     Returns the tangent of angle (a float interpreted in radi-
     ans).

     string = tochar(int)

     Returns a one character string made from the character code
     specified by int.


     int = toint(string)

     Returns the character code of the first character of string.

     string = typeof(any)

     Returns the type name (a string) of any.  See the section on
     types above for the possible type names.

     array = vstack()

     Returns a representation of the call stack of the current
     program at the time of the call. It can be used to perform
     stack tracebacks and related debugging operations. The
     result is an array of structures, each of which is a vari-
     able scope (see scope) structure of successively deeper
     nestings of the current function nesting.

     event = waitfor(event...)

     Blocks (waits) until an event indicated by any of its argu-
     ments occurs, then returns that argument.  The interpreta-
     tion of an event depends on the nature of each argument.  A
     file argument is triggered when input is available on the
     file. A float argument waits for that many seconds to
     expire, an int for that many millisecond (they then return
     0, not the argument given). Other interpretations are imple-
     mentation dependent. Where several events occur simultane-
     ously, the first as listed in the arguments will be
     returned.

     Note that in some implementations some file types may always
     appear ready for input, despite the fact that they are not.


     Pointers

     Pointers in ici work basically like pointers in C - but are
     implemented as objects in their own right.  Pointers in ici
     are completely safe.

     An ici pointer consists of two parts, the object being
     pointed to, and a key to indicate the specific part of the
     object.

     Integers can be added to or subtracted from a pointer if the
     object is indexed by an integer (like an array or a string).

         auto x = [array 1, "fred", 3];
         auto    p = &x[0];
         ++p;
         printf("*p is a %s0, typeof(*p));
     *p is a string

         x = "Hello!";
         p = &x[0];
         printf("*p is %s0, *p);
     *p is H
         p += 5;
         printf("*p is %s0, *p);
     *p is !



     Unix System Calls

     Most Unix implementation of ici provide access to many of
     the Unix system calls and other useful C library functions.
     Note that not all system calls are supported and those that
     are may be incompletely supported (e.g., signal). Most sys-
     tem call functions return integers, zero if the call suc-
     ceeded. Errors are reported using ici's error handling and
     "system calls" will never return the -1 error return value.
     If an error is raised by a system call the value of "error"
     in the error handler will be the error message (as printed
     by the perror(3) function or returned by the ANSI C strer-
     ror() function).

     To assist in the use of system calls ici pre-defines vari-
     ables to hold the various flags and other values used when
     calling the system calls. These variables are equivalent to
     the macros used in C. Not all systems support all these
     variables. If the C header files do not define a value then
     ici will not pre-define the variable.

     Win32 Support

     The version of ici for Microsoft's 32-bit Windows platforms
     (Win32) supports many of these functions. Functions sup-
     ported on Win32 platforms (Windows 95 and Windows NT) are
     marked with WIN32. In addition some functions are only
     available on Win32 platforms and are marked as so.

     The following list summarises the Unix system call interface
     pre-defined variables. See the documentation for the C mac-
     ros for information as to their use.

     Values for open's flags parameter,

               O_RDONLY
               O_WRONLY
               O_RDWR
               O_APPEND
               O_CREAT
               O_TRUNC
               O_EXCL
               O_SYNC
               O_NDELAY
               O_NONBLOCK
               O_BINARY            (WIN32 only)

     Values for spawn's mode parameter,

               _P_WAIT             (WIN32 only)
               _P_NOWAIT           (WIN32 only)

     Values for access's mode parameter,

               R_OK
               W_OK
               X_OK
               F_OK

     Values for lseek's whence parameter,

               SEEK_SET
               SEEK_CUR
               SEEK_END

     The following list summarises the system interface func-
     tions. Following this is a detailed description of each of
     them.
               int =     access(string [, int])
               int =     creat(string, int)
               array =   dir([string, ] [regexp, ] [string ])
               int =     dup(int [, int])
                         exec(string, array)
                         exec(string, string...)
               int =     lseek(int, int [, int])
               int =     open(string, int [, int])
               array =   pipe()
               struct =  stat(string|int|file)
               int =     wait()
               string =  ctime(int)
               int =     time()
               file =    fdopen(int)
               string =  getcwd()
                         alarm(int)
                         acct(string)
                         chdir(string)
                         chmod(string, int)
                         chown(string, int, int)
                         chroot(string)
                         _close(int)
                         _exit(int)
               int =     fork()
               int =     getpid()
               int =     getpgrp()
               int =     getppid()
               int =     getuid()
               int =     geteuid()
               int =     getgid()
               int =     getegid()
                         kill(int, int)
                         link(string, string)
                         mkdir(string, int)
                         mknod(string, int, int)
                         nice(int)
                         pause()
                         rmdir(string)
                         setpgrp()
                         setuid(int)
                         setgid(int)
                         signal(int, int)
                         sync()
                         ulimit(int, int)
                         umask(int)
                         unlink(string)
                         clock()
                         system(string)
                         lockf(int, int, int)
                         sleep(int)
               int =     spawn([int, ] string, string...)
               int =     spawn([int, ] string, array)
                         rename(string, string)
               struct =  passwd(int|string)
               array =   passwd()


     int = access(string [, int])

     Call the access(2) function to determine the accessibility
     of a file. The first parameter is the pathname of the file
     system object to be tested. The second, optional, parameter
     is the mode (a bitwise combination of R_OK, W_OK and X_OK or
     the special value, F_OK).  If mode is not passed F_OK is
     assumed. Access returns 0 if the file system object is
     accessible. Also supported on WIN32 platforms.

     int = creat(pathname, mode)

     Create a new ordinary file with the given pathname and mode
     (permissions etc...) and return the file descriptor, open
     for writing, for the file. Also supported on WIN32 plat-
     forms.

     array =   dir([pathname, ] [regexp, ] [selector ])

     This function is used to obtain the names of files within a
     given directory.  It returns an array of filenames.

     The pathname argument provides the directory pathname.  If
     NULL or omitted, the current directory is used.

     A regular expression can be used to select just matching
     names.

     A string argument after a regular expression, or the 2nd
     string argument if two are provided, is interpreted as a
     file-type selector.  It is a string comprised of these char-
     acters:

     f         Include regular files.

     d         Include directories.

     a         Include all files - regular, directory and spe-
               cial.


files.
     If no format is provided, the default is to select only regular
     All hidden files are also returned.  So, when traversing
     directories, make sure you skip ``.'' and ``..''.


     int = dup(int [, int])

     Duplicate a file descriptor by calling dup(2) or dup2(2) and
     return a new descriptor. If only a single parameter is
     passed dup(2) is called otherwise dup2(2) is called. Also
     supported on WIN32 platforms.

     exec(pathname, array)

     exec(pathname, string...)

     Execute a new program in the current process. The first
     parameter to exec is the pathname of an executable file (the
     program). The remaining parameters are either; an array of
     strings defining the parameters to be passed to the program,
     or, a variable number of strings that are passed, in order,
     to the program as its parameters. The first form is similar
     to C's execv function and the second form to C's execl func-
     tions. Note that no searching of the user's path is per-
     formed and the environment passed to the program is that of
     the current process (i.e., both are implemented by calls to
     execv(2)). This function is also available on Win32
     platforms.

     int = lseek(int, int [, int])

     Set the read/write position for an open file. The first
     parameter is the file descriptor associated with the file
     system object, the second parameter the offset. The third is
     the whence value which determines how the new file position
     is calculated. The whence value may be one of SEEK_SET,
     SEEK_CUR or SEEK_END and defaults to SEEK_SET if not speci-
     fied. Also supported on WIN32 platforms.

     int = open(pathname, int [, int])

     Open the named file for reading or writing depending upon
     the value of the second parameter, flags, and return a file
     descriptor. The second parameter is a bitwise combination of
     the various O_ values (see above) and if this set includes
     the O_CREAT flag a third parameter, mode, must also be sup-
     plied. Also supported on WIN32 platforms.

     array = pipe()

     Create a pipe and return an array containing two, integer,
     file descriptors used to refer to the input and output end-
     points of the pipe.

     struct = stat(pathname|int|file)

     Obtain information on the named file system object, file
     descriptor or file underlying an ici file object and return
     a struct containing that information. If the parameter is a
     file object that file object must refer to a file opened
     with ici's fopen function. The returned struct contains the
     following keys (which have the same names as the fields of
     the Unix statbuf structure with the leading "st_" prefix
     removed):

               dev
               ino
               mode
               nlink
               uid
               gid
               rdev
               size
               atime
               mtime
               ctime
               blksize
               blocks

     All values are integers. Also supported on WIN32 platforms.

     int = wait()

     Wait until a signal is received or a child process ter-
     minates or stops due to tracing and return the status
     returned by system call.

     string = ctime(int)

     Convert a time value (see time, below) to a string of the
     form "Sun Sep 16 01:03:52 1973\n" and return that string.
     This is primarily of use when converting the time values
     returned by stat. Also supported on WIN32 platforms.

     int = time()

     Return the time since 00:00:00 GMT,  Jan.  1,  1970, meas-
     ured in seconds.  Also supported on WIN32 platforms.

     file = fdopen(int [, mode])

     Returns a file object that can be used to perform I/O on the
     specified file descriptor. The file is opened for reading or
     writing according to mode (see fopen). If mode is specified
     "r" (reading) is assumed.

     string = getcwd()

     Returns the name of the current working directory. Also sup-
     ported on WIN32 platforms.

     alarm(int)

     Schedule a SIGALRM signal to be posted to the current pro-
     cess in the specified number of seconds. If the parameter is
     zero any alarm is cancelled.

     acct(pathname)

     Enable accounting on the specified file.

     chdir(pathname)

     Change the process's current working directory to the speci-
     fied path. Also supported on WIN32 platforms.

     chmod(pathname, int)

     Change the mode of a file system object.

     chown(pathname, int, int)

     Change the owner and group identifiers for a file system
     object.

     chroot(pathname)

     Change root directory for process.

     _close(int)

     Close a file descriptor. Also supported on WIN32 platforms.

     _exit(int)

     Exit the current process returning an integer exit status to
     the parent. Also supported on WIN32 platforms.

     int = fork()

     Create a new process. In the parent this returns the process
     identifier for the newly created process. In the newly
     created process it returns zero.

     int = getpid()

     Get the process identifier for the current process.

     int = getpgrp()

     Get the current process group identifier.

     int = getppid()

     Get the parent process identifier.

     int = getuid()

     Get the real user identifier of the owner of the current
     process.

     int = geteuid()

     Get the effective user identifier for the owner of the
     current process.

     int = getgid()

     Get the real group identifier for the current process.

     int = getegid()

     Get the effective group identifier for the current process.

     kill(int, int)

     Post a signal to a process.

     link(pathname, pathname)

     Create a link to an existing file.

     mkdir(pathname, int)

     Create a directory with the specified mode. Also supported
     on WIN32 platforms.

     mknod(pathname, int, int)

     Create a special file.

     nice(int)

     Change the nice value of a process.

     pause()

     Wait until a signal is delivered to the process.

     rmdir(pathname)

     Remove a directory. Also supported on WIN32 platforms.

     setpgrp()

     Set the process group.

     setuid(int)

     Set the real and effective user identifier for the current
     process.

     setgid(int)

     Set the real and effective group identifier for the current
     process.

     signal(int, int)

     Control signal handling in the process. Note at present
     handlers cannot be installed so signals are of limited use
     in ici programs.

     sync()

     Schedule in-memory file data to be written to disk.

     ulimit(int, int)

     Get and set user limits.

     umask(int)

     Set file creation mask.

     unlink(pathname)

     Remove a file. Also supported on WIN32 platforms.

     system(string)

     Execute a system command and return its exit status. Also
     supported on WIN32 platforms however using the system's com-
     mand interpreter.

     sleep(int)

     Suspend the process for the specified number of seconds.

     int = spawn([mode,] string, string...)

     int = spawn([mode, ] string, array)

     int = spawnp([mode,] string, string...)

     int = spawnp([mode, ] string, array)

     Spawn a sub-process. The parameters, other than mode, are as
     for exec - the string is the name of the executable and the
     remaining parameters form the command line arguments passed
     to the executable.

     The mode parameter controls whether or not the parent pro-
     cess waits for the spawned process to termiante. If mode is
     _P_WAIT the call to spawn returns when the process ter-
     minates and the result of spawn is the process exit status.
     If mode is not passed or is _P_NOWAIT the call to spawn
     returns prior to the process terminating and the result is
     the Win32 process handle for the new process.

     The spawnp variant will search the directories listed in the
     PATH environment variable for the executable program. In all
     other respects it is indentical to spawn.

     This function is only available on Win32 platforms.

     rename(pathname, pathname)

     Change the name of a file. The first parameter is the name
     of an existing file and the second is the new name that it
     is to be given.

     struct = passwd(int | string)

     array = passwd()

     The passwd() function accesses the Unix password file (which
     may or may not be an actual file according to the local
     system configuration). With no parameters passwd() returns
     an array of all password file entries, each entry is a
     struct. With a parameter passwd() returns the entry for the
     specific user id., int parameter, or user name, string
     parameter. A password file entry is a struct with the fol-
     lowing keys and values:


     name      The user's login name, a string.

     passwd    The user's encrypted password, a string. Note that
               some systems protect this (shadow password files)
               and this field may not be an actual encrypted
               password.

     uid       The user id., an int.

     gid       The user's default group, an int.

     gecos     The so-called gecos field, a string.

     dir       The user's home directory, a string.

     shell     The user's shell (initial program), a string.


     Terminal-window Interface

     This extension provides rectangular ascii character window
     areas that can be stacked over one another within an area of
     the screen, and also provides character input and output.

     The extension introduces a new type, window, to hold window
     objects.  Screen positions are described in terms of (line,
     column) pairs.  The top of the screen is line 0, the left
     column is 0.

     The following list summarises the window interface func-
     tions. Following this is a detailed description of each of
     them.

               w_box(window);
               w_clear(window);
               w_cursorat(window, int, int);
               string = w_edit(window, int, int, int, string);
               string = w_getchar();
               w_mesg(string);
               w_pop(window);
               int = w_paint(window, int, int, string [,
     string]);
               win = w_push(int, int, int, int);
               win = w_refresh();
               w_suspend();
               win = w_textwin(int, int, string [, string]);
               w_ungetchar();


     The window type has these components:


               w_nlines  The number of lines in the window.


               w_ncols   The number of columns in the window.


               w_atline  The line number of the cursor position.


               w_atcol   The column number of the cursor posi-
                         tion.

     A window can also be indexed by an integer - it returns the
     character at that position within the window, or an empty
     string if the index was out of bounds.


     w_box(window);

     Outlines the window by filling in the characters along the
     borders appropriately.

     w_clear(window);

     Blanks the window (writes a space character at every posi-
     tion).

     w_cursorat(window, line, col);

     Positions the cursor at the window co-ordinate.

     string = w_edit(window, line, col, width, string);

     The function allows the user to edit the window in the
     specified area determined by the window co-ordinate and the
     width.  It returns the contents of the edited area.

     It is terminated by ... ###?

     The string can be edited with the backspace character, the
     arrow keys, and of course by typing.

     string = w_getchar();

     This returns the name of the key struck.  In the case of
     printable ascii characters, this is just a string containing
     the single ascii character.

     On a Unix system, it does more.  In the case of a function
     key it returns the function key name ("F1" .. "F12" ..), in
     the case of other keys it returns "LEFT", "RIGHT", "UP",
     "DOWN", "HOME", "END", "PGUP", "PGDOWN" as appropriate.

     w_mesg(string);

     Paints the string onto the current window at the current
     cursor position.  Note that the window is not re-drawn until
     w_refresh() is called.

     w_pop(window);

     Destroys the window.  Any windows below will no longer be
     obscured.

     int = w_paint(window, line, col, msg [, tabs]);

     The string msg is painted onto the window starting at the
     given window co-ordinates.  Line and col are treated as in
     w_push(), below.

     The optional string is a sequence of tab specifiers with no
     separators.  They define tab positions, with the same
     semantics as in troff.  This basically means that when a tab
     is painted onto the window, the cursor is moved to the
     appropriate position.  If missing, tabs are assumed to move
     to the next column that is a multiple of 8.  This is
     equivalent to setting "8L" (see below).

     Each tab specifier is an optional +, followed by a decimal
     number followed by an optional leader character, followed by
     one of L (normal left tabbing), C (tab centred) or R (right
     tabs).

     The `+' means the position is relative to the previous tab.
     Otherwise, tab positions are relative to the starting
     column.  The last tab specifier repeats.

     The tab specifiers make it very easy to lay out text in win-
     dows neatly.

     win = w_push(line, col, nlines, ncols);

     Create a new window positioned at the given screen co-
     ordinates, and with the number of lines and columns.  Assum-
     ing that `screen_lines' and `screen_cols' represent the
     number of lines and columns in the terminal window then:

     If nlines < 0, it is set to screen_lines - abs(nlines %
     screen_lines)

     If ncols < 0: it is set to screen_cols - abs(ncols %
     screen_cols)

     If line == -2: the window is positioned so it touches the
     bottom of the screen.  If line == -1: the window is posi-
     tioned so it's vertically centered.

     If col == -2: the window is positioned so it touches the
     right edge of the screen.  If col == -1: the window is posi-
     tioned so it's horizontally centered.

     win = w_refresh();

     Update the screen from all the windows that have been modi-
     fied.  Does a reasonably optimal update to minimise actual
     changes.

     w_suspend();

     ###

     win = w_textwin(line, col, string [, tabs]);

     A new window is created at the screen co-ordinate (line,
     column), big enough to hold the given text, and the first
     string is painted onto it.  Line and col are treated as in
     w_push().  The optional string is a sequence of tab specif-
     iers with no separators.  (See w_paint().)

     Try this:

             auto w;

             w =
                 w_textwin
                 (
                     3, 7,
                     "Mary had a little lamb\n"
                     "Its fleas were black as pitch\n"
                     "So everywhere that Mary went\n"
                     "She had a dreadful itch\n"
                 );
             w_refresh();
             sleep(2);

             w_box(w);
             w_mesg("Dying soon...");
             w_refresh();
             sleep(2);

             w_pop(w);
             w_refresh();

     w_ungetchar();

     Put a keystroke back into the input stream.  You can only
     push back a maximum of one.



     Sockets Interface

     The sockets extension is available on systems that provide
     BSD-compatible sockets calls and for Win32 platforms. The
     extension allows ici programs to access network functions.
     The sockets extension is generally compatible with the C
     sockets functions but uses types and calling semantics more
     akin to the ici environment.

     The sockets extension introduces a new type, socket, to hold
     socket objects. The new intrinsic function, socket, returns
     a socket object.

     Network Addresses

     The sockets interfaces specifies IP network addresses using
     strings. Network addresses are of the form port@ host where
     the @host part is optional. The port may be specified as an
     integer number or a string which is looked up in the ser-
     vices database. If the port is a service name it may be in
     the form name/protocol with protocol being either tcp or
     udp.  The host portion of the address may be a domain name,
     an IP address in dotted decimal notation or one of the spe-
     cial addresses local ("." - dot), any ("?") or all ("*"). If
     the host portion is omitted the default host depends on the
     context. See the descriptions of the connect and bind func-
     tions below.

     The following list summarises the sockets interface func-
     tions. Following this is a detailed description of each of
     them.

               skt =     socket(string)
               skt =     listen(skt)
               skt =     accept(skt)
               skt =     connect(skt, string)
               skt =     bind(skt, string)
               struct =  select([int,] set [, set [, set]])
               int =     getsockopt(skt, string)
                         setsockopt(skt, string, int)
               string =  domainname()
               string =  hostname()
               string =  username([int])
               string =  getpeername(skt)
               string =  getsockname(skt)
                         sendto(skt, string, string)
               struct =  recvfrom(skt, int)
                         send(skt, string)
               string =  recv(skt, int)
               int =     getportno(skt)
               string =  gethostbyname(string)
               int =     sktno(skt)
               file =    sktopen(skt [, mode])
               array =   socketpair()

     skt = socket(string)

     Create and return a new socket object of the specified pro-
     tocol. The string, the protocol, may be one of tcp or udp.
     For example:

     skt = socket("tcp");

     skt = accept(skt)

     Accept a connection to a TCP socket and return a new socket
     for that connection.

     skt = listen(skt)

     Allow connections to a TCP socket. Returns the socket
     passed.

     skt = connect(skt, address)

     Establish a TCP connection to the specified address or asso-
     ciate the address with as the destination for messages on a
     UDP socket. If the host portion of the address is not speci-
     fied "." (dot) is used to connect to the local host. The
     original socket is returned.

     skt = bind(skt [, address|int])

     Associate a local address for the socket (TCP or UDP). If
     the address is not specified the system selects an unused
     local port number for the socket. If the host portion of the
     address is not specified "?" (any) is used. If the address
     is passed as an integer it specifies the port number to be
     bound, the host portion is "?". Bind returns the socket
     parameter.

     struct = select([int,] set|NULL [, set|NULL [, set|NULL]])

     Check sockets for I/O readiness with optional timeout.
     Select may be passed up to three sets of sockets that are
     checked for readiness to perform I/O. The first set holds
     the sockets to test for input pending, the second set the
     sockets to test for output able and the third set the sock-
     ets to test for exceptional states. NULL may be passed in
     place of a set parameter to avoid passing empty sets. An
     integer may also appear in the parameter list. This integer
     specifies the number of milliseconds to wait for the sockets
     to become ready. If a zero timeout is passed the sockets are
     polled to test their state. If no timeout is passed the call
     blocks until at least one of the sockets is ready for I/O.

     The result of select is a struct containing three sets, of
     sockets, identified by the keys read, write and except.

     int = getsockopt(skt, string, int)

     Retrieve the value of a socket option. A socket may have
     various attributes associated with it. These are accessed
     via the getsockopt and setsockopt functions. The attributes
     are identified using string keys from the following list:

               debug
               reuseaddr
               keepalive
               dontroute
               useloopback
               linger
               broadcast
               oobinline
               sndbuf
               rcvbuf
               type
               error

     setsockopt(skt, string, int)

     Set a socket option (see getsockopt for option names) to the
     integer value.

     string = domainname()

     Return the domain name of the current host.

     string = hostname()

     Return the name of the current host.

     string = username([int])

     Return the name of the owner of the current process or if an
     integer, user number, is passed, of that user.

     string = getpeername(skt)

     Return the address of the peer of a TCP socket.

     string = getsockname(skt)

     Return the local address of a socket.

     sendto(skt, string, string)

     Send the data in the second parameter to the specified
     address.

     array = socketpair()

     Returns an array containing a pair of connected sockets.

     struct = recvfrom(skt, int)

     Receive a message on a socket and return a struct containing
     the data of the message, in string, and the source address
     of the data. The int parameter gives the maximum number of
     bytes to receive. The result is a struct with the keys msg
     and addr used to access the returned information.

     send(skt, string)

     Send the content of the string on a socket.

     string = recv(skt, int)

     Receive data from a socket and return it as a string. The
     int parameter fives the maximum size of message that will be
     received.

     int = getportno(skt)

     Return the local port number assigned to a TCP or UDP
     socket.

     string = gethostbyname(string)

     Match a network address against the hosts database and
     return a hostname.

     int = sktno(skt)

     Return the file descriptor associated with a socket.

     file = sktopen(skt [, mode])

     Open a socket as a file, for input or output according to
     mode (see fopen). This function is not available on WIN32
     platforms.


SEE ALSO
     anici is an extension of ici that provides loadable dynamic
     libraries, a type extension method, object serialisation,
     and many other goodies.  See
     http://www.zeta.org.au/~atrn/ici/

     OpenPage is a page-based graphics language for high quality
     colour printing built from ici as the language core.  See
     http://www.research.canon.com.au/openpage.html


AUTHOR
     Tim Long <timl@research.canon.com.au>

     With the assistance of:
     Andy Newman
     Chris Amies
     Luke Kendall
     Giordano Pezzoli
     Yiorgos Adamopolous
     Gary Gendel
     John Rosauer
     Ross Cartlidge

     not to mention:

     Henry Spencer












